non-GPL/Events/EventsTrace/EventsTrace.c (1,081 lines of code) (raw):
// SPDX-License-Identifier: Elastic-2.0
/*
* Copyright 2021 Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under
* one or more contributor license agreements. Licensed under the Elastic
* License 2.0; you may not use this file except in compliance with the Elastic
* License 2.0.
*/
#include <argp.h>
#include <ctype.h>
#include <errno.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <sys/resource.h>
#include <sys/time.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <EbpfEvents.h>
const char *argp_program_bug_address = "https://github.com/elastic/ebpf/issues";
const char argp_program_doc[] =
"CLI frontend for the Elastic ebpf events library\n"
"\n"
"Prints process, network and file events sourced from the Elastic ebpf events library\n"
"\n"
"USAGE: ./EventsTrace [--all|-a] [--file-delete] [--file-create] [--file-rename] "
"[--file-memfd-open] [--file-shmem-open] [--file-modify]\n"
"[--process-fork] [--process-exec] [--process-exit] [--process-setsid] [--process-setuid] "
"[--process-setgid] [--process-tty-write] [--process-memfd_create] [--process-shmget] "
"[--process-ptrace] [--process-load_module]\n"
"[--net-conn-accept] [--net-conn-attempt] [--net-conn-closed]\n"
"[--print-features-on-init] [--stats|-s] [--unbuffer-stdout] [--libbpf-verbose]\n";
// Somewhat kludgy way of ensuring argp doesn't print the EBPF_* constants that
// happen to be valid ASCII values as short options. We pass these enum values
// to argp and start them at 0x80, so argp doesn't recognize them as valid
// ASCII and print them as short options.
enum cmdline_opts {
// Events
FILE_DELETE = 0x80,
FILE_CREATE,
FILE_RENAME,
FILE_MODIFY,
FILE_MEMFD_OPEN,
FILE_SHMEM_OPEN,
PROCESS_FORK,
PROCESS_EXEC,
PROCESS_EXIT,
PROCESS_SETSID,
PROCESS_SETUID,
PROCESS_SETGID,
PROCESS_TTY_WRITE,
PROCESS_MEMFD_CREATE,
PROCESS_SHMGET,
PROCESS_PTRACE,
PROCESS_LOAD_MODULE,
NETWORK_CONNECTION_ATTEMPTED,
NETWORK_CONNECTION_ACCEPTED,
NETWORK_CONNECTION_CLOSED,
NETWORK_DNS_PKT,
CMDLINE_MAX
};
static uint64_t cmdline_to_lib[CMDLINE_MAX] = {
// clang-format off
#define x(name) [name] = EBPF_EVENT_##name,
x(FILE_DELETE)
x(FILE_CREATE)
x(FILE_RENAME)
x(FILE_MODIFY)
x(FILE_MEMFD_OPEN)
x(FILE_SHMEM_OPEN)
x(PROCESS_FORK)
x(PROCESS_EXEC)
x(PROCESS_EXIT)
x(PROCESS_SETSID)
x(PROCESS_SETUID)
x(PROCESS_SETGID)
x(PROCESS_TTY_WRITE)
x(PROCESS_MEMFD_CREATE)
x(PROCESS_SHMGET)
x(PROCESS_PTRACE)
x(PROCESS_LOAD_MODULE)
x(NETWORK_CONNECTION_ATTEMPTED)
x(NETWORK_CONNECTION_ACCEPTED)
x(NETWORK_CONNECTION_CLOSED)
x(NETWORK_DNS_PKT)
#undef x
// clang-format on
};
static const struct argp_option opts[] = {
{"all", 'a', NULL, false, "Print all events", 0},
{"file-delete", FILE_DELETE, NULL, false, "Print file delete events", 0},
{"file-create", FILE_CREATE, NULL, false, "Print file create events", 0},
{"file-rename", FILE_RENAME, NULL, false, "Print file rename events", 0},
{"file-modify", FILE_MODIFY, NULL, false, "Print file modification events", 0},
{"file-memfd-open", FILE_MEMFD_OPEN, NULL, false, "Print memfd file open events", 0},
{"file-shmem-open", FILE_SHMEM_OPEN, NULL, false, "Print shmem (/dev/shm) file open events", 0},
{"process-fork", PROCESS_FORK, NULL, false, "Print process fork events", 0},
{"process-exec", PROCESS_EXEC, NULL, false, "Print process exec events", 0},
{"process-exit", PROCESS_EXIT, NULL, false, "Print process exit events", 0},
{"process-setsid", PROCESS_SETSID, NULL, false, "Print process setsid events", 0},
{"process-setuid", PROCESS_SETUID, NULL, false, "Print process setuid events", 0},
{"process-setgid", PROCESS_SETGID, NULL, false, "Print process setgid events", 0},
{"process-tty-write", PROCESS_TTY_WRITE, NULL, false, "Print process tty-write events", 0},
{"process-memfd-create", PROCESS_MEMFD_CREATE, NULL, false, "Print memfd_create events", 0},
{"process-shmget", PROCESS_SHMGET, NULL, false, "Print shmget events", 0},
{"process-ptrace", PROCESS_PTRACE, NULL, false, "Print ptrace events", 0},
{"process-load-module", PROCESS_LOAD_MODULE, NULL, false, "Print kernel module load events", 0},
{"net-conn-accept", NETWORK_CONNECTION_ACCEPTED, NULL, false,
"Print network connection accepted events", 0},
{"net-conn-dns-pkt", NETWORK_DNS_PKT, NULL, false, "Print DNS events", 0},
{"net-conn-attempt", NETWORK_CONNECTION_ATTEMPTED, NULL, false,
"Print network connection attempted events", 0},
{"net-conn-closed", NETWORK_CONNECTION_CLOSED, NULL, false,
"Print network connection closed events", 0},
{"print-features-on-init", 'i', NULL, false,
"Print a message with feature information when probes have been successfully loaded", 1},
{"stats", 's', NULL, false, "Print event statistics", 0},
{"unbuffer-stdout", 'u', NULL, false, "Disable userspace stdout buffering", 2},
{"libbpf-verbose", 'v', NULL, false, "Log verbose libbpf logs to stderr", 2},
{},
};
uint64_t g_events_env = 0;
uint64_t g_features_env = 0;
bool g_print_features_init = 0;
bool g_features_printed = 0;
bool g_unbuffer_stdout = 0;
bool g_libbpf_verbose = 0;
bool g_stats = 0;
static error_t parse_arg(int key, char *arg, struct argp_state *state)
{
switch (key) {
case 'i':
g_print_features_init = 1;
break;
case 'u':
g_unbuffer_stdout = 1;
break;
case 'v':
g_libbpf_verbose = 1;
break;
case 'a':
g_events_env = UINT64_MAX;
break;
case 's':
g_stats = 1;
break;
case FILE_DELETE:
case FILE_CREATE:
case FILE_RENAME:
case FILE_MODIFY:
case FILE_MEMFD_OPEN:
case FILE_SHMEM_OPEN:
case PROCESS_FORK:
case PROCESS_EXEC:
case PROCESS_EXIT:
case PROCESS_SETSID:
case PROCESS_SETUID:
case PROCESS_SETGID:
case PROCESS_TTY_WRITE:
case PROCESS_MEMFD_CREATE:
case PROCESS_SHMGET:
case PROCESS_PTRACE:
case PROCESS_LOAD_MODULE:
case NETWORK_CONNECTION_ACCEPTED:
case NETWORK_CONNECTION_ATTEMPTED:
case NETWORK_CONNECTION_CLOSED:
case NETWORK_DNS_PKT:
g_events_env |= cmdline_to_lib[key];
break;
case ARGP_KEY_ARG:
argp_usage(state);
break;
default:
return ARGP_ERR_UNKNOWN;
}
return 0;
}
static const struct argp argp = {
.options = opts,
.parser = parse_arg,
.doc = argp_program_doc,
};
static volatile sig_atomic_t exiting = 0;
static void sig_int(int signo)
{
if (exiting)
return;
exiting = 1;
fprintf(stdout, "Received SIGINT, exiting...\n");
}
static void out_comma(void)
{
printf(",");
}
static void out_newline(void)
{
printf("\n");
}
static void out_array_start(void)
{
printf("[");
}
static void out_array_end(void)
{
printf("]");
}
static void out_object_start(void)
{
printf("{");
}
static void out_object_end(void)
{
printf("}");
}
static void out_event_type(const char *type)
{
printf("\"event_type\":\"%s\"", type);
}
static void out_uint(const char *name, const unsigned long value)
{
printf("\"%s\":%lu", name, value);
}
static void out_int(const char *name, const long value)
{
printf("\"%s\":%ld", name, value);
}
static void out_octal(const char *name, const short unsigned value)
{
printf("\"%s\":%o", name, value);
}
static void out_escaped_string(const char *value)
{
for (size_t i = 0; i < strlen(value); i++) {
char c = value[i];
switch (c) {
case '\n':
printf("\\n");
break;
case '\r':
printf("\\r");
break;
case '\\':
printf("\\\\");
break;
case '"':
printf("\\\"");
break;
case '\t':
printf("\\t");
break;
case '\b':
printf("\\b");
break;
default:
if (!isascii(c) || iscntrl(c))
// \x is not a valid escape character in json,
// and something like '\xff' will break a remarkable number of JSON parsers.
// we have to print as '0xff'
printf("0x%02x", (uint8_t)c);
else
printf("%c", c);
}
}
}
static void out_string(const char *name, const char *value)
{
printf("\"%s\":\"", name);
out_escaped_string(value);
printf("\"");
}
static void out_bool(const char *name, bool value)
{
printf("\"%s\":%s", name, value ? "true" : "false");
}
static void out_tty_dev(const char *name, struct ebpf_tty_dev *tty_dev)
{
printf("\"%s\":", name);
out_object_start();
out_int("major", tty_dev->major);
out_comma();
out_int("minor", tty_dev->minor);
out_comma();
out_int("winsize_rows", tty_dev->winsize.rows);
out_comma();
out_int("winsize_cols", tty_dev->winsize.cols);
out_object_end();
}
static void out_pid_info(const char *name, struct ebpf_pid_info *pid_info)
{
printf("\"%s\":", name);
out_object_start();
out_int("tid", pid_info->tid);
out_comma();
out_int("tgid", pid_info->tgid);
out_comma();
out_int("ppid", pid_info->ppid);
out_comma();
out_int("pgid", pid_info->pgid);
out_comma();
out_int("sid", pid_info->sid);
out_comma();
out_uint("start_time_ns", pid_info->start_time_ns);
out_object_end();
}
static void out_cred_info(const char *name, struct ebpf_cred_info *cred_info)
{
printf("\"%s\":", name);
out_object_start();
out_int("ruid", cred_info->ruid);
out_comma();
out_int("rgid", cred_info->rgid);
out_comma();
out_int("euid", cred_info->euid);
out_comma();
out_int("egid", cred_info->egid);
out_comma();
out_int("suid", cred_info->suid);
out_comma();
out_int("sgid", cred_info->sgid);
out_comma();
printf("\"cap_permitted\": \"%lu\"", cred_info->cap_permitted);
out_comma();
printf("\"cap_effective\": \"%lu\"", cred_info->cap_effective);
out_object_end();
}
static void out_file_info(const char *name, struct ebpf_file_info *finfo)
{
printf("\"%s\":", name);
out_object_start();
switch (finfo->type) {
case EBPF_FILE_TYPE_DIR:
out_string("type", "DIR");
break;
case EBPF_FILE_TYPE_FILE:
out_string("type", "FILE");
break;
case EBPF_FILE_TYPE_SYMLINK:
out_string("type", "SYMLINK");
break;
case EBPF_FILE_TYPE_CHARACTER_DEVICE:
out_string("type", "CHARACTER_DEVICE");
break;
case EBPF_FILE_TYPE_BLOCK_DEVICE:
out_string("type", "BLOCK_DEVICE");
break;
case EBPF_FILE_TYPE_NAMED_PIPE:
out_string("type", "NAMED_PIPE");
break;
case EBPF_FILE_TYPE_SOCKET:
out_string("type", "SOCKET");
break;
case EBPF_FILE_TYPE_UNKNOWN:
out_string("type", "UNKNOWN");
break;
}
out_comma();
out_uint("inode", finfo->inode);
out_comma();
out_octal("mode", finfo->mode);
out_comma();
out_uint("size", finfo->size);
out_comma();
out_int("uid", finfo->uid);
out_comma();
out_int("gid", finfo->gid);
out_comma();
out_uint("atime", finfo->atime);
out_comma();
out_uint("mtime", finfo->mtime);
out_comma();
out_uint("ctime", finfo->ctime);
out_object_end();
}
static void out_ns_info(const char *name, struct ebpf_namespace_info *ns)
{
printf("\"%s\":", name);
out_object_start();
out_uint("uts", ns->uts_inonum);
out_comma();
out_uint("ipc", ns->ipc_inonum);
out_comma();
out_uint("mnt", ns->mnt_inonum);
out_comma();
out_uint("net", ns->net_inonum);
out_comma();
out_uint("cgroup", ns->cgroup_inonum);
out_comma();
out_uint("time", ns->time_inonum);
out_comma();
out_uint("pid", ns->pid_inonum);
out_object_end();
}
static void out_null_delimited_string_array(const char *name, char *buf, size_t buf_size)
{
// buf is an array (argv, env etc.) with multiple values delimited by a '\0'
printf("\"%s\":", name);
out_array_start();
for (uint64_t index = 0; index < buf_size && buf_size != 1;) {
char *elem = buf + index;
printf(" \"");
out_escaped_string(elem);
printf("\"");
index += (strlen(elem) + 1);
if (index < buf_size - 1) {
out_comma();
} else {
printf(" ");
}
}
out_array_end();
}
static void out_file_delete(struct ebpf_file_delete_event *evt)
{
out_object_start();
out_event_type("FILE_DELETE");
out_comma();
out_pid_info("pids", &evt->pids);
out_comma();
out_cred_info("creds", &evt->creds);
out_comma();
out_int("mount_namespace", evt->mntns);
out_comma();
out_string("comm", (const char *)&evt->comm);
out_comma();
out_file_info("file_info", &evt->finfo);
struct ebpf_varlen_field *field;
FOR_EACH_VARLEN_FIELD(evt->vl_fields, field)
{
out_comma();
switch (field->type) {
case EBPF_VL_FIELD_PATH:
out_string("path", field->data);
break;
case EBPF_VL_FIELD_SYMLINK_TARGET_PATH:
out_string("symlink_target_path", field->data);
break;
case EBPF_VL_FIELD_PIDS_SS_CGROUP_PATH:
out_string("pids_ss_cgroup_path", field->data);
break;
default:
fprintf(stderr, "Unexpected variable length field: %d\n", field->type);
break;
}
}
out_object_end();
out_newline();
}
static void out_file_generic(struct ebpf_file_create_event *evt, const char *event_type_str)
{
out_object_start();
out_event_type(event_type_str);
out_comma();
out_pid_info("pids", &evt->pids);
out_comma();
out_cred_info("creds", &evt->creds);
out_comma();
out_int("mount_namespace", evt->mntns);
out_comma();
out_string("comm", (const char *)&evt->comm);
out_comma();
out_file_info("file_info", &evt->finfo);
struct ebpf_varlen_field *field;
FOR_EACH_VARLEN_FIELD(evt->vl_fields, field)
{
out_comma();
switch (field->type) {
case EBPF_VL_FIELD_PATH:
out_string("path", field->data);
break;
case EBPF_VL_FIELD_SYMLINK_TARGET_PATH:
out_string("symlink_target_path", field->data);
break;
case EBPF_VL_FIELD_PIDS_SS_CGROUP_PATH:
out_string("pids_ss_cgroup_path", field->data);
break;
default:
fprintf(stderr, "Unexpected variable length field: %d\n", field->type);
break;
}
}
out_object_end();
out_newline();
}
static void out_file_create(struct ebpf_file_create_event *evt)
{
out_file_generic(evt, "FILE_CREATE");
}
// reuse struct ebpf_file_create_event for memfd open events
static void out_file_memfd_open(struct ebpf_file_create_event *evt)
{
out_file_generic(evt, "FILE_MEMFD_OPEN");
}
// reuse struct ebpf_file_create_event for shmem open events
static void out_file_shmem_open(struct ebpf_file_create_event *evt)
{
out_file_generic(evt, "FILE_SHMEM_OPEN");
}
// kernel load module event
static void out_process_load_module(struct ebpf_process_load_module_event *evt)
{
out_object_start();
out_event_type("PROCESS_LOAD_MODULE");
out_comma();
out_pid_info("pids", &evt->pids);
struct ebpf_varlen_field *field;
FOR_EACH_VARLEN_FIELD(evt->vl_fields, field)
{
out_comma();
switch (field->type) {
case EBPF_VL_FIELD_FILENAME:
out_string("filename", field->data);
break;
case EBPF_VL_FIELD_MOD_VERSION:
out_string("mod_version", field->data);
break;
case EBPF_VL_FIELD_MOD_SRCVERSION:
out_string("mod_srcversion", field->data);
break;
default:
fprintf(stderr, "Unexpected variable length field: %d\n", field->type);
break;
}
}
out_object_end();
out_newline();
}
// ptrace() event
static void out_process_ptrace(struct ebpf_process_ptrace_event *evt)
{
out_object_start();
out_event_type("PROCESS_PTRACE");
out_comma();
out_pid_info("pids", &evt->pids);
out_comma();
out_int("child_pid", (int)evt->child_pid);
out_comma();
out_int("request", evt->request);
out_object_end();
out_newline();
}
// shmget() event
static void out_process_shmget(struct ebpf_process_shmget_event *evt)
{
out_object_start();
out_event_type("PROCESS_SHMGET");
out_comma();
out_pid_info("pids", &evt->pids);
out_comma();
out_uint("key", (unsigned int)evt->key);
out_comma();
out_uint("size", evt->size);
out_comma();
out_int("shmflg", evt->shmflg);
out_object_end();
out_newline();
}
// memfd_create() event
static void out_process_memfd_create(struct ebpf_process_memfd_create_event *evt)
{
out_object_start();
out_event_type("PROCESS_MEMFD_CREATE");
out_comma();
out_pid_info("pids", &evt->pids);
out_comma();
out_uint("flags", evt->flags);
out_comma();
out_bool("flag_cloexec", evt->flags & MFD_CLOEXEC);
out_comma();
out_bool("flag_allow_seal", evt->flags & MFD_ALLOW_SEALING);
out_comma();
out_bool("flag_hugetlb", evt->flags & MFD_HUGETLB);
out_comma();
out_bool("flag_noexec_seal", evt->flags & MFD_NOEXEC_SEAL);
out_comma();
out_bool("flag_exec", evt->flags & MFD_EXEC);
struct ebpf_varlen_field *field;
FOR_EACH_VARLEN_FIELD(evt->vl_fields, field)
{
out_comma();
switch (field->type) {
case EBPF_VL_FIELD_FILENAME:
out_string("filename", field->data);
break;
default:
fprintf(stderr, "Unexpected variable length field: %d\n", field->type);
break;
}
}
out_object_end();
out_newline();
}
static void out_file_rename(struct ebpf_file_rename_event *evt)
{
out_object_start();
out_event_type("FILE_RENAME");
out_comma();
out_pid_info("pids", &evt->pids);
out_comma();
out_cred_info("creds", &evt->creds);
out_comma();
out_int("mount_namespace", evt->mntns);
out_comma();
out_string("comm", (const char *)&evt->comm);
out_comma();
out_file_info("file_info", &evt->finfo);
struct ebpf_varlen_field *field;
FOR_EACH_VARLEN_FIELD(evt->vl_fields, field)
{
out_comma();
switch (field->type) {
case EBPF_VL_FIELD_OLD_PATH:
out_string("old_path", field->data);
break;
case EBPF_VL_FIELD_NEW_PATH:
out_string("new_path", field->data);
break;
case EBPF_VL_FIELD_SYMLINK_TARGET_PATH:
out_string("symlink_target_path", field->data);
break;
case EBPF_VL_FIELD_PIDS_SS_CGROUP_PATH:
out_string("pids_ss_cgroup_path", field->data);
break;
default:
fprintf(stderr, "Unexpected variable length field: %d\n", field->type);
break;
}
}
out_object_end();
out_newline();
}
static void out_file_modify(struct ebpf_file_modify_event *evt)
{
out_object_start();
out_event_type("FILE_MODIFY");
out_comma();
out_pid_info("pids", &evt->pids);
out_comma();
out_cred_info("creds", &evt->creds);
out_comma();
out_int("mount_namespace", evt->mntns);
out_comma();
out_string("comm", (const char *)&evt->comm);
out_comma();
switch (evt->change_type) {
case EBPF_FILE_CHANGE_TYPE_CONTENT:
out_string("change_type", "CONTENT");
break;
case EBPF_FILE_CHANGE_TYPE_PERMISSIONS:
out_string("change_type", "PERMISSIONS");
break;
case EBPF_FILE_CHANGE_TYPE_OWNER:
out_string("change_type", "OWNER");
break;
case EBPF_FILE_CHANGE_TYPE_XATTRS:
out_string("change_type", "XATTRS");
break;
default:
fprintf(stderr, "Invalid change type: %d\n", evt->change_type);
break;
}
out_comma();
out_file_info("file_info", &evt->finfo);
struct ebpf_varlen_field *field;
FOR_EACH_VARLEN_FIELD(evt->vl_fields, field)
{
out_comma();
switch (field->type) {
case EBPF_VL_FIELD_PATH:
out_string("path", field->data);
break;
case EBPF_VL_FIELD_SYMLINK_TARGET_PATH:
out_string("symlink_target_path", field->data);
break;
case EBPF_VL_FIELD_PIDS_SS_CGROUP_PATH:
out_string("pids_ss_cgroup_path", field->data);
break;
default:
fprintf(stderr, "Unexpected variable length field: %d\n", field->type);
break;
}
}
out_object_end();
out_newline();
}
static void out_process_fork(struct ebpf_process_fork_event *evt)
{
out_object_start();
out_event_type("PROCESS_FORK");
out_comma();
out_pid_info("parent_pids", &evt->parent_pids);
out_comma();
out_pid_info("child_pids", &evt->child_pids);
out_comma();
out_cred_info("creds", &evt->creds);
out_comma();
out_tty_dev("ctty", &evt->ctty);
out_comma();
out_string("comm", evt->comm);
out_comma();
out_ns_info("ns", &evt->ns);
struct ebpf_varlen_field *field;
FOR_EACH_VARLEN_FIELD(evt->vl_fields, field)
{
out_comma();
switch (field->type) {
case EBPF_VL_FIELD_PIDS_SS_CGROUP_PATH:
out_string("pids_ss_cgroup_path", field->data);
break;
case EBPF_VL_FIELD_CWD:
out_string("cwd", field->data);
break;
default:
fprintf(stderr, "Unexpected variable length field: %d\n", field->type);
break;
}
}
out_object_end();
out_newline();
}
static void out_process_exec(struct ebpf_process_exec_event *evt)
{
out_object_start();
out_event_type("PROCESS_EXEC");
out_comma();
out_pid_info("pids", &evt->pids);
out_comma();
out_cred_info("creds", &evt->creds);
out_comma();
out_tty_dev("ctty", &evt->ctty);
out_comma();
out_string("comm", evt->comm);
out_comma();
out_bool("is_setuid", evt->flags & EXEC_F_SETUID);
out_comma();
out_bool("is_setgid", evt->flags & EXEC_F_SETGID);
out_comma();
out_bool("is_memfd", evt->flags & EXEC_F_MEMFD);
out_comma();
unsigned int nlink = evt->inode_nlink;
out_uint("inode_nlink", nlink);
struct ebpf_varlen_field *field;
FOR_EACH_VARLEN_FIELD(evt->vl_fields, field)
{
out_comma();
switch (field->type) {
case EBPF_VL_FIELD_PIDS_SS_CGROUP_PATH:
out_string("pids_ss_cgroup_path", field->data);
break;
case EBPF_VL_FIELD_FILENAME:
out_string("filename", field->data);
break;
case EBPF_VL_FIELD_CWD:
out_string("cwd", field->data);
break;
case EBPF_VL_FIELD_ARGV:
out_null_delimited_string_array("argv", field->data, field->size);
break;
case EBPF_VL_FIELD_ENV:
out_null_delimited_string_array("env", field->data, field->size);
break;
default:
fprintf(stderr, "Unexpected variable length field: %d\n", field->type);
break;
}
}
out_object_end();
out_newline();
}
static void out_process_setsid(struct ebpf_process_setsid_event *evt)
{
out_object_start();
out_event_type("PROCESS_SETSID");
out_comma();
out_pid_info("pids", &evt->pids);
out_object_end();
out_newline();
}
static void out_process_setuid(struct ebpf_process_setuid_event *evt)
{
out_object_start();
out_event_type("PROCESS_SETUID");
out_comma();
out_pid_info("pids", &evt->pids);
out_comma();
out_uint("new_ruid", evt->new_ruid);
out_comma();
out_uint("new_euid", evt->new_euid);
out_object_end();
out_newline();
}
static void out_process_setgid(struct ebpf_process_setgid_event *evt)
{
out_object_start();
out_event_type("PROCESS_SETGID");
out_comma();
out_pid_info("pids", &evt->pids);
out_comma();
out_uint("new_rgid", evt->new_rgid);
out_comma();
out_uint("new_egid", evt->new_egid);
out_object_end();
out_newline();
}
static void out_process_tty_write(struct ebpf_process_tty_write_event *evt)
{
out_object_start();
out_event_type("PROCESS_TTY_WRITE");
out_comma();
out_pid_info("pids", &evt->pids);
out_comma();
out_uint("tty_out_truncated", evt->tty_out_truncated);
out_comma();
out_tty_dev("tty", &evt->tty);
out_comma();
out_string("comm", (const char *)&evt->comm);
struct ebpf_varlen_field *field;
FOR_EACH_VARLEN_FIELD(evt->vl_fields, field)
{
out_comma();
switch (field->type) {
case EBPF_VL_FIELD_TTY_OUT:
out_string("tty_out", field->data);
break;
default:
fprintf(stderr, "Unexpected variable length field: %d\n", field->type);
break;
}
}
out_object_end();
out_newline();
}
static void out_process_exit(struct ebpf_process_exit_event *evt)
{
out_object_start();
out_event_type("PROCESS_EXIT");
out_comma();
out_pid_info("pids", &evt->pids);
out_comma();
out_tty_dev("ctty", &evt->ctty);
out_comma();
out_string("comm", evt->comm);
out_comma();
out_int("exit_code", evt->exit_code);
struct ebpf_varlen_field *field;
FOR_EACH_VARLEN_FIELD(evt->vl_fields, field)
{
out_comma();
switch (field->type) {
case EBPF_VL_FIELD_PIDS_SS_CGROUP_PATH:
out_string("pids_ss_cgroup_path", field->data);
break;
default:
fprintf(stderr, "Unexpected variable length field: %d\n", field->type);
break;
}
}
out_object_end();
out_newline();
}
static void out_ip_addr(const char *name, const void *addr)
{
char buf[INET_ADDRSTRLEN];
inet_ntop(AF_INET, addr, buf, sizeof(buf));
printf("\"%s\":\"%s\"", name, buf);
}
static void out_ip6_addr(const char *name, const void *addr)
{
char buf[INET6_ADDRSTRLEN];
inet_ntop(AF_INET6, addr, buf, sizeof(buf));
printf("\"%s\":\"%s\"", name, buf);
}
static void out_net_info(const char *name, struct ebpf_net_info *net, struct ebpf_event_header *hdr)
{
printf("\"%s\":", name);
out_object_start();
switch (net->transport) {
case EBPF_NETWORK_EVENT_TRANSPORT_TCP:
out_string("transport", "TCP");
out_comma();
break;
case EBPF_NETWORK_EVENT_TRANSPORT_UDP:
out_string("transport", "UDP");
out_comma();
break;
}
switch (net->family) {
case EBPF_NETWORK_EVENT_AF_INET:
out_string("family", "AF_INET");
out_comma();
out_ip_addr("source_address", &net->saddr);
out_comma();
out_int("source_port", net->sport);
out_comma();
out_ip_addr("destination_address", &net->daddr);
out_comma();
out_int("destination_port", net->dport);
break;
case EBPF_NETWORK_EVENT_AF_INET6:
out_string("family", "AF_INET6");
out_comma();
out_ip6_addr("source_address", &net->saddr6);
out_comma();
out_int("source_port", net->sport);
out_comma();
out_ip6_addr("destination_address", &net->daddr6);
out_comma();
out_int("destination_port", net->dport);
break;
}
out_comma();
out_int("network_namespace", net->netns);
switch (hdr->type) {
case EBPF_EVENT_NETWORK_CONNECTION_CLOSED:
out_comma();
out_uint("bytes_sent", net->tcp.close.bytes_sent);
out_comma();
out_uint("bytes_received", net->tcp.close.bytes_received);
break;
}
out_object_end();
}
static void out_network_event(const char *name, struct ebpf_net_event *evt)
{
out_object_start();
out_event_type(name);
out_comma();
out_pid_info("pids", &evt->pids);
out_comma();
out_net_info("net", &evt->net, &evt->hdr);
out_comma();
out_string("comm", (const char *)&evt->comm);
out_object_end();
out_newline();
}
static void out_network_connection_accepted_event(struct ebpf_net_event *evt)
{
out_network_event("NETWORK_CONNECTION_ACCEPTED", evt);
}
static void out_network_dns_event(struct ebpf_dns_event *event)
{
out_object_start();
out_event_type("DNS_PKT");
out_comma();
out_int("tgid", event->tgid);
out_comma();
out_int("cap_len", event->cap_len);
out_comma();
out_int("orig_len", event->orig_len);
out_comma();
out_string("direction", event->direction == EBPF_NETWORK_DIR_INGRESS ? "in" : "out");
out_comma();
printf("\"data\":");
out_array_start();
struct ebpf_varlen_field *field;
FOR_EACH_VARLEN_FIELD(event->vl_fields, field)
{
for (size_t i = 0; i < field->size; i++) {
uint8_t part = field->data[i];
printf("%d", part);
if (i < field->size - 1) {
printf(", ");
}
}
}
out_array_end();
out_object_end();
out_newline();
}
static void out_network_connection_attempted_event(struct ebpf_net_event *evt)
{
out_network_event("NETWORK_CONNECTION_ATTEMPTED", evt);
}
static void out_network_connection_closed_event(struct ebpf_net_event *evt)
{
out_network_event("NETWORK_CONNECTION_CLOSED", evt);
}
static int event_ctx_callback(struct ebpf_event_header *evt_hdr)
{
if (g_print_features_init)
while (!g_features_printed)
usleep(100000);
switch (evt_hdr->type) {
case EBPF_EVENT_PROCESS_FORK:
out_process_fork((struct ebpf_process_fork_event *)evt_hdr);
break;
case EBPF_EVENT_PROCESS_EXEC:
out_process_exec((struct ebpf_process_exec_event *)evt_hdr);
break;
case EBPF_EVENT_PROCESS_EXIT:
out_process_exit((struct ebpf_process_exit_event *)evt_hdr);
break;
case EBPF_EVENT_PROCESS_SETSID:
out_process_setsid((struct ebpf_process_setsid_event *)evt_hdr);
break;
case EBPF_EVENT_PROCESS_SETUID:
out_process_setuid((struct ebpf_process_setuid_event *)evt_hdr);
break;
case EBPF_EVENT_PROCESS_SETGID:
out_process_setgid((struct ebpf_process_setgid_event *)evt_hdr);
break;
case EBPF_EVENT_PROCESS_TTY_WRITE:
out_process_tty_write((struct ebpf_process_tty_write_event *)evt_hdr);
break;
case EBPF_EVENT_PROCESS_MEMFD_CREATE:
out_process_memfd_create((struct ebpf_process_memfd_create_event *)evt_hdr);
break;
case EBPF_EVENT_PROCESS_SHMGET:
out_process_shmget((struct ebpf_process_shmget_event *)evt_hdr);
break;
case EBPF_EVENT_PROCESS_PTRACE:
out_process_ptrace((struct ebpf_process_ptrace_event *)evt_hdr);
break;
case EBPF_EVENT_PROCESS_LOAD_MODULE:
out_process_load_module((struct ebpf_process_load_module_event *)evt_hdr);
break;
case EBPF_EVENT_FILE_DELETE:
out_file_delete((struct ebpf_file_delete_event *)evt_hdr);
break;
case EBPF_EVENT_FILE_CREATE:
out_file_create((struct ebpf_file_create_event *)evt_hdr);
break;
case EBPF_EVENT_FILE_RENAME:
out_file_rename((struct ebpf_file_rename_event *)evt_hdr);
break;
case EBPF_EVENT_FILE_MODIFY:
out_file_modify((struct ebpf_file_modify_event *)evt_hdr);
break;
case EBPF_EVENT_FILE_MEMFD_OPEN:
// reuses struct ebpf_file_create_event
out_file_memfd_open((struct ebpf_file_create_event *)evt_hdr);
break;
case EBPF_EVENT_FILE_SHMEM_OPEN:
// reuses struct ebpf_file_create_event
out_file_shmem_open((struct ebpf_file_create_event *)evt_hdr);
break;
case EBPF_EVENT_NETWORK_CONNECTION_ACCEPTED:
out_network_connection_accepted_event((struct ebpf_net_event *)evt_hdr);
break;
case EBPF_EVENT_NETWORK_CONNECTION_ATTEMPTED:
out_network_connection_attempted_event((struct ebpf_net_event *)evt_hdr);
break;
case EBPF_EVENT_NETWORK_CONNECTION_CLOSED:
out_network_connection_closed_event((struct ebpf_net_event *)evt_hdr);
break;
case EBPF_EVENT_NETWORK_DNS_PKT:
out_network_dns_event((struct ebpf_dns_event *)evt_hdr);
break;
}
return 0;
}
static void print_init_msg(uint64_t features)
{
printf("{\"probes_initialized\": true, \"features\": {");
printf("\"bpf_tramp\": %s", (features & EBPF_FEATURE_BPF_TRAMP) ? "true" : "false");
printf("}}\n");
}
int main(int argc, char **argv)
{
int err = 0;
struct ebpf_event_ctx *ctx = NULL;
if (signal(SIGINT, sig_int) == SIG_ERR) {
fprintf(stderr, "Failed to register SIGINT handler\n");
goto out;
}
err = argp_parse(&argp, argc, argv, 0, NULL, NULL);
if (err)
goto out;
if (g_unbuffer_stdout) {
err = setvbuf(stdout, NULL, _IONBF, 0);
if (err < 0) {
fprintf(stderr, "Could not turn off stdout buffering: %d %s\n", err, strerror(err));
goto out;
}
}
if (g_libbpf_verbose)
ebpf_set_verbose_logging();
err = ebpf_event_ctx__new(&ctx, event_ctx_callback, g_events_env);
if (err < 0) {
fprintf(stderr, "Could not create event context: %d %s\n", err, strerror(-err));
goto out;
}
if (g_print_features_init) {
print_init_msg(ebpf_event_ctx__get_features(ctx));
g_features_printed = 1;
}
while (!exiting) {
err = ebpf_event_ctx__next(ctx, 10);
if (err < 0 && err != -EINTR) {
fprintf(stderr, "Failed to poll event context %d: %s\n", err, strerror(-err));
break;
}
if (g_stats) {
struct ebpf_event_stats ees;
if (ebpf_event_ctx__read_stats(ctx, &ees) == 0)
printf("sent %lu lost %lu\n", ees.sent, ees.lost);
else
fprintf(stderr, "Failed to read stats: %s\n", strerror(errno));
}
}
ebpf_event_ctx__destroy(&ctx);
out:
return err != 0;
}