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