1 // SPDX-License-Identifier: GPL-2.0
2 // Copyright (c) 2018 Facebook
9 #include <sys/socket.h>
11 #include <linux/filter.h>
15 #include "cgroup_helpers.h"
16 #include <bpf/bpf_endian.h>
19 #define CG_PATH "/foo"
22 char bpf_log_buf[BPF_LOG_BUF_SIZE];
23 static bool verbose = false;
27 /* BPF prog properties */
28 struct bpf_insn insns[MAX_INSNS];
29 enum bpf_attach_type expected_attach_type;
30 enum bpf_attach_type attach_type;
31 /* Socket properties */
34 /* Endpoint to bind() to */
37 unsigned short port_retry;
38 /* Expected test result */
49 static struct sock_test tests[] = {
51 .descr = "bind4 load with invalid access: src_ip6",
53 BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
54 BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
55 offsetof(struct bpf_sock, src_ip6[0])),
56 BPF_MOV64_IMM(BPF_REG_0, 1),
59 .expected_attach_type = BPF_CGROUP_INET4_POST_BIND,
60 .attach_type = BPF_CGROUP_INET4_POST_BIND,
61 .result = LOAD_REJECT,
64 .descr = "bind4 load with invalid access: mark",
66 BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
67 BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
68 offsetof(struct bpf_sock, mark)),
69 BPF_MOV64_IMM(BPF_REG_0, 1),
72 .expected_attach_type = BPF_CGROUP_INET4_POST_BIND,
73 .attach_type = BPF_CGROUP_INET4_POST_BIND,
74 .result = LOAD_REJECT,
77 .descr = "bind6 load with invalid access: src_ip4",
79 BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
80 BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
81 offsetof(struct bpf_sock, src_ip4)),
82 BPF_MOV64_IMM(BPF_REG_0, 1),
85 .expected_attach_type = BPF_CGROUP_INET6_POST_BIND,
86 .attach_type = BPF_CGROUP_INET6_POST_BIND,
87 .result = LOAD_REJECT,
90 .descr = "sock_create load with invalid access: src_port",
92 BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
93 BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
94 offsetof(struct bpf_sock, src_port)),
95 BPF_MOV64_IMM(BPF_REG_0, 1),
98 .expected_attach_type = BPF_CGROUP_INET_SOCK_CREATE,
99 .attach_type = BPF_CGROUP_INET_SOCK_CREATE,
100 .result = LOAD_REJECT,
103 .descr = "sock_create load w/o expected_attach_type (compat mode)",
105 BPF_MOV64_IMM(BPF_REG_0, 1),
108 .expected_attach_type = 0,
109 .attach_type = BPF_CGROUP_INET_SOCK_CREATE,
117 .descr = "sock_create load w/ expected_attach_type",
119 BPF_MOV64_IMM(BPF_REG_0, 1),
122 .expected_attach_type = BPF_CGROUP_INET_SOCK_CREATE,
123 .attach_type = BPF_CGROUP_INET_SOCK_CREATE,
131 .descr = "attach type mismatch bind4 vs bind6",
133 BPF_MOV64_IMM(BPF_REG_0, 1),
136 .expected_attach_type = BPF_CGROUP_INET4_POST_BIND,
137 .attach_type = BPF_CGROUP_INET6_POST_BIND,
138 .result = ATTACH_REJECT,
141 .descr = "attach type mismatch bind6 vs bind4",
143 BPF_MOV64_IMM(BPF_REG_0, 1),
146 .expected_attach_type = BPF_CGROUP_INET6_POST_BIND,
147 .attach_type = BPF_CGROUP_INET4_POST_BIND,
148 .result = ATTACH_REJECT,
151 .descr = "attach type mismatch default vs bind4",
153 BPF_MOV64_IMM(BPF_REG_0, 1),
156 .expected_attach_type = 0,
157 .attach_type = BPF_CGROUP_INET4_POST_BIND,
158 .result = ATTACH_REJECT,
161 .descr = "attach type mismatch bind6 vs sock_create",
163 BPF_MOV64_IMM(BPF_REG_0, 1),
166 .expected_attach_type = BPF_CGROUP_INET6_POST_BIND,
167 .attach_type = BPF_CGROUP_INET_SOCK_CREATE,
168 .result = ATTACH_REJECT,
171 .descr = "bind4 reject all",
173 BPF_MOV64_IMM(BPF_REG_0, 0),
176 .expected_attach_type = BPF_CGROUP_INET4_POST_BIND,
177 .attach_type = BPF_CGROUP_INET4_POST_BIND,
181 .result = BIND_REJECT,
184 .descr = "bind6 reject all",
186 BPF_MOV64_IMM(BPF_REG_0, 0),
189 .expected_attach_type = BPF_CGROUP_INET6_POST_BIND,
190 .attach_type = BPF_CGROUP_INET6_POST_BIND,
194 .result = BIND_REJECT,
197 .descr = "bind6 deny specific IP & port",
199 BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
201 /* if (ip == expected && port == expected) */
202 BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
203 offsetof(struct bpf_sock, src_ip6[3])),
204 BPF_JMP_IMM(BPF_JNE, BPF_REG_7,
205 __bpf_constant_ntohl(0x00000001), 4),
206 BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
207 offsetof(struct bpf_sock, src_port)),
208 BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 0x2001, 2),
211 BPF_MOV64_IMM(BPF_REG_0, 0),
214 /* else return ALLOW; */
215 BPF_MOV64_IMM(BPF_REG_0, 1),
218 .expected_attach_type = BPF_CGROUP_INET6_POST_BIND,
219 .attach_type = BPF_CGROUP_INET6_POST_BIND,
224 .result = BIND_REJECT,
227 .descr = "bind4 allow specific IP & port",
229 BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
231 /* if (ip == expected && port == expected) */
232 BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
233 offsetof(struct bpf_sock, src_ip4)),
234 BPF_JMP_IMM(BPF_JNE, BPF_REG_7,
235 __bpf_constant_ntohl(0x7F000001), 4),
236 BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
237 offsetof(struct bpf_sock, src_port)),
238 BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 0x1002, 2),
241 BPF_MOV64_IMM(BPF_REG_0, 1),
244 /* else return DENY; */
245 BPF_MOV64_IMM(BPF_REG_0, 0),
248 .expected_attach_type = BPF_CGROUP_INET4_POST_BIND,
249 .attach_type = BPF_CGROUP_INET4_POST_BIND,
257 .descr = "bind4 deny specific IP & port of TCP, and retry",
259 BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
261 /* if (ip == expected && port == expected) */
262 BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
263 offsetof(struct bpf_sock, src_ip4)),
264 BPF_JMP_IMM(BPF_JNE, BPF_REG_7,
265 __bpf_constant_ntohl(0x7F000001), 4),
266 BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
267 offsetof(struct bpf_sock, src_port)),
268 BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 0x1002, 2),
271 BPF_MOV64_IMM(BPF_REG_0, 0),
274 /* else return ALLOW; */
275 BPF_MOV64_IMM(BPF_REG_0, 1),
278 .expected_attach_type = BPF_CGROUP_INET4_POST_BIND,
279 .attach_type = BPF_CGROUP_INET4_POST_BIND,
285 .result = RETRY_SUCCESS,
288 .descr = "bind4 deny specific IP & port of UDP, and retry",
290 BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
292 /* if (ip == expected && port == expected) */
293 BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
294 offsetof(struct bpf_sock, src_ip4)),
295 BPF_JMP_IMM(BPF_JNE, BPF_REG_7,
296 __bpf_constant_ntohl(0x7F000001), 4),
297 BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
298 offsetof(struct bpf_sock, src_port)),
299 BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 0x1002, 2),
302 BPF_MOV64_IMM(BPF_REG_0, 0),
305 /* else return ALLOW; */
306 BPF_MOV64_IMM(BPF_REG_0, 1),
309 .expected_attach_type = BPF_CGROUP_INET4_POST_BIND,
310 .attach_type = BPF_CGROUP_INET4_POST_BIND,
316 .result = RETRY_SUCCESS,
319 .descr = "bind6 deny specific IP & port, and retry",
321 BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
323 /* if (ip == expected && port == expected) */
324 BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
325 offsetof(struct bpf_sock, src_ip6[3])),
326 BPF_JMP_IMM(BPF_JNE, BPF_REG_7,
327 __bpf_constant_ntohl(0x00000001), 4),
328 BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
329 offsetof(struct bpf_sock, src_port)),
330 BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 0x2001, 2),
333 BPF_MOV64_IMM(BPF_REG_0, 0),
336 /* else return ALLOW; */
337 BPF_MOV64_IMM(BPF_REG_0, 1),
340 .expected_attach_type = BPF_CGROUP_INET6_POST_BIND,
341 .attach_type = BPF_CGROUP_INET6_POST_BIND,
347 .result = RETRY_SUCCESS,
350 .descr = "bind4 allow all",
352 BPF_MOV64_IMM(BPF_REG_0, 1),
355 .expected_attach_type = BPF_CGROUP_INET4_POST_BIND,
356 .attach_type = BPF_CGROUP_INET4_POST_BIND,
363 .descr = "bind6 allow all",
365 BPF_MOV64_IMM(BPF_REG_0, 1),
368 .expected_attach_type = BPF_CGROUP_INET6_POST_BIND,
369 .attach_type = BPF_CGROUP_INET6_POST_BIND,
377 static size_t probe_prog_length(const struct bpf_insn *fp)
381 for (len = MAX_INSNS - 1; len > 0; --len)
382 if (fp[len].code != 0 || fp[len].imm != 0)
387 static int load_sock_prog(const struct bpf_insn *prog,
388 enum bpf_attach_type attach_type)
390 LIBBPF_OPTS(bpf_prog_load_opts, opts);
393 insn_cnt = probe_prog_length(prog);
395 opts.expected_attach_type = attach_type;
396 opts.log_buf = bpf_log_buf;
397 opts.log_size = BPF_LOG_BUF_SIZE;
400 ret = bpf_prog_load(BPF_PROG_TYPE_CGROUP_SOCK, NULL, "GPL", prog, insn_cnt, &opts);
401 if (verbose && ret < 0)
402 fprintf(stderr, "%s\n", bpf_log_buf);
407 static int attach_sock_prog(int cgfd, int progfd,
408 enum bpf_attach_type attach_type)
410 return bpf_prog_attach(progfd, cgfd, attach_type, BPF_F_ALLOW_OVERRIDE);
413 static int bind_sock(int domain, int type, const char *ip,
414 unsigned short port, unsigned short port_retry)
416 struct sockaddr_storage addr;
417 struct sockaddr_in6 *addr6;
418 struct sockaddr_in *addr4;
423 sockfd = socket(domain, type, 0);
427 memset(&addr, 0, sizeof(addr));
429 if (domain == AF_INET) {
430 len = sizeof(struct sockaddr_in);
431 addr4 = (struct sockaddr_in *)&addr;
432 addr4->sin_family = domain;
433 addr4->sin_port = htons(port);
434 if (inet_pton(domain, ip, (void *)&addr4->sin_addr) != 1)
436 } else if (domain == AF_INET6) {
437 len = sizeof(struct sockaddr_in6);
438 addr6 = (struct sockaddr_in6 *)&addr;
439 addr6->sin6_family = domain;
440 addr6->sin6_port = htons(port);
441 if (inet_pton(domain, ip, (void *)&addr6->sin6_addr) != 1)
447 if (bind(sockfd, (const struct sockaddr *)&addr, len) == -1) {
448 /* sys_bind() may fail for different reasons, errno has to be
449 * checked to confirm that BPF program rejected it.
461 if (domain == AF_INET)
462 addr4->sin_port = htons(port_retry);
464 addr6->sin6_port = htons(port_retry);
465 if (bind(sockfd, (const struct sockaddr *)&addr, len) == -1) {
480 static int run_test_case(int cgfd, const struct sock_test *test)
486 printf("Test case: %s .. ", test->descr);
487 progfd = load_sock_prog(test->insns, test->expected_attach_type);
489 if (test->result == LOAD_REJECT)
495 if (attach_sock_prog(cgfd, progfd, test->attach_type) < 0) {
496 if (test->result == ATTACH_REJECT)
502 res = bind_sock(test->domain, test->type, test->ip, test->port,
504 if (res > 0 && test->result == res)
510 /* Detaching w/o checking return code: best effort attempt. */
512 bpf_prog_detach(cgfd, test->attach_type);
514 printf("[%s]\n", err ? "FAIL" : "PASS");
518 static int run_tests(int cgfd)
524 for (i = 0; i < ARRAY_SIZE(tests); ++i) {
525 if (run_test_case(cgfd, &tests[i]))
530 printf("Summary: %d PASSED, %d FAILED\n", passes, fails);
531 return fails ? -1 : 0;
534 int main(int argc, char **argv)
539 cgfd = cgroup_setup_and_join(CG_PATH);
543 /* Use libbpf 1.0 API mode */
544 libbpf_set_strict_mode(LIBBPF_STRICT_ALL);
554 cleanup_cgroup_environment();