Skip to content

Commit

Permalink
link: move BPF_PROG_ATTACH support into new package
Browse files Browse the repository at this point in the history
Program.Attach and Program.Detach are low level interfaces: the semantics
of flags, etc. depends on the kind of program you are trying to attach.
At the same time they are useful as an escape hatch for features that
the library doesn't yet support with a nicer API.

Move the two functions into a separate link package, which will
contain other primitives that handle attaching programs to various
hooks. Future proof the API by using argument structs.
  • Loading branch information
lmb committed Jun 15, 2020
1 parent 66c46bf commit 5727d65
Show file tree
Hide file tree
Showing 9 changed files with 220 additions and 52 deletions.
24 changes: 24 additions & 0 deletions internal/syscall.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,27 @@ func BPF(cmd BPFCmd, attr unsafe.Pointer, size uintptr) (uintptr, error) {

return r1, err
}

type BPFProgAttachAttr struct {
TargetFd uint32
AttachBpfFd uint32
AttachType uint32
AttachFlags uint32
ReplaceBpfFd uint32
}

func BPFProgAttach(attr *BPFProgAttachAttr) error {
_, err := BPF(BPF_PROG_ATTACH, unsafe.Pointer(attr), unsafe.Sizeof(*attr))
return err
}

type BPFProgDetachAttr struct {
TargetFd uint32
AttachBpfFd uint32
AttachType uint32
}

