linux/tools/testing/selftests/bpf/test_sock.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2// Copyright (c) 2018 Facebook
   3
   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_rlimit.h"
  18#include "bpf_util.h"
  19
  20#define CG_PATH         "/foo"
  21#define MAX_INSNS       512
  22
  23char bpf_log_buf[BPF_LOG_BUF_SIZE];
  24static bool verbose = false;
  25
  26struct sock_test {
  27        const char *descr;
  28        /* BPF prog properties */
  29        struct bpf_insn insns[MAX_INSNS];
  30        enum bpf_attach_type expected_attach_type;
  31        enum bpf_attach_type attach_type;
  32        /* Socket properties */
  33        int domain;
  34        int type;
  35        /* Endpoint to bind() to */
  36        const char *ip;
  37        unsigned short port;
  38        /* Expected test result */
  39        enum {
  40                LOAD_REJECT,
  41                ATTACH_REJECT,
  42                BIND_REJECT,
  43                SUCCESS,
  44        } result;
  45};
  46
  47static struct sock_test tests[] = {
  48        {
  49                "bind4 load with invalid access: src_ip6",
  50                .insns = {
  51                        BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
  52                        BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
  53                                    offsetof(struct bpf_sock, src_ip6[0])),
  54                        BPF_MOV64_IMM(BPF_REG_0, 1),
  55                        BPF_EXIT_INSN(),
  56                },
  57                BPF_CGROUP_INET4_POST_BIND,
  58                BPF_CGROUP_INET4_POST_BIND,
  59                0,
  60                0,
  61                NULL,
  62                0,
  63                LOAD_REJECT,
  64        },
  65        {
  66                "bind4 load with invalid access: mark",
  67                .insns = {
  68                        BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
  69                        BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
  70                                    offsetof(struct bpf_sock, mark)),
  71                        BPF_MOV64_IMM(BPF_REG_0, 1),
  72                        BPF_EXIT_INSN(),
  73                },
  74                BPF_CGROUP_INET4_POST_BIND,
  75                BPF_CGROUP_INET4_POST_BIND,
  76                0,
  77                0,
  78                NULL,
  79                0,
  80                LOAD_REJECT,
  81        },
  82        {
  83                "bind6 load with invalid access: src_ip4",
  84                .insns = {
  85                        BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
  86                        BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
  87                                    offsetof(struct bpf_sock, src_ip4)),
  88                        BPF_MOV64_IMM(BPF_REG_0, 1),
  89                        BPF_EXIT_INSN(),
  90                },
  91                BPF_CGROUP_INET6_POST_BIND,
  92                BPF_CGROUP_INET6_POST_BIND,
  93                0,
  94                0,
  95                NULL,
  96                0,
  97                LOAD_REJECT,
  98        },
  99        {
 100                "sock_create load with invalid access: src_port",
 101                .insns = {
 102                        BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
 103                        BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
 104                                    offsetof(struct bpf_sock, src_port)),
 105                        BPF_MOV64_IMM(BPF_REG_0, 1),
 106                        BPF_EXIT_INSN(),
 107                },
 108                BPF_CGROUP_INET_SOCK_CREATE,
 109                BPF_CGROUP_INET_SOCK_CREATE,
 110                0,
 111                0,
 112                NULL,
 113                0,
 114                LOAD_REJECT,
 115        },
 116        {
 117                "sock_create load w/o expected_attach_type (compat mode)",
 118                .insns = {
 119                        BPF_MOV64_IMM(BPF_REG_0, 1),
 120                        BPF_EXIT_INSN(),
 121                },
 122                0,
 123                BPF_CGROUP_INET_SOCK_CREATE,
 124                AF_INET,
 125                SOCK_STREAM,
 126                "127.0.0.1",
 127                8097,
 128                SUCCESS,
 129        },
 130        {
 131                "sock_create load w/ expected_attach_type",
 132                .insns = {
 133                        BPF_MOV64_IMM(BPF_REG_0, 1),
 134                        BPF_EXIT_INSN(),
 135                },
 136                BPF_CGROUP_INET_SOCK_CREATE,
 137                BPF_CGROUP_INET_SOCK_CREATE,
 138                AF_INET,
 139                SOCK_STREAM,
 140                "127.0.0.1",
 141                8097,
 142                SUCCESS,
 143        },
 144        {
 145                "attach type mismatch bind4 vs bind6",
 146                .insns = {
 147                        BPF_MOV64_IMM(BPF_REG_0, 1),
 148                        BPF_EXIT_INSN(),
 149                },
 150                BPF_CGROUP_INET4_POST_BIND,
 151                BPF_CGROUP_INET6_POST_BIND,
 152                0,
 153                0,
 154                NULL,
 155                0,
 156                ATTACH_REJECT,
 157        },
 158        {
 159                "attach type mismatch bind6 vs bind4",
 160                .insns = {
 161                        BPF_MOV64_IMM(BPF_REG_0, 1),
 162                        BPF_EXIT_INSN(),
 163                },
 164                BPF_CGROUP_INET6_POST_BIND,
 165                BPF_CGROUP_INET4_POST_BIND,
 166                0,
 167                0,
 168                NULL,
 169                0,
 170                ATTACH_REJECT,
 171        },
 172        {
 173                "attach type mismatch default vs bind4",
 174                .insns = {
 175                        BPF_MOV64_IMM(BPF_REG_0, 1),
 176                        BPF_EXIT_INSN(),
 177                },
 178                0,
 179                BPF_CGROUP_INET4_POST_BIND,
 180                0,
 181                0,
 182                NULL,
 183                0,
 184                ATTACH_REJECT,
 185        },
 186        {
 187                "attach type mismatch bind6 vs sock_create",
 188                .insns = {
 189                        BPF_MOV64_IMM(BPF_REG_0, 1),
 190                        BPF_EXIT_INSN(),
 191                },
 192                BPF_CGROUP_INET6_POST_BIND,
 193                BPF_CGROUP_INET_SOCK_CREATE,
 194                0,
 195                0,
 196                NULL,
 197                0,
 198                ATTACH_REJECT,
 199        },
 200        {
 201                "bind4 reject all",
 202                .insns = {
 203                        BPF_MOV64_IMM(BPF_REG_0, 0),
 204                        BPF_EXIT_INSN(),
 205                },
 206                BPF_CGROUP_INET4_POST_BIND,
 207                BPF_CGROUP_INET4_POST_BIND,
 208                AF_INET,
 209                SOCK_STREAM,
 210                "0.0.0.0",
 211                0,
 212                BIND_REJECT,
 213        },
 214        {
 215                "bind6 reject all",
 216                .insns = {
 217                        BPF_MOV64_IMM(BPF_REG_0, 0),
 218                        BPF_EXIT_INSN(),
 219                },
 220                BPF_CGROUP_INET6_POST_BIND,
 221                BPF_CGROUP_INET6_POST_BIND,
 222                AF_INET6,
 223                SOCK_STREAM,
 224                "::",
 225                0,
 226                BIND_REJECT,
 227        },
 228        {
 229                "bind6 deny specific IP & port",
 230                .insns = {
 231                        BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
 232
 233                        /* if (ip == expected && port == expected) */
 234                        BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
 235                                    offsetof(struct bpf_sock, src_ip6[3])),
 236                        BPF_JMP_IMM(BPF_JNE, BPF_REG_7,
 237                                    __bpf_constant_ntohl(0x00000001), 4),
 238                        BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
 239                                    offsetof(struct bpf_sock, src_port)),
 240                        BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 0x2001, 2),
 241
 242                        /* return DENY; */
 243                        BPF_MOV64_IMM(BPF_REG_0, 0),
 244                        BPF_JMP_A(1),
 245
 246                        /* else return ALLOW; */
 247                        BPF_MOV64_IMM(BPF_REG_0, 1),
 248                        BPF_EXIT_INSN(),
 249                },
 250                BPF_CGROUP_INET6_POST_BIND,
 251                BPF_CGROUP_INET6_POST_BIND,
 252                AF_INET6,
 253                SOCK_STREAM,
 254                "::1",
 255                8193,
 256                BIND_REJECT,
 257        },
 258        {
 259                "bind4 allow specific IP & port",
 260                .insns = {
 261                        BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
 262
 263                        /* if (ip == expected && port == expected) */
 264                        BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
 265                                    offsetof(struct bpf_sock, src_ip4)),
 266                        BPF_JMP_IMM(BPF_JNE, BPF_REG_7,
 267                                    __bpf_constant_ntohl(0x7F000001), 4),
 268                        BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
 269                                    offsetof(struct bpf_sock, src_port)),
 270                        BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 0x1002, 2),
 271
 272                        /* return ALLOW; */
 273                        BPF_MOV64_IMM(BPF_REG_0, 1),
 274                        BPF_JMP_A(1),
 275
 276                        /* else return DENY; */
 277                        BPF_MOV64_IMM(BPF_REG_0, 0),
 278                        BPF_EXIT_INSN(),
 279                },
 280                BPF_CGROUP_INET4_POST_BIND,
 281                BPF_CGROUP_INET4_POST_BIND,
 282                AF_INET,
 283                SOCK_STREAM,
 284                "127.0.0.1",
 285                4098,
 286                SUCCESS,
 287        },
 288        {
 289                "bind4 allow all",
 290                .insns = {
 291                        BPF_MOV64_IMM(BPF_REG_0, 1),
 292                        BPF_EXIT_INSN(),
 293                },
 294                BPF_CGROUP_INET4_POST_BIND,
 295                BPF_CGROUP_INET4_POST_BIND,
 296                AF_INET,
 297                SOCK_STREAM,
 298                "0.0.0.0",
 299                0,
 300                SUCCESS,
 301        },
 302        {
 303                "bind6 allow all",
 304                .insns = {
 305                        BPF_MOV64_IMM(BPF_REG_0, 1),
 306                        BPF_EXIT_INSN(),
 307                },
 308                BPF_CGROUP_INET6_POST_BIND,
 309                BPF_CGROUP_INET6_POST_BIND,
 310                AF_INET6,
 311                SOCK_STREAM,
 312                "::",
 313                0,
 314                SUCCESS,
 315        },
 316};
 317
 318static size_t probe_prog_length(const struct bpf_insn *fp)
 319{
 320        size_t len;
 321
 322        for (len = MAX_INSNS - 1; len > 0; --len)
 323                if (fp[len].code != 0 || fp[len].imm != 0)
 324                        break;
 325        return len + 1;
 326}
 327
 328static int load_sock_prog(const struct bpf_insn *prog,
 329                          enum bpf_attach_type attach_type)
 330{
 331        struct bpf_load_program_attr attr;
 332        int ret;
 333
 334        memset(&attr, 0, sizeof(struct bpf_load_program_attr));
 335        attr.prog_type = BPF_PROG_TYPE_CGROUP_SOCK;
 336        attr.expected_attach_type = attach_type;
 337        attr.insns = prog;
 338        attr.insns_cnt = probe_prog_length(attr.insns);
 339        attr.license = "GPL";
 340        attr.log_level = 2;
 341
 342        ret = bpf_load_program_xattr(&attr, bpf_log_buf, BPF_LOG_BUF_SIZE);
 343        if (verbose && ret < 0)
 344                fprintf(stderr, "%s\n", bpf_log_buf);
 345
 346        return ret;
 347}
 348
 349static int attach_sock_prog(int cgfd, int progfd,
 350                            enum bpf_attach_type attach_type)
 351{
 352        return bpf_prog_attach(progfd, cgfd, attach_type, BPF_F_ALLOW_OVERRIDE);
 353}
 354
 355static int bind_sock(int domain, int type, const char *ip, unsigned short port)
 356{
 357        struct sockaddr_storage addr;
 358        struct sockaddr_in6 *addr6;
 359        struct sockaddr_in *addr4;
 360        int sockfd = -1;
 361        socklen_t len;
 362        int err = 0;
 363
 364        sockfd = socket(domain, type, 0);
 365        if (sockfd < 0)
 366                goto err;
 367
 368        memset(&addr, 0, sizeof(addr));
 369
 370        if (domain == AF_INET) {
 371                len = sizeof(struct sockaddr_in);
 372                addr4 = (struct sockaddr_in *)&addr;
 373                addr4->sin_family = domain;
 374                addr4->sin_port = htons(port);
 375                if (inet_pton(domain, ip, (void *)&addr4->sin_addr) != 1)
 376                        goto err;
 377        } else if (domain == AF_INET6) {
 378                len = sizeof(struct sockaddr_in6);
 379                addr6 = (struct sockaddr_in6 *)&addr;
 380                addr6->sin6_family = domain;
 381                addr6->sin6_port = htons(port);
 382                if (inet_pton(domain, ip, (void *)&addr6->sin6_addr) != 1)
 383                        goto err;
 384        } else {
 385                goto err;
 386        }
 387
 388        if (bind(sockfd, (const struct sockaddr *)&addr, len) == -1)
 389                goto err;
 390
 391        goto out;
 392err:
 393        err = -1;
 394out:
 395        close(sockfd);
 396        return err;
 397}
 398
 399static int run_test_case(int cgfd, const struct sock_test *test)
 400{
 401        int progfd = -1;
 402        int err = 0;
 403
 404        printf("Test case: %s .. ", test->descr);
 405        progfd = load_sock_prog(test->insns, test->expected_attach_type);
 406        if (progfd < 0) {
 407                if (test->result == LOAD_REJECT)
 408                        goto out;
 409                else
 410                        goto err;
 411        }
 412
 413        if (attach_sock_prog(cgfd, progfd, test->attach_type) == -1) {
 414                if (test->result == ATTACH_REJECT)
 415                        goto out;
 416                else
 417                        goto err;
 418        }
 419
 420        if (bind_sock(test->domain, test->type, test->ip, test->port) == -1) {
 421                /* sys_bind() may fail for different reasons, errno has to be
 422                 * checked to confirm that BPF program rejected it.
 423                 */
 424                if (test->result == BIND_REJECT && errno == EPERM)
 425                        goto out;
 426                else
 427                        goto err;
 428        }
 429
 430
 431        if (test->result != SUCCESS)
 432                goto err;
 433
 434        goto out;
 435err:
 436        err = -1;
 437out:
 438        /* Detaching w/o checking return code: best effort attempt. */
 439        if (progfd != -1)
 440                bpf_prog_detach(cgfd, test->attach_type);
 441        close(progfd);
 442        printf("[%s]\n", err ? "FAIL" : "PASS");
 443        return err;
 444}
 445
 446static int run_tests(int cgfd)
 447{
 448        int passes = 0;
 449        int fails = 0;
 450        int i;
 451
 452        for (i = 0; i < ARRAY_SIZE(tests); ++i) {
 453                if (run_test_case(cgfd, &tests[i]))
 454                        ++fails;
 455                else
 456                        ++passes;
 457        }
 458        printf("Summary: %d PASSED, %d FAILED\n", passes, fails);
 459        return fails ? -1 : 0;
 460}
 461
 462int main(int argc, char **argv)
 463{
 464        int cgfd = -1;
 465        int err = 0;
 466
 467        cgfd = cgroup_setup_and_join(CG_PATH);
 468        if (cgfd < 0)
 469                goto err;
 470
 471        if (run_tests(cgfd))
 472                goto err;
 473
 474        goto out;
 475err:
 476        err = -1;
 477out:
 478        close(cgfd);
 479        cleanup_cgroup_environment();
 480        return err;
 481}
 482