1 // SPDX-License-Identifier: GPL-2.0 2 // Copyright (c) 2017-2018 Covalent IO, Inc. http://covalent.io 3 #include <stdio.h> 4 #include <stdlib.h> 5 #include <sys/socket.h> 6 #include <sys/ioctl.h> 7 #include <sys/select.h> 8 #include <netinet/in.h> 9 #include <arpa/inet.h> 10 #include <unistd.h> 11 #include <string.h> 12 #include <errno.h> 13 #include <sys/ioctl.h> 14 #include <stdbool.h> 15 #include <signal.h> 16 #include <fcntl.h> 17 #include <sys/wait.h> 18 #include <time.h> 19 #include <sched.h> 20 21 #include <sys/time.h> 22 #include <sys/resource.h> 23 #include <sys/types.h> 24 #include <sys/sendfile.h> 25 26 #include <linux/netlink.h> 27 #include <linux/socket.h> 28 #include <linux/sock_diag.h> 29 #include <linux/bpf.h> 30 #include <linux/if_link.h> 31 #include <assert.h> 32 #include <libgen.h> 33 34 #include <getopt.h> 35 36 #include <bpf/bpf.h> 37 #include <bpf/libbpf.h> 38 39 #include "bpf_util.h" 40 #include "bpf_rlimit.h" 41 #include "cgroup_helpers.h" 42 43 int running; 44 static void running_handler(int a); 45 46 /* randomly selected ports for testing on lo */ 47 #define S1_PORT 10000 48 #define S2_PORT 10001 49 50 #define BPF_SOCKMAP_FILENAME "test_sockmap_kern.o" 51 #define BPF_SOCKHASH_FILENAME "test_sockhash_kern.o" 52 #define CG_PATH "/sockmap" 53 54 /* global sockets */ 55 int s1, s2, c1, c2, p1, p2; 56 int test_cnt; 57 int passed; 58 int failed; 59 int map_fd[8]; 60 struct bpf_map *maps[8]; 61 int prog_fd[11]; 62 63 int txmsg_pass; 64 int txmsg_noisy; 65 int txmsg_redir; 66 int txmsg_redir_noisy; 67 int txmsg_drop; 68 int txmsg_apply; 69 int txmsg_cork; 70 int txmsg_start; 71 int txmsg_end; 72 int txmsg_ingress; 73 int txmsg_skb; 74 75 static const struct option long_options[] = { 76 {"help", no_argument, NULL, 'h' }, 77 {"cgroup", required_argument, NULL, 'c' }, 78 {"rate", required_argument, NULL, 'r' }, 79 {"verbose", no_argument, NULL, 'v' }, 80 {"iov_count", required_argument, NULL, 'i' }, 81 {"length", required_argument, NULL, 'l' }, 82 {"test", required_argument, NULL, 't' }, 83 {"data_test", no_argument, NULL, 'd' }, 84 {"txmsg", no_argument, &txmsg_pass, 1 }, 85 {"txmsg_noisy", no_argument, &txmsg_noisy, 1 }, 86 {"txmsg_redir", no_argument, &txmsg_redir, 1 }, 87 {"txmsg_redir_noisy", no_argument, &txmsg_redir_noisy, 1}, 88 {"txmsg_drop", no_argument, &txmsg_drop, 1 }, 89 {"txmsg_apply", required_argument, NULL, 'a'}, 90 {"txmsg_cork", required_argument, NULL, 'k'}, 91 {"txmsg_start", required_argument, NULL, 's'}, 92 {"txmsg_end", required_argument, NULL, 'e'}, 93 {"txmsg_ingress", no_argument, &txmsg_ingress, 1 }, 94 {"txmsg_skb", no_argument, &txmsg_skb, 1 }, 95 {0, 0, NULL, 0 } 96 }; 97 98 static void usage(char *argv[]) 99 { 100 int i; 101 102 printf(" Usage: %s --cgroup <cgroup_path>\n", argv[0]); 103 printf(" options:\n"); 104 for (i = 0; long_options[i].name != 0; i++) { 105 printf(" --%-12s", long_options[i].name); 106 if (long_options[i].flag != NULL) 107 printf(" flag (internal value:%d)\n", 108 *long_options[i].flag); 109 else 110 printf(" -%c\n", long_options[i].val); 111 } 112 printf("\n"); 113 } 114 115 static int sockmap_init_sockets(int verbose) 116 { 117 int i, err, one = 1; 118 struct sockaddr_in addr; 119 int *fds[4] = {&s1, &s2, &c1, &c2}; 120 121 s1 = s2 = p1 = p2 = c1 = c2 = 0; 122 123 /* Init sockets */ 124 for (i = 0; i < 4; i++) { 125 *fds[i] = socket(AF_INET, SOCK_STREAM, 0); 126 if (*fds[i] < 0) { 127 perror("socket s1 failed()"); 128 return errno; 129 } 130 } 131 132 /* Allow reuse */ 133 for (i = 0; i < 2; i++) { 134 err = setsockopt(*fds[i], SOL_SOCKET, SO_REUSEADDR, 135 (char *)&one, sizeof(one)); 136 if (err) { 137 perror("setsockopt failed()"); 138 return errno; 139 } 140 } 141 142 /* Non-blocking sockets */ 143 for (i = 0; i < 2; i++) { 144 err = ioctl(*fds[i], FIONBIO, (char *)&one); 145 if (err < 0) { 146 perror("ioctl s1 failed()"); 147 return errno; 148 } 149 } 150 151 /* Bind server sockets */ 152 memset(&addr, 0, sizeof(struct sockaddr_in)); 153 addr.sin_family = AF_INET; 154 addr.sin_addr.s_addr = inet_addr("127.0.0.1"); 155 156 addr.sin_port = htons(S1_PORT); 157 err = bind(s1, (struct sockaddr *)&addr, sizeof(addr)); 158 if (err < 0) { 159 perror("bind s1 failed()\n"); 160 return errno; 161 } 162 163 addr.sin_port = htons(S2_PORT); 164 err = bind(s2, (struct sockaddr *)&addr, sizeof(addr)); 165 if (err < 0) { 166 perror("bind s2 failed()\n"); 167 return errno; 168 } 169 170 /* Listen server sockets */ 171 addr.sin_port = htons(S1_PORT); 172 err = listen(s1, 32); 173 if (err < 0) { 174 perror("listen s1 failed()\n"); 175 return errno; 176 } 177 178 addr.sin_port = htons(S2_PORT); 179 err = listen(s2, 32); 180 if (err < 0) { 181 perror("listen s1 failed()\n"); 182 return errno; 183 } 184 185 /* Initiate Connect */ 186 addr.sin_port = htons(S1_PORT); 187 err = connect(c1, (struct sockaddr *)&addr, sizeof(addr)); 188 if (err < 0 && errno != EINPROGRESS) { 189 perror("connect c1 failed()\n"); 190 return errno; 191 } 192 193 addr.sin_port = htons(S2_PORT); 194 err = connect(c2, (struct sockaddr *)&addr, sizeof(addr)); 195 if (err < 0 && errno != EINPROGRESS) { 196 perror("connect c2 failed()\n"); 197 return errno; 198 } else if (err < 0) { 199 err = 0; 200 } 201 202 /* Accept Connecrtions */ 203 p1 = accept(s1, NULL, NULL); 204 if (p1 < 0) { 205 perror("accept s1 failed()\n"); 206 return errno; 207 } 208 209 p2 = accept(s2, NULL, NULL); 210 if (p2 < 0) { 211 perror("accept s1 failed()\n"); 212 return errno; 213 } 214 215 if (verbose) { 216 printf("connected sockets: c1 <-> p1, c2 <-> p2\n"); 217 printf("cgroups binding: c1(%i) <-> s1(%i) - - - c2(%i) <-> s2(%i)\n", 218 c1, s1, c2, s2); 219 } 220 return 0; 221 } 222 223 struct msg_stats { 224 size_t bytes_sent; 225 size_t bytes_recvd; 226 struct timespec start; 227 struct timespec end; 228 }; 229 230 struct sockmap_options { 231 int verbose; 232 bool base; 233 bool sendpage; 234 bool data_test; 235 bool drop_expected; 236 int iov_count; 237 int iov_length; 238 int rate; 239 }; 240 241 static int msg_loop_sendpage(int fd, int iov_length, int cnt, 242 struct msg_stats *s, 243 struct sockmap_options *opt) 244 { 245 bool drop = opt->drop_expected; 246 unsigned char k = 0; 247 FILE *file; 248 int i, fp; 249 250 file = fopen(".sendpage_tst.tmp", "w+"); 251 for (i = 0; i < iov_length * cnt; i++, k++) 252 fwrite(&k, sizeof(char), 1, file); 253 fflush(file); 254 fseek(file, 0, SEEK_SET); 255 fclose(file); 256 257 fp = open(".sendpage_tst.tmp", O_RDONLY); 258 clock_gettime(CLOCK_MONOTONIC, &s->start); 259 for (i = 0; i < cnt; i++) { 260 int sent = sendfile(fd, fp, NULL, iov_length); 261 262 if (!drop && sent < 0) { 263 perror("send loop error:"); 264 close(fp); 265 return sent; 266 } else if (drop && sent >= 0) { 267 printf("sendpage loop error expected: %i\n", sent); 268 close(fp); 269 return -EIO; 270 } 271 272 if (sent > 0) 273 s->bytes_sent += sent; 274 } 275 clock_gettime(CLOCK_MONOTONIC, &s->end); 276 close(fp); 277 return 0; 278 } 279 280 static int msg_loop(int fd, int iov_count, int iov_length, int cnt, 281 struct msg_stats *s, bool tx, 282 struct sockmap_options *opt) 283 { 284 struct msghdr msg = {0}; 285 int err, i, flags = MSG_NOSIGNAL; 286 struct iovec *iov; 287 unsigned char k; 288 bool data_test = opt->data_test; 289 bool drop = opt->drop_expected; 290 291 iov = calloc(iov_count, sizeof(struct iovec)); 292 if (!iov) 293 return errno; 294 295 k = 0; 296 for (i = 0; i < iov_count; i++) { 297 unsigned char *d = calloc(iov_length, sizeof(char)); 298 299 if (!d) { 300 fprintf(stderr, "iov_count %i/%i OOM\n", i, iov_count); 301 goto out_errno; 302 } 303 iov[i].iov_base = d; 304 iov[i].iov_len = iov_length; 305 306 if (data_test && tx) { 307 int j; 308 309 for (j = 0; j < iov_length; j++) 310 d[j] = k++; 311 } 312 } 313 314 msg.msg_iov = iov; 315 msg.msg_iovlen = iov_count; 316 k = 0; 317 318 if (tx) { 319 clock_gettime(CLOCK_MONOTONIC, &s->start); 320 for (i = 0; i < cnt; i++) { 321 int sent = sendmsg(fd, &msg, flags); 322 323 if (!drop && sent < 0) { 324 perror("send loop error:"); 325 goto out_errno; 326 } else if (drop && sent >= 0) { 327 printf("send loop error expected: %i\n", sent); 328 errno = -EIO; 329 goto out_errno; 330 } 331 if (sent > 0) 332 s->bytes_sent += sent; 333 } 334 clock_gettime(CLOCK_MONOTONIC, &s->end); 335 } else { 336 int slct, recv, max_fd = fd; 337 int fd_flags = O_NONBLOCK; 338 struct timeval timeout; 339 float total_bytes; 340 int bytes_cnt = 0; 341 int chunk_sz; 342 fd_set w; 343 344 if (opt->sendpage) 345 chunk_sz = iov_length * cnt; 346 else 347 chunk_sz = iov_length * iov_count; 348 349 fcntl(fd, fd_flags); 350 total_bytes = (float)iov_count * (float)iov_length * (float)cnt; 351 err = clock_gettime(CLOCK_MONOTONIC, &s->start); 352 if (err < 0) 353 perror("recv start time: "); 354 while (s->bytes_recvd < total_bytes) { 355 if (txmsg_cork) { 356 timeout.tv_sec = 0; 357 timeout.tv_usec = 300000; 358 } else { 359 timeout.tv_sec = 1; 360 timeout.tv_usec = 0; 361 } 362 363 /* FD sets */ 364 FD_ZERO(&w); 365 FD_SET(fd, &w); 366 367 slct = select(max_fd + 1, &w, NULL, NULL, &timeout); 368 if (slct == -1) { 369 perror("select()"); 370 clock_gettime(CLOCK_MONOTONIC, &s->end); 371 goto out_errno; 372 } else if (!slct) { 373 if (opt->verbose) 374 fprintf(stderr, "unexpected timeout\n"); 375 errno = -EIO; 376 clock_gettime(CLOCK_MONOTONIC, &s->end); 377 goto out_errno; 378 } 379 380 recv = recvmsg(fd, &msg, flags); 381 if (recv < 0) { 382 if (errno != EWOULDBLOCK) { 383 clock_gettime(CLOCK_MONOTONIC, &s->end); 384 perror("recv failed()\n"); 385 goto out_errno; 386 } 387 } 388 389 s->bytes_recvd += recv; 390 391 if (data_test) { 392 int j; 393 394 for (i = 0; i < msg.msg_iovlen; i++) { 395 unsigned char *d = iov[i].iov_base; 396 397 for (j = 0; 398 j < iov[i].iov_len && recv; j++) { 399 if (d[j] != k++) { 400 errno = -EIO; 401 fprintf(stderr, 402 "detected data corruption @iov[%i]:%i %02x != %02x, %02x ?= %02x\n", 403 i, j, d[j], k - 1, d[j+1], k); 404 goto out_errno; 405 } 406 bytes_cnt++; 407 if (bytes_cnt == chunk_sz) { 408 k = 0; 409 bytes_cnt = 0; 410 } 411 recv--; 412 } 413 } 414 } 415 } 416 clock_gettime(CLOCK_MONOTONIC, &s->end); 417 } 418 419 for (i = 0; i < iov_count; i++) 420 free(iov[i].iov_base); 421 free(iov); 422 return 0; 423 out_errno: 424 for (i = 0; i < iov_count; i++) 425 free(iov[i].iov_base); 426 free(iov); 427 return errno; 428 } 429 430 static float giga = 1000000000; 431 432 static inline float sentBps(struct msg_stats s) 433 { 434 return s.bytes_sent / (s.end.tv_sec - s.start.tv_sec); 435 } 436 437 static inline float recvdBps(struct msg_stats s) 438 { 439 return s.bytes_recvd / (s.end.tv_sec - s.start.tv_sec); 440 } 441 442 static int sendmsg_test(struct sockmap_options *opt) 443 { 444 float sent_Bps = 0, recvd_Bps = 0; 445 int rx_fd, txpid, rxpid, err = 0; 446 struct msg_stats s = {0}; 447 int iov_count = opt->iov_count; 448 int iov_buf = opt->iov_length; 449 int rx_status, tx_status; 450 int cnt = opt->rate; 451 452 errno = 0; 453 454 if (opt->base) 455 rx_fd = p1; 456 else 457 rx_fd = p2; 458 459 rxpid = fork(); 460 if (rxpid == 0) { 461 if (opt->drop_expected) 462 exit(0); 463 464 if (opt->sendpage) 465 iov_count = 1; 466 err = msg_loop(rx_fd, iov_count, iov_buf, 467 cnt, &s, false, opt); 468 if (err && opt->verbose) 469 fprintf(stderr, 470 "msg_loop_rx: iov_count %i iov_buf %i cnt %i err %i\n", 471 iov_count, iov_buf, cnt, err); 472 if (s.end.tv_sec - s.start.tv_sec) { 473 sent_Bps = sentBps(s); 474 recvd_Bps = recvdBps(s); 475 } 476 if (opt->verbose) 477 fprintf(stdout, 478 "rx_sendmsg: TX: %zuB %fB/s %fGB/s RX: %zuB %fB/s %fGB/s\n", 479 s.bytes_sent, sent_Bps, sent_Bps/giga, 480 s.bytes_recvd, recvd_Bps, recvd_Bps/giga); 481 if (err && txmsg_cork) 482 err = 0; 483 exit(err ? 1 : 0); 484 } else if (rxpid == -1) { 485 perror("msg_loop_rx: "); 486 return errno; 487 } 488 489 txpid = fork(); 490 if (txpid == 0) { 491 if (opt->sendpage) 492 err = msg_loop_sendpage(c1, iov_buf, cnt, &s, opt); 493 else 494 err = msg_loop(c1, iov_count, iov_buf, 495 cnt, &s, true, opt); 496 497 if (err) 498 fprintf(stderr, 499 "msg_loop_tx: iov_count %i iov_buf %i cnt %i err %i\n", 500 iov_count, iov_buf, cnt, err); 501 if (s.end.tv_sec - s.start.tv_sec) { 502 sent_Bps = sentBps(s); 503 recvd_Bps = recvdBps(s); 504 } 505 if (opt->verbose) 506 fprintf(stdout, 507 "tx_sendmsg: TX: %zuB %fB/s %f GB/s RX: %zuB %fB/s %fGB/s\n", 508 s.bytes_sent, sent_Bps, sent_Bps/giga, 509 s.bytes_recvd, recvd_Bps, recvd_Bps/giga); 510 exit(err ? 1 : 0); 511 } else if (txpid == -1) { 512 perror("msg_loop_tx: "); 513 return errno; 514 } 515 516 assert(waitpid(rxpid, &rx_status, 0) == rxpid); 517 assert(waitpid(txpid, &tx_status, 0) == txpid); 518 if (WIFEXITED(rx_status)) { 519 err = WEXITSTATUS(rx_status); 520 if (err) { 521 fprintf(stderr, "rx thread exited with err %d. ", err); 522 goto out; 523 } 524 } 525 if (WIFEXITED(tx_status)) { 526 err = WEXITSTATUS(tx_status); 527 if (err) 528 fprintf(stderr, "tx thread exited with err %d. ", err); 529 } 530 out: 531 return err; 532 } 533 534 static int forever_ping_pong(int rate, struct sockmap_options *opt) 535 { 536 struct timeval timeout; 537 char buf[1024] = {0}; 538 int sc; 539 540 timeout.tv_sec = 10; 541 timeout.tv_usec = 0; 542 543 /* Ping/Pong data from client to server */ 544 sc = send(c1, buf, sizeof(buf), 0); 545 if (sc < 0) { 546 perror("send failed()\n"); 547 return sc; 548 } 549 550 do { 551 int s, rc, i, max_fd = p2; 552 fd_set w; 553 554 /* FD sets */ 555 FD_ZERO(&w); 556 FD_SET(c1, &w); 557 FD_SET(c2, &w); 558 FD_SET(p1, &w); 559 FD_SET(p2, &w); 560 561 s = select(max_fd + 1, &w, NULL, NULL, &timeout); 562 if (s == -1) { 563 perror("select()"); 564 break; 565 } else if (!s) { 566 fprintf(stderr, "unexpected timeout\n"); 567 break; 568 } 569 570 for (i = 0; i <= max_fd && s > 0; ++i) { 571 if (!FD_ISSET(i, &w)) 572 continue; 573 574 s--; 575 576 rc = recv(i, buf, sizeof(buf), 0); 577 if (rc < 0) { 578 if (errno != EWOULDBLOCK) { 579 perror("recv failed()\n"); 580 return rc; 581 } 582 } 583 584 if (rc == 0) { 585 close(i); 586 break; 587 } 588 589 sc = send(i, buf, rc, 0); 590 if (sc < 0) { 591 perror("send failed()\n"); 592 return sc; 593 } 594 } 595 596 if (rate) 597 sleep(rate); 598 599 if (opt->verbose) { 600 printf("."); 601 fflush(stdout); 602 603 } 604 } while (running); 605 606 return 0; 607 } 608 609 enum { 610 PING_PONG, 611 SENDMSG, 612 BASE, 613 BASE_SENDPAGE, 614 SENDPAGE, 615 }; 616 617 static int run_options(struct sockmap_options *options, int cg_fd, int test) 618 { 619 int i, key, next_key, err, tx_prog_fd = -1, zero = 0; 620 621 /* If base test skip BPF setup */ 622 if (test == BASE || test == BASE_SENDPAGE) 623 goto run; 624 625 /* Attach programs to sockmap */ 626 err = bpf_prog_attach(prog_fd[0], map_fd[0], 627 BPF_SK_SKB_STREAM_PARSER, 0); 628 if (err) { 629 fprintf(stderr, 630 "ERROR: bpf_prog_attach (sockmap %i->%i): %d (%s)\n", 631 prog_fd[0], map_fd[0], err, strerror(errno)); 632 return err; 633 } 634 635 err = bpf_prog_attach(prog_fd[1], map_fd[0], 636 BPF_SK_SKB_STREAM_VERDICT, 0); 637 if (err) { 638 fprintf(stderr, "ERROR: bpf_prog_attach (sockmap): %d (%s)\n", 639 err, strerror(errno)); 640 return err; 641 } 642 643 /* Attach to cgroups */ 644 err = bpf_prog_attach(prog_fd[2], cg_fd, BPF_CGROUP_SOCK_OPS, 0); 645 if (err) { 646 fprintf(stderr, "ERROR: bpf_prog_attach (groups): %d (%s)\n", 647 err, strerror(errno)); 648 return err; 649 } 650 651 run: 652 err = sockmap_init_sockets(options->verbose); 653 if (err) { 654 fprintf(stderr, "ERROR: test socket failed: %d\n", err); 655 goto out; 656 } 657 658 /* Attach txmsg program to sockmap */ 659 if (txmsg_pass) 660 tx_prog_fd = prog_fd[3]; 661 else if (txmsg_noisy) 662 tx_prog_fd = prog_fd[4]; 663 else if (txmsg_redir) 664 tx_prog_fd = prog_fd[5]; 665 else if (txmsg_redir_noisy) 666 tx_prog_fd = prog_fd[6]; 667 else if (txmsg_drop) 668 tx_prog_fd = prog_fd[9]; 669 /* apply and cork must be last */ 670 else if (txmsg_apply) 671 tx_prog_fd = prog_fd[7]; 672 else if (txmsg_cork) 673 tx_prog_fd = prog_fd[8]; 674 else 675 tx_prog_fd = 0; 676 677 if (tx_prog_fd) { 678 int redir_fd, i = 0; 679 680 err = bpf_prog_attach(tx_prog_fd, 681 map_fd[1], BPF_SK_MSG_VERDICT, 0); 682 if (err) { 683 fprintf(stderr, 684 "ERROR: bpf_prog_attach (txmsg): %d (%s)\n", 685 err, strerror(errno)); 686 goto out; 687 } 688 689 err = bpf_map_update_elem(map_fd[1], &i, &c1, BPF_ANY); 690 if (err) { 691 fprintf(stderr, 692 "ERROR: bpf_map_update_elem (txmsg): %d (%s\n", 693 err, strerror(errno)); 694 goto out; 695 } 696 697 if (txmsg_redir || txmsg_redir_noisy) 698 redir_fd = c2; 699 else 700 redir_fd = c1; 701 702 err = bpf_map_update_elem(map_fd[2], &i, &redir_fd, BPF_ANY); 703 if (err) { 704 fprintf(stderr, 705 "ERROR: bpf_map_update_elem (txmsg): %d (%s\n", 706 err, strerror(errno)); 707 goto out; 708 } 709 710 if (txmsg_apply) { 711 err = bpf_map_update_elem(map_fd[3], 712 &i, &txmsg_apply, BPF_ANY); 713 if (err) { 714 fprintf(stderr, 715 "ERROR: bpf_map_update_elem (apply_bytes): %d (%s\n", 716 err, strerror(errno)); 717 goto out; 718 } 719 } 720 721 if (txmsg_cork) { 722 err = bpf_map_update_elem(map_fd[4], 723 &i, &txmsg_cork, BPF_ANY); 724 if (err) { 725 fprintf(stderr, 726 "ERROR: bpf_map_update_elem (cork_bytes): %d (%s\n", 727 err, strerror(errno)); 728 goto out; 729 } 730 } 731 732 if (txmsg_start) { 733 err = bpf_map_update_elem(map_fd[5], 734 &i, &txmsg_start, BPF_ANY); 735 if (err) { 736 fprintf(stderr, 737 "ERROR: bpf_map_update_elem (txmsg_start): %d (%s)\n", 738 err, strerror(errno)); 739 goto out; 740 } 741 } 742 743 if (txmsg_end) { 744 i = 1; 745 err = bpf_map_update_elem(map_fd[5], 746 &i, &txmsg_end, BPF_ANY); 747 if (err) { 748 fprintf(stderr, 749 "ERROR: bpf_map_update_elem (txmsg_end): %d (%s)\n", 750 err, strerror(errno)); 751 goto out; 752 } 753 } 754 755 if (txmsg_ingress) { 756 int in = BPF_F_INGRESS; 757 758 i = 0; 759 err = bpf_map_update_elem(map_fd[6], &i, &in, BPF_ANY); 760 if (err) { 761 fprintf(stderr, 762 "ERROR: bpf_map_update_elem (txmsg_ingress): %d (%s)\n", 763 err, strerror(errno)); 764 } 765 i = 1; 766 err = bpf_map_update_elem(map_fd[1], &i, &p1, BPF_ANY); 767 if (err) { 768 fprintf(stderr, 769 "ERROR: bpf_map_update_elem (p1 txmsg): %d (%s)\n", 770 err, strerror(errno)); 771 } 772 err = bpf_map_update_elem(map_fd[2], &i, &p1, BPF_ANY); 773 if (err) { 774 fprintf(stderr, 775 "ERROR: bpf_map_update_elem (p1 redir): %d (%s)\n", 776 err, strerror(errno)); 777 } 778 779 i = 2; 780 err = bpf_map_update_elem(map_fd[2], &i, &p2, BPF_ANY); 781 if (err) { 782 fprintf(stderr, 783 "ERROR: bpf_map_update_elem (p2 txmsg): %d (%s)\n", 784 err, strerror(errno)); 785 } 786 } 787 788 if (txmsg_skb) { 789 int skb_fd = (test == SENDMSG || test == SENDPAGE) ? 790 p2 : p1; 791 int ingress = BPF_F_INGRESS; 792 793 i = 0; 794 err = bpf_map_update_elem(map_fd[7], 795 &i, &ingress, BPF_ANY); 796 if (err) { 797 fprintf(stderr, 798 "ERROR: bpf_map_update_elem (txmsg_ingress): %d (%s)\n", 799 err, strerror(errno)); 800 } 801 802 i = 3; 803 err = bpf_map_update_elem(map_fd[0], 804 &i, &skb_fd, BPF_ANY); 805 if (err) { 806 fprintf(stderr, 807 "ERROR: bpf_map_update_elem (c1 sockmap): %d (%s)\n", 808 err, strerror(errno)); 809 } 810 } 811 } 812 813 if (txmsg_drop) 814 options->drop_expected = true; 815 816 if (test == PING_PONG) 817 err = forever_ping_pong(options->rate, options); 818 else if (test == SENDMSG) { 819 options->base = false; 820 options->sendpage = false; 821 err = sendmsg_test(options); 822 } else if (test == SENDPAGE) { 823 options->base = false; 824 options->sendpage = true; 825 err = sendmsg_test(options); 826 } else if (test == BASE) { 827 options->base = true; 828 options->sendpage = false; 829 err = sendmsg_test(options); 830 } else if (test == BASE_SENDPAGE) { 831 options->base = true; 832 options->sendpage = true; 833 err = sendmsg_test(options); 834 } else 835 fprintf(stderr, "unknown test\n"); 836 out: 837 /* Detatch and zero all the maps */ 838 bpf_prog_detach2(prog_fd[2], cg_fd, BPF_CGROUP_SOCK_OPS); 839 bpf_prog_detach2(prog_fd[0], map_fd[0], BPF_SK_SKB_STREAM_PARSER); 840 bpf_prog_detach2(prog_fd[1], map_fd[0], BPF_SK_SKB_STREAM_VERDICT); 841 if (tx_prog_fd >= 0) 842 bpf_prog_detach2(tx_prog_fd, map_fd[1], BPF_SK_MSG_VERDICT); 843 844 for (i = 0; i < 8; i++) { 845 key = next_key = 0; 846 bpf_map_update_elem(map_fd[i], &key, &zero, BPF_ANY); 847 while (bpf_map_get_next_key(map_fd[i], &key, &next_key) == 0) { 848 bpf_map_update_elem(map_fd[i], &key, &zero, BPF_ANY); 849 key = next_key; 850 } 851 } 852 853 close(s1); 854 close(s2); 855 close(p1); 856 close(p2); 857 close(c1); 858 close(c2); 859 return err; 860 } 861 862 static char *test_to_str(int test) 863 { 864 switch (test) { 865 case SENDMSG: 866 return "sendmsg"; 867 case SENDPAGE: 868 return "sendpage"; 869 } 870 return "unknown"; 871 } 872 873 #define OPTSTRING 60 874 static void test_options(char *options) 875 { 876 char tstr[OPTSTRING]; 877 878 memset(options, 0, OPTSTRING); 879 880 if (txmsg_pass) 881 strncat(options, "pass,", OPTSTRING); 882 if (txmsg_noisy) 883 strncat(options, "pass_noisy,", OPTSTRING); 884 if (txmsg_redir) 885 strncat(options, "redir,", OPTSTRING); 886 if (txmsg_redir_noisy) 887 strncat(options, "redir_noisy,", OPTSTRING); 888 if (txmsg_drop) 889 strncat(options, "drop,", OPTSTRING); 890 if (txmsg_apply) { 891 snprintf(tstr, OPTSTRING, "apply %d,", txmsg_apply); 892 strncat(options, tstr, OPTSTRING); 893 } 894 if (txmsg_cork) { 895 snprintf(tstr, OPTSTRING, "cork %d,", txmsg_cork); 896 strncat(options, tstr, OPTSTRING); 897 } 898 if (txmsg_start) { 899 snprintf(tstr, OPTSTRING, "start %d,", txmsg_start); 900 strncat(options, tstr, OPTSTRING); 901 } 902 if (txmsg_end) { 903 snprintf(tstr, OPTSTRING, "end %d,", txmsg_end); 904 strncat(options, tstr, OPTSTRING); 905 } 906 if (txmsg_ingress) 907 strncat(options, "ingress,", OPTSTRING); 908 if (txmsg_skb) 909 strncat(options, "skb,", OPTSTRING); 910 } 911 912 static int __test_exec(int cgrp, int test, struct sockmap_options *opt) 913 { 914 char *options = calloc(OPTSTRING, sizeof(char)); 915 int err; 916 917 if (test == SENDPAGE) 918 opt->sendpage = true; 919 else 920 opt->sendpage = false; 921 922 if (txmsg_drop) 923 opt->drop_expected = true; 924 else 925 opt->drop_expected = false; 926 927 test_options(options); 928 929 fprintf(stdout, 930 "[TEST %i]: (%i, %i, %i, %s, %s): ", 931 test_cnt, opt->rate, opt->iov_count, opt->iov_length, 932 test_to_str(test), options); 933 fflush(stdout); 934 err = run_options(opt, cgrp, test); 935 fprintf(stdout, "%s\n", !err ? "PASS" : "FAILED"); 936 test_cnt++; 937 !err ? passed++ : failed++; 938 free(options); 939 return err; 940 } 941 942 static int test_exec(int cgrp, struct sockmap_options *opt) 943 { 944 int err = __test_exec(cgrp, SENDMSG, opt); 945 946 if (err) 947 goto out; 948 949 err = __test_exec(cgrp, SENDPAGE, opt); 950 out: 951 return err; 952 } 953 954 static int test_loop(int cgrp) 955 { 956 struct sockmap_options opt; 957 958 int err, i, l, r; 959 960 opt.verbose = 0; 961 opt.base = false; 962 opt.sendpage = false; 963 opt.data_test = false; 964 opt.drop_expected = false; 965 opt.iov_count = 0; 966 opt.iov_length = 0; 967 opt.rate = 0; 968 969 r = 1; 970 for (i = 1; i < 100; i += 33) { 971 for (l = 1; l < 100; l += 33) { 972 opt.rate = r; 973 opt.iov_count = i; 974 opt.iov_length = l; 975 err = test_exec(cgrp, &opt); 976 if (err) 977 goto out; 978 } 979 } 980 sched_yield(); 981 out: 982 return err; 983 } 984 985 static int test_txmsg(int cgrp) 986 { 987 int err; 988 989 txmsg_pass = txmsg_noisy = txmsg_redir_noisy = txmsg_drop = 0; 990 txmsg_apply = txmsg_cork = 0; 991 txmsg_ingress = txmsg_skb = 0; 992 993 txmsg_pass = 1; 994 err = test_loop(cgrp); 995 txmsg_pass = 0; 996 if (err) 997 goto out; 998 999 txmsg_redir = 1; 1000 err = test_loop(cgrp); 1001 txmsg_redir = 0; 1002 if (err) 1003 goto out; 1004 1005 txmsg_drop = 1; 1006 err = test_loop(cgrp); 1007 txmsg_drop = 0; 1008 if (err) 1009 goto out; 1010 1011 txmsg_redir = 1; 1012 txmsg_ingress = 1; 1013 err = test_loop(cgrp); 1014 txmsg_redir = 0; 1015 txmsg_ingress = 0; 1016 if (err) 1017 goto out; 1018 out: 1019 txmsg_pass = 0; 1020 txmsg_redir = 0; 1021 txmsg_drop = 0; 1022 return err; 1023 } 1024 1025 static int test_send(struct sockmap_options *opt, int cgrp) 1026 { 1027 int err; 1028 1029 opt->iov_length = 1; 1030 opt->iov_count = 1; 1031 opt->rate = 1; 1032 err = test_exec(cgrp, opt); 1033 if (err) 1034 goto out; 1035 1036 opt->iov_length = 1; 1037 opt->iov_count = 1024; 1038 opt->rate = 1; 1039 err = test_exec(cgrp, opt); 1040 if (err) 1041 goto out; 1042 1043 opt->iov_length = 1024; 1044 opt->iov_count = 1; 1045 opt->rate = 1; 1046 err = test_exec(cgrp, opt); 1047 if (err) 1048 goto out; 1049 1050 opt->iov_length = 1; 1051 opt->iov_count = 1; 1052 opt->rate = 512; 1053 err = test_exec(cgrp, opt); 1054 if (err) 1055 goto out; 1056 1057 opt->iov_length = 256; 1058 opt->iov_count = 1024; 1059 opt->rate = 2; 1060 err = test_exec(cgrp, opt); 1061 if (err) 1062 goto out; 1063 1064 opt->rate = 100; 1065 opt->iov_count = 1; 1066 opt->iov_length = 5; 1067 err = test_exec(cgrp, opt); 1068 if (err) 1069 goto out; 1070 out: 1071 sched_yield(); 1072 return err; 1073 } 1074 1075 static int test_mixed(int cgrp) 1076 { 1077 struct sockmap_options opt = {0}; 1078 int err; 1079 1080 txmsg_pass = txmsg_noisy = txmsg_redir_noisy = txmsg_drop = 0; 1081 txmsg_apply = txmsg_cork = 0; 1082 txmsg_start = txmsg_end = 0; 1083 /* Test small and large iov_count values with pass/redir/apply/cork */ 1084 txmsg_pass = 1; 1085 txmsg_redir = 0; 1086 txmsg_apply = 1; 1087 txmsg_cork = 0; 1088 err = test_send(&opt, cgrp); 1089 if (err) 1090 goto out; 1091 1092 txmsg_pass = 1; 1093 txmsg_redir = 0; 1094 txmsg_apply = 0; 1095 txmsg_cork = 1; 1096 err = test_send(&opt, cgrp); 1097 if (err) 1098 goto out; 1099 1100 txmsg_pass = 1; 1101 txmsg_redir = 0; 1102 txmsg_apply = 1; 1103 txmsg_cork = 1; 1104 err = test_send(&opt, cgrp); 1105 if (err) 1106 goto out; 1107 1108 txmsg_pass = 1; 1109 txmsg_redir = 0; 1110 txmsg_apply = 1024; 1111 txmsg_cork = 0; 1112 err = test_send(&opt, cgrp); 1113 if (err) 1114 goto out; 1115 1116 txmsg_pass = 1; 1117 txmsg_redir = 0; 1118 txmsg_apply = 0; 1119 txmsg_cork = 1024; 1120 err = test_send(&opt, cgrp); 1121 if (err) 1122 goto out; 1123 1124 txmsg_pass = 1; 1125 txmsg_redir = 0; 1126 txmsg_apply = 1024; 1127 txmsg_cork = 1024; 1128 err = test_send(&opt, cgrp); 1129 if (err) 1130 goto out; 1131 1132 txmsg_pass = 1; 1133 txmsg_redir = 0; 1134 txmsg_cork = 4096; 1135 txmsg_apply = 4096; 1136 err = test_send(&opt, cgrp); 1137 if (err) 1138 goto out; 1139 1140 txmsg_pass = 0; 1141 txmsg_redir = 1; 1142 txmsg_apply = 1; 1143 txmsg_cork = 0; 1144 err = test_send(&opt, cgrp); 1145 if (err) 1146 goto out; 1147 1148 txmsg_pass = 0; 1149 txmsg_redir = 1; 1150 txmsg_apply = 0; 1151 txmsg_cork = 1; 1152 err = test_send(&opt, cgrp); 1153 if (err) 1154 goto out; 1155 1156 txmsg_pass = 0; 1157 txmsg_redir = 1; 1158 txmsg_apply = 1024; 1159 txmsg_cork = 0; 1160 err = test_send(&opt, cgrp); 1161 if (err) 1162 goto out; 1163 1164 txmsg_pass = 0; 1165 txmsg_redir = 1; 1166 txmsg_apply = 0; 1167 txmsg_cork = 1024; 1168 err = test_send(&opt, cgrp); 1169 if (err) 1170 goto out; 1171 1172 txmsg_pass = 0; 1173 txmsg_redir = 1; 1174 txmsg_apply = 1024; 1175 txmsg_cork = 1024; 1176 err = test_send(&opt, cgrp); 1177 if (err) 1178 goto out; 1179 1180 txmsg_pass = 0; 1181 txmsg_redir = 1; 1182 txmsg_cork = 4096; 1183 txmsg_apply = 4096; 1184 err = test_send(&opt, cgrp); 1185 if (err) 1186 goto out; 1187 out: 1188 return err; 1189 } 1190 1191 static int test_start_end(int cgrp) 1192 { 1193 struct sockmap_options opt = {0}; 1194 int err, i; 1195 1196 /* Test basic start/end with lots of iov_count and iov_lengths */ 1197 txmsg_start = 1; 1198 txmsg_end = 2; 1199 err = test_txmsg(cgrp); 1200 if (err) 1201 goto out; 1202 1203 /* Test start/end with cork */ 1204 opt.rate = 16; 1205 opt.iov_count = 1; 1206 opt.iov_length = 100; 1207 txmsg_cork = 1600; 1208 1209 for (i = 99; i <= 1600; i += 500) { 1210 txmsg_start = 0; 1211 txmsg_end = i; 1212 err = test_exec(cgrp, &opt); 1213 if (err) 1214 goto out; 1215 } 1216 1217 /* Test start/end with cork but pull data in middle */ 1218 for (i = 199; i <= 1600; i += 500) { 1219 txmsg_start = 100; 1220 txmsg_end = i; 1221 err = test_exec(cgrp, &opt); 1222 if (err) 1223 goto out; 1224 } 1225 1226 /* Test start/end with cork pulling last sg entry */ 1227 txmsg_start = 1500; 1228 txmsg_end = 1600; 1229 err = test_exec(cgrp, &opt); 1230 if (err) 1231 goto out; 1232 1233 /* Test start/end pull of single byte in last page */ 1234 txmsg_start = 1111; 1235 txmsg_end = 1112; 1236 err = test_exec(cgrp, &opt); 1237 if (err) 1238 goto out; 1239 1240 /* Test start/end with end < start */ 1241 txmsg_start = 1111; 1242 txmsg_end = 0; 1243 err = test_exec(cgrp, &opt); 1244 if (err) 1245 goto out; 1246 1247 /* Test start/end with end > data */ 1248 txmsg_start = 0; 1249 txmsg_end = 1601; 1250 err = test_exec(cgrp, &opt); 1251 if (err) 1252 goto out; 1253 1254 /* Test start/end with start > data */ 1255 txmsg_start = 1601; 1256 txmsg_end = 1600; 1257 err = test_exec(cgrp, &opt); 1258 1259 out: 1260 txmsg_start = 0; 1261 txmsg_end = 0; 1262 sched_yield(); 1263 return err; 1264 } 1265 1266 char *map_names[] = { 1267 "sock_map", 1268 "sock_map_txmsg", 1269 "sock_map_redir", 1270 "sock_apply_bytes", 1271 "sock_cork_bytes", 1272 "sock_pull_bytes", 1273 "sock_redir_flags", 1274 "sock_skb_opts", 1275 }; 1276 1277 int prog_attach_type[] = { 1278 BPF_SK_SKB_STREAM_PARSER, 1279 BPF_SK_SKB_STREAM_VERDICT, 1280 BPF_CGROUP_SOCK_OPS, 1281 BPF_SK_MSG_VERDICT, 1282 BPF_SK_MSG_VERDICT, 1283 BPF_SK_MSG_VERDICT, 1284 BPF_SK_MSG_VERDICT, 1285 BPF_SK_MSG_VERDICT, 1286 BPF_SK_MSG_VERDICT, 1287 BPF_SK_MSG_VERDICT, 1288 }; 1289 1290 int prog_type[] = { 1291 BPF_PROG_TYPE_SK_SKB, 1292 BPF_PROG_TYPE_SK_SKB, 1293 BPF_PROG_TYPE_SOCK_OPS, 1294 BPF_PROG_TYPE_SK_MSG, 1295 BPF_PROG_TYPE_SK_MSG, 1296 BPF_PROG_TYPE_SK_MSG, 1297 BPF_PROG_TYPE_SK_MSG, 1298 BPF_PROG_TYPE_SK_MSG, 1299 BPF_PROG_TYPE_SK_MSG, 1300 BPF_PROG_TYPE_SK_MSG, 1301 }; 1302 1303 static int populate_progs(char *bpf_file) 1304 { 1305 struct bpf_program *prog; 1306 struct bpf_object *obj; 1307 int i = 0; 1308 long err; 1309 1310 obj = bpf_object__open(bpf_file); 1311 err = libbpf_get_error(obj); 1312 if (err) { 1313 char err_buf[256]; 1314 1315 libbpf_strerror(err, err_buf, sizeof(err_buf)); 1316 printf("Unable to load eBPF objects in file '%s' : %s\n", 1317 bpf_file, err_buf); 1318 return -1; 1319 } 1320 1321 bpf_object__for_each_program(prog, obj) { 1322 bpf_program__set_type(prog, prog_type[i]); 1323 bpf_program__set_expected_attach_type(prog, 1324 prog_attach_type[i]); 1325 i++; 1326 } 1327 1328 i = bpf_object__load(obj); 1329 i = 0; 1330 bpf_object__for_each_program(prog, obj) { 1331 prog_fd[i] = bpf_program__fd(prog); 1332 i++; 1333 } 1334 1335 for (i = 0; i < sizeof(map_fd)/sizeof(int); i++) { 1336 maps[i] = bpf_object__find_map_by_name(obj, map_names[i]); 1337 map_fd[i] = bpf_map__fd(maps[i]); 1338 if (map_fd[i] < 0) { 1339 fprintf(stderr, "load_bpf_file: (%i) %s\n", 1340 map_fd[i], strerror(errno)); 1341 return -1; 1342 } 1343 } 1344 1345 return 0; 1346 } 1347 1348 static int __test_suite(int cg_fd, char *bpf_file) 1349 { 1350 int err, cleanup = cg_fd; 1351 1352 err = populate_progs(bpf_file); 1353 if (err < 0) { 1354 fprintf(stderr, "ERROR: (%i) load bpf failed\n", err); 1355 return err; 1356 } 1357 1358 if (cg_fd < 0) { 1359 if (setup_cgroup_environment()) { 1360 fprintf(stderr, "ERROR: cgroup env failed\n"); 1361 return -EINVAL; 1362 } 1363 1364 cg_fd = create_and_get_cgroup(CG_PATH); 1365 if (cg_fd < 0) { 1366 fprintf(stderr, 1367 "ERROR: (%i) open cg path failed: %s\n", 1368 cg_fd, optarg); 1369 return cg_fd; 1370 } 1371 1372 if (join_cgroup(CG_PATH)) { 1373 fprintf(stderr, "ERROR: failed to join cgroup\n"); 1374 return -EINVAL; 1375 } 1376 } 1377 1378 /* Tests basic commands and APIs with range of iov values */ 1379 txmsg_start = txmsg_end = 0; 1380 err = test_txmsg(cg_fd); 1381 if (err) 1382 goto out; 1383 1384 /* Tests interesting combinations of APIs used together */ 1385 err = test_mixed(cg_fd); 1386 if (err) 1387 goto out; 1388 1389 /* Tests pull_data API using start/end API */ 1390 err = test_start_end(cg_fd); 1391 if (err) 1392 goto out; 1393 1394 out: 1395 printf("Summary: %i PASSED %i FAILED\n", passed, failed); 1396 if (cleanup < 0) { 1397 cleanup_cgroup_environment(); 1398 close(cg_fd); 1399 } 1400 return err; 1401 } 1402 1403 static int test_suite(int cg_fd) 1404 { 1405 int err; 1406 1407 err = __test_suite(cg_fd, BPF_SOCKMAP_FILENAME); 1408 if (err) 1409 goto out; 1410 err = __test_suite(cg_fd, BPF_SOCKHASH_FILENAME); 1411 out: 1412 if (cg_fd > -1) 1413 close(cg_fd); 1414 return err; 1415 } 1416 1417 int main(int argc, char **argv) 1418 { 1419 int iov_count = 1, length = 1024, rate = 1; 1420 struct sockmap_options options = {0}; 1421 int opt, longindex, err, cg_fd = 0; 1422 char *bpf_file = BPF_SOCKMAP_FILENAME; 1423 int test = PING_PONG; 1424 1425 if (argc < 2) 1426 return test_suite(-1); 1427 1428 while ((opt = getopt_long(argc, argv, ":dhvc:r:i:l:t:", 1429 long_options, &longindex)) != -1) { 1430 switch (opt) { 1431 case 's': 1432 txmsg_start = atoi(optarg); 1433 break; 1434 case 'e': 1435 txmsg_end = atoi(optarg); 1436 break; 1437 case 'a': 1438 txmsg_apply = atoi(optarg); 1439 break; 1440 case 'k': 1441 txmsg_cork = atoi(optarg); 1442 break; 1443 case 'c': 1444 cg_fd = open(optarg, O_DIRECTORY, O_RDONLY); 1445 if (cg_fd < 0) { 1446 fprintf(stderr, 1447 "ERROR: (%i) open cg path failed: %s\n", 1448 cg_fd, optarg); 1449 return cg_fd; 1450 } 1451 break; 1452 case 'r': 1453 rate = atoi(optarg); 1454 break; 1455 case 'v': 1456 options.verbose = 1; 1457 break; 1458 case 'i': 1459 iov_count = atoi(optarg); 1460 break; 1461 case 'l': 1462 length = atoi(optarg); 1463 break; 1464 case 'd': 1465 options.data_test = true; 1466 break; 1467 case 't': 1468 if (strcmp(optarg, "ping") == 0) { 1469 test = PING_PONG; 1470 } else if (strcmp(optarg, "sendmsg") == 0) { 1471 test = SENDMSG; 1472 } else if (strcmp(optarg, "base") == 0) { 1473 test = BASE; 1474 } else if (strcmp(optarg, "base_sendpage") == 0) { 1475 test = BASE_SENDPAGE; 1476 } else if (strcmp(optarg, "sendpage") == 0) { 1477 test = SENDPAGE; 1478 } else { 1479 usage(argv); 1480 return -1; 1481 } 1482 break; 1483 case 0: 1484 break; 1485 case 'h': 1486 default: 1487 usage(argv); 1488 return -1; 1489 } 1490 } 1491 1492 if (argc <= 3 && cg_fd) 1493 return test_suite(cg_fd); 1494 1495 if (!cg_fd) { 1496 fprintf(stderr, "%s requires cgroup option: --cgroup <path>\n", 1497 argv[0]); 1498 return -1; 1499 } 1500 1501 err = populate_progs(bpf_file); 1502 if (err) { 1503 fprintf(stderr, "populate program: (%s) %s\n", 1504 bpf_file, strerror(errno)); 1505 return 1; 1506 } 1507 running = 1; 1508 1509 /* catch SIGINT */ 1510 signal(SIGINT, running_handler); 1511 1512 options.iov_count = iov_count; 1513 options.iov_length = length; 1514 options.rate = rate; 1515 1516 err = run_options(&options, cg_fd, test); 1517 close(cg_fd); 1518 return err; 1519 } 1520 1521 void running_handler(int a) 1522 { 1523 running = 0; 1524 } 1525