github.com/cilium/ebpf@v0.16.0/link/cgroup.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/internal/sys"
    10  )
    11  
    12  type cgroupAttachFlags uint32
    13  
    14  const (
    15  	// Allow programs attached to sub-cgroups to override the verdict of this
    16  	// program.
    17  	flagAllowOverride cgroupAttachFlags = 1 << iota
    18  	// Allow attaching multiple programs to the cgroup. Only works if the cgroup
    19  	// has zero or more programs attached using the Multi flag. Implies override.
    20  	flagAllowMulti
    21  	// Set automatically by progAttachCgroup.Update(). Used for updating a
    22  	// specific given program attached in multi-mode.
    23  	flagReplace
    24  )
    25  
    26  type CgroupOptions struct {
    27  	// Path to a cgroupv2 folder.
    28  	Path string
    29  	// One of the AttachCgroup* constants
    30  	Attach ebpf.AttachType
    31  	// Program must be of type CGroup*, and the attach type must match Attach.
    32  	Program *ebpf.Program
    33  }
    34  
    35  // AttachCgroup links a BPF program to a cgroup.
    36  //
    37  // If the running kernel doesn't support bpf_link, attempts to emulate its
    38  // semantics using the legacy PROG_ATTACH mechanism. If bpf_link is not
    39  // available, the returned [Link] will not support pinning to bpffs.
    40  //
    41  // If you need more control over attachment flags or the attachment mechanism
    42  // used, look at [RawAttachProgram] and [AttachRawLink] instead.
    43  func AttachCgroup(opts CgroupOptions) (cg Link, err error) {
    44  	cgroup, err := os.Open(opts.Path)
    45  	if err != nil {
    46  		return nil, fmt.Errorf("can't open cgroup: %s", err)
    47  	}
    48  	defer func() {
    49  		if _, ok := cg.(*progAttachCgroup); ok {
    50  			// Skip closing the cgroup handle if we return a valid progAttachCgroup,
    51  			// where the handle is retained to implement Update().
    52  			return
    53  		}
    54  		cgroup.Close()
    55  	}()
    56  
    57  	cg, err = newLinkCgroup(cgroup, opts.Attach, opts.Program)
    58  	if err == nil {
    59  		return cg, nil
    60  	}
    61  
    62  	if errors.Is(err, ErrNotSupported) {
    63  		cg, err = newProgAttachCgroup(cgroup, opts.Attach, opts.Program, flagAllowMulti)
    64  	}
    65  	if errors.Is(err, ErrNotSupported) {
    66  		cg, err = newProgAttachCgroup(cgroup, opts.Attach, opts.Program, flagAllowOverride)
    67  	}
    68  	if err != nil {
    69  		return nil, err
    70  	}
    71  
    72  	return cg, nil
    73  }
    74  
    75  type progAttachCgroup struct {
    76  	cgroup     *os.File
    77  	current    *ebpf.Program
    78  	attachType ebpf.AttachType
    79  	flags      cgroupAttachFlags
    80  }
    81  
    82  var _ Link = (*progAttachCgroup)(nil)
    83  
    84  func (cg *progAttachCgroup) isLink() {}
    85  
    86  // newProgAttachCgroup attaches prog to cgroup using BPF_PROG_ATTACH.
    87  // cgroup and prog are retained by [progAttachCgroup].
    88  func newProgAttachCgroup(cgroup *os.File, attach ebpf.AttachType, prog *ebpf.Program, flags cgroupAttachFlags) (*progAttachCgroup, error) {
    89  	if flags&flagAllowMulti > 0 {
    90  		if err := haveProgAttachReplace(); err != nil {
    91  			return nil, fmt.Errorf("can't support multiple programs: %w", err)
    92  		}
    93  	}
    94  
    95  	// Use a program handle that cannot be closed by the caller.
    96  	clone, err := prog.Clone()
    97  	if err != nil {
    98  		return nil, err
    99  	}
   100  
   101  	err = RawAttachProgram(RawAttachProgramOptions{
   102  		Target:  int(cgroup.Fd()),
   103  		Program: clone,
   104  		Flags:   uint32(flags),
   105  		Attach:  attach,
   106  	})
   107  	if err != nil {
   108  		clone.Close()
   109  		return nil, fmt.Errorf("cgroup: %w", err)
   110  	}
   111  
   112  	return &progAttachCgroup{cgroup, clone, attach, flags}, nil
   113  }
   114  
   115  func (cg *progAttachCgroup) Close() error {
   116  	defer cg.cgroup.Close()
   117  	defer cg.current.Close()
   118  
   119  	err := RawDetachProgram(RawDetachProgramOptions{
   120  		Target:  int(cg.cgroup.Fd()),
   121  		Program: cg.current,
   122  		Attach:  cg.attachType,
   123  	})
   124  	if err != nil {
   125  		return fmt.Errorf("close cgroup: %s", err)
   126  	}
   127  	return nil
   128  }
   129  
   130  func (cg *progAttachCgroup) Update(prog *ebpf.Program) error {
   131  	new, err := prog.Clone()
   132  	if err != nil {
   133  		return err
   134  	}
   135  
   136  	args := RawAttachProgramOptions{
   137  		Target:  int(cg.cgroup.Fd()),
   138  		Program: prog,
   139  		Attach:  cg.attachType,
   140  		Flags:   uint32(cg.flags),
   141  	}
   142  
   143  	if cg.flags&flagAllowMulti > 0 {
   144  		// Atomically replacing multiple programs requires at least
   145  		// 5.5 (commit 7dd68b3279f17921 "bpf: Support replacing cgroup-bpf
   146  		// program in MULTI mode")
   147  		args.Anchor = ReplaceProgram(cg.current)
   148  	}
   149  
   150  	if err := RawAttachProgram(args); err != nil {
   151  		new.Close()
   152  		return fmt.Errorf("can't update cgroup: %s", err)
   153  	}
   154  
   155  	cg.current.Close()
   156  	cg.current = new
   157  	return nil
   158  }
   159  
   160  func (cg *progAttachCgroup) Pin(string) error {
   161  	return fmt.Errorf("can't pin cgroup: %w", ErrNotSupported)
   162  }
   163  
   164  func (cg *progAttachCgroup) Unpin() error {
   165  	return fmt.Errorf("can't unpin cgroup: %w", ErrNotSupported)
   166  }
   167  
   168  func (cg *progAttachCgroup) Info() (*Info, error) {
   169  	return nil, fmt.Errorf("can't get cgroup info: %w", ErrNotSupported)
   170  }
   171  
   172  type linkCgroup struct {
   173  	RawLink
   174  }
   175  
   176  var _ Link = (*linkCgroup)(nil)
   177  
   178  // newLinkCgroup attaches prog to cgroup using BPF_LINK_CREATE.
   179  func newLinkCgroup(cgroup *os.File, attach ebpf.AttachType, prog *ebpf.Program) (*linkCgroup, error) {
   180  	link, err := AttachRawLink(RawLinkOptions{
   181  		Target:  int(cgroup.Fd()),
   182  		Program: prog,
   183  		Attach:  attach,
   184  	})
   185  	if err != nil {
   186  		return nil, err
   187  	}
   188  
   189  	return &linkCgroup{*link}, err
   190  }
   191  
   192  func (cg *linkCgroup) Info() (*Info, error) {
   193  	var info sys.CgroupLinkInfo
   194  	if err := sys.ObjInfo(cg.fd, &info); err != nil {
   195  		return nil, fmt.Errorf("cgroup link info: %s", err)
   196  	}
   197  	extra := &CgroupInfo{
   198  		CgroupId:   info.CgroupId,
   199  		AttachType: info.AttachType,
   200  	}
   201  
   202  	return &Info{
   203  		info.Type,
   204  		info.Id,
   205  		ebpf.ProgramID(info.ProgId),
   206  		extra,
   207  	}, nil
   208  }