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
14 const (
15 flagAllowOverride cgroupAttachFlags = 1 << iota
16 flagAllowMulti
17 flagReplace
18 )
19
20 type CgroupOptions struct {
21
22 Path string
23
24 Attach ebpf.AttachType
25
26 Program *ebpf.Program
27 }
28
29
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
120
121
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 }
166
View as plain text