Skip to content

Commit 57e8868

Browse files
committed
Include namespace inode numbers on process events
This diff adds the inode number of each namespace the process belongs to, to process events. Two namespaces were excluded: pid_for_children and time_for_children, see namespaces(7). The pid namespace is a bit special as it's not in nsproxy, the user facing pid level is the last/deepest. There was an inclusion of mnt ns, that is not used by beats or endpoint, I kept it but made it fetch from the new "path". While here, add support for changing the compiler in gen_initramfs.sh by setting CC. While Fedora includes a ${arch}-linux-gnu-gcc, it doesn't include matching headers, so I can't really compile the tests locally.
1 parent 05ce927 commit 57e8868

File tree

8 files changed

+142
-18
lines changed

8 files changed

+142
-18
lines changed

GPL/Events/EbpfEventProto.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,16 @@ struct ebpf_file_info {
157157
uint64_t ctime;
158158
} __attribute__((packed));
159159

160+
struct ebpf_namespace_info {
161+
uint32_t uts_inonum;
162+
uint32_t ipc_inonum;
163+
uint32_t mnt_inonum;
164+
uint32_t net_inonum;
165+
uint32_t cgroup_inonum;
166+
uint32_t time_inonum;
167+
uint32_t pid_inonum;
168+
} __attribute__((packed));
169+
160170
// Full events follow
161171
struct ebpf_file_delete_event {
162172
struct ebpf_event_header hdr;
@@ -223,6 +233,7 @@ struct ebpf_process_fork_event {
223233
struct ebpf_cred_info creds;
224234
struct ebpf_tty_dev ctty;
225235
char comm[TASK_COMM_LEN];
236+
struct ebpf_namespace_info ns;
226237

227238
// Variable length fields: pids_ss_cgroup_path
228239
struct ebpf_varlen_fields_start vl_fields;
@@ -238,6 +249,7 @@ struct ebpf_process_exec_event {
238249
struct ebpf_cred_info creds;
239250
struct ebpf_tty_dev ctty;
240251
char comm[TASK_COMM_LEN];
252+
struct ebpf_namespace_info ns;
241253
uint32_t inode_nlink;
242254
uint32_t flags;
243255

@@ -251,6 +263,7 @@ struct ebpf_process_exit_event {
251263
struct ebpf_cred_info creds;
252264
struct ebpf_tty_dev ctty;
253265
char comm[TASK_COMM_LEN];
266+
struct ebpf_namespace_info ns;
254267
int32_t exit_code;
255268

256269
// Variable length fields: pids_ss_cgroup_path

GPL/Events/File/Probe.bpf.c

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,6 @@ DECL_FUNC_ARG_EXISTS(vfs_rename, rd);
3131
DECL_FUNC_ARG(do_truncate, filp);
3232
DECL_FUNC_RET(do_truncate);
3333

34-
static int mntns(const struct task_struct *task)
35-
{
36-
return BPF_CORE_READ(task, nsproxy, mnt_ns, ns.inum);
37-
}
38-
3934
static int do_unlinkat__enter()
4035
{
4136
struct ebpf_events_state state = {};
@@ -129,9 +124,11 @@ static int vfs_unlink__exit(int ret)
129124
ebpf_cred_info__fill(&event->creds, task);
130125

131126
struct path p;
132-
p.dentry = &state->unlink.de;
133-
p.mnt = state->unlink.mnt;
134-
event->mntns = mntns(task);
127+
p.dentry = &state->unlink.de;
128+
p.mnt = state->unlink.mnt;
129+
struct ebpf_namespace_info ns;
130+
ebpf_ns__fill(&ns, task);
131+
event->mntns = ns.mnt_inonum;
135132
bpf_get_current_comm(event->comm, TASK_COMM_LEN);
136133
ebpf_file_info__fill(&event->finfo, p.dentry);
137134

@@ -236,7 +233,9 @@ static void prepare_and_send_file_event(struct file *f,
236233
struct path p = BPF_CORE_READ(f, f_path);
237234
ebpf_pid_info__fill(&event->pids, task);
238235
ebpf_cred_info__fill(&event->creds, task);
239-
event->mntns = mntns(task);
236+
struct ebpf_namespace_info ns;
237+
ebpf_ns__fill(&ns, task);
238+
event->mntns = ns.mnt_inonum;
240239
bpf_get_current_comm(event->comm, TASK_COMM_LEN);
241240
ebpf_file_info__fill(&event->finfo, p.dentry);
242241

@@ -489,7 +488,9 @@ static int vfs_rename__exit(int ret)
489488
event->hdr.ts_boot = bpf_ktime_get_boot_ns_helper();
490489
ebpf_pid_info__fill(&event->pids, task);
491490
ebpf_cred_info__fill(&event->creds, task);
492-
event->mntns = mntns(task);
491+
struct ebpf_namespace_info ns;
492+
ebpf_ns__fill(&ns, task);
493+
event->mntns = ns.mnt_inonum;
493494
bpf_get_current_comm(event->comm, TASK_COMM_LEN);
494495
ebpf_file_info__fill(&event->finfo, de);
495496

@@ -559,7 +560,9 @@ static void file_modify_event__emit(enum ebpf_file_change_type typ, struct path
559560
event->change_type = typ;
560561
ebpf_pid_info__fill(&event->pids, task);
561562
ebpf_cred_info__fill(&event->creds, task);
562-
event->mntns = mntns(task);
563+
struct ebpf_namespace_info ns;
564+
ebpf_ns__fill(&ns, task);
565+
event->mntns = ns.mnt_inonum;
563566
bpf_get_current_comm(event->comm, TASK_COMM_LEN);
564567
struct dentry *d = BPF_CORE_READ(path, dentry);
565568
ebpf_file_info__fill(&event->finfo, d);

GPL/Events/Helpers.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,27 @@ static void ebpf_comm__fill(char *comm, size_t len, const struct task_struct *ta
306306
read_kernel_str_or_empty_str(comm, len, BPF_CORE_READ(task, comm));
307307
}
308308

309+
static void ebpf_ns__fill(struct ebpf_namespace_info *nsi, const struct task_struct *task)
310+
{
311+
struct pid *pid;
312+
int pid_level;
313+
314+
nsi->uts_inonum = BPF_CORE_READ(task, nsproxy, uts_ns, ns.inum);
315+
nsi->ipc_inonum = BPF_CORE_READ(task, nsproxy, ipc_ns, ns.inum);
316+
nsi->mnt_inonum = BPF_CORE_READ(task, nsproxy, mnt_ns, ns.inum);
317+
nsi->net_inonum = BPF_CORE_READ(task, nsproxy, net_ns, ns.inum);
318+
nsi->cgroup_inonum = BPF_CORE_READ(task, nsproxy, cgroup_ns, ns.inum);
319+
nsi->time_inonum = BPF_CORE_READ(task, nsproxy, time_ns, ns.inum);
320+
321+
pid = BPF_CORE_READ(task, thread_pid);
322+
if (pid == NULL) {
323+
nsi->pid_inonum = 0;
324+
return;
325+
}
326+
pid_level = BPF_CORE_READ(pid, level);
327+
nsi->pid_inonum = BPF_CORE_READ(pid, numbers[pid_level].ns, ns.inum);
328+
}
329+
309330
struct {
310331
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
311332
__type(key, u32);

GPL/Events/Process/Probe.bpf.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ int BPF_PROG(sched_process_fork, const struct task_struct *parent, const struct
6060
ebpf_cred_info__fill(&event->creds, parent);
6161
ebpf_ctty__fill(&event->ctty, child);
6262
ebpf_comm__fill(event->comm, sizeof(event->comm), child);
63+
ebpf_ns__fill(&event->ns, child);
6364

6465
// Variable length fields
6566
ebpf_vl_fields__init(&event->vl_fields);
@@ -111,6 +112,7 @@ int BPF_PROG(sched_process_exec,
111112
ebpf_cred_info__fill(&event->creds, task);
112113
ebpf_ctty__fill(&event->ctty, task);
113114
ebpf_comm__fill(event->comm, sizeof(event->comm), task);
115+
ebpf_ns__fill(&event->ns, task);
114116

115117
// set setuid and setgid flags
116118
struct file *f = BPF_CORE_READ(binprm, file);
@@ -211,6 +213,7 @@ static int taskstats_exit__enter(const struct task_struct *task, int group_dead)
211213
ebpf_cred_info__fill(&event->creds, task);
212214
ebpf_ctty__fill(&event->ctty, task);
213215
ebpf_comm__fill(event->comm, sizeof(event->comm), task);
216+
ebpf_ns__fill(&event->ns, task);
214217

215218
// Variable length fields
216219
ebpf_vl_fields__init(&event->vl_fields);

non-GPL/Events/EventsTrace/EventsTrace.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,26 @@ static void out_file_info(const char *name, struct ebpf_file_info *finfo)
414414
out_object_end();
415415
}
416416

417+
static void out_ns_info(const char *name, struct ebpf_namespace_info *ns)
418+
{
419+
printf("\"%s\":", name);
420+
out_object_start();
421+
out_uint("uts", ns->uts_inonum);
422+
out_comma();
423+
out_uint("ipc", ns->ipc_inonum);
424+
out_comma();
425+
out_uint("mnt", ns->mnt_inonum);
426+
out_comma();
427+
out_uint("net", ns->net_inonum);
428+
out_comma();
429+
out_uint("cgroup", ns->cgroup_inonum);
430+
out_comma();
431+
out_uint("time", ns->time_inonum);
432+
out_comma();
433+
out_uint("pid", ns->pid_inonum);
434+
out_object_end();
435+
}
436+
417437
static void out_null_delimited_string_array(const char *name, char *buf, size_t buf_size)
418438
{
419439
// buf is an array (argv, env etc.) with multiple values delimited by a '\0'
@@ -768,6 +788,9 @@ static void out_process_fork(struct ebpf_process_fork_event *evt)
768788
out_comma();
769789

770790
out_string("comm", evt->comm);
791+
out_comma();
792+
793+
out_ns_info("ns", &evt->ns);
771794

772795
struct ebpf_varlen_field *field;
773796
FOR_EACH_VARLEN_FIELD(evt->vl_fields, field)

testing/scripts/gen_initramfs.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ build_testbins() {
5656
for c_src in *.c; do
5757
local bin_path=bin/$arch/$(basename $c_src .c)
5858

59-
${arch}-linux-gnu-gcc -g -static $c_src -o $bin_path \
59+
${CC-${arch}-linux-gnu-gcc} -g -static $c_src -o $bin_path \
6060
|| exit_error "compilation of $c_src for $arch failed (see above)"
6161
done
6262

testing/testrunner/ebpf_test.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,11 @@ func ForkExit(t *testing.T, et *Runner) {
9494
require.Equal(t, forkEvent.ChildPids.Sid, forkEvent.ParentPids.Sid)
9595
require.Equal(t, forkEvent.ChildPids.Pgid, forkEvent.ParentPids.Pgid)
9696
require.NotEqual(t, forkEvent.ChildPids.Tgid, forkEvent.ParentPids.Tgid)
97+
98+
// Check if all namespace values match /proc/self/ns/*
99+
ns, err := FetchNsFromProc()
100+
require.NoError(t, err)
101+
require.Equal(t, forkEvent.Ns, ns)
97102
}
98103

99104
func ForkExec(t *testing.T, et *Runner) {
@@ -154,7 +159,6 @@ func ForkExec(t *testing.T, et *Runner) {
154159
require.Equal(t, execEvent.Env[0], "TEST_ENV_KEY1=TEST_ENV_VAL1")
155160
require.Equal(t, execEvent.Env[1], "TEST_ENV_KEY2=TEST_ENV_VAL2")
156161
require.Equal(t, execEvent.Cwd, "/")
157-
158162
}
159163

160164
func FileCreate(t *testing.T, et *Runner) {
@@ -185,7 +189,6 @@ func FileCreate(t *testing.T, et *Runner) {
185189
}
186190

187191
func FileDelete(t *testing.T, et *Runner) {
188-
189192
var binOutput struct {
190193
PidInfo TestPidInfo `json:"pid_info"`
191194
FileNameOrig string `json:"filename_orig"`
@@ -426,7 +429,6 @@ func Tcpv4ConnectionAttempt(t *testing.T, et *Runner) {
426429
require.Equal(t, ev.Net.DestPort, binOutput.ServerPort)
427430
require.Equal(t, ev.Net.NetNs, binOutput.NetNs)
428431
require.Equal(t, ev.Comm, "tcpv4_connect")
429-
430432
}
431433

432434
func Tcpv4ConnectionAccept(t *testing.T, et *Runner) {
@@ -665,5 +667,4 @@ func TestEbpf(t *testing.T) {
665667
run.Stop()
666668
})
667669
}
668-
669670
}

testing/testrunner/utils.go

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"os/exec"
1919
"path/filepath"
2020
"runtime"
21+
"strconv"
2122
"strings"
2223
"testing"
2324

@@ -90,11 +91,22 @@ type FileInfo struct {
9091
Ctime uint64 `json:"ctime"`
9192
}
9293

94+
type NsInfo struct {
95+
Uts uint32 `json:"uts"`
96+
Ipc uint32 `json:"ipc"`
97+
Mnt uint32 `json:"mnt"`
98+
Net uint32 `json:"net"`
99+
Cgroup uint32 `json:"cgroup"`
100+
Time uint32 `json:"time"`
101+
Pid uint32 `json:"pid"`
102+
}
103+
93104
type ProcessForkEvent struct {
94105
ParentPids PidInfo `json:"parent_pids"`
95106
ChildPids PidInfo `json:"child_pids"`
96107
Creds CredInfo `json:"creds"`
97108
Ctty TtyInfo `json:"ctty"`
109+
Ns NsInfo `json:"ns"`
98110
}
99111

100112
type ProcessExecEvent struct {
@@ -195,8 +207,10 @@ var testBinaryPath = "/"
195207
var eventsTracePath = "/EventsTrace"
196208

197209
// Path to the TC filter test binary and probe. This one is weird and lives outside the rest of the test binaries
198-
var tcTestPath = "/BPFTcFilterTests"
199-
var tcObjPath = "/TcFilter.bpf.o"
210+
var (
211+
tcTestPath = "/BPFTcFilterTests"
212+
tcObjPath = "/TcFilter.bpf.o"
213+
)
200214

201215
// init will run at startup and figure out if we're running in the bluebox test env or not,
202216
// and set paths for the binaries as needed
@@ -327,3 +341,49 @@ func PrintDebugOutputOnFail() {
327341

328342
fmt.Println("BPF test failed, see errors and stacktrace above")
329343
}
344+
345+
func FetchNsFromProc() (NsInfo, error) {
346+
var ns NsInfo
347+
348+
fetch := func(name string, dst *uint32) error {
349+
s, err := os.Readlink("/proc/self/ns/" + name)
350+
if err != nil {
351+
return err
352+
}
353+
start := strings.IndexByte(s, '[')
354+
if start == -1 {
355+
return fmt.Errorf("`[` not found for ns %s", name)
356+
}
357+
start++
358+
end := strings.IndexByte(s, ']')
359+
if end == -1 {
360+
return fmt.Errorf("`]` not found for ns %s", name)
361+
}
362+
v, err := strconv.Atoi(s[start:end])
363+
if err != nil {
364+
return err
365+
}
366+
*dst = uint32(v)
367+
return nil
368+
}
369+
370+
calls := []struct {
371+
name string
372+
dst *uint32
373+
}{
374+
{"uts", &ns.Uts},
375+
{"ipc", &ns.Ipc},
376+
{"mnt", &ns.Mnt},
377+
{"net", &ns.Net},
378+
{"cgroup", &ns.Cgroup},
379+
{"time", &ns.Time},
380+
{"pid", &ns.Pid},
381+
}
382+
for _, call := range calls {
383+
if err := fetch(call.name, call.dst); err != nil {
384+
return ns, err
385+
}
386+
}
387+
388+
return ns, nil
389+
}

0 commit comments

Comments
 (0)