replace bcc with libbpf to optimize resource usage

This commit is contained in:
Fancy Zhang
2020-06-18 11:38:47 +08:00
parent 3f1bee745b
commit 58159dbc15
24 changed files with 108568 additions and 20 deletions

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "execsnoop-libbpf/libbpf"]
path = execsnoop-libbpf/libbpf
url = https://github.com/libbpf/libbpf.git

View File

@@ -8,7 +8,7 @@ add_compile_options(-Wall -Wextra -Wpedantic -Wno-unused-result -Wno-unused-para
# for clangd
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
option(with_execsnoop "enable program level proxy control feature, need bcc installed" ON)
# option(with_execsnoop "enable program level proxy control feature, need bcc installed" ON)
option(build_tools OFF)
option(build_test OFF)

View File

@@ -0,0 +1,6 @@
include_directories(${PROJECT_SOURCE_DIR})
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
add_library(execsnoop MODULE execsnoop.cpp ../src/common.cpp)
target_link_libraries(execsnoop bcc)
install(TARGETS execsnoop DESTINATION /usr/lib/cgproxy/ PERMISSIONS ${basic_permission})

87
execsnoop-libbpf/Makefile Normal file
View File

@@ -0,0 +1,87 @@
# SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
OUTPUT := $(abspath build)
CLANG ?= clang
CFLAGS ?= -g -O2 -Wall
LLVM_STRIP ?= llvm-strip
BPFTOOL ?= bpftool
LIBBPF_DIR := $(abspath libbpf/src)
LIBBPF_OBJ := $(OUTPUT)/usr/lib64/libbpf.a
LIBBPF_H := $(OUTPUT)/usr/include
INCLUDES := -I$(OUTPUT) -I$(LIBBPF_H)
ARCH := $(shell uname -m | sed 's/x86_64/x86/')
APPS = execsnoop
LIBSO = libexecsnoop.so
.PHONY: all
all: $(APPS) $(LIBSO)
ifeq ($(V),1)
Q =
msg =
else
Q = @
msg = @printf ' %-8s %s%s\n' "$(1)" "$(notdir $(2))" "$(if $(3), $(3))";
MAKEFLAGS += --no-print-directory
endif
COMMON_OBJ = \
$(OUTPUT)/trace_helpers.o \
$(OUTPUT)/syscall_helpers.o \
$(OUTPUT)/errno_helpers.o \
.PHONY: install
install: $(LIBSO)
install -D $(LIBSO) -t $(DESTDIR)/usr/lib/cgproxy/
.PHONY: clean
clean:
$(call msg,CLEAN)
$(Q)rm -rf $(OUTPUT) $(APPS) $(LIBSO)
$(OUTPUT) $(OUTPUT)/libbpf:
$(call msg,MKDIR,$@)
$(Q)mkdir -p $@
$(APPS): %: $(OUTPUT)/%.o $(LIBBPF_OBJ) $(COMMON_OBJ) | $(OUTPUT)
$(call msg,BINARY,$@)
$(Q)$(CC) $(CFLAGS) $^ -lelf -lz -o $@
$(patsubst %,$(OUTPUT)/%.o,$(APPS)): %.o: %.skel.h
$(OUTPUT)/%.o: %.c $(wildcard %.h) | $(OUTPUT)
$(call msg,CC,$@)
$(Q)$(CC) $(CFLAGS) $(INCLUDES) -fPIC -c $(filter %.c,$^) -o $@
$(OUTPUT)/%.skel.h: $(OUTPUT)/%.bpf.o | $(OUTPUT)
$(call msg,GEN-SKEL,$@)
$(Q)$(BPFTOOL) gen skeleton $< > $@
$(OUTPUT)/%.bpf.o: %.bpf.c $(LIBBPF_OBJ) $(wildcard %.h) vmlinux.h | $(OUTPUT)
$(call msg,BPF,$@)
$(Q)$(CLANG) -g -O2 -target bpf -D__TARGET_ARCH_$(ARCH) \
$(INCLUDES) -c $(filter %.c,$^) -o $@ && \
$(LLVM_STRIP) -g $@
vmlinux.h:
$(Q)$(BPFTOOL) btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h
$(LIBBPF_OBJ): patch
$(Q)cd $(LIBBPF_DIR) && make DESTDIR=${OUTPUT} prefix=$(OUTPUT) install install_headers
patch: | $(LIBBPF_DIR)/Makefile
@echo "patching libbpf-fPIC.patch"
$(Q)cd libbpf/src && patch -p1 --forward -i ../../libbpf-fPIC.patch || true
# build libexecsnoop.so
$(LIBSO): execsnoop_share.cpp $(OUTPUT)/execsnoop.skel.h $(LIBBPF_OBJ) $(COMMON_OBJ) | $(OUTPUT)
$(call msg,LIB,$@)
$(Q)$(CXX) -std=c++17 $(CFLAGS) $(INCLUDES) -fPIC $< $(COMMON_OBJ) -shared -lelf -lz -Wl,--no-whole-archive,--no-undefined $(LIBBPF_OBJ) -o $@
# delete failed targets
.DELETE_ON_ERROR:
# keep intermediate (.skel.h, .bpf.o, etc) targets
.SECONDARY:

View File

@@ -0,0 +1,232 @@
// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
// Copyright (c) 2020 Anton Protopopov
#include <stdlib.h>
#include <limits.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
#define warn(...) fprintf(stderr, __VA_ARGS__)
#ifdef __x86_64__
static int errno_by_name_x86_64(const char *errno_name)
{
#define strcase(X, N) if (!strcmp(errno_name, (X))) return N
strcase("EPERM", 1);
strcase("ENOENT", 2);
strcase("ESRCH", 3);
strcase("EINTR", 4);
strcase("EIO", 5);
strcase("ENXIO", 6);
strcase("E2BIG", 7);
strcase("ENOEXEC", 8);
strcase("EBADF", 9);
strcase("ECHILD", 10);
strcase("EAGAIN", 11);
strcase("EWOULDBLOCK", 11);
strcase("ENOMEM", 12);
strcase("EACCES", 13);
strcase("EFAULT", 14);
strcase("ENOTBLK", 15);
strcase("EBUSY", 16);
strcase("EEXIST", 17);
strcase("EXDEV", 18);
strcase("ENODEV", 19);
strcase("ENOTDIR", 20);
strcase("EISDIR", 21);
strcase("EINVAL", 22);
strcase("ENFILE", 23);
strcase("EMFILE", 24);
strcase("ENOTTY", 25);
strcase("ETXTBSY", 26);
strcase("EFBIG", 27);
strcase("ENOSPC", 28);
strcase("ESPIPE", 29);
strcase("EROFS", 30);
strcase("EMLINK", 31);
strcase("EPIPE", 32);
strcase("EDOM", 33);
strcase("ERANGE", 34);
strcase("EDEADLK", 35);
strcase("EDEADLOCK", 35);
strcase("ENAMETOOLONG", 36);
strcase("ENOLCK", 37);
strcase("ENOSYS", 38);
strcase("ENOTEMPTY", 39);
strcase("ELOOP", 40);
strcase("ENOMSG", 42);
strcase("EIDRM", 43);
strcase("ECHRNG", 44);
strcase("EL2NSYNC", 45);
strcase("EL3HLT", 46);
strcase("EL3RST", 47);
strcase("ELNRNG", 48);
strcase("EUNATCH", 49);
strcase("ENOCSI", 50);
strcase("EL2HLT", 51);
strcase("EBADE", 52);
strcase("EBADR", 53);
strcase("EXFULL", 54);
strcase("ENOANO", 55);
strcase("EBADRQC", 56);
strcase("EBADSLT", 57);
strcase("EBFONT", 59);
strcase("ENOSTR", 60);
strcase("ENODATA", 61);
strcase("ETIME", 62);
strcase("ENOSR", 63);
strcase("ENONET", 64);
strcase("ENOPKG", 65);
strcase("EREMOTE", 66);
strcase("ENOLINK", 67);
strcase("EADV", 68);
strcase("ESRMNT", 69);
strcase("ECOMM", 70);
strcase("EPROTO", 71);
strcase("EMULTIHOP", 72);
strcase("EDOTDOT", 73);
strcase("EBADMSG", 74);
strcase("EOVERFLOW", 75);
strcase("ENOTUNIQ", 76);
strcase("EBADFD", 77);
strcase("EREMCHG", 78);
strcase("ELIBACC", 79);
strcase("ELIBBAD", 80);
strcase("ELIBSCN", 81);
strcase("ELIBMAX", 82);
strcase("ELIBEXEC", 83);
strcase("EILSEQ", 84);
strcase("ERESTART", 85);
strcase("ESTRPIPE", 86);
strcase("EUSERS", 87);
strcase("ENOTSOCK", 88);
strcase("EDESTADDRREQ", 89);
strcase("EMSGSIZE", 90);
strcase("EPROTOTYPE", 91);
strcase("ENOPROTOOPT", 92);
strcase("EPROTONOSUPPORT", 93);
strcase("ESOCKTNOSUPPORT", 94);
strcase("ENOTSUP", 95);
strcase("EOPNOTSUPP", 95);
strcase("EPFNOSUPPORT", 96);
strcase("EAFNOSUPPORT", 97);
strcase("EADDRINUSE", 98);
strcase("EADDRNOTAVAIL", 99);
strcase("ENETDOWN", 100);
strcase("ENETUNREACH", 101);
strcase("ENETRESET", 102);
strcase("ECONNABORTED", 103);
strcase("ECONNRESET", 104);
strcase("ENOBUFS", 105);
strcase("EISCONN", 106);
strcase("ENOTCONN", 107);
strcase("ESHUTDOWN", 108);
strcase("ETOOMANYREFS", 109);
strcase("ETIMEDOUT", 110);
strcase("ECONNREFUSED", 111);
strcase("EHOSTDOWN", 112);
strcase("EHOSTUNREACH", 113);
strcase("EALREADY", 114);
strcase("EINPROGRESS", 115);
strcase("ESTALE", 116);
strcase("EUCLEAN", 117);
strcase("ENOTNAM", 118);
strcase("ENAVAIL", 119);
strcase("EISNAM", 120);
strcase("EREMOTEIO", 121);
strcase("EDQUOT", 122);
strcase("ENOMEDIUM", 123);
strcase("EMEDIUMTYPE", 124);
strcase("ECANCELED", 125);
strcase("ENOKEY", 126);
strcase("EKEYEXPIRED", 127);
strcase("EKEYREVOKED", 128);
strcase("EKEYREJECTED", 129);
strcase("EOWNERDEAD", 130);
strcase("ENOTRECOVERABLE", 131);
strcase("ERFKILL", 132);
strcase("EHWPOISON", 133);
#undef strcase
return -1;
}
#endif
/* Try to find the errno number using the errno(1) program */
static int errno_by_name_dynamic(const char *errno_name)
{
int len = strlen(errno_name);
int err, number = -1;
char buf[128];
char cmd[64];
char *end;
long val;
FILE *f;
/* sanity check to not call popen with random input */
for (int i = 0; i < len; i++) {
if (errno_name[i] < 'A' || errno_name[i] > 'Z') {
warn("errno_name contains invalid char 0x%02x: %s\n",
errno_name[i], errno_name);
return -1;
}
}
snprintf(cmd, sizeof(cmd), "errno %s", errno_name);
f = popen(cmd, "r");
if (!f) {
warn("popen: %s: %s\n", cmd, strerror(errno));
return -1;
}
if (!fgets(buf, sizeof(buf), f)) {
goto close;
} else if (ferror(f)) {
warn("fgets: %s\n", strerror(errno));
goto close;
}
// expecting "<name> <number> <description>"
if (strncmp(errno_name, buf, len) || strlen(buf) < len+2) {
warn("expected '%s': %s\n", errno_name, buf);
goto close;
}
errno = 0;
val = strtol(buf+len+2, &end, 10);
if (errno || end == (buf+len+2) || number < 0 || number > INT_MAX) {
warn("can't parse the second column, expected int: %s\n", buf);
goto close;
}
number = val;
close:
err = pclose(f);
if (err < 0)
warn("pclose: %s\n", strerror(errno));
#ifndef __x86_64__
/* Ignore the error for x86_64 where we have a table compiled in */
else if (err && WEXITSTATUS(err) == 127) {
warn("errno(1) required for errno name/number mapping\n");
} else if (err) {
warn("errno(1) exit status (see wait(2)): 0x%x\n", err);
}
#endif
return number;
}
int errno_by_name(const char *errno_name)
{
#ifdef __x86_64__
int err;
err = errno_by_name_x86_64(errno_name);
if (err >= 0)
return err;
#endif
return errno_by_name_dynamic(errno_name);
}

View File

@@ -0,0 +1,7 @@
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
#ifndef __ERRNO_HELPERS_H
#define __ERRNO_HELPERS_H
int errno_by_name(const char *errno_name);
#endif /* __ERRNO_HELPERS_H */

View File

@@ -0,0 +1,128 @@
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_core_read.h>
#include "execsnoop.h"
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 = {};
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");
static __always_inline bool valid_uid(uid_t uid) {
return uid != INVALID_UID;
}
SEC("tracepoint/syscalls/sys_enter_execve")
int tracepoint__syscalls__sys_enter_execve(struct trace_event_raw_sys_enter* ctx)
{
u64 id;
pid_t pid, tgid;
unsigned int ret;
struct event *event;
struct task_struct *task;
const char **args = (const char **)(ctx->args[1]);
const char *argp;
uid_t uid = (u32)bpf_get_current_uid_gid();
if (valid_uid(targ_uid) && targ_uid != uid)
return 0;
id = bpf_get_current_pid_tgid();
pid = (pid_t)id;
tgid = id >> 32;
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->pid = pid;
event->tgid = tgid;
event->uid = uid;
task = (struct task_struct*)bpf_get_current_task();
event->ppid = (pid_t)BPF_CORE_READ(task, real_parent, tgid);
event->args_count = 0;
event->args_size = 0;
ret = bpf_probe_read_str(event->args, ARGSIZE, (const char*)ctx->args[0]);
if (ret <= ARGSIZE) {
event->args_size += ret;
} else {
/* write an empty string */
event->args[0] = '\0';
event->args_size++;
}
event->args_count++;
#pragma unroll
for (int i = 1; i < TOTAL_MAX_ARGS && i < max_args; i++) {
bpf_probe_read(&argp, sizeof(argp), &args[i]);
if (!argp)
return 0;
if (event->args_size > LAST_ARG)
return 0;
ret = bpf_probe_read_str(&event->args[event->args_size], ARGSIZE, argp);
if (ret > ARGSIZE)
return 0;
event->args_count++;
event->args_size += ret;
}
/* try to read one more argument to check if there is one */
bpf_probe_read(&argp, sizeof(argp), &args[max_args]);
if (!argp)
return 0;
/* pointer to max_args+1 isn't null, asume we have more arguments */
event->args_count++;
return 0;
}
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;
u32 uid = (u32)bpf_get_current_uid_gid();
if (valid_uid(targ_uid) && targ_uid != uid)
return 0;
id = bpf_get_current_pid_tgid();
pid = (pid_t)id;
event = bpf_map_lookup_elem(&execs, &pid);
if (!event)
return 0;
ret = ctx->ret;
if (ignore_failed && ret < 0)
goto cleanup;
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);
cleanup:
bpf_map_delete_elem(&execs, &pid);
return 0;
}
char LICENSE[] SEC("license") = "GPL";

