eBPF(二)—— eBPF开发工具

admin 2023年3月31日09:47:34评论6 views字数 17126阅读57分5秒阅读模式
eBPF(二)—— eBPF开发工具
eBPF(二)—— eBPF开发工具

eBPF(二)—— eBPF开发工具

eBPF(二)—— eBPF开发工具
目录

○ 前言

○ bpftrace

    ◇ 安装

    ◇ 命令行模式

    ◇ 脚本模式

○ BCC

    ◇ 安装

    ◇ 测试

    ◇ Map映射

    ◇ Hash映射

○ libbpf C/C++库

    ◇ 安装

    ◇ CO-RE

    ◇ 测试

○ libbpf-bootstrap

    ◇ 安装依赖

    ◇ 源码编译

    ◇ 测试

○ 声明


前言

eBPF(二)—— eBPF开发工具

NO.1

目前eBPF存在几个开发工具链来协助 eBPF 程序的开发和管理,用于满足用户的不同需求:

    1.bpftrace

    2.BCC库

    3.libbpf C/C++库

    4.eBPF GO库

    ····

本篇我们将对几种常用开发工具进行介绍。


bpftrace

eBPF(二)—— eBPF开发工具

NO.2

bpftrace 是 Linux eBPF 的高级跟踪语言,可用于最新的 Linux 内核。bpftrace 使用 LLVM 作为后端将脚本编译为 eBPF 字节码,并利用 BCC 与 Linux eBPF 子系统以及现有的 Linux 跟踪功能进行交互:内核动态跟踪 (kprobes)、用户级动态跟踪 (uprobes) 和跟踪点。bpftrace 语言的灵感来自 awk、C 和前身跟踪器,例如 DTrace 和 SystemTap。


安装

apt-get install bpftrace


命令行模式

1)列出所有插桩点

bpftrace -l


2)添加查询条件,例如只查询xxx类型的插桩点

bpftrace -l 'xxx:*'


例:

root@ubuntu:~# bpftrace -l "tracepoint:*"tracepoint:vsock:virtio_transport_alloc_pkttracepoint:vsock:virtio_transport_recv_pkttracepoint:vb2:vb2_buf_done...


3)查询插桩点声明

    通过调试信息查询

cat /sys/kernel/debug/tracing/events/syscalls/xxx/format

    

    例:查询sys_enter_execve事件的声明

root@ubuntu:~# cat /sys/kernel/debug/tracing/events/syscalls/sys_enter_execve/formatname: sys_enter_execveID: 716format:  field:unsigned short common_type;  offset:0;  size:2;  signed:0;  field:unsigned char common_flags;  offset:2;  size:1;  signed:0;  field:unsigned char common_preempt_count;  offset:3;  size:1;  signed:0;  field:int common_pid;  offset:4;  size:4;  signed:1;
field:int __syscall_nr; offset:8; size:4; signed:1; field:const char * filename; offset:16; size:8; signed:0; field:const char *const * argv; offset:24; size:8; signed:0; field:const char *const * envp; offset:32; size:8; signed:0;
print fmt: "filename: 0x%08lx, argv: 0x%08lx, envp: 0x%08lx", ((unsigned long)(REC->filename)), ((unsigned long)(REC->argv)), ((unsigned long)(REC->envp))

    通过bpftrace -lv命令查询

bpftrace -lv xxx

  

  例:查询sys_enter_execve事件的声明

root@ubuntu:~# bpftrace -lv tracepoint:syscalls:sys_enter_execvetracepoint:syscalls:sys_enter_execve    int __syscall_nr;    const char * filename;    const char *const * argv;    const char *const * envp;


4)跟踪execve系统调用,监控enter状态的命令与参数 

bpftrace -e 'tracepoint:syscalls:sys_enter_execve{printf("pid=%d called=%s filename=%s argv=",pid,comm,str(args->filename));join(args->argv);}'

eBPF(二)—— eBPF开发工具


5)跟踪execve系统调用,监控exit状态的返回值

bpftrace -e 'tracepoint:syscalls:sys_exit_execve{printf("pid=%d command=%s ret=%d n",pid,comm,args->ret);}'


eBPF(二)—— eBPF开发工具


