2023-01-17 21:59:04 +01:00
|
|
|
package link
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
|
|
|
|
"github.com/cilium/ebpf"
|
|
|
|
)
|
|
|
|
|
|
|
|
type cgroupAttachFlags uint32
|
|
|
|
|
|
|
|
// cgroup attach flags
|
|
|
|
const (
|
|
|
|
flagAllowOverride cgroupAttachFlags = 1 << iota
|
|
|
|
flagAllowMulti
|
|
|
|
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.
|
|
|
|
func AttachCgroup(opts CgroupOptions) (Link, error) {
|
|
|
|
cgroup, err := os.Open(opts.Path)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("can't open cgroup: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
clone, err := opts.Program.Clone()
|
|
|
|
if err != nil {
|
|
|
|
cgroup.Close()
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
var cg Link
|
|
|
|
cg, err = newLinkCgroup(cgroup, opts.Attach, clone)
|
|
|
|
if errors.Is(err, ErrNotSupported) {
|
|
|
|
cg, err = newProgAttachCgroup(cgroup, opts.Attach, clone, flagAllowMulti)
|
|
|
|
}
|
|
|
|
if errors.Is(err, ErrNotSupported) {
|
|
|
|
cg, err = newProgAttachCgroup(cgroup, opts.Attach, clone, flagAllowOverride)
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
cgroup.Close()
|
|
|
|
clone.Close()
|
|
|
|
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() {}
|
|
|
|
|
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
err := RawAttachProgram(RawAttachProgramOptions{
|
|
|
|
Target: int(cgroup.Fd()),
|
|
|
|
Program: prog,
|
|
|
|
Flags: uint32(flags),
|
|
|
|
Attach: attach,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("cgroup: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return &progAttachCgroup{cgroup, prog, 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.Flags |= uint32(flagReplace)
|
|
|
|
args.Replace = 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)
|
|
|
|
}
|
|
|
|
|
2023-04-03 11:16:17 +02:00
|
|
|
func (cg *progAttachCgroup) Unpin() error {
|
|
|
|
return fmt.Errorf("can't pin cgroup: %w", ErrNotSupported)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (cg *progAttachCgroup) Info() (*Info, error) {
|
|
|
|
return nil, fmt.Errorf("can't get cgroup info: %w", ErrNotSupported)
|
|
|
|
}
|
|
|
|
|
2023-01-17 21:59:04 +01:00
|
|
|
type linkCgroup struct {
|
2023-04-03 11:16:17 +02:00
|
|
|
RawLink
|
2023-01-17 21:59:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
var _ Link = (*linkCgroup)(nil)
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2023-04-03 11:16:17 +02:00
|
|
|
return &linkCgroup{*link}, err
|
2023-01-17 21:59:04 +01:00
|
|
|
}
|