Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EventProbe: capture file info from inode #178

Merged
merged 3 commits into from
Oct 27, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
mmat11 marked this conversation as resolved.
Show resolved Hide resolved

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);
nicholasberlin marked this conversation as resolved.
Show resolved Hide resolved
finfo->ctime = BPF_CORE_READ(ino, i_ctime.tv_nsec);

if (S_ISREG(finfo->mode)) {
finfo->type = EBPF_FILE_TYPE_FILE;
mmat11 marked this conversation as resolved.
Show resolved Hide resolved
} 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);
mmat11 marked this conversation as resolved.
Show resolved Hide resolved
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)) {
mmat11 marked this conversation as resolved.
Show resolved Hide resolved
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);
mmat11 marked this conversation as resolved.
Show resolved Hide resolved
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;
mmat11 marked this conversation as resolved.
Show resolved Hide resolved

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
Loading
Loading