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 } }