-
Notifications
You must be signed in to change notification settings - Fork 375
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Mahe Tardy <mahe.tardy@gmail.com>
- Loading branch information
Showing
3 changed files
with
226 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) | ||
/* Copyright Authors of Cilium */ | ||
|
||
#include "vmlinux.h" | ||
|
||
#include "bpf_tracing.h" // bpf_printk | ||
|
||
#include "bpf_task.h" | ||
#include "process/bpf_process_event.h" | ||
|
||
char _license[] __attribute__((section("license"), used)) = "Dual BSD/GPL"; | ||
|
||
#define MAX_BUF_LEN 4096 | ||
|
||
struct test_prepend_name_state_map_value { | ||
char buf[MAX_BUF_LEN]; | ||
u64 buflen; | ||
char dname[MAX_BUF_LEN]; | ||
u32 dlen; | ||
u32 offset; | ||
}; | ||
|
||
struct { | ||
__uint(type, BPF_MAP_TYPE_ARRAY); | ||
__uint(max_entries, 1); | ||
__type(key, int); | ||
__type(value, struct test_prepend_name_state_map_value); | ||
} test_prepend_name_state_map SEC(".maps"); | ||
|
||
__attribute__((section("raw_tracepoint/test"), used)) int | ||
test_prepend_name() | ||
{ | ||
struct test_prepend_name_state_map_value *ts; | ||
int zero = 0; | ||
|
||
ts = map_lookup_elem(&test_prepend_name_state_map, &zero); | ||
if (!ts) | ||
return 1; | ||
|
||
if (ts->buflen < 0 || ts->buflen > 256) | ||
return 2; | ||
|
||
char *bufptr = ts->buf + ts->buflen; | ||
|
||
// bpf_printk("buf: 0x%x, bufptr: 0x%x", ts->buf, bufptr); | ||
// bpf_printk("buffer: %s", ts->buf); | ||
// bpf_printk("buflen: %d", ts->buflen); | ||
// bpf_printk("dentry->name: %s", ts->dname); | ||
// bpf_printk("dentry->len: %d", ts->dlen); | ||
|
||
ts->dlen &= 255; | ||
|
||
int ret = prepend_name((char *) &ts->buf, &bufptr, (int *) &ts->buflen, ts->dname, ts->dlen); | ||
|
||
ts->offset = bufptr - (char *)&ts->buf; | ||
|
||
return ret; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
package bpf | ||
|
||
import ( | ||
"bytes" | ||
"errors" | ||
"slices" | ||
"strings" | ||
"testing" | ||
|
||
"github.com/cilium/ebpf" | ||
"github.com/cilium/tetragon/pkg/testutils" | ||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
const ( | ||
// those constants must be synchronized with the BPF code | ||
MAX_BUF_LEN = 4096 | ||
testPrependNameStateMapName = "test_prepend_name_state_map" | ||
programName = "test_prepend_name" | ||
|
||
bufLen = 10 | ||
) | ||
|
||
var ( | ||
zero uint32 = 0 | ||
) | ||
|
||
type testPrependNameStateMapValue struct { | ||
Buf [MAX_BUF_LEN]byte | ||
Buflen uint64 | ||
Dname [MAX_BUF_LEN]byte | ||
Dlen uint32 | ||
Offset uint32 | ||
} | ||
|
||
func updateDentry(t *testing.T, stateMap *ebpf.Map, dentry string) { | ||
var state testPrependNameStateMapValue | ||
|
||
err := stateMap.Lookup(&zero, &state) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
dentryName := [MAX_BUF_LEN]byte{} | ||
length := copy(dentryName[:], []byte(dentry)) | ||
if length != len(dentry) { | ||
t.Fatalf("dentry buffer is too small for string: %s", dentry) | ||
} | ||
|
||
state.Dname = dentryName | ||
state.Dlen = uint32(length) | ||
|
||
err = stateMap.Update(&zero, &state, ebpf.UpdateAny) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
} | ||
|
||
func Test_PrependName(t *testing.T) { | ||
// load test program | ||
coll, err := ebpf.LoadCollection(testutils.RepoRootPath("bpf/objs/bpf_prepend_name_test.o")) | ||
if err != nil { | ||
var ve *ebpf.VerifierError | ||
if errors.As(err, &ve) { | ||
t.Logf("Verifier error: %+v\n", ve) | ||
} | ||
t.Fatal(err) | ||
} | ||
defer coll.Close() | ||
|
||
// get ref to objects | ||
prog, ok := coll.Programs[programName] | ||
if !ok { | ||
t.Fatalf("%s not found", programName) | ||
} | ||
stateMap := coll.Maps[testPrependNameStateMapName] | ||
if stateMap == nil { | ||
t.Fatalf("%s not found", testPrependNameStateMapName) | ||
} | ||
var state testPrependNameStateMapValue | ||
|
||
// reset the test state map | ||
resetState := func(buflen int) { | ||
state = testPrependNameStateMapValue{ | ||
Buf: [MAX_BUF_LEN]byte{}, | ||
Buflen: uint64(buflen), | ||
Dname: [MAX_BUF_LEN]byte{}, | ||
Dlen: 0, | ||
} | ||
err = stateMap.Update(&zero, &state, ebpf.UpdateAny) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
} | ||
|
||
// runPrependName BPF code | ||
runPrependName := func() int { | ||
code, err := prog.Run(&ebpf.RunOptions{}) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
return int(code) | ||
} | ||
|
||
// simulate a dentry walk on path | ||
testPath := func(path string) { | ||
// simulate dentries walk | ||
dentries := strings.Split(path, "/") | ||
if len(dentries) > 0 && strings.HasPrefix(path, "/") { | ||
dentries = dentries[1:] | ||
} | ||
slices.Reverse(dentries) // walk from local to root | ||
for _, dentry := range dentries { | ||
// update dentry | ||
updateDentry(t, stateMap, dentry) | ||
|
||
// run prepend_name | ||
code := runPrependName() | ||
if code != 0 { | ||
t.Fatalf("unexpected return code: %d", code) | ||
} | ||
} | ||
} | ||
|
||
// this should be a method on state I guess or the buffer | ||
bufferToString := func() string { | ||
err = stateMap.Lookup(&zero, &state) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
return string(bytes.TrimRight(state.Buf[state.Offset:], "\x00")) | ||
} | ||
|
||
logBuffer := func() { | ||
err = stateMap.Lookup(&zero, &state) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
t.Log(string(state.Buf[:])) | ||
} | ||
|
||
t.Run("ExactBufferSize", func(t *testing.T) { | ||
resetState(len("/usr/bin/cat")) | ||
|
||
updateDentry(t, stateMap, "cat") | ||
code := runPrependName() | ||
assert.Equal(t, 0, code) | ||
assert.Equal(t, "/cat", bufferToString()) | ||
|
||
updateDentry(t, stateMap, "bin") | ||
code = runPrependName() | ||
assert.Equal(t, 0, code) | ||
assert.Equal(t, "/bin/cat", bufferToString()) | ||
|
||
updateDentry(t, stateMap, "usr") | ||
code = runPrependName() | ||
assert.Equal(t, 0, code) | ||
assert.Equal(t, "/usr/bin/cat", bufferToString()) | ||
}) | ||
|
||
t.Run("BufferTooSmall", func(t *testing.T) { | ||
resetState(10) | ||
testPath("/home/mahe/recipes/pizza") | ||
logBuffer() | ||
}) | ||
|
||
} |