Repository: cilium/ebpf
Pull request #15 (2019-10-11 15:41:23)

map, program: allow creation from raw FD
BPF maps and programs can be sent around via SCM_RIGHTS, so users should
be able to interact with them.

lmb: I removed the "name" argument, since we should be able to infer it from the fd itself.

Patch:
diff --git a/abi.go b/abi.go
index 7dd017e..b164fab 100644
--- a/abi.go
+++ b/abi.go
@@ -105,9 +105,13 @@ func newProgramABIFromFd(fd *bpfFD) (*ProgramABI, error) {
 		return nil, err
 	}
 
+	return newProgramABIFromInfo(info), nil
+}
+
+func newProgramABIFromInfo(info *bpfProgInfo) *ProgramABI {
 	return &ProgramABI{
 		Type: ProgramType(info.progType),
-	}, nil
+	}
 }
 
 // Check verifies that a Program conforms to the ABI.
diff --git a/map.go b/map.go
index 4b2b986..376c968 100644
--- a/map.go
+++ b/map.go
@@ -55,6 +55,23 @@ type Map struct {
 	fullValueSize int
 }
 
+// NewMapFromFD creates a map from a raw fd.
+//
+// You should not use fd after calling this function.
+func NewMapFromFD(fd int) (*Map, error) {
+	if fd < 0 {
+		return nil, errors.New("invalid fd")
+	}
+	bpfFd := newBPFFD(uint32(fd))
+
+	abi, err := newMapABIFromFd(bpfFd)
+	if err != nil {
+		bpfFd.forget()
+		return nil, err
+	}
+	return newMap(bpfFd, abi)
+}
+
 // NewMap creates a new Map.
 //
 // Creating a map for the first time will perform feature detection
diff --git a/prog.go b/prog.go
index 9f020a9..d9888a6 100644
--- a/prog.go
+++ b/prog.go
@@ -136,6 +136,31 @@ func NewProgramWithOptions(spec *ProgramSpec, opts ProgramOptions) (*Program, er
 	return nil, &loadError{err, logs}
 }
 
+// NewProgramFromFD creates a program from a raw fd.
+//
+// You should not use fd after calling this function.
+func NewProgramFromFD(fd int) (*Program, error) {
+	if fd < 0 {
+		return nil, errors.New("invalid fd")
+	}
+	bpfFd := newBPFFD(uint32(fd))
+
+	info, err := bpfGetProgInfoByFD(bpfFd)
+	if err != nil {
+		bpfFd.forget()
+		return nil, err
+	}
+
+	var name string
+	if bpfName := convertCString(info.name[:]); bpfName != "" {
+		name = bpfName
+	} else {
+		name = convertCString(info.tag[:])
+	}
+
+	return newProgram(bpfFd, name, newProgramABIFromInfo(info)), nil
+}
+
 func newProgram(fd *bpfFD, name string, abi *ProgramABI) *Program {
 	return &Program{
 		name: name,
diff --git a/syscalls.go b/syscalls.go
index 011e3af..6c9b26f 100644
--- a/syscalls.go
+++ b/syscalls.go
@@ -45,10 +45,14 @@ func (fd *bpfFD) close() error {
 	value := int(fd.raw)
 	fd.raw = -1
 
-	runtime.SetFinalizer(fd, nil)
+	fd.forget()
 	return unix.Close(value)
 }
 
+func (fd *bpfFD) forget() {
+	runtime.SetFinalizer(fd, nil)
+}
+
 func (fd *bpfFD) dup() (*bpfFD, error) {
 	if fd.raw < 0 {
 		return nil, errClosedFd


Test patch:
diff --git a/map_test.go b/map_test.go
index 29ce568..41e8bd6 100644
--- a/map_test.go
+++ b/map_test.go
@@ -501,6 +501,29 @@ func TestMapName(t *testing.T) {
 	}
 }
 
+func TestMapFromFD(t *testing.T) {
+	m := createArray(t)
+	if err := m.Put(uint32(0), uint32(123)); err != nil {
+		t.Fatal(err)
+	}
+
+	// If you're thinking about copying this, don't. Use
+	// Clone() instead.
+	m2, err := NewMapFromFD(m.FD())
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	var val uint32
+	if err := m2.Lookup(uint32(0), &val); err != nil {
+		t.Fatal("Can't look up key:", err)
+	}
+
+	if val != 123 {
+		t.Error("Wrong value")
+	}
+}
+
 type benchValue struct {
 	ID      uint32
 	Val16   uint16
diff --git a/prog_test.go b/prog_test.go
index e25e6d4..0939bae 100644
--- a/prog_test.go
+++ b/prog_test.go
@@ -236,6 +236,30 @@ func TestProgramMarshaling(t *testing.T) {
 	}
 }
 
+func TestProgramFromFD(t *testing.T) {
+	prog, err := NewProgram(&ProgramSpec{
+		Type: SocketFilter,
+		Instructions: asm.Instructions{
+			asm.LoadImm(asm.R0, 0, asm.DWord),
+			asm.Return(),
+		},
+		License: "MIT",
+	})
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer prog.Close()
+
+	// If you're thinking about copying this, don't. Use
+	// Clone() instead.
+	prog2, err := NewProgramFromFD(prog.FD())
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	prog2.Close()
+}
+
 func createProgramArray(t *testing.T) *Map {
 	t.Helper()