Skip to content

Commit

Permalink
EventProbe: capture file info from inode
Browse files Browse the repository at this point in the history
  • Loading branch information
mmat11 committed Oct 24, 2023
1 parent 039ceef commit 13ee1b1
Show file tree
Hide file tree
Showing 5 changed files with 213 additions and 6 deletions.
27 changes: 24 additions & 3 deletions GPL/Events/EbpfEventProto.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ enum ebpf_varlen_field_type {
EBPF_VL_FIELD_NEW_PATH,
EBPF_VL_FIELD_TTY_OUT,
EBPF_VL_FIELD_PIDS_SS_CGROUP_PATH,
EBPF_VL_FIELD_SYMLINK_TARGET_PATH,
};

// Convenience macro to iterate all the variable length fields in an event
Expand Down Expand Up @@ -117,34 +118,54 @@ struct ebpf_tty_dev {
struct ebpf_tty_termios termios;
} __attribute__((packed));

enum ebpf_file_type {
EBPF_FILE_TYPE_FILE = 1,
EBPF_FILE_TYPE_DIR = 2,
EBPF_FILE_TYPE_SYMLINK = 3,
};

struct ebpf_file_info {
enum ebpf_file_type type;
uint64_t inode;
uint16_t mode;
uint64_t size;
uint32_t uid;
uint32_t gid;
uint64_t mtime;
uint64_t ctime;
} __attribute__((packed));

// Full events follow
struct ebpf_file_delete_event {
struct ebpf_event_header hdr;
struct ebpf_pid_info pids;
struct ebpf_file_info finfo;
uint32_t mntns;
char comm[TASK_COMM_LEN];

// Variable length fields: path
// Variable length fields: path, symlink_target_path
struct ebpf_varlen_fields_start vl_fields;
} __attribute__((packed));

struct ebpf_file_create_event {
struct ebpf_event_header hdr;
struct ebpf_pid_info pids;
struct ebpf_file_info finfo;
uint32_t mntns;
char comm[TASK_COMM_LEN];

// Variable length fields: path
// Variable length fields: path, symlink_target_path
struct ebpf_varlen_fields_start vl_fields;
} __attribute__((packed));

struct ebpf_file_rename_event {
struct ebpf_event_header hdr;
struct ebpf_pid_info pids;
struct ebpf_file_info finfo;
uint32_t mntns;
char comm[TASK_COMM_LEN];

// Variable length fields: old_path, new_path
// Variable length fields: old_path, new_path, symlink_target_path
struct ebpf_varlen_fields_start vl_fields;
} __attribute__((packed));

Expand Down
79 changes: 79 additions & 0 deletions GPL/Events/File/File.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause

/*
* Copyright (C) 2021 Elasticsearch BV
*
* This software is dual-licensed under the BSD 2-Clause and GPL v2 licenses.
* You may choose either one of them if you use this software.
*/

#ifndef EBPF_EVENTPROBE_FILE_H
#define EBPF_EVENTPROBE_FILE_H

#include "EbpfEventProto.h"

#define PATH_MAX 4096

// include/uapi/linux/stat.h
#define S_IFMT 00170000
#define S_IFSOCK 0140000
#define S_IFLNK 0120000
#define S_IFREG 0100000
#define S_IFBLK 0060000
#define S_IFDIR 0040000
#define S_IFCHR 0020000
#define S_IFIFO 0010000
#define S_ISUID 0004000
#define S_ISGID 0002000
#define S_ISVTX 0001000

#define S_ISLNK(m) (((m)&S_IFMT) == S_IFLNK)
#define S_ISREG(m) (((m)&S_IFMT) == S_IFREG)
#define S_ISDIR(m) (((m)&S_IFMT) == S_IFDIR)
#define S_ISCHR(m) (((m)&S_IFMT) == S_IFCHR)
#define S_ISBLK(m) (((m)&S_IFMT) == S_IFBLK)
#define S_ISFIFO(m) (((m)&S_IFMT) == S_IFIFO)
#define S_ISSOCK(m) (((m)&S_IFMT) == S_IFSOCK)

#define S_IRWXU 00700
#define S_IRUSR 00400
#define S_IWUSR 00200
#define S_IXUSR 00100

#define S_IRWXG 00070
#define S_IRGRP 00040
#define S_IWGRP 00020
#define S_IXGRP 00010

