208 lines
4.0 KiB
Go
208 lines
4.0 KiB
Go
package ebpf
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"syscall"
|
|
|
|
"github.com/cilium/ebpf/internal"
|
|
|
|
"golang.org/x/xerrors"
|
|
)
|
|
|
|
// MapABI are the attributes of a Map which are available across all supported kernels.
|
|
type MapABI struct {
|
|
Type MapType
|
|
KeySize uint32
|
|
ValueSize uint32
|
|
MaxEntries uint32
|
|
Flags uint32
|
|
}
|
|
|
|
func newMapABIFromSpec(spec *MapSpec) *MapABI {
|
|
return &MapABI{
|
|
spec.Type,
|
|
spec.KeySize,
|
|
spec.ValueSize,
|
|
spec.MaxEntries,
|
|
spec.Flags,
|
|
}
|
|
}
|
|
|
|
func newMapABIFromFd(fd *internal.FD) (string, *MapABI, error) {
|
|
info, err := bpfGetMapInfoByFD(fd)
|
|
if err != nil {
|
|
if xerrors.Is(err, syscall.EINVAL) {
|
|
abi, err := newMapABIFromProc(fd)
|
|
return "", abi, err
|
|
}
|
|
return "", nil, err
|
|
}
|
|
|
|
return "", &MapABI{
|
|
MapType(info.mapType),
|
|
info.keySize,
|
|
info.valueSize,
|
|
info.maxEntries,
|
|
info.flags,
|
|
}, nil
|
|
}
|
|
|
|
func newMapABIFromProc(fd *internal.FD) (*MapABI, error) {
|
|
var abi MapABI
|
|
err := scanFdInfo(fd, map[string]interface{}{
|
|
"map_type": &abi.Type,
|
|
"key_size": &abi.KeySize,
|
|
"value_size": &abi.ValueSize,
|
|
"max_entries": &abi.MaxEntries,
|
|
"map_flags": &abi.Flags,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &abi, nil
|
|
}
|
|
|
|
// Equal returns true if two ABIs have the same values.
|
|
func (abi *MapABI) Equal(other *MapABI) bool {
|
|
switch {
|
|
case abi.Type != other.Type:
|
|
return false
|
|
case abi.KeySize != other.KeySize:
|
|
return false
|
|
case abi.ValueSize != other.ValueSize:
|
|
return false
|
|
case abi.MaxEntries != other.MaxEntries:
|
|
return false
|
|
case abi.Flags != other.Flags:
|
|
return false
|
|
default:
|
|
return true
|
|
}
|
|
}
|
|
|
|
// ProgramABI are the attributes of a Program which are available across all supported kernels.
|
|
type ProgramABI struct {
|
|
Type ProgramType
|
|
}
|
|
|
|
func newProgramABIFromSpec(spec *ProgramSpec) *ProgramABI {
|
|
return &ProgramABI{
|
|
spec.Type,
|
|
}
|
|
}
|
|
|
|
func newProgramABIFromFd(fd *internal.FD) (string, *ProgramABI, error) {
|
|
info, err := bpfGetProgInfoByFD(fd)
|
|
if err != nil {
|
|
if xerrors.Is(err, syscall.EINVAL) {
|
|
return newProgramABIFromProc(fd)
|
|
}
|
|
|
|
return "", nil, err
|
|
}
|
|
|
|
var name string
|
|
if bpfName := internal.CString(info.name[:]); bpfName != "" {
|
|
name = bpfName
|
|
} else {
|
|
name = internal.CString(info.tag[:])
|
|
}
|
|
|
|
return name, &ProgramABI{
|
|
Type: ProgramType(info.progType),
|
|
}, nil
|
|
}
|
|
|
|
func newProgramABIFromProc(fd *internal.FD) (string, *ProgramABI, error) {
|
|
var (
|
|
abi ProgramABI
|
|
name string
|
|
)
|
|
|
|
err := scanFdInfo(fd, map[string]interface{}{
|
|
"prog_type": &abi.Type,
|
|
"prog_tag": &name,
|
|
})
|
|
if xerrors.Is(err, errMissingFields) {
|
|
return "", nil, &internal.UnsupportedFeatureError{
|
|
Name: "reading ABI from /proc/self/fdinfo",
|
|
MinimumVersion: internal.Version{4, 11, 0},
|
|
}
|
|
}
|
|
if err != nil {
|
|
return "", nil, err
|
|
}
|
|
|
|
return name, &abi, nil
|
|
}
|
|
|
|
func scanFdInfo(fd *internal.FD, fields map[string]interface{}) error {
|
|
raw, err := fd.Value()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
fh, err := os.Open(fmt.Sprintf("/proc/self/fdinfo/%d", raw))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer fh.Close()
|
|
|
|
if err := scanFdInfoReader(fh, fields); err != nil {
|
|
return xerrors.Errorf("%s: %w", fh.Name(), err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
var errMissingFields = xerrors.New("missing fields")
|
|
|
|
func scanFdInfoReader(r io.Reader, fields map[string]interface{}) error {
|
|
var (
|
|
scanner = bufio.NewScanner(r)
|
|
scanned int
|
|
)
|
|
|
|
for scanner.Scan() {
|
|
parts := bytes.SplitN(scanner.Bytes(), []byte("\t"), 2)
|
|
if len(parts) != 2 {
|
|
continue
|
|
}
|
|
|
|
name := bytes.TrimSuffix(parts[0], []byte(":"))
|
|
field, ok := fields[string(name)]
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
if n, err := fmt.Fscanln(bytes.NewReader(parts[1]), field); err != nil || n != 1 {
|
|
return xerrors.Errorf("can't parse field %s: %v", name, err)
|
|
}
|
|
|
|
scanned++
|
|
}
|
|
|
|
if err := scanner.Err(); err != nil {
|
|
return err
|
|
}
|
|
|
|
if scanned != len(fields) {
|
|
return errMissingFields
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Equal returns true if two ABIs have the same values.
|
|
func (abi *ProgramABI) Equal(other *ProgramABI) bool {
|
|
switch {
|
|
case abi.Type != other.Type:
|
|
return false
|
|
default:
|
|
return true
|
|
}
|
|
}
|