View File

@@ -0,0 +1,329 @@
// Based on execsnoop(8) from BCC by Brendan Gregg and others.
//
#include <argp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <time.h>
#include <unistd.h>
#include <bpf/libbpf.h>
#include <bpf/bpf.h>
#include "execsnoop.h"
#include "execsnoop.skel.h"
#include "trace_helpers.h"
#define PERF_BUFFER_PAGES 64
#define NSEC_PRECISION (NSEC_PER_SEC / 1000)
#define MAX_ARGS_KEY 259
static struct env {
bool time;
bool timestamp;
bool fails;
uid_t uid;
bool quote;
const char *name;
const char *line;
bool print_uid;
bool verbose;
int max_args;
} env = {
.max_args = DEFAULT_MAXARGS,
.uid = INVALID_UID
};
static struct timespec start_time;
const char *argp_program_version = "execsnoop 0.1";
const char *argp_program_bug_address = "<bpf@vger.kernel.org>";
const char argp_program_doc[] =
"Trace open family syscalls\n"
"\n"
"USAGE: execsnoop [-h] [-T] [-t] [-x] [-u UID] [-q] [-n NAME] [-l LINE] [-U]\n"
" [--max-args MAX_ARGS]\n"
"\n"
"EXAMPLES:\n"
" ./execsnoop # trace all exec() syscalls\n"
" ./execsnoop -x # include failed exec()s\n"
" ./execsnoop -T # include time (HH:MM:SS)\n"
" ./execsnoop -U # include UID\n"
" ./execsnoop -u 1000 # only trace UID 1000\n"
" ./execsnoop -t # include timestamps\n"
" ./execsnoop -q # add \"quotemarks\" around arguments\n"
" ./execsnoop -n main # only print command lines containing \"main\"\n"
" ./execsnoop -l tpkg # only print command where arguments contains \"tpkg\"";
static const struct argp_option opts[] = {
{ "time", 'T', NULL, 0, "include time column on output (HH:MM:SS)"},
{ "timestamp", 't', NULL, 0, "include timestamp on output"},
{ "fails", 'x', NULL, 0, "include failed exec()s"},
{ "uid", 'u', "UID", 0, "trace this UID only"},
{ "quote", 'q', NULL, 0, "Add quotemarks (\") around arguments"},
{ "name", 'n', "NAME", 0, "only print commands matching this name, any arg"},
{ "line", 'l', "LINE", 0, "only print commands where arg contains this line"},
{ "print-uid", 'U', NULL, 0, "print UID column"},
{ "max-args", MAX_ARGS_KEY, "MAX_ARGS", 0,
"maximum number of arguments parsed and displayed, defaults to 20"},
{ "verbose", 'v', NULL, 0, "Verbose debug output" },
{},
};
static error_t parse_arg(int key, char *arg, struct argp_state *state)
{
long int uid, max_args;
switch (key) {
case 'h':
argp_usage(state);
break;
case 'T':
env.time = true;
break;
case 't':
env.timestamp = true;
break;
case 'x':
env.fails = true;
break;
case 'u':
errno = 0;
uid = strtol(arg, NULL, 10);
if (errno || uid < 0 || uid >= INVALID_UID) {
fprintf(stderr, "Invalid UID %s\n", arg);
argp_usage(state);
}
env.uid = uid;
break;
case 'q':
env.quote = true;
break;
case 'n':
env.name = arg;
break;
case 'l':
env.line = arg;
break;
case 'U':
env.print_uid = true;
break;
case 'v':
env.verbose = true;
break;
case MAX_ARGS_KEY:
errno = 0;
max_args = strtol(arg, NULL, 10);
if (errno || max_args < 1 || max_args > TOTAL_MAX_ARGS) {
fprintf(stderr, "Invalid MAX_ARGS %s, should be in [1, %d] range\n",
arg, TOTAL_MAX_ARGS);
argp_usage(state);
}
env.max_args = max_args;
break;
default:
return ARGP_ERR_UNKNOWN;
}
return 0;
}
int libbpf_print_fn(enum libbpf_print_level level,
const char *format, va_list args)
{
if (level == LIBBPF_DEBUG && !env.verbose)
return 0;
return vfprintf(stderr, format, args);
}
static void time_since_start()
{
long nsec, sec;
static struct timespec cur_time;
double time_diff;
clock_gettime(CLOCK_MONOTONIC, &cur_time);
nsec = cur_time.tv_nsec - start_time.tv_nsec;
sec = cur_time.tv_sec - start_time.tv_sec;
if (nsec < 0) {
nsec += NSEC_PER_SEC;
sec--;
}
time_diff = sec + (double)nsec / NSEC_PER_SEC;
printf("%-8.3f", time_diff);
}
static void inline quoted_symbol(char c) {
switch(c) {
case '"':
putchar('\\');
putchar('"');
break;
case '\t':
putchar('\\');
putchar('t');
break;
case '\n':
putchar('\\');
putchar('n');
break;
default:
putchar(c);
break;
}
}
static void print_args(const struct event *e, bool quote)
{
int args_counter = 0;
if (env.quote)
putchar('"');
for (int i = 0; i < e->args_size && args_counter < e->args_count; i++) {
char c = e->args[i];
if (env.quote) {
if (c == '\0') {
args_counter++;
putchar('"');
putchar(' ');
if (args_counter < e->args_count) {
putchar('"');
}
} else {
quoted_symbol(c);
}
} else {
if (c == '\0') {
args_counter++;
putchar(' ');
} else {
putchar(c);
}
}
}
if (e->args_count == env.max_args + 1) {
fputs(" ...", stdout);
}
}
void handle_event(void *ctx, int cpu, void *data, __u32 data_sz)
{
const struct event *e = data;
time_t t;
struct tm *tm;
char ts[32];
/* TODO: use pcre lib */
if (env.name && strstr(e->comm, env.name) == NULL)
return;
/* TODO: use pcre lib */
if (env.line && strstr(e->comm, env.line) == NULL)
return;
time(&t);
tm = localtime(&t);
strftime(ts, sizeof(ts), "%H:%M:%S", tm);
if (env.time) {
printf("%-8s ", ts);
}
if (env.timestamp) {
time_since_start();
}
if (env.print_uid)
printf("%-6d", e->uid);
printf("%-16s %-6d %-6d %3d ", e->comm, e->pid, e->ppid, e->retval);
print_args(e, env.quote);
putchar('\n');
}
void handle_lost_events(void *ctx, int cpu, __u64 lost_cnt)
{
fprintf(stderr, "Lost %llu events on CPU #%d!\n", lost_cnt, cpu);
}
int main(int argc, char **argv)
{
static const struct argp argp = {
.options = opts,
.parser = parse_arg,
.doc = argp_program_doc,
};
struct perf_buffer_opts pb_opts;
struct perf_buffer *pb = NULL;
struct execsnoop_bpf *obj;
int err;
err = argp_parse(&argp, argc, argv, 0, NULL, NULL);
if (err)
return err;
libbpf_set_print(libbpf_print_fn);
err = bump_memlock_rlimit();
if (err) {
fprintf(stderr, "failed to increase rlimit: %d\n", err);
return 1;
}
obj = execsnoop_bpf__open();
if (!obj) {
fprintf(stderr, "failed to open and/or load BPF object\n");
return 1;
}
/* initialize global data (filtering options) */
obj->rodata->ignore_failed = !env.fails;
obj->rodata->targ_uid = env.uid;
obj->rodata->max_args = env.max_args;
err = execsnoop_bpf__load(obj);
if (err) {
fprintf(stderr, "failed to load BPF object: %d\n", err);
goto cleanup;
}
clock_gettime(CLOCK_MONOTONIC, &start_time);
err = execsnoop_bpf__attach(obj);
if (err) {
fprintf(stderr, "failed to attach BPF programs\n");
goto cleanup;
}
/* print headers */
if (env.time) {
printf("%-9s", "TIME");
}
if (env.timestamp) {
printf("%-8s ", "TIME(s)");
}
if (env.print_uid) {
printf("%-6s ", "UID");
}
printf("%-16s %-6s %-6s %3s %s\n", "PCOMM", "PID", "PPID", "RET", "ARGS");
/* setup event callbacks */
pb_opts.sample_cb = handle_event;
pb_opts.lost_cb = handle_lost_events;
pb = perf_buffer__new(bpf_map__fd(obj->maps.events), PERF_BUFFER_PAGES, &pb_opts);
err = libbpf_get_error(pb);
if (err) {
pb = NULL;
fprintf(stderr, "failed to open perf buffer: %d\n", err);
goto cleanup;
}
/* main: poll */
while ((err = perf_buffer__poll(pb, 100)) >= 0)
;
printf("Error polling perf buffer: %d\n", err);
cleanup:
perf_buffer__free(pb);
execsnoop_bpf__destroy(obj);
return err != 0;
}