6)查询内核函数调用栈(ctrl+c后显示结果)

sudo bpftrace -e 'kprobe:vfs_open {@[kstack(perf)] = count();}'


eBPF(二)—— eBPF开发工具


7)追踪自定义程序

/*/* Filename: test.c/* Compile: gcc -g test.c -o test*/ 
#include <stdio.h>
int main(int argc, char **argv){ printf("hello world!n"); return 0;}


追踪main函数,打印参数个数和第一个参数:

bpftrace -e 'uprobe:/Work/Project/eBPF/bpftrace/test:main{printf("argc=%d argv[0]=",arg0);join(arg1);}'


eBPF(二)—— eBPF开发工具


追踪main函数,打印返回值:

sudo bpftrace -e 'uretprobe:/Work/Project/eBPF/bpftrace/test:main{printf("main:return_value=%dn",retval);}'


eBPF(二)—— eBPF开发工具


8)跟踪开源应用bash

查询bash指令的位置:

root@ubuntu:~# which bash/usr/bin/bash


查询bash中与readline相关的跟踪点:

root@ubuntu:~# bpftrace -l "uprobe:/usr/bin/bash:*" | grep readlineuprobe:/usr/bin/bash:readline_internal_charuprobe:/usr/bin/bash:readline_internal_setupuprobe:/usr/bin/bash:posix_readline_initializeuprobe:/usr/bin/bash:readlineuprobe:/usr/bin/bash:initialize_readlineuprobe:/usr/bin/bash:pcomp_set_readline_variablesuprobe:/usr/bin/bash:readline_internal_teardown

跟踪bash应用的readline探测点,列出用户与命令:

root@ubuntu:~# bpftrace -e 'uretprobe:/usr/bin/bash:readline{printf("User %d executed "%s" commandn",uid,str(retval));}'Attaching 1 probe...User 0 executed "ls" command...

9)探测软件事件
查询探针software支持的事件列表:

root@ubuntu:~# bpftrace  -l 's:*'software:alignment-faults:software:bpf-output:software:context-switches:software:cpu-clock:software:cpu-migrations:software:dummy:software:emulation-faults:software:major-faults:software:minor-faults:software:page-faults:software:task-clock:

探测page-faults:100事件(ctrl+c后显示结果):

bpftrace -e 'software:page-faults:100{@[comm]=count();}'

eBPF(二)—— eBPF开发工具


10)探测硬件事件

查询探针hardware支持的事件列表:

root@ubuntu:~# bpftrace  -l 'h:*'hardware:backend-stalls:hardware:branch-instructions:hardware:branch-misses:hardware:bus-cycles:hardware:cache-misses:hardware:cache-references:hardware:cpu-cycles:hardware:frontend-stalls:hardware:instructions:hardware:ref-cycles:

尝试统计30秒内cache-missed次数超过1000000的进程,不知道为什么跟踪hardware所有事件都报错:

bpftrace -e 'hardware:cache-misses:1000000{@[pid]=count();}interval:s:30{exit();}'

eBPF(二)—— eBPF开发工具


脚本模式


将命令编写为脚本模式,文件名:script.bt

#!/usr/bin/env bpftrace
tracepoint:syscalls:sys_enter_execve { printf("pid=%d command=%s called=%s n",pid,comm,str(args->filename)); }


运行效果:

eBPF(二)—— eBPF开发工具


BCC

eBPF(二)—— eBPF开发工具

NO.3

BCC 是一个框架,使用户能够编写带有嵌入其中的 eBPF 程序的 python 程序。该框架主要针对涉及应用程序和系统分析/跟踪的用例,其中 eBPF 程序用于收集统计信息或生成事件,而用户空间中的对应物收集数据并以人类可读的形式显示。运行 python 程序将生成 eBPF 字节码并将其加载到内核中。


eBPF(二)—— eBPF开发工具


安装

可以参考官方提供的安装文档或尝试使用以下命令安装:

sudo apt-get install bpfcc-tools linux-headers-$(uname -r)

测试

文件名:hello.c

int hello_world(void *ctx){    bpf_trace_printk("Hello, World!");        return 0;}

文件名:hello.py