#define S_IRWXO 00007
#define S_IROTH 00004
#define S_IWOTH 00002
#define S_IXOTH 00001

static int ebpf_file_info__fill(struct ebpf_file_info *finfo, struct dentry *de)
{
struct inode *ino = BPF_CORE_READ(de, d_inode);

finfo->inode = BPF_CORE_READ(ino, i_ino);
finfo->mode = BPF_CORE_READ(ino, i_mode);
finfo->size = BPF_CORE_READ(ino, i_size);
finfo->uid = BPF_CORE_READ(ino, i_uid.val);
finfo->gid = BPF_CORE_READ(ino, i_gid.val);
finfo->mtime = BPF_CORE_READ(ino, i_mtime.tv_nsec);
finfo->ctime = BPF_CORE_READ(ino, i_ctime.tv_nsec);

if (S_ISREG(finfo->mode)) {
finfo->type = EBPF_FILE_TYPE_FILE;
} else if (S_ISDIR(finfo->mode)) {
finfo->type = EBPF_FILE_TYPE_DIR;
} else if (S_ISLNK(finfo->mode)) {
finfo->type = EBPF_FILE_TYPE_SYMLINK;
} else {
bpf_printk("unknown file type (mode=%d)", finfo->mode);
return -1;
}

return 0;
}

#endif // EBPF_EVENTPROBE_FILE_H
44 changes: 42 additions & 2 deletions GPL/Events/File/Probe.bpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>

#include "File.h"
#include "Helpers.h"
#include "PathResolver.h"
#include "State.h"
Expand Down Expand Up @@ -123,11 +124,16 @@ static int vfs_unlink__exit(int ret)
ebpf_pid_info__fill(&event->pids, task);

struct path p;
p.dentry = state->unlink.de;
p.dentry = &state->unlink.de;
p.mnt = state->unlink.mnt;
event->mntns = mntns(task);
bpf_get_current_comm(event->comm, TASK_COMM_LEN);

if (ebpf_file_info__fill(&event->finfo, p.dentry)) {
bpf_printk("vfs_unlink__exit: failed to fill file info\n");
goto out;
}

// Variable length fields
ebpf_vl_fields__init(&event->vl_fields);
struct ebpf_varlen_field *field;
Expand All @@ -138,6 +144,12 @@ static int vfs_unlink__exit(int ret)
size = ebpf_resolve_path_to_string(field->data, &p, task);
ebpf_vl_field__set_size(&event->vl_fields, field, size);

// symlink_target_path
field = ebpf_vl_field__add(&event->vl_fields, EBPF_VL_FIELD_SYMLINK_TARGET_PATH);
char *link = BPF_CORE_READ(p.dentry, d_inode, i_link);
size = read_kernel_str_or_empty_str(field->data, PATH_MAX, link);
ebpf_vl_field__set_size(&event->vl_fields, field, size);

bpf_ringbuf_output(&ringbuf, event, EVENT_SIZE(event), 0);

// Certain filesystems (eg. overlayfs) call vfs_unlink twice during the same
Expand Down Expand Up @@ -170,7 +182,10 @@ static int vfs_unlink__enter(struct dentry *de)
goto out;
}

state->unlink.de = de;
if (bpf_core_read(&state->unlink.de, sizeof(struct dentry), de)) {
bpf_printk("vfs_unlink__enter: failed to read dentry\n");
goto out;
}
state->unlink.step = UNLINK_STATE_DENTRY_SET;

out:
Expand Down Expand Up @@ -225,6 +240,11 @@ static int do_filp_open__exit(struct file *f)
event->mntns = mntns(task);
bpf_get_current_comm(event->comm, TASK_COMM_LEN);

if (ebpf_file_info__fill(&event->finfo, p.dentry)) {
bpf_printk("do_filp_open__exit: failed to fill file info\n");
goto out;
}

// Variable length fields
ebpf_vl_fields__init(&event->vl_fields);
struct ebpf_varlen_field *field;
Expand All @@ -235,6 +255,12 @@ static int do_filp_open__exit(struct file *f)
size = ebpf_resolve_path_to_string(field->data, &p, task);
ebpf_vl_field__set_size(&event->vl_fields, field, size);

// symlink_target_path
field = ebpf_vl_field__add(&event->vl_fields, EBPF_VL_FIELD_SYMLINK_TARGET_PATH);
char *link = BPF_CORE_READ(p.dentry, d_inode, i_link);
size = read_kernel_str_or_empty_str(field->data, PATH_MAX, link);
ebpf_vl_field__set_size(&event->vl_fields, field, size);