View File

@@ -0,0 +1,27 @@
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
#ifndef __EXECSNOOP_H
#define __EXECSNOOP_H
#define ARGSIZE 128
#define TASK_COMM_LEN 16
#define TOTAL_MAX_ARGS 60
#define DEFAULT_MAXARGS 20
#define FULL_MAX_ARGS_ARR (TOTAL_MAX_ARGS * ARGSIZE)
#define INVALID_UID ((uid_t)-1)
#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;
pid_t tgid;
pid_t ppid;
uid_t uid;
int retval;
int args_count;
unsigned int args_size;
char args[FULL_MAX_ARGS_ARR];
};
#endif /* __EXECSNOOP_H */

View File

@@ -0,0 +1,101 @@
// Based on execsnoop(8) from BCC by Brendan Gregg and others.
//
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
extern "C" {
#define typeof(x) decltype(x)
#include "execsnoop.h"
#include "execsnoop.skel.h"
#include "trace_helpers.h"
}
#include "execsnoop_share.h"
#define PERF_BUFFER_PAGES 64
#define NSEC_PRECISION (NSEC_PER_SEC / 1000)
#define MAX_ARGS_KEY 259
namespace CGPROXY::EXECSNOOP {
function<int(int)> callback = NULL;
promise<void> status;
void handle_event(void *ctx, int cpu, void *data, __u32 data_sz) {
auto e = static_cast<event*>(data);
int pid = e->pid;
if (callback) callback(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 execsnoop() {
struct perf_buffer_opts pb_opts;
struct perf_buffer *pb = NULL;
struct execsnoop_bpf *obj;
int err;
// libbpf_set_print(libbpf_print_fn);
err = bump_memlock_rlimit();
if (err) {
fprintf(stderr, "failed to increase rlimit: %d\n", err);
return 1;
}
obj = execsnoop_bpf__open();
if (!obj) {
fprintf(stderr, "failed to open and/or load BPF object\n");
return 1;
}
/* initialize global data (filtering options) */
obj->rodata->ignore_failed = true;
err = execsnoop_bpf__load(obj);
if (err) {
fprintf(stderr, "failed to load BPF object: %d\n", err);
goto cleanup;
}
err = execsnoop_bpf__attach(obj);
if (err) {
fprintf(stderr, "failed to attach BPF programs\n");
goto cleanup;
}
/* setup event callbacks */
pb_opts.sample_cb = handle_event;
pb_opts.lost_cb = handle_lost_events;
pb = perf_buffer__new(bpf_map__fd(obj->maps.events), PERF_BUFFER_PAGES, &pb_opts);
err = libbpf_get_error(pb);
if (err) {
pb = NULL;
fprintf(stderr, "failed to open perf buffer: %d\n", err);
goto cleanup;
}
status.set_value();
/* main: poll */
while ((err = perf_buffer__poll(pb, 100)) >= 0);
printf("Error polling perf buffer: %d\n", err);
cleanup:
perf_buffer__free(pb);
execsnoop_bpf__destroy(obj);
return err != 0;
}
void startThread(function<int(int)> c, promise<void> _status) {
status = move(_status);
callback = c;
execsnoop();
}
}

View File

@@ -0,0 +1,16 @@
#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);
// typedef void startThread_t(function<int(int)>, promise<void>);
using startThread_t=decltype(startThread);
startThread_t *_startThread; // only for dlsym()
} // namespace CGPROXY::EXECSNOOP
#endif

View File

@@ -0,0 +1,21 @@
diff --git src/Makefile src/Makefile
index d0308c3..fcc3b6f 100644
--- src/Makefile
+++ src/Makefile
@@ -15,6 +15,7 @@ ifneq ($(FEATURE_REALLOCARRAY),)
ALL_CFLAGS += -DCOMPAT_NEED_REALLOCARRAY
endif
+STATIC_CFLAGS += -fPIC
SHARED_CFLAGS += -fPIC -fvisibility=hidden -DSHARED
CFLAGS ?= -g -O2 -Werror -Wall
@@ -99,7 +100,7 @@ $(SHARED_OBJDIR):
mkdir -p $(SHARED_OBJDIR)
$(STATIC_OBJDIR)/%.o: %.c | $(STATIC_OBJDIR)
- $(CC) $(ALL_CFLAGS) $(CPPFLAGS) -c $< -o $@
+ $(CC) $(ALL_CFLAGS) $(STATIC_CFLAGS) $(CPPFLAGS) -c $< -o $@
$(SHARED_OBJDIR)/%.o: %.c | $(SHARED_OBJDIR)
$(CC) $(ALL_CFLAGS) $(SHARED_CFLAGS) $(CPPFLAGS) -c $< -o $@

View File

@@ -0,0 +1,11 @@
## Depency
- libbpf
- /usr/lib/libbpf.a
- some head file
- bpf
- `/usr/bin/bpftool` to generate skeleton and dump btf
## Refer
- https://facebookmicrosites.github.io/bpf/blog/2020/02/19/bpf-portability-and-co-re.html

View File

@@ -0,0 +1,526 @@
// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
// Copyright (c) 2020 Anton Protopopov
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <stdio.h>
#include <errno.h>
static const char **syscall_names;
static size_t syscall_names_size;
#define warn(...) fprintf(stderr, __VA_ARGS__)
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
static const char *parse_syscall(const char *buf, int *number)
{
char *end;
long x;
errno = 0;
x = strtol(buf, &end, 10);
if (errno) {
warn("strtol(%s): %s\n", buf, strerror(errno));
return NULL;
} else if (end == buf) {
warn("strtol(%s): no digits found\n", buf);
return NULL;
} else if (x < 0 || x > INT_MAX) {
warn("strtol(%s): bad syscall number: %ld\n", buf, x);
return NULL;
}
if (*end != '\t') {
warn("bad input: %s (expected <num>\t<name>)\n", buf);
return NULL;
}
*number = x;
return ++end;
}
void init_syscall_names(void)
{
size_t old_size, size = 1024;
const char *name;
char buf[64];
int number;
int err;
FILE *f;
f = popen("ausyscall --dump 2>/dev/null", "r");
if (!f) {
warn("popen: ausyscall --dump: %s\n", strerror(errno));
return;
}
syscall_names = calloc(size, sizeof(char *));
if (!syscall_names) {
warn("calloc: %s\n", strerror(errno));
goto close;
}
/* skip the header */
fgets(buf, sizeof(buf), f);
while (fgets(buf, sizeof(buf), f)) {
if (buf[strlen(buf) - 1] == '\n')
buf[strlen(buf) - 1] = '\0';
name = parse_syscall(buf, &number);
if (!name || !name[0])
goto close;
/* In a rare case when syscall number is > than initial 1024 */
if (number >= size) {
old_size = size;
size = 1024 * (1 + number / 1024);
syscall_names = realloc(syscall_names,
size * sizeof(char *));
if (!syscall_names) {
warn("realloc: %s\n", strerror(errno));
goto close;
}
memset(syscall_names+old_size, 0,
(size - old_size) * sizeof(char *));
}
if (syscall_names[number]) {
warn("duplicate number: %d (stored: %s)",
number, syscall_names[number]);
goto close;
}
syscall_names[number] = strdup(name);
if (!syscall_names[number]) {
warn("strdup: %s\n", strerror(errno));
goto close;
}
syscall_names_size = MAX(number+1, syscall_names_size);
}
if (ferror(f))
warn("fgets: %s\n", strerror(errno));
close:
err = pclose(f);
if (err < 0)
warn("pclose: %s\n", strerror(errno));
#ifndef __x86_64__
/* Ignore the error for x86_64 where we have a table compiled in */
else if (err && WEXITSTATUS(err) == 127) {
warn("ausyscall required for syscalls number/name mapping\n");
} else if (err) {
warn("ausyscall exit status (see wait(2)): 0x%x\n", err);
}
#endif
}
void free_syscall_names(void)
{
for (size_t i = 0; i < syscall_names_size; i++)
free((void *) syscall_names[i]);
free(syscall_names);
}
/*
* Syscall table for Linux x86_64.
*
* Semi-automatically generated from strace/linux/x86_64/syscallent.h and
* linux/syscallent-common.h using the following commands:
*
* awk -F\" '/SEN/{printf("%d %s\n", substr($0,2,3), $(NF-1));}' syscallent.h
* awk '/SEN/ { printf("%d %s\n", $3, $9); }' syscallent-common.h
*
* (The idea is taken from src/python/bcc/syscall.py.)
*/
#ifdef __x86_64__
static const char *syscall_names_x86_64[] = {
[0] = "read",
[1] = "write",
[2] = "open",
[3] = "close",
[4] = "stat",
[5] = "fstat",
[6] = "lstat",
[7] = "poll",
[8] = "lseek",
[9] = "mmap",
[10] = "mprotect",
[11] = "munmap",
[12] = "brk",
[13] = "rt_sigaction",
[14] = "rt_sigprocmask",
[15] = "rt_sigreturn",
[16] = "ioctl",
[17] = "pread64",
[18] = "pwrite64",
[19] = "readv",
[20] = "writev",
[21] = "access",
[22] = "pipe",
[23] = "select",
[24] = "sched_yield",
[25] = "mremap",
[26] = "msync",
[27] = "mincore",
[28] = "madvise",
[29] = "shmget",
[30] = "shmat",
[31] = "shmctl",
[32] = "dup",
[33] = "dup2",
[34] = "pause",
[35] = "nanosleep",
[36] = "getitimer",
[37] = "alarm",
[38] = "setitimer",
[39] = "getpid",
[40] = "sendfile",
[41] = "socket",
[42] = "connect",
[43] = "accept",
[44] = "sendto",
[45] = "recvfrom",
[46] = "sendmsg",
[47] = "recvmsg",
[48] = "shutdown",
[49] = "bind",
[50] = "listen",
[51] = "getsockname",
[52] = "getpeername",
[53] = "socketpair",
[54] = "setsockopt",
[55] = "getsockopt",
[56] = "clone",
[57] = "fork",
[58] = "vfork",
[59] = "execve",
[60] = "exit",
[61] = "wait4",
[62] = "kill",
[63] = "uname",
[64] = "semget",
[65] = "semop",
[66] = "semctl",
[67] = "shmdt",
[68] = "msgget",
[69] = "msgsnd",
[70] = "msgrcv",
[71] = "msgctl",
[72] = "fcntl",
[73] = "flock",
[74] = "fsync",
[75] = "fdatasync",
[76] = "truncate",
[77] = "ftruncate",
[78] = "getdents",
[79] = "getcwd",
[80] = "chdir",
[81] = "fchdir",
[82] = "rename",
[83] = "mkdir",
[84] = "rmdir",
[85] = "creat",
[86] = "link",
[87] = "unlink",
[88] = "symlink",
[89] = "readlink",
[90] = "chmod",
[91] = "fchmod",
[92] = "chown",
[93] = "fchown",
[94] = "lchown",
[95] = "umask",
[96] = "gettimeofday",
[97] = "getrlimit",
[98] = "getrusage",
[99] = "sysinfo",
[100] = "times",
[101] = "ptrace",
[102] = "getuid",
[103] = "syslog",
[104] = "getgid",
[105] = "setuid",
[106] = "setgid",
[107] = "geteuid",
[108] = "getegid",
[109] = "setpgid",
[110] = "getppid",
[111] = "getpgrp",
[112] = "setsid",
[113] = "setreuid",
[114] = "setregid",
[115] = "getgroups",
[116] = "setgroups",
[117] = "setresuid",
[118] = "getresuid",
[119] = "setresgid",
[120] = "getresgid",
[121] = "getpgid",
[122] = "setfsuid",
[123] = "setfsgid",
[124] = "getsid",
[125] = "capget",
[126] = "capset",
[127] = "rt_sigpending",
[128] = "rt_sigtimedwait",
[129] = "rt_sigqueueinfo",
[130] = "rt_sigsuspend",
[131] = "sigaltstack",
[132] = "utime",
[133] = "mknod",
[134] = "uselib",
[135] = "personality",
[136] = "ustat",
[137] = "statfs",
[138] = "fstatfs",
[139] = "sysfs",
[140] = "getpriority",
[141] = "setpriority",
[142] = "sched_setparam",
[143] = "sched_getparam",
[144] = "sched_setscheduler",
[145] = "sched_getscheduler",
[146] = "sched_get_priority_max",
[147] = "sched_get_priority_min",
[148] = "sched_rr_get_interval",
[149] = "mlock",
[150] = "munlock",
[151] = "mlockall",
[152] = "munlockall",
[153] = "vhangup",
[154] = "modify_ldt",
[155] = "pivot_root",
[156] = "_sysctl",
[157] = "prctl",
[158] = "arch_prctl",
[159] = "adjtimex",
[160] = "setrlimit",
[161] = "chroot",
[162] = "sync",
[163] = "acct",
[164] = "settimeofday",
[165] = "mount",
[166] = "umount2",
[167] = "swapon",
[168] = "swapoff",
[169] = "reboot",
[170] = "sethostname",
[171] = "setdomainname",
[172] = "iopl",
[173] = "ioperm",
[174] = "create_module",
[175] = "init_module",
[176] = "delete_module",
[177] = "get_kernel_syms",
[178] = "query_module",
[179] = "quotactl",
[180] = "nfsservctl",
[181] = "getpmsg",
[182] = "putpmsg",
[183] = "afs_syscall",
[184] = "tuxcall",
[185] = "security",
[186] = "gettid",
[187] = "readahead",
[188] = "setxattr",
[189] = "lsetxattr",
[190] = "fsetxattr",
[191] = "getxattr",
[192] = "lgetxattr",
[193] = "fgetxattr",
[194] = "listxattr",
[195] = "llistxattr",
[196] = "flistxattr",
[197] = "removexattr",
[198] = "lremovexattr",
[199] = "fremovexattr",
[200] = "tkill",
[201] = "time",
[202] = "futex",
[203] = "sched_setaffinity",
[204] = "sched_getaffinity",
[205] = "set_thread_area",
[206] = "io_setup",
[207] = "io_destroy",
[208] = "io_getevents",
[209] = "io_submit",
[210] = "io_cancel",
[211] = "get_thread_area",
[212] = "lookup_dcookie",
[213] = "epoll_create",
[214] = "epoll_ctl_old",
[215] = "epoll_wait_old",
[216] = "remap_file_pages",
[217] = "getdents64",
[218] = "set_tid_address",
[219] = "restart_syscall",
[220] = "semtimedop",
[221] = "fadvise64",
[222] = "timer_create",
[223] = "timer_settime",
[224] = "timer_gettime",
[225] = "timer_getoverrun",
[226] = "timer_delete",
[227] = "clock_settime",
[228] = "clock_gettime",
[229] = "clock_getres",
[230] = "clock_nanosleep",
[231] = "exit_group",
[232] = "epoll_wait",
[233] = "epoll_ctl",
[234] = "tgkill",
[235] = "utimes",
[236] = "vserver",
[237] = "mbind",
[238] = "set_mempolicy",
[239] = "get_mempolicy",
[240] = "mq_open",
[241] = "mq_unlink",
[242] = "mq_timedsend",
[243] = "mq_timedreceive",
[244] = "mq_notify",
[245] = "mq_getsetattr",
[246] = "kexec_load",
[247] = "waitid",
[248] = "add_key",
[249] = "request_key",
[250] = "keyctl",
[251] = "ioprio_set",
[252] = "ioprio_get",
[253] = "inotify_init",
[254] = "inotify_add_watch",
[255] = "inotify_rm_watch",
[256] = "migrate_pages",
[257] = "openat",
[258] = "mkdirat",
[259] = "mknodat",
[260] = "fchownat",
[261] = "futimesat",
[262] = "newfstatat",
[263] = "unlinkat",
[264] = "renameat",
[265] = "linkat",
[266] = "symlinkat",
[267] = "readlinkat",
[268] = "fchmodat",
[269] = "faccessat",
[270] = "pselect6",
[271] = "ppoll",
[272] = "unshare",
[273] = "set_robust_list",
[274] = "get_robust_list",
[275] = "splice",
[276] = "tee",
[277] = "sync_file_range",
[278] = "vmsplice",
[279] = "move_pages",
[280] = "utimensat",
[281] = "epoll_pwait",
[282] = "signalfd",
[283] = "timerfd_create",
[284] = "eventfd",
[285] = "fallocate",
[286] = "timerfd_settime",
[287] = "timerfd_gettime",
[288] = "accept4",
[289] = "signalfd4",
[290] = "eventfd2",
[291] = "epoll_create1",
[292] = "dup3",
[293] = "pipe2",
[294] = "inotify_init1",
[295] = "preadv",
[296] = "pwritev",
[297] = "rt_tgsigqueueinfo",
[298] = "perf_event_open",
[299] = "recvmmsg",
[300] = "fanotify_init",
[301] = "fanotify_mark",
[302] = "prlimit64",
[303] = "name_to_handle_at",
[304] = "open_by_handle_at",
[305] = "clock_adjtime",
[306] = "syncfs",
[307] = "sendmmsg",
[308] = "setns",
[309] = "getcpu",
[310] = "process_vm_readv",
[311] = "process_vm_writev",
[312] = "kcmp",
[313] = "finit_module",
[314] = "sched_setattr",
[315] = "sched_getattr",
[316] = "renameat2",
[317] = "seccomp",
[318] = "getrandom",
[319] = "memfd_create",
[320] = "kexec_file_load",
[321] = "bpf",
[322] = "execveat",
[323] = "userfaultfd",
[324] = "membarrier",
[325] = "mlock2",
[326] = "copy_file_range",
[327] = "preadv2",
[328] = "pwritev2",
[329] = "pkey_mprotect",
[330] = "pkey_alloc",
[331] = "pkey_free",
[332] = "statx",
[333] = "io_pgetevents",
[334] = "rseq",
[424] = "pidfd_send_signal",
[425] = "io_uring_setup",
[426] = "io_uring_enter",
[427] = "io_uring_register",
[428] = "open_tree",
[429] = "move_mount",
[430] = "fsopen",
[431] = "fsconfig",
[432] = "fsmount",
[433] = "fspick",
[434] = "pidfd_open",
[435] = "clone3",
[437] = "openat2",
[438] = "pidfd_getfd",
};
size_t syscall_names_x86_64_size = sizeof(syscall_names_x86_64)/sizeof(char*);
#endif
void syscall_name(unsigned n, char *buf, size_t size)
{
const char *name = NULL;
if (n < syscall_names_size)
name = syscall_names[n];
#ifdef __x86_64__
else if (n < syscall_names_x86_64_size)
name = syscall_names_x86_64[n];
#endif
if (name)
strncpy(buf, name, size-1);
else
snprintf(buf, size, "[unknown: %u]", n);
}
int list_syscalls(void)
{
const char **list = syscall_names;
size_t size = syscall_names_size;
#ifdef __x86_64__
if (!size) {
size = syscall_names_x86_64_size;
list = syscall_names_x86_64;
}
#endif
for (size_t i = 0; i < size; i++) {
if (list[i])
printf("%3zd: %s\n", i, list[i]);
}
return (!list || !size);
}

View File

@@ -0,0 +1,12 @@
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
#ifndef __SYSCALL_HELPERS_H
#define __SYSCALL_HELPERS_H
#include <stddef.h>
void init_syscall_names(void);
void free_syscall_names(void);
void list_syscalls(void);
void syscall_name(unsigned n, char *buf, size_t size);
#endif /* __SYSCALL_HELPERS_H */

View File

@@ -0,0 +1,234 @@
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <sys/resource.h>
#include <time.h>
#include "trace_helpers.h"
#define min(x, y) ({ \
typeof(x) _min1 = (x); \
typeof(y) _min2 = (y); \
(void) (&_min1 == &_min2); \
_min1 < _min2 ? _min1 : _min2; })
struct ksyms {
struct ksym *syms;
int syms_sz;
int syms_cap;
char *strs;
int strs_sz;
int strs_cap;
};
static int ksyms__add_symbol(struct ksyms *ksyms, const char *name, unsigned long addr)
{
size_t new_cap, name_len = strlen(name) + 1;
struct ksym *ksym;
void *tmp;
if (ksyms->strs_sz + name_len > ksyms->strs_cap) {
new_cap = ksyms->strs_cap * 4 / 3;
if (new_cap < ksyms->strs_sz + name_len)
new_cap = ksyms->strs_sz + name_len;
if (new_cap < 1024)
new_cap = 1024;
tmp = realloc(ksyms->strs, new_cap);
if (!tmp)
return -1;
ksyms->strs = tmp;
ksyms->strs_cap = new_cap;
}
if (ksyms->syms_sz + 1 > ksyms->syms_cap) {
new_cap = ksyms->syms_cap * 4 / 3;
if (new_cap < 1024)
new_cap = 1024;
tmp = realloc(ksyms->syms, sizeof(*ksyms->syms) * new_cap);
if (!tmp)
return -1;
ksyms->syms = tmp;
ksyms->syms_cap = new_cap;
}
ksym = &ksyms->syms[ksyms->syms_sz];
/* while constructing, re-use pointer as just a plain offset */
ksym->name = (void *)(unsigned long)ksyms->strs_sz;
ksym->addr = addr;
memcpy(ksyms->strs + ksyms->strs_sz, name, name_len);
ksyms->strs_sz += name_len;
ksyms->syms_sz++;
return 0;
}
static int ksym_cmp(const void *p1, const void *p2)
{
const struct ksym *s1 = p1, *s2 = p2;
if (s1->addr == s2->addr)
return strcmp(s1->name, s2->name);
return s1->addr < s2->addr ? -1 : 1;
}
struct ksyms *ksyms__load(void)
{
char sym_type, sym_name[256];
struct ksyms *ksyms;
unsigned long sym_addr;
int i, ret;
FILE *f;
f = fopen("/proc/kallsyms", "r");
if (!f)
return NULL;
ksyms = calloc(1, sizeof(*ksyms));
if (!ksyms)
goto err_out;
while (true) {
ret = fscanf(f, "%lx %c %s%*[^\n]\n",
&sym_addr, &sym_type, sym_name);
if (ret == EOF && feof(f))
break;
if (ret != 3)
goto err_out;
if (ksyms__add_symbol(ksyms, sym_name, sym_addr))
goto err_out;
}
/* now when strings are finalized, adjust pointers properly */
for (i = 0; i < ksyms->syms_sz; i++)
ksyms->syms[i].name += (unsigned long)ksyms->strs;
qsort(ksyms->syms, ksyms->syms_sz, sizeof(*ksyms->syms), ksym_cmp);
fclose(f);
return ksyms;
err_out:
ksyms__free(ksyms);
fclose(f);
return NULL;
}
void ksyms__free(struct ksyms *ksyms)
{
if (!ksyms)
return;
free(ksyms->syms);
free(ksyms->strs);
free(ksyms);
}
const struct ksym *ksyms__map_addr(const struct ksyms *ksyms,
unsigned long addr)
{
int start = 0, end = ksyms->syms_sz - 1, mid;
unsigned long sym_addr;
/* find largest sym_addr <= addr using binary search */
while (start < end) {
mid = start + (end - start + 1) / 2;
sym_addr = ksyms->syms[mid].addr;
if (sym_addr <= addr)
start = mid;
else
end = mid - 1;
}
if (start == end && ksyms->syms[start].addr <= addr)
return &ksyms->syms[start];
return NULL;
}
const struct ksym *ksyms__get_symbol(const struct ksyms *ksyms,
const char *name)
{
int i;
for (i = 0; i < ksyms->syms_sz; i++) {
if (strcmp(ksyms->syms[i].name, name) == 0)
return &ksyms->syms[i];
}
return NULL;
}
static void print_stars(unsigned int val, unsigned int val_max, int width)
{
int num_stars, num_spaces, i;
bool need_plus;
num_stars = min(val, val_max) * width / val_max;
num_spaces = width - num_stars;
need_plus = val > val_max;
for (i = 0; i < num_stars; i++)
printf("*");
for (i = 0; i < num_spaces; i++)
printf(" ");
if (need_plus)
printf("+");
}
void print_log2_hist(unsigned int *vals, int vals_size, char *val_type)
{
int stars_max = 40, idx_max = -1;
unsigned int val, val_max = 0;
unsigned long long low, high;
int stars, width, i;
for (i = 0; i < vals_size; i++) {
val = vals[i];
if (val > 0)
idx_max = i;
if (val > val_max)
val_max = val;
}
if (idx_max < 0)
return;
printf("%*s%-*s : count distribution\n", idx_max <= 32 ? 5 : 15, "",
idx_max <= 32 ? 19 : 29, val_type);
if (idx_max <= 32)
stars = stars_max;
else
stars = stars_max / 2;
for (i = 0; i <= idx_max; i++) {
low = (1ULL << (i + 1)) >> 1;
high = (1ULL << (i + 1)) - 1;
if (low == high)
low -= 1;
val = vals[i];
width = idx_max <= 32 ? 10 : 20;
printf("%*lld -> %-*lld : %-8d |", width, low, width, high, val);
print_stars(val, val_max, stars);
printf("|\n");
}
}
unsigned long long get_ktime_ns(void)
{
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return ts.tv_sec * NSEC_PER_SEC + ts.tv_nsec;
}
int bump_memlock_rlimit(void)
{
struct rlimit rlim_new = {
.rlim_cur = RLIM_INFINITY,
.rlim_max = RLIM_INFINITY,
};
return setrlimit(RLIMIT_MEMLOCK, &rlim_new);
}

