1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
|
package link
import (
"fmt"
"unsafe"
"github.com/cilium/ebpf"
"github.com/cilium/ebpf/internal/sys"
)
// QueryOptions defines additional parameters when querying for programs.
type QueryOptions struct {
// Target to query. This is usually a file descriptor but may refer to
// something else based on the attach type.
Target int
// Attach specifies the AttachType of the programs queried for
Attach ebpf.AttachType
// QueryFlags are flags for BPF_PROG_QUERY, e.g. BPF_F_QUERY_EFFECTIVE
QueryFlags uint32
}
// QueryResult describes which programs and links are active.
type QueryResult struct {
// List of attached programs.
Programs []AttachedProgram
// Incremented by one every time the set of attached programs changes.
// May be zero if not supported by the [ebpf.AttachType].
Revision uint64
}
// HaveLinkInfo returns true if the kernel supports querying link information
// for a particular [ebpf.AttachType].
func (qr *QueryResult) HaveLinkInfo() bool {
return qr.Revision > 0
}
type AttachedProgram struct {
ID ebpf.ProgramID
linkID ID
}
// LinkID returns the ID associated with the program.
//
// Returns 0, false if the kernel doesn't support retrieving the ID or if the
// program wasn't attached via a link. See [QueryResult.HaveLinkInfo] if you
// need to tell the two apart.
func (ap *AttachedProgram) LinkID() (ID, bool) {
return ap.linkID, ap.linkID != 0
}
// QueryPrograms retrieves a list of programs for the given AttachType.
//
// Returns a slice of attached programs, which may be empty.
// revision counts how many times the set of attached programs has changed and
// may be zero if not supported by the [ebpf.AttachType].
// Returns ErrNotSupportd on a kernel without BPF_PROG_QUERY
func QueryPrograms(opts QueryOptions) (*QueryResult, error) {
// query the number of programs to allocate correct slice size
attr := sys.ProgQueryAttr{
TargetFdOrIfindex: uint32(opts.Target),
AttachType: sys.AttachType(opts.Attach),
QueryFlags: opts.QueryFlags,
}
err := sys.ProgQuery(&attr)
if err != nil {
if haveFeatErr := haveProgQuery(); haveFeatErr != nil {
return nil, fmt.Errorf("query programs: %w", haveFeatErr)
}
return nil, fmt.Errorf("query programs: %w", err)
}
if attr.Count == 0 {
return &QueryResult{Revision: attr.Revision}, nil
}
// The minimum bpf_mprog revision is 1, so we can use the field to detect
// whether the attach type supports link ids.
haveLinkIDs := attr.Revision != 0
count := attr.Count
progIds := make([]ebpf.ProgramID, count)
attr = sys.ProgQueryAttr{
TargetFdOrIfindex: uint32(opts.Target),
AttachType: sys.AttachType(opts.Attach),
QueryFlags: opts.QueryFlags,
Count: count,
ProgIds: sys.NewPointer(unsafe.Pointer(&progIds[0])),
}
var linkIds []ID
if haveLinkIDs {
linkIds = make([]ID, count)
attr.LinkIds = sys.NewPointer(unsafe.Pointer(&linkIds[0]))
}
if err := sys.ProgQuery(&attr); err != nil {
return nil, fmt.Errorf("query programs: %w", err)
}
// NB: attr.Count might have changed between the two syscalls.
var programs []AttachedProgram
for i, id := range progIds[:attr.Count] {
ap := AttachedProgram{ID: id}
if haveLinkIDs {
ap.linkID = linkIds[i]
}
programs = append(programs, ap)
}
return &QueryResult{programs, attr.Revision}, nil
}
|