1 // SPDX-License-Identifier: GPL-2.0 2 /* Copyright Amazon.com Inc. or its affiliates. */ 3 4 #include <fcntl.h> 5 #include <string.h> 6 #include <unistd.h> 7 8 #include <netinet/in.h> 9 #include <sys/epoll.h> 10 #include <sys/ioctl.h> 11 #include <sys/signalfd.h> 12 #include <sys/socket.h> 13 14 #include "../../kselftest_harness.h" 15 16 #define BUF_SZ 32 17 18 FIXTURE(msg_oob) 19 { 20 int fd[4]; /* 0: AF_UNIX sender 21 * 1: AF_UNIX receiver 22 * 2: TCP sender 23 * 3: TCP receiver 24 */ 25 int signal_fd; 26 int epoll_fd[2]; /* 0: AF_UNIX receiver 27 * 1: TCP receiver 28 */ 29 bool tcp_compliant; 30 }; 31 32 FIXTURE_VARIANT(msg_oob) 33 { 34 bool peek; 35 }; 36 37 FIXTURE_VARIANT_ADD(msg_oob, no_peek) 38 { 39 .peek = false, 40 }; 41 42 FIXTURE_VARIANT_ADD(msg_oob, peek) 43 { 44 .peek = true 45 }; 46 47 static void create_unix_socketpair(struct __test_metadata *_metadata, 48 FIXTURE_DATA(msg_oob) *self) 49 { 50 int ret; 51 52 ret = socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0, self->fd); 53 ASSERT_EQ(ret, 0); 54 } 55 56 static void create_tcp_socketpair(struct __test_metadata *_metadata, 57 FIXTURE_DATA(msg_oob) *self) 58 { 59 struct sockaddr_in addr; 60 socklen_t addrlen; 61 int listen_fd; 62 int ret; 63 64 listen_fd = socket(AF_INET, SOCK_STREAM, 0); 65 ASSERT_GE(listen_fd, 0); 66 67 ret = listen(listen_fd, -1); 68 ASSERT_EQ(ret, 0); 69 70 addrlen = sizeof(addr); 71 ret = getsockname(listen_fd, (struct sockaddr *)&addr, &addrlen); 72 ASSERT_EQ(ret, 0); 73 74 self->fd[2] = socket(AF_INET, SOCK_STREAM, 0); 75 ASSERT_GE(self->fd[2], 0); 76 77 ret = connect(self->fd[2], (struct sockaddr *)&addr, addrlen); 78 ASSERT_EQ(ret, 0); 79 80 self->fd[3] = accept(listen_fd, (struct sockaddr *)&addr, &addrlen); 81 ASSERT_GE(self->fd[3], 0); 82 83 ret = fcntl(self->fd[3], F_SETFL, O_NONBLOCK); 84 ASSERT_EQ(ret, 0); 85 } 86 87 static void setup_sigurg(struct __test_metadata *_metadata, 88 FIXTURE_DATA(msg_oob) *self) 89 { 90 struct signalfd_siginfo siginfo; 91 int pid = getpid(); 92 sigset_t mask; 93 int i, ret; 94 95 for (i = 0; i < 2; i++) { 96 ret = ioctl(self->fd[i * 2 + 1], FIOSETOWN, &pid); 97 ASSERT_EQ(ret, 0); 98 } 99 100 ret = sigemptyset(&mask); 101 ASSERT_EQ(ret, 0); 102 103 ret = sigaddset(&mask, SIGURG); 104 ASSERT_EQ(ret, 0); 105 106 ret = sigprocmask(SIG_BLOCK, &mask, NULL); 107 ASSERT_EQ(ret, 0); 108 109 self->signal_fd = signalfd(-1, &mask, SFD_NONBLOCK); 110 ASSERT_GE(self->signal_fd, 0); 111 112 ret = read(self->signal_fd, &siginfo, sizeof(siginfo)); 113 ASSERT_EQ(ret, -1); 114 } 115 116 static void setup_epollpri(struct __test_metadata *_metadata, 117 FIXTURE_DATA(msg_oob) *self) 118 { 119 struct epoll_event event = { 120 .events = EPOLLPRI, 121 }; 122 int i; 123 124 for (i = 0; i < 2; i++) { 125 int ret; 126 127 self->epoll_fd[i] = epoll_create1(0); 128 ASSERT_GE(self->epoll_fd[i], 0); 129 130 ret = epoll_ctl(self->epoll_fd[i], EPOLL_CTL_ADD, self->fd[i * 2 + 1], &event); 131 ASSERT_EQ(ret, 0); 132 } 133 } 134 135 static void close_sockets(FIXTURE_DATA(msg_oob) *self) 136 { 137 int i; 138 139 for (i = 0; i < 4; i++) 140 close(self->fd[i]); 141 } 142 143 FIXTURE_SETUP(msg_oob) 144 { 145 create_unix_socketpair(_metadata, self); 146 create_tcp_socketpair(_metadata, self); 147 148 setup_sigurg(_metadata, self); 149 setup_epollpri(_metadata, self); 150 151 self->tcp_compliant = true; 152 } 153 154 FIXTURE_TEARDOWN(msg_oob) 155 { 156 close_sockets(self); 157 } 158 159 static void __epollpair(struct __test_metadata *_metadata, 160 FIXTURE_DATA(msg_oob) *self, 161 bool oob_remaining) 162 { 163 struct epoll_event event[2] = {}; 164 int i, ret[2]; 165 166 for (i = 0; i < 2; i++) 167 ret[i] = epoll_wait(self->epoll_fd[i], &event[i], 1, 0); 168 169 ASSERT_EQ(ret[0], oob_remaining); 170 171 if (self->tcp_compliant) 172 ASSERT_EQ(ret[0], ret[1]); 173 174 if (oob_remaining) { 175 ASSERT_EQ(event[0].events, EPOLLPRI); 176 177 if (self->tcp_compliant) 178 ASSERT_EQ(event[0].events, event[1].events); 179 } 180 } 181 182 static void __sendpair(struct __test_metadata *_metadata, 183 FIXTURE_DATA(msg_oob) *self, 184 const void *buf, size_t len, int flags) 185 { 186 int i, ret[2]; 187 188 for (i = 0; i < 2; i++) { 189 struct signalfd_siginfo siginfo = {}; 190 int bytes; 191 192 ret[i] = send(self->fd[i * 2], buf, len, flags); 193 194 bytes = read(self->signal_fd, &siginfo, sizeof(siginfo)); 195 196 if (flags & MSG_OOB) { 197 ASSERT_EQ(bytes, sizeof(siginfo)); 198 ASSERT_EQ(siginfo.ssi_signo, SIGURG); 199 200 bytes = read(self->signal_fd, &siginfo, sizeof(siginfo)); 201 } 202 203 ASSERT_EQ(bytes, -1); 204 } 205 206 ASSERT_EQ(ret[0], len); 207 ASSERT_EQ(ret[0], ret[1]); 208 } 209 210 static void __recvpair(struct __test_metadata *_metadata, 211 FIXTURE_DATA(msg_oob) *self, 212 const char *expected_buf, int expected_len, 213 int buf_len, int flags) 214 { 215 int i, ret[2], recv_errno[2], expected_errno = 0; 216 char recv_buf[2][BUF_SZ] = {}; 217 bool printed = false; 218 219 ASSERT_GE(BUF_SZ, buf_len); 220 221 errno = 0; 222 223 for (i = 0; i < 2; i++) { 224 ret[i] = recv(self->fd[i * 2 + 1], recv_buf[i], buf_len, flags); 225 recv_errno[i] = errno; 226 } 227 228 if (expected_len < 0) { 229 expected_errno = -expected_len; 230 expected_len = -1; 231 } 232 233 if (ret[0] != expected_len || recv_errno[0] != expected_errno) { 234 TH_LOG("AF_UNIX :%s", ret[0] < 0 ? strerror(recv_errno[0]) : recv_buf[0]); 235 TH_LOG("Expected:%s", expected_errno ? strerror(expected_errno) : expected_buf); 236 237 ASSERT_EQ(ret[0], expected_len); 238 ASSERT_EQ(recv_errno[0], expected_errno); 239 } 240 241 if (ret[0] != ret[1] || recv_errno[0] != recv_errno[1]) { 242 TH_LOG("AF_UNIX :%s", ret[0] < 0 ? strerror(recv_errno[0]) : recv_buf[0]); 243 TH_LOG("TCP :%s", ret[1] < 0 ? strerror(recv_errno[1]) : recv_buf[1]); 244 245 printed = true; 246 247 if (self->tcp_compliant) { 248 ASSERT_EQ(ret[0], ret[1]); 249 ASSERT_EQ(recv_errno[0], recv_errno[1]); 250 } 251 } 252 253 if (expected_len >= 0) { 254 int cmp; 255 256 cmp = strncmp(expected_buf, recv_buf[0], expected_len); 257 if (cmp) { 258 TH_LOG("AF_UNIX :%s", ret[0] < 0 ? strerror(recv_errno[0]) : recv_buf[0]); 259 TH_LOG("Expected:%s", expected_errno ? strerror(expected_errno) : expected_buf); 260 261 ASSERT_EQ(cmp, 0); 262 } 263 264 cmp = strncmp(recv_buf[0], recv_buf[1], expected_len); 265 if (cmp) { 266 if (!printed) { 267 TH_LOG("AF_UNIX :%s", ret[0] < 0 ? strerror(recv_errno[0]) : recv_buf[0]); 268 TH_LOG("TCP :%s", ret[1] < 0 ? strerror(recv_errno[1]) : recv_buf[1]); 269 } 270 271 if (self->tcp_compliant) 272 ASSERT_EQ(cmp, 0); 273 } 274 } 275 } 276 277 static void __setinlinepair(struct __test_metadata *_metadata, 278 FIXTURE_DATA(msg_oob) *self) 279 { 280 int i, oob_inline = 1; 281 282 for (i = 0; i < 2; i++) { 283 int ret; 284 285 ret = setsockopt(self->fd[i * 2 + 1], SOL_SOCKET, SO_OOBINLINE, 286 &oob_inline, sizeof(oob_inline)); 287 ASSERT_EQ(ret, 0); 288 } 289 } 290 291 static void __siocatmarkpair(struct __test_metadata *_metadata, 292 FIXTURE_DATA(msg_oob) *self, 293 bool oob_head) 294 { 295 int answ[2] = {}; 296 int i; 297 298 for (i = 0; i < 2; i++) { 299 int ret; 300 301 ret = ioctl(self->fd[i * 2 + 1], SIOCATMARK, &answ[i]); 302 ASSERT_EQ(ret, 0); 303 } 304 305 ASSERT_EQ(answ[0], oob_head); 306 307 if (self->tcp_compliant) 308 ASSERT_EQ(answ[0], answ[1]); 309 } 310 311 #define sendpair(buf, len, flags) \ 312 __sendpair(_metadata, self, buf, len, flags) 313 314 #define recvpair(expected_buf, expected_len, buf_len, flags) \ 315 do { \ 316 if (variant->peek) \ 317 __recvpair(_metadata, self, \ 318 expected_buf, expected_len, \ 319 buf_len, (flags) | MSG_PEEK); \ 320 __recvpair(_metadata, self, \ 321 expected_buf, expected_len, buf_len, flags); \ 322 } while (0) 323 324 #define epollpair(oob_remaining) \ 325 __epollpair(_metadata, self, oob_remaining) 326 327 #define siocatmarkpair(oob_head) \ 328 __siocatmarkpair(_metadata, self, oob_head) 329 330 #define setinlinepair() \ 331 __setinlinepair(_metadata, self) 332 333 #define tcp_incompliant \ 334 for (self->tcp_compliant = false; \ 335 self->tcp_compliant == false; \ 336 self->tcp_compliant = true) 337 338 TEST_F(msg_oob, non_oob) 339 { 340 sendpair("x", 1, 0); 341 epollpair(false); 342 siocatmarkpair(false); 343 344 recvpair("", -EINVAL, 1, MSG_OOB); 345 epollpair(false); 346 siocatmarkpair(false); 347 } 348 349 TEST_F(msg_oob, oob) 350 { 351 sendpair("x", 1, MSG_OOB); 352 epollpair(true); 353 siocatmarkpair(true); 354 355 recvpair("x", 1, 1, MSG_OOB); 356 epollpair(false); 357 siocatmarkpair(true); 358 } 359 360 TEST_F(msg_oob, oob_drop) 361 { 362 sendpair("x", 1, MSG_OOB); 363 epollpair(true); 364 siocatmarkpair(true); 365 366 recvpair("", -EAGAIN, 1, 0); /* Drop OOB. */ 367 epollpair(false); 368 siocatmarkpair(false); 369 370 recvpair("", -EINVAL, 1, MSG_OOB); 371 epollpair(false); 372 siocatmarkpair(false); 373 } 374 375 TEST_F(msg_oob, oob_ahead) 376 { 377 sendpair("hello", 5, MSG_OOB); 378 epollpair(true); 379 siocatmarkpair(false); 380 381 recvpair("o", 1, 1, MSG_OOB); 382 epollpair(false); 383 siocatmarkpair(false); 384 385 recvpair("hell", 4, 4, 0); 386 epollpair(false); 387 siocatmarkpair(true); 388 } 389 390 TEST_F(msg_oob, oob_break) 391 { 392 sendpair("hello", 5, MSG_OOB); 393 epollpair(true); 394 siocatmarkpair(false); 395 396 recvpair("hell", 4, 5, 0); /* Break at OOB even with enough buffer. */ 397 epollpair(true); 398 siocatmarkpair(true); 399 400 recvpair("o", 1, 1, MSG_OOB); 401 epollpair(false); 402 siocatmarkpair(true); 403 404 recvpair("", -EAGAIN, 1, 0); 405 siocatmarkpair(false); 406 } 407 408 TEST_F(msg_oob, oob_ahead_break) 409 { 410 sendpair("hello", 5, MSG_OOB); 411 epollpair(true); 412 siocatmarkpair(false); 413 414 sendpair("world", 5, 0); 415 epollpair(true); 416 siocatmarkpair(false); 417 418 recvpair("o", 1, 1, MSG_OOB); 419 epollpair(false); 420 siocatmarkpair(false); 421 422 recvpair("hell", 4, 9, 0); /* Break at OOB even after it's recv()ed. */ 423 epollpair(false); 424 siocatmarkpair(true); 425 426 recvpair("world", 5, 5, 0); 427 epollpair(false); 428 siocatmarkpair(false); 429 } 430 431 TEST_F(msg_oob, oob_break_drop) 432 { 433 sendpair("hello", 5, MSG_OOB); 434 epollpair(true); 435 siocatmarkpair(false); 436 437 sendpair("world", 5, 0); 438 epollpair(true); 439 siocatmarkpair(false); 440 441 recvpair("hell", 4, 10, 0); /* Break at OOB even with enough buffer. */ 442 epollpair(true); 443 siocatmarkpair(true); 444 445 recvpair("world", 5, 10, 0); /* Drop OOB and recv() the next skb. */ 446 epollpair(false); 447 siocatmarkpair(false); 448 449 recvpair("", -EINVAL, 1, MSG_OOB); 450 epollpair(false); 451 siocatmarkpair(false); 452 } 453 454 TEST_F(msg_oob, ex_oob_break) 455 { 456 sendpair("hello", 5, MSG_OOB); 457 epollpair(true); 458 siocatmarkpair(false); 459 460 sendpair("wor", 3, MSG_OOB); 461 epollpair(true); 462 siocatmarkpair(false); 463 464 sendpair("ld", 2, 0); 465 epollpair(true); 466 siocatmarkpair(false); 467 468 recvpair("hellowo", 7, 10, 0); /* Break at OOB but not at ex-OOB. */ 469 epollpair(true); 470 siocatmarkpair(true); 471 472 recvpair("r", 1, 1, MSG_OOB); 473 epollpair(false); 474 siocatmarkpair(true); 475 476 recvpair("ld", 2, 2, 0); 477 epollpair(false); 478 siocatmarkpair(false); 479 } 480 481 TEST_F(msg_oob, ex_oob_drop) 482 { 483 sendpair("x", 1, MSG_OOB); 484 epollpair(true); 485 siocatmarkpair(true); 486 487 sendpair("y", 1, MSG_OOB); /* TCP drops "x" at this moment. */ 488 epollpair(true); 489 490 tcp_incompliant { 491 siocatmarkpair(false); 492 493 recvpair("x", 1, 1, 0); /* TCP drops "y" by passing through it. */ 494 epollpair(true); 495 siocatmarkpair(true); 496 497 recvpair("y", 1, 1, MSG_OOB); /* TCP returns -EINVAL. */ 498 epollpair(false); 499 siocatmarkpair(true); 500 } 501 } 502 503 TEST_F(msg_oob, ex_oob_drop_2) 504 { 505 sendpair("x", 1, MSG_OOB); 506 epollpair(true); 507 siocatmarkpair(true); 508 509 sendpair("y", 1, MSG_OOB); /* TCP drops "x" at this moment. */ 510 epollpair(true); 511 512 tcp_incompliant { 513 siocatmarkpair(false); 514 } 515 516 recvpair("y", 1, 1, MSG_OOB); 517 epollpair(false); 518 519 tcp_incompliant { 520 siocatmarkpair(false); 521 522 recvpair("x", 1, 1, 0); /* TCP returns -EAGAIN. */ 523 epollpair(false); 524 siocatmarkpair(true); 525 } 526 } 527 528 TEST_F(msg_oob, ex_oob_ahead_break) 529 { 530 sendpair("hello", 5, MSG_OOB); 531 epollpair(true); 532 siocatmarkpair(false); 533 534 sendpair("wor", 3, MSG_OOB); 535 epollpair(true); 536 siocatmarkpair(false); 537 538 recvpair("r", 1, 1, MSG_OOB); 539 epollpair(false); 540 siocatmarkpair(false); 541 542 sendpair("ld", 2, MSG_OOB); 543 epollpair(true); 544 siocatmarkpair(false); 545 546 tcp_incompliant { 547 recvpair("hellowol", 8, 10, 0); /* TCP recv()s "helloworl", why "r" ?? */ 548 } 549 550 epollpair(true); 551 siocatmarkpair(true); 552 553 recvpair("d", 1, 1, MSG_OOB); 554 epollpair(false); 555 siocatmarkpair(true); 556 } 557 558 TEST_F(msg_oob, ex_oob_siocatmark) 559 { 560 sendpair("hello", 5, MSG_OOB); 561 epollpair(true); 562 siocatmarkpair(false); 563 564 recvpair("o", 1, 1, MSG_OOB); 565 epollpair(false); 566 siocatmarkpair(false); 567 568 sendpair("world", 5, MSG_OOB); 569 epollpair(true); 570 siocatmarkpair(false); 571 572 recvpair("hell", 4, 4, 0); /* Intentionally stop at ex-OOB. */ 573 epollpair(true); 574 siocatmarkpair(false); 575 } 576 577 TEST_F(msg_oob, inline_oob) 578 { 579 setinlinepair(); 580 581 sendpair("x", 1, MSG_OOB); 582 epollpair(true); 583 siocatmarkpair(true); 584 585 recvpair("", -EINVAL, 1, MSG_OOB); 586 epollpair(true); 587 siocatmarkpair(true); 588 589 recvpair("x", 1, 1, 0); 590 epollpair(false); 591 siocatmarkpair(false); 592 } 593 594 TEST_F(msg_oob, inline_oob_break) 595 { 596 setinlinepair(); 597 598 sendpair("hello", 5, MSG_OOB); 599 epollpair(true); 600 siocatmarkpair(false); 601 602 recvpair("", -EINVAL, 1, MSG_OOB); 603 epollpair(true); 604 siocatmarkpair(false); 605 606 recvpair("hell", 4, 5, 0); /* Break at OOB but not at ex-OOB. */ 607 epollpair(true); 608 siocatmarkpair(true); 609 610 recvpair("o", 1, 1, 0); 611 epollpair(false); 612 siocatmarkpair(false); 613 } 614 615 TEST_F(msg_oob, inline_oob_ahead_break) 616 { 617 sendpair("hello", 5, MSG_OOB); 618 epollpair(true); 619 siocatmarkpair(false); 620 621 sendpair("world", 5, 0); 622 epollpair(true); 623 siocatmarkpair(false); 624 625 recvpair("o", 1, 1, MSG_OOB); 626 epollpair(false); 627 siocatmarkpair(false); 628 629 setinlinepair(); 630 631 recvpair("hell", 4, 9, 0); /* Break at OOB even with enough buffer. */ 632 epollpair(false); 633 siocatmarkpair(true); 634 635 tcp_incompliant { 636 recvpair("world", 5, 6, 0); /* TCP recv()s "oworld", ... "o" ??? */ 637 } 638 639 epollpair(false); 640 siocatmarkpair(false); 641 } 642 643 TEST_F(msg_oob, inline_ex_oob_break) 644 { 645 sendpair("hello", 5, MSG_OOB); 646 epollpair(true); 647 siocatmarkpair(false); 648 649 sendpair("wor", 3, MSG_OOB); 650 epollpair(true); 651 siocatmarkpair(false); 652 653 sendpair("ld", 2, 0); 654 epollpair(true); 655 siocatmarkpair(false); 656 657 setinlinepair(); 658 659 recvpair("hellowo", 7, 10, 0); /* Break at OOB but not at ex-OOB. */ 660 epollpair(true); 661 siocatmarkpair(true); 662 663 recvpair("rld", 3, 3, 0); 664 epollpair(false); 665 siocatmarkpair(false); 666 } 667 668 TEST_F(msg_oob, inline_ex_oob_no_drop) 669 { 670 sendpair("x", 1, MSG_OOB); 671 epollpair(true); 672 siocatmarkpair(true); 673 674 setinlinepair(); 675 676 sendpair("y", 1, MSG_OOB); /* TCP does NOT drops "x" at this moment. */ 677 epollpair(true); 678 siocatmarkpair(false); 679 680 recvpair("x", 1, 1, 0); 681 epollpair(true); 682 siocatmarkpair(true); 683 684 recvpair("y", 1, 1, 0); 685 epollpair(false); 686 siocatmarkpair(false); 687 } 688 689 TEST_F(msg_oob, inline_ex_oob_drop) 690 { 691 sendpair("x", 1, MSG_OOB); 692 epollpair(true); 693 siocatmarkpair(true); 694 695 sendpair("y", 1, MSG_OOB); /* TCP drops "x" at this moment. */ 696 epollpair(true); 697 698 setinlinepair(); 699 700 tcp_incompliant { 701 siocatmarkpair(false); 702 703 recvpair("x", 1, 1, 0); /* TCP recv()s "y". */ 704 epollpair(true); 705 siocatmarkpair(true); 706 707 recvpair("y", 1, 1, 0); /* TCP returns -EAGAIN. */ 708 epollpair(false); 709 siocatmarkpair(false); 710 } 711 } 712 713 TEST_F(msg_oob, inline_ex_oob_siocatmark) 714 { 715 sendpair("hello", 5, MSG_OOB); 716 epollpair(true); 717 siocatmarkpair(false); 718 719 recvpair("o", 1, 1, MSG_OOB); 720 epollpair(false); 721 siocatmarkpair(false); 722 723 setinlinepair(); 724 725 sendpair("world", 5, MSG_OOB); 726 epollpair(true); 727 siocatmarkpair(false); 728 729 recvpair("hell", 4, 4, 0); /* Intentionally stop at ex-OOB. */ 730 epollpair(true); 731 siocatmarkpair(false); 732 } 733 734 TEST_HARNESS_MAIN 735