Commit | Line | Data |
---|---|---|
e50b0a6f AI |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // Copyright (c) 2018 Facebook | |
3 | ||
4 | #include <stdio.h> | |
5 | #include <stdlib.h> | |
6 | #include <unistd.h> | |
7 | ||
8 | #include <arpa/inet.h> | |
9 | #include <sys/types.h> | |
10 | #include <sys/socket.h> | |
11 | ||
12 | #include <linux/filter.h> | |
13 | ||
14 | #include <bpf/bpf.h> | |
622adafb | 15 | #include <bpf/libbpf.h> |
e50b0a6f AI |
16 | |
17 | #include "cgroup_helpers.h" | |
0a0a7e00 | 18 | #include "bpf_rlimit.h" |
e50b0a6f | 19 | |
9be71aa6 AI |
20 | #ifndef ARRAY_SIZE |
21 | # define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) | |
22 | #endif | |
23 | ||
e50b0a6f | 24 | #define CG_PATH "/foo" |
622adafb AI |
25 | #define CONNECT4_PROG_PATH "./connect4_prog.o" |
26 | #define CONNECT6_PROG_PATH "./connect6_prog.o" | |
e50b0a6f AI |
27 | |
28 | #define SERV4_IP "192.168.1.254" | |
29 | #define SERV4_REWRITE_IP "127.0.0.1" | |
9be71aa6 | 30 | #define SRC4_REWRITE_IP "127.0.0.4" |
e50b0a6f AI |
31 | #define SERV4_PORT 4040 |
32 | #define SERV4_REWRITE_PORT 4444 | |
33 | ||
34 | #define SERV6_IP "face:b00c:1234:5678::abcd" | |
35 | #define SERV6_REWRITE_IP "::1" | |
9be71aa6 | 36 | #define SRC6_REWRITE_IP "::6" |
e50b0a6f AI |
37 | #define SERV6_PORT 6060 |
38 | #define SERV6_REWRITE_PORT 6666 | |
39 | ||
40 | #define INET_NTOP_BUF 40 | |
41 | ||
9be71aa6 AI |
42 | struct sock_addr_test; |
43 | ||
44 | typedef int (*load_fn)(const struct sock_addr_test *test); | |
e50b0a6f AI |
45 | typedef int (*info_fn)(int, struct sockaddr *, socklen_t *); |
46 | ||
9be71aa6 AI |
47 | char bpf_log_buf[BPF_LOG_BUF_SIZE]; |
48 | ||
49 | struct sock_addr_test { | |
50 | const char *descr; | |
51 | /* BPF prog properties */ | |
52 | load_fn loadfn; | |
53 | enum bpf_attach_type expected_attach_type; | |
54 | enum bpf_attach_type attach_type; | |
55 | /* Socket properties */ | |
56 | int domain; | |
57 | int type; | |
58 | /* IP:port pairs for BPF prog to override */ | |
59 | const char *requested_ip; | |
60 | unsigned short requested_port; | |
61 | const char *expected_ip; | |
62 | unsigned short expected_port; | |
63 | const char *expected_src_ip; | |
64 | /* Expected test result */ | |
65 | enum { | |
66 | LOAD_REJECT, | |
67 | ATTACH_REJECT, | |
68 | SUCCESS, | |
69 | } expected_result; | |
e50b0a6f AI |
70 | }; |
71 | ||
9be71aa6 AI |
72 | static int bind4_prog_load(const struct sock_addr_test *test); |
73 | static int bind6_prog_load(const struct sock_addr_test *test); | |
74 | static int connect4_prog_load(const struct sock_addr_test *test); | |
75 | static int connect6_prog_load(const struct sock_addr_test *test); | |
76 | ||
77 | static struct sock_addr_test tests[] = { | |
78 | /* bind */ | |
79 | { | |
80 | "bind4: load prog with wrong expected attach type", | |
81 | bind4_prog_load, | |
82 | BPF_CGROUP_INET6_BIND, | |
83 | BPF_CGROUP_INET4_BIND, | |
84 | AF_INET, | |
85 | SOCK_STREAM, | |
86 | NULL, | |
87 | 0, | |
88 | NULL, | |
89 | 0, | |
90 | NULL, | |
91 | LOAD_REJECT, | |
92 | }, | |
93 | { | |
94 | "bind4: attach prog with wrong attach type", | |
95 | bind4_prog_load, | |
96 | BPF_CGROUP_INET4_BIND, | |
97 | BPF_CGROUP_INET6_BIND, | |
98 | AF_INET, | |
99 | SOCK_STREAM, | |
100 | NULL, | |
101 | 0, | |
102 | NULL, | |
103 | 0, | |
104 | NULL, | |
105 | ATTACH_REJECT, | |
106 | }, | |
107 | { | |
108 | "bind4: rewrite IP & TCP port in", | |
109 | bind4_prog_load, | |
110 | BPF_CGROUP_INET4_BIND, | |
111 | BPF_CGROUP_INET4_BIND, | |
112 | AF_INET, | |
113 | SOCK_STREAM, | |
114 | SERV4_IP, | |
115 | SERV4_PORT, | |
116 | SERV4_REWRITE_IP, | |
117 | SERV4_REWRITE_PORT, | |
118 | NULL, | |
119 | SUCCESS, | |
120 | }, | |
121 | { | |
122 | "bind4: rewrite IP & UDP port in", | |
123 | bind4_prog_load, | |
124 | BPF_CGROUP_INET4_BIND, | |
125 | BPF_CGROUP_INET4_BIND, | |
126 | AF_INET, | |
127 | SOCK_DGRAM, | |
128 | SERV4_IP, | |
129 | SERV4_PORT, | |
130 | SERV4_REWRITE_IP, | |
131 | SERV4_REWRITE_PORT, | |
132 | NULL, | |
133 | SUCCESS, | |
134 | }, | |
135 | { | |
136 | "bind6: load prog with wrong expected attach type", | |
137 | bind6_prog_load, | |
138 | BPF_CGROUP_INET4_BIND, | |
139 | BPF_CGROUP_INET6_BIND, | |
140 | AF_INET6, | |
141 | SOCK_STREAM, | |
142 | NULL, | |
143 | 0, | |
144 | NULL, | |
145 | 0, | |
146 | NULL, | |
147 | LOAD_REJECT, | |
148 | }, | |
149 | { | |
150 | "bind6: attach prog with wrong attach type", | |
151 | bind6_prog_load, | |
152 | BPF_CGROUP_INET6_BIND, | |
153 | BPF_CGROUP_INET4_BIND, | |
154 | AF_INET, | |
155 | SOCK_STREAM, | |
156 | NULL, | |
157 | 0, | |
158 | NULL, | |
159 | 0, | |
160 | NULL, | |
161 | ATTACH_REJECT, | |
162 | }, | |
163 | { | |
164 | "bind6: rewrite IP & TCP port in", | |
165 | bind6_prog_load, | |
166 | BPF_CGROUP_INET6_BIND, | |
167 | BPF_CGROUP_INET6_BIND, | |
168 | AF_INET6, | |
169 | SOCK_STREAM, | |
170 | SERV6_IP, | |
171 | SERV6_PORT, | |
172 | SERV6_REWRITE_IP, | |
173 | SERV6_REWRITE_PORT, | |
174 | NULL, | |
175 | SUCCESS, | |
176 | }, | |
177 | { | |
178 | "bind6: rewrite IP & UDP port in", | |
179 | bind6_prog_load, | |
180 | BPF_CGROUP_INET6_BIND, | |
181 | BPF_CGROUP_INET6_BIND, | |
182 | AF_INET6, | |
183 | SOCK_DGRAM, | |
184 | SERV6_IP, | |
185 | SERV6_PORT, | |
186 | SERV6_REWRITE_IP, | |
187 | SERV6_REWRITE_PORT, | |
188 | NULL, | |
189 | SUCCESS, | |
190 | }, | |
191 | ||
192 | /* connect */ | |
193 | { | |
194 | "connect4: load prog with wrong expected attach type", | |
195 | connect4_prog_load, | |
196 | BPF_CGROUP_INET6_CONNECT, | |
197 | BPF_CGROUP_INET4_CONNECT, | |
198 | AF_INET, | |
199 | SOCK_STREAM, | |
200 | NULL, | |
201 | 0, | |
202 | NULL, | |
203 | 0, | |
204 | NULL, | |
205 | LOAD_REJECT, | |
206 | }, | |
207 | { | |
208 | "connect4: attach prog with wrong attach type", | |
209 | connect4_prog_load, | |
210 | BPF_CGROUP_INET4_CONNECT, | |
211 | BPF_CGROUP_INET6_CONNECT, | |
212 | AF_INET, | |
213 | SOCK_STREAM, | |
214 | NULL, | |
215 | 0, | |
216 | NULL, | |
217 | 0, | |
218 | NULL, | |
219 | ATTACH_REJECT, | |
220 | }, | |
221 | { | |
222 | "connect4: rewrite IP & TCP port", | |
223 | connect4_prog_load, | |
224 | BPF_CGROUP_INET4_CONNECT, | |
225 | BPF_CGROUP_INET4_CONNECT, | |
226 | AF_INET, | |
227 | SOCK_STREAM, | |
228 | SERV4_IP, | |
229 | SERV4_PORT, | |
230 | SERV4_REWRITE_IP, | |
231 | SERV4_REWRITE_PORT, | |
232 | SRC4_REWRITE_IP, | |
233 | SUCCESS, | |
234 | }, | |
235 | { | |
236 | "connect4: rewrite IP & UDP port", | |
237 | connect4_prog_load, | |
238 | BPF_CGROUP_INET4_CONNECT, | |
239 | BPF_CGROUP_INET4_CONNECT, | |
240 | AF_INET, | |
241 | SOCK_DGRAM, | |
242 | SERV4_IP, | |
243 | SERV4_PORT, | |
244 | SERV4_REWRITE_IP, | |
245 | SERV4_REWRITE_PORT, | |
246 | SRC4_REWRITE_IP, | |
247 | SUCCESS, | |
248 | }, | |
249 | { | |
250 | "connect6: load prog with wrong expected attach type", | |
251 | connect6_prog_load, | |
252 | BPF_CGROUP_INET4_CONNECT, | |
253 | BPF_CGROUP_INET6_CONNECT, | |
254 | AF_INET6, | |
255 | SOCK_STREAM, | |
256 | NULL, | |
257 | 0, | |
258 | NULL, | |
259 | 0, | |
260 | NULL, | |
261 | LOAD_REJECT, | |
262 | }, | |
263 | { | |
264 | "connect6: attach prog with wrong attach type", | |
265 | connect6_prog_load, | |
266 | BPF_CGROUP_INET6_CONNECT, | |
267 | BPF_CGROUP_INET4_CONNECT, | |
268 | AF_INET, | |
269 | SOCK_STREAM, | |
270 | NULL, | |
271 | 0, | |
272 | NULL, | |
273 | 0, | |
274 | NULL, | |
275 | ATTACH_REJECT, | |
276 | }, | |
277 | { | |
278 | "connect6: rewrite IP & TCP port", | |
279 | connect6_prog_load, | |
280 | BPF_CGROUP_INET6_CONNECT, | |
281 | BPF_CGROUP_INET6_CONNECT, | |
282 | AF_INET6, | |
283 | SOCK_STREAM, | |
284 | SERV6_IP, | |
285 | SERV6_PORT, | |
286 | SERV6_REWRITE_IP, | |
287 | SERV6_REWRITE_PORT, | |
288 | SRC6_REWRITE_IP, | |
289 | SUCCESS, | |
290 | }, | |
291 | { | |
292 | "connect6: rewrite IP & UDP port", | |
293 | connect6_prog_load, | |
294 | BPF_CGROUP_INET6_CONNECT, | |
295 | BPF_CGROUP_INET6_CONNECT, | |
296 | AF_INET6, | |
297 | SOCK_DGRAM, | |
298 | SERV6_IP, | |
299 | SERV6_PORT, | |
300 | SERV6_REWRITE_IP, | |
301 | SERV6_REWRITE_PORT, | |
302 | SRC6_REWRITE_IP, | |
303 | SUCCESS, | |
304 | }, | |
305 | }; | |
e50b0a6f AI |
306 | |
307 | static int mk_sockaddr(int domain, const char *ip, unsigned short port, | |
308 | struct sockaddr *addr, socklen_t addr_len) | |
309 | { | |
310 | struct sockaddr_in6 *addr6; | |
311 | struct sockaddr_in *addr4; | |
312 | ||
313 | if (domain != AF_INET && domain != AF_INET6) { | |
314 | log_err("Unsupported address family"); | |
315 | return -1; | |
316 | } | |
317 | ||
318 | memset(addr, 0, addr_len); | |
319 | ||
320 | if (domain == AF_INET) { | |
321 | if (addr_len < sizeof(struct sockaddr_in)) | |
322 | return -1; | |
323 | addr4 = (struct sockaddr_in *)addr; | |
324 | addr4->sin_family = domain; | |
325 | addr4->sin_port = htons(port); | |
326 | if (inet_pton(domain, ip, (void *)&addr4->sin_addr) != 1) { | |
327 | log_err("Invalid IPv4: %s", ip); | |
328 | return -1; | |
329 | } | |
330 | } else if (domain == AF_INET6) { | |
331 | if (addr_len < sizeof(struct sockaddr_in6)) | |
332 | return -1; | |
333 | addr6 = (struct sockaddr_in6 *)addr; | |
334 | addr6->sin6_family = domain; | |
335 | addr6->sin6_port = htons(port); | |
336 | if (inet_pton(domain, ip, (void *)&addr6->sin6_addr) != 1) { | |
337 | log_err("Invalid IPv6: %s", ip); | |
338 | return -1; | |
339 | } | |
340 | } | |
341 | ||
342 | return 0; | |
343 | } | |
344 | ||
9be71aa6 AI |
345 | static int load_insns(const struct sock_addr_test *test, |
346 | const struct bpf_insn *insns, size_t insns_cnt) | |
e50b0a6f AI |
347 | { |
348 | struct bpf_load_program_attr load_attr; | |
349 | int ret; | |
350 | ||
351 | memset(&load_attr, 0, sizeof(struct bpf_load_program_attr)); | |
352 | load_attr.prog_type = BPF_PROG_TYPE_CGROUP_SOCK_ADDR; | |
9be71aa6 | 353 | load_attr.expected_attach_type = test->expected_attach_type; |
e50b0a6f AI |
354 | load_attr.insns = insns; |
355 | load_attr.insns_cnt = insns_cnt; | |
356 | load_attr.license = "GPL"; | |
357 | ||
358 | ret = bpf_load_program_xattr(&load_attr, bpf_log_buf, BPF_LOG_BUF_SIZE); | |
9be71aa6 AI |
359 | if (ret < 0 && test->expected_result != LOAD_REJECT) { |
360 | log_err(">>> Loading program error.\n" | |
361 | ">>> Verifier output:\n%s\n-------\n", bpf_log_buf); | |
e50b0a6f AI |
362 | } |
363 | ||
364 | return ret; | |
365 | } | |
366 | ||
367 | /* [1] These testing programs try to read different context fields, including | |
368 | * narrow loads of different sizes from user_ip4 and user_ip6, and write to | |
369 | * those allowed to be overridden. | |
370 | * | |
371 | * [2] BPF_LD_IMM64 & BPF_JMP_REG are used below whenever there is a need to | |
372 | * compare a register with unsigned 32bit integer. BPF_JMP_IMM can't be used | |
373 | * in such cases since it accepts only _signed_ 32bit integer as IMM | |
374 | * argument. Also note that BPF_LD_IMM64 contains 2 instructions what matters | |
375 | * to count jumps properly. | |
376 | */ | |
377 | ||
9be71aa6 | 378 | static int bind4_prog_load(const struct sock_addr_test *test) |
e50b0a6f AI |
379 | { |
380 | union { | |
381 | uint8_t u4_addr8[4]; | |
382 | uint16_t u4_addr16[2]; | |
383 | uint32_t u4_addr32; | |
384 | } ip4; | |
385 | struct sockaddr_in addr4_rw; | |
386 | ||
387 | if (inet_pton(AF_INET, SERV4_IP, (void *)&ip4) != 1) { | |
388 | log_err("Invalid IPv4: %s", SERV4_IP); | |
389 | return -1; | |
390 | } | |
391 | ||
392 | if (mk_sockaddr(AF_INET, SERV4_REWRITE_IP, SERV4_REWRITE_PORT, | |
393 | (struct sockaddr *)&addr4_rw, sizeof(addr4_rw)) == -1) | |
394 | return -1; | |
395 | ||
396 | /* See [1]. */ | |
397 | struct bpf_insn insns[] = { | |
398 | BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), | |
399 | ||
400 | /* if (sk.family == AF_INET && */ | |
401 | BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6, | |
402 | offsetof(struct bpf_sock_addr, family)), | |
403 | BPF_JMP_IMM(BPF_JNE, BPF_REG_7, AF_INET, 16), | |
404 | ||
405 | /* (sk.type == SOCK_DGRAM || sk.type == SOCK_STREAM) && */ | |
406 | BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6, | |
407 | offsetof(struct bpf_sock_addr, type)), | |
408 | BPF_JMP_IMM(BPF_JNE, BPF_REG_7, SOCK_DGRAM, 1), | |
409 | BPF_JMP_A(1), | |
410 | BPF_JMP_IMM(BPF_JNE, BPF_REG_7, SOCK_STREAM, 12), | |
411 | ||
412 | /* 1st_byte_of_user_ip4 == expected && */ | |
413 | BPF_LDX_MEM(BPF_B, BPF_REG_7, BPF_REG_6, | |
414 | offsetof(struct bpf_sock_addr, user_ip4)), | |
415 | BPF_JMP_IMM(BPF_JNE, BPF_REG_7, ip4.u4_addr8[0], 10), | |
416 | ||
417 | /* 1st_half_of_user_ip4 == expected && */ | |
418 | BPF_LDX_MEM(BPF_H, BPF_REG_7, BPF_REG_6, | |
419 | offsetof(struct bpf_sock_addr, user_ip4)), | |
420 | BPF_JMP_IMM(BPF_JNE, BPF_REG_7, ip4.u4_addr16[0], 8), | |
421 | ||
422 | /* whole_user_ip4 == expected) { */ | |
423 | BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6, | |
424 | offsetof(struct bpf_sock_addr, user_ip4)), | |
425 | BPF_LD_IMM64(BPF_REG_8, ip4.u4_addr32), /* See [2]. */ | |
426 | BPF_JMP_REG(BPF_JNE, BPF_REG_7, BPF_REG_8, 4), | |
427 | ||
428 | /* user_ip4 = addr4_rw.sin_addr */ | |
429 | BPF_MOV32_IMM(BPF_REG_7, addr4_rw.sin_addr.s_addr), | |
430 | BPF_STX_MEM(BPF_W, BPF_REG_6, BPF_REG_7, | |
431 | offsetof(struct bpf_sock_addr, user_ip4)), | |
432 | ||
433 | /* user_port = addr4_rw.sin_port */ | |
434 | BPF_MOV32_IMM(BPF_REG_7, addr4_rw.sin_port), | |
435 | BPF_STX_MEM(BPF_W, BPF_REG_6, BPF_REG_7, | |
436 | offsetof(struct bpf_sock_addr, user_port)), | |
437 | /* } */ | |
438 | ||
439 | /* return 1 */ | |
440 | BPF_MOV64_IMM(BPF_REG_0, 1), | |
441 | BPF_EXIT_INSN(), | |
442 | }; | |
443 | ||
9be71aa6 | 444 | return load_insns(test, insns, sizeof(insns) / sizeof(struct bpf_insn)); |
e50b0a6f AI |
445 | } |
446 | ||
9be71aa6 | 447 | static int bind6_prog_load(const struct sock_addr_test *test) |
e50b0a6f AI |
448 | { |
449 | struct sockaddr_in6 addr6_rw; | |
450 | struct in6_addr ip6; | |
451 | ||
452 | if (inet_pton(AF_INET6, SERV6_IP, (void *)&ip6) != 1) { | |
453 | log_err("Invalid IPv6: %s", SERV6_IP); | |
454 | return -1; | |
455 | } | |
456 | ||
457 | if (mk_sockaddr(AF_INET6, SERV6_REWRITE_IP, SERV6_REWRITE_PORT, | |
458 | (struct sockaddr *)&addr6_rw, sizeof(addr6_rw)) == -1) | |
459 | return -1; | |
460 | ||
461 | /* See [1]. */ | |
462 | struct bpf_insn insns[] = { | |
463 | BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), | |
464 | ||
465 | /* if (sk.family == AF_INET6 && */ | |
466 | BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6, | |
467 | offsetof(struct bpf_sock_addr, family)), | |
468 | BPF_JMP_IMM(BPF_JNE, BPF_REG_7, AF_INET6, 18), | |
469 | ||
470 | /* 5th_byte_of_user_ip6 == expected && */ | |
471 | BPF_LDX_MEM(BPF_B, BPF_REG_7, BPF_REG_6, | |
472 | offsetof(struct bpf_sock_addr, user_ip6[1])), | |
473 | BPF_JMP_IMM(BPF_JNE, BPF_REG_7, ip6.s6_addr[4], 16), | |
474 | ||
475 | /* 3rd_half_of_user_ip6 == expected && */ | |
476 | BPF_LDX_MEM(BPF_H, BPF_REG_7, BPF_REG_6, | |
477 | offsetof(struct bpf_sock_addr, user_ip6[1])), | |
478 | BPF_JMP_IMM(BPF_JNE, BPF_REG_7, ip6.s6_addr16[2], 14), | |
479 | ||
480 | /* last_word_of_user_ip6 == expected) { */ | |
481 | BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6, | |
482 | offsetof(struct bpf_sock_addr, user_ip6[3])), | |
483 | BPF_LD_IMM64(BPF_REG_8, ip6.s6_addr32[3]), /* See [2]. */ | |
484 | BPF_JMP_REG(BPF_JNE, BPF_REG_7, BPF_REG_8, 10), | |
485 | ||
486 | ||
487 | #define STORE_IPV6_WORD(N) \ | |
488 | BPF_MOV32_IMM(BPF_REG_7, addr6_rw.sin6_addr.s6_addr32[N]), \ | |
489 | BPF_STX_MEM(BPF_W, BPF_REG_6, BPF_REG_7, \ | |
490 | offsetof(struct bpf_sock_addr, user_ip6[N])) | |
491 | ||
492 | /* user_ip6 = addr6_rw.sin6_addr */ | |
493 | STORE_IPV6_WORD(0), | |
494 | STORE_IPV6_WORD(1), | |
495 | STORE_IPV6_WORD(2), | |
496 | STORE_IPV6_WORD(3), | |
497 | ||
498 | /* user_port = addr6_rw.sin6_port */ | |
499 | BPF_MOV32_IMM(BPF_REG_7, addr6_rw.sin6_port), | |
500 | BPF_STX_MEM(BPF_W, BPF_REG_6, BPF_REG_7, | |
501 | offsetof(struct bpf_sock_addr, user_port)), | |
502 | ||
503 | /* } */ | |
504 | ||
505 | /* return 1 */ | |
506 | BPF_MOV64_IMM(BPF_REG_0, 1), | |
507 | BPF_EXIT_INSN(), | |
508 | }; | |
509 | ||
9be71aa6 | 510 | return load_insns(test, insns, sizeof(insns) / sizeof(struct bpf_insn)); |
e50b0a6f AI |
511 | } |
512 | ||
9be71aa6 | 513 | static int load_path(const struct sock_addr_test *test, const char *path) |
622adafb AI |
514 | { |
515 | struct bpf_prog_load_attr attr; | |
516 | struct bpf_object *obj; | |
517 | int prog_fd; | |
518 | ||
519 | memset(&attr, 0, sizeof(struct bpf_prog_load_attr)); | |
520 | attr.file = path; | |
521 | attr.prog_type = BPF_PROG_TYPE_CGROUP_SOCK_ADDR; | |
9be71aa6 | 522 | attr.expected_attach_type = test->expected_attach_type; |
622adafb AI |
523 | |
524 | if (bpf_prog_load_xattr(&attr, &obj, &prog_fd)) { | |
9be71aa6 AI |
525 | if (test->expected_result != LOAD_REJECT) |
526 | log_err(">>> Loading program (%s) error.\n", path); | |
622adafb AI |
527 | return -1; |
528 | } | |
529 | ||
530 | return prog_fd; | |
531 | } | |
532 | ||
9be71aa6 | 533 | static int connect4_prog_load(const struct sock_addr_test *test) |
622adafb | 534 | { |
9be71aa6 | 535 | return load_path(test, CONNECT4_PROG_PATH); |
622adafb AI |
536 | } |
537 | ||
9be71aa6 | 538 | static int connect6_prog_load(const struct sock_addr_test *test) |
622adafb | 539 | { |
9be71aa6 | 540 | return load_path(test, CONNECT6_PROG_PATH); |
622adafb AI |
541 | } |
542 | ||
9be71aa6 AI |
543 | static int cmp_addr(const struct sockaddr_storage *addr1, |
544 | const struct sockaddr_storage *addr2, int cmp_port) | |
e50b0a6f | 545 | { |
9be71aa6 AI |
546 | const struct sockaddr_in *four1, *four2; |
547 | const struct sockaddr_in6 *six1, *six2; | |
548 | ||
549 | if (addr1->ss_family != addr2->ss_family) | |
550 | return -1; | |
551 | ||
552 | if (addr1->ss_family == AF_INET) { | |
553 | four1 = (const struct sockaddr_in *)addr1; | |
554 | four2 = (const struct sockaddr_in *)addr2; | |
555 | return !((four1->sin_port == four2->sin_port || !cmp_port) && | |
556 | four1->sin_addr.s_addr == four2->sin_addr.s_addr); | |
557 | } else if (addr1->ss_family == AF_INET6) { | |
558 | six1 = (const struct sockaddr_in6 *)addr1; | |
559 | six2 = (const struct sockaddr_in6 *)addr2; | |
560 | return !((six1->sin6_port == six2->sin6_port || !cmp_port) && | |
561 | !memcmp(&six1->sin6_addr, &six2->sin6_addr, | |
562 | sizeof(struct in6_addr))); | |
e50b0a6f | 563 | } |
9be71aa6 AI |
564 | |
565 | return -1; | |
566 | } | |
567 | ||
568 | static int cmp_sock_addr(info_fn fn, int sock1, | |
569 | const struct sockaddr_storage *addr2, int cmp_port) | |
570 | { | |
571 | struct sockaddr_storage addr1; | |
572 | socklen_t len1 = sizeof(addr1); | |
573 | ||
574 | memset(&addr1, 0, len1); | |
575 | if (fn(sock1, (struct sockaddr *)&addr1, (socklen_t *)&len1) != 0) | |
576 | return -1; | |
577 | ||
578 | return cmp_addr(&addr1, addr2, cmp_port); | |
579 | } | |
580 | ||
581 | static int cmp_local_ip(int sock1, const struct sockaddr_storage *addr2) | |
582 | { | |
583 | return cmp_sock_addr(getsockname, sock1, addr2, /*cmp_port*/ 0); | |
e50b0a6f AI |
584 | } |
585 | ||
9be71aa6 | 586 | static int cmp_local_addr(int sock1, const struct sockaddr_storage *addr2) |
e50b0a6f | 587 | { |
9be71aa6 | 588 | return cmp_sock_addr(getsockname, sock1, addr2, /*cmp_port*/ 1); |
e50b0a6f AI |
589 | } |
590 | ||
9be71aa6 | 591 | static int cmp_peer_addr(int sock1, const struct sockaddr_storage *addr2) |
622adafb | 592 | { |
9be71aa6 | 593 | return cmp_sock_addr(getpeername, sock1, addr2, /*cmp_port*/ 1); |
622adafb AI |
594 | } |
595 | ||
e50b0a6f AI |
596 | static int start_server(int type, const struct sockaddr_storage *addr, |
597 | socklen_t addr_len) | |
598 | { | |
e50b0a6f AI |
599 | int fd; |
600 | ||
601 | fd = socket(addr->ss_family, type, 0); | |
602 | if (fd == -1) { | |
603 | log_err("Failed to create server socket"); | |
604 | goto out; | |
605 | } | |
606 | ||
607 | if (bind(fd, (const struct sockaddr *)addr, addr_len) == -1) { | |
608 | log_err("Failed to bind server socket"); | |
609 | goto close_out; | |
610 | } | |
611 | ||
612 | if (type == SOCK_STREAM) { | |
613 | if (listen(fd, 128) == -1) { | |
614 | log_err("Failed to listen on server socket"); | |
615 | goto close_out; | |
616 | } | |
617 | } | |
618 | ||
e50b0a6f AI |
619 | goto out; |
620 | close_out: | |
621 | close(fd); | |
622 | fd = -1; | |
623 | out: | |
624 | return fd; | |
625 | } | |
626 | ||
622adafb AI |
627 | static int connect_to_server(int type, const struct sockaddr_storage *addr, |
628 | socklen_t addr_len) | |
629 | { | |
630 | int domain; | |
9be71aa6 | 631 | int fd = -1; |
622adafb AI |
632 | |
633 | domain = addr->ss_family; | |
634 | ||
635 | if (domain != AF_INET && domain != AF_INET6) { | |
636 | log_err("Unsupported address family"); | |
9be71aa6 | 637 | goto err; |
622adafb AI |
638 | } |
639 | ||
640 | fd = socket(domain, type, 0); | |
641 | if (fd == -1) { | |
9be71aa6 AI |
642 | log_err("Failed to create client socket"); |
643 | goto err; | |
622adafb AI |
644 | } |
645 | ||
646 | if (connect(fd, (const struct sockaddr *)addr, addr_len) == -1) { | |
647 | log_err("Fail to connect to server"); | |
648 | goto err; | |
649 | } | |
650 | ||
9be71aa6 | 651 | goto out; |
622adafb AI |
652 | err: |
653 | close(fd); | |
9be71aa6 AI |
654 | fd = -1; |
655 | out: | |
656 | return fd; | |
622adafb AI |
657 | } |
658 | ||
9be71aa6 AI |
659 | static int init_addrs(const struct sock_addr_test *test, |
660 | struct sockaddr_storage *requested_addr, | |
661 | struct sockaddr_storage *expected_addr, | |
662 | struct sockaddr_storage *expected_src_addr) | |
e50b0a6f | 663 | { |
9be71aa6 AI |
664 | socklen_t addr_len = sizeof(struct sockaddr_storage); |
665 | ||
666 | if (mk_sockaddr(test->domain, test->expected_ip, test->expected_port, | |
667 | (struct sockaddr *)expected_addr, addr_len) == -1) | |
668 | goto err; | |
669 | ||
670 | if (mk_sockaddr(test->domain, test->requested_ip, test->requested_port, | |
671 | (struct sockaddr *)requested_addr, addr_len) == -1) | |
672 | goto err; | |
673 | ||
674 | if (test->expected_src_ip && | |
675 | mk_sockaddr(test->domain, test->expected_src_ip, 0, | |
676 | (struct sockaddr *)expected_src_addr, addr_len) == -1) | |
677 | goto err; | |
678 | ||
679 | return 0; | |
680 | err: | |
681 | return -1; | |
e50b0a6f AI |
682 | } |
683 | ||
9be71aa6 | 684 | static int run_bind_test_case(const struct sock_addr_test *test) |
e50b0a6f | 685 | { |
9be71aa6 AI |
686 | socklen_t addr_len = sizeof(struct sockaddr_storage); |
687 | struct sockaddr_storage requested_addr; | |
688 | struct sockaddr_storage expected_addr; | |
689 | int clientfd = -1; | |
e50b0a6f AI |
690 | int servfd = -1; |
691 | int err = 0; | |
692 | ||
9be71aa6 AI |
693 | if (init_addrs(test, &requested_addr, &expected_addr, NULL)) |
694 | goto err; | |
e50b0a6f | 695 | |
9be71aa6 | 696 | servfd = start_server(test->type, &requested_addr, addr_len); |
e50b0a6f AI |
697 | if (servfd == -1) |
698 | goto err; | |
699 | ||
9be71aa6 AI |
700 | if (cmp_local_addr(servfd, &expected_addr)) |
701 | goto err; | |
702 | ||
703 | /* Try to connect to server just in case */ | |
704 | clientfd = connect_to_server(test->type, &expected_addr, addr_len); | |
705 | if (clientfd == -1) | |
622adafb AI |
706 | goto err; |
707 | ||
e50b0a6f AI |
708 | goto out; |
709 | err: | |
710 | err = -1; | |
711 | out: | |
9be71aa6 | 712 | close(clientfd); |
e50b0a6f AI |
713 | close(servfd); |
714 | return err; | |
715 | } | |
716 | ||
9be71aa6 | 717 | static int run_connect_test_case(const struct sock_addr_test *test) |
e50b0a6f | 718 | { |
9be71aa6 AI |
719 | socklen_t addr_len = sizeof(struct sockaddr_storage); |
720 | struct sockaddr_storage expected_src_addr; | |
721 | struct sockaddr_storage requested_addr; | |
722 | struct sockaddr_storage expected_addr; | |
723 | int clientfd = -1; | |
724 | int servfd = -1; | |
725 | int err = 0; | |
e50b0a6f | 726 | |
9be71aa6 AI |
727 | if (init_addrs(test, &requested_addr, &expected_addr, |
728 | &expected_src_addr)) | |
729 | goto err; | |
e50b0a6f | 730 | |
9be71aa6 AI |
731 | /* Prepare server to connect to */ |
732 | servfd = start_server(test->type, &expected_addr, addr_len); | |
733 | if (servfd == -1) | |
734 | goto err; | |
622adafb | 735 | |
9be71aa6 AI |
736 | clientfd = connect_to_server(test->type, &requested_addr, addr_len); |
737 | if (clientfd == -1) | |
738 | goto err; | |
622adafb | 739 | |
9be71aa6 AI |
740 | /* Make sure src and dst addrs were overridden properly */ |
741 | if (cmp_peer_addr(clientfd, &expected_addr)) | |
742 | goto err; | |
e50b0a6f | 743 | |
9be71aa6 AI |
744 | if (cmp_local_ip(clientfd, &expected_src_addr)) |
745 | goto err; | |
746 | ||
747 | goto out; | |
e50b0a6f | 748 | err: |
9be71aa6 AI |
749 | err = -1; |
750 | out: | |
751 | close(clientfd); | |
752 | close(servfd); | |
753 | return err; | |
e50b0a6f AI |
754 | } |
755 | ||
9be71aa6 | 756 | static int run_test_case(int cgfd, const struct sock_addr_test *test) |
e50b0a6f | 757 | { |
9be71aa6 | 758 | int progfd = -1; |
e50b0a6f AI |
759 | int err = 0; |
760 | ||
9be71aa6 AI |
761 | printf("Test case: %s .. ", test->descr); |
762 | ||
763 | progfd = test->loadfn(test); | |
764 | if (test->expected_result == LOAD_REJECT && progfd < 0) | |
765 | goto out; | |
766 | else if (test->expected_result == LOAD_REJECT || progfd < 0) | |
767 | goto err; | |
768 | ||
769 | err = bpf_prog_attach(progfd, cgfd, test->attach_type, | |
770 | BPF_F_ALLOW_OVERRIDE); | |
771 | if (test->expected_result == ATTACH_REJECT && err) { | |
772 | err = 0; /* error was expected, reset it */ | |
773 | goto out; | |
774 | } else if (test->expected_result == ATTACH_REJECT || err) { | |
e50b0a6f | 775 | goto err; |
9be71aa6 | 776 | } |
e50b0a6f | 777 | |
9be71aa6 AI |
778 | switch (test->attach_type) { |
779 | case BPF_CGROUP_INET4_BIND: | |
780 | case BPF_CGROUP_INET6_BIND: | |
781 | err = run_bind_test_case(test); | |
782 | break; | |
783 | case BPF_CGROUP_INET4_CONNECT: | |
784 | case BPF_CGROUP_INET6_CONNECT: | |
785 | err = run_connect_test_case(test); | |
786 | break; | |
787 | default: | |
e50b0a6f | 788 | goto err; |
9be71aa6 | 789 | } |
e50b0a6f | 790 | |
9be71aa6 | 791 | if (err || test->expected_result != SUCCESS) |
e50b0a6f AI |
792 | goto err; |
793 | ||
794 | goto out; | |
795 | err: | |
796 | err = -1; | |
797 | out: | |
9be71aa6 AI |
798 | /* Detaching w/o checking return code: best effort attempt. */ |
799 | if (progfd != -1) | |
800 | bpf_prog_detach(cgfd, test->attach_type); | |
801 | close(progfd); | |
802 | printf("[%s]\n", err ? "FAIL" : "PASS"); | |
e50b0a6f AI |
803 | return err; |
804 | } | |
805 | ||
9be71aa6 AI |
806 | static int run_tests(int cgfd) |
807 | { | |
808 | int passes = 0; | |
809 | int fails = 0; | |
810 | int i; | |
811 | ||
812 | for (i = 0; i < ARRAY_SIZE(tests); ++i) { | |
813 | if (run_test_case(cgfd, &tests[i])) | |
814 | ++fails; | |
815 | else | |
816 | ++passes; | |
817 | } | |
818 | printf("Summary: %d PASSED, %d FAILED\n", passes, fails); | |
819 | return fails ? -1 : 0; | |
820 | } | |
821 | ||
822 | int main(int argc, char **argv) | |
e50b0a6f | 823 | { |
e50b0a6f AI |
824 | int cgfd = -1; |
825 | int err = 0; | |
826 | ||
9be71aa6 AI |
827 | if (argc < 2) { |
828 | fprintf(stderr, | |
829 | "%s has to be run via %s.sh. Skip direct run.\n", | |
830 | argv[0], argv[0]); | |
831 | exit(err); | |
832 | } | |
e50b0a6f AI |
833 | |
834 | if (setup_cgroup_environment()) | |
835 | goto err; | |
836 | ||
837 | cgfd = create_and_get_cgroup(CG_PATH); | |
838 | if (!cgfd) | |
839 | goto err; | |
840 | ||
841 | if (join_cgroup(CG_PATH)) | |
842 | goto err; | |
843 | ||
9be71aa6 | 844 | if (run_tests(cgfd)) |
e50b0a6f AI |
845 | goto err; |
846 | ||
847 | goto out; | |
848 | err: | |
849 | err = -1; | |
850 | out: | |
851 | close(cgfd); | |
852 | cleanup_cgroup_environment(); | |
e50b0a6f AI |
853 | return err; |
854 | } |