github.com/cilium/ebpf@v0.15.1-0.20240517100537-8079b37aa138/link/link.go (about)

     1  package link
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"os"
     7  
     8  	"github.com/cilium/ebpf"
     9  	"github.com/cilium/ebpf/btf"
    10  	"github.com/cilium/ebpf/internal"
    11  	"github.com/cilium/ebpf/internal/sys"
    12  )
    13  
    14  var ErrNotSupported = internal.ErrNotSupported
    15  
    16  // Link represents a Program attached to a BPF hook.
    17  type Link interface {
    18  	// Replace the current program with a new program.
    19  	//
    20  	// Passing a nil program is an error. May return an error wrapping ErrNotSupported.
    21  	Update(*ebpf.Program) error
    22  
    23  	// Persist a link by pinning it into a bpffs.
    24  	//
    25  	// May return an error wrapping ErrNotSupported.
    26  	Pin(string) error
    27  
    28  	// Undo a previous call to Pin.
    29  	//
    30  	// May return an error wrapping ErrNotSupported.
    31  	Unpin() error
    32  
    33  	// Close frees resources.
    34  	//
    35  	// The link will be broken unless it has been successfully pinned.
    36  	// A link may continue past the lifetime of the process if Close is
    37  	// not called.
    38  	Close() error
    39  
    40  	// Info returns metadata on a link.
    41  	//
    42  	// May return an error wrapping ErrNotSupported.
    43  	Info() (*Info, error)
    44  
    45  	// Prevent external users from implementing this interface.
    46  	isLink()
    47  }
    48  
    49  // NewLinkFromFD creates a link from a raw fd.
    50  //
    51  // Deprecated: use [NewFromFD] instead.
    52  func NewLinkFromFD(fd int) (Link, error) {
    53  	return NewFromFD(fd)
    54  }
    55  
    56  // NewFromFD creates a link from a raw fd.
    57  //
    58  // You should not use fd after calling this function.
    59  func NewFromFD(fd int) (Link, error) {
    60  	sysFD, err := sys.NewFD(fd)
    61  	if err != nil {
    62  		return nil, err
    63  	}
    64  
    65  	return wrapRawLink(&RawLink{fd: sysFD})
    66  }
    67  
    68  // NewFromID returns the link associated with the given id.
    69  //
    70  // Returns ErrNotExist if there is no link with the given id.
    71  func NewFromID(id ID) (Link, error) {
    72  	getFdAttr := &sys.LinkGetFdByIdAttr{Id: id}
    73  	fd, err := sys.LinkGetFdById(getFdAttr)
    74  	if err != nil {
    75  		return nil, fmt.Errorf("get link fd from ID %d: %w", id, err)
    76  	}
    77  
    78  	return wrapRawLink(&RawLink{fd, ""})
    79  }
    80  
    81  // LoadPinnedLink loads a link that was persisted into a bpffs.
    82  func LoadPinnedLink(fileName string, opts *ebpf.LoadPinOptions) (Link, error) {
    83  	raw, err := loadPinnedRawLink(fileName, opts)
    84  	if err != nil {
    85  		return nil, err
    86  	}
    87  
    88  	return wrapRawLink(raw)
    89  }
    90  
    91  // wrap a RawLink in a more specific type if possible.
    92  //
    93  // The function takes ownership of raw and closes it on error.
    94  func wrapRawLink(raw *RawLink) (_ Link, err error) {
    95  	defer func() {
    96  		if err != nil {
    97  			raw.Close()
    98  		}
    99  	}()
   100  
   101  	info, err := raw.Info()
   102  	if err != nil {
   103  		return nil, err
   104  	}
   105  
   106  	switch info.Type {
   107  	case RawTracepointType:
   108  		return &rawTracepoint{*raw}, nil
   109  	case TracingType:
   110  		return &tracing{*raw}, nil
   111  	case CgroupType:
   112  		return &linkCgroup{*raw}, nil
   113  	case IterType:
   114  		return &Iter{*raw}, nil
   115  	case NetNsType:
   116  		return &NetNsLink{*raw}, nil
   117  	case KprobeMultiType:
   118  		return &kprobeMultiLink{*raw}, nil
   119  	case UprobeMultiType:
   120  		return &uprobeMultiLink{*raw}, nil
   121  	case PerfEventType:
   122  		return nil, fmt.Errorf("recovering perf event fd: %w", ErrNotSupported)
   123  	case TCXType:
   124  		return &tcxLink{*raw}, nil
   125  	case NetfilterType:
   126  		return &netfilterLink{*raw}, nil
   127  	case NetkitType:
   128  		return &netkitLink{*raw}, nil
   129  	default:
   130  		return raw, nil
   131  	}
   132  }
   133  
   134  // ID uniquely identifies a BPF link.
   135  type ID = sys.LinkID
   136  
   137  // RawLinkOptions control the creation of a raw link.
   138  type RawLinkOptions struct {
   139  	// File descriptor to attach to. This differs for each attach type.
   140  	Target int
   141  	// Program to attach.
   142  	Program *ebpf.Program
   143  	// Attach must match the attach type of Program.
   144  	Attach ebpf.AttachType
   145  	// BTF is the BTF of the attachment target.
   146  	BTF btf.TypeID
   147  	// Flags control the attach behaviour.
   148  	Flags uint32
   149  }
   150  
   151  // Info contains metadata on a link.
   152  type Info struct {
   153  	Type    Type
   154  	ID      ID
   155  	Program ebpf.ProgramID
   156  	extra   interface{}
   157  }
   158  
   159  type TracingInfo struct {
   160  	AttachType  sys.AttachType
   161  	TargetObjId uint32
   162  	TargetBtfId sys.TypeID
   163  }
   164  
   165  type CgroupInfo struct {
   166  	CgroupId   uint64
   167  	AttachType sys.AttachType
   168  	_          [4]byte
   169  }
   170  
   171  type NetNsInfo struct {
   172  	NetnsIno   uint32
   173  	AttachType sys.AttachType
   174  }
   175  
   176  type TCXInfo struct {
   177  	Ifindex    uint32
   178  	AttachType sys.AttachType
   179  }
   180  
   181  type XDPInfo struct {
   182  	Ifindex uint32
   183  }
   184  
   185  type NetfilterInfo struct {
   186  	Pf       uint32
   187  	Hooknum  uint32
   188  	Priority int32
   189  	Flags    uint32
   190  }
   191  
   192  type NetkitInfo struct {
   193  	Ifindex    uint32
   194  	AttachType sys.AttachType
   195  }
   196  
   197  type KprobeMultiInfo struct {
   198  	count  uint32
   199  	flags  uint32
   200  	missed uint64
   201  }
   202  
   203  // AddressCount is the number of addresses hooked by the kprobe.
   204  func (kpm *KprobeMultiInfo) AddressCount() (uint32, bool) {
   205  	return kpm.count, kpm.count > 0
   206  }
   207  
   208  func (kpm *KprobeMultiInfo) Flags() (uint32, bool) {
   209  	return kpm.flags, kpm.count > 0
   210  }
   211  
   212  func (kpm *KprobeMultiInfo) Missed() (uint64, bool) {
   213  	return kpm.missed, kpm.count > 0
   214  }
   215  
   216  type PerfEventInfo struct {
   217  	Type  sys.PerfEventType
   218  	extra interface{}
   219  }
   220  
   221  func (r *PerfEventInfo) Kprobe() *KprobeInfo {
   222  	e, _ := r.extra.(*KprobeInfo)
   223  	return e
   224  }
   225  
   226  type KprobeInfo struct {
   227  	address uint64
   228  	missed  uint64
   229  }
   230  
   231  func (kp *KprobeInfo) Address() (uint64, bool) {
   232  	return kp.address, kp.address > 0
   233  }
   234  
   235  func (kp *KprobeInfo) Missed() (uint64, bool) {
   236  	return kp.missed, kp.address > 0
   237  }
   238  
   239  // Tracing returns tracing type-specific link info.
   240  //
   241  // Returns nil if the type-specific link info isn't available.
   242  func (r Info) Tracing() *TracingInfo {
   243  	e, _ := r.extra.(*TracingInfo)
   244  	return e
   245  }
   246  
   247  // Cgroup returns cgroup type-specific link info.
   248  //
   249  // Returns nil if the type-specific link info isn't available.
   250  func (r Info) Cgroup() *CgroupInfo {
   251  	e, _ := r.extra.(*CgroupInfo)
   252  	return e
   253  }
   254  
   255  // NetNs returns netns type-specific link info.
   256  //
   257  // Returns nil if the type-specific link info isn't available.
   258  func (r Info) NetNs() *NetNsInfo {
   259  	e, _ := r.extra.(*NetNsInfo)
   260  	return e
   261  }
   262  
   263  // XDP returns XDP type-specific link info.
   264  //
   265  // Returns nil if the type-specific link info isn't available.
   266  func (r Info) XDP() *XDPInfo {
   267  	e, _ := r.extra.(*XDPInfo)
   268  	return e
   269  }
   270  
   271  // TCX returns TCX type-specific link info.
   272  //
   273  // Returns nil if the type-specific link info isn't available.
   274  func (r Info) TCX() *TCXInfo {
   275  	e, _ := r.extra.(*TCXInfo)
   276  	return e
   277  }
   278  
   279  // Netfilter returns netfilter type-specific link info.
   280  //
   281  // Returns nil if the type-specific link info isn't available.
   282  func (r Info) Netfilter() *NetfilterInfo {
   283  	e, _ := r.extra.(*NetfilterInfo)
   284  	return e
   285  }
   286  
   287  // Netkit returns netkit type-specific link info.
   288  //
   289  // Returns nil if the type-specific link info isn't available.
   290  func (r Info) Netkit() *NetkitInfo {
   291  	e, _ := r.extra.(*NetkitInfo)
   292  	return e
   293  }
   294  
   295  // KprobeMulti returns kprobe-multi type-specific link info.
   296  //
   297  // Returns nil if the type-specific link info isn't available.
   298  func (r Info) KprobeMulti() *KprobeMultiInfo {
   299  	e, _ := r.extra.(*KprobeMultiInfo)
   300  	return e
   301  }
   302  
   303  // PerfEvent returns perf-event type-specific link info.
   304  //
   305  // Returns nil if the type-specific link info isn't available.
   306  func (r Info) PerfEvent() *PerfEventInfo {
   307  	e, _ := r.extra.(*PerfEventInfo)
   308  	return e
   309  }
   310  
   311  // RawLink is the low-level API to bpf_link.
   312  //
   313  // You should consider using the higher level interfaces in this
   314  // package instead.
   315  type RawLink struct {
   316  	fd         *sys.FD
   317  	pinnedPath string
   318  }
   319  
   320  // AttachRawLink creates a raw link.
   321  func AttachRawLink(opts RawLinkOptions) (*RawLink, error) {
   322  	if err := haveBPFLink(); err != nil {
   323  		return nil, err
   324  	}
   325  
   326  	if opts.Target < 0 {
   327  		return nil, fmt.Errorf("invalid target: %s", sys.ErrClosedFd)
   328  	}
   329  
   330  	progFd := opts.Program.FD()
   331  	if progFd < 0 {
   332  		return nil, fmt.Errorf("invalid program: %s", sys.ErrClosedFd)
   333  	}
   334  
   335  	attr := sys.LinkCreateAttr{
   336  		TargetFd:    uint32(opts.Target),
   337  		ProgFd:      uint32(progFd),
   338  		AttachType:  sys.AttachType(opts.Attach),
   339  		TargetBtfId: opts.BTF,
   340  		Flags:       opts.Flags,
   341  	}
   342  	fd, err := sys.LinkCreate(&attr)
   343  	if err != nil {
   344  		return nil, fmt.Errorf("create link: %w", err)
   345  	}
   346  
   347  	return &RawLink{fd, ""}, nil
   348  }
   349  
   350  func loadPinnedRawLink(fileName string, opts *ebpf.LoadPinOptions) (*RawLink, error) {
   351  	fd, err := sys.ObjGet(&sys.ObjGetAttr{
   352  		Pathname:  sys.NewStringPointer(fileName),
   353  		FileFlags: opts.Marshal(),
   354  	})
   355  	if err != nil {
   356  		return nil, fmt.Errorf("load pinned link: %w", err)
   357  	}
   358  
   359  	return &RawLink{fd, fileName}, nil
   360  }
   361  
   362  func (l *RawLink) isLink() {}
   363  
   364  // FD returns the raw file descriptor.
   365  func (l *RawLink) FD() int {
   366  	return l.fd.Int()
   367  }
   368  
   369  // Close breaks the link.
   370  //
   371  // Use Pin if you want to make the link persistent.
   372  func (l *RawLink) Close() error {
   373  	return l.fd.Close()
   374  }
   375  
   376  // Pin persists a link past the lifetime of the process.
   377  //
   378  // Calling Close on a pinned Link will not break the link
   379  // until the pin is removed.
   380  func (l *RawLink) Pin(fileName string) error {
   381  	if err := internal.Pin(l.pinnedPath, fileName, l.fd); err != nil {
   382  		return err
   383  	}
   384  	l.pinnedPath = fileName
   385  	return nil
   386  }
   387  
   388  // Unpin implements the Link interface.
   389  func (l *RawLink) Unpin() error {
   390  	if err := internal.Unpin(l.pinnedPath); err != nil {
   391  		return err
   392  	}
   393  	l.pinnedPath = ""
   394  	return nil
   395  }
   396  
   397  // IsPinned returns true if the Link has a non-empty pinned path.
   398  func (l *RawLink) IsPinned() bool {
   399  	return l.pinnedPath != ""
   400  }
   401  
   402  // Update implements the Link interface.
   403  func (l *RawLink) Update(new *ebpf.Program) error {
   404  	return l.UpdateArgs(RawLinkUpdateOptions{
   405  		New: new,
   406  	})
   407  }
   408  
   409  // RawLinkUpdateOptions control the behaviour of RawLink.UpdateArgs.
   410  type RawLinkUpdateOptions struct {
   411  	New   *ebpf.Program
   412  	Old   *ebpf.Program
   413  	Flags uint32
   414  }
   415  
   416  // UpdateArgs updates a link based on args.
   417  func (l *RawLink) UpdateArgs(opts RawLinkUpdateOptions) error {
   418  	newFd := opts.New.FD()
   419  	if newFd < 0 {
   420  		return fmt.Errorf("invalid program: %s", sys.ErrClosedFd)
   421  	}
   422  
   423  	var oldFd int
   424  	if opts.Old != nil {
   425  		oldFd = opts.Old.FD()
   426  		if oldFd < 0 {
   427  			return fmt.Errorf("invalid replacement program: %s", sys.ErrClosedFd)
   428  		}
   429  	}
   430  
   431  	attr := sys.LinkUpdateAttr{
   432  		LinkFd:    l.fd.Uint(),
   433  		NewProgFd: uint32(newFd),
   434  		OldProgFd: uint32(oldFd),
   435  		Flags:     opts.Flags,
   436  	}
   437  	return sys.LinkUpdate(&attr)
   438  }
   439  
   440  // Info returns metadata about the link.
   441  func (l *RawLink) Info() (*Info, error) {
   442  	var info sys.LinkInfo
   443  
   444  	if err := sys.ObjInfo(l.fd, &info); err != nil {
   445  		return nil, fmt.Errorf("link info: %s", err)
   446  	}
   447  
   448  	var extra interface{}
   449  	switch info.Type {
   450  	case CgroupType:
   451  		var cgroupInfo sys.CgroupLinkInfo
   452  		if err := sys.ObjInfo(l.fd, &cgroupInfo); err != nil {
   453  			return nil, fmt.Errorf("cgroup link info: %s", err)
   454  		}
   455  		extra = &CgroupInfo{
   456  			CgroupId:   cgroupInfo.CgroupId,
   457  			AttachType: cgroupInfo.AttachType,
   458  		}
   459  	case NetNsType:
   460  		var netnsInfo sys.NetNsLinkInfo
   461  		if err := sys.ObjInfo(l.fd, &netnsInfo); err != nil {
   462  			return nil, fmt.Errorf("netns link info: %s", err)
   463  		}
   464  		extra = &NetNsInfo{
   465  			NetnsIno:   netnsInfo.NetnsIno,
   466  			AttachType: netnsInfo.AttachType,
   467  		}
   468  	case TracingType:
   469  		var tracingInfo sys.TracingLinkInfo
   470  		if err := sys.ObjInfo(l.fd, &tracingInfo); err != nil {
   471  			return nil, fmt.Errorf("tracing link info: %s", err)
   472  		}
   473  		extra = &TracingInfo{
   474  			TargetObjId: tracingInfo.TargetObjId,
   475  			TargetBtfId: tracingInfo.TargetBtfId,
   476  			AttachType:  tracingInfo.AttachType,
   477  		}
   478  	case XDPType:
   479  		var xdpInfo sys.XDPLinkInfo
   480  		if err := sys.ObjInfo(l.fd, &xdpInfo); err != nil {
   481  			return nil, fmt.Errorf("xdp link info: %s", err)
   482  		}
   483  		extra = &XDPInfo{
   484  			Ifindex: xdpInfo.Ifindex,
   485  		}
   486  	case RawTracepointType, IterType, UprobeMultiType:
   487  		// Extra metadata not supported.
   488  	case TCXType:
   489  		var tcxInfo sys.TcxLinkInfo
   490  		if err := sys.ObjInfo(l.fd, &tcxInfo); err != nil {
   491  			return nil, fmt.Errorf("tcx link info: %s", err)
   492  		}
   493  		extra = &TCXInfo{
   494  			Ifindex:    tcxInfo.Ifindex,
   495  			AttachType: tcxInfo.AttachType,
   496  		}
   497  	case NetfilterType:
   498  		var netfilterInfo sys.NetfilterLinkInfo
   499  		if err := sys.ObjInfo(l.fd, &netfilterInfo); err != nil {
   500  			return nil, fmt.Errorf("netfilter link info: %s", err)
   501  		}
   502  		extra = &NetfilterInfo{
   503  			Pf:       netfilterInfo.Pf,
   504  			Hooknum:  netfilterInfo.Hooknum,
   505  			Priority: netfilterInfo.Priority,
   506  			Flags:    netfilterInfo.Flags,
   507  		}
   508  	case NetkitType:
   509  		var netkitInfo sys.NetkitLinkInfo
   510  		if err := sys.ObjInfo(l.fd, &netkitInfo); err != nil {
   511  			return nil, fmt.Errorf("tcx link info: %s", err)
   512  		}
   513  		extra = &NetkitInfo{
   514  			Ifindex:    netkitInfo.Ifindex,
   515  			AttachType: netkitInfo.AttachType,
   516  		}
   517  	case KprobeMultiType:
   518  		var kprobeMultiInfo sys.KprobeMultiLinkInfo
   519  		if err := sys.ObjInfo(l.fd, &kprobeMultiInfo); err != nil {
   520  			return nil, fmt.Errorf("kprobe multi link info: %s", err)
   521  		}
   522  		extra = &KprobeMultiInfo{
   523  			count:  kprobeMultiInfo.Count,
   524  			flags:  kprobeMultiInfo.Flags,
   525  			missed: kprobeMultiInfo.Missed,
   526  		}
   527  	case PerfEventType:
   528  		var perfEventInfo sys.PerfEventLinkInfo
   529  		if err := sys.ObjInfo(l.fd, &perfEventInfo); err != nil {
   530  			return nil, fmt.Errorf("perf event link info: %s", err)
   531  		}
   532  
   533  		var extra2 interface{}
   534  		switch perfEventInfo.PerfEventType {
   535  		case sys.BPF_PERF_EVENT_KPROBE, sys.BPF_PERF_EVENT_KRETPROBE:
   536  			var kprobeInfo sys.KprobeLinkInfo
   537  			if err := sys.ObjInfo(l.fd, &kprobeInfo); err != nil {
   538  				return nil, fmt.Errorf("kprobe multi link info: %s", err)
   539  			}
   540  			extra2 = &KprobeInfo{
   541  				address: kprobeInfo.Addr,
   542  				missed:  kprobeInfo.Missed,
   543  			}
   544  		}
   545  
   546  		extra = &PerfEventInfo{
   547  			Type:  perfEventInfo.PerfEventType,
   548  			extra: extra2,
   549  		}
   550  	default:
   551  		return nil, fmt.Errorf("unknown link info type: %d", info.Type)
   552  	}
   553  
   554  	return &Info{
   555  		info.Type,
   556  		info.Id,
   557  		ebpf.ProgramID(info.ProgId),
   558  		extra,
   559  	}, nil
   560  }
   561  
   562  // Iterator allows iterating over links attached into the kernel.
   563  type Iterator struct {
   564  	// The ID of the current link. Only valid after a call to Next
   565  	ID ID
   566  	// The current link. Only valid until a call to Next.
   567  	// See Take if you want to retain the link.
   568  	Link Link
   569  	err  error
   570  }
   571  
   572  // Next retrieves the next link.
   573  //
   574  // Returns true if another link was found. Call [Iterator.Err] after the function returns false.
   575  func (it *Iterator) Next() bool {
   576  	id := it.ID
   577  	for {
   578  		getIdAttr := &sys.LinkGetNextIdAttr{Id: id}
   579  		err := sys.LinkGetNextId(getIdAttr)
   580  		if errors.Is(err, os.ErrNotExist) {
   581  			// There are no more links.
   582  			break
   583  		} else if err != nil {
   584  			it.err = fmt.Errorf("get next link ID: %w", err)
   585  			break
   586  		}
   587  
   588  		id = getIdAttr.NextId
   589  		l, err := NewFromID(id)
   590  		if errors.Is(err, os.ErrNotExist) {
   591  			// Couldn't load the link fast enough. Try next ID.
   592  			continue
   593  		} else if err != nil {
   594  			it.err = fmt.Errorf("get link for ID %d: %w", id, err)
   595  			break
   596  		}
   597  
   598  		if it.Link != nil {
   599  			it.Link.Close()
   600  		}
   601  		it.ID, it.Link = id, l
   602  		return true
   603  	}
   604  
   605  	// No more links or we encountered an error.
   606  	if it.Link != nil {
   607  		it.Link.Close()
   608  	}
   609  	it.Link = nil
   610  	return false
   611  }
   612  
   613  // Take the ownership of the current link.
   614  //
   615  // It's the callers responsibility to close the link.
   616  func (it *Iterator) Take() Link {
   617  	l := it.Link
   618  	it.Link = nil
   619  	return l
   620  }
   621  
   622  // Err returns an error if iteration failed for some reason.
   623  func (it *Iterator) Err() error {
   624  	return it.err
   625  }
   626  
   627  func (it *Iterator) Close() {
   628  	if it.Link != nil {
   629  		it.Link.Close()
   630  	}
   631  }