diff --git a/map-low.go b/map-low.go index 0bc73bbc..20ab6c3d 100644 --- a/map-low.go +++ b/map-low.go @@ -107,6 +107,35 @@ func (m *BPFMapLow) FileDescriptor() int { return m.fd } +func (m *BPFMapLow) ReuseFD(fd int) error { + info, err := GetMapInfoByFD(fd) + if err != nil { + return fmt.Errorf("failed to reuse fd %d: %w", fd, err) + } + + newFD, err := syscall.Open("/", syscall.O_RDONLY|syscall.O_CLOEXEC, 0) + if err != nil { + return fmt.Errorf("failed to reuse fd %d: %w", fd, err) + } + + err = syscall.Dup3(fd, newFD, syscall.O_CLOEXEC) + if err != nil { + _ = syscall.Close(newFD) + return fmt.Errorf("failed to reuse fd %d: %w", fd, err) + } + + err = syscall.Close(m.FileDescriptor()) + if err != nil { + _ = syscall.Close(newFD) + return fmt.Errorf("failed to reuse fd %d: %w", fd, err) + } + + m.fd = newFD + m.info = info + + return nil +} + func (m *BPFMapLow) Name() string { return m.info.Name } diff --git a/map.go b/map.go index 99132f7c..7eb3e7df 100644 --- a/map.go +++ b/map.go @@ -45,9 +45,33 @@ func (m *BPFMap) GetFd() int { return m.FileDescriptor() } -// bpf_map__reuse_fd -// func (m *BPFMap) ReuseFD(fd int) error { -// } +// ReuseFD associates the BPFMap instance with the provided map file descriptor. +// +// This function is useful for reusing a map that was previously created by a +// different process. By passing the file descriptor of the existing map, the +// current BPFMap instance becomes linked to that map. +// +// NOTE: The function closes the current file descriptor associated with the +// BPFMap instance and replaces it with a duplicated descriptor pointing to the +// given fd. As a result, the instance original file descriptor becomes invalid, +// and all associated information is overwritten. +func (m *BPFMap) ReuseFD(fd int) error { + retC := C.bpf_map__reuse_fd(m.bpfMap, C.int(fd)) + if retC < 0 { + return fmt.Errorf("failed to reuse fd %d: %w", fd, syscall.Errno(-retC)) + } + + newFD := m.FileDescriptor() + info, err := GetMapInfoByFD(newFD) + if err != nil { + return err + } + + m.bpfMapLow.fd = newFD + m.bpfMapLow.info = info + + return nil +} func (m *BPFMap) Name() string { return C.GoString(C.bpf_map__name(m.bpfMap)) diff --git a/selftest/reuse-fd/Makefile b/selftest/reuse-fd/Makefile new file mode 120000 index 00000000..d981720c --- /dev/null +++ b/selftest/reuse-fd/Makefile @@ -0,0 +1 @@ +../common/Makefile \ No newline at end of file diff --git a/selftest/reuse-fd/go.mod b/selftest/reuse-fd/go.mod new file mode 100644 index 00000000..1fd0933d --- /dev/null +++ b/selftest/reuse-fd/go.mod @@ -0,0 +1,7 @@ +module github.com/aquasecurity/libbpfgo/selftest/map-update + +go 1.18 + +require github.com/aquasecurity/libbpfgo v0.4.7-libbpf-1.2.0-b2e29a1 + +replace github.com/aquasecurity/libbpfgo => ../../ diff --git a/selftest/reuse-fd/go.sum b/selftest/reuse-fd/go.sum new file mode 100644 index 00000000..c60af667 --- /dev/null +++ b/selftest/reuse-fd/go.sum @@ -0,0 +1,4 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/selftest/reuse-fd/main.bpf.c b/selftest/reuse-fd/main.bpf.c new file mode 100644 index 00000000..29396df0 --- /dev/null +++ b/selftest/reuse-fd/main.bpf.c @@ -0,0 +1,26 @@ +//+build ignore + +#include + +#include + +struct value { + int x; + char y; +}; + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __type(key, u32); + __type(value, struct value); + __uint(max_entries, 1 << 4); +} tester SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __type(key, u32); + __type(value, struct value); + __uint(max_entries, 1 << 4); +} tester_reused SEC(".maps"); + +char LICENSE[] SEC("license") = "Dual BSD/GPL"; diff --git a/selftest/reuse-fd/main.go b/selftest/reuse-fd/main.go new file mode 100644 index 00000000..fdb43646 --- /dev/null +++ b/selftest/reuse-fd/main.go @@ -0,0 +1,91 @@ +package main + +import "C" + +import ( + "bytes" + "log" + "unsafe" + + bpf "github.com/aquasecurity/libbpfgo" +) + +func main() { + bpfModule, err := bpf.NewModuleFromFile("main.bpf.o") + if err != nil { + log.Fatal(err) + } + defer bpfModule.Close() + + bpfModule.BPFLoadObject() + + testerMap, err := bpfModule.GetMap("tester") + if err != nil { + log.Fatal(err) + } + + testerReusedMap, err := bpfModule.GetMap("tester_reused") + if err != nil { + log.Fatal(err) + } + + // + // BPFMap ReuseFD + // + + // The current instance of "tester_reused" will be closed, and testerReusedMap + // will now point to the same map as testerMap "tester", however, via a + // different FD. + err = testerReusedMap.ReuseFD(testerMap.FileDescriptor()) + if err != nil { + log.Fatal(err) + } + + if err != nil { + log.Fatal(err) + } + + key1 := uint32(1) + value1 := struct{ x int }{50} + key1Unsafe := unsafe.Pointer(&key1) + value1Unsafe := unsafe.Pointer(&value1) + err = testerMap.Update(key1Unsafe, value1Unsafe) // update "tester" + if err != nil { + log.Fatal(err) + } + + key2 := int64(42069420) + value2 := []byte{'a', 'b', 'c'} + key2Unsafe := unsafe.Pointer(&key2) + value2Unsafe := unsafe.Pointer(&value2[0]) + err = testerReusedMap.Update(key2Unsafe, value2Unsafe) // also update "tester" + if err != nil { + log.Fatal(err) + } + + // + // BPFMapLow ReuseFD + // + + toReuseCreated, err := bpf.CreateMap(bpf.MapTypeArray, "toreuse", 4, 4, 420, nil) + if err != nil { + log.Fatal(err) + } + + // The current instance of "toreuse" will be closed, and toReuseCreated + // will now point to the same map as testerMap "tester", however, via a + // different FD. + err = toReuseCreated.ReuseFD(testerMap.FileDescriptor()) + if err != nil { + log.Fatal(err) + } + + val2, err := toReuseCreated.GetValue(key2Unsafe) // lookup "tester" + if err != nil { + log.Fatal(err) + } + + if bytes.Equal(val2, value2) { + log.Fatal("wrong value") + } +} diff --git a/selftest/reuse-fd/run.sh b/selftest/reuse-fd/run.sh new file mode 120000 index 00000000..aee911b2 --- /dev/null +++ b/selftest/reuse-fd/run.sh @@ -0,0 +1 @@ +../common/run.sh \ No newline at end of file