Rough solution for Tasks 1, 2, 3

main.go:

	- reading /proc
	- iteration over entries in NNN/maps
	- filter glob-search for "*python3*" in pathname
	- find symbol and its offset in pathnanme
	- calculate offset in memory
	- add pid and offset to map

	TODO: encapsulating this into a module

ebpf.go:

	- added type MapFD int, changing all function on a FD to methods
	  This allows us to enrich the data type going forward

	- added bpf_update_elem() from the manpage ebpf2.
	  .updateElement() is the verbatim wrapper to it.

	- added .Add/.Change/.Set methods, which call .updateElement
	  with specific flags

	TODO: re-implement ebpf.go with pure go, using direct syscalls.
This commit is contained in:
Özgür Kesim 2020-01-15 19:04:56 +01:00
parent 64f54c622d
commit a9f0f27ee2
2 changed files with 59 additions and 15 deletions

View File

@ -20,8 +20,8 @@ static int bpf_create_map(
{ {
union bpf_attr attr = { union bpf_attr attr = {
.map_type = map_type, .map_type = map_type,
.key_size = key_size, .key_size = key_size,
.value_size = value_size, .value_size = value_size,
.max_entries = max_entries}; .max_entries = max_entries};
return bpf(BPF_MAP_CREATE, &attr, sizeof(attr)); return bpf(BPF_MAP_CREATE, &attr, sizeof(attr));
@ -31,8 +31,8 @@ int bpf_get_next_key(int fd, const void *key, void *next_key)
{ {
union bpf_attr attr = { union bpf_attr attr = {
.map_fd = fd, .map_fd = fd,
.key = (__u64) (unsigned long) key, .key = (__u64) (unsigned long) key,
.next_key = (__u64) (unsigned long) next_key}; .next_key = (__u64) (unsigned long) next_key};
return bpf(BPF_MAP_GET_NEXT_KEY, &attr, sizeof(attr)); return bpf(BPF_MAP_GET_NEXT_KEY, &attr, sizeof(attr));
} }
@ -41,15 +41,25 @@ int bpf_get_next_key(int fd, const void *key, void *next_key)
int bpf_lookup_elem(int fd, const void *key, void *value) int bpf_lookup_elem(int fd, const void *key, void *value)
{ {
union bpf_attr attr = { union bpf_attr attr = {
.map_fd = fd, .map_fd = fd,
.key = (__u64) (unsigned long) key, .key = (__u64) (unsigned long) key,
.value = (__u64) (unsigned long) value, .value = (__u64) (unsigned long) value,
}; };
return bpf(BPF_MAP_LOOKUP_ELEM, &attr, sizeof(attr)); return bpf(BPF_MAP_LOOKUP_ELEM, &attr, sizeof(attr));
} }
// TODO: add insert int bpf_update_elem(int fd, const void *key, const void *value, uint64_t flags)
{
union bpf_attr attr = {
.map_fd = fd,
.key = (__u64) (unsigned long) key,
.value = (__u64) (unsigned long) value,
.flags = flags,
};
return bpf(BPF_MAP_UPDATE_ELEM, &attr, sizeof(attr));
}
*/ */
import "C" import "C"
@ -59,18 +69,26 @@ import (
"unsafe" "unsafe"
) )
type MapFD int
// CreateMap creates an eBPF map from int->uint64. The file descriptor of the // CreateMap creates an eBPF map from int->uint64. The file descriptor of the
// created map is returned. // created map is returned.
func CreateMap() (int, error) { func CreateMap() (MapFD, error) {
mapFD := C.bpf_create_map(C.BPF_MAP_TYPE_HASH, 4, 8, 64)
return int(mapFD), nil r, errno := C.bpf_create_map(C.BPF_MAP_TYPE_HASH, 4, 8, 64)
if r == -1 {
return MapFD(r), fmt.Errorf("bpf_create_map with errno %d", errno)
}
return MapFD(r), nil
} }
// GetMap gets the key/value pairs from the specified eBPF map as a Go map // GetMap gets the key/value pairs from the specified eBPF map as a Go map
func GetMap(mapFD int) (map[int]uint64, error) { func (mfd MapFD) GetMap() (map[int]uint64, error) {
retMap := make(map[int]uint64) retMap := make(map[int]uint64)
mapFDC := C.int(mapFD) mapFDC := C.int(mfd)
keyC := C.int(0) keyC := C.int(0)
nextKeyC := C.int(0) nextKeyC := C.int(0)
valC := C.uint64_t(0) valC := C.uint64_t(0)
@ -94,3 +112,29 @@ func GetMap(mapFD int) (map[int]uint64, error) {
keyC = nextKeyC keyC = nextKeyC
} }
} }
func (mfd MapFD) Add(key int, value uint64) error {
return mfd.updateElement(key, value, C.BPF_NOEXIST)
}
func (mfd MapFD) Change(key int, value uint64) error {
return mfd.updateElement(key, value, C.BPF_EXIST)
}
func (mfd MapFD) Set(key int, value uint64) error {
return mfd.updateElement(key, value, C.BPF_ANY)
}
func (mfd MapFD) updateElement(key int, value uint64, flag C.uint64_t) error {
r, errno := C.bpf_update_elem(C.int(mfd),
unsafe.Pointer(&key),
unsafe.Pointer(&value),
flag)
if r == -1 {
return fmt.Errorf("bpf_update_elem failed with errno %d", errno)
}
return nil
}

View File

@ -56,11 +56,11 @@ func main() {
} else if offset, found := searchSymbolIn(pid_s, "*python3*", "_PyRuntime"); !found { } else if offset, found := searchSymbolIn(pid_s, "*python3*", "_PyRuntime"); !found {
continue continue
} else { } else {
fmt.Printf("going to set (%d -> 0x%x)\n\n", pid, offset) mapFD.Add(pid, uint64(offset))
} }
} }
mapContents, err := ebpf.GetMap(mapFD) mapContents, err := mapFD.GetMap()
if err != nil { if err != nil {
fmt.Printf("Failed to get the map contents: %s", err) fmt.Printf("Failed to get the map contents: %s", err)
os.Exit(1) os.Exit(1)
@ -161,7 +161,7 @@ func searchSymbolIn(pid, glob, symbol string) (offset uint64, ok bool) {
memoff := sym.Value - section.Addr + alignedOffset(section) memoff := sym.Value - section.Addr + alignedOffset(section)
fmt.Printf("pid: %s\nsym: %#v\nsection: %#v\nmemoff: 0x%x\narange: %#v\nmap-fileoffset: 0x%x\npathname: %s\n", pid, sym, section, memoff, arange, fileoffset, pathname) // fmt.Printf("pid: %s\nsym: %#v\nsection: %#v\nmemoff: 0x%x\narange: %#v\nmap-fileoffset: 0x%x\npathname: %s\n", pid, sym, section, memoff, arange, fileoffset, pathname)
// stop when only _one_ is found // stop when only _one_ is found
return arange.start + memoff - fileoffset, true return arange.start + memoff - fileoffset, true