#!/usr/bin/env python3
# 1) import bcc libraryfrom bcc import BPF
# 2) load BPF programb = BPF(src_file="hello.c", cflags=["-Wno-macro-redefined"])
# 3) attach kprobeb.attach_kprobe(event="__x64_sys_execve", fn_name="hello_world")
# 4) read and print /sys/kernel/debug/tracing/trace_pipeb.trace_print()

运行效果:

eBPF(二)—— eBPF开发工具


Map映射


Map映射能够将BPF数据在内核态与用户态互相映射,部分细节笔者还没有研究得很透彻。


文件名:open.c

#include <uapi/linux/openat2.h>#include <linux/sched.h>
// 定义数据结构struct data_t { u32 pid; u64 ts; char comm[TASK_COMM_LEN]; char fname[NAME_MAX];};
// 定义性能事件映射BPF_PERF_OUTPUT(events);
// 定义kprobe处理函数int bcc_do_sys_openat2(struct pt_regs *ctx, int dfd, const char __user * filename, struct open_how *how){ struct data_t data = { };
// 获取PID和时间 data.pid = bpf_get_current_pid_tgid(); data.ts = bpf_ktime_get_ns();
// 获取进程名 if (bpf_get_current_comm(&data.comm, sizeof(data.comm)) == 0) { bpf_probe_read(&data.fname, sizeof(data.fname), (void *)filename); }
// 提交性能事件 events.perf_submit(ctx, &data, sizeof(data)); return 0;}

文件名:open.py

代码

运行效果:

from bcc import BPF
# 1) 加载eBPF代码b = BPF(src_file="open.c", cflags=["-Wno-macro-redefined"])b.attach_kprobe(event="do_sys_openat2", fn_name="bcc_do_sys_openat2")
# 2) 输出头print("%-18s %-16s %-6s %-16s" % ("TIME(s)", "COMM", "PID", "FILE"))
# 3) 定义性能事件打印函数start = 0def print_event(cpu, data, size): global start event = b["events"].event(data) if start == 0: start = event.ts time_s = (float(event.ts - start)) / 1000000000 print("%-18.9f %-16s %-6d %-16s" % (time_s, event.comm, event.pid, event.fname))
# 4) 绑定性能事件映射和输出函数,并从映射中循环读取数据b["events"].open_perf_buffer(print_event)while 1: try: b.perf_buffer_poll() except KeyboardInterrupt:        exit()


Hash映射


Hash映射能使多个跟踪点之间互相共享数据,例如让sys_enter_execve和sys_exit_execve共享同一段数据。


文件名:execsnoop.c

#include <uapi/linux/ptrace.h>#include <linux/sched.h>#include <linux/fs.h>
// consts for arguments (ensure below stack size limit 512)#define ARGSIZE 64#define TOTAL_MAX_ARGS 5#define FULL_MAX_ARGS_ARR (TOTAL_MAX_ARGS * ARGSIZE)#define LAST_ARG (FULL_MAX_ARGS_ARR - ARGSIZE)
// perf event map (sharing data to userspace) and hash map (sharing data between tracepoints)struct data_t { u32 pid; char comm[TASK_COMM_LEN]; int retval; unsigned int args_size; char argv[FULL_MAX_ARGS_ARR];};
BPF_PERF_OUTPUT(events);BPF_HASH(tasks, u32, struct data_t);
// helper function to read string from userspace.static int __bpf_read_arg_str(struct data_t *data, const char *ptr){ if (data->args_size > LAST_ARG) { return -1; }
int ret = bpf_probe_read_user_str(&data->argv[data->args_size], ARGSIZE, (void *)ptr); if (ret > ARGSIZE || ret < 0) { return -1; } // increase the args size. the first tailing '' is not counted and hence it // would be overwritten by the next call. data->args_size += (ret - 1);
return 0;}
//定义sys_enter_execve跟踪点处理函数.TRACEPOINT_PROBE(syscalls, sys_enter_execve){ // 变量定义 unsigned int ret = 0; const char **argv = (const char **)(args->argv);
// 获取进程PID和进程名称 struct data_t data = { }; u32 pid = bpf_get_current_pid_tgid(); data.pid = pid; bpf_get_current_comm(&data.comm, sizeof(data.comm));
// 获取第一个参数(即可执行文件的名字) if (__bpf_read_arg_str(&data, (const char *)argv[0]) < 0) { goto out; }
// 获取其他参数(限定最多5个) for (int i = 1; i < TOTAL_MAX_ARGS; i++) { if (__bpf_read_arg_str(&data, (const char *)argv[i]) < 0) { goto out; } }
out: // 存储到哈希映射中 tasks.update(&pid, &data); return 0;}
// 定义sys_exit_execve跟踪点处理函数.TRACEPOINT_PROBE(syscalls, sys_exit_execve){ // 从哈希映射中查询进程基本信息 u32 pid = bpf_get_current_pid_tgid(); struct data_t *data = tasks.lookup(&pid);
// 填充返回值并提交到性能事件映射中 if (data != NULL) { data->retval = args->ret; events.perf_submit(args, data, sizeof(struct data_t));
// 最后清理进程信息 tasks.delete(&pid); }
return 0;}

