Skip to content

Commit 5129256

Browse files
yinjipingrvql
authored andcommitted
feat: agent - eBPF Add data retrieval related to mounts
Adjustments made for obtaining mount information: * Some file read/write operations may occur within nested mount namespaces. Using `/proc/<pid>/ns/mnt` to get the `mntns_id` only retrieves the first-level mount namespace ID and cannot determine the `mntns_id` of the namespace where the file actually resides. Therefore, we removed the method of obtaining `mntns_id` from `/proc` and instead retrieve the `mntns_id` of a file directly from kernel structures. This value is then passed to the upper-level program, which queries the mount information (`mountinfo`) based on the `mntns_id` obtained via eBPF. * Mount points were previously identified using device numbers (`major:minor`). However, when the same device is mounted at multiple mount points, the device number is identical for all of them, making it impossible to distinguish which mount point a file belongs to. To solve this, we now obtain the `mountID` from kernel structures. Each mount point has a unique `mountID`, which we use for accurate differentiation. * We need to obtain field offsets from kernel structures. Since these offsets may vary across different kernel versions, we implemented an automatic offset inference mechanism to handle this issue. Moreover, if BTF is supported, this problem does not exist.
1 parent 36ffa85 commit 5129256

12 files changed

Lines changed: 603 additions & 92 deletions

File tree

agent/src/ebpf/kernel/files_rw.bpf.c

Lines changed: 168 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121

