1 package link
2
3 import (
4 "bytes"
5 "encoding/binary"
6 "fmt"
7
8 "github.com/cilium/ebpf"
9 "github.com/cilium/ebpf/btf"
10 "github.com/cilium/ebpf/internal"
11 "github.com/cilium/ebpf/internal/sys"
12 )
13
14 var ErrNotSupported = internal.ErrNotSupported
15
16
17 type Link interface {
18
19
20
21 Update(*ebpf.Program) error
22
23
24
25
26 Pin(string) error
27
28
29
30
31 Unpin() error
32
33
34
35
36
37
38 Close() error
39
40
41
42
43 Info() (*Info, error)
44
45
46 isLink()
47 }
48
49
50 func LoadPinnedLink(fileName string, opts *ebpf.LoadPinOptions) (Link, error) {
51 raw, err := loadPinnedRawLink(fileName, opts)
52 if err != nil {
53 return nil, err
54 }
55
56 return wrapRawLink(raw)
57 }
58
59
60
61
62 func wrapRawLink(raw *RawLink) (Link, error) {
63 info, err := raw.Info()
64 if err != nil {
65 raw.Close()
66 return nil, err
67 }
68
69 switch info.Type {
70 case RawTracepointType:
71 return &rawTracepoint{*raw}, nil
72 case TracingType:
73 return &tracing{*raw}, nil
74 case CgroupType:
75 return &linkCgroup{*raw}, nil
76 case IterType:
77 return &Iter{*raw}, nil
78 case NetNsType:
79 return &NetNsLink{*raw}, nil
80 default:
81 return raw, nil
82 }
83 }
84
85
86 type ID = sys.LinkID
87
88
89 type RawLinkOptions struct {
90
91 Target int
92
93 Program *ebpf.Program
94
95 Attach ebpf.AttachType
96
97 BTF btf.TypeID
98
99 Flags uint32
100 }
101
102
103 type Info struct {
104 Type Type
105 ID ID
106 Program ebpf.ProgramID
107 extra interface{}
108 }
109
110 type TracingInfo sys.TracingLinkInfo
111 type CgroupInfo sys.CgroupLinkInfo
112 type NetNsInfo sys.NetNsLinkInfo
113 type XDPInfo sys.XDPLinkInfo
114
115
116
117
118 func (r Info) Tracing() *TracingInfo {
119 e, _ := r.extra.(*TracingInfo)
120 return e
121 }
122
123
124
125
126 func (r Info) Cgroup() *CgroupInfo {
127 e, _ := r.extra.(*CgroupInfo)
128 return e
129 }
130
131
132
133
134 func (r Info) NetNs() *NetNsInfo {
135 e, _ := r.extra.(*NetNsInfo)
136 return e
137 }
138
139
140
141
142 func (r Info) XDP() *XDPInfo {
143 e, _ := r.extra.(*XDPInfo)
144 return e
145 }
146
147
148
149
150
151 type RawLink struct {
152 fd *sys.FD
153 pinnedPath string
154 }
155
156
157 func AttachRawLink(opts RawLinkOptions) (*RawLink, error) {
158 if err := haveBPFLink(); err != nil {
159 return nil, err
160 }
161
162 if opts.Target < 0 {
163 return nil, fmt.Errorf("invalid target: %s", sys.ErrClosedFd)
164 }
165
166 progFd := opts.Program.FD()
167 if progFd < 0 {
168 return nil, fmt.Errorf("invalid program: %s", sys.ErrClosedFd)
169 }
170
171 attr := sys.LinkCreateAttr{
172 TargetFd: uint32(opts.Target),
173 ProgFd: uint32(progFd),
174 AttachType: sys.AttachType(opts.Attach),
175 TargetBtfId: uint32(opts.BTF),
176 Flags: opts.Flags,
177 }
178 fd, err := sys.LinkCreate(&attr)
179 if err != nil {
180 return nil, fmt.Errorf("can't create link: %s", err)
181 }
182
183 return &RawLink{fd, ""}, nil
184 }
185
186 func loadPinnedRawLink(fileName string, opts *ebpf.LoadPinOptions) (*RawLink, error) {
187 fd, err := sys.ObjGet(&sys.ObjGetAttr{
188 Pathname: sys.NewStringPointer(fileName),
189 FileFlags: opts.Marshal(),
190 })
191 if err != nil {
192 return nil, fmt.Errorf("load pinned link: %w", err)
193 }
194
195 return &RawLink{fd, fileName}, nil
196 }
197
198 func (l *RawLink) isLink() {}
199
200
201 func (l *RawLink) FD() int {
202 return l.fd.Int()
203 }
204
205
206
207
208 func (l *RawLink) Close() error {
209 return l.fd.Close()
210 }
211
212
213
214
215
216 func (l *RawLink) Pin(fileName string) error {
217 if err := internal.Pin(l.pinnedPath, fileName, l.fd); err != nil {
218 return err
219 }
220 l.pinnedPath = fileName
221 return nil
222 }
223
224
225 func (l *RawLink) Unpin() error {
226 if err := internal.Unpin(l.pinnedPath); err != nil {
227 return err
228 }
229 l.pinnedPath = ""
230 return nil
231 }
232
233
234 func (l *RawLink) Update(new *ebpf.Program) error {
235 return l.UpdateArgs(RawLinkUpdateOptions{
236 New: new,
237 })
238 }
239
240
241 type RawLinkUpdateOptions struct {
242 New *ebpf.Program
243 Old *ebpf.Program
244 Flags uint32
245 }
246
247
248 func (l *RawLink) UpdateArgs(opts RawLinkUpdateOptions) error {
249 newFd := opts.New.FD()
250 if newFd < 0 {
251 return fmt.Errorf("invalid program: %s", sys.ErrClosedFd)
252 }
253
254 var oldFd int
255 if opts.Old != nil {
256 oldFd = opts.Old.FD()
257 if oldFd < 0 {
258 return fmt.Errorf("invalid replacement program: %s", sys.ErrClosedFd)
259 }
260 }
261
262 attr := sys.LinkUpdateAttr{
263 LinkFd: l.fd.Uint(),
264 NewProgFd: uint32(newFd),
265 OldProgFd: uint32(oldFd),
266 Flags: opts.Flags,
267 }
268 return sys.LinkUpdate(&attr)
269 }
270
271
272 func (l *RawLink) Info() (*Info, error) {
273 var info sys.LinkInfo
274
275 if err := sys.ObjInfo(l.fd, &info); err != nil {
276 return nil, fmt.Errorf("link info: %s", err)
277 }
278
279 var extra interface{}
280 switch info.Type {
281 case CgroupType:
282 extra = &CgroupInfo{}
283 case IterType:
284
285 case NetNsType:
286 extra = &NetNsInfo{}
287 case RawTracepointType:
288
289 case TracingType:
290 extra = &TracingInfo{}
291 case XDPType:
292 extra = &XDPInfo{}
293 case PerfEventType:
294
295 default:
296 return nil, fmt.Errorf("unknown link info type: %d", info.Type)
297 }
298
299 if info.Type != RawTracepointType && info.Type != IterType && info.Type != PerfEventType {
300 buf := bytes.NewReader(info.Extra[:])
301 err := binary.Read(buf, internal.NativeEndian, extra)
302 if err != nil {
303 return nil, fmt.Errorf("can not read extra link info: %w", err)
304 }
305 }
306
307 return &Info{
308 info.Type,
309 info.Id,
310 ebpf.ProgramID(info.ProgId),
311 extra,
312 }, nil
313 }
314
View as plain text