selftests/bpf: Extend sockopt tests to use BPF_LINK_CREATE
[linux-2.6-block.git] / tools / testing / selftests / bpf / prog_tests / sockopt.c
CommitLineData
9ec8a4c9 1// SPDX-License-Identifier: GPL-2.0
4a647421 2#include <test_progs.h>
b9ec9132 3#include <io_uring/mini_liburing.h>
9ec8a4c9
SF
4#include "cgroup_helpers.h"
5
9ec8a4c9
SF
6static char bpf_log_buf[4096];
7static bool verbose;
8
989a4a7d
SF
9#ifndef PAGE_SIZE
10#define PAGE_SIZE 4096
11#endif
12
9ec8a4c9
SF
13enum sockopt_test_error {
14 OK = 0,
15 DENY_LOAD,
16 DENY_ATTACH,
989a4a7d 17 EOPNOTSUPP_GETSOCKOPT,
9ec8a4c9
SF
18 EPERM_GETSOCKOPT,
19 EFAULT_GETSOCKOPT,
20 EPERM_SETSOCKOPT,
21 EFAULT_SETSOCKOPT,
22};
23
24static struct sockopt_test {
25 const char *descr;
26 const struct bpf_insn insns[64];
27 enum bpf_attach_type attach_type;
28 enum bpf_attach_type expected_attach_type;
29
30 int set_optname;
31 int set_level;
32 const char set_optval[64];
33 socklen_t set_optlen;
34
35 int get_optname;
36 int get_level;
37 const char get_optval[64];
38 socklen_t get_optlen;
39 socklen_t get_optlen_ret;
40
41 enum sockopt_test_error error;
b9ec9132 42 bool io_uring_support;
9ec8a4c9
SF
43} tests[] = {
44
45 /* ==================== getsockopt ==================== */
46
47 {
48 .descr = "getsockopt: no expected_attach_type",
49 .insns = {
50 /* return 1 */
51 BPF_MOV64_IMM(BPF_REG_0, 1),
52 BPF_EXIT_INSN(),
53
54 },
55 .attach_type = BPF_CGROUP_GETSOCKOPT,
56 .expected_attach_type = 0,
57 .error = DENY_LOAD,
58 },
59 {
60 .descr = "getsockopt: wrong expected_attach_type",
61 .insns = {
62 /* return 1 */
63 BPF_MOV64_IMM(BPF_REG_0, 1),
64 BPF_EXIT_INSN(),
65
66 },
67 .attach_type = BPF_CGROUP_GETSOCKOPT,
68 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
69 .error = DENY_ATTACH,
70 },
71 {
72 .descr = "getsockopt: bypass bpf hook",
73 .insns = {
74 /* return 1 */
75 BPF_MOV64_IMM(BPF_REG_0, 1),
76 BPF_EXIT_INSN(),
77 },
78 .attach_type = BPF_CGROUP_GETSOCKOPT,
79 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
80
81 .get_level = SOL_IP,
82 .set_level = SOL_IP,
83
84 .get_optname = IP_TOS,
85 .set_optname = IP_TOS,
86
87 .set_optval = { 1 << 3 },
88 .set_optlen = 1,
89
90 .get_optval = { 1 << 3 },
91 .get_optlen = 1,
92 },
93 {
94 .descr = "getsockopt: return EPERM from bpf hook",
95 .insns = {
96 BPF_MOV64_IMM(BPF_REG_0, 0),
97 BPF_EXIT_INSN(),
98 },
99 .attach_type = BPF_CGROUP_GETSOCKOPT,
100 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
101
102 .get_level = SOL_IP,
103 .get_optname = IP_TOS,
104
105 .get_optlen = 1,
106 .error = EPERM_GETSOCKOPT,
107 },
108 {
109 .descr = "getsockopt: no optval bounds check, deny loading",
110 .insns = {
111 /* r6 = ctx->optval */
112 BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1,
113 offsetof(struct bpf_sockopt, optval)),
114
115 /* ctx->optval[0] = 0x80 */
116 BPF_MOV64_IMM(BPF_REG_0, 0x80),
117 BPF_STX_MEM(BPF_W, BPF_REG_6, BPF_REG_0, 0),
118
119 /* return 1 */
120 BPF_MOV64_IMM(BPF_REG_0, 1),
121 BPF_EXIT_INSN(),
122 },
123 .attach_type = BPF_CGROUP_GETSOCKOPT,
124 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
125 .error = DENY_LOAD,
126 },
127 {
128 .descr = "getsockopt: read ctx->level",
129 .insns = {
130 /* r6 = ctx->level */
131 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
132 offsetof(struct bpf_sockopt, level)),
133
134 /* if (ctx->level == 123) { */
135 BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 123, 4),
136 /* ctx->retval = 0 */
137 BPF_MOV64_IMM(BPF_REG_0, 0),
138 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
139 offsetof(struct bpf_sockopt, retval)),
140 /* return 1 */
141 BPF_MOV64_IMM(BPF_REG_0, 1),
142 BPF_JMP_A(1),
143 /* } else { */
144 /* return 0 */
145 BPF_MOV64_IMM(BPF_REG_0, 0),
146 /* } */
147 BPF_EXIT_INSN(),
148 },
149 .attach_type = BPF_CGROUP_GETSOCKOPT,
150 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
151
152 .get_level = 123,
153
154 .get_optlen = 1,
155 },
156 {
157 .descr = "getsockopt: deny writing to ctx->level",
158 .insns = {
159 /* ctx->level = 1 */
160 BPF_MOV64_IMM(BPF_REG_0, 1),
161 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
162 offsetof(struct bpf_sockopt, level)),
163 BPF_EXIT_INSN(),
164 },
165 .attach_type = BPF_CGROUP_GETSOCKOPT,
166 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
167
168 .error = DENY_LOAD,
169 },
170 {
171 .descr = "getsockopt: read ctx->optname",
172 .insns = {
173 /* r6 = ctx->optname */
174 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
175 offsetof(struct bpf_sockopt, optname)),
176
177 /* if (ctx->optname == 123) { */
178 BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 123, 4),
179 /* ctx->retval = 0 */
180 BPF_MOV64_IMM(BPF_REG_0, 0),
181 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
182 offsetof(struct bpf_sockopt, retval)),
183 /* return 1 */
184 BPF_MOV64_IMM(BPF_REG_0, 1),
185 BPF_JMP_A(1),
186 /* } else { */
187 /* return 0 */
188 BPF_MOV64_IMM(BPF_REG_0, 0),
189 /* } */
190 BPF_EXIT_INSN(),
191 },
192 .attach_type = BPF_CGROUP_GETSOCKOPT,
193 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
194
195 .get_optname = 123,
196
197 .get_optlen = 1,
198 },
199 {
200 .descr = "getsockopt: read ctx->retval",
201 .insns = {
202 /* r6 = ctx->retval */
203 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
204 offsetof(struct bpf_sockopt, retval)),
205
206 /* return 1 */
207 BPF_MOV64_IMM(BPF_REG_0, 1),
208 BPF_EXIT_INSN(),
209 },
210 .attach_type = BPF_CGROUP_GETSOCKOPT,
211 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
212
213 .get_level = SOL_IP,
214 .get_optname = IP_TOS,
215 .get_optlen = 1,
216 },
217 {
218 .descr = "getsockopt: deny writing to ctx->optname",
219 .insns = {
220 /* ctx->optname = 1 */
221 BPF_MOV64_IMM(BPF_REG_0, 1),
222 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
223 offsetof(struct bpf_sockopt, optname)),
224 BPF_EXIT_INSN(),
225 },
226 .attach_type = BPF_CGROUP_GETSOCKOPT,
227 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
228
229 .error = DENY_LOAD,
230 },
231 {
232 .descr = "getsockopt: read ctx->optlen",
233 .insns = {
234 /* r6 = ctx->optlen */
235 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
236 offsetof(struct bpf_sockopt, optlen)),
237
238 /* if (ctx->optlen == 64) { */
239 BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 64, 4),
240 /* ctx->retval = 0 */
241 BPF_MOV64_IMM(BPF_REG_0, 0),
242 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
243 offsetof(struct bpf_sockopt, retval)),
244 /* return 1 */
245 BPF_MOV64_IMM(BPF_REG_0, 1),
246 BPF_JMP_A(1),
247 /* } else { */
248 /* return 0 */
249 BPF_MOV64_IMM(BPF_REG_0, 0),
250 /* } */
251 BPF_EXIT_INSN(),
252 },
253 .attach_type = BPF_CGROUP_GETSOCKOPT,
254 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
255
b9ec9132 256 .get_level = SOL_SOCKET,
9ec8a4c9 257 .get_optlen = 64,
b9ec9132 258 .io_uring_support = true,
9ec8a4c9
SF
259 },
260 {
261 .descr = "getsockopt: deny bigger ctx->optlen",
262 .insns = {
263 /* ctx->optlen = 65 */
264 BPF_MOV64_IMM(BPF_REG_0, 65),
265 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
266 offsetof(struct bpf_sockopt, optlen)),
267
268 /* ctx->retval = 0 */
269 BPF_MOV64_IMM(BPF_REG_0, 0),
270 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
271 offsetof(struct bpf_sockopt, retval)),
272
273 /* return 1 */
274 BPF_MOV64_IMM(BPF_REG_0, 1),
275 BPF_EXIT_INSN(),
276 },
277 .attach_type = BPF_CGROUP_GETSOCKOPT,
278 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
279
280 .get_optlen = 64,
281
282 .error = EFAULT_GETSOCKOPT,
b9ec9132 283 .io_uring_support = true,
9ec8a4c9
SF
284 },
285 {
989a4a7d 286 .descr = "getsockopt: ignore >PAGE_SIZE optlen",
9ec8a4c9 287 .insns = {
989a4a7d
SF
288 /* write 0xFF to the first optval byte */
289
290 /* r6 = ctx->optval */
291 BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1,
292 offsetof(struct bpf_sockopt, optval)),
293 /* r2 = ctx->optval */
294 BPF_MOV64_REG(BPF_REG_2, BPF_REG_6),
295 /* r6 = ctx->optval + 1 */
296 BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 1),
297
298 /* r7 = ctx->optval_end */
299 BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_1,
300 offsetof(struct bpf_sockopt, optval_end)),
301
302 /* if (ctx->optval + 1 <= ctx->optval_end) { */
303 BPF_JMP_REG(BPF_JGT, BPF_REG_6, BPF_REG_7, 1),
304 /* ctx->optval[0] = 0xF0 */
305 BPF_ST_MEM(BPF_B, BPF_REG_2, 0, 0xFF),
306 /* } */
307
308 /* retval changes are ignored */
309 /* ctx->retval = 5 */
310 BPF_MOV64_IMM(BPF_REG_0, 5),
9ec8a4c9
SF
311 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
312 offsetof(struct bpf_sockopt, retval)),
313
314 /* return 1 */
315 BPF_MOV64_IMM(BPF_REG_0, 1),
316 BPF_EXIT_INSN(),
317 },
318 .attach_type = BPF_CGROUP_GETSOCKOPT,
319 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
320
989a4a7d
SF
321 .get_level = 1234,
322 .get_optname = 5678,
323 .get_optval = {}, /* the changes are ignored */
324 .get_optlen = PAGE_SIZE + 1,
325 .error = EOPNOTSUPP_GETSOCKOPT,
b9ec9132 326 .io_uring_support = true,
9ec8a4c9
SF
327 },
328 {
329 .descr = "getsockopt: support smaller ctx->optlen",
330 .insns = {
331 /* ctx->optlen = 32 */
332 BPF_MOV64_IMM(BPF_REG_0, 32),
333 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
334 offsetof(struct bpf_sockopt, optlen)),
335 /* ctx->retval = 0 */
336 BPF_MOV64_IMM(BPF_REG_0, 0),
337 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
338 offsetof(struct bpf_sockopt, retval)),
339 /* return 1 */
340 BPF_MOV64_IMM(BPF_REG_0, 1),
341 BPF_EXIT_INSN(),
342 },
343 .attach_type = BPF_CGROUP_GETSOCKOPT,
344 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
345
b9ec9132 346 .get_level = SOL_SOCKET,
9ec8a4c9
SF
347 .get_optlen = 64,
348 .get_optlen_ret = 32,
b9ec9132 349 .io_uring_support = true,
9ec8a4c9
SF
350 },
351 {
352 .descr = "getsockopt: deny writing to ctx->optval",
353 .insns = {
354 /* ctx->optval = 1 */
355 BPF_MOV64_IMM(BPF_REG_0, 1),
356 BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0,
357 offsetof(struct bpf_sockopt, optval)),
358 BPF_EXIT_INSN(),
359 },
360 .attach_type = BPF_CGROUP_GETSOCKOPT,
361 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
362
363 .error = DENY_LOAD,
364 },
365 {
366 .descr = "getsockopt: deny writing to ctx->optval_end",
367 .insns = {
368 /* ctx->optval_end = 1 */
369 BPF_MOV64_IMM(BPF_REG_0, 1),
370 BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0,
371 offsetof(struct bpf_sockopt, optval_end)),
372 BPF_EXIT_INSN(),
373 },
374 .attach_type = BPF_CGROUP_GETSOCKOPT,
375 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
376
377 .error = DENY_LOAD,
378 },
379 {
380 .descr = "getsockopt: rewrite value",
381 .insns = {
382 /* r6 = ctx->optval */
383 BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1,
384 offsetof(struct bpf_sockopt, optval)),
385 /* r2 = ctx->optval */
386 BPF_MOV64_REG(BPF_REG_2, BPF_REG_6),
387 /* r6 = ctx->optval + 1 */
388 BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 1),
389
390 /* r7 = ctx->optval_end */
391 BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_1,
392 offsetof(struct bpf_sockopt, optval_end)),
393
394 /* if (ctx->optval + 1 <= ctx->optval_end) { */
395 BPF_JMP_REG(BPF_JGT, BPF_REG_6, BPF_REG_7, 1),
396 /* ctx->optval[0] = 0xF0 */
397 BPF_ST_MEM(BPF_B, BPF_REG_2, 0, 0xF0),
398 /* } */
399
400 /* ctx->retval = 0 */
401 BPF_MOV64_IMM(BPF_REG_0, 0),
402 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
403 offsetof(struct bpf_sockopt, retval)),
404
405 /* return 1*/
406 BPF_MOV64_IMM(BPF_REG_0, 1),
407 BPF_EXIT_INSN(),
408 },
409 .attach_type = BPF_CGROUP_GETSOCKOPT,
410 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
411
412 .get_level = SOL_IP,
413 .get_optname = IP_TOS,
414
415 .get_optval = { 0xF0 },
416 .get_optlen = 1,
417 },
418
419 /* ==================== setsockopt ==================== */
420
421 {
422 .descr = "setsockopt: no expected_attach_type",
423 .insns = {
424 /* return 1 */
425 BPF_MOV64_IMM(BPF_REG_0, 1),
426 BPF_EXIT_INSN(),
427
428 },
429 .attach_type = BPF_CGROUP_SETSOCKOPT,
430 .expected_attach_type = 0,
431 .error = DENY_LOAD,
432 },
433 {
434 .descr = "setsockopt: wrong expected_attach_type",
435 .insns = {
436 /* return 1 */
437 BPF_MOV64_IMM(BPF_REG_0, 1),
438 BPF_EXIT_INSN(),
439
440 },
441 .attach_type = BPF_CGROUP_SETSOCKOPT,
442 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
443 .error = DENY_ATTACH,
444 },
445 {
446 .descr = "setsockopt: bypass bpf hook",
447 .insns = {
448 /* return 1 */
449 BPF_MOV64_IMM(BPF_REG_0, 1),
450 BPF_EXIT_INSN(),
451 },
452 .attach_type = BPF_CGROUP_SETSOCKOPT,
453 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
454
455 .get_level = SOL_IP,
456 .set_level = SOL_IP,
457
458 .get_optname = IP_TOS,
459 .set_optname = IP_TOS,
460
461 .set_optval = { 1 << 3 },
462 .set_optlen = 1,
463
464 .get_optval = { 1 << 3 },
465 .get_optlen = 1,
466 },
467 {
468 .descr = "setsockopt: return EPERM from bpf hook",
469 .insns = {
470 /* return 0 */
471 BPF_MOV64_IMM(BPF_REG_0, 0),
472 BPF_EXIT_INSN(),
473 },
474 .attach_type = BPF_CGROUP_SETSOCKOPT,
475 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
476
477 .set_level = SOL_IP,
478 .set_optname = IP_TOS,
479
480 .set_optlen = 1,
481 .error = EPERM_SETSOCKOPT,
482 },
483 {
484 .descr = "setsockopt: no optval bounds check, deny loading",
485 .insns = {
486 /* r6 = ctx->optval */
487 BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1,
488 offsetof(struct bpf_sockopt, optval)),
489
490 /* r0 = ctx->optval[0] */
491 BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_6, 0),
492
493 /* return 1 */
494 BPF_MOV64_IMM(BPF_REG_0, 1),
495 BPF_EXIT_INSN(),
496 },
497 .attach_type = BPF_CGROUP_SETSOCKOPT,
498 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
499 .error = DENY_LOAD,
500 },
501 {
502 .descr = "setsockopt: read ctx->level",
503 .insns = {
504 /* r6 = ctx->level */
505 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
506 offsetof(struct bpf_sockopt, level)),
507
508 /* if (ctx->level == 123) { */
509 BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 123, 4),
510 /* ctx->optlen = -1 */
511 BPF_MOV64_IMM(BPF_REG_0, -1),
512 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
513 offsetof(struct bpf_sockopt, optlen)),
514 /* return 1 */
515 BPF_MOV64_IMM(BPF_REG_0, 1),
516 BPF_JMP_A(1),
517 /* } else { */
518 /* return 0 */
519 BPF_MOV64_IMM(BPF_REG_0, 0),
520 /* } */
521 BPF_EXIT_INSN(),
522 },
523 .attach_type = BPF_CGROUP_SETSOCKOPT,
524 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
525
526 .set_level = 123,
527
528 .set_optlen = 1,
b9ec9132 529 .io_uring_support = true,
9ec8a4c9
SF
530 },
531 {
532 .descr = "setsockopt: allow changing ctx->level",
533 .insns = {
534 /* ctx->level = SOL_IP */
535 BPF_MOV64_IMM(BPF_REG_0, SOL_IP),
536 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
537 offsetof(struct bpf_sockopt, level)),
538 /* return 1 */
539 BPF_MOV64_IMM(BPF_REG_0, 1),
540 BPF_EXIT_INSN(),
541 },
542 .attach_type = BPF_CGROUP_SETSOCKOPT,
543 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
544
545 .get_level = SOL_IP,
546 .set_level = 234, /* should be rewritten to SOL_IP */
547
548 .get_optname = IP_TOS,
549 .set_optname = IP_TOS,
550
551 .set_optval = { 1 << 3 },
552 .set_optlen = 1,
553 .get_optval = { 1 << 3 },
554 .get_optlen = 1,
555 },
556 {
557 .descr = "setsockopt: read ctx->optname",
558 .insns = {
559 /* r6 = ctx->optname */
560 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
561 offsetof(struct bpf_sockopt, optname)),
562
563 /* if (ctx->optname == 123) { */
564 BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 123, 4),
565 /* ctx->optlen = -1 */
566 BPF_MOV64_IMM(BPF_REG_0, -1),
567 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
568 offsetof(struct bpf_sockopt, optlen)),
569 /* return 1 */
570 BPF_MOV64_IMM(BPF_REG_0, 1),
571 BPF_JMP_A(1),
572 /* } else { */
573 /* return 0 */
574 BPF_MOV64_IMM(BPF_REG_0, 0),
575 /* } */
576 BPF_EXIT_INSN(),
577 },
578 .attach_type = BPF_CGROUP_SETSOCKOPT,
579 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
580
581 .set_optname = 123,
582
583 .set_optlen = 1,
b9ec9132 584 .io_uring_support = true,
9ec8a4c9
SF
585 },
586 {
587 .descr = "setsockopt: allow changing ctx->optname",
588 .insns = {
589 /* ctx->optname = IP_TOS */
590 BPF_MOV64_IMM(BPF_REG_0, IP_TOS),
591 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
592 offsetof(struct bpf_sockopt, optname)),
593 /* return 1 */
594 BPF_MOV64_IMM(BPF_REG_0, 1),
595 BPF_EXIT_INSN(),
596 },
597 .attach_type = BPF_CGROUP_SETSOCKOPT,
598 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
599
600 .get_level = SOL_IP,
601 .set_level = SOL_IP,
602
603 .get_optname = IP_TOS,
604 .set_optname = 456, /* should be rewritten to IP_TOS */
605
606 .set_optval = { 1 << 3 },
607 .set_optlen = 1,
608 .get_optval = { 1 << 3 },
609 .get_optlen = 1,
610 },
611 {
612 .descr = "setsockopt: read ctx->optlen",
613 .insns = {
614 /* r6 = ctx->optlen */
615 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
616 offsetof(struct bpf_sockopt, optlen)),
617
618 /* if (ctx->optlen == 64) { */
619 BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 64, 4),
620 /* ctx->optlen = -1 */
621 BPF_MOV64_IMM(BPF_REG_0, -1),
622 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
623 offsetof(struct bpf_sockopt, optlen)),
624 /* return 1 */
625 BPF_MOV64_IMM(BPF_REG_0, 1),
626 BPF_JMP_A(1),
627 /* } else { */
628 /* return 0 */
629 BPF_MOV64_IMM(BPF_REG_0, 0),
630 /* } */
631 BPF_EXIT_INSN(),
632 },
633 .attach_type = BPF_CGROUP_SETSOCKOPT,
634 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
635
636 .set_optlen = 64,
b9ec9132 637 .io_uring_support = true,
9ec8a4c9
SF
638 },
639 {
640 .descr = "setsockopt: ctx->optlen == -1 is ok",
641 .insns = {
642 /* ctx->optlen = -1 */
643 BPF_MOV64_IMM(BPF_REG_0, -1),
644 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
645 offsetof(struct bpf_sockopt, optlen)),
646 /* return 1 */
647 BPF_MOV64_IMM(BPF_REG_0, 1),
648 BPF_EXIT_INSN(),
649 },
650 .attach_type = BPF_CGROUP_SETSOCKOPT,
651 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
652
653 .set_optlen = 64,
b9ec9132 654 .io_uring_support = true,
9ec8a4c9
SF
655 },
656 {
657 .descr = "setsockopt: deny ctx->optlen < 0 (except -1)",
658 .insns = {
659 /* ctx->optlen = -2 */
660 BPF_MOV64_IMM(BPF_REG_0, -2),
661 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
662 offsetof(struct bpf_sockopt, optlen)),
663 /* return 1 */
664 BPF_MOV64_IMM(BPF_REG_0, 1),
665 BPF_EXIT_INSN(),
666 },
667 .attach_type = BPF_CGROUP_SETSOCKOPT,
668 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
669
670 .set_optlen = 4,
671
672 .error = EFAULT_SETSOCKOPT,
b9ec9132 673 .io_uring_support = true,
9ec8a4c9
SF
674 },
675 {
676 .descr = "setsockopt: deny ctx->optlen > input optlen",
677 .insns = {
678 /* ctx->optlen = 65 */
679 BPF_MOV64_IMM(BPF_REG_0, 65),
680 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
681 offsetof(struct bpf_sockopt, optlen)),
682 BPF_MOV64_IMM(BPF_REG_0, 1),
683 BPF_EXIT_INSN(),
684 },
685 .attach_type = BPF_CGROUP_SETSOCKOPT,
686 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
687
688 .set_optlen = 64,
689
690 .error = EFAULT_SETSOCKOPT,
b9ec9132 691 .io_uring_support = true,
9ec8a4c9 692 },
989a4a7d
SF
693 {
694 .descr = "setsockopt: ignore >PAGE_SIZE optlen",
695 .insns = {
696 /* write 0xFF to the first optval byte */
697
698 /* r6 = ctx->optval */
699 BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1,
700 offsetof(struct bpf_sockopt, optval)),
701 /* r2 = ctx->optval */
702 BPF_MOV64_REG(BPF_REG_2, BPF_REG_6),
703 /* r6 = ctx->optval + 1 */
704 BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 1),
705
706 /* r7 = ctx->optval_end */
707 BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_1,
708 offsetof(struct bpf_sockopt, optval_end)),
709
710 /* if (ctx->optval + 1 <= ctx->optval_end) { */
711 BPF_JMP_REG(BPF_JGT, BPF_REG_6, BPF_REG_7, 1),
712 /* ctx->optval[0] = 0xF0 */
713 BPF_ST_MEM(BPF_B, BPF_REG_2, 0, 0xF0),
714 /* } */
715
716 BPF_MOV64_IMM(BPF_REG_0, 1),
717 BPF_EXIT_INSN(),
718 },
719 .attach_type = BPF_CGROUP_SETSOCKOPT,
720 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
721
722 .set_level = SOL_IP,
723 .set_optname = IP_TOS,
724 .set_optval = {},
725 .set_optlen = PAGE_SIZE + 1,
726
727 .get_level = SOL_IP,
728 .get_optname = IP_TOS,
729 .get_optval = {}, /* the changes are ignored */
730 .get_optlen = 4,
731 },
9ec8a4c9
SF
732 {
733 .descr = "setsockopt: allow changing ctx->optlen within bounds",
734 .insns = {
735 /* r6 = ctx->optval */
736 BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1,
737 offsetof(struct bpf_sockopt, optval)),
738 /* r2 = ctx->optval */
739 BPF_MOV64_REG(BPF_REG_2, BPF_REG_6),
740 /* r6 = ctx->optval + 1 */
741 BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 1),
742
743 /* r7 = ctx->optval_end */
744 BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_1,
745 offsetof(struct bpf_sockopt, optval_end)),
746
747 /* if (ctx->optval + 1 <= ctx->optval_end) { */
748 BPF_JMP_REG(BPF_JGT, BPF_REG_6, BPF_REG_7, 1),
749 /* ctx->optval[0] = 1 << 3 */
750 BPF_ST_MEM(BPF_B, BPF_REG_2, 0, 1 << 3),
751 /* } */
752
753 /* ctx->optlen = 1 */
754 BPF_MOV64_IMM(BPF_REG_0, 1),
755 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
756 offsetof(struct bpf_sockopt, optlen)),
757
758 /* return 1*/
759 BPF_MOV64_IMM(BPF_REG_0, 1),
760 BPF_EXIT_INSN(),
761 },
762 .attach_type = BPF_CGROUP_SETSOCKOPT,
763 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
764
765 .get_level = SOL_IP,
766 .set_level = SOL_IP,
767
768 .get_optname = IP_TOS,
769 .set_optname = IP_TOS,
770
771 .set_optval = { 1, 1, 1, 1 },
772 .set_optlen = 4,
773 .get_optval = { 1 << 3 },
774 .get_optlen = 1,
775 },
776 {
777 .descr = "setsockopt: deny write ctx->retval",
778 .insns = {
779 /* ctx->retval = 0 */
780 BPF_MOV64_IMM(BPF_REG_0, 0),
781 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
782 offsetof(struct bpf_sockopt, retval)),
783
784 /* return 1 */
785 BPF_MOV64_IMM(BPF_REG_0, 1),
786 BPF_EXIT_INSN(),
787 },
788 .attach_type = BPF_CGROUP_SETSOCKOPT,
789 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
790
791 .error = DENY_LOAD,
792 },
793 {
794 .descr = "setsockopt: deny read ctx->retval",
795 .insns = {
796 /* r6 = ctx->retval */
797 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
798 offsetof(struct bpf_sockopt, retval)),
799
800 /* return 1 */
801 BPF_MOV64_IMM(BPF_REG_0, 1),
802 BPF_EXIT_INSN(),
803 },
804 .attach_type = BPF_CGROUP_SETSOCKOPT,
805 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
806
807 .error = DENY_LOAD,
808 },
809 {
810 .descr = "setsockopt: deny writing to ctx->optval",
811 .insns = {
812 /* ctx->optval = 1 */
813 BPF_MOV64_IMM(BPF_REG_0, 1),
814 BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0,
815 offsetof(struct bpf_sockopt, optval)),
816 BPF_EXIT_INSN(),
817 },
818 .attach_type = BPF_CGROUP_SETSOCKOPT,
819 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
820
821 .error = DENY_LOAD,
822 },
823 {
824 .descr = "setsockopt: deny writing to ctx->optval_end",
825 .insns = {
826 /* ctx->optval_end = 1 */
827 BPF_MOV64_IMM(BPF_REG_0, 1),
828 BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0,
829 offsetof(struct bpf_sockopt, optval_end)),
830 BPF_EXIT_INSN(),
831 },
832 .attach_type = BPF_CGROUP_SETSOCKOPT,
833 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
834
835 .error = DENY_LOAD,
836 },
837 {
838 .descr = "setsockopt: allow IP_TOS <= 128",
839 .insns = {
840 /* r6 = ctx->optval */
841 BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1,
842 offsetof(struct bpf_sockopt, optval)),
843 /* r7 = ctx->optval + 1 */
844 BPF_MOV64_REG(BPF_REG_7, BPF_REG_6),
845 BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, 1),
846
847 /* r8 = ctx->optval_end */
848 BPF_LDX_MEM(BPF_DW, BPF_REG_8, BPF_REG_1,
849 offsetof(struct bpf_sockopt, optval_end)),
850
851 /* if (ctx->optval + 1 <= ctx->optval_end) { */
852 BPF_JMP_REG(BPF_JGT, BPF_REG_7, BPF_REG_8, 4),
853
854 /* r9 = ctx->optval[0] */
855 BPF_LDX_MEM(BPF_B, BPF_REG_9, BPF_REG_6, 0),
856
857 /* if (ctx->optval[0] < 128) */
858 BPF_JMP_IMM(BPF_JGT, BPF_REG_9, 128, 2),
859 BPF_MOV64_IMM(BPF_REG_0, 1),
860 BPF_JMP_A(1),
861 /* } */
862
863 /* } else { */
864 BPF_MOV64_IMM(BPF_REG_0, 0),
865 /* } */
866
867 BPF_EXIT_INSN(),
868 },
869 .attach_type = BPF_CGROUP_SETSOCKOPT,
870 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
871
872 .get_level = SOL_IP,
873 .set_level = SOL_IP,
874
875 .get_optname = IP_TOS,
876 .set_optname = IP_TOS,
877
878 .set_optval = { 0x80 },
879 .set_optlen = 1,
880 .get_optval = { 0x80 },
881 .get_optlen = 1,
882 },
883 {
884 .descr = "setsockopt: deny IP_TOS > 128",
885 .insns = {
886 /* r6 = ctx->optval */
887 BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1,
888 offsetof(struct bpf_sockopt, optval)),
889 /* r7 = ctx->optval + 1 */
890 BPF_MOV64_REG(BPF_REG_7, BPF_REG_6),
891 BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, 1),
892
893 /* r8 = ctx->optval_end */
894 BPF_LDX_MEM(BPF_DW, BPF_REG_8, BPF_REG_1,
895 offsetof(struct bpf_sockopt, optval_end)),
896
897 /* if (ctx->optval + 1 <= ctx->optval_end) { */
898 BPF_JMP_REG(BPF_JGT, BPF_REG_7, BPF_REG_8, 4),
899
900 /* r9 = ctx->optval[0] */
901 BPF_LDX_MEM(BPF_B, BPF_REG_9, BPF_REG_6, 0),
902
903 /* if (ctx->optval[0] < 128) */
904 BPF_JMP_IMM(BPF_JGT, BPF_REG_9, 128, 2),
905 BPF_MOV64_IMM(BPF_REG_0, 1),
906 BPF_JMP_A(1),
907 /* } */
908
909 /* } else { */
910 BPF_MOV64_IMM(BPF_REG_0, 0),
911 /* } */
912
913 BPF_EXIT_INSN(),
914 },
915 .attach_type = BPF_CGROUP_SETSOCKOPT,
916 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
917
918 .get_level = SOL_IP,
919 .set_level = SOL_IP,
920
921 .get_optname = IP_TOS,
922 .set_optname = IP_TOS,
923
924 .set_optval = { 0x81 },
925 .set_optlen = 1,
926 .get_optval = { 0x00 },
927 .get_optlen = 1,
928
929 .error = EPERM_SETSOCKOPT,
930 },
931};
932
933static int load_prog(const struct bpf_insn *insns,
934 enum bpf_attach_type expected_attach_type)
935{
d8e86407 936 LIBBPF_OPTS(bpf_prog_load_opts, opts,
9ec8a4c9 937 .expected_attach_type = expected_attach_type,
9ec8a4c9 938 .log_level = 2,
d8e86407
AN
939 .log_buf = bpf_log_buf,
940 .log_size = sizeof(bpf_log_buf),
941 );
942 int fd, insns_cnt = 0;
9ec8a4c9
SF
943
944 for (;
d8e86407
AN
945 insns[insns_cnt].code != (BPF_JMP | BPF_EXIT);
946 insns_cnt++) {
9ec8a4c9 947 }
d8e86407 948 insns_cnt++;
9ec8a4c9 949
d8e86407 950 fd = bpf_prog_load(BPF_PROG_TYPE_CGROUP_SOCKOPT, NULL, "GPL", insns, insns_cnt, &opts);
9ec8a4c9
SF
951 if (verbose && fd < 0)
952 fprintf(stderr, "%s\n", bpf_log_buf);
953
954 return fd;
955}
956
b9ec9132
BL
957/* Core function that handles io_uring ring initialization,
958 * sending SQE with sockopt command and waiting for the CQE.
959 */
960static int uring_sockopt(int op, int fd, int level, int optname,
961 const void *optval, socklen_t optlen)
962{
963 struct io_uring_cqe *cqe;
964 struct io_uring_sqe *sqe;
965 struct io_uring ring;
966 int err;
967
968 err = io_uring_queue_init(1, &ring, 0);
969 if (!ASSERT_OK(err, "io_uring initialization"))
970 return err;
971
972 sqe = io_uring_get_sqe(&ring);
973 if (!ASSERT_NEQ(sqe, NULL, "Get an SQE")) {
974 err = -1;
975 goto fail;
976 }
977
978 io_uring_prep_cmd(sqe, op, fd, level, optname, optval, optlen);
979
980 err = io_uring_submit(&ring);
981 if (!ASSERT_EQ(err, 1, "Submit SQE"))
982 goto fail;
983
984 err = io_uring_wait_cqe(&ring, &cqe);
985 if (!ASSERT_OK(err, "Wait for CQE"))
986 goto fail;
987
988 err = cqe->res;
989
990fail:
991 io_uring_queue_exit(&ring);
992
993 return err;
994}
995
996static int uring_setsockopt(int fd, int level, int optname, const void *optval,
997 socklen_t optlen)
998{
999 return uring_sockopt(SOCKET_URING_OP_SETSOCKOPT, fd, level, optname,
1000 optval, optlen);
1001}
1002
1003static int uring_getsockopt(int fd, int level, int optname, void *optval,
1004 socklen_t *optlen)
1005{
1006 int ret = uring_sockopt(SOCKET_URING_OP_GETSOCKOPT, fd, level, optname,
1007 optval, *optlen);
1008 if (ret < 0)
1009 return ret;
1010
1011 /* Populate optlen back to be compatible with systemcall interface,
1012 * and simplify the test.
1013 */
1014 *optlen = ret;
1015
1016 return 0;
1017}
1018
1019/* Execute the setsocktopt operation */
1020static int call_setsockopt(bool use_io_uring, int fd, int level, int optname,
1021 const void *optval, socklen_t optlen)
1022{
1023 if (use_io_uring)
1024 return uring_setsockopt(fd, level, optname, optval, optlen);
1025
1026 return setsockopt(fd, level, optname, optval, optlen);
1027}
1028
1029/* Execute the getsocktopt operation */
1030static int call_getsockopt(bool use_io_uring, int fd, int level, int optname,
1031 void *optval, socklen_t *optlen)
1032{
1033 if (use_io_uring)
1034 return uring_getsockopt(fd, level, optname, optval, optlen);
1035
1036 return getsockopt(fd, level, optname, optval, optlen);
1037}
1038
d70b2660
SF
1039static int run_test(int cgroup_fd, struct sockopt_test *test, bool use_io_uring,
1040 bool use_link)
9ec8a4c9 1041{
d70b2660 1042 int sock_fd, err, prog_fd, link_fd = -1;
9ec8a4c9
SF
1043 void *optval = NULL;
1044 int ret = 0;
1045
1046 prog_fd = load_prog(test->insns, test->expected_attach_type);
1047 if (prog_fd < 0) {
1048 if (test->error == DENY_LOAD)
1049 return 0;
1050
1051 log_err("Failed to load BPF program");
1052 return -1;
1053 }
1054
d70b2660
SF
1055 if (use_link) {
1056 err = bpf_link_create(prog_fd, cgroup_fd, test->attach_type, NULL);
1057 link_fd = err;
1058 } else {
1059 err = bpf_prog_attach(prog_fd, cgroup_fd, test->attach_type, 0);
1060 }
9ec8a4c9
SF
1061 if (err < 0) {
1062 if (test->error == DENY_ATTACH)
1063 goto close_prog_fd;
1064
1065 log_err("Failed to attach BPF program");
1066 ret = -1;
1067 goto close_prog_fd;
1068 }
1069
1070 sock_fd = socket(AF_INET, SOCK_STREAM, 0);
1071 if (sock_fd < 0) {
1072 log_err("Failed to create AF_INET socket");
1073 ret = -1;
1074 goto detach_prog;
1075 }
1076
1077 if (test->set_optlen) {
989a4a7d
SF
1078 if (test->set_optlen >= PAGE_SIZE) {
1079 int num_pages = test->set_optlen / PAGE_SIZE;
1080 int remainder = test->set_optlen % PAGE_SIZE;
1081
1082 test->set_optlen = num_pages * sysconf(_SC_PAGESIZE) + remainder;
1083 }
1084
b9ec9132
BL
1085 err = call_setsockopt(use_io_uring, sock_fd, test->set_level,
1086 test->set_optname, test->set_optval,
1087 test->set_optlen);
9ec8a4c9
SF
1088 if (err) {
1089 if (errno == EPERM && test->error == EPERM_SETSOCKOPT)
1090 goto close_sock_fd;
1091 if (errno == EFAULT && test->error == EFAULT_SETSOCKOPT)
1092 goto free_optval;
1093
1094 log_err("Failed to call setsockopt");
1095 ret = -1;
1096 goto close_sock_fd;
1097 }
1098 }
1099
1100 if (test->get_optlen) {
989a4a7d
SF
1101 if (test->get_optlen >= PAGE_SIZE) {
1102 int num_pages = test->get_optlen / PAGE_SIZE;
1103 int remainder = test->get_optlen % PAGE_SIZE;
1104
1105 test->get_optlen = num_pages * sysconf(_SC_PAGESIZE) + remainder;
1106 }
1107
9ec8a4c9 1108 optval = malloc(test->get_optlen);
989a4a7d 1109 memset(optval, 0, test->get_optlen);
9ec8a4c9
SF
1110 socklen_t optlen = test->get_optlen;
1111 socklen_t expected_get_optlen = test->get_optlen_ret ?:
1112 test->get_optlen;
1113
b9ec9132
BL
1114 err = call_getsockopt(use_io_uring, sock_fd, test->get_level,
1115 test->get_optname, optval, &optlen);
9ec8a4c9 1116 if (err) {
989a4a7d
SF
1117 if (errno == EOPNOTSUPP && test->error == EOPNOTSUPP_GETSOCKOPT)
1118 goto free_optval;
9ec8a4c9
SF
1119 if (errno == EPERM && test->error == EPERM_GETSOCKOPT)
1120 goto free_optval;
1121 if (errno == EFAULT && test->error == EFAULT_GETSOCKOPT)
1122 goto free_optval;
1123
1124 log_err("Failed to call getsockopt");
1125 ret = -1;
1126 goto free_optval;
1127 }
1128
1129 if (optlen != expected_get_optlen) {
1130 errno = 0;
1131 log_err("getsockopt returned unexpected optlen");
1132 ret = -1;
1133 goto free_optval;
1134 }
1135
1136 if (memcmp(optval, test->get_optval, optlen) != 0) {
1137 errno = 0;
1138 log_err("getsockopt returned unexpected optval");
1139 ret = -1;
1140 goto free_optval;
1141 }
1142 }
1143
1144 ret = test->error != OK;
1145
1146free_optval:
1147 free(optval);
1148close_sock_fd:
1149 close(sock_fd);
1150detach_prog:
d70b2660
SF
1151 if (use_link) {
1152 if (link_fd >= 0)
1153 close(link_fd);
1154 } else {
1155 bpf_prog_detach2(prog_fd, cgroup_fd, test->attach_type);
1156 }
9ec8a4c9
SF
1157close_prog_fd:
1158 close(prog_fd);
1159 return ret;
1160}
1161
4a647421 1162void test_sockopt(void)
9ec8a4c9 1163{
9ec8a4c9
SF
1164 int cgroup_fd, i;
1165
4a647421 1166 cgroup_fd = test__join_cgroup("/sockopt");
099763e7 1167 if (!ASSERT_GE(cgroup_fd, 0, "join_cgroup"))
4a647421 1168 return;
9ec8a4c9
SF
1169
1170 for (i = 0; i < ARRAY_SIZE(tests); i++) {
321a64b3
DM
1171 if (!test__start_subtest(tests[i].descr))
1172 continue;
1173
d70b2660
SF
1174 ASSERT_OK(run_test(cgroup_fd, &tests[i], false, false),
1175 tests[i].descr);
1176 ASSERT_OK(run_test(cgroup_fd, &tests[i], false, true),
b9ec9132
BL
1177 tests[i].descr);
1178 if (tests[i].io_uring_support)
d70b2660 1179 ASSERT_OK(run_test(cgroup_fd, &tests[i], true, false),
b9ec9132 1180 tests[i].descr);
9ec8a4c9
SF
1181 }
1182
9ec8a4c9 1183 close(cgroup_fd);
9ec8a4c9 1184}