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 }