Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/kernel/bpf/mprog.c
29280 views
1
// SPDX-License-Identifier: GPL-2.0
2
/* Copyright (c) 2023 Isovalent */
3
4
#include <linux/bpf.h>
5
#include <linux/bpf_mprog.h>
6
7
static int bpf_mprog_link(struct bpf_tuple *tuple,
8
u32 id_or_fd, u32 flags,
9
enum bpf_prog_type type)
10
{
11
struct bpf_link *link = ERR_PTR(-EINVAL);
12
bool id = flags & BPF_F_ID;
13
14
if (id)
15
link = bpf_link_by_id(id_or_fd);
16
else if (id_or_fd)
17
link = bpf_link_get_from_fd(id_or_fd);
18
if (IS_ERR(link))
19
return PTR_ERR(link);
20
if (type && link->prog->type != type) {
21
bpf_link_put(link);
22
return -EINVAL;
23
}
24
25
tuple->link = link;
26
tuple->prog = link->prog;
27
return 0;
28
}
29
30
static int bpf_mprog_prog(struct bpf_tuple *tuple,
31
u32 id_or_fd, u32 flags,
32
enum bpf_prog_type type)
33
{
34
struct bpf_prog *prog = ERR_PTR(-EINVAL);
35
bool id = flags & BPF_F_ID;
36
37
if (id)
38
prog = bpf_prog_by_id(id_or_fd);
39
else if (id_or_fd)
40
prog = bpf_prog_get(id_or_fd);
41
if (IS_ERR(prog))
42
return PTR_ERR(prog);
43
if (type && prog->type != type) {
44
bpf_prog_put(prog);
45
return -EINVAL;
46
}
47
48
tuple->link = NULL;
49
tuple->prog = prog;
50
return 0;
51
}
52
53
static int bpf_mprog_tuple_relative(struct bpf_tuple *tuple,
54
u32 id_or_fd, u32 flags,
55
enum bpf_prog_type type)
56
{
57
bool link = flags & BPF_F_LINK;
58
bool id = flags & BPF_F_ID;
59
60
memset(tuple, 0, sizeof(*tuple));
61
if (link)
62
return bpf_mprog_link(tuple, id_or_fd, flags, type);
63
/* If no relevant flag is set and no id_or_fd was passed, then
64
* tuple link/prog is just NULLed. This is the case when before/
65
* after selects first/last position without passing fd.
66
*/
67
if (!id && !id_or_fd)
68
return 0;
69
return bpf_mprog_prog(tuple, id_or_fd, flags, type);
70
}
71
72
static void bpf_mprog_tuple_put(struct bpf_tuple *tuple)
73
{
74
if (tuple->link)
75
bpf_link_put(tuple->link);
76
else if (tuple->prog)
77
bpf_prog_put(tuple->prog);
78
}
79
80
/* The bpf_mprog_{replace,delete}() operate on exact idx position with the
81
* one exception that for deletion we support delete from front/back. In
82
* case of front idx is -1, in case of back idx is bpf_mprog_total(entry).
83
* Adjustment to first and last entry is trivial. The bpf_mprog_insert()
84
* we have to deal with the following cases:
85
*
86
* idx + before:
87
*
88
* Insert P4 before P3: idx for old array is 1, idx for new array is 2,
89
* hence we adjust target idx for the new array, so that memmove copies
90
* P1 and P2 to the new entry, and we insert P4 into idx 2. Inserting
91
* before P1 would have old idx -1 and new idx 0.
92
*
93
* +--+--+--+ +--+--+--+--+ +--+--+--+--+
94
* |P1|P2|P3| ==> |P1|P2| |P3| ==> |P1|P2|P4|P3|
95
* +--+--+--+ +--+--+--+--+ +--+--+--+--+
96
*
97
* idx + after:
98
*
99
* Insert P4 after P2: idx for old array is 2, idx for new array is 2.
100
* Again, memmove copies P1 and P2 to the new entry, and we insert P4
101
* into idx 2. Inserting after P3 would have both old/new idx at 4 aka
102
* bpf_mprog_total(entry).
103
*
104
* +--+--+--+ +--+--+--+--+ +--+--+--+--+
105
* |P1|P2|P3| ==> |P1|P2| |P3| ==> |P1|P2|P4|P3|
106
* +--+--+--+ +--+--+--+--+ +--+--+--+--+
107
*/
108
static int bpf_mprog_replace(struct bpf_mprog_entry *entry,
109
struct bpf_mprog_entry **entry_new,
110
struct bpf_tuple *ntuple, int idx)
111
{
112
struct bpf_mprog_fp *fp;
113
struct bpf_mprog_cp *cp;
114
struct bpf_prog *oprog;
115
116
bpf_mprog_read(entry, idx, &fp, &cp);
117
oprog = READ_ONCE(fp->prog);
118
bpf_mprog_write(fp, cp, ntuple);
119
if (!ntuple->link) {
120
WARN_ON_ONCE(cp->link);
121
bpf_prog_put(oprog);
122
}
123
*entry_new = entry;
124
return 0;
125
}
126
127
static int bpf_mprog_insert(struct bpf_mprog_entry *entry,
128
struct bpf_mprog_entry **entry_new,
129
struct bpf_tuple *ntuple, int idx, u32 flags)
130
{
131
int total = bpf_mprog_total(entry);
132
struct bpf_mprog_entry *peer;
133
struct bpf_mprog_fp *fp;
134
struct bpf_mprog_cp *cp;
135
136
peer = bpf_mprog_peer(entry);
137
bpf_mprog_entry_copy(peer, entry);
138
if (idx == total)
139
goto insert;
140
else if (flags & BPF_F_BEFORE)
141
idx += 1;
142
bpf_mprog_entry_grow(peer, idx);
143
insert:
144
bpf_mprog_read(peer, idx, &fp, &cp);
145
bpf_mprog_write(fp, cp, ntuple);
146
bpf_mprog_inc(peer);
147
*entry_new = peer;
148
return 0;
149
}
150
151
static int bpf_mprog_delete(struct bpf_mprog_entry *entry,
152
struct bpf_mprog_entry **entry_new,
153
struct bpf_tuple *dtuple, int idx)
154
{
155
int total = bpf_mprog_total(entry);
156
struct bpf_mprog_entry *peer;
157
158
peer = bpf_mprog_peer(entry);
159
bpf_mprog_entry_copy(peer, entry);
160
if (idx == -1)
161
idx = 0;
162
else if (idx == total)
163
idx = total - 1;
164
bpf_mprog_entry_shrink(peer, idx);
165
bpf_mprog_dec(peer);
166
bpf_mprog_mark_for_release(peer, dtuple);
167
*entry_new = peer;
168
return 0;
169
}
170
171
/* In bpf_mprog_pos_*() we evaluate the target position for the BPF
172
* program/link that needs to be replaced, inserted or deleted for
173
* each "rule" independently. If all rules agree on that position
174
* or existing element, then enact replacement, addition or deletion.
175
* If this is not the case, then the request cannot be satisfied and
176
* we bail out with an error.
177
*/
178
static int bpf_mprog_pos_exact(struct bpf_mprog_entry *entry,
179
struct bpf_tuple *tuple)
180
{
181
struct bpf_mprog_fp *fp;
182
struct bpf_mprog_cp *cp;
183
int i;
184
185
for (i = 0; i < bpf_mprog_total(entry); i++) {
186
bpf_mprog_read(entry, i, &fp, &cp);
187
if (tuple->prog == READ_ONCE(fp->prog))
188
return tuple->link == cp->link ? i : -EBUSY;
189
}
190
return -ENOENT;
191
}
192
193
static int bpf_mprog_pos_before(struct bpf_mprog_entry *entry,
194
struct bpf_tuple *tuple)
195
{
196
struct bpf_mprog_fp *fp;
197
struct bpf_mprog_cp *cp;
198
int i;
199
200
for (i = 0; i < bpf_mprog_total(entry); i++) {
201
bpf_mprog_read(entry, i, &fp, &cp);
202
if (tuple->prog == READ_ONCE(fp->prog) &&
203
(!tuple->link || tuple->link == cp->link))
204
return i - 1;
205
}
206
return tuple->prog ? -ENOENT : -1;
207
}
208
209
static int bpf_mprog_pos_after(struct bpf_mprog_entry *entry,
210
struct bpf_tuple *tuple)
211
{
212
struct bpf_mprog_fp *fp;
213
struct bpf_mprog_cp *cp;
214
int i;
215
216
for (i = 0; i < bpf_mprog_total(entry); i++) {
217
bpf_mprog_read(entry, i, &fp, &cp);
218
if (tuple->prog == READ_ONCE(fp->prog) &&
219
(!tuple->link || tuple->link == cp->link))
220
return i + 1;
221
}
222
return tuple->prog ? -ENOENT : bpf_mprog_total(entry);
223
}
224
225
int bpf_mprog_attach(struct bpf_mprog_entry *entry,
226
struct bpf_mprog_entry **entry_new,
227
struct bpf_prog *prog_new, struct bpf_link *link,
228
struct bpf_prog *prog_old,
229
u32 flags, u32 id_or_fd, u64 revision)
230
{
231
struct bpf_tuple rtuple, ntuple = {
232
.prog = prog_new,
233
.link = link,
234
}, otuple = {
235
.prog = prog_old,
236
.link = link,
237
};
238
int ret, idx = -ERANGE, tidx;
239
240
if (revision && revision != bpf_mprog_revision(entry))
241
return -ESTALE;
242
if (bpf_mprog_exists(entry, prog_new))
243
return -EEXIST;
244
ret = bpf_mprog_tuple_relative(&rtuple, id_or_fd,
245
flags & ~BPF_F_REPLACE,
246
prog_new->type);
247
if (ret)
248
return ret;
249
if (flags & BPF_F_REPLACE) {
250
tidx = bpf_mprog_pos_exact(entry, &otuple);
251
if (tidx < 0) {
252
ret = tidx;
253
goto out;
254
}
255
idx = tidx;
256
} else if (bpf_mprog_total(entry) == bpf_mprog_max()) {
257
ret = -ERANGE;
258
goto out;
259
}
260
if (flags & BPF_F_BEFORE) {
261
tidx = bpf_mprog_pos_before(entry, &rtuple);
262
if (tidx < -1 || (idx >= -1 && tidx != idx)) {
263
ret = tidx < -1 ? tidx : -ERANGE;
264
goto out;
265
}
266
idx = tidx;
267
}
268
if (flags & BPF_F_AFTER) {
269
tidx = bpf_mprog_pos_after(entry, &rtuple);
270
if (tidx < -1 || (idx >= -1 && tidx != idx)) {
271
ret = tidx < 0 ? tidx : -ERANGE;
272
goto out;
273
}
274
idx = tidx;
275
}
276
if (idx < -1) {
277
if (rtuple.prog || flags) {
278
ret = -EINVAL;
279
goto out;
280
}
281
idx = bpf_mprog_total(entry);
282
flags = BPF_F_AFTER;
283
}
284
if (idx >= bpf_mprog_max()) {
285
ret = -ERANGE;
286
goto out;
287
}
288
if (flags & BPF_F_REPLACE)
289
ret = bpf_mprog_replace(entry, entry_new, &ntuple, idx);
290
else
291
ret = bpf_mprog_insert(entry, entry_new, &ntuple, idx, flags);
292
out:
293
bpf_mprog_tuple_put(&rtuple);
294
return ret;
295
}
296
297
static int bpf_mprog_fetch(struct bpf_mprog_entry *entry,
298
struct bpf_tuple *tuple, int idx)
299
{
300
int total = bpf_mprog_total(entry);
301
struct bpf_mprog_cp *cp;
302
struct bpf_mprog_fp *fp;
303
struct bpf_prog *prog;
304
struct bpf_link *link;
305
306
if (idx == -1)
307
idx = 0;
308
else if (idx == total)
309
idx = total - 1;
310
bpf_mprog_read(entry, idx, &fp, &cp);
311
prog = READ_ONCE(fp->prog);
312
link = cp->link;
313
/* The deletion request can either be without filled tuple in which
314
* case it gets populated here based on idx, or with filled tuple
315
* where the only thing we end up doing is the WARN_ON_ONCE() assert.
316
* If we hit a BPF link at the given index, it must not be removed
317
* from opts path.
318
*/
319
if (link && !tuple->link)
320
return -EBUSY;
321
WARN_ON_ONCE(tuple->prog && tuple->prog != prog);
322
WARN_ON_ONCE(tuple->link && tuple->link != link);
323
tuple->prog = prog;
324
tuple->link = link;
325
return 0;
326
}
327
328
int bpf_mprog_detach(struct bpf_mprog_entry *entry,
329
struct bpf_mprog_entry **entry_new,
330
struct bpf_prog *prog, struct bpf_link *link,
331
u32 flags, u32 id_or_fd, u64 revision)
332
{
333
struct bpf_tuple rtuple, dtuple = {
334
.prog = prog,
335
.link = link,
336
};
337
int ret, idx = -ERANGE, tidx;
338
339
if (flags & BPF_F_REPLACE)
340
return -EINVAL;
341
if (revision && revision != bpf_mprog_revision(entry))
342
return -ESTALE;
343
if (!bpf_mprog_total(entry))
344
return -ENOENT;
345
ret = bpf_mprog_tuple_relative(&rtuple, id_or_fd, flags,
346
prog ? prog->type :
347
BPF_PROG_TYPE_UNSPEC);
348
if (ret)
349
return ret;
350
if (dtuple.prog) {
351
tidx = bpf_mprog_pos_exact(entry, &dtuple);
352
if (tidx < 0) {
353
ret = tidx;
354
goto out;
355
}
356
idx = tidx;
357
}
358
if (flags & BPF_F_BEFORE) {
359
tidx = bpf_mprog_pos_before(entry, &rtuple);
360
if (tidx < -1 || (idx >= -1 && tidx != idx)) {
361
ret = tidx < -1 ? tidx : -ERANGE;
362
goto out;
363
}
364
idx = tidx;
365
}
366
if (flags & BPF_F_AFTER) {
367
tidx = bpf_mprog_pos_after(entry, &rtuple);
368
if (tidx < -1 || (idx >= -1 && tidx != idx)) {
369
ret = tidx < 0 ? tidx : -ERANGE;
370
goto out;
371
}
372
idx = tidx;
373
}
374
if (idx < -1) {
375
if (rtuple.prog || flags) {
376
ret = -EINVAL;
377
goto out;
378
}
379
idx = bpf_mprog_total(entry);
380
flags = BPF_F_AFTER;
381
}
382
if (idx >= bpf_mprog_max()) {
383
ret = -ERANGE;
384
goto out;
385
}
386
ret = bpf_mprog_fetch(entry, &dtuple, idx);
387
if (ret)
388
goto out;
389
ret = bpf_mprog_delete(entry, entry_new, &dtuple, idx);
390
out:
391
bpf_mprog_tuple_put(&rtuple);
392
return ret;
393
}
394
395
int bpf_mprog_query(const union bpf_attr *attr, union bpf_attr __user *uattr,
396
struct bpf_mprog_entry *entry)
397
{
398
u32 __user *uprog_flags, *ulink_flags;
399
u32 __user *uprog_id, *ulink_id;
400
struct bpf_mprog_fp *fp;
401
struct bpf_mprog_cp *cp;
402
struct bpf_prog *prog;
403
const u32 flags = 0;
404
u32 id, count = 0;
405
u64 revision = 1;
406
int i, ret = 0;
407
408
if (attr->query.query_flags || attr->query.attach_flags)
409
return -EINVAL;
410
if (entry) {
411
revision = bpf_mprog_revision(entry);
412
count = bpf_mprog_total(entry);
413
}
414
if (copy_to_user(&uattr->query.attach_flags, &flags, sizeof(flags)))
415
return -EFAULT;
416
if (copy_to_user(&uattr->query.revision, &revision, sizeof(revision)))
417
return -EFAULT;
418
if (copy_to_user(&uattr->query.count, &count, sizeof(count)))
419
return -EFAULT;
420
uprog_id = u64_to_user_ptr(attr->query.prog_ids);
421
uprog_flags = u64_to_user_ptr(attr->query.prog_attach_flags);
422
ulink_id = u64_to_user_ptr(attr->query.link_ids);
423
ulink_flags = u64_to_user_ptr(attr->query.link_attach_flags);
424
if (attr->query.count == 0 || !uprog_id || !count)
425
return 0;
426
if (attr->query.count < count) {
427
count = attr->query.count;
428
ret = -ENOSPC;
429
}
430
for (i = 0; i < bpf_mprog_max(); i++) {
431
bpf_mprog_read(entry, i, &fp, &cp);
432
prog = READ_ONCE(fp->prog);
433
if (!prog)
434
break;
435
id = prog->aux->id;
436
if (copy_to_user(uprog_id + i, &id, sizeof(id)))
437
return -EFAULT;
438
if (uprog_flags &&
439
copy_to_user(uprog_flags + i, &flags, sizeof(flags)))
440
return -EFAULT;
441
id = cp->link ? cp->link->id : 0;
442
if (ulink_id &&
443
copy_to_user(ulink_id + i, &id, sizeof(id)))
444
return -EFAULT;
445
if (ulink_flags &&
446
copy_to_user(ulink_flags + i, &flags, sizeof(flags)))
447
return -EFAULT;
448
if (i + 1 == count)
449
break;
450
}
451
return ret;
452
}
453
454