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