Repository: cilium/ebpf
Pull request #540 (2021-12-29 18:27:57)

Support attaching SocketFilter programs to raw sockets
Adds support for attaching Socket Filter programs to raw sockets. It would look like this:

```go
link.SocketFilterAttachProgram(conn, objs.Filter)

link.SocketFilterDetachProgram(conn)
```

Any feedback is welcome. I'll also provide an example.

folbricht: I've been using the stdlib to open the raw socket (similar to the example above). If I understand your suggestion correctly, I should:
- Wrap the FD into a syscall.Conn which would prevent direct access to it
- Add a couple of functions to attach/detach. As for naming them, how does `RawAttachSocketFilter(syscall.Conn, *ebpf.Program)` sound?
lmb: > Add a couple of functions to attach/detach. 

Yes, without introducing a new link type though. So "replacing linkSocketFilter with two free function" is what I'd like to do.

Can you add a test as well please?
folbricht: Replaced the Link interface implementation with two functions, and added a simple test (which just checks that attaching/detaching) works. Using a full raw socket involves a bit more code to implement syscall.Conn but I could do that in an example.
lmb: Renamed the free functions to be closer to other functions in the package.

Patch:
diff --git a/internal/unix/types_linux.go b/internal/unix/types_linux.go
index cbfbde3..0a7c648 100644
--- a/internal/unix/types_linux.go
+++ b/internal/unix/types_linux.go
@@ -66,6 +66,9 @@ const (
 	PERF_RECORD_SAMPLE       = linux.PERF_RECORD_SAMPLE
 	AT_FDCWD                 = linux.AT_FDCWD
 	RENAME_NOREPLACE         = linux.RENAME_NOREPLACE
+	SO_ATTACH_BPF            = linux.SO_ATTACH_BPF
+	SO_DETACH_BPF            = linux.SO_DETACH_BPF
+	SOL_SOCKET               = linux.SOL_SOCKET
 )
 
 // Statfs_t is a wrapper
diff --git a/internal/unix/types_other.go b/internal/unix/types_other.go
index 0374317..abd8ea9 100644
--- a/internal/unix/types_other.go
+++ b/internal/unix/types_other.go
@@ -68,6 +68,9 @@ const (
 	PERF_RECORD_SAMPLE       = 9
 	AT_FDCWD                 = -0x2
 	RENAME_NOREPLACE         = 0x1
+	SO_ATTACH_BPF            = 0x32
+	SO_DETACH_BPF            = 0x1b
+	SOL_SOCKET               = 0x1
 )
 
 // Statfs_t is a wrapper
diff --git a/link/socket_filter.go b/link/socket_filter.go
new file mode 100644
index 0000000..94f3958
--- /dev/null
+++ b/link/socket_filter.go
@@ -0,0 +1,40 @@
+package link
+
+import (
+	"syscall"
+
+	"github.com/cilium/ebpf"
+	"github.com/cilium/ebpf/internal/unix"
+)
+
+// AttachSocketFilter attaches a SocketFilter BPF program to a socket.
+func AttachSocketFilter(conn syscall.Conn, program *ebpf.Program) error {
+	rawConn, err := conn.SyscallConn()
+	if err != nil {
+		return err
+	}
+	var ssoErr error
+	err = rawConn.Control(func(fd uintptr) {
+		ssoErr = syscall.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_ATTACH_BPF, program.FD())
+	})
+	if ssoErr != nil {
+		return ssoErr
+	}
+	return err
+}
+
+// DetachSocketFilter detaches a SocketFilter BPF program from a socket.
+func DetachSocketFilter(conn syscall.Conn) error {
+	rawConn, err := conn.SyscallConn()
+	if err != nil {
+		return err
+	}
+	var ssoErr error
+	err = rawConn.Control(func(fd uintptr) {
+		ssoErr = syscall.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_DETACH_BPF, 0)
+	})
+	if ssoErr != nil {
+		return ssoErr
+	}
+	return err
+}


Test patch:
diff --git a/link/socket_filter_test.go b/link/socket_filter_test.go
new file mode 100644
index 0000000..a3279a2
--- /dev/null
+++ b/link/socket_filter_test.go
@@ -0,0 +1,28 @@
+package link
+
+import (
+	"net"
+	"testing"
+
+	"github.com/cilium/ebpf"
+)
+
+func TestSocketFilterAttach(t *testing.T) {
+	prog := mustLoadProgram(t, ebpf.SocketFilter, 0, "")
+
+	defer prog.Close()
+
+	conn, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1)})
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer conn.Close()
+
+	if err := AttachSocketFilter(conn, prog); err != nil {
+		t.Fatal(err)
+	}
+
+	if err := DetachSocketFilter(conn); err != nil {
+		t.Fatal(err)
+	}
+}