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  }