1
2
3#include <errno.h>
4#include <stdio.h>
5#include <unistd.h>
6#include <sys/types.h>
7#include <sys/socket.h>
8#include <netinet/in.h>
9
10#include <linux/filter.h>
11#include <bpf/bpf.h>
12#include <bpf/libbpf.h>
13
14#include "bpf_rlimit.h"
15#include "bpf_util.h"
16#include "cgroup_helpers.h"
17
18#define CG_PATH "/sockopt"
19
20static char bpf_log_buf[4096];
21static bool verbose;
22
23enum sockopt_test_error {
24 OK = 0,
25 DENY_LOAD,
26 DENY_ATTACH,
27 EPERM_GETSOCKOPT,
28 EFAULT_GETSOCKOPT,
29 EPERM_SETSOCKOPT,
30 EFAULT_SETSOCKOPT,
31};
32
33static struct sockopt_test {
34 const char *descr;
35 const struct bpf_insn insns[64];
36 enum bpf_attach_type attach_type;
37 enum bpf_attach_type expected_attach_type;
38
39 int set_optname;
40 int set_level;
41 const char set_optval[64];
42 socklen_t set_optlen;
43
44 int get_optname;
45 int get_level;
46 const char get_optval[64];
47 socklen_t get_optlen;
48 socklen_t get_optlen_ret;
49
50 enum sockopt_test_error error;
51} tests[] = {
52
53
54
55 {
56 .descr = "getsockopt: no expected_attach_type",
57 .insns = {
58
59 BPF_MOV64_IMM(BPF_REG_0, 1),
60 BPF_EXIT_INSN(),
61
62 },
63 .attach_type = BPF_CGROUP_GETSOCKOPT,
64 .expected_attach_type = 0,
65 .error = DENY_LOAD,
66 },
67 {
68 .descr = "getsockopt: wrong expected_attach_type",
69 .insns = {
70
71 BPF_MOV64_IMM(BPF_REG_0, 1),
72 BPF_EXIT_INSN(),
73
74 },
75 .attach_type = BPF_CGROUP_GETSOCKOPT,
76 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
77 .error = DENY_ATTACH,
78 },
79 {
80 .descr = "getsockopt: bypass bpf hook",
81 .insns = {
82
83 BPF_MOV64_IMM(BPF_REG_0, 1),
84 BPF_EXIT_INSN(),
85 },
86 .attach_type = BPF_CGROUP_GETSOCKOPT,
87 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
88
89 .get_level = SOL_IP,
90 .set_level = SOL_IP,
91
92 .get_optname = IP_TOS,
93 .set_optname = IP_TOS,
94
95 .set_optval = { 1 << 3 },
96 .set_optlen = 1,
97
98 .get_optval = { 1 << 3 },
99 .get_optlen = 1,
100 },
101 {
102 .descr = "getsockopt: return EPERM from bpf hook",
103 .insns = {
104 BPF_MOV64_IMM(BPF_REG_0, 0),
105 BPF_EXIT_INSN(),
106 },
107 .attach_type = BPF_CGROUP_GETSOCKOPT,
108 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
109
110 .get_level = SOL_IP,
111 .get_optname = IP_TOS,
112
113 .get_optlen = 1,
114 .error = EPERM_GETSOCKOPT,
115 },
116 {
117 .descr = "getsockopt: no optval bounds check, deny loading",
118 .insns = {
119
120 BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1,
121 offsetof(struct bpf_sockopt, optval)),
122
123
124 BPF_MOV64_IMM(BPF_REG_0, 0x80),
125 BPF_STX_MEM(BPF_W, BPF_REG_6, BPF_REG_0, 0),
126
127
128 BPF_MOV64_IMM(BPF_REG_0, 1),
129 BPF_EXIT_INSN(),
130 },
131 .attach_type = BPF_CGROUP_GETSOCKOPT,
132 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
133 .error = DENY_LOAD,
134 },
135 {
136 .descr = "getsockopt: read ctx->level",
137 .insns = {
138
139 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
140 offsetof(struct bpf_sockopt, level)),
141
142
143 BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 123, 4),
144
145 BPF_MOV64_IMM(BPF_REG_0, 0),
146 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
147 offsetof(struct bpf_sockopt, retval)),
148
149 BPF_MOV64_IMM(BPF_REG_0, 1),
150 BPF_JMP_A(1),
151
152
153 BPF_MOV64_IMM(BPF_REG_0, 0),
154
155 BPF_EXIT_INSN(),
156 },
157 .attach_type = BPF_CGROUP_GETSOCKOPT,
158 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
159
160 .get_level = 123,
161
162 .get_optlen = 1,
163 },
164 {
165 .descr = "getsockopt: deny writing to ctx->level",
166 .insns = {
167
168 BPF_MOV64_IMM(BPF_REG_0, 1),
169 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
170 offsetof(struct bpf_sockopt, level)),
171 BPF_EXIT_INSN(),
172 },
173 .attach_type = BPF_CGROUP_GETSOCKOPT,
174 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
175
176 .error = DENY_LOAD,
177 },
178 {
179 .descr = "getsockopt: read ctx->optname",
180 .insns = {
181
182 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
183 offsetof(struct bpf_sockopt, optname)),
184
185
186 BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 123, 4),
187
188 BPF_MOV64_IMM(BPF_REG_0, 0),
189 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
190 offsetof(struct bpf_sockopt, retval)),
191
192 BPF_MOV64_IMM(BPF_REG_0, 1),
193 BPF_JMP_A(1),
194
195
196 BPF_MOV64_IMM(BPF_REG_0, 0),
197
198 BPF_EXIT_INSN(),
199 },
200 .attach_type = BPF_CGROUP_GETSOCKOPT,
201 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
202
203 .get_optname = 123,
204
205 .get_optlen = 1,
206 },
207 {
208 .descr = "getsockopt: read ctx->retval",
209 .insns = {
210
211 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
212 offsetof(struct bpf_sockopt, retval)),
213
214
215 BPF_MOV64_IMM(BPF_REG_0, 1),
216 BPF_EXIT_INSN(),
217 },
218 .attach_type = BPF_CGROUP_GETSOCKOPT,
219 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
220
221 .get_level = SOL_IP,
222 .get_optname = IP_TOS,
223 .get_optlen = 1,
224 },
225 {
226 .descr = "getsockopt: deny writing to ctx->optname",
227 .insns = {
228
229 BPF_MOV64_IMM(BPF_REG_0, 1),
230 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
231 offsetof(struct bpf_sockopt, optname)),
232 BPF_EXIT_INSN(),
233 },
234 .attach_type = BPF_CGROUP_GETSOCKOPT,
235 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
236
237 .error = DENY_LOAD,
238 },
239 {
240 .descr = "getsockopt: read ctx->optlen",
241 .insns = {
242
243 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
244 offsetof(struct bpf_sockopt, optlen)),
245
246
247 BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 64, 4),
248
249 BPF_MOV64_IMM(BPF_REG_0, 0),
250 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
251 offsetof(struct bpf_sockopt, retval)),
252
253 BPF_MOV64_IMM(BPF_REG_0, 1),
254 BPF_JMP_A(1),
255
256
257 BPF_MOV64_IMM(BPF_REG_0, 0),
258
259 BPF_EXIT_INSN(),
260 },
261 .attach_type = BPF_CGROUP_GETSOCKOPT,
262 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
263
264 .get_optlen = 64,
265 },
266 {
267 .descr = "getsockopt: deny bigger ctx->optlen",
268 .insns = {
269
270 BPF_MOV64_IMM(BPF_REG_0, 65),
271 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
272 offsetof(struct bpf_sockopt, optlen)),
273
274
275 BPF_MOV64_IMM(BPF_REG_0, 0),
276 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
277 offsetof(struct bpf_sockopt, retval)),
278
279
280 BPF_MOV64_IMM(BPF_REG_0, 1),
281 BPF_EXIT_INSN(),
282 },
283 .attach_type = BPF_CGROUP_GETSOCKOPT,
284 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
285
286 .get_optlen = 64,
287
288 .error = EFAULT_GETSOCKOPT,
289 },
290 {
291 .descr = "getsockopt: deny arbitrary ctx->retval",
292 .insns = {
293
294 BPF_MOV64_IMM(BPF_REG_0, 123),
295 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
296 offsetof(struct bpf_sockopt, retval)),
297
298
299 BPF_MOV64_IMM(BPF_REG_0, 1),
300 BPF_EXIT_INSN(),
301 },
302 .attach_type = BPF_CGROUP_GETSOCKOPT,
303 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
304
305 .get_optlen = 64,
306
307 .error = EFAULT_GETSOCKOPT,
308 },
309 {
310 .descr = "getsockopt: support smaller ctx->optlen",
311 .insns = {
312
313 BPF_MOV64_IMM(BPF_REG_0, 32),
314 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
315 offsetof(struct bpf_sockopt, optlen)),
316
317 BPF_MOV64_IMM(BPF_REG_0, 0),
318 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
319 offsetof(struct bpf_sockopt, retval)),
320
321 BPF_MOV64_IMM(BPF_REG_0, 1),
322 BPF_EXIT_INSN(),
323 },
324 .attach_type = BPF_CGROUP_GETSOCKOPT,
325 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
326
327 .get_optlen = 64,
328 .get_optlen_ret = 32,
329 },
330 {
331 .descr = "getsockopt: deny writing to ctx->optval",
332 .insns = {
333
334 BPF_MOV64_IMM(BPF_REG_0, 1),
335 BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0,
336 offsetof(struct bpf_sockopt, optval)),
337 BPF_EXIT_INSN(),
338 },
339 .attach_type = BPF_CGROUP_GETSOCKOPT,
340 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
341
342 .error = DENY_LOAD,
343 },
344 {
345 .descr = "getsockopt: deny writing to ctx->optval_end",
346 .insns = {
347
348 BPF_MOV64_IMM(BPF_REG_0, 1),
349 BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0,
350 offsetof(struct bpf_sockopt, optval_end)),
351 BPF_EXIT_INSN(),
352 },
353 .attach_type = BPF_CGROUP_GETSOCKOPT,
354 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
355
356 .error = DENY_LOAD,
357 },
358 {
359 .descr = "getsockopt: rewrite value",
360 .insns = {
361
362 BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1,
363 offsetof(struct bpf_sockopt, optval)),
364
365 BPF_MOV64_REG(BPF_REG_2, BPF_REG_6),
366
367 BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 1),
368
369
370 BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_1,
371 offsetof(struct bpf_sockopt, optval_end)),
372
373
374 BPF_JMP_REG(BPF_JGT, BPF_REG_6, BPF_REG_7, 1),
375
376 BPF_ST_MEM(BPF_B, BPF_REG_2, 0, 0xF0),
377
378
379
380 BPF_MOV64_IMM(BPF_REG_0, 0),
381 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
382 offsetof(struct bpf_sockopt, retval)),
383
384
385 BPF_MOV64_IMM(BPF_REG_0, 1),
386 BPF_EXIT_INSN(),
387 },
388 .attach_type = BPF_CGROUP_GETSOCKOPT,
389 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
390
391 .get_level = SOL_IP,
392 .get_optname = IP_TOS,
393
394 .get_optval = { 0xF0 },
395 .get_optlen = 1,
396 },
397
398
399
400 {
401 .descr = "setsockopt: no expected_attach_type",
402 .insns = {
403
404 BPF_MOV64_IMM(BPF_REG_0, 1),
405 BPF_EXIT_INSN(),
406
407 },
408 .attach_type = BPF_CGROUP_SETSOCKOPT,
409 .expected_attach_type = 0,
410 .error = DENY_LOAD,
411 },
412 {
413 .descr = "setsockopt: wrong expected_attach_type",
414 .insns = {
415
416 BPF_MOV64_IMM(BPF_REG_0, 1),
417 BPF_EXIT_INSN(),
418
419 },
420 .attach_type = BPF_CGROUP_SETSOCKOPT,
421 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
422 .error = DENY_ATTACH,
423 },
424 {
425 .descr = "setsockopt: bypass bpf hook",
426 .insns = {
427
428 BPF_MOV64_IMM(BPF_REG_0, 1),
429 BPF_EXIT_INSN(),
430 },
431 .attach_type = BPF_CGROUP_SETSOCKOPT,
432 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
433
434 .get_level = SOL_IP,
435 .set_level = SOL_IP,
436
437 .get_optname = IP_TOS,
438 .set_optname = IP_TOS,
439
440 .set_optval = { 1 << 3 },
441 .set_optlen = 1,
442
443 .get_optval = { 1 << 3 },
444 .get_optlen = 1,
445 },
446 {
447 .descr = "setsockopt: return EPERM from bpf hook",
448 .insns = {
449
450 BPF_MOV64_IMM(BPF_REG_0, 0),
451 BPF_EXIT_INSN(),
452 },
453 .attach_type = BPF_CGROUP_SETSOCKOPT,
454 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
455
456 .set_level = SOL_IP,
457 .set_optname = IP_TOS,
458
459 .set_optlen = 1,
460 .error = EPERM_SETSOCKOPT,
461 },
462 {
463 .descr = "setsockopt: no optval bounds check, deny loading",
464 .insns = {
465
466 BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1,
467 offsetof(struct bpf_sockopt, optval)),
468
469
470 BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_6, 0),
471
472
473 BPF_MOV64_IMM(BPF_REG_0, 1),
474 BPF_EXIT_INSN(),
475 },
476 .attach_type = BPF_CGROUP_SETSOCKOPT,
477 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
478 .error = DENY_LOAD,
479 },
480 {
481 .descr = "setsockopt: read ctx->level",
482 .insns = {
483
484 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
485 offsetof(struct bpf_sockopt, level)),
486
487
488 BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 123, 4),
489
490 BPF_MOV64_IMM(BPF_REG_0, -1),
491 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
492 offsetof(struct bpf_sockopt, optlen)),
493
494 BPF_MOV64_IMM(BPF_REG_0, 1),
495 BPF_JMP_A(1),
496
497
498 BPF_MOV64_IMM(BPF_REG_0, 0),
499
500 BPF_EXIT_INSN(),
501 },
502 .attach_type = BPF_CGROUP_SETSOCKOPT,
503 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
504
505 .set_level = 123,
506
507 .set_optlen = 1,
508 },
509 {
510 .descr = "setsockopt: allow changing ctx->level",
511 .insns = {
512
513 BPF_MOV64_IMM(BPF_REG_0, SOL_IP),
514 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
515 offsetof(struct bpf_sockopt, level)),
516
517 BPF_MOV64_IMM(BPF_REG_0, 1),
518 BPF_EXIT_INSN(),
519 },
520 .attach_type = BPF_CGROUP_SETSOCKOPT,
521 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
522
523 .get_level = SOL_IP,
524 .set_level = 234,
525
526 .get_optname = IP_TOS,
527 .set_optname = IP_TOS,
528
529 .set_optval = { 1 << 3 },
530 .set_optlen = 1,
531 .get_optval = { 1 << 3 },
532 .get_optlen = 1,
533 },
534 {
535 .descr = "setsockopt: read ctx->optname",
536 .insns = {
537
538 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
539 offsetof(struct bpf_sockopt, optname)),
540
541
542 BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 123, 4),
543
544 BPF_MOV64_IMM(BPF_REG_0, -1),
545 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
546 offsetof(struct bpf_sockopt, optlen)),
547
548 BPF_MOV64_IMM(BPF_REG_0, 1),
549 BPF_JMP_A(1),
550
551
552 BPF_MOV64_IMM(BPF_REG_0, 0),
553
554 BPF_EXIT_INSN(),
555 },
556 .attach_type = BPF_CGROUP_SETSOCKOPT,
557 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
558
559 .set_optname = 123,
560
561 .set_optlen = 1,
562 },
563 {
564 .descr = "setsockopt: allow changing ctx->optname",
565 .insns = {
566
567 BPF_MOV64_IMM(BPF_REG_0, IP_TOS),
568 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
569 offsetof(struct bpf_sockopt, optname)),
570
571 BPF_MOV64_IMM(BPF_REG_0, 1),
572 BPF_EXIT_INSN(),
573 },
574 .attach_type = BPF_CGROUP_SETSOCKOPT,
575 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
576
577 .get_level = SOL_IP,
578 .set_level = SOL_IP,
579
580 .get_optname = IP_TOS,
581 .set_optname = 456,
582
583 .set_optval = { 1 << 3 },
584 .set_optlen = 1,
585 .get_optval = { 1 << 3 },
586 .get_optlen = 1,
587 },
588 {
589 .descr = "setsockopt: read ctx->optlen",
590 .insns = {
591
592 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
593 offsetof(struct bpf_sockopt, optlen)),
594
595
596 BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 64, 4),
597
598 BPF_MOV64_IMM(BPF_REG_0, -1),
599 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
600 offsetof(struct bpf_sockopt, optlen)),
601
602 BPF_MOV64_IMM(BPF_REG_0, 1),
603 BPF_JMP_A(1),
604
605
606 BPF_MOV64_IMM(BPF_REG_0, 0),
607
608 BPF_EXIT_INSN(),
609 },
610 .attach_type = BPF_CGROUP_SETSOCKOPT,
611 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
612
613 .set_optlen = 64,
614 },
615 {
616 .descr = "setsockopt: ctx->optlen == -1 is ok",
617 .insns = {
618
619 BPF_MOV64_IMM(BPF_REG_0, -1),
620 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
621 offsetof(struct bpf_sockopt, optlen)),
622
623 BPF_MOV64_IMM(BPF_REG_0, 1),
624 BPF_EXIT_INSN(),
625 },
626 .attach_type = BPF_CGROUP_SETSOCKOPT,
627 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
628
629 .set_optlen = 64,
630 },
631 {
632 .descr = "setsockopt: deny ctx->optlen < 0 (except -1)",
633 .insns = {
634
635 BPF_MOV64_IMM(BPF_REG_0, -2),
636 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
637 offsetof(struct bpf_sockopt, optlen)),
638
639 BPF_MOV64_IMM(BPF_REG_0, 1),
640 BPF_EXIT_INSN(),
641 },
642 .attach_type = BPF_CGROUP_SETSOCKOPT,
643 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
644
645 .set_optlen = 4,
646
647 .error = EFAULT_SETSOCKOPT,
648 },
649 {
650 .descr = "setsockopt: deny ctx->optlen > input optlen",
651 .insns = {
652
653 BPF_MOV64_IMM(BPF_REG_0, 65),
654 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
655 offsetof(struct bpf_sockopt, optlen)),
656 BPF_MOV64_IMM(BPF_REG_0, 1),
657 BPF_EXIT_INSN(),
658 },
659 .attach_type = BPF_CGROUP_SETSOCKOPT,
660 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
661
662 .set_optlen = 64,
663
664 .error = EFAULT_SETSOCKOPT,
665 },
666 {
667 .descr = "setsockopt: allow changing ctx->optlen within bounds",
668 .insns = {
669
670 BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1,
671 offsetof(struct bpf_sockopt, optval)),
672
673 BPF_MOV64_REG(BPF_REG_2, BPF_REG_6),
674
675 BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 1),
676
677
678 BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_1,
679 offsetof(struct bpf_sockopt, optval_end)),
680
681
682 BPF_JMP_REG(BPF_JGT, BPF_REG_6, BPF_REG_7, 1),
683
684 BPF_ST_MEM(BPF_B, BPF_REG_2, 0, 1 << 3),
685
686
687
688 BPF_MOV64_IMM(BPF_REG_0, 1),
689 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
690 offsetof(struct bpf_sockopt, optlen)),
691
692
693 BPF_MOV64_IMM(BPF_REG_0, 1),
694 BPF_EXIT_INSN(),
695 },
696 .attach_type = BPF_CGROUP_SETSOCKOPT,
697 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
698
699 .get_level = SOL_IP,
700 .set_level = SOL_IP,
701
702 .get_optname = IP_TOS,
703 .set_optname = IP_TOS,
704
705 .set_optval = { 1, 1, 1, 1 },
706 .set_optlen = 4,
707 .get_optval = { 1 << 3 },
708 .get_optlen = 1,
709 },
710 {
711 .descr = "setsockopt: deny write ctx->retval",
712 .insns = {
713
714 BPF_MOV64_IMM(BPF_REG_0, 0),
715 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
716 offsetof(struct bpf_sockopt, retval)),
717
718
719 BPF_MOV64_IMM(BPF_REG_0, 1),
720 BPF_EXIT_INSN(),
721 },
722 .attach_type = BPF_CGROUP_SETSOCKOPT,
723 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
724
725 .error = DENY_LOAD,
726 },
727 {
728 .descr = "setsockopt: deny read ctx->retval",
729 .insns = {
730
731 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
732 offsetof(struct bpf_sockopt, retval)),
733
734
735 BPF_MOV64_IMM(BPF_REG_0, 1),
736 BPF_EXIT_INSN(),
737 },
738 .attach_type = BPF_CGROUP_SETSOCKOPT,
739 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
740
741 .error = DENY_LOAD,
742 },
743 {
744 .descr = "setsockopt: deny writing to ctx->optval",
745 .insns = {
746
747 BPF_MOV64_IMM(BPF_REG_0, 1),
748 BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0,
749 offsetof(struct bpf_sockopt, optval)),
750 BPF_EXIT_INSN(),
751 },
752 .attach_type = BPF_CGROUP_SETSOCKOPT,
753 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
754
755 .error = DENY_LOAD,
756 },
757 {
758 .descr = "setsockopt: deny writing to ctx->optval_end",
759 .insns = {
760
761 BPF_MOV64_IMM(BPF_REG_0, 1),
762 BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0,
763 offsetof(struct bpf_sockopt, optval_end)),
764 BPF_EXIT_INSN(),
765 },
766 .attach_type = BPF_CGROUP_SETSOCKOPT,
767 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
768
769 .error = DENY_LOAD,
770 },
771 {
772 .descr = "setsockopt: allow IP_TOS <= 128",
773 .insns = {
774
775 BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1,
776 offsetof(struct bpf_sockopt, optval)),
777
778 BPF_MOV64_REG(BPF_REG_7, BPF_REG_6),
779 BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, 1),
780
781
782 BPF_LDX_MEM(BPF_DW, BPF_REG_8, BPF_REG_1,
783 offsetof(struct bpf_sockopt, optval_end)),
784
785
786 BPF_JMP_REG(BPF_JGT, BPF_REG_7, BPF_REG_8, 4),
787
788
789 BPF_LDX_MEM(BPF_B, BPF_REG_9, BPF_REG_6, 0),
790
791
792 BPF_JMP_IMM(BPF_JGT, BPF_REG_9, 128, 2),
793 BPF_MOV64_IMM(BPF_REG_0, 1),
794 BPF_JMP_A(1),
795
796
797
798 BPF_MOV64_IMM(BPF_REG_0, 0),
799
800
801 BPF_EXIT_INSN(),
802 },
803 .attach_type = BPF_CGROUP_SETSOCKOPT,
804 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
805
806 .get_level = SOL_IP,
807 .set_level = SOL_IP,
808
809 .get_optname = IP_TOS,
810 .set_optname = IP_TOS,
811
812 .set_optval = { 0x80 },
813 .set_optlen = 1,
814 .get_optval = { 0x80 },
815 .get_optlen = 1,
816 },
817 {
818 .descr = "setsockopt: deny IP_TOS > 128",
819 .insns = {
820
821 BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1,
822 offsetof(struct bpf_sockopt, optval)),
823
824 BPF_MOV64_REG(BPF_REG_7, BPF_REG_6),
825 BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, 1),
826
827
828 BPF_LDX_MEM(BPF_DW, BPF_REG_8, BPF_REG_1,
829 offsetof(struct bpf_sockopt, optval_end)),
830
831
832 BPF_JMP_REG(BPF_JGT, BPF_REG_7, BPF_REG_8, 4),
833
834
835 BPF_LDX_MEM(BPF_B, BPF_REG_9, BPF_REG_6, 0),
836
837
838 BPF_JMP_IMM(BPF_JGT, BPF_REG_9, 128, 2),
839 BPF_MOV64_IMM(BPF_REG_0, 1),
840 BPF_JMP_A(1),
841
842
843
844 BPF_MOV64_IMM(BPF_REG_0, 0),
845
846
847 BPF_EXIT_INSN(),
848 },
849 .attach_type = BPF_CGROUP_SETSOCKOPT,
850 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
851
852 .get_level = SOL_IP,
853 .set_level = SOL_IP,
854
855 .get_optname = IP_TOS,
856 .set_optname = IP_TOS,
857
858 .set_optval = { 0x81 },
859 .set_optlen = 1,
860 .get_optval = { 0x00 },
861 .get_optlen = 1,
862
863 .error = EPERM_SETSOCKOPT,
864 },
865};
866
867static int load_prog(const struct bpf_insn *insns,
868 enum bpf_attach_type expected_attach_type)
869{
870 struct bpf_load_program_attr attr = {
871 .prog_type = BPF_PROG_TYPE_CGROUP_SOCKOPT,
872 .expected_attach_type = expected_attach_type,
873 .insns = insns,
874 .license = "GPL",
875 .log_level = 2,
876 };
877 int fd;
878
879 for (;
880 insns[attr.insns_cnt].code != (BPF_JMP | BPF_EXIT);
881 attr.insns_cnt++) {
882 }
883 attr.insns_cnt++;
884
885 fd = bpf_load_program_xattr(&attr, bpf_log_buf, sizeof(bpf_log_buf));
886 if (verbose && fd < 0)
887 fprintf(stderr, "%s\n", bpf_log_buf);
888
889 return fd;
890}
891
892static int run_test(int cgroup_fd, struct sockopt_test *test)
893{
894 int sock_fd, err, prog_fd;
895 void *optval = NULL;
896 int ret = 0;
897
898 prog_fd = load_prog(test->insns, test->expected_attach_type);
899 if (prog_fd < 0) {
900 if (test->error == DENY_LOAD)
901 return 0;
902
903 log_err("Failed to load BPF program");
904 return -1;
905 }
906
907 err = bpf_prog_attach(prog_fd, cgroup_fd, test->attach_type, 0);
908 if (err < 0) {
909 if (test->error == DENY_ATTACH)
910 goto close_prog_fd;
911
912 log_err("Failed to attach BPF program");
913 ret = -1;
914 goto close_prog_fd;
915 }
916
917 sock_fd = socket(AF_INET, SOCK_STREAM, 0);
918 if (sock_fd < 0) {
919 log_err("Failed to create AF_INET socket");
920 ret = -1;
921 goto detach_prog;
922 }
923
924 if (test->set_optlen) {
925 err = setsockopt(sock_fd, test->set_level, test->set_optname,
926 test->set_optval, test->set_optlen);
927 if (err) {
928 if (errno == EPERM && test->error == EPERM_SETSOCKOPT)
929 goto close_sock_fd;
930 if (errno == EFAULT && test->error == EFAULT_SETSOCKOPT)
931 goto free_optval;
932
933 log_err("Failed to call setsockopt");
934 ret = -1;
935 goto close_sock_fd;
936 }
937 }
938
939 if (test->get_optlen) {
940 optval = malloc(test->get_optlen);
941 socklen_t optlen = test->get_optlen;
942 socklen_t expected_get_optlen = test->get_optlen_ret ?:
943 test->get_optlen;
944
945 err = getsockopt(sock_fd, test->get_level, test->get_optname,
946 optval, &optlen);
947 if (err) {
948 if (errno == EPERM && test->error == EPERM_GETSOCKOPT)
949 goto free_optval;
950 if (errno == EFAULT && test->error == EFAULT_GETSOCKOPT)
951 goto free_optval;
952
953 log_err("Failed to call getsockopt");
954 ret = -1;
955 goto free_optval;
956 }
957
958 if (optlen != expected_get_optlen) {
959 errno = 0;
960 log_err("getsockopt returned unexpected optlen");
961 ret = -1;
962 goto free_optval;
963 }
964
965 if (memcmp(optval, test->get_optval, optlen) != 0) {
966 errno = 0;
967 log_err("getsockopt returned unexpected optval");
968 ret = -1;
969 goto free_optval;
970 }
971 }
972
973 ret = test->error != OK;
974
975free_optval:
976 free(optval);
977close_sock_fd:
978 close(sock_fd);
979detach_prog:
980 bpf_prog_detach2(prog_fd, cgroup_fd, test->attach_type);
981close_prog_fd:
982 close(prog_fd);
983 return ret;
984}
985
986int main(int args, char **argv)
987{
988 int err = EXIT_FAILURE, error_cnt = 0;
989 int cgroup_fd, i;
990
991 if (setup_cgroup_environment())
992 goto cleanup_obj;
993
994 cgroup_fd = create_and_get_cgroup(CG_PATH);
995 if (cgroup_fd < 0)
996 goto cleanup_cgroup_env;
997
998 if (join_cgroup(CG_PATH))
999 goto cleanup_cgroup;
1000
1001 for (i = 0; i < ARRAY_SIZE(tests); i++) {
1002 int err = run_test(cgroup_fd, &tests[i]);
1003
1004 if (err)
1005 error_cnt++;
1006
1007 printf("#%d %s: %s\n", i, err ? "FAIL" : "PASS",
1008 tests[i].descr);
1009 }
1010
1011 printf("Summary: %ld PASSED, %d FAILED\n",
1012 ARRAY_SIZE(tests) - error_cnt, error_cnt);
1013 err = error_cnt ? EXIT_FAILURE : EXIT_SUCCESS;
1014
1015cleanup_cgroup:
1016 close(cgroup_fd);
1017cleanup_cgroup_env:
1018 cleanup_cgroup_environment();
1019cleanup_obj:
1020 return err;
1021}
1022