func BPFProgDetach(attr *BPFProgDetachAttr) error {
_, err := BPF(BPF_PROG_DETACH, unsafe.Pointer(attr), unsafe.Sizeof(*attr))
return err
}
1 change: 1 addition & 0 deletions internal/unix/types_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const (
EINVAL = linux.EINVAL
EPOLLIN = linux.EPOLLIN
EINTR = linux.EINTR
EPERM = linux.EPERM
ESRCH = linux.ESRCH
ENODEV = linux.ENODEV
BPF_F_RDONLY_PROG = linux.BPF_F_RDONLY_PROG
Expand Down
1 change: 1 addition & 0 deletions internal/unix/types_other.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const (
ENOSPC = syscall.ENOSPC
EINVAL = syscall.EINVAL
EINTR = syscall.EINTR
EPERM = syscall.EPERM
ESRCH = syscall.ESRCH
ENODEV = syscall.ENODEV
BPF_F_RDONLY_PROG = 0
Expand Down
76 changes: 76 additions & 0 deletions link/program.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package link

import (
"github.com/cilium/ebpf"
"github.com/cilium/ebpf/internal"

"golang.org/x/xerrors"
)

type RawAttachProgramOptions struct {
// File descriptor to attach to. This differs for each attach type.
Target int
// Program to attach.
Program *ebpf.Program
// Program to replace (cgroups).
Replace *ebpf.Program
// Attach must match the attach type of Program (and Replace).
Attach ebpf.AttachType
// Flags control the attach behaviour. This differs for each attach type.
Flags uint32
}

// RawAttachProgram is a low level wrapper around BPF_PROG_ATTACH.
//
// You should use one of the higher level abstractions available in this
// package if possible.
func RawAttachProgram(opts RawAttachProgramOptions) error {
if err := haveProgAttach(); err != nil {
return err
}

var replaceFd uint32
if opts.Replace != nil {
replaceFd = uint32(opts.Replace.FD())
}

attr := internal.BPFProgAttachAttr{
TargetFd: uint32(opts.Target),
AttachBpfFd: uint32(opts.Program.FD()),
ReplaceBpfFd: replaceFd,
AttachType: uint32(opts.Attach),
AttachFlags: uint32(opts.Flags),
}

if err := internal.BPFProgAttach(&attr); err != nil {
return xerrors.Errorf("can't attach program: %s", err)
}
return nil
}

type RawDetachProgramOptions struct {
Target int
Program *ebpf.Program
Attach ebpf.AttachType
}

// RawDetachProgram is a low level wrapper around BPF_PROG_DETACH.
//
// You should use one of the higher level abstractions available in this
// package if possible.
func RawDetachProgram(opts RawDetachProgramOptions) error {
if err := haveProgAttach(); err != nil {
return err
}

attr := internal.BPFProgDetachAttr{
TargetFd: uint32(opts.Target),
AttachBpfFd: uint32(opts.Program.FD()),
AttachType: uint32(opts.Attach),
}
if err := internal.BPFProgDetach(&attr); err != nil {
return xerrors.Errorf("can't detach program: %s", err)
}

return nil
}
58 changes: 58 additions & 0 deletions link/program_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package link

import (
"testing"

"github.com/cilium/ebpf"
"github.com/cilium/ebpf/asm"
"github.com/cilium/ebpf/internal/testutils"
)

func TestProgramAlter(t *testing.T) {
testutils.SkipOnOldKernel(t, "4.13", "SkSKB type")

var err error
var prog *ebpf.Program
prog, err = ebpf.NewProgram(&ebpf.ProgramSpec{
Type: ebpf.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 *ebpf.Map
sockMap, err = ebpf.NewMap(&ebpf.MapSpec{
Type: ebpf.MapType(15), // BPF_MAP_TYPE_SOCKMAP
KeySize: 4,
ValueSize: 4,
MaxEntries: 2,
})
if err != nil {
t.Fatal(err)
}
defer sockMap.Close()

err = RawAttachProgram(RawAttachProgramOptions{
Target: sockMap.FD(),
Program: prog,
Attach: ebpf.AttachSkSKBStreamParser,
})
if err != nil {
t.Fatal(err)
}

err = RawDetachProgram(RawDetachProgramOptions{
Target: sockMap.FD(),
Program: prog,
Attach: ebpf.AttachSkSKBStreamParser,
})
if err != nil {
t.Fatal(err)
}
}
28 changes: 28 additions & 0 deletions link/syscalls.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package link

import (
"github.com/cilium/ebpf"
"github.com/cilium/ebpf/asm"
"github.com/cilium/ebpf/internal"
)

var haveProgAttach = internal.FeatureTest("BPF_PROG_ATTACH", "4.10", func() (bool, error) {
prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{
Type: ebpf.CGroupSKB,
AttachType: ebpf.AttachCGroupInetIngress,
License: "MIT",
Instructions: asm.Instructions{
asm.Mov.Imm(asm.R0, 0),
asm.Return(),
},
})
if err != nil {
return false, nil
}

// BPF_PROG_ATTACH was introduced at the same time as CGgroupSKB,
// so being able to load the program is enough to infer that we
// have the syscall.
prog.Close()
return true, nil
})
11 changes: 11 additions & 0 deletions link/syscalls_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package link

import (
"testing"

"github.com/cilium/ebpf/internal/testutils"
)

func TestHaveProgAttach(t *testing.T) {
testutils.CheckFeatureTest(t, haveProgAttach)
}
35 changes: 21 additions & 14 deletions prog.go
Original file line number Diff line number Diff line change
Expand Up @@ -454,7 +454,9 @@ func (p *Program) MarshalBinary() ([]byte, error) {
return buf, nil
}

// Attach a Program to a container object fd
// Attach a Program.
//
// Deprecated: use link.RawAttachProgram instead.
func (p *Program) Attach(fd int, typ AttachType, flags AttachFlags) error {
if fd < 0 {
return xerrors.New("invalid fd")
Expand All @@ -465,35 +467,40 @@ func (p *Program) Attach(fd int, typ AttachType, flags AttachFlags) error {
return err
}

attr := bpfProgAlterAttr{
targetFd: uint32(fd),
attachBpfFd: pfd,
attachType: uint32(typ),
attachFlags: uint32(flags),
attr := internal.BPFProgAttachAttr{
TargetFd: uint32(fd),
AttachBpfFd: pfd,
AttachType: uint32(typ),
AttachFlags: uint32(flags),
}

return bpfProgAlter(internal.BPF_PROG_ATTACH, &attr)
return internal.BPFProgAttach(&attr)
}

// Detach a Program from a container object fd
// Detach a Program.
//
// Deprecated: use link.RawDetachProgram instead.
func (p *Program) Detach(fd int, typ AttachType, flags AttachFlags) error {
if fd < 0 {
return xerrors.New("invalid fd")
}

if flags != 0 {
return xerrors.New("flags must be zero")
}

pfd, err := p.fd.Value()
if err != nil {
return err
}

attr := bpfProgAlterAttr{
targetFd: uint32(fd),
attachBpfFd: pfd,
attachType: uint32(typ),
attachFlags: uint32(flags),
attr := internal.BPFProgDetachAttr{
TargetFd: uint32(fd),
AttachBpfFd: pfd,
AttachType: uint32(typ),
}

return bpfProgAlter(internal.BPF_PROG_DETACH, &attr)
return internal.BPFProgDetach(&attr)
}

// LoadPinnedProgram loads a Program from a BPF file.
Expand Down
38 changes: 0 additions & 38 deletions prog_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -388,44 +388,6 @@ func TestProgramFromFD(t *testing.T) {
prog2.Close()
}

func TestProgramAlter(t *testing.T) {
testutils.SkipOnOldKernel(t, "4.13", "SkSKB type")

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 TestHaveProgTestRun(t *testing.T) {
testutils.CheckFeatureTest(t, haveProgTestRun)
}
Expand Down

0 comments on commit 5727d65

Please sign in to comment.