Repository: cilium/ebpf
Pull request #25 (2019-10-24 12:46:40)

Add prog Attach/Detach
Signed-off-by: Lehner Florian <dev@der-flo.net>

This PR adds the syscall wrappers to attach/detach a program as mentioned in https://github.com/cilium/ebpf/issues/22.

florianl: In the updated PR I've addressed the issues as suggested.

For the test, I'm still trying to find a solution. Most use cases of BPF_PROG_ATTACH I'm aware of, are in combination with cgroups. But I'm not sure if it is good to add such a dependency just for tests.
florianl: The just added test case I heavily inspired by https://blog.cloudflare.com/sockmap-tcp-splicing-of-the-future/. 

Patch:
diff --git a/prog.go b/prog.go
index d9888a6..03b24fb 100644
--- a/prog.go
+++ b/prog.go
@@ -413,6 +413,48 @@ func (p *Program) MarshalBinary() ([]byte, error) {
 	return buf, nil
 }
 
+// Attach a Program to a container object fd
+func (p *Program) Attach(fd int, typ AttachType, flags AttachFlags) error {
+	if fd < 0 {
+		return errors.New("invalid fd")
+	}
+
+	pfd, err := p.fd.value()
+	if err != nil {
+		return err
+	}
+
+	attr := bpfProgAlterAttr{
+		targetFd:    uint32(fd),
+		attachBpfFd: pfd,
+		attachType:  uint32(typ),
+		attachFlags: uint32(flags),
+	}
+
+	return bpfProgAlter(_ProgAttach, &attr)
+}
+
+// Detach a Program from a container object fd
+func (p *Program) Detach(fd int, typ AttachType, flags AttachFlags) error {
+	if fd < 0 {
+		return errors.New("invalid fd")
+	}
+
+	pfd, err := p.fd.value()
+	if err != nil {
+		return err
+	}
+
+	attr := bpfProgAlterAttr{
+		targetFd:    uint32(fd),
+		attachBpfFd: pfd,
+		attachType:  uint32(typ),
+		attachFlags: uint32(flags),
+	}
+
+	return bpfProgAlter(_ProgDetach, &attr)
+}
+
 // LoadPinnedProgram loads a Program from a BPF file.
 //
 // Requires at least Linux 4.13, use LoadPinnedProgramExplicit on
diff --git a/syscalls.go b/syscalls.go
index 65b3ead..68abd3b 100644
--- a/syscalls.go
+++ b/syscalls.go
@@ -173,6 +173,13 @@ type bpfProgTestRunAttr struct {
 	duration    uint32
 }
 
+type bpfProgAlterAttr struct {
+	targetFd    uint32
+	attachBpfFd uint32
+	attachType  uint32
+	attachFlags uint32
+}
+
 type bpfObjGetInfoByFDAttr struct {
 	fd      uint32
 	infoLen uint32
@@ -205,6 +212,11 @@ func bpfProgLoad(attr *bpfProgLoadAttr) (*bpfFD, error) {
 	}
 }
 
+func bpfProgAlter(cmd int, attr *bpfProgAlterAttr) error {
+	_, err := bpfCall(cmd, unsafe.Pointer(attr), unsafe.Sizeof(*attr))
+	return err
+}
+
 func bpfMapCreate(attr *bpfMapCreateAttr) (*bpfFD, error) {
 	fd, err := bpfCall(_MapCreate, unsafe.Pointer(attr), unsafe.Sizeof(*attr))
 	if err != nil {
diff --git a/types.go b/types.go
index c7abb93..0daf9a7 100644
--- a/types.go
+++ b/types.go
@@ -184,3 +184,6 @@ const (
 	AttachCGroupGetsockopt
 	AttachCGroupSetsockopt
 )
+
+// AttachFlags of the eBPF program used in BPF_PROG_ATTACH command
+type AttachFlags uint32


Test patch:
diff --git a/prog_test.go b/prog_test.go
index 0939bae..21d7fea 100644
--- a/prog_test.go
+++ b/prog_test.go
@@ -260,6 +260,42 @@ func TestProgramFromFD(t *testing.T) {
 	prog2.Close()
 }
 
+func TestProgramAlter(t *testing.T) {
+	var err error
+	var prog *Program
+	prog, err = NewProgram(&ProgramSpec{
+		Type: SkSKB,
+		Instructions: asm.Instructions{
+			asm.LoadImm(asm.R0, 0, asm.DWord),
+			asm.Return(),
+		},
+		License: "MIT",
+	})
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer prog.Close()
+
+	var sockMap *Map
+	sockMap, err = NewMap(&MapSpec{
+		Type:       MapType(15), // BPF_MAP_TYPE_SOCKMAP
+		KeySize:    4,
+		ValueSize:  4,
+		MaxEntries: 2,
+	})
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer sockMap.Close()
+
+	if err := prog.Attach(sockMap.FD(), AttachSkSKBStreamParser, AttachFlags(0)); err != nil {
+		t.Fatal(err)
+	}
+	if err := prog.Detach(sockMap.FD(), AttachSkSKBStreamParser, AttachFlags(0)); err != nil {
+		t.Fatal(err)
+	}
+}
+
 func createProgramArray(t *testing.T) *Map {
 	t.Helper()