bpf_ringbuf_output(&ringbuf, event, EVENT_SIZE(event), 0);
}

Expand Down Expand Up @@ -316,6 +342,7 @@ static int vfs_rename__enter(struct dentry *old_dentry, struct dentry *new_dentr
ebpf_resolve_path_to_string(ss->rename.new_path, &p, task);

state->rename.step = RENAME_STATE_PATHS_SET;
state->rename.de = old_dentry;

out:
return 0;
Expand Down Expand Up @@ -395,6 +422,11 @@ static int vfs_rename__exit(int ret)
event->mntns = mntns(task);
bpf_get_current_comm(event->comm, TASK_COMM_LEN);

if (ebpf_file_info__fill(&event->finfo, state->rename.de)) {
bpf_printk("vfs_rename__exit: failed to fill file info\n");
goto out;
}

// Variable length fields
ebpf_vl_fields__init(&event->vl_fields);
struct ebpf_varlen_field *field;
Expand All @@ -410,6 +442,14 @@ static int vfs_rename__exit(int ret)
size = read_kernel_str_or_empty_str(field->data, PATH_MAX, ss->rename.new_path);
ebpf_vl_field__set_size(&event->vl_fields, field, size);

// symlink_target_path
field = ebpf_vl_field__add(&event->vl_fields, EBPF_VL_FIELD_SYMLINK_TARGET_PATH);
// NOTE: this temp variable is necessary to keep the verifier happy
struct dentry *tmp = (struct dentry *)state->rename.de;
char *link = BPF_CORE_READ(tmp, d_inode, i_link);
size = read_kernel_str_or_empty_str(field->data, PATH_MAX, link);
ebpf_vl_field__set_size(&event->vl_fields, field, size);

bpf_ringbuf_output(&ringbuf, event, EVENT_SIZE(event), 0);

// Certain filesystems (eg. overlayfs) call vfs_rename twice during the same
Expand Down
6 changes: 5 additions & 1 deletion GPL/Events/State.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,10 @@ enum ebpf_events_unlink_state_step {
struct ebpf_events_unlink_state {
enum ebpf_events_unlink_state_step step;
struct vfsmount *mnt;
struct dentry *de;
// NOTE: for this specific hook, storing a dentry pointer
// doesn't work because the content will be emptied out
// in the exit function.
struct dentry de;
};

enum ebpf_events_rename_state_step {
Expand All @@ -44,6 +47,7 @@ enum ebpf_events_rename_state_step {
struct ebpf_events_rename_state {
enum ebpf_events_rename_state_step step;
struct vfsmount *mnt;
struct dentry *de;
};

struct ebpf_events_tcp_connect_state {
Expand Down
63 changes: 63 additions & 0 deletions non-GPL/Events/EventsTrace/EventsTrace.c
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,11 @@ 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++) {
Expand Down Expand Up @@ -302,6 +307,46 @@ static void out_cred_info(const char *name, struct ebpf_cred_info *cred_info)
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;
}
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("mtime", finfo->mtime);
out_comma();

out_uint("ctime", finfo->ctime);
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'
Expand Down Expand Up @@ -337,6 +382,9 @@ static void out_file_delete(struct ebpf_file_delete_event *evt)
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)
Expand All @@ -346,6 +394,9 @@ static void out_file_delete(struct ebpf_file_delete_event *evt)
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;
default:
fprintf(stderr, "Unexpected variable length field: %d\n", field->type);
break;
Expand All @@ -369,6 +420,9 @@ static void out_file_create(struct ebpf_file_create_event *evt)
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)
Expand All @@ -378,6 +432,9 @@ static void out_file_create(struct ebpf_file_create_event *evt)
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;
default:
fprintf(stderr, "Unexpected variable length field: %d\n", field->type);
break;
Expand All @@ -401,6 +458,9 @@ static void out_file_rename(struct ebpf_file_rename_event *evt)
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)
Expand All @@ -413,6 +473,9 @@ static void out_file_rename(struct ebpf_file_rename_event *evt)
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;
default:
fprintf(stderr, "Unexpected variable length field: %d\n", field->type);
break;
Expand Down

0 comments on commit 13ee1b1

Please sign in to comment.