1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // Copyright (c) 2018 Facebook |
3 | |
4 | #define _GNU_SOURCE |
5 | |
6 | #include <stdio.h> |
7 | #include <stdlib.h> |
8 | #include <unistd.h> |
9 | |
10 | #include <arpa/inet.h> |
11 | #include <netinet/in.h> |
12 | #include <sys/types.h> |
13 | #include <sys/select.h> |
14 | #include <sys/socket.h> |
15 | |
16 | #include <linux/filter.h> |
17 | |
18 | #include <bpf/bpf.h> |
19 | #include <bpf/libbpf.h> |
20 | |
21 | #include "cgroup_helpers.h" |
22 | #include "testing_helpers.h" |
23 | #include "bpf_util.h" |
24 | |
25 | #ifndef ENOTSUPP |
26 | # define ENOTSUPP 524 |
27 | #endif |
28 | |
29 | #define CG_PATH "/foo" |
30 | #define CONNECT4_PROG_PATH "./connect4_prog.bpf.o" |
31 | #define CONNECT6_PROG_PATH "./connect6_prog.bpf.o" |
32 | #define SENDMSG4_PROG_PATH "./sendmsg4_prog.bpf.o" |
33 | #define SENDMSG6_PROG_PATH "./sendmsg6_prog.bpf.o" |
34 | #define RECVMSG4_PROG_PATH "./recvmsg4_prog.bpf.o" |
35 | #define RECVMSG6_PROG_PATH "./recvmsg6_prog.bpf.o" |
36 | #define BIND4_PROG_PATH "./bind4_prog.bpf.o" |
37 | #define BIND6_PROG_PATH "./bind6_prog.bpf.o" |
38 | |
39 | #define SERV4_IP "192.168.1.254" |
40 | #define SERV4_REWRITE_IP "127.0.0.1" |
41 | #define SRC4_IP "172.16.0.1" |
42 | #define SRC4_REWRITE_IP "127.0.0.4" |
43 | #define SERV4_PORT 4040 |
44 | #define SERV4_REWRITE_PORT 4444 |
45 | |
46 | #define SERV6_IP "face:b00c:1234:5678::abcd" |
47 | #define SERV6_REWRITE_IP "::1" |
48 | #define SERV6_V4MAPPED_IP "::ffff:192.168.0.4" |
49 | #define SRC6_IP "::1" |
50 | #define SRC6_REWRITE_IP "::6" |
51 | #define WILDCARD6_IP "::" |
52 | #define SERV6_PORT 6060 |
53 | #define SERV6_REWRITE_PORT 6666 |
54 | |
55 | #define INET_NTOP_BUF 40 |
56 | |
57 | struct sock_addr_test; |
58 | |
59 | typedef int (*load_fn)(const struct sock_addr_test *test); |
60 | typedef int (*info_fn)(int, struct sockaddr *, socklen_t *); |
61 | |
62 | char bpf_log_buf[BPF_LOG_BUF_SIZE]; |
63 | |
64 | struct sock_addr_test { |
65 | const char *descr; |
66 | /* BPF prog properties */ |
67 | load_fn loadfn; |
68 | enum bpf_attach_type expected_attach_type; |
69 | enum bpf_attach_type attach_type; |
70 | /* Socket properties */ |
71 | int domain; |
72 | int type; |
73 | /* IP:port pairs for BPF prog to override */ |
74 | const char *requested_ip; |
75 | unsigned short requested_port; |
76 | const char *expected_ip; |
77 | unsigned short expected_port; |
78 | const char *expected_src_ip; |
79 | /* Expected test result */ |
80 | enum { |
81 | LOAD_REJECT, |
82 | ATTACH_REJECT, |
83 | ATTACH_OKAY, |
84 | SYSCALL_EPERM, |
85 | SYSCALL_ENOTSUPP, |
86 | SUCCESS, |
87 | } expected_result; |
88 | }; |
89 | |
90 | static int bind4_prog_load(const struct sock_addr_test *test); |
91 | static int bind6_prog_load(const struct sock_addr_test *test); |
92 | static int connect4_prog_load(const struct sock_addr_test *test); |
93 | static int connect6_prog_load(const struct sock_addr_test *test); |
94 | static int sendmsg_allow_prog_load(const struct sock_addr_test *test); |
95 | static int sendmsg_deny_prog_load(const struct sock_addr_test *test); |
96 | static int recvmsg_allow_prog_load(const struct sock_addr_test *test); |
97 | static int recvmsg_deny_prog_load(const struct sock_addr_test *test); |
98 | static int sendmsg4_rw_asm_prog_load(const struct sock_addr_test *test); |
99 | static int recvmsg4_rw_c_prog_load(const struct sock_addr_test *test); |
100 | static int sendmsg4_rw_c_prog_load(const struct sock_addr_test *test); |
101 | static int sendmsg6_rw_asm_prog_load(const struct sock_addr_test *test); |
102 | static int recvmsg6_rw_c_prog_load(const struct sock_addr_test *test); |
103 | static int sendmsg6_rw_c_prog_load(const struct sock_addr_test *test); |
104 | static int sendmsg6_rw_v4mapped_prog_load(const struct sock_addr_test *test); |
105 | static int sendmsg6_rw_wildcard_prog_load(const struct sock_addr_test *test); |
106 | |
107 | static struct sock_addr_test tests[] = { |
108 | /* bind */ |
109 | { |
110 | "bind4: load prog with wrong expected attach type" , |
111 | bind4_prog_load, |
112 | BPF_CGROUP_INET6_BIND, |
113 | BPF_CGROUP_INET4_BIND, |
114 | AF_INET, |
115 | SOCK_STREAM, |
116 | NULL, |
117 | 0, |
118 | NULL, |
119 | 0, |
120 | NULL, |
121 | LOAD_REJECT, |
122 | }, |
123 | { |
124 | "bind4: attach prog with wrong attach type" , |
125 | bind4_prog_load, |
126 | BPF_CGROUP_INET4_BIND, |
127 | BPF_CGROUP_INET6_BIND, |
128 | AF_INET, |
129 | SOCK_STREAM, |
130 | NULL, |
131 | 0, |
132 | NULL, |
133 | 0, |
134 | NULL, |
135 | ATTACH_REJECT, |
136 | }, |
137 | { |
138 | "bind4: rewrite IP & TCP port in" , |
139 | bind4_prog_load, |
140 | BPF_CGROUP_INET4_BIND, |
141 | BPF_CGROUP_INET4_BIND, |
142 | AF_INET, |
143 | SOCK_STREAM, |
144 | SERV4_IP, |
145 | SERV4_PORT, |
146 | SERV4_REWRITE_IP, |
147 | SERV4_REWRITE_PORT, |
148 | NULL, |
149 | SUCCESS, |
150 | }, |
151 | { |
152 | "bind4: rewrite IP & UDP port in" , |
153 | bind4_prog_load, |
154 | BPF_CGROUP_INET4_BIND, |
155 | BPF_CGROUP_INET4_BIND, |
156 | AF_INET, |
157 | SOCK_DGRAM, |
158 | SERV4_IP, |
159 | SERV4_PORT, |
160 | SERV4_REWRITE_IP, |
161 | SERV4_REWRITE_PORT, |
162 | NULL, |
163 | SUCCESS, |
164 | }, |
165 | { |
166 | "bind6: load prog with wrong expected attach type" , |
167 | bind6_prog_load, |
168 | BPF_CGROUP_INET4_BIND, |
169 | BPF_CGROUP_INET6_BIND, |
170 | AF_INET6, |
171 | SOCK_STREAM, |
172 | NULL, |
173 | 0, |
174 | NULL, |
175 | 0, |
176 | NULL, |
177 | LOAD_REJECT, |
178 | }, |
179 | { |
180 | "bind6: attach prog with wrong attach type" , |
181 | bind6_prog_load, |
182 | BPF_CGROUP_INET6_BIND, |
183 | BPF_CGROUP_INET4_BIND, |
184 | AF_INET, |
185 | SOCK_STREAM, |
186 | NULL, |
187 | 0, |
188 | NULL, |
189 | 0, |
190 | NULL, |
191 | ATTACH_REJECT, |
192 | }, |
193 | { |
194 | "bind6: rewrite IP & TCP port in" , |
195 | bind6_prog_load, |
196 | BPF_CGROUP_INET6_BIND, |
197 | BPF_CGROUP_INET6_BIND, |
198 | AF_INET6, |
199 | SOCK_STREAM, |
200 | SERV6_IP, |
201 | SERV6_PORT, |
202 | SERV6_REWRITE_IP, |
203 | SERV6_REWRITE_PORT, |
204 | NULL, |
205 | SUCCESS, |
206 | }, |
207 | { |
208 | "bind6: rewrite IP & UDP port in" , |
209 | bind6_prog_load, |
210 | BPF_CGROUP_INET6_BIND, |
211 | BPF_CGROUP_INET6_BIND, |
212 | AF_INET6, |
213 | SOCK_DGRAM, |
214 | SERV6_IP, |
215 | SERV6_PORT, |
216 | SERV6_REWRITE_IP, |
217 | SERV6_REWRITE_PORT, |
218 | NULL, |
219 | SUCCESS, |
220 | }, |
221 | |
222 | /* connect */ |
223 | { |
224 | "connect4: load prog with wrong expected attach type" , |
225 | connect4_prog_load, |
226 | BPF_CGROUP_INET6_CONNECT, |
227 | BPF_CGROUP_INET4_CONNECT, |
228 | AF_INET, |
229 | SOCK_STREAM, |
230 | NULL, |
231 | 0, |
232 | NULL, |
233 | 0, |
234 | NULL, |
235 | LOAD_REJECT, |
236 | }, |
237 | { |
238 | "connect4: attach prog with wrong attach type" , |
239 | connect4_prog_load, |
240 | BPF_CGROUP_INET4_CONNECT, |
241 | BPF_CGROUP_INET6_CONNECT, |
242 | AF_INET, |
243 | SOCK_STREAM, |
244 | NULL, |
245 | 0, |
246 | NULL, |
247 | 0, |
248 | NULL, |
249 | ATTACH_REJECT, |
250 | }, |
251 | { |
252 | "connect4: rewrite IP & TCP port" , |
253 | connect4_prog_load, |
254 | BPF_CGROUP_INET4_CONNECT, |
255 | BPF_CGROUP_INET4_CONNECT, |
256 | AF_INET, |
257 | SOCK_STREAM, |
258 | SERV4_IP, |
259 | SERV4_PORT, |
260 | SERV4_REWRITE_IP, |
261 | SERV4_REWRITE_PORT, |
262 | SRC4_REWRITE_IP, |
263 | SUCCESS, |
264 | }, |
265 | { |
266 | "connect4: rewrite IP & UDP port" , |
267 | connect4_prog_load, |
268 | BPF_CGROUP_INET4_CONNECT, |
269 | BPF_CGROUP_INET4_CONNECT, |
270 | AF_INET, |
271 | SOCK_DGRAM, |
272 | SERV4_IP, |
273 | SERV4_PORT, |
274 | SERV4_REWRITE_IP, |
275 | SERV4_REWRITE_PORT, |
276 | SRC4_REWRITE_IP, |
277 | SUCCESS, |
278 | }, |
279 | { |
280 | "connect6: load prog with wrong expected attach type" , |
281 | connect6_prog_load, |
282 | BPF_CGROUP_INET4_CONNECT, |
283 | BPF_CGROUP_INET6_CONNECT, |
284 | AF_INET6, |
285 | SOCK_STREAM, |
286 | NULL, |
287 | 0, |
288 | NULL, |
289 | 0, |
290 | NULL, |
291 | LOAD_REJECT, |
292 | }, |
293 | { |
294 | "connect6: attach prog with wrong attach type" , |
295 | connect6_prog_load, |
296 | BPF_CGROUP_INET6_CONNECT, |
297 | BPF_CGROUP_INET4_CONNECT, |
298 | AF_INET, |
299 | SOCK_STREAM, |
300 | NULL, |
301 | 0, |
302 | NULL, |
303 | 0, |
304 | NULL, |
305 | ATTACH_REJECT, |
306 | }, |
307 | { |
308 | "connect6: rewrite IP & TCP port" , |
309 | connect6_prog_load, |
310 | BPF_CGROUP_INET6_CONNECT, |
311 | BPF_CGROUP_INET6_CONNECT, |
312 | AF_INET6, |
313 | SOCK_STREAM, |
314 | SERV6_IP, |
315 | SERV6_PORT, |
316 | SERV6_REWRITE_IP, |
317 | SERV6_REWRITE_PORT, |
318 | SRC6_REWRITE_IP, |
319 | SUCCESS, |
320 | }, |
321 | { |
322 | "connect6: rewrite IP & UDP port" , |
323 | connect6_prog_load, |
324 | BPF_CGROUP_INET6_CONNECT, |
325 | BPF_CGROUP_INET6_CONNECT, |
326 | AF_INET6, |
327 | SOCK_DGRAM, |
328 | SERV6_IP, |
329 | SERV6_PORT, |
330 | SERV6_REWRITE_IP, |
331 | SERV6_REWRITE_PORT, |
332 | SRC6_REWRITE_IP, |
333 | SUCCESS, |
334 | }, |
335 | |
336 | /* sendmsg */ |
337 | { |
338 | "sendmsg4: load prog with wrong expected attach type" , |
339 | sendmsg4_rw_asm_prog_load, |
340 | BPF_CGROUP_UDP6_SENDMSG, |
341 | BPF_CGROUP_UDP4_SENDMSG, |
342 | AF_INET, |
343 | SOCK_DGRAM, |
344 | NULL, |
345 | 0, |
346 | NULL, |
347 | 0, |
348 | NULL, |
349 | LOAD_REJECT, |
350 | }, |
351 | { |
352 | "sendmsg4: attach prog with wrong attach type" , |
353 | sendmsg4_rw_asm_prog_load, |
354 | BPF_CGROUP_UDP4_SENDMSG, |
355 | BPF_CGROUP_UDP6_SENDMSG, |
356 | AF_INET, |
357 | SOCK_DGRAM, |
358 | NULL, |
359 | 0, |
360 | NULL, |
361 | 0, |
362 | NULL, |
363 | ATTACH_REJECT, |
364 | }, |
365 | { |
366 | "sendmsg4: rewrite IP & port (asm)" , |
367 | sendmsg4_rw_asm_prog_load, |
368 | BPF_CGROUP_UDP4_SENDMSG, |
369 | BPF_CGROUP_UDP4_SENDMSG, |
370 | AF_INET, |
371 | SOCK_DGRAM, |
372 | SERV4_IP, |
373 | SERV4_PORT, |
374 | SERV4_REWRITE_IP, |
375 | SERV4_REWRITE_PORT, |
376 | SRC4_REWRITE_IP, |
377 | SUCCESS, |
378 | }, |
379 | { |
380 | "sendmsg4: rewrite IP & port (C)" , |
381 | sendmsg4_rw_c_prog_load, |
382 | BPF_CGROUP_UDP4_SENDMSG, |
383 | BPF_CGROUP_UDP4_SENDMSG, |
384 | AF_INET, |
385 | SOCK_DGRAM, |
386 | SERV4_IP, |
387 | SERV4_PORT, |
388 | SERV4_REWRITE_IP, |
389 | SERV4_REWRITE_PORT, |
390 | SRC4_REWRITE_IP, |
391 | SUCCESS, |
392 | }, |
393 | { |
394 | "sendmsg4: deny call" , |
395 | sendmsg_deny_prog_load, |
396 | BPF_CGROUP_UDP4_SENDMSG, |
397 | BPF_CGROUP_UDP4_SENDMSG, |
398 | AF_INET, |
399 | SOCK_DGRAM, |
400 | SERV4_IP, |
401 | SERV4_PORT, |
402 | SERV4_REWRITE_IP, |
403 | SERV4_REWRITE_PORT, |
404 | SRC4_REWRITE_IP, |
405 | SYSCALL_EPERM, |
406 | }, |
407 | { |
408 | "sendmsg6: load prog with wrong expected attach type" , |
409 | sendmsg6_rw_asm_prog_load, |
410 | BPF_CGROUP_UDP4_SENDMSG, |
411 | BPF_CGROUP_UDP6_SENDMSG, |
412 | AF_INET6, |
413 | SOCK_DGRAM, |
414 | NULL, |
415 | 0, |
416 | NULL, |
417 | 0, |
418 | NULL, |
419 | LOAD_REJECT, |
420 | }, |
421 | { |
422 | "sendmsg6: attach prog with wrong attach type" , |
423 | sendmsg6_rw_asm_prog_load, |
424 | BPF_CGROUP_UDP6_SENDMSG, |
425 | BPF_CGROUP_UDP4_SENDMSG, |
426 | AF_INET6, |
427 | SOCK_DGRAM, |
428 | NULL, |
429 | 0, |
430 | NULL, |
431 | 0, |
432 | NULL, |
433 | ATTACH_REJECT, |
434 | }, |
435 | { |
436 | "sendmsg6: rewrite IP & port (asm)" , |
437 | sendmsg6_rw_asm_prog_load, |
438 | BPF_CGROUP_UDP6_SENDMSG, |
439 | BPF_CGROUP_UDP6_SENDMSG, |
440 | AF_INET6, |
441 | SOCK_DGRAM, |
442 | SERV6_IP, |
443 | SERV6_PORT, |
444 | SERV6_REWRITE_IP, |
445 | SERV6_REWRITE_PORT, |
446 | SRC6_REWRITE_IP, |
447 | SUCCESS, |
448 | }, |
449 | { |
450 | "sendmsg6: rewrite IP & port (C)" , |
451 | sendmsg6_rw_c_prog_load, |
452 | BPF_CGROUP_UDP6_SENDMSG, |
453 | BPF_CGROUP_UDP6_SENDMSG, |
454 | AF_INET6, |
455 | SOCK_DGRAM, |
456 | SERV6_IP, |
457 | SERV6_PORT, |
458 | SERV6_REWRITE_IP, |
459 | SERV6_REWRITE_PORT, |
460 | SRC6_REWRITE_IP, |
461 | SUCCESS, |
462 | }, |
463 | { |
464 | "sendmsg6: IPv4-mapped IPv6" , |
465 | sendmsg6_rw_v4mapped_prog_load, |
466 | BPF_CGROUP_UDP6_SENDMSG, |
467 | BPF_CGROUP_UDP6_SENDMSG, |
468 | AF_INET6, |
469 | SOCK_DGRAM, |
470 | SERV6_IP, |
471 | SERV6_PORT, |
472 | SERV6_REWRITE_IP, |
473 | SERV6_REWRITE_PORT, |
474 | SRC6_REWRITE_IP, |
475 | SYSCALL_ENOTSUPP, |
476 | }, |
477 | { |
478 | "sendmsg6: set dst IP = [::] (BSD'ism)" , |
479 | sendmsg6_rw_wildcard_prog_load, |
480 | BPF_CGROUP_UDP6_SENDMSG, |
481 | BPF_CGROUP_UDP6_SENDMSG, |
482 | AF_INET6, |
483 | SOCK_DGRAM, |
484 | SERV6_IP, |
485 | SERV6_PORT, |
486 | SERV6_REWRITE_IP, |
487 | SERV6_REWRITE_PORT, |
488 | SRC6_REWRITE_IP, |
489 | SUCCESS, |
490 | }, |
491 | { |
492 | "sendmsg6: preserve dst IP = [::] (BSD'ism)" , |
493 | sendmsg_allow_prog_load, |
494 | BPF_CGROUP_UDP6_SENDMSG, |
495 | BPF_CGROUP_UDP6_SENDMSG, |
496 | AF_INET6, |
497 | SOCK_DGRAM, |
498 | WILDCARD6_IP, |
499 | SERV6_PORT, |
500 | SERV6_REWRITE_IP, |
501 | SERV6_PORT, |
502 | SRC6_IP, |
503 | SUCCESS, |
504 | }, |
505 | { |
506 | "sendmsg6: deny call" , |
507 | sendmsg_deny_prog_load, |
508 | BPF_CGROUP_UDP6_SENDMSG, |
509 | BPF_CGROUP_UDP6_SENDMSG, |
510 | AF_INET6, |
511 | SOCK_DGRAM, |
512 | SERV6_IP, |
513 | SERV6_PORT, |
514 | SERV6_REWRITE_IP, |
515 | SERV6_REWRITE_PORT, |
516 | SRC6_REWRITE_IP, |
517 | SYSCALL_EPERM, |
518 | }, |
519 | |
520 | /* recvmsg */ |
521 | { |
522 | "recvmsg4: return code ok" , |
523 | recvmsg_allow_prog_load, |
524 | BPF_CGROUP_UDP4_RECVMSG, |
525 | BPF_CGROUP_UDP4_RECVMSG, |
526 | AF_INET, |
527 | SOCK_DGRAM, |
528 | NULL, |
529 | 0, |
530 | NULL, |
531 | 0, |
532 | NULL, |
533 | ATTACH_OKAY, |
534 | }, |
535 | { |
536 | "recvmsg4: return code !ok" , |
537 | recvmsg_deny_prog_load, |
538 | BPF_CGROUP_UDP4_RECVMSG, |
539 | BPF_CGROUP_UDP4_RECVMSG, |
540 | AF_INET, |
541 | SOCK_DGRAM, |
542 | NULL, |
543 | 0, |
544 | NULL, |
545 | 0, |
546 | NULL, |
547 | LOAD_REJECT, |
548 | }, |
549 | { |
550 | "recvmsg6: return code ok" , |
551 | recvmsg_allow_prog_load, |
552 | BPF_CGROUP_UDP6_RECVMSG, |
553 | BPF_CGROUP_UDP6_RECVMSG, |
554 | AF_INET6, |
555 | SOCK_DGRAM, |
556 | NULL, |
557 | 0, |
558 | NULL, |
559 | 0, |
560 | NULL, |
561 | ATTACH_OKAY, |
562 | }, |
563 | { |
564 | "recvmsg6: return code !ok" , |
565 | recvmsg_deny_prog_load, |
566 | BPF_CGROUP_UDP6_RECVMSG, |
567 | BPF_CGROUP_UDP6_RECVMSG, |
568 | AF_INET6, |
569 | SOCK_DGRAM, |
570 | NULL, |
571 | 0, |
572 | NULL, |
573 | 0, |
574 | NULL, |
575 | LOAD_REJECT, |
576 | }, |
577 | { |
578 | "recvmsg4: rewrite IP & port (C)" , |
579 | recvmsg4_rw_c_prog_load, |
580 | BPF_CGROUP_UDP4_RECVMSG, |
581 | BPF_CGROUP_UDP4_RECVMSG, |
582 | AF_INET, |
583 | SOCK_DGRAM, |
584 | SERV4_REWRITE_IP, |
585 | SERV4_REWRITE_PORT, |
586 | SERV4_REWRITE_IP, |
587 | SERV4_REWRITE_PORT, |
588 | SERV4_IP, |
589 | SUCCESS, |
590 | }, |
591 | { |
592 | "recvmsg6: rewrite IP & port (C)" , |
593 | recvmsg6_rw_c_prog_load, |
594 | BPF_CGROUP_UDP6_RECVMSG, |
595 | BPF_CGROUP_UDP6_RECVMSG, |
596 | AF_INET6, |
597 | SOCK_DGRAM, |
598 | SERV6_REWRITE_IP, |
599 | SERV6_REWRITE_PORT, |
600 | SERV6_REWRITE_IP, |
601 | SERV6_REWRITE_PORT, |
602 | SERV6_IP, |
603 | SUCCESS, |
604 | }, |
605 | }; |
606 | |
607 | static int mk_sockaddr(int domain, const char *ip, unsigned short port, |
608 | struct sockaddr *addr, socklen_t addr_len) |
609 | { |
610 | struct sockaddr_in6 *addr6; |
611 | struct sockaddr_in *addr4; |
612 | |
613 | if (domain != AF_INET && domain != AF_INET6) { |
614 | log_err("Unsupported address family" ); |
615 | return -1; |
616 | } |
617 | |
618 | memset(addr, 0, addr_len); |
619 | |
620 | if (domain == AF_INET) { |
621 | if (addr_len < sizeof(struct sockaddr_in)) |
622 | return -1; |
623 | addr4 = (struct sockaddr_in *)addr; |
624 | addr4->sin_family = domain; |
625 | addr4->sin_port = htons(port); |
626 | if (inet_pton(domain, ip, (void *)&addr4->sin_addr) != 1) { |
627 | log_err("Invalid IPv4: %s" , ip); |
628 | return -1; |
629 | } |
630 | } else if (domain == AF_INET6) { |
631 | if (addr_len < sizeof(struct sockaddr_in6)) |
632 | return -1; |
633 | addr6 = (struct sockaddr_in6 *)addr; |
634 | addr6->sin6_family = domain; |
635 | addr6->sin6_port = htons(port); |
636 | if (inet_pton(domain, ip, (void *)&addr6->sin6_addr) != 1) { |
637 | log_err("Invalid IPv6: %s" , ip); |
638 | return -1; |
639 | } |
640 | } |
641 | |
642 | return 0; |
643 | } |
644 | |
645 | static int load_insns(const struct sock_addr_test *test, |
646 | const struct bpf_insn *insns, size_t insns_cnt) |
647 | { |
648 | LIBBPF_OPTS(bpf_prog_load_opts, opts); |
649 | int ret; |
650 | |
651 | opts.expected_attach_type = test->expected_attach_type; |
652 | opts.log_buf = bpf_log_buf; |
653 | opts.log_size = BPF_LOG_BUF_SIZE; |
654 | |
655 | ret = bpf_prog_load(BPF_PROG_TYPE_CGROUP_SOCK_ADDR, NULL, "GPL" , insns, insns_cnt, &opts); |
656 | if (ret < 0 && test->expected_result != LOAD_REJECT) { |
657 | log_err(">>> Loading program error.\n" |
658 | ">>> Verifier output:\n%s\n-------\n" , bpf_log_buf); |
659 | } |
660 | |
661 | return ret; |
662 | } |
663 | |
664 | static int load_path(const struct sock_addr_test *test, const char *path) |
665 | { |
666 | struct bpf_object *obj; |
667 | struct bpf_program *prog; |
668 | int err; |
669 | |
670 | obj = bpf_object__open_file(path, NULL); |
671 | err = libbpf_get_error(obj); |
672 | if (err) { |
673 | log_err(">>> Opening BPF object (%s) error.\n" , path); |
674 | return -1; |
675 | } |
676 | |
677 | prog = bpf_object__next_program(obj, NULL); |
678 | if (!prog) |
679 | goto err_out; |
680 | |
681 | bpf_program__set_type(prog, BPF_PROG_TYPE_CGROUP_SOCK_ADDR); |
682 | bpf_program__set_expected_attach_type(prog, test->expected_attach_type); |
683 | bpf_program__set_flags(prog, testing_prog_flags()); |
684 | |
685 | err = bpf_object__load(obj); |
686 | if (err) { |
687 | if (test->expected_result != LOAD_REJECT) |
688 | log_err(">>> Loading program (%s) error.\n" , path); |
689 | goto err_out; |
690 | } |
691 | |
692 | return bpf_program__fd(prog); |
693 | err_out: |
694 | bpf_object__close(obj); |
695 | return -1; |
696 | } |
697 | |
698 | static int bind4_prog_load(const struct sock_addr_test *test) |
699 | { |
700 | return load_path(test, BIND4_PROG_PATH); |
701 | } |
702 | |
703 | static int bind6_prog_load(const struct sock_addr_test *test) |
704 | { |
705 | return load_path(test, BIND6_PROG_PATH); |
706 | } |
707 | |
708 | static int connect4_prog_load(const struct sock_addr_test *test) |
709 | { |
710 | return load_path(test, CONNECT4_PROG_PATH); |
711 | } |
712 | |
713 | static int connect6_prog_load(const struct sock_addr_test *test) |
714 | { |
715 | return load_path(test, CONNECT6_PROG_PATH); |
716 | } |
717 | |
718 | static int xmsg_ret_only_prog_load(const struct sock_addr_test *test, |
719 | int32_t rc) |
720 | { |
721 | struct bpf_insn insns[] = { |
722 | /* return rc */ |
723 | BPF_MOV64_IMM(BPF_REG_0, rc), |
724 | BPF_EXIT_INSN(), |
725 | }; |
726 | return load_insns(test, insns, ARRAY_SIZE(insns)); |
727 | } |
728 | |
729 | static int sendmsg_allow_prog_load(const struct sock_addr_test *test) |
730 | { |
731 | return xmsg_ret_only_prog_load(test, /*rc*/ 1); |
732 | } |
733 | |
734 | static int sendmsg_deny_prog_load(const struct sock_addr_test *test) |
735 | { |
736 | return xmsg_ret_only_prog_load(test, /*rc*/ 0); |
737 | } |
738 | |
739 | static int recvmsg_allow_prog_load(const struct sock_addr_test *test) |
740 | { |
741 | return xmsg_ret_only_prog_load(test, /*rc*/ 1); |
742 | } |
743 | |
744 | static int recvmsg_deny_prog_load(const struct sock_addr_test *test) |
745 | { |
746 | return xmsg_ret_only_prog_load(test, /*rc*/ 0); |
747 | } |
748 | |
749 | static int sendmsg4_rw_asm_prog_load(const struct sock_addr_test *test) |
750 | { |
751 | struct sockaddr_in dst4_rw_addr; |
752 | struct in_addr src4_rw_ip; |
753 | |
754 | if (inet_pton(AF_INET, SRC4_REWRITE_IP, (void *)&src4_rw_ip) != 1) { |
755 | log_err("Invalid IPv4: %s" , SRC4_REWRITE_IP); |
756 | return -1; |
757 | } |
758 | |
759 | if (mk_sockaddr(AF_INET, SERV4_REWRITE_IP, SERV4_REWRITE_PORT, |
760 | (struct sockaddr *)&dst4_rw_addr, |
761 | sizeof(dst4_rw_addr)) == -1) |
762 | return -1; |
763 | |
764 | struct bpf_insn insns[] = { |
765 | BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), |
766 | |
767 | /* if (sk.family == AF_INET && */ |
768 | BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6, |
769 | offsetof(struct bpf_sock_addr, family)), |
770 | BPF_JMP_IMM(BPF_JNE, BPF_REG_7, AF_INET, 8), |
771 | |
772 | /* sk.type == SOCK_DGRAM) { */ |
773 | BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6, |
774 | offsetof(struct bpf_sock_addr, type)), |
775 | BPF_JMP_IMM(BPF_JNE, BPF_REG_7, SOCK_DGRAM, 6), |
776 | |
777 | /* msg_src_ip4 = src4_rw_ip */ |
778 | BPF_MOV32_IMM(BPF_REG_7, src4_rw_ip.s_addr), |
779 | BPF_STX_MEM(BPF_W, BPF_REG_6, BPF_REG_7, |
780 | offsetof(struct bpf_sock_addr, msg_src_ip4)), |
781 | |
782 | /* user_ip4 = dst4_rw_addr.sin_addr */ |
783 | BPF_MOV32_IMM(BPF_REG_7, dst4_rw_addr.sin_addr.s_addr), |
784 | BPF_STX_MEM(BPF_W, BPF_REG_6, BPF_REG_7, |
785 | offsetof(struct bpf_sock_addr, user_ip4)), |
786 | |
787 | /* user_port = dst4_rw_addr.sin_port */ |
788 | BPF_MOV32_IMM(BPF_REG_7, dst4_rw_addr.sin_port), |
789 | BPF_STX_MEM(BPF_W, BPF_REG_6, BPF_REG_7, |
790 | offsetof(struct bpf_sock_addr, user_port)), |
791 | /* } */ |
792 | |
793 | /* return 1 */ |
794 | BPF_MOV64_IMM(BPF_REG_0, 1), |
795 | BPF_EXIT_INSN(), |
796 | }; |
797 | |
798 | return load_insns(test, insns, ARRAY_SIZE(insns)); |
799 | } |
800 | |
801 | static int recvmsg4_rw_c_prog_load(const struct sock_addr_test *test) |
802 | { |
803 | return load_path(test, RECVMSG4_PROG_PATH); |
804 | } |
805 | |
806 | static int sendmsg4_rw_c_prog_load(const struct sock_addr_test *test) |
807 | { |
808 | return load_path(test, SENDMSG4_PROG_PATH); |
809 | } |
810 | |
811 | static int sendmsg6_rw_dst_asm_prog_load(const struct sock_addr_test *test, |
812 | const char *rw_dst_ip) |
813 | { |
814 | struct sockaddr_in6 dst6_rw_addr; |
815 | struct in6_addr src6_rw_ip; |
816 | |
817 | if (inet_pton(AF_INET6, SRC6_REWRITE_IP, (void *)&src6_rw_ip) != 1) { |
818 | log_err("Invalid IPv6: %s" , SRC6_REWRITE_IP); |
819 | return -1; |
820 | } |
821 | |
822 | if (mk_sockaddr(AF_INET6, rw_dst_ip, SERV6_REWRITE_PORT, |
823 | (struct sockaddr *)&dst6_rw_addr, |
824 | sizeof(dst6_rw_addr)) == -1) |
825 | return -1; |
826 | |
827 | struct bpf_insn insns[] = { |
828 | BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), |
829 | |
830 | /* if (sk.family == AF_INET6) { */ |
831 | BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6, |
832 | offsetof(struct bpf_sock_addr, family)), |
833 | BPF_JMP_IMM(BPF_JNE, BPF_REG_7, AF_INET6, 18), |
834 | |
835 | #define STORE_IPV6_WORD_N(DST, SRC, N) \ |
836 | BPF_MOV32_IMM(BPF_REG_7, SRC[N]), \ |
837 | BPF_STX_MEM(BPF_W, BPF_REG_6, BPF_REG_7, \ |
838 | offsetof(struct bpf_sock_addr, DST[N])) |
839 | |
840 | #define STORE_IPV6(DST, SRC) \ |
841 | STORE_IPV6_WORD_N(DST, SRC, 0), \ |
842 | STORE_IPV6_WORD_N(DST, SRC, 1), \ |
843 | STORE_IPV6_WORD_N(DST, SRC, 2), \ |
844 | STORE_IPV6_WORD_N(DST, SRC, 3) |
845 | |
846 | STORE_IPV6(msg_src_ip6, src6_rw_ip.s6_addr32), |
847 | STORE_IPV6(user_ip6, dst6_rw_addr.sin6_addr.s6_addr32), |
848 | |
849 | /* user_port = dst6_rw_addr.sin6_port */ |
850 | BPF_MOV32_IMM(BPF_REG_7, dst6_rw_addr.sin6_port), |
851 | BPF_STX_MEM(BPF_W, BPF_REG_6, BPF_REG_7, |
852 | offsetof(struct bpf_sock_addr, user_port)), |
853 | |
854 | /* } */ |
855 | |
856 | /* return 1 */ |
857 | BPF_MOV64_IMM(BPF_REG_0, 1), |
858 | BPF_EXIT_INSN(), |
859 | }; |
860 | |
861 | return load_insns(test, insns, ARRAY_SIZE(insns)); |
862 | } |
863 | |
864 | static int sendmsg6_rw_asm_prog_load(const struct sock_addr_test *test) |
865 | { |
866 | return sendmsg6_rw_dst_asm_prog_load(test, SERV6_REWRITE_IP); |
867 | } |
868 | |
869 | static int recvmsg6_rw_c_prog_load(const struct sock_addr_test *test) |
870 | { |
871 | return load_path(test, RECVMSG6_PROG_PATH); |
872 | } |
873 | |
874 | static int sendmsg6_rw_v4mapped_prog_load(const struct sock_addr_test *test) |
875 | { |
876 | return sendmsg6_rw_dst_asm_prog_load(test, SERV6_V4MAPPED_IP); |
877 | } |
878 | |
879 | static int sendmsg6_rw_wildcard_prog_load(const struct sock_addr_test *test) |
880 | { |
881 | return sendmsg6_rw_dst_asm_prog_load(test, WILDCARD6_IP); |
882 | } |
883 | |
884 | static int sendmsg6_rw_c_prog_load(const struct sock_addr_test *test) |
885 | { |
886 | return load_path(test, SENDMSG6_PROG_PATH); |
887 | } |
888 | |
889 | static int cmp_addr(const struct sockaddr_storage *addr1, |
890 | const struct sockaddr_storage *addr2, int cmp_port) |
891 | { |
892 | const struct sockaddr_in *four1, *four2; |
893 | const struct sockaddr_in6 *six1, *six2; |
894 | |
895 | if (addr1->ss_family != addr2->ss_family) |
896 | return -1; |
897 | |
898 | if (addr1->ss_family == AF_INET) { |
899 | four1 = (const struct sockaddr_in *)addr1; |
900 | four2 = (const struct sockaddr_in *)addr2; |
901 | return !((four1->sin_port == four2->sin_port || !cmp_port) && |
902 | four1->sin_addr.s_addr == four2->sin_addr.s_addr); |
903 | } else if (addr1->ss_family == AF_INET6) { |
904 | six1 = (const struct sockaddr_in6 *)addr1; |
905 | six2 = (const struct sockaddr_in6 *)addr2; |
906 | return !((six1->sin6_port == six2->sin6_port || !cmp_port) && |
907 | !memcmp(p: &six1->sin6_addr, q: &six2->sin6_addr, |
908 | size: sizeof(struct in6_addr))); |
909 | } |
910 | |
911 | return -1; |
912 | } |
913 | |
914 | static int cmp_sock_addr(info_fn fn, int sock1, |
915 | const struct sockaddr_storage *addr2, int cmp_port) |
916 | { |
917 | struct sockaddr_storage addr1; |
918 | socklen_t len1 = sizeof(addr1); |
919 | |
920 | memset(&addr1, 0, len1); |
921 | if (fn(sock1, (struct sockaddr *)&addr1, (socklen_t *)&len1) != 0) |
922 | return -1; |
923 | |
924 | return cmp_addr(addr1: &addr1, addr2, cmp_port); |
925 | } |
926 | |
927 | static int cmp_local_ip(int sock1, const struct sockaddr_storage *addr2) |
928 | { |
929 | return cmp_sock_addr(getsockname, sock1, addr2, /*cmp_port*/ 0); |
930 | } |
931 | |
932 | static int cmp_local_addr(int sock1, const struct sockaddr_storage *addr2) |
933 | { |
934 | return cmp_sock_addr(getsockname, sock1, addr2, /*cmp_port*/ 1); |
935 | } |
936 | |
937 | static int cmp_peer_addr(int sock1, const struct sockaddr_storage *addr2) |
938 | { |
939 | return cmp_sock_addr(getpeername, sock1, addr2, /*cmp_port*/ 1); |
940 | } |
941 | |
942 | static int start_server(int type, const struct sockaddr_storage *addr, |
943 | socklen_t addr_len) |
944 | { |
945 | int fd; |
946 | |
947 | fd = socket(addr->ss_family, type, 0); |
948 | if (fd == -1) { |
949 | log_err("Failed to create server socket" ); |
950 | goto out; |
951 | } |
952 | |
953 | if (bind(fd, (const struct sockaddr *)addr, addr_len) == -1) { |
954 | log_err("Failed to bind server socket" ); |
955 | goto close_out; |
956 | } |
957 | |
958 | if (type == SOCK_STREAM) { |
959 | if (listen(fd, 128) == -1) { |
960 | log_err("Failed to listen on server socket" ); |
961 | goto close_out; |
962 | } |
963 | } |
964 | |
965 | goto out; |
966 | close_out: |
967 | close(fd); |
968 | fd = -1; |
969 | out: |
970 | return fd; |
971 | } |
972 | |
973 | static int connect_to_server(int type, const struct sockaddr_storage *addr, |
974 | socklen_t addr_len) |
975 | { |
976 | int domain; |
977 | int fd = -1; |
978 | |
979 | domain = addr->ss_family; |
980 | |
981 | if (domain != AF_INET && domain != AF_INET6) { |
982 | log_err("Unsupported address family" ); |
983 | goto err; |
984 | } |
985 | |
986 | fd = socket(domain, type, 0); |
987 | if (fd == -1) { |
988 | log_err("Failed to create client socket" ); |
989 | goto err; |
990 | } |
991 | |
992 | if (connect(fd, (const struct sockaddr *)addr, addr_len) == -1) { |
993 | log_err("Fail to connect to server" ); |
994 | goto err; |
995 | } |
996 | |
997 | goto out; |
998 | err: |
999 | close(fd); |
1000 | fd = -1; |
1001 | out: |
1002 | return fd; |
1003 | } |
1004 | |
1005 | int init_pktinfo(int domain, struct cmsghdr *cmsg) |
1006 | { |
1007 | struct in6_pktinfo *pktinfo6; |
1008 | struct in_pktinfo *pktinfo4; |
1009 | |
1010 | if (domain == AF_INET) { |
1011 | cmsg->cmsg_level = SOL_IP; |
1012 | cmsg->cmsg_type = IP_PKTINFO; |
1013 | cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); |
1014 | pktinfo4 = (struct in_pktinfo *)CMSG_DATA(cmsg); |
1015 | memset(pktinfo4, 0, sizeof(struct in_pktinfo)); |
1016 | if (inet_pton(domain, SRC4_IP, |
1017 | (void *)&pktinfo4->ipi_spec_dst) != 1) |
1018 | return -1; |
1019 | } else if (domain == AF_INET6) { |
1020 | cmsg->cmsg_level = SOL_IPV6; |
1021 | cmsg->cmsg_type = IPV6_PKTINFO; |
1022 | cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); |
1023 | pktinfo6 = (struct in6_pktinfo *)CMSG_DATA(cmsg); |
1024 | memset(pktinfo6, 0, sizeof(struct in6_pktinfo)); |
1025 | if (inet_pton(domain, SRC6_IP, |
1026 | (void *)&pktinfo6->ipi6_addr) != 1) |
1027 | return -1; |
1028 | } else { |
1029 | return -1; |
1030 | } |
1031 | |
1032 | return 0; |
1033 | } |
1034 | |
1035 | static int sendmsg_to_server(int type, const struct sockaddr_storage *addr, |
1036 | socklen_t addr_len, int set_cmsg, int flags, |
1037 | int *syscall_err) |
1038 | { |
1039 | union { |
1040 | char buf[CMSG_SPACE(sizeof(struct in6_pktinfo))]; |
1041 | struct cmsghdr align; |
1042 | } control6; |
1043 | union { |
1044 | char buf[CMSG_SPACE(sizeof(struct in_pktinfo))]; |
1045 | struct cmsghdr align; |
1046 | } control4; |
1047 | struct msghdr hdr; |
1048 | struct iovec iov; |
1049 | char data = 'a'; |
1050 | int domain; |
1051 | int fd = -1; |
1052 | |
1053 | domain = addr->ss_family; |
1054 | |
1055 | if (domain != AF_INET && domain != AF_INET6) { |
1056 | log_err("Unsupported address family" ); |
1057 | goto err; |
1058 | } |
1059 | |
1060 | fd = socket(domain, type, 0); |
1061 | if (fd == -1) { |
1062 | log_err("Failed to create client socket" ); |
1063 | goto err; |
1064 | } |
1065 | |
1066 | memset(&iov, 0, sizeof(iov)); |
1067 | iov.iov_base = &data; |
1068 | iov.iov_len = sizeof(data); |
1069 | |
1070 | memset(&hdr, 0, sizeof(hdr)); |
1071 | hdr.msg_name = (void *)addr; |
1072 | hdr.msg_namelen = addr_len; |
1073 | hdr.msg_iov = &iov; |
1074 | hdr.msg_iovlen = 1; |
1075 | |
1076 | if (set_cmsg) { |
1077 | if (domain == AF_INET) { |
1078 | hdr.msg_control = &control4; |
1079 | hdr.msg_controllen = sizeof(control4.buf); |
1080 | } else if (domain == AF_INET6) { |
1081 | hdr.msg_control = &control6; |
1082 | hdr.msg_controllen = sizeof(control6.buf); |
1083 | } |
1084 | if (init_pktinfo(domain, CMSG_FIRSTHDR(&hdr))) { |
1085 | log_err("Fail to init pktinfo" ); |
1086 | goto err; |
1087 | } |
1088 | } |
1089 | |
1090 | if (sendmsg(fd, &hdr, flags) != sizeof(data)) { |
1091 | log_err("Fail to send message to server" ); |
1092 | *syscall_err = errno; |
1093 | goto err; |
1094 | } |
1095 | |
1096 | goto out; |
1097 | err: |
1098 | close(fd); |
1099 | fd = -1; |
1100 | out: |
1101 | return fd; |
1102 | } |
1103 | |
1104 | static int fastconnect_to_server(const struct sockaddr_storage *addr, |
1105 | socklen_t addr_len) |
1106 | { |
1107 | int sendmsg_err; |
1108 | |
1109 | return sendmsg_to_server(SOCK_STREAM, addr, addr_len, /*set_cmsg*/0, |
1110 | MSG_FASTOPEN, &sendmsg_err); |
1111 | } |
1112 | |
1113 | static int recvmsg_from_client(int sockfd, struct sockaddr_storage *src_addr) |
1114 | { |
1115 | struct timeval tv; |
1116 | struct msghdr hdr; |
1117 | struct iovec iov; |
1118 | char data[64]; |
1119 | fd_set rfds; |
1120 | |
1121 | FD_ZERO(&rfds); |
1122 | FD_SET(sockfd, &rfds); |
1123 | |
1124 | tv.tv_sec = 2; |
1125 | tv.tv_usec = 0; |
1126 | |
1127 | if (select(sockfd + 1, &rfds, NULL, NULL, &tv) <= 0 || |
1128 | !FD_ISSET(sockfd, &rfds)) |
1129 | return -1; |
1130 | |
1131 | memset(&iov, 0, sizeof(iov)); |
1132 | iov.iov_base = data; |
1133 | iov.iov_len = sizeof(data); |
1134 | |
1135 | memset(&hdr, 0, sizeof(hdr)); |
1136 | hdr.msg_name = src_addr; |
1137 | hdr.msg_namelen = sizeof(struct sockaddr_storage); |
1138 | hdr.msg_iov = &iov; |
1139 | hdr.msg_iovlen = 1; |
1140 | |
1141 | return recvmsg(sockfd, &hdr, 0); |
1142 | } |
1143 | |
1144 | static int init_addrs(const struct sock_addr_test *test, |
1145 | struct sockaddr_storage *requested_addr, |
1146 | struct sockaddr_storage *expected_addr, |
1147 | struct sockaddr_storage *expected_src_addr) |
1148 | { |
1149 | socklen_t addr_len = sizeof(struct sockaddr_storage); |
1150 | |
1151 | if (mk_sockaddr(test->domain, test->expected_ip, test->expected_port, |
1152 | (struct sockaddr *)expected_addr, addr_len) == -1) |
1153 | goto err; |
1154 | |
1155 | if (mk_sockaddr(test->domain, test->requested_ip, test->requested_port, |
1156 | (struct sockaddr *)requested_addr, addr_len) == -1) |
1157 | goto err; |
1158 | |
1159 | if (test->expected_src_ip && |
1160 | mk_sockaddr(test->domain, test->expected_src_ip, 0, |
1161 | (struct sockaddr *)expected_src_addr, addr_len) == -1) |
1162 | goto err; |
1163 | |
1164 | return 0; |
1165 | err: |
1166 | return -1; |
1167 | } |
1168 | |
1169 | static int run_bind_test_case(const struct sock_addr_test *test) |
1170 | { |
1171 | socklen_t addr_len = sizeof(struct sockaddr_storage); |
1172 | struct sockaddr_storage requested_addr; |
1173 | struct sockaddr_storage expected_addr; |
1174 | int clientfd = -1; |
1175 | int servfd = -1; |
1176 | int err = 0; |
1177 | |
1178 | if (init_addrs(test, requested_addr: &requested_addr, expected_addr: &expected_addr, NULL)) |
1179 | goto err; |
1180 | |
1181 | servfd = start_server(test->type, &requested_addr, addr_len); |
1182 | if (servfd == -1) |
1183 | goto err; |
1184 | |
1185 | if (cmp_local_addr(sock1: servfd, addr2: &expected_addr)) |
1186 | goto err; |
1187 | |
1188 | /* Try to connect to server just in case */ |
1189 | clientfd = connect_to_server(test->type, &expected_addr, addr_len); |
1190 | if (clientfd == -1) |
1191 | goto err; |
1192 | |
1193 | goto out; |
1194 | err: |
1195 | err = -1; |
1196 | out: |
1197 | close(clientfd); |
1198 | close(servfd); |
1199 | return err; |
1200 | } |
1201 | |
1202 | static int run_connect_test_case(const struct sock_addr_test *test) |
1203 | { |
1204 | socklen_t addr_len = sizeof(struct sockaddr_storage); |
1205 | struct sockaddr_storage expected_src_addr; |
1206 | struct sockaddr_storage requested_addr; |
1207 | struct sockaddr_storage expected_addr; |
1208 | int clientfd = -1; |
1209 | int servfd = -1; |
1210 | int err = 0; |
1211 | |
1212 | if (init_addrs(test, requested_addr: &requested_addr, expected_addr: &expected_addr, |
1213 | expected_src_addr: &expected_src_addr)) |
1214 | goto err; |
1215 | |
1216 | /* Prepare server to connect to */ |
1217 | servfd = start_server(test->type, &expected_addr, addr_len); |
1218 | if (servfd == -1) |
1219 | goto err; |
1220 | |
1221 | clientfd = connect_to_server(test->type, &requested_addr, addr_len); |
1222 | if (clientfd == -1) |
1223 | goto err; |
1224 | |
1225 | /* Make sure src and dst addrs were overridden properly */ |
1226 | if (cmp_peer_addr(sock1: clientfd, addr2: &expected_addr)) |
1227 | goto err; |
1228 | |
1229 | if (cmp_local_ip(sock1: clientfd, addr2: &expected_src_addr)) |
1230 | goto err; |
1231 | |
1232 | if (test->type == SOCK_STREAM) { |
1233 | /* Test TCP Fast Open scenario */ |
1234 | clientfd = fastconnect_to_server(&requested_addr, addr_len); |
1235 | if (clientfd == -1) |
1236 | goto err; |
1237 | |
1238 | /* Make sure src and dst addrs were overridden properly */ |
1239 | if (cmp_peer_addr(sock1: clientfd, addr2: &expected_addr)) |
1240 | goto err; |
1241 | |
1242 | if (cmp_local_ip(sock1: clientfd, addr2: &expected_src_addr)) |
1243 | goto err; |
1244 | } |
1245 | |
1246 | goto out; |
1247 | err: |
1248 | err = -1; |
1249 | out: |
1250 | close(clientfd); |
1251 | close(servfd); |
1252 | return err; |
1253 | } |
1254 | |
1255 | static int run_xmsg_test_case(const struct sock_addr_test *test, int max_cmsg) |
1256 | { |
1257 | socklen_t addr_len = sizeof(struct sockaddr_storage); |
1258 | struct sockaddr_storage expected_addr; |
1259 | struct sockaddr_storage server_addr; |
1260 | struct sockaddr_storage sendmsg_addr; |
1261 | struct sockaddr_storage recvmsg_addr; |
1262 | int clientfd = -1; |
1263 | int servfd = -1; |
1264 | int set_cmsg; |
1265 | int err = 0; |
1266 | |
1267 | if (test->type != SOCK_DGRAM) |
1268 | goto err; |
1269 | |
1270 | if (init_addrs(test, requested_addr: &sendmsg_addr, expected_addr: &server_addr, expected_src_addr: &expected_addr)) |
1271 | goto err; |
1272 | |
1273 | /* Prepare server to sendmsg to */ |
1274 | servfd = start_server(test->type, &server_addr, addr_len); |
1275 | if (servfd == -1) |
1276 | goto err; |
1277 | |
1278 | for (set_cmsg = 0; set_cmsg <= max_cmsg; ++set_cmsg) { |
1279 | if (clientfd >= 0) |
1280 | close(clientfd); |
1281 | |
1282 | clientfd = sendmsg_to_server(test->type, &sendmsg_addr, |
1283 | addr_len, set_cmsg, /*flags*/0, |
1284 | &err); |
1285 | if (err) |
1286 | goto out; |
1287 | else if (clientfd == -1) |
1288 | goto err; |
1289 | |
1290 | /* Try to receive message on server instead of using |
1291 | * getpeername(2) on client socket, to check that client's |
1292 | * destination address was rewritten properly, since |
1293 | * getpeername(2) doesn't work with unconnected datagram |
1294 | * sockets. |
1295 | * |
1296 | * Get source address from recvmsg(2) as well to make sure |
1297 | * source was rewritten properly: getsockname(2) can't be used |
1298 | * since socket is unconnected and source defined for one |
1299 | * specific packet may differ from the one used by default and |
1300 | * returned by getsockname(2). |
1301 | */ |
1302 | if (recvmsg_from_client(sockfd: servfd, src_addr: &recvmsg_addr) == -1) |
1303 | goto err; |
1304 | |
1305 | if (cmp_addr(addr1: &recvmsg_addr, addr2: &expected_addr, /*cmp_port*/0)) |
1306 | goto err; |
1307 | } |
1308 | |
1309 | goto out; |
1310 | err: |
1311 | err = -1; |
1312 | out: |
1313 | close(clientfd); |
1314 | close(servfd); |
1315 | return err; |
1316 | } |
1317 | |
1318 | static int run_test_case(int cgfd, const struct sock_addr_test *test) |
1319 | { |
1320 | int progfd = -1; |
1321 | int err = 0; |
1322 | |
1323 | printf("Test case: %s .. " , test->descr); |
1324 | |
1325 | progfd = test->loadfn(test); |
1326 | if (test->expected_result == LOAD_REJECT && progfd < 0) |
1327 | goto out; |
1328 | else if (test->expected_result == LOAD_REJECT || progfd < 0) |
1329 | goto err; |
1330 | |
1331 | err = bpf_prog_attach(progfd, cgfd, test->attach_type, |
1332 | BPF_F_ALLOW_OVERRIDE); |
1333 | if (test->expected_result == ATTACH_REJECT && err) { |
1334 | err = 0; /* error was expected, reset it */ |
1335 | goto out; |
1336 | } else if (test->expected_result == ATTACH_REJECT || err) { |
1337 | goto err; |
1338 | } else if (test->expected_result == ATTACH_OKAY) { |
1339 | err = 0; |
1340 | goto out; |
1341 | } |
1342 | |
1343 | switch (test->attach_type) { |
1344 | case BPF_CGROUP_INET4_BIND: |
1345 | case BPF_CGROUP_INET6_BIND: |
1346 | err = run_bind_test_case(test); |
1347 | break; |
1348 | case BPF_CGROUP_INET4_CONNECT: |
1349 | case BPF_CGROUP_INET6_CONNECT: |
1350 | err = run_connect_test_case(test); |
1351 | break; |
1352 | case BPF_CGROUP_UDP4_SENDMSG: |
1353 | case BPF_CGROUP_UDP6_SENDMSG: |
1354 | err = run_xmsg_test_case(test, max_cmsg: 1); |
1355 | break; |
1356 | case BPF_CGROUP_UDP4_RECVMSG: |
1357 | case BPF_CGROUP_UDP6_RECVMSG: |
1358 | err = run_xmsg_test_case(test, max_cmsg: 0); |
1359 | break; |
1360 | default: |
1361 | goto err; |
1362 | } |
1363 | |
1364 | if (test->expected_result == SYSCALL_EPERM && err == EPERM) { |
1365 | err = 0; /* error was expected, reset it */ |
1366 | goto out; |
1367 | } |
1368 | |
1369 | if (test->expected_result == SYSCALL_ENOTSUPP && err == ENOTSUPP) { |
1370 | err = 0; /* error was expected, reset it */ |
1371 | goto out; |
1372 | } |
1373 | |
1374 | if (err || test->expected_result != SUCCESS) |
1375 | goto err; |
1376 | |
1377 | goto out; |
1378 | err: |
1379 | err = -1; |
1380 | out: |
1381 | /* Detaching w/o checking return code: best effort attempt. */ |
1382 | if (progfd != -1) |
1383 | bpf_prog_detach(cgfd, test->attach_type); |
1384 | close(progfd); |
1385 | printf("[%s]\n" , err ? "FAIL" : "PASS" ); |
1386 | return err; |
1387 | } |
1388 | |
1389 | static int run_tests(int cgfd) |
1390 | { |
1391 | int passes = 0; |
1392 | int fails = 0; |
1393 | int i; |
1394 | |
1395 | for (i = 0; i < ARRAY_SIZE(tests); ++i) { |
1396 | if (run_test_case(cgfd, test: &tests[i])) |
1397 | ++fails; |
1398 | else |
1399 | ++passes; |
1400 | } |
1401 | printf("Summary: %d PASSED, %d FAILED\n" , passes, fails); |
1402 | return fails ? -1 : 0; |
1403 | } |
1404 | |
1405 | int main(int argc, char **argv) |
1406 | { |
1407 | int cgfd = -1; |
1408 | int err = 0; |
1409 | |
1410 | if (argc < 2) { |
1411 | fprintf(stderr, |
1412 | "%s has to be run via %s.sh. Skip direct run.\n" , |
1413 | argv[0], argv[0]); |
1414 | exit(err); |
1415 | } |
1416 | |
1417 | cgfd = cgroup_setup_and_join(CG_PATH); |
1418 | if (cgfd < 0) |
1419 | goto err; |
1420 | |
1421 | /* Use libbpf 1.0 API mode */ |
1422 | libbpf_set_strict_mode(LIBBPF_STRICT_ALL); |
1423 | |
1424 | if (run_tests(cgfd)) |
1425 | goto err; |
1426 | |
1427 | goto out; |
1428 | err: |
1429 | err = -1; |
1430 | out: |
1431 | close(cgfd); |
1432 | cleanup_cgroup_environment(); |
1433 | return err; |
1434 | } |
1435 | |