文件名:execsnoop.py

from bcc import BPFfrom bcc.utils import printb
# 1) 加载eBPF代码b = BPF(src_file="execsnoop.c", cflags=["-Wno-macro-redefined"])
# 2) print headerprint("%-6s %-16s %-3s %s" % ("PID", "COMM", "RET", "ARGS"))
# 3) 定义性能事件打印函数def print_event(cpu, data, size): # BCC自动根据"struct data_t"生成数据结构 event = b["events"].event(data) printb(b"%-6d %-16s %-3d %-16s" % (event.pid, event.comm, event.retval, event.argv))
# 4) 绑定性能事件映射和输出函数,并从映射中循环读取数据b["events"].open_perf_buffer(print_event)while 1: try: b.perf_buffer_poll() except KeyboardInterrupt:        exit()

运行效果:

eBPF(二)—— eBPF开发工具


libbpf C/C++库

eBPF(二)—— eBPF开发工具

NO.4

libbpf 库是一个基于 C/C++ 的通用 eBPF 库,它有助于将由 clang/LLVM 编译器生成的 eBPF 目标文件加载到内核中,并且通常通过提供易于使用的库 API 来抽象与 BPF 系统调用的交互应用程序。


eBPF(二)—— eBPF开发工具


安装


方法一:使用apt安装相关模块

apt-get install libbpf-dev

方法二:使用源码构建静态libbpf.a和共享libbpf.so,尽量选择与当前系统内核版本相近的源码版本

cd srcmake

CO-RE


CO-RE (Compile Once – Run Everywhere)中文译为”一次编译,到处运行“。使用libbpf编译的程序,只要目标服务器内核支持BPF,即可将程序在不依赖LLVM、CLANG及内核头文件的情况下部署到目标服务器。


Libbpf+BPF CO-RE的理念是,BPF程序与任何"正常"用户空间程序没有太大区别:它们应该汇编成小型二进制文件,然后以紧凑的形式进行部署,以瞄准主机。Libbpf 扮演 BPF 程序装载机的角色,执行平凡的设置工作(重定位、加载和验证 BPF 程序、创建 BPF map、连接到 BPF 挂钩等),让开发人员只担心 BPF 程序的正确性和性能。这种方法将开销保持在最低水平,消除沉重的依赖关系,使整体开发人员体验更加愉快


测试

1)生成内核头文件

sudo bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h

2)开发内核态程序,生成脚手架文件(包含各项声明与BPF字节码,解除环境依赖)

文件名:execsnoop.h

#ifndef __EXECSNOOP_H#define __EXECSNOOP_H
#define ARGSIZE 128#define TASK_COMM_LEN 16#define TOTAL_MAX_ARGS 60#define FULL_MAX_ARGS_ARR (TOTAL_MAX_ARGS * ARGSIZE)#define BASE_EVENT_SIZE (size_t)(&((struct event*)0)->args)#define EVENT_SIZE(e) (BASE_EVENT_SIZE + e->args_size)#define LAST_ARG (FULL_MAX_ARGS_ARR - ARGSIZE)
struct event { char comm[TASK_COMM_LEN]; pid_t pid; int retval; int args_count; unsigned int args_size; char args[FULL_MAX_ARGS_ARR];};
#endif        /* __EXECSNOOP_H */

文件名:execsnoop.bpf.c

#include "vmlinux.h"#include "execsnoop.h"#include <bpf/bpf_helpers.h>
static const struct event empty_event = { };
// 定义哈希映射struct { __uint(type, BPF_MAP_TYPE_HASH); __uint(max_entries, 10240); __type(key, pid_t); __type(value, struct event);} execs SEC(".maps");
// 定义性能事件映射struct { __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY); __uint(key_size, sizeof(u32)); __uint(value_size, sizeof(u32));}events SEC(".maps");
// 定义sys_enter_execve跟踪点函数SEC("tracepoint/syscalls/sys_enter_execve")int tracepoint__syscalls__sys_enter_execve(struct trace_event_raw_sys_enter *ctx){ struct event *event; const char **args = (const char **)(ctx->args[1]); const char *argp;
// 查询PID u64 id = bpf_get_current_pid_tgid(); pid_t pid = (pid_t) id;
// 保存一个空的event到哈希映射中 if (bpf_map_update_elem(&execs, &pid, &empty_event, BPF_NOEXIST)) { return 0; } event = bpf_map_lookup_elem(&execs, &pid); if (!event) { return 0; } // 初始化event变量 event->pid = pid; event->args_count = 0; event->args_size = 0;
// 查询第一个参数 unsigned int ret = bpf_probe_read_user_str(event->args, ARGSIZE, (const char *)ctx->args[0]); if (ret <= ARGSIZE) { event->args_size += ret; } else { /* write an empty string */ event->args[0] = ''; event->args_size++; }
// 查询其他参数,使用pragma unroll控制循环次数 event->args_count++;#pragma unroll for (int i = 1; i < TOTAL_MAX_ARGS; i++) { bpf_probe_read_user(&argp, sizeof(argp), &args[i]); if (!argp) return 0;
if (event->args_size > LAST_ARG) return 0;
ret = bpf_probe_read_user_str(&event->args[event->args_size], ARGSIZE, argp); if (ret > ARGSIZE) return 0;
event->args_count++; event->args_size += ret; }
// 再尝试一次,确认是否还有未读取的参数 bpf_probe_read_user(&argp, sizeof(argp), &args[TOTAL_MAX_ARGS]); if (!argp) return 0;
// 如果还有未读取参数,则增加参数数量(用于输出"...") event->args_count++;
return 0;}
// 定义sys_exit_execve跟踪点函数SEC("tracepoint/syscalls/sys_exit_execve")int tracepoint__syscalls__sys_exit_execve(struct trace_event_raw_sys_exit *ctx){ u64 id; pid_t pid; int ret; struct event *event;
// 从哈希映射中查询进程基本信息 id = bpf_get_current_pid_tgid(); pid = (pid_t) id; event = bpf_map_lookup_elem(&execs, &pid); if (!event) return 0;
// 更新返回值和进程名称 ret = ctx->ret; event->retval = ret; bpf_get_current_comm(&event->comm, sizeof(event->comm));
// 提交性能事件 size_t len = EVENT_SIZE(event); if (len <= sizeof(*event)) bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, event, len);
// 清理哈希映射 bpf_map_delete_elem(&execs, &pid); return 0;}// 定义许可证(前述的BCC默认使用GPL)char LICENSE[] SEC("license") = "GPL";

编译命令:

# clang -g -O2 -target bpf -D__TARGET_ARCH_x86_64 -I/usr/include/x86_64-linux-gnu -I. -c execsnoop.bpf.c -o execsnoop.bpf.o# bpftool gen skeleton execsnoop.bpf.o > execsnoop.skel.h

3)开发用户态程序,处理收集的信息

文件名:execsnoop.c

#include <stdio.h>#include <unistd.h>#include <stdlib.h>#include <sys/resource.h>#include <bpf/libbpf.h>#include <time.h>#include "execsnoop.h"#include "execsnoop.skel.h"
// libbpf错误和调试信息回调的处理程序。static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args){#ifdef DEBUGBPF return vfprintf(stderr, format, args);#else return 0;#endif}
// 丢失事件的处理程序。void handle_lost_events(void *ctx, int cpu, __u64 lost_cnt){ fprintf(stderr, "Lost %llu events on CPU #%d!n", lost_cnt, cpu);}
// 打印参数(替换''为空格)static void print_args(const struct event *e){ int args_counter = 0;
for (int i = 0; i < e->args_size && args_counter < e->args_count; i++) { char c = e->args[i]; if (c == '') { args_counter++; putchar(' '); } else { putchar(c); } } if (e->args_count > TOTAL_MAX_ARGS) { fputs(" ...", stdout); }}
// 性能事件回调函数(向终端中打印进程名、PID、返回值以及参数)void handle_event(void *ctx, int cpu, void *data, __u32 data_sz){ const struct event *e = data; printf("%-16s %-6d %3d ", e->comm, e->pid, e->retval); print_args(e); putchar('n');}
// Bump RLIMIT_MEMLOCK,允许BPF子系统做任何它需要的事情。 static void bump_memlock_rlimit(void){ struct rlimit rlim_new = { .rlim_cur = RLIM_INFINITY, .rlim_max = RLIM_INFINITY, };
if (setrlimit(RLIMIT_MEMLOCK, &rlim_new)) { fprintf(stderr, "Failed to increase RLIMIT_MEMLOCK limit!n"); exit(1); }}
int main(int argc, char **argv){ struct execsnoop_bpf *skel; struct perf_buffer_opts pb_opts; struct perf_buffer *pb = NULL; int err;
// 1. 设置调试输出函数,libbpf发生错误会回调libbpf_print_fn libbpf_set_print(libbpf_print_fn);
// 2. 增大进程限制的内存,默认值通常太小,不足以存入BPF映射的内容 bump_memlock_rlimit();
// 3. 打开BPF程序 skel = execsnoop_bpf__open(); if (!skel) { fprintf(stderr, "Failed to open BPF skeletonn"); return 1; }
// 4. 加载BPF字节码 err = execsnoop_bpf__load(skel); if (err) { fprintf(stderr, "Failed to load and verify BPF skeletonn"); goto cleanup; }
// 5. 挂载BPF字节码到跟踪点 err = execsnoop_bpf__attach(skel); if (err) { fprintf(stderr, "Failed to attach BPF skeletonn"); goto cleanup; }
// 6. 配置性能事件回调函数 pb_opts.sample_cb = handle_event; pb_opts.lost_cb = handle_lost_events; pb = perf_buffer__new(bpf_map__fd(skel->maps.events), 64, &pb_opts); err = libbpf_get_error(pb); if (err) { pb = NULL; fprintf(stderr, "failed to open perf buffer: %dn", err); goto cleanup; }
printf("%-16s %-6s %3s %sn", "COMM", "PID", "RET", "ARGS");
// 7. 从缓冲区中循环读取数据 while ((err = perf_buffer__poll(pb, 100)) >= 0) ; printf("Error polling perf buffer: %dn", err);
cleanup: perf_buffer__free(pb); execsnoop_bpf__destroy(skel); return err != 0;}

编译命令:

# clang -g -O2 -Wall -I . -c execsnoop.c -o execsnoop.oclang -Wall -O2 -g execsnoop.o -static -lbpf -lelf -lz -o execsnoop

运行效果:

eBPF(二)—— eBPF开发工具


libbpf-bootstrap

eBPF(二)—— eBPF开发工具

NO.5

libbpf 在使用上并不是很直观,所以 eBPF 维护者开发了一个脚手架项目 libbpf-bootstrap,它结合了 BPF 社区的最佳开发实践,为初学者提供了一个简单易用的上手框架。


安装依赖

apt install clang libelf1 libelf-dev zlib1g-dev


源码编译

# checkout libbpf-bootstrapgit clone https://github.com/libbpf/libbpf-bootstrap
# update submodulescd libbpf-bootstrapgit submodule update --init --recursive
# build existing samplescd example/cmake

测试

文件名:hello.bpf.c

#include <linux/bpf.h>#include <bpf/bpf_helpers.h>
SEC("tracepoint/syscalls/sys_enter_execve")int handle_tp(void *ctx){ int pid = bpf_get_current_pid_tgid()>> 32; char fmt[] = "BPF triggered from PID %d.n"; bpf_trace_printk(fmt, sizeof(fmt), pid); return 0;}
char LICENSE[] SEC("license") = "Dual BSD/GPL";


文件名:hello.c

#include <stdio.h>#include <stdlib.h>#include <string.h>#include <assert.h>#include <errno.h>#include <fcntl.h>#include <unistd.h>#include <sys/resource.h>#include <bpf/libbpf.h>#include "hello.skel.h"
#define DEBUGFS "/sys/kernel/debug/tracing/"
/* logging function used for debugging */static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args){#ifdef DEBUGBPF return vfprintf(stderr, format, args);#else return 0;#endif}
/* read trace logs from debug fs */void read_trace_pipe(void){ int trace_fd;
trace_fd = open(DEBUGFS "trace_pipe", O_RDONLY, 0); if (trace_fd < 0) return;
while (1) { static char buf[4096]; ssize_t sz;
sz = read(trace_fd, buf, sizeof(buf) - 1); if (sz> 0) { buf[sz] = 0; puts(buf); } }}
/* set rlimit (required for every app) */static void bump_memlock_rlimit(void){ struct rlimit rlim_new = { .rlim_cur = RLIM_INFINITY, .rlim_max = RLIM_INFINITY, };
if (setrlimit(RLIMIT_MEMLOCK, &rlim_new)) { fprintf(stderr, "Failed to increase RLIMIT_MEMLOCK limit!n"); exit(1); }}
int main(int argc, char **argv){ struct hello_bpf *skel; int err;
/* Set up libbpf errors and debug info callback */ libbpf_set_print(libbpf_print_fn);
/* Bump RLIMIT_MEMLOCK to allow BPF sub-system to do anything */ bump_memlock_rlimit();
/* Open BPF application */ skel = hello_bpf__open(); if (!skel) { fprintf(stderr, "Failed to open BPF skeletonn"); return 1; }
/* Load & verify BPF programs */ err = hello_bpf__load(skel); if (err) { fprintf(stderr, "Failed to load and verify BPF skeletonn"); goto cleanup; }
/* Attach tracepoint handler */ err = hello_bpf__attach(skel); if (err) { fprintf(stderr, "Failed to attach BPF skeletonn"); goto cleanup; }
printf("Hello BPF started, hit Ctrl+C to stop!n");
read_trace_pipe();
cleanup: hello_bpf__destroy(skel); return -err;}

更新Makefile中待编译的APP列表:

APPS = minimal minimal_legacy bootstrap uprobe kprobe fentry usdt sockfilter tc hello

编译:

eBPF(二)—— eBPF开发工具


运行效果:

eBPF(二)—— eBPF开发工具


声明

eBPF(二)—— eBPF开发工具

NO.6

本文作者:lzyddf

本文编辑:Yusa

感谢lzyddf师傅 (๑•̀ㅂ•́)و✧


原文链接:

https://blog.csdn.net/qq_41988448/article/details/127813132?spm=1001.2014.3001.5501

参考文档:

https://ebpf.io/what-is-ebpf/

https://blog.csdn.net/qq_41988448/article/details/127813132?spm=1001.2014.3001.5501

https://blog.csdn.net/sinat_22338935/article/details/123095926?spm=1001.2014.3001.5502

https://blog.csdn.net/sinat_22338935/article/details/123318084

https://feisky.xyz/posts/2021-01-29-ebpf-program/

https://github.com/iovisor/bcc/issues/3366



往期回顾


eBPF(二)—— eBPF开发工具

鼠标指针文件格式解析——Windows(三)

eBPF(二)—— eBPF开发工具

eBPF(一)—— eBPF介绍&内核编译

eBPF(二)—— eBPF开发工具

中间件常见漏洞之Tomcat


eBPF(二)—— eBPF开发工具
eBPF(二)—— eBPF开发工具

扫码关注我们


天虞实验室为赛宁网安旗下专业技术团队,重点攻关公司业务相关信息安全前沿技术。

原文始发于微信公众号(天虞实验室):eBPF(二)—— eBPF开发工具

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年3月31日09:47:34
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   eBPF(二)—— eBPF开发工具https://cn-sec.com/archives/1707390.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息