cgroup.go (3658B)
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 // cgroup attach flags 14 const ( 15 flagAllowOverride cgroupAttachFlags = 1 << iota 16 flagAllowMulti 17 flagReplace 18 ) 19 20 type CgroupOptions struct { 21 // Path to a cgroupv2 folder. 22 Path string 23 // One of the AttachCgroup* constants 24 Attach ebpf.AttachType 25 // Program must be of type CGroup*, and the attach type must match Attach. 26 Program *ebpf.Program 27 } 28 29 // AttachCgroup links a BPF program to a cgroup. 30 func AttachCgroup(opts CgroupOptions) (Link, error) { 31 cgroup, err := os.Open(opts.Path) 32 if err != nil { 33 return nil, fmt.Errorf("can't open cgroup: %s", err) 34 } 35 36 clone, err := opts.Program.Clone() 37 if err != nil { 38 cgroup.Close() 39 return nil, err 40 } 41 42 var cg Link 43 cg, err = newLinkCgroup(cgroup, opts.Attach, clone) 44 if errors.Is(err, ErrNotSupported) { 45 cg, err = newProgAttachCgroup(cgroup, opts.Attach, clone, flagAllowMulti) 46 } 47 if errors.Is(err, ErrNotSupported) { 48 cg, err = newProgAttachCgroup(cgroup, opts.Attach, clone, flagAllowOverride) 49 } 50 if err != nil { 51 cgroup.Close() 52 clone.Close() 53 return nil, err 54 } 55 56 return cg, nil 57 } 58 59 type progAttachCgroup struct { 60 cgroup *os.File 61 current *ebpf.Program 62 attachType ebpf.AttachType 63 flags cgroupAttachFlags 64 } 65 66 var _ Link = (*progAttachCgroup)(nil) 67 68 func (cg *progAttachCgroup) isLink() {} 69 70 func newProgAttachCgroup(cgroup *os.File, attach ebpf.AttachType, prog *ebpf.Program, flags cgroupAttachFlags) (*progAttachCgroup, error) { 71 if flags&flagAllowMulti > 0 { 72 if err := haveProgAttachReplace(); err != nil { 73 return nil, fmt.Errorf("can't support multiple programs: %w", err) 74 } 75 } 76 77 err := RawAttachProgram(RawAttachProgramOptions{ 78 Target: int(cgroup.Fd()), 79 Program: prog, 80 Flags: uint32(flags), 81 Attach: attach, 82 }) 83 if err != nil { 84 return nil, fmt.Errorf("cgroup: %w", err) 85 } 86 87 return &progAttachCgroup{cgroup, prog, attach, flags}, nil 88 } 89 90 func (cg *progAttachCgroup) Close() error { 91 defer cg.cgroup.Close() 92 defer cg.current.Close() 93 94 err := RawDetachProgram(RawDetachProgramOptions{ 95 Target: int(cg.cgroup.Fd()), 96 Program: cg.current, 97 Attach: cg.attachType, 98 }) 99 if err != nil { 100 return fmt.Errorf("close cgroup: %s", err) 101 } 102 return nil 103 } 104 105 func (cg *progAttachCgroup) Update(prog *ebpf.Program) error { 106 new, err := prog.Clone() 107 if err != nil { 108 return err 109 } 110 111 args := RawAttachProgramOptions{ 112 Target: int(cg.cgroup.Fd()), 113 Program: prog, 114 Attach: cg.attachType, 115 Flags: uint32(cg.flags), 116 } 117 118 if cg.flags&flagAllowMulti > 0 { 119 // Atomically replacing multiple programs requires at least 120 // 5.5 (commit 7dd68b3279f17921 "bpf: Support replacing cgroup-bpf 121 // program in MULTI mode") 122 args.Flags |= uint32(flagReplace) 123 args.Replace = cg.current 124 } 125 126 if err := RawAttachProgram(args); err != nil { 127 new.Close() 128 return fmt.Errorf("can't update cgroup: %s", err) 129 } 130 131 cg.current.Close() 132 cg.current = new 133 return nil 134 } 135 136 func (cg *progAttachCgroup) Pin(string) error { 137 return fmt.Errorf("can't pin cgroup: %w", ErrNotSupported) 138 } 139 140 func (cg *progAttachCgroup) Unpin() error { 141 return fmt.Errorf("can't pin cgroup: %w", ErrNotSupported) 142 } 143 144 func (cg *progAttachCgroup) Info() (*Info, error) { 145 return nil, fmt.Errorf("can't get cgroup info: %w", ErrNotSupported) 146 } 147 148 type linkCgroup struct { 149 RawLink 150 } 151 152 var _ Link = (*linkCgroup)(nil) 153 154 func newLinkCgroup(cgroup *os.File, attach ebpf.AttachType, prog *ebpf.Program) (*linkCgroup, error) { 155 link, err := AttachRawLink(RawLinkOptions{ 156 Target: int(cgroup.Fd()), 157 Program: prog, 158 Attach: attach, 159 }) 160 if err != nil { 161 return nil, err 162 } 163 164 return &linkCgroup{*link}, err 165 }