2222
// Default value set when file read/write latency timestamp rollback occurs.
2323
#define TIME_ROLLBACK_DEFAULT_LATENCY_NS 50000
24-
24+
2525
static __inline bool is_regular_file(int fd,
2626
struct member_fields_offset *off_ptr)
2727
{
@@ -35,12 +35,153 @@ static __inline bool is_regular_file(int fd,
3535
return S_ISREG(i_mode);
3636
}
3737

38+
static __inline void *get_mount_ptr(void *file,
39+
struct member_fields_offset *off_ptr)
40+
{
41+
void *vfsmount = NULL;
42+
43+
/*
44+
* struct_file_f_path_offset: Starting from Linux 6.5, this offset has
45+
* undergone significant changes and automatic inference is not supported.
46+
*/
47+
48+
// file -> path -> vfsmount
49+
bpf_probe_read_kernel(&vfsmount, sizeof(vfsmount),
50+
file + off_ptr->struct_file_f_path_offset +
51+
off_ptr->struct_path_mnt_offset);
52+
if (vfsmount == NULL)
53+
return NULL;
54+
55+
// `vfsmount` is embedded in `struct mount`
56+
return (vfsmount - off_ptr->struct_mount_mnt_offset);
57+
}
58+
59+
static __inline void get_mount_ids(void *file,
60+
struct __io_event_buffer *buffer,
61+
struct member_fields_offset *off_ptr)
62+
{
63+
void *mount, *mnt_ns = NULL;
64+
buffer->mntns_id = 0;
65+
buffer->mnt_id = -1;
66+
mount = get_mount_ptr(file, off_ptr);
67+
if (mount == NULL)
68+
return;
69+
70+
//bpf_debug("mount_mnt_id_offset 0x%x\n", off_ptr->struct_mount_mnt_id_offset);
71+
if (off_ptr->struct_mount_mnt_id_offset != INVALID_OFFSET) {
72+
bpf_probe_read_kernel(&buffer->mnt_id, sizeof(buffer->mnt_id),
73+
mount +
74+
off_ptr->struct_mount_mnt_id_offset);
75+
}
76+
77+
if (off_ptr->struct_mount_mnt_ns_offset == INVALID_OFFSET)
78+
return;
79+
80+
bpf_probe_read_kernel(&mnt_ns, sizeof(mnt_ns),
81+
mount + off_ptr->struct_mount_mnt_ns_offset);
82+
if (mnt_ns == NULL)
83+
return;
84+
85+
// mnt_namespace -> ns_common -> inum
86+
bpf_probe_read_kernel(&buffer->mntns_id, sizeof(buffer->mntns_id),
87+
mnt_ns + off_ptr->struct_mnt_namespace_ns_offset +
88+
off_ptr->struct_ns_common_inum_offset);
89+
}
90+
91+
static __inline int infer_mount_offset(int fd,
92+
struct member_fields_offset *off_ptr)
93+
{
94+
// The inference has been completed.
95+
if (off_ptr->files_infer_done)
96+
return 0;
97+
98+
__u32 k0 = 0;
99+
struct adapt_kern_data *adapt_data;
100+
adapt_data = adapt_kern_data_map__lookup(&k0);
101+
if (!adapt_data)
102+
goto error;
103+
104+
// The inference is limited to the threads we have designated.
105+
if (adapt_data->id != bpf_get_current_pid_tgid())
106+
return -1;
107+
108+
void *file = fd_to_file(fd, off_ptr);
109+
if (file == NULL)
110+
goto error;
111+
112+
void *mount = get_mount_ptr(file, off_ptr);
113+
if (mount == NULL)
114+
goto error;
115+
116+
int i, mnt_id = 0;
117+
bool found = false;
118+
__u16 mnt_id_offsets[] = { 0x10c, 0x114, 0x11c, 0x124 };
119+
#pragma unroll
120+
for (i = 0; i < ARRAY_SIZE(mnt_id_offsets); i++) {
121+
bpf_probe_read_kernel(&mnt_id, sizeof(mnt_id),
122+
mount + mnt_id_offsets[i]);
123+
if (mnt_id == adapt_data->mnt_id) {
124+
found = true;
125+
break;
126+
}
127+
}
128+
129+
if (found) {
130+
off_ptr->struct_mount_mnt_id_offset = mnt_id_offsets[i];
131+
} else {
132+
off_ptr->struct_mount_mnt_id_offset = INVALID_OFFSET;
133+
}
134+
135+
__u32 mntns_id = 0;
136+
void *mnt_ns = NULL;
137+
__u16 mnt_ns_offset[] = { 0xe0, 0xe8 };
138+
off_ptr->struct_mount_mnt_ns_offset = INVALID_OFFSET;
139+
off_ptr->struct_mnt_namespace_ns_offset = INVALID_OFFSET;
140+
#pragma unroll
141+
for (i = 0; i < ARRAY_SIZE(mnt_ns_offset); i++) {
142+
bpf_probe_read_kernel(&mnt_ns, sizeof(mnt_ns),
143+
mount + mnt_ns_offset[i]);
144+
if (mnt_ns == NULL)
145+
continue;
146+
// mnt_namespace -> ns_common -> inum
147+
bpf_probe_read_kernel(&mntns_id, sizeof(mntns_id),
148+
mnt_ns +
149+
off_ptr->struct_ns_common_inum_offset);
150+
if (mntns_id == adapt_data->mntns_id) {
151+
found = true;
152+
off_ptr->struct_mount_mnt_ns_offset = mnt_ns_offset[i];
153+
off_ptr->struct_mnt_namespace_ns_offset = 0;
154+
off_ptr->files_infer_done = 1;
155+
return 0;
156+
}
157+
bpf_probe_read_kernel(&mntns_id, sizeof(mntns_id),
158+
mnt_ns + 0x8 +
159+
off_ptr->struct_ns_common_inum_offset);
160+
if (mntns_id == adapt_data->mntns_id) {
161+
off_ptr->struct_mount_mnt_ns_offset = mnt_ns_offset[i];
162+
off_ptr->struct_mnt_namespace_ns_offset = 0x8;
163+
off_ptr->files_infer_done = 1;
164+
return 0;
165+
}
166+
}
167+
168+
off_ptr->files_infer_done = 1;
169+
return -1;
170+
171+
error:
172+
off_ptr->struct_mount_mnt_id_offset = INVALID_OFFSET;
173+
off_ptr->struct_mount_mnt_ns_offset = INVALID_OFFSET;
174+
off_ptr->struct_mnt_namespace_ns_offset = INVALID_OFFSET;
175+
off_ptr->files_infer_done = 1;
176+
return -1;
177+
}
178+
38179
static __inline void set_file_metric_data(struct __io_event_buffer *buffer,
39180
struct __socket_data *v,
40181
int fd,
41182
struct member_fields_offset *off_ptr)
42183
{
43-
#define MAX_DIRECTORY_DEPTH 19
184+
#define MAX_DIRECTORY_DEPTH 18
44185

45186
struct member_fields_offset *offset = off_ptr;
46187
if (offset == NULL) {
@@ -90,6 +231,8 @@ static __inline void set_file_metric_data(struct __io_event_buffer *buffer,
90231
void *dentry = NULL, *parent;
91232
bpf_probe_read_kernel(&dentry, sizeof(dentry),
92233
file + offset->struct_file_dentry_offset);
234+
get_mount_ids(file, buffer, offset);
235+
//bpf_debug("buffer->mnt_id %d\n", buffer->mnt_id);
93236
buffer->len = 0;
94237
#pragma unroll
95238
for (int i = 0; i < MAX_DIRECTORY_DEPTH; i++) {
@@ -239,6 +382,9 @@ static __inline int do_sys_enter_pread(int fd, enum syscall_src_func fn)
239382
if (!offset)
240383
return 0;
241384

385+
// Attempt to infer file-related structure offsets.
386+
infer_mount_offset(fd, offset);
387+
242388
__u64 id = bpf_get_current_pid_tgid();
243389
// Stash arguments.
244390
struct data_args_t read_args = {};
@@ -252,7 +398,7 @@ static __inline int do_sys_enter_pread(int fd, enum syscall_src_func fn)
252398
#ifdef SUPPORTS_KPROBE_ONLY
253399
//ssize_t ksys_pread64(unsigned int fd, char __user *buf, size_t count,
254400
// loff_t pos)
255-
KPROG(ksys_pread64) (struct pt_regs *ctx) {
401+
KPROG(ksys_pread64) (struct pt_regs * ctx) {
256402
int fd = (unsigned int)PT_REGS_PARM1(ctx);
257403
return do_sys_enter_pread(fd, SYSCALL_FUNC_PREAD64);
258404
}
@@ -262,25 +408,25 @@ KPROG(ksys_pread64) (struct pt_regs *ctx) {
262408
* static ssize_t do_preadv(unsigned long fd, const struct iovec __user *vec,
263409
* unsigned long vlen, loff_t pos, rwf_t flags);
264410
*/
265-
KPROG(do_preadv) (struct pt_regs *ctx) {
411+
KPROG(do_preadv) (struct pt_regs * ctx) {
266412
int fd = (unsigned int)PT_REGS_PARM1(ctx);
267413
return do_sys_enter_pread(fd, SYSCALL_FUNC_PREADV);
268414
}
269415
#else
270416
// /sys/kernel/debug/tracing/events/syscalls/sys_enter_pread64/format
271-
TP_SYSCALL_PROG(enter_pread64) (struct syscall_comm_enter_ctx *ctx) {
417+
TP_SYSCALL_PROG(enter_pread64) (struct syscall_comm_enter_ctx * ctx) {
272418
int fd = ctx->fd;
273419
return do_sys_enter_pread(fd, SYSCALL_FUNC_PREAD64);
274420
}
275421

276422
// /sys/kernel/debug/tracing/events/syscalls/sys_enter_preadv/format
277-
TP_SYSCALL_PROG(enter_preadv) (struct syscall_comm_enter_ctx *ctx) {
423+
TP_SYSCALL_PROG(enter_preadv) (struct syscall_comm_enter_ctx * ctx) {
278424
int fd = ctx->fd;
279425
return do_sys_enter_pread(fd, SYSCALL_FUNC_PREADV);
280426
}
281427

282428
// /sys/kernel/debug/tracing/events/syscalls/sys_enter_preadv2/format
283-
TP_SYSCALL_PROG(enter_preadv2) (struct syscall_comm_enter_ctx *ctx) {
429+
TP_SYSCALL_PROG(enter_preadv2) (struct syscall_comm_enter_ctx * ctx) {
284430
int fd = ctx->fd;
285431
return do_sys_enter_pread(fd, SYSCALL_FUNC_PREADV2);
286432
}
@@ -306,28 +452,28 @@ static __inline int do_sys_exit_pread(void *ctx, ssize_t bytes_count)
306452
}
307453

308454
#ifdef SUPPORTS_KPROBE_ONLY
309-
KRETPROG(ksys_pread64) (struct pt_regs *ctx) {
455+
KRETPROG(ksys_pread64) (struct pt_regs * ctx) {
310456
ssize_t bytes_count = PT_REGS_RC(ctx);
311457
return do_sys_exit_pread((void *)ctx, bytes_count);
312458
}
313459

314-
KRETPROG(do_preadv) (struct pt_regs *ctx) {
460+
KRETPROG(do_preadv) (struct pt_regs * ctx) {
315461
ssize_t bytes_count = PT_REGS_RC(ctx);
316462
return do_sys_exit_pread((void *)ctx, bytes_count);
317463
}
318464
#else
319465
// /sys/kernel/debug/tracing/events/syscalls/sys_exit_pwrite64/format
320-
TP_SYSCALL_PROG(exit_pread64) (struct syscall_comm_exit_ctx *ctx) {
466+
TP_SYSCALL_PROG(exit_pread64) (struct syscall_comm_exit_ctx * ctx) {
321467
return do_sys_exit_pread((void *)ctx, (ssize_t) ctx->ret);
322468
}
323469

324470
// /sys/kernel/debug/tracing/events/syscalls/sys_exit_pwritev/format
325-
TP_SYSCALL_PROG(exit_preadv) (struct syscall_comm_exit_ctx *ctx) {
471+
TP_SYSCALL_PROG(exit_preadv) (struct syscall_comm_exit_ctx * ctx) {
326472
return do_sys_exit_pread((void *)ctx, (ssize_t) ctx->ret);
327473
}
328474

329475
// /sys/kernel/debug/tracing/events/syscalls/sys_exit_pwritev2/format
330-
TP_SYSCALL_PROG(exit_preadv2) (struct syscall_comm_exit_ctx *ctx) {
476+
TP_SYSCALL_PROG(exit_preadv2) (struct syscall_comm_exit_ctx * ctx) {
331477
return do_sys_exit_pread((void *)ctx, (ssize_t) ctx->ret);
332478
}
333479
#endif /* SUPPORTS_KPROBE_ONLY */
@@ -352,7 +498,7 @@ static __inline int do_sys_enter_pwrite(int fd, enum syscall_src_func fn)
352498
//ssize_t ksys_pwrite64(unsigned int fd, const char __user *buf,
353499
// size_t count, loff_t pos);
354500
#ifdef SUPPORTS_KPROBE_ONLY
355-
KPROG(ksys_pwrite64) (struct pt_regs *ctx) {
501+
KPROG(ksys_pwrite64) (struct pt_regs * ctx) {
356502
int fd = (int)PT_REGS_PARM1(ctx);
357503
return do_sys_enter_pwrite(fd, SYSCALL_FUNC_PWRITE64);
358504
}
@@ -362,25 +508,25 @@ KPROG(ksys_pwrite64) (struct pt_regs *ctx) {
362508
* static ssize_t do_pwritev(unsigned long fd, const struct iovec __user *vec,
363509
* unsigned long vlen, loff_t pos, rwf_t flags);
364510
*/
365-
KPROG(do_pwritev) (struct pt_regs *ctx) {
511+
KPROG(do_pwritev) (struct pt_regs * ctx) {
366512
int fd = (int)PT_REGS_PARM1(ctx);
367513
return do_sys_enter_pwrite(fd, SYSCALL_FUNC_PWRITEV);
368514
}
369515
#else
370516
// /sys/kernel/debug/tracing/events/syscalls/sys_enter_pwrite64/format
371-
TP_SYSCALL_PROG(enter_pwrite64) (struct syscall_comm_enter_ctx *ctx) {
517+
TP_SYSCALL_PROG(enter_pwrite64) (struct syscall_comm_enter_ctx * ctx) {
372518
int fd = ctx->fd;
373519
return do_sys_enter_pwrite(fd, SYSCALL_FUNC_PWRITE64);
374520
}
375521

376522
// /sys/kernel/debug/tracing/events/syscalls/sys_enter_pwritev/format
377-
TP_SYSCALL_PROG(enter_pwritev) (struct syscall_comm_enter_ctx *ctx) {
523+
TP_SYSCALL_PROG(enter_pwritev) (struct syscall_comm_enter_ctx * ctx) {
378524
int fd = ctx->fd;
379525
return do_sys_enter_pwrite(fd, SYSCALL_FUNC_PWRITEV);
380526
}
381527

382528
// /sys/kernel/debug/tracing/events/syscalls/sys_enter_pwritev2/format
383-
TP_SYSCALL_PROG(enter_pwritev2) (struct syscall_comm_enter_ctx *ctx) {
529+
TP_SYSCALL_PROG(enter_pwritev2) (struct syscall_comm_enter_ctx * ctx) {
384530
int fd = ctx->fd;
385531
return do_sys_enter_pwrite(fd, SYSCALL_FUNC_PWRITEV2);
386532
}
@@ -407,28 +553,28 @@ static __inline int do_sys_exit_pwrite(void *ctx, ssize_t bytes_count)
407553
}
408554

409555
#ifdef SUPPORTS_KPROBE_ONLY
410-
KRETPROG(ksys_pwrite64) (struct pt_regs *ctx) {
556+
KRETPROG(ksys_pwrite64) (struct pt_regs * ctx) {
411557
ssize_t bytes_count = PT_REGS_RC(ctx);
412558
return do_sys_exit_pwrite((void *)ctx, bytes_count);
413559
}
414560

415-
KRETPROG(do_pwritev) (struct pt_regs *ctx) {
561+
KRETPROG(do_pwritev) (struct pt_regs * ctx) {
416562
ssize_t bytes_count = PT_REGS_RC(ctx);
417563
return do_sys_exit_pwrite((void *)ctx, bytes_count);
418564
}
419565
#else
420566
// /sys/kernel/debug/tracing/events/syscalls/sys_exit_pwrite64/format
421-
TP_SYSCALL_PROG(exit_pwrite64) (struct syscall_comm_exit_ctx *ctx) {
567+
TP_SYSCALL_PROG(exit_pwrite64) (struct syscall_comm_exit_ctx * ctx) {
422568
return do_sys_exit_pwrite((void *)ctx, (ssize_t) ctx->ret);
423569
}
424570

425571
// /sys/kernel/debug/tracing/events/syscalls/sys_exit_pwritev/format
426-
TP_SYSCALL_PROG(exit_pwritev) (struct syscall_comm_exit_ctx *ctx) {
572+
TP_SYSCALL_PROG(exit_pwritev) (struct syscall_comm_exit_ctx * ctx) {
427573
return do_sys_exit_pwrite((void *)ctx, (ssize_t) ctx->ret);
428574
}
429575

430576
// /sys/kernel/debug/tracing/events/syscalls/sys_exit_pwritev2/format
431-
TP_SYSCALL_PROG(exit_pwritev2) (struct syscall_comm_exit_ctx *ctx) {
577+
TP_SYSCALL_PROG(exit_pwritev2) (struct syscall_comm_exit_ctx * ctx) {
432578
return do_sys_exit_pwrite((void *)ctx, (ssize_t) ctx->ret);
433579
}
434580
#endif /* SUPPORTS_KPROBE_ONLY */

agent/src/ebpf/kernel/include/socket_trace_common.h

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,16 @@
2626

2727
#include "../config.h"
2828

29+
#define INVALID_OFFSET 0xFFFF
30+
31+
// Structure used to store kernel mount information for adaptation purposes.
32+
// Helps to infer kernel structure offsets for different kernel versions.
33+
struct adapt_kern_data {
34+
__u64 id; // Combined identifier, e.g., {tgid, pid} of the process
35+
int mnt_id; // Mount ID corresponding to the file
36+
__u32 mntns_id; // Mount namespace ID corresponding to the file
37+
};
38+
2939
enum endpoint_role {
3040
ROLE_UNKNOWN,
3141
ROLE_CLIENT,
@@ -234,6 +244,11 @@ struct __io_event_buffer {
234244
// The number of bytes of offset within the file content
235245
__u64 offset;
236246

247+
// Mount ID of the file’s mount
248+
int mnt_id;
249+
// Mount namespace ID of the file’s mount
250+
__u32 mntns_id;
251+
237252
// filename length
238253
__u32 len;
239254

@@ -260,6 +275,8 @@ struct user_io_event_buffer {
260275
char mount_source[MOUNT_SOURCE_SZ];
261276
char mount_point[MOUNT_POINT_SZ];
262277
char file_dir[FILE_PATH_SZ];
278+
int mnt_id;
279+
__u32 mntns_id;
263280
} __attribute__ ((packed));
264281

265282
// struct ebpf_proc_info -> offsets[] arrays index.
@@ -344,7 +361,9 @@ struct member_fields_offset {
344361
__u8 ready;
345362
__u8 kprobe_invalid:1; // This indicates that the KPROBE feature has been disabled.
346363
__u8 enable_unix_socket:1; // Enable flag for Unix socket tracing
347-
__u8 reserved:6;
364+
__u8 files_infer_done:1; // 0: file-related structure offset inference not completed
365+
// 1: file-related structure offset inference completed
366+
__u8 reserved:5;
348367
__u16 struct_dentry_d_parent_offset; // offsetof(struct dentry, d_parent)
349368
__u32 task__files_offset;
350369
__u32 sock__flags_offset;
@@ -369,6 +388,17 @@ struct member_fields_offset {
369388
__u32 struct_sock_sport_offset; // offsetof(struct sock_common, skc_num)
370389
__u32 struct_sock_skc_state_offset; // offsetof(struct sock_common, skc_state)
371390
__u32 struct_sock_common_ipv6only_offset; // offsetof(struct sock_common, skc_flags)
391+
392+
/*
393+
* Mount information related offsets
394+
*/
395+
__u16 struct_file_f_path_offset; // offsetof(struct file, f_path)
396+
__u16 struct_path_mnt_offset; // offsetof(struct path, mnt)
397+
__u16 struct_mount_mnt_offset; // offsetof(struct mount, mnt)
398+
__u16 struct_mount_mnt_ns_offset; // offsetof(struct mount, mnt_ns)
399+
__u16 struct_mnt_namespace_ns_offset; // offsetof(struct mnt_namespace, ns)
400+
__u16 struct_ns_common_inum_offset; // offsetof(struct mnt_common, inum)
401+
__u16 struct_mount_mnt_id_offset; // offsetof(struct mount, mnt_id)
372402
};
373403

374404
typedef struct member_fields_offset bpf_offset_param_t;

0 commit comments

Comments
 (0)