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