557 lines · c
1// SPDX-License-Identifier: GPL-2.02// Copyright (c) 2018 Facebook3 4#include <stdio.h>5#include <unistd.h>6 7#include <arpa/inet.h>8#include <sys/types.h>9#include <sys/socket.h>10 11#include <linux/filter.h>12 13#include <bpf/bpf.h>14 15#include "cgroup_helpers.h"16#include <bpf/bpf_endian.h>17#include "bpf_util.h"18 19#define CG_PATH "/foo"20#define MAX_INSNS 51221 22char bpf_log_buf[BPF_LOG_BUF_SIZE];23static bool verbose = false;24 25struct sock_test {26 const char *descr;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 */32 int domain;33 int type;34 /* Endpoint to bind() to */35 const char *ip;36 unsigned short port;37 unsigned short port_retry;38 /* Expected test result */39 enum {40 LOAD_REJECT,41 ATTACH_REJECT,42 BIND_REJECT,43 SUCCESS,44 RETRY_SUCCESS,45 RETRY_REJECT46 } result;47};48 49static struct sock_test tests[] = {50 {51 .descr = "bind4 load with invalid access: src_ip6",52 .insns = {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),57 BPF_EXIT_INSN(),58 },59 .expected_attach_type = BPF_CGROUP_INET4_POST_BIND,60 .attach_type = BPF_CGROUP_INET4_POST_BIND,61 .result = LOAD_REJECT,62 },63 {64 .descr = "bind4 load with invalid access: mark",65 .insns = {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),70 BPF_EXIT_INSN(),71 },72 .expected_attach_type = BPF_CGROUP_INET4_POST_BIND,73 .attach_type = BPF_CGROUP_INET4_POST_BIND,74 .result = LOAD_REJECT,75 },76 {77 .descr = "bind6 load with invalid access: src_ip4",78 .insns = {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),83 BPF_EXIT_INSN(),84 },85 .expected_attach_type = BPF_CGROUP_INET6_POST_BIND,86 .attach_type = BPF_CGROUP_INET6_POST_BIND,87 .result = LOAD_REJECT,88 },89 {90 .descr = "sock_create load with invalid access: src_port",91 .insns = {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),96 BPF_EXIT_INSN(),97 },98 .expected_attach_type = BPF_CGROUP_INET_SOCK_CREATE,99 .attach_type = BPF_CGROUP_INET_SOCK_CREATE,100 .result = LOAD_REJECT,101 },102 {103 .descr = "sock_create load w/o expected_attach_type (compat mode)",104 .insns = {105 BPF_MOV64_IMM(BPF_REG_0, 1),106 BPF_EXIT_INSN(),107 },108 .expected_attach_type = 0,109 .attach_type = BPF_CGROUP_INET_SOCK_CREATE,110 .domain = AF_INET,111 .type = SOCK_STREAM,112 .ip = "127.0.0.1",113 .port = 8097,114 .result = SUCCESS,115 },116 {117 .descr = "sock_create load w/ expected_attach_type",118 .insns = {119 BPF_MOV64_IMM(BPF_REG_0, 1),120 BPF_EXIT_INSN(),121 },122 .expected_attach_type = BPF_CGROUP_INET_SOCK_CREATE,123 .attach_type = BPF_CGROUP_INET_SOCK_CREATE,124 .domain = AF_INET,125 .type = SOCK_STREAM,126 .ip = "127.0.0.1",127 .port = 8097,128 .result = SUCCESS,129 },130 {131 .descr = "attach type mismatch bind4 vs bind6",132 .insns = {133 BPF_MOV64_IMM(BPF_REG_0, 1),134 BPF_EXIT_INSN(),135 },136 .expected_attach_type = BPF_CGROUP_INET4_POST_BIND,137 .attach_type = BPF_CGROUP_INET6_POST_BIND,138 .result = ATTACH_REJECT,139 },140 {141 .descr = "attach type mismatch bind6 vs bind4",142 .insns = {143 BPF_MOV64_IMM(BPF_REG_0, 1),144 BPF_EXIT_INSN(),145 },146 .expected_attach_type = BPF_CGROUP_INET6_POST_BIND,147 .attach_type = BPF_CGROUP_INET4_POST_BIND,148 .result = ATTACH_REJECT,149 },150 {151 .descr = "attach type mismatch default vs bind4",152 .insns = {153 BPF_MOV64_IMM(BPF_REG_0, 1),154 BPF_EXIT_INSN(),155 },156 .expected_attach_type = 0,157 .attach_type = BPF_CGROUP_INET4_POST_BIND,158 .result = ATTACH_REJECT,159 },160 {161 .descr = "attach type mismatch bind6 vs sock_create",162 .insns = {163 BPF_MOV64_IMM(BPF_REG_0, 1),164 BPF_EXIT_INSN(),165 },166 .expected_attach_type = BPF_CGROUP_INET6_POST_BIND,167 .attach_type = BPF_CGROUP_INET_SOCK_CREATE,168 .result = ATTACH_REJECT,169 },170 {171 .descr = "bind4 reject all",172 .insns = {173 BPF_MOV64_IMM(BPF_REG_0, 0),174 BPF_EXIT_INSN(),175 },176 .expected_attach_type = BPF_CGROUP_INET4_POST_BIND,177 .attach_type = BPF_CGROUP_INET4_POST_BIND,178 .domain = AF_INET,179 .type = SOCK_STREAM,180 .ip = "0.0.0.0",181 .result = BIND_REJECT,182 },183 {184 .descr = "bind6 reject all",185 .insns = {186 BPF_MOV64_IMM(BPF_REG_0, 0),187 BPF_EXIT_INSN(),188 },189 .expected_attach_type = BPF_CGROUP_INET6_POST_BIND,190 .attach_type = BPF_CGROUP_INET6_POST_BIND,191 .domain = AF_INET6,192 .type = SOCK_STREAM,193 .ip = "::",194 .result = BIND_REJECT,195 },196 {197 .descr = "bind6 deny specific IP & port",198 .insns = {199 BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),200 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),209 210 /* return DENY; */211 BPF_MOV64_IMM(BPF_REG_0, 0),212 BPF_JMP_A(1),213 214 /* else return ALLOW; */215 BPF_MOV64_IMM(BPF_REG_0, 1),216 BPF_EXIT_INSN(),217 },218 .expected_attach_type = BPF_CGROUP_INET6_POST_BIND,219 .attach_type = BPF_CGROUP_INET6_POST_BIND,220 .domain = AF_INET6,221 .type = SOCK_STREAM,222 .ip = "::1",223 .port = 8193,224 .result = BIND_REJECT,225 },226 {227 .descr = "bind4 allow specific IP & port",228 .insns = {229 BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),230 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),239 240 /* return ALLOW; */241 BPF_MOV64_IMM(BPF_REG_0, 1),242 BPF_JMP_A(1),243 244 /* else return DENY; */245 BPF_MOV64_IMM(BPF_REG_0, 0),246 BPF_EXIT_INSN(),247 },248 .expected_attach_type = BPF_CGROUP_INET4_POST_BIND,249 .attach_type = BPF_CGROUP_INET4_POST_BIND,250 .domain = AF_INET,251 .type = SOCK_STREAM,252 .ip = "127.0.0.1",253 .port = 4098,254 .result = SUCCESS,255 },256 {257 .descr = "bind4 deny specific IP & port of TCP, and retry",258 .insns = {259 BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),260 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),269 270 /* return DENY; */271 BPF_MOV64_IMM(BPF_REG_0, 0),272 BPF_JMP_A(1),273 274 /* else return ALLOW; */275 BPF_MOV64_IMM(BPF_REG_0, 1),276 BPF_EXIT_INSN(),277 },278 .expected_attach_type = BPF_CGROUP_INET4_POST_BIND,279 .attach_type = BPF_CGROUP_INET4_POST_BIND,280 .domain = AF_INET,281 .type = SOCK_STREAM,282 .ip = "127.0.0.1",283 .port = 4098,284 .port_retry = 5000,285 .result = RETRY_SUCCESS,286 },287 {288 .descr = "bind4 deny specific IP & port of UDP, and retry",289 .insns = {290 BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),291 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),300 301 /* return DENY; */302 BPF_MOV64_IMM(BPF_REG_0, 0),303 BPF_JMP_A(1),304 305 /* else return ALLOW; */306 BPF_MOV64_IMM(BPF_REG_0, 1),307 BPF_EXIT_INSN(),308 },309 .expected_attach_type = BPF_CGROUP_INET4_POST_BIND,310 .attach_type = BPF_CGROUP_INET4_POST_BIND,311 .domain = AF_INET,312 .type = SOCK_DGRAM,313 .ip = "127.0.0.1",314 .port = 4098,315 .port_retry = 5000,316 .result = RETRY_SUCCESS,317 },318 {319 .descr = "bind6 deny specific IP & port, and retry",320 .insns = {321 BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),322 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),331 332 /* return DENY; */333 BPF_MOV64_IMM(BPF_REG_0, 0),334 BPF_JMP_A(1),335 336 /* else return ALLOW; */337 BPF_MOV64_IMM(BPF_REG_0, 1),338 BPF_EXIT_INSN(),339 },340 .expected_attach_type = BPF_CGROUP_INET6_POST_BIND,341 .attach_type = BPF_CGROUP_INET6_POST_BIND,342 .domain = AF_INET6,343 .type = SOCK_STREAM,344 .ip = "::1",345 .port = 8193,346 .port_retry = 9000,347 .result = RETRY_SUCCESS,348 },349 {350 .descr = "bind4 allow all",351 .insns = {352 BPF_MOV64_IMM(BPF_REG_0, 1),353 BPF_EXIT_INSN(),354 },355 .expected_attach_type = BPF_CGROUP_INET4_POST_BIND,356 .attach_type = BPF_CGROUP_INET4_POST_BIND,357 .domain = AF_INET,358 .type = SOCK_STREAM,359 .ip = "0.0.0.0",360 .result = SUCCESS,361 },362 {363 .descr = "bind6 allow all",364 .insns = {365 BPF_MOV64_IMM(BPF_REG_0, 1),366 BPF_EXIT_INSN(),367 },368 .expected_attach_type = BPF_CGROUP_INET6_POST_BIND,369 .attach_type = BPF_CGROUP_INET6_POST_BIND,370 .domain = AF_INET6,371 .type = SOCK_STREAM,372 .ip = "::",373 .result = SUCCESS,374 },375};376 377static size_t probe_prog_length(const struct bpf_insn *fp)378{379 size_t len;380 381 for (len = MAX_INSNS - 1; len > 0; --len)382 if (fp[len].code != 0 || fp[len].imm != 0)383 break;384 return len + 1;385}386 387static int load_sock_prog(const struct bpf_insn *prog,388 enum bpf_attach_type attach_type)389{390 LIBBPF_OPTS(bpf_prog_load_opts, opts);391 int ret, insn_cnt;392 393 insn_cnt = probe_prog_length(prog);394 395 opts.expected_attach_type = attach_type;396 opts.log_buf = bpf_log_buf;397 opts.log_size = BPF_LOG_BUF_SIZE;398 opts.log_level = 2;399 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);403 404 return ret;405}406 407static int attach_sock_prog(int cgfd, int progfd,408 enum bpf_attach_type attach_type)409{410 return bpf_prog_attach(progfd, cgfd, attach_type, BPF_F_ALLOW_OVERRIDE);411}412 413static int bind_sock(int domain, int type, const char *ip,414 unsigned short port, unsigned short port_retry)415{416 struct sockaddr_storage addr;417 struct sockaddr_in6 *addr6;418 struct sockaddr_in *addr4;419 int sockfd = -1;420 socklen_t len;421 int res = SUCCESS;422 423 sockfd = socket(domain, type, 0);424 if (sockfd < 0)425 goto err;426 427 memset(&addr, 0, sizeof(addr));428 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)435 goto err;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)442 goto err;443 } else {444 goto err;445 }446 447 if (bind(sockfd, (const struct sockaddr *)&addr, len) == -1) {448 /* sys_bind() may fail for different reasons, errno has to be449 * checked to confirm that BPF program rejected it.450 */451 if (errno != EPERM)452 goto err;453 if (port_retry)454 goto retry;455 res = BIND_REJECT;456 goto out;457 }458 459 goto out;460retry:461 if (domain == AF_INET)462 addr4->sin_port = htons(port_retry);463 else464 addr6->sin6_port = htons(port_retry);465 if (bind(sockfd, (const struct sockaddr *)&addr, len) == -1) {466 if (errno != EPERM)467 goto err;468 res = RETRY_REJECT;469 } else {470 res = RETRY_SUCCESS;471 }472 goto out;473err:474 res = -1;475out:476 close(sockfd);477 return res;478}479 480static int run_test_case(int cgfd, const struct sock_test *test)481{482 int progfd = -1;483 int err = 0;484 int res;485 486 printf("Test case: %s .. ", test->descr);487 progfd = load_sock_prog(test->insns, test->expected_attach_type);488 if (progfd < 0) {489 if (test->result == LOAD_REJECT)490 goto out;491 else492 goto err;493 }494 495 if (attach_sock_prog(cgfd, progfd, test->attach_type) < 0) {496 if (test->result == ATTACH_REJECT)497 goto out;498 else499 goto err;500 }501 502 res = bind_sock(test->domain, test->type, test->ip, test->port,503 test->port_retry);504 if (res > 0 && test->result == res)505 goto out;506 507err:508 err = -1;509out:510 /* Detaching w/o checking return code: best effort attempt. */511 if (progfd != -1)512 bpf_prog_detach(cgfd, test->attach_type);513 close(progfd);514 printf("[%s]\n", err ? "FAIL" : "PASS");515 return err;516}517 518static int run_tests(int cgfd)519{520 int passes = 0;521 int fails = 0;522 int i;523 524 for (i = 0; i < ARRAY_SIZE(tests); ++i) {525 if (run_test_case(cgfd, &tests[i]))526 ++fails;527 else528 ++passes;529 }530 printf("Summary: %d PASSED, %d FAILED\n", passes, fails);531 return fails ? -1 : 0;532}533 534int main(int argc, char **argv)535{536 int cgfd = -1;537 int err = 0;538 539 cgfd = cgroup_setup_and_join(CG_PATH);540 if (cgfd < 0)541 goto err;542 543 /* Use libbpf 1.0 API mode */544 libbpf_set_strict_mode(LIBBPF_STRICT_ALL);545 546 if (run_tests(cgfd))547 goto err;548 549 goto out;550err:551 err = -1;552out:553 close(cgfd);554 cleanup_cgroup_environment();555 return err;556}557