View File

@@ -0,0 +1,26 @@
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
#ifndef __TRACE_HELPERS_H
#define __TRACE_HELPERS_H
#define NSEC_PER_SEC 1000000000ULL
struct ksym {
const char *name;
unsigned long addr;
};
struct ksyms;
struct ksyms *ksyms__load(void);
void ksyms__free(struct ksyms *ksyms);
const struct ksym *ksyms__map_addr(const struct ksyms *ksyms,
unsigned long addr);
const struct ksym *ksyms__get_symbol(const struct ksyms *ksyms,
const char *name);
void print_log2_hist(unsigned int *vals, int vals_size, char *val_type);
unsigned long long get_ktime_ns(void);
int bump_memlock_rlimit(void);
#endif /* __TRACE_HELPERS_H */

106795
execsnoop-libbpf/vmlinux.h Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -55,7 +55,8 @@ Main feature:
## How to install
```bash
mkdir build && cd build && cmake .. && make && make install
cd execsnoop-libbpf && make install
mkdir build && cd build && cmake .. && make install
```
- It is alreay in [archlinux AUR](https://aur.archlinux.org/packages/?K=cgproxy).
@@ -109,7 +110,7 @@ Config file: **/etc/cgproxy/config.json**
- **port** tproxy listenning port
- program level proxy control, need `bcc/libbpfcc` and `linux-headers` installed to work
- program level proxy control:
- **program_proxy** program need to be proxied
- **program_noproxy** program that won't be proxied
@@ -181,16 +182,6 @@ sudo systemctl restart cgproxy.service
cgnoproxy [--debug] --pid <PID>
```
- `cgattach` attach specific process pid to specific cgroup which will create if not exist , cgroup can be only one level down exist cgroup, otherwise created fail.
You need to set `set(build_tools ON)` in *CmakeLists.txt* to build this.
```bash
cgattch <pid> <cgroup>
# example
cgattch 9999 /proxy.slice
```
- For more detail command usage, see `man cgproxyd` `man cgproxy` `man cgnoproxy`
## NOTES

View File

@@ -1,14 +1,9 @@
find_package(Threads REQUIRED)
find_package(nlohmann_json REQUIRED)
include_directories(${PROJECT_SOURCE_DIR})
include_directories(${PROJECT_SOURCE_DIR}/execsnoop-libbpf/)
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
if (with_execsnoop)
add_library(execsnoop MODULE execsnoop.cpp common.cpp)
target_link_libraries(execsnoop bcc)
install(TARGETS execsnoop DESTINATION /usr/lib/cgproxy/ PERMISSIONS ${basic_permission})
endif()
add_executable(main main.cpp
common.cpp config.cpp cgroup_attach.cpp
socket_client.cpp socket_server.cpp)

View File

@@ -4,7 +4,7 @@
#include "cgroup_attach.h"
#include "common.h"
#include "config.h"
#include "execsnoop.h"
#include "execsnoop_share.h"
#include "socket_server.h"
#include <algorithm>
#include <csignal>