2020-08-10 17:43:49 +00:00
|
|
|
package internal
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"path/filepath"
|
|
|
|
"runtime"
|
2021-07-02 08:43:15 +00:00
|
|
|
"syscall"
|
2020-08-10 17:43:49 +00:00
|
|
|
"unsafe"
|
|
|
|
|
|
|
|
"github.com/cilium/ebpf/internal/unix"
|
|
|
|
)
|
|
|
|
|
|
|
|
//go:generate stringer -output syscall_string.go -type=BPFCmd
|
|
|
|
|
|
|
|
// BPFCmd identifies a subcommand of the bpf syscall.
|
|
|
|
type BPFCmd int
|
|
|
|
|
|
|
|
// Well known BPF commands.
|
|
|
|
const (
|
|
|
|
BPF_MAP_CREATE BPFCmd = iota
|
|
|
|
BPF_MAP_LOOKUP_ELEM
|
|
|
|
BPF_MAP_UPDATE_ELEM
|
|
|
|
BPF_MAP_DELETE_ELEM
|
|
|
|
BPF_MAP_GET_NEXT_KEY
|
|
|
|
BPF_PROG_LOAD
|
|
|
|
BPF_OBJ_PIN
|
|
|
|
BPF_OBJ_GET
|
|
|
|
BPF_PROG_ATTACH
|
|
|
|
BPF_PROG_DETACH
|
|
|
|
BPF_PROG_TEST_RUN
|
|
|
|
BPF_PROG_GET_NEXT_ID
|
|
|
|
BPF_MAP_GET_NEXT_ID
|
|
|
|
BPF_PROG_GET_FD_BY_ID
|
|
|
|
BPF_MAP_GET_FD_BY_ID
|
|
|
|
BPF_OBJ_GET_INFO_BY_FD
|
|
|
|
BPF_PROG_QUERY
|
|
|
|
BPF_RAW_TRACEPOINT_OPEN
|
|
|
|
BPF_BTF_LOAD
|
|
|
|
BPF_BTF_GET_FD_BY_ID
|
|
|
|
BPF_TASK_FD_QUERY
|
|
|
|
BPF_MAP_LOOKUP_AND_DELETE_ELEM
|
|
|
|
BPF_MAP_FREEZE
|
|
|
|
BPF_BTF_GET_NEXT_ID
|
|
|
|
BPF_MAP_LOOKUP_BATCH
|
|
|
|
BPF_MAP_LOOKUP_AND_DELETE_BATCH
|
|
|
|
BPF_MAP_UPDATE_BATCH
|
|
|
|
BPF_MAP_DELETE_BATCH
|
|
|
|
BPF_LINK_CREATE
|
|
|
|
BPF_LINK_UPDATE
|
|
|
|
BPF_LINK_GET_FD_BY_ID
|
|
|
|
BPF_LINK_GET_NEXT_ID
|
|
|
|
BPF_ENABLE_STATS
|
|
|
|
BPF_ITER_CREATE
|
|
|
|
)
|
|
|
|
|
|
|
|
// BPF wraps SYS_BPF.
|
|
|
|
//
|
|
|
|
// Any pointers contained in attr must use the Pointer type from this package.
|
|
|
|
func BPF(cmd BPFCmd, attr unsafe.Pointer, size uintptr) (uintptr, error) {
|
|
|
|
r1, _, errNo := unix.Syscall(unix.SYS_BPF, uintptr(cmd), uintptr(attr), size)
|
|
|
|
runtime.KeepAlive(attr)
|
|
|
|
|
|
|
|
var err error
|
|
|
|
if errNo != 0 {
|
2021-07-02 08:43:15 +00:00
|
|
|
err = wrappedErrno{errNo}
|
2020-08-10 17:43:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2021-04-14 18:11:13 +00:00
|
|
|
type BPFEnableStatsAttr struct {
|
|
|
|
StatsType uint32
|
|
|
|
}
|
|
|
|
|
|
|
|
func BPFEnableStats(attr *BPFEnableStatsAttr) (*FD, error) {
|
|
|
|
ptr, err := BPF(BPF_ENABLE_STATS, unsafe.Pointer(attr), unsafe.Sizeof(*attr))
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("enable stats: %w", err)
|
|
|
|
}
|
|
|
|
return NewFD(uint32(ptr)), nil
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-08-10 17:43:49 +00:00
|
|
|
type bpfObjAttr struct {
|
|
|
|
fileName Pointer
|
|
|
|
fd uint32
|
|
|
|
fileFlags uint32
|
|
|
|
}
|
|
|
|
|
|
|
|
const bpfFSType = 0xcafe4a11
|
|
|
|
|
|
|
|
// BPFObjPin wraps BPF_OBJ_PIN.
|
|
|
|
func BPFObjPin(fileName string, fd *FD) error {
|
|
|
|
dirName := filepath.Dir(fileName)
|
|
|
|
var statfs unix.Statfs_t
|
|
|
|
if err := unix.Statfs(dirName, &statfs); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if uint64(statfs.Type) != bpfFSType {
|
|
|
|
return fmt.Errorf("%s is not on a bpf filesystem", fileName)
|
|
|
|
}
|
|
|
|
|
|
|
|
value, err := fd.Value()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
attr := bpfObjAttr{
|
|
|
|
fileName: NewStringPointer(fileName),
|
|
|
|
fd: value,
|
|
|
|
}
|
|
|
|
_, err = BPF(BPF_OBJ_PIN, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("pin object %s: %w", fileName, err)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// BPFObjGet wraps BPF_OBJ_GET.
|
2021-04-14 18:11:13 +00:00
|
|
|
func BPFObjGet(fileName string, flags uint32) (*FD, error) {
|
2020-08-10 17:43:49 +00:00
|
|
|
attr := bpfObjAttr{
|
2021-04-14 18:11:13 +00:00
|
|
|
fileName: NewStringPointer(fileName),
|
|
|
|
fileFlags: flags,
|
2020-08-10 17:43:49 +00:00
|
|
|
}
|
|
|
|
ptr, err := BPF(BPF_OBJ_GET, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("get object %s: %w", fileName, err)
|
|
|
|
}
|
|
|
|
return NewFD(uint32(ptr)), nil
|
|
|
|
}
|
2021-04-14 18:11:13 +00:00
|
|
|
|
|
|
|
type bpfObjGetInfoByFDAttr struct {
|
|
|
|
fd uint32
|
|
|
|
infoLen uint32
|
|
|
|
info Pointer
|
|
|
|
}
|
|
|
|
|
|
|
|
// BPFObjGetInfoByFD wraps BPF_OBJ_GET_INFO_BY_FD.
|
|
|
|
//
|
|
|
|
// Available from 4.13.
|
|
|
|
func BPFObjGetInfoByFD(fd *FD, info unsafe.Pointer, size uintptr) error {
|
|
|
|
value, err := fd.Value()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
attr := bpfObjGetInfoByFDAttr{
|
|
|
|
fd: value,
|
|
|
|
infoLen: uint32(size),
|
|
|
|
info: NewPointer(info),
|
|
|
|
}
|
|
|
|
_, err = BPF(BPF_OBJ_GET_INFO_BY_FD, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("fd %v: %w", fd, err)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2021-07-02 08:43:15 +00:00
|
|
|
|
|
|
|
// BPFObjName is a null-terminated string made up of
|
|
|
|
// 'A-Za-z0-9_' characters.
|
|
|
|
type BPFObjName [unix.BPF_OBJ_NAME_LEN]byte
|
|
|
|
|
|
|
|
// NewBPFObjName truncates the result if it is too long.
|
|
|
|
func NewBPFObjName(name string) BPFObjName {
|
|
|
|
var result BPFObjName
|
|
|
|
copy(result[:unix.BPF_OBJ_NAME_LEN-1], name)
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
|
|
|
type BPFMapCreateAttr struct {
|
|
|
|
MapType uint32
|
|
|
|
KeySize uint32
|
|
|
|
ValueSize uint32
|
|
|
|
MaxEntries uint32
|
|
|
|
Flags uint32
|
|
|
|
InnerMapFd uint32 // since 4.12 56f668dfe00d
|
|
|
|
NumaNode uint32 // since 4.14 96eabe7a40aa
|
|
|
|
MapName BPFObjName // since 4.15 ad5b177bd73f
|
|
|
|
MapIfIndex uint32
|
|
|
|
BTFFd uint32
|
|
|
|
BTFKeyTypeID uint32
|
|
|
|
BTFValueTypeID uint32
|
|
|
|
}
|
|
|
|
|
|
|
|
func BPFMapCreate(attr *BPFMapCreateAttr) (*FD, error) {
|
|
|
|
fd, err := BPF(BPF_MAP_CREATE, unsafe.Pointer(attr), unsafe.Sizeof(*attr))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return NewFD(uint32(fd)), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// wrappedErrno wraps syscall.Errno to prevent direct comparisons with
|
|
|
|
// syscall.E* or unix.E* constants.
|
|
|
|
//
|
|
|
|
// You should never export an error of this type.
|
|
|
|
type wrappedErrno struct {
|
|
|
|
syscall.Errno
|
|
|
|
}
|
|
|
|
|
|
|
|
func (we wrappedErrno) Unwrap() error {
|
|
|
|
return we.Errno
|
|
|
|
}
|
|
|
|
|
|
|
|
type syscallError struct {
|
|
|
|
error
|
|
|
|
errno syscall.Errno
|
|
|
|
}
|
|
|
|
|
|
|
|
func SyscallError(err error, errno syscall.Errno) error {
|
|
|
|
return &syscallError{err, errno}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (se *syscallError) Is(target error) bool {
|
|
|
|
return target == se.error
|
|
|
|
}
|
|
|
|
|
|
|
|
func (se *syscallError) Unwrap() error {
|
|
|
|
return se.errno
|
|
|
|
}
|