move to libbpf 1.x

This commit is contained in:
Fancy Zhang
2022-12-30 15:25:45 +08:00
parent aaa628a76b
commit fd01861c62
10 changed files with 324 additions and 6 deletions

View File

@@ -2,6 +2,8 @@
"cmake.configureOnOpen": true,
"cmake.configureArgs": [
"-Dbuild_static=OFF",
"-Dbuild_execsnoop_dl=OFF"
"-Dbuild_execsnoop_dl=ON",
"-Dbuild_tools=ON",
"-Dbuild_test=ON"
]
}

View File

@@ -2,11 +2,11 @@ cmake_minimum_required(VERSION 3.14)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
project(cgproxy VERSION 0.19)
project(cgproxy VERSION 1.00)
include(GNUInstallDirs)
add_compile_options(-Wall -Wextra -Wpedantic -Wno-unused-result -Wno-unused-parameter -Wl,--no-undefined)
add_compile_options(-Wall -Wextra -Wpedantic -Wno-unused-result -Wno-unused-parameter -Wno-overlength-strings)
# for clangd
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
@@ -18,7 +18,7 @@ option(build_tools OFF)
option(build_test "for develop" OFF)
add_subdirectory(src)
add_subdirectory(execsnoop-kernel)
add_subdirectory(execsnoop-libbpf)
add_subdirectory(pack)
if (build_tools)
add_subdirectory(tools)

View File

@@ -0,0 +1,19 @@
find_library(LIBBPF bpf REQUIRED)
# generate execsnoop.skel.h
add_custom_command(OUTPUT execsnoop.skel.h
COMMAND bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h
COMMAND clang -O2 -g -target bpf -c execsnoop.bpf.c -o execsnoop.bpf.o
COMMAND bpftool gen skeleton execsnoop.bpf.o > execsnoop.skel.h
DEPENDS execsnoop.bpf.c
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
if (build_execsnoop_dl)
add_library(execsnoop MODULE execsnoop_share.cpp execsnoop.skel.h)
target_link_libraries(execsnoop PRIVATE ${LIBBPF})
install(TARGETS execsnoop DESTINATION ${CMAKE_INSTALL_FULL_LIBDIR}/cgproxy/)
else()
add_library(execsnoop execsnoop_share.cpp execsnoop.skel.h)
target_link_libraries(execsnoop PRIVATE ${LIBBPF})
endif()

View File

@@ -0,0 +1,92 @@
#include "vmlinux.h"
#include <linux/version.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#define TASK_COMM_LEN 16
struct event {
char comm[TASK_COMM_LEN];
pid_t pid;
pid_t tgid;
pid_t ppid;
uid_t uid;
};
/* /sys/kernel/debug/tracing/events/syscalls/sys_enter_execve/format */
struct syscalls_enter_execve_args {
__u64 unused;
int syscall_nr;
const char filename_ptr;
const char *const * argv;
const char *const * envp;
};
/* /sys/kernel/debug/tracing/events/syscalls/sys_exit_execve/format */
struct syscalls_exit_execve_args {
__u64 unused;
int syscall_nr;
long ret;
};
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 10240);
__type(key, pid_t);
__type(value, struct event);
} records SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
__uint(max_entries, 128);
__type(key, u32);
__type(value, u32);
} perf_events SEC(".maps");
SEC("tracepoint/syscalls/sys_enter_execve")
int syscall_enter_execve(struct syscalls_enter_execve_args *ctx){
pid_t pid, tgid; uid_t uid;
struct event *event;
struct task_struct *task, *task_p;
u64 id = bpf_get_current_pid_tgid();
pid = (pid_t)id;
tgid = id >> 32;
uid = (u32)bpf_get_current_uid_gid();
struct event empty_event={};
if (bpf_map_update_elem(&records, &pid, &empty_event, BPF_NOEXIST)!=0) return 0;
event = bpf_map_lookup_elem(&records, &pid);
if (!event) return 0;
event->pid = pid;
event->tgid = tgid;
event->uid = uid;
/* ppid is not reliable here, normal in arch, but become 0 in ubuntu 20.04 */
task = (struct task_struct*)bpf_get_current_task();
bpf_probe_read(&task_p, sizeof(struct task_struct*),&task->real_parent);
bpf_probe_read(&event->ppid,sizeof(pid_t),&task_p->tgid);
return 0;
}
SEC("tracepoint/syscalls/sys_exit_execve")
int syscall_exit_execve(struct syscalls_exit_execve_args *ctx){
pid_t pid;
struct event *event;
pid = (pid_t)bpf_get_current_pid_tgid();
event = bpf_map_lookup_elem(&records,&pid);
if (!event) return 0;
if (ctx->ret < 0) goto cleanup;
/* get comm */
bpf_get_current_comm(&event->comm,sizeof(event->comm));
bpf_perf_event_output(ctx, &perf_events, BPF_F_CURRENT_CPU, event, sizeof(struct event));
cleanup:
bpf_map_delete_elem(&records, &pid);
return 0;
}
char _license[] SEC("license") = "GPL";
u32 _version SEC("version") = LINUX_VERSION_CODE;

View File

@@ -0,0 +1,71 @@
#include <signal.h>
#include <bpf/libbpf.h>
#include <sys/resource.h>
#include "execsnoop.skel.h"
#define TASK_COMM_LEN 16
struct event {
char comm[TASK_COMM_LEN];
pid_t pid;
pid_t tgid;
pid_t ppid;
uid_t uid;
};
#define PERF_BUFFER_PAGES 64
static void handle_event(void *ctx, int cpu, void *data, __u32 size) {
auto e = static_cast<event*>(data);
printf("comm: %s, pid: %d, tgid: %d, ppid: %d, uid: %d\n",e->comm,e->pid,e->tgid,e->ppid,e->uid);
}
void handle_lost_events(void *ctx, int cpu, __u64 lost_cnt) {
fprintf(stderr, "Lost %llu events on CPU #%d!\n", lost_cnt, cpu);
}
int bump_memlock_rlimit(void) {
struct rlimit rlim_new = { RLIM_INFINITY, RLIM_INFINITY };
return setrlimit(RLIMIT_MEMLOCK, &rlim_new);
}
int main(int argc, char **argv) {
struct perf_buffer_opts pb_opts = {};
struct perf_buffer *pb;
int err;
err = bump_memlock_rlimit();
if (err) {
fprintf(stderr, "failed to increase rlimit: %d\n", err);
return 1;
}
struct execsnoop_bpf *obj=execsnoop_bpf__open_and_load();
if (!obj) {
fprintf(stderr, "failed to open and/or load BPF object\n");
return 1;
}
err = execsnoop_bpf__attach(obj);
if (err) {
fprintf(stderr, "failed to attach BPF programs\n");
return err;
}
#if LIBBPF_MAJOR_VERSION == 0
pb_opts.sample_cb = handle_event;
pb_opts.lost_cb = handle_lost_events;
pb = perf_buffer__new(bpf_map__fd(obj->maps.perf_events), PERF_BUFFER_PAGES, &pb_opts);
#else
pb_opts.sz = sizeof(pb_opts);
pb = perf_buffer__new(bpf_map__fd(obj->maps.perf_events), PERF_BUFFER_PAGES, handle_event, handle_lost_events, nullptr, &pb_opts);
#endif
err = libbpf_get_error(pb);
if (err) {
printf("failed to setup perf_buffer: %d\n", err);
return 1;
}
while ((err = perf_buffer__poll(pb, -1)) >= 0) {}
kill(0, SIGINT);
return err;
}

View File

@@ -0,0 +1,96 @@
#include "execsnoop_share.h"
#include <errno.h>
#include <signal.h>
#include <bpf/libbpf.h>
#include <sys/resource.h>
#include "execsnoop.skel.h"
namespace CGPROXY::EXECSNOOP {
#define PERF_BUFFER_PAGES 64
#define TASK_COMM_LEN 16
struct event {
char comm[TASK_COMM_LEN];
pid_t pid;
pid_t tgid;
pid_t ppid;
uid_t uid;
};
function<int(int)> callback = NULL;
promise<void> status;
static void handle_event(void *ctx, int cpu, void *data, __u32 size) {
auto e = static_cast<event*>(data);
if (callback) callback(e->pid);
}
void handle_lost_events(void *ctx, int cpu, __u64 lost_cnt) {
fprintf(stderr, "Lost %llu events on CPU #%d!\n", lost_cnt, cpu);
}
int bump_memlock_rlimit(void) {
struct rlimit rlim_new = { RLIM_INFINITY, RLIM_INFINITY };
return setrlimit(RLIMIT_MEMLOCK, &rlim_new);
}
int execsnoop() {
struct perf_buffer_opts pb_opts = {};
struct perf_buffer *pb;
int err;
bool notified=false;
err = bump_memlock_rlimit();
if (err) {
fprintf(stderr, "failed to increase rlimit: %d\n", err);
return 1;
}
struct execsnoop_bpf *obj=execsnoop_bpf__open_and_load();
if (!obj) {
fprintf(stderr, "failed to open and/or load BPF object\n");
return 1;
}
err = execsnoop_bpf__attach(obj);
if (err) {
fprintf(stderr, "failed to attach BPF programs\n");
return err;
}
main_loop:
#if LIBBPF_MAJOR_VERSION == 0
pb_opts.sample_cb = handle_event;
pb_opts.lost_cb = handle_lost_events;
pb = perf_buffer__new(bpf_map__fd(obj->maps.perf_events), PERF_BUFFER_PAGES, &pb_opts);
#else
pb_opts.sz = sizeof(pb_opts);
pb = perf_buffer__new(bpf_map__fd(obj->maps.perf_events), PERF_BUFFER_PAGES, handle_event, handle_lost_events, nullptr, &pb_opts);
#endif
err = libbpf_get_error(pb);
if (err) {
printf("failed to setup perf_buffer: %d\n", err);
return 1;
}
// notify
if (!notified) {status.set_value(); notified=true;}
while ((err = perf_buffer__poll(pb, -1)) >= 0) {}
perf_buffer__free(pb);
/* handle Interrupted system call when sleep */
if (err == -EINTR) goto main_loop;
perror("perf_buffer__poll");
kill(0, SIGINT);
return err;
}
void startThread(function<int(int)> c, promise<void> _status) {
status = move(_status);
callback = c;
execsnoop();
}
}

View File

@@ -0,0 +1,20 @@
#ifndef EXECSNOOP_SHARE_HPP
#define EXECSNOOP_SHARE_HPP 1
#include <functional>
#include <future>
#include <string>
using namespace std;
namespace CGPROXY::EXECSNOOP {
extern "C" void startThread(function<int(int)> c, promise<void> _status);
#ifdef BUIlD_EXECSNOOP_DL
// only for dlsym()
using startThread_t=decltype(startThread);
startThread_t *_startThread;
#endif
} // namespace CGPROXY::EXECSNOOP
#endif

View File

@@ -0,0 +1,18 @@
generate `vmlinux.h`
```shell
bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h
```
compiled into BPF ELF file
```shell
clang -O2 -g -target bpf -c execsnoop.bpf.c -o execsnoop.bpf.o
```
generate BPF skeleton .skel.h
```shell
bpftool gen skeleton execsnoop.bpf.o > execsnoop.skel.h
```

View File

@@ -1,10 +1,10 @@
find_package(nlohmann_json REQUIRED)
include_directories(${PROJECT_SOURCE_DIR})
include_directories(${PROJECT_SOURCE_DIR}/execsnoop-kernel/)
include_directories(${PROJECT_SOURCE_DIR}/execsnoop-libbpf/)
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
include_directories(${CMAKE_CURRENT_BINARY_DIR})
configure_file(common.h.cmake common.h)
configure_file(common.cmake.h ${CMAKE_CURRENT_SOURCE_DIR}/common.h)
if (build_execsnoop_dl)
add_definitions(-DBUIlD_EXECSNOOP_DL)