1 // SPDX-License-Identifier: GPL-2.0 2 3 #define _GNU_SOURCE 4 5 #include <errno.h> 6 #include <limits.h> 7 #include <fcntl.h> 8 #include <string.h> 9 #include <stdbool.h> 10 #include <stdint.h> 11 #include <stdio.h> 12 #include <stdlib.h> 13 #include <strings.h> 14 #include <signal.h> 15 #include <unistd.h> 16 17 #include <sys/poll.h> 18 #include <sys/sendfile.h> 19 #include <sys/stat.h> 20 #include <sys/socket.h> 21 #include <sys/types.h> 22 #include <sys/mman.h> 23 24 #include <netdb.h> 25 #include <netinet/in.h> 26 27 #include <linux/tcp.h> 28 29 extern int optind; 30 31 #ifndef IPPROTO_MPTCP 32 #define IPPROTO_MPTCP 262 33 #endif 34 #ifndef TCP_ULP 35 #define TCP_ULP 31 36 #endif 37 38 static int poll_timeout = 10 * 1000; 39 static bool listen_mode; 40 static bool quit; 41 42 enum cfg_mode { 43 CFG_MODE_POLL, 44 CFG_MODE_MMAP, 45 CFG_MODE_SENDFILE, 46 }; 47 48 static enum cfg_mode cfg_mode = CFG_MODE_POLL; 49 static const char *cfg_host; 50 static const char *cfg_port = "12000"; 51 static int cfg_sock_proto = IPPROTO_MPTCP; 52 static bool tcpulp_audit; 53 static int pf = AF_INET; 54 static int cfg_sndbuf; 55 static int cfg_rcvbuf; 56 static bool cfg_join; 57 static bool cfg_remove; 58 static int cfg_wait; 59 60 static void die_usage(void) 61 { 62 fprintf(stderr, "Usage: mptcp_connect [-6] [-u] [-s MPTCP|TCP] [-p port] [-m mode]" 63 "[-l] [-w sec] connect_address\n"); 64 fprintf(stderr, "\t-6 use ipv6\n"); 65 fprintf(stderr, "\t-t num -- set poll timeout to num\n"); 66 fprintf(stderr, "\t-S num -- set SO_SNDBUF to num\n"); 67 fprintf(stderr, "\t-R num -- set SO_RCVBUF to num\n"); 68 fprintf(stderr, "\t-p num -- use port num\n"); 69 fprintf(stderr, "\t-s [MPTCP|TCP] -- use mptcp(default) or tcp sockets\n"); 70 fprintf(stderr, "\t-m [poll|mmap|sendfile] -- use poll(default)/mmap+write/sendfile\n"); 71 fprintf(stderr, "\t-u -- check mptcp ulp\n"); 72 fprintf(stderr, "\t-w num -- wait num sec before closing the socket\n"); 73 exit(1); 74 } 75 76 static void handle_signal(int nr) 77 { 78 quit = true; 79 } 80 81 static const char *getxinfo_strerr(int err) 82 { 83 if (err == EAI_SYSTEM) 84 return strerror(errno); 85 86 return gai_strerror(err); 87 } 88 89 static void xgetnameinfo(const struct sockaddr *addr, socklen_t addrlen, 90 char *host, socklen_t hostlen, 91 char *serv, socklen_t servlen) 92 { 93 int flags = NI_NUMERICHOST | NI_NUMERICSERV; 94 int err = getnameinfo(addr, addrlen, host, hostlen, serv, servlen, 95 flags); 96 97 if (err) { 98 const char *errstr = getxinfo_strerr(err); 99 100 fprintf(stderr, "Fatal: getnameinfo: %s\n", errstr); 101 exit(1); 102 } 103 } 104 105 static void xgetaddrinfo(const char *node, const char *service, 106 const struct addrinfo *hints, 107 struct addrinfo **res) 108 { 109 int err = getaddrinfo(node, service, hints, res); 110 111 if (err) { 112 const char *errstr = getxinfo_strerr(err); 113 114 fprintf(stderr, "Fatal: getaddrinfo(%s:%s): %s\n", 115 node ? node : "", service ? service : "", errstr); 116 exit(1); 117 } 118 } 119 120 static void set_rcvbuf(int fd, unsigned int size) 121 { 122 int err; 123 124 err = setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)); 125 if (err) { 126 perror("set SO_RCVBUF"); 127 exit(1); 128 } 129 } 130 131 static void set_sndbuf(int fd, unsigned int size) 132 { 133 int err; 134 135 err = setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size)); 136 if (err) { 137 perror("set SO_SNDBUF"); 138 exit(1); 139 } 140 } 141 142 static int sock_listen_mptcp(const char * const listenaddr, 143 const char * const port) 144 { 145 int sock; 146 struct addrinfo hints = { 147 .ai_protocol = IPPROTO_TCP, 148 .ai_socktype = SOCK_STREAM, 149 .ai_flags = AI_PASSIVE | AI_NUMERICHOST 150 }; 151 152 hints.ai_family = pf; 153 154 struct addrinfo *a, *addr; 155 int one = 1; 156 157 xgetaddrinfo(listenaddr, port, &hints, &addr); 158 hints.ai_family = pf; 159 160 for (a = addr; a; a = a->ai_next) { 161 sock = socket(a->ai_family, a->ai_socktype, cfg_sock_proto); 162 if (sock < 0) 163 continue; 164 165 if (-1 == setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, 166 sizeof(one))) 167 perror("setsockopt"); 168 169 if (bind(sock, a->ai_addr, a->ai_addrlen) == 0) 170 break; /* success */ 171 172 perror("bind"); 173 close(sock); 174 sock = -1; 175 } 176 177 freeaddrinfo(addr); 178 179 if (sock < 0) { 180 fprintf(stderr, "Could not create listen socket\n"); 181 return sock; 182 } 183 184 if (listen(sock, 20)) { 185 perror("listen"); 186 close(sock); 187 return -1; 188 } 189 190 return sock; 191 } 192 193 static bool sock_test_tcpulp(const char * const remoteaddr, 194 const char * const port) 195 { 196 struct addrinfo hints = { 197 .ai_protocol = IPPROTO_TCP, 198 .ai_socktype = SOCK_STREAM, 199 }; 200 struct addrinfo *a, *addr; 201 int sock = -1, ret = 0; 202 bool test_pass = false; 203 204 hints.ai_family = AF_INET; 205 206 xgetaddrinfo(remoteaddr, port, &hints, &addr); 207 for (a = addr; a; a = a->ai_next) { 208 sock = socket(a->ai_family, a->ai_socktype, IPPROTO_TCP); 209 if (sock < 0) { 210 perror("socket"); 211 continue; 212 } 213 ret = setsockopt(sock, IPPROTO_TCP, TCP_ULP, "mptcp", 214 sizeof("mptcp")); 215 if (ret == -1 && errno == EOPNOTSUPP) 216 test_pass = true; 217 close(sock); 218 219 if (test_pass) 220 break; 221 if (!ret) 222 fprintf(stderr, 223 "setsockopt(TCP_ULP) returned 0\n"); 224 else 225 perror("setsockopt(TCP_ULP)"); 226 } 227 return test_pass; 228 } 229 230 static int sock_connect_mptcp(const char * const remoteaddr, 231 const char * const port, int proto) 232 { 233 struct addrinfo hints = { 234 .ai_protocol = IPPROTO_TCP, 235 .ai_socktype = SOCK_STREAM, 236 }; 237 struct addrinfo *a, *addr; 238 int sock = -1; 239 240 hints.ai_family = pf; 241 242 xgetaddrinfo(remoteaddr, port, &hints, &addr); 243 for (a = addr; a; a = a->ai_next) { 244 sock = socket(a->ai_family, a->ai_socktype, proto); 245 if (sock < 0) { 246 perror("socket"); 247 continue; 248 } 249 250 if (connect(sock, a->ai_addr, a->ai_addrlen) == 0) 251 break; /* success */ 252 253 perror("connect()"); 254 close(sock); 255 sock = -1; 256 } 257 258 freeaddrinfo(addr); 259 return sock; 260 } 261 262 static size_t do_rnd_write(const int fd, char *buf, const size_t len) 263 { 264 static bool first = true; 265 unsigned int do_w; 266 ssize_t bw; 267 268 do_w = rand() & 0xffff; 269 if (do_w == 0 || do_w > len) 270 do_w = len; 271 272 if (cfg_join && first && do_w > 100) 273 do_w = 100; 274 275 if (cfg_remove && do_w > 50) 276 do_w = 50; 277 278 bw = write(fd, buf, do_w); 279 if (bw < 0) 280 perror("write"); 281 282 /* let the join handshake complete, before going on */ 283 if (cfg_join && first) { 284 usleep(200000); 285 first = false; 286 } 287 288 if (cfg_remove) 289 usleep(200000); 290 291 return bw; 292 } 293 294 static size_t do_write(const int fd, char *buf, const size_t len) 295 { 296 size_t offset = 0; 297 298 while (offset < len) { 299 size_t written; 300 ssize_t bw; 301 302 bw = write(fd, buf + offset, len - offset); 303 if (bw < 0) { 304 perror("write"); 305 return 0; 306 } 307 308 written = (size_t)bw; 309 offset += written; 310 } 311 312 return offset; 313 } 314 315 static ssize_t do_rnd_read(const int fd, char *buf, const size_t len) 316 { 317 size_t cap = rand(); 318 319 cap &= 0xffff; 320 321 if (cap == 0) 322 cap = 1; 323 else if (cap > len) 324 cap = len; 325 326 return read(fd, buf, cap); 327 } 328 329 static void set_nonblock(int fd) 330 { 331 int flags = fcntl(fd, F_GETFL); 332 333 if (flags == -1) 334 return; 335 336 fcntl(fd, F_SETFL, flags | O_NONBLOCK); 337 } 338 339 static int copyfd_io_poll(int infd, int peerfd, int outfd) 340 { 341 struct pollfd fds = { 342 .fd = peerfd, 343 .events = POLLIN | POLLOUT, 344 }; 345 unsigned int woff = 0, wlen = 0; 346 char wbuf[8192]; 347 348 set_nonblock(peerfd); 349 350 for (;;) { 351 char rbuf[8192]; 352 ssize_t len; 353 354 if (fds.events == 0) 355 break; 356 357 switch (poll(&fds, 1, poll_timeout)) { 358 case -1: 359 if (errno == EINTR) 360 continue; 361 perror("poll"); 362 return 1; 363 case 0: 364 fprintf(stderr, "%s: poll timed out (events: " 365 "POLLIN %u, POLLOUT %u)\n", __func__, 366 fds.events & POLLIN, fds.events & POLLOUT); 367 return 2; 368 } 369 370 if (fds.revents & POLLIN) { 371 len = do_rnd_read(peerfd, rbuf, sizeof(rbuf)); 372 if (len == 0) { 373 /* no more data to receive: 374 * peer has closed its write side 375 */ 376 fds.events &= ~POLLIN; 377 378 if ((fds.events & POLLOUT) == 0) 379 /* and nothing more to send */ 380 break; 381 382 /* Else, still have data to transmit */ 383 } else if (len < 0) { 384 perror("read"); 385 return 3; 386 } 387 388 do_write(outfd, rbuf, len); 389 } 390 391 if (fds.revents & POLLOUT) { 392 if (wlen == 0) { 393 woff = 0; 394 wlen = read(infd, wbuf, sizeof(wbuf)); 395 } 396 397 if (wlen > 0) { 398 ssize_t bw; 399 400 bw = do_rnd_write(peerfd, wbuf + woff, wlen); 401 if (bw < 0) 402 return 111; 403 404 woff += bw; 405 wlen -= bw; 406 } else if (wlen == 0) { 407 /* We have no more data to send. */ 408 fds.events &= ~POLLOUT; 409 410 if ((fds.events & POLLIN) == 0) 411 /* ... and peer also closed already */ 412 break; 413 414 /* ... but we still receive. 415 * Close our write side, ev. give some time 416 * for address notification and/or checking 417 * the current status 418 */ 419 if (cfg_wait) 420 usleep(cfg_wait); 421 shutdown(peerfd, SHUT_WR); 422 } else { 423 if (errno == EINTR) 424 continue; 425 perror("read"); 426 return 4; 427 } 428 } 429 430 if (fds.revents & (POLLERR | POLLNVAL)) { 431 fprintf(stderr, "Unexpected revents: " 432 "POLLERR/POLLNVAL(%x)\n", fds.revents); 433 return 5; 434 } 435 } 436 437 /* leave some time for late join/announce */ 438 if (cfg_join || cfg_remove) 439 usleep(cfg_wait); 440 441 close(peerfd); 442 return 0; 443 } 444 445 static int do_recvfile(int infd, int outfd) 446 { 447 ssize_t r; 448 449 do { 450 char buf[16384]; 451 452 r = do_rnd_read(infd, buf, sizeof(buf)); 453 if (r > 0) { 454 if (write(outfd, buf, r) != r) 455 break; 456 } else if (r < 0) { 457 perror("read"); 458 } 459 } while (r > 0); 460 461 return (int)r; 462 } 463 464 static int do_mmap(int infd, int outfd, unsigned int size) 465 { 466 char *inbuf = mmap(NULL, size, PROT_READ, MAP_SHARED, infd, 0); 467 ssize_t ret = 0, off = 0; 468 size_t rem; 469 470 if (inbuf == MAP_FAILED) { 471 perror("mmap"); 472 return 1; 473 } 474 475 rem = size; 476 477 while (rem > 0) { 478 ret = write(outfd, inbuf + off, rem); 479 480 if (ret < 0) { 481 perror("write"); 482 break; 483 } 484 485 off += ret; 486 rem -= ret; 487 } 488 489 munmap(inbuf, size); 490 return rem; 491 } 492 493 static int get_infd_size(int fd) 494 { 495 struct stat sb; 496 ssize_t count; 497 int err; 498 499 err = fstat(fd, &sb); 500 if (err < 0) { 501 perror("fstat"); 502 return -1; 503 } 504 505 if ((sb.st_mode & S_IFMT) != S_IFREG) { 506 fprintf(stderr, "%s: stdin is not a regular file\n", __func__); 507 return -2; 508 } 509 510 count = sb.st_size; 511 if (count > INT_MAX) { 512 fprintf(stderr, "File too large: %zu\n", count); 513 return -3; 514 } 515 516 return (int)count; 517 } 518 519 static int do_sendfile(int infd, int outfd, unsigned int count) 520 { 521 while (count > 0) { 522 ssize_t r; 523 524 r = sendfile(outfd, infd, NULL, count); 525 if (r < 0) { 526 perror("sendfile"); 527 return 3; 528 } 529 530 count -= r; 531 } 532 533 return 0; 534 } 535 536 static int copyfd_io_mmap(int infd, int peerfd, int outfd, 537 unsigned int size) 538 { 539 int err; 540 541 if (listen_mode) { 542 err = do_recvfile(peerfd, outfd); 543 if (err) 544 return err; 545 546 err = do_mmap(infd, peerfd, size); 547 } else { 548 err = do_mmap(infd, peerfd, size); 549 if (err) 550 return err; 551 552 shutdown(peerfd, SHUT_WR); 553 554 err = do_recvfile(peerfd, outfd); 555 } 556 557 return err; 558 } 559 560 static int copyfd_io_sendfile(int infd, int peerfd, int outfd, 561 unsigned int size) 562 { 563 int err; 564 565 if (listen_mode) { 566 err = do_recvfile(peerfd, outfd); 567 if (err) 568 return err; 569 570 err = do_sendfile(infd, peerfd, size); 571 } else { 572 err = do_sendfile(infd, peerfd, size); 573 if (err) 574 return err; 575 err = do_recvfile(peerfd, outfd); 576 } 577 578 return err; 579 } 580 581 static int copyfd_io(int infd, int peerfd, int outfd) 582 { 583 int file_size; 584 585 switch (cfg_mode) { 586 case CFG_MODE_POLL: 587 return copyfd_io_poll(infd, peerfd, outfd); 588 case CFG_MODE_MMAP: 589 file_size = get_infd_size(infd); 590 if (file_size < 0) 591 return file_size; 592 return copyfd_io_mmap(infd, peerfd, outfd, file_size); 593 case CFG_MODE_SENDFILE: 594 file_size = get_infd_size(infd); 595 if (file_size < 0) 596 return file_size; 597 return copyfd_io_sendfile(infd, peerfd, outfd, file_size); 598 } 599 600 fprintf(stderr, "Invalid mode %d\n", cfg_mode); 601 602 die_usage(); 603 return 1; 604 } 605 606 static void check_sockaddr(int pf, struct sockaddr_storage *ss, 607 socklen_t salen) 608 { 609 struct sockaddr_in6 *sin6; 610 struct sockaddr_in *sin; 611 socklen_t wanted_size = 0; 612 613 switch (pf) { 614 case AF_INET: 615 wanted_size = sizeof(*sin); 616 sin = (void *)ss; 617 if (!sin->sin_port) 618 fprintf(stderr, "accept: something wrong: ip connection from port 0"); 619 break; 620 case AF_INET6: 621 wanted_size = sizeof(*sin6); 622 sin6 = (void *)ss; 623 if (!sin6->sin6_port) 624 fprintf(stderr, "accept: something wrong: ipv6 connection from port 0"); 625 break; 626 default: 627 fprintf(stderr, "accept: Unknown pf %d, salen %u\n", pf, salen); 628 return; 629 } 630 631 if (salen != wanted_size) 632 fprintf(stderr, "accept: size mismatch, got %d expected %d\n", 633 (int)salen, wanted_size); 634 635 if (ss->ss_family != pf) 636 fprintf(stderr, "accept: pf mismatch, expect %d, ss_family is %d\n", 637 (int)ss->ss_family, pf); 638 } 639 640 static void check_getpeername(int fd, struct sockaddr_storage *ss, socklen_t salen) 641 { 642 struct sockaddr_storage peerss; 643 socklen_t peersalen = sizeof(peerss); 644 645 if (getpeername(fd, (struct sockaddr *)&peerss, &peersalen) < 0) { 646 perror("getpeername"); 647 return; 648 } 649 650 if (peersalen != salen) { 651 fprintf(stderr, "%s: %d vs %d\n", __func__, peersalen, salen); 652 return; 653 } 654 655 if (memcmp(ss, &peerss, peersalen)) { 656 char a[INET6_ADDRSTRLEN]; 657 char b[INET6_ADDRSTRLEN]; 658 char c[INET6_ADDRSTRLEN]; 659 char d[INET6_ADDRSTRLEN]; 660 661 xgetnameinfo((struct sockaddr *)ss, salen, 662 a, sizeof(a), b, sizeof(b)); 663 664 xgetnameinfo((struct sockaddr *)&peerss, peersalen, 665 c, sizeof(c), d, sizeof(d)); 666 667 fprintf(stderr, "%s: memcmp failure: accept %s vs peername %s, %s vs %s salen %d vs %d\n", 668 __func__, a, c, b, d, peersalen, salen); 669 } 670 } 671 672 static void check_getpeername_connect(int fd) 673 { 674 struct sockaddr_storage ss; 675 socklen_t salen = sizeof(ss); 676 char a[INET6_ADDRSTRLEN]; 677 char b[INET6_ADDRSTRLEN]; 678 679 if (getpeername(fd, (struct sockaddr *)&ss, &salen) < 0) { 680 perror("getpeername"); 681 return; 682 } 683 684 xgetnameinfo((struct sockaddr *)&ss, salen, 685 a, sizeof(a), b, sizeof(b)); 686 687 if (strcmp(cfg_host, a) || strcmp(cfg_port, b)) 688 fprintf(stderr, "%s: %s vs %s, %s vs %s\n", __func__, 689 cfg_host, a, cfg_port, b); 690 } 691 692 static void maybe_close(int fd) 693 { 694 unsigned int r = rand(); 695 696 if (!(cfg_join || cfg_remove) && (r & 1)) 697 close(fd); 698 } 699 700 int main_loop_s(int listensock) 701 { 702 struct sockaddr_storage ss; 703 struct pollfd polls; 704 socklen_t salen; 705 int remotesock; 706 707 polls.fd = listensock; 708 polls.events = POLLIN; 709 710 switch (poll(&polls, 1, poll_timeout)) { 711 case -1: 712 perror("poll"); 713 return 1; 714 case 0: 715 fprintf(stderr, "%s: timed out\n", __func__); 716 close(listensock); 717 return 2; 718 } 719 720 salen = sizeof(ss); 721 remotesock = accept(listensock, (struct sockaddr *)&ss, &salen); 722 if (remotesock >= 0) { 723 maybe_close(listensock); 724 check_sockaddr(pf, &ss, salen); 725 check_getpeername(remotesock, &ss, salen); 726 727 return copyfd_io(0, remotesock, 1); 728 } 729 730 perror("accept"); 731 732 return 1; 733 } 734 735 static void init_rng(void) 736 { 737 int fd = open("/dev/urandom", O_RDONLY); 738 unsigned int foo; 739 740 if (fd > 0) { 741 int ret = read(fd, &foo, sizeof(foo)); 742 743 if (ret < 0) 744 srand(fd + foo); 745 close(fd); 746 } 747 748 srand(foo); 749 } 750 751 int main_loop(void) 752 { 753 int fd; 754 755 /* listener is ready. */ 756 fd = sock_connect_mptcp(cfg_host, cfg_port, cfg_sock_proto); 757 if (fd < 0) 758 return 2; 759 760 check_getpeername_connect(fd); 761 762 if (cfg_rcvbuf) 763 set_rcvbuf(fd, cfg_rcvbuf); 764 if (cfg_sndbuf) 765 set_sndbuf(fd, cfg_sndbuf); 766 767 return copyfd_io(0, fd, 1); 768 } 769 770 int parse_proto(const char *proto) 771 { 772 if (!strcasecmp(proto, "MPTCP")) 773 return IPPROTO_MPTCP; 774 if (!strcasecmp(proto, "TCP")) 775 return IPPROTO_TCP; 776 777 fprintf(stderr, "Unknown protocol: %s\n.", proto); 778 die_usage(); 779 780 /* silence compiler warning */ 781 return 0; 782 } 783 784 int parse_mode(const char *mode) 785 { 786 if (!strcasecmp(mode, "poll")) 787 return CFG_MODE_POLL; 788 if (!strcasecmp(mode, "mmap")) 789 return CFG_MODE_MMAP; 790 if (!strcasecmp(mode, "sendfile")) 791 return CFG_MODE_SENDFILE; 792 793 fprintf(stderr, "Unknown test mode: %s\n", mode); 794 fprintf(stderr, "Supported modes are:\n"); 795 fprintf(stderr, "\t\t\"poll\" - interleaved read/write using poll()\n"); 796 fprintf(stderr, "\t\t\"mmap\" - send entire input file (mmap+write), then read response (-l will read input first)\n"); 797 fprintf(stderr, "\t\t\"sendfile\" - send entire input file (sendfile), then read response (-l will read input first)\n"); 798 799 die_usage(); 800 801 /* silence compiler warning */ 802 return 0; 803 } 804 805 static int parse_int(const char *size) 806 { 807 unsigned long s; 808 809 errno = 0; 810 811 s = strtoul(size, NULL, 0); 812 813 if (errno) { 814 fprintf(stderr, "Invalid sndbuf size %s (%s)\n", 815 size, strerror(errno)); 816 die_usage(); 817 } 818 819 if (s > INT_MAX) { 820 fprintf(stderr, "Invalid sndbuf size %s (%s)\n", 821 size, strerror(ERANGE)); 822 die_usage(); 823 } 824 825 return (int)s; 826 } 827 828 static void parse_opts(int argc, char **argv) 829 { 830 int c; 831 832 while ((c = getopt(argc, argv, "6jrlp:s:hut:m:S:R:w:")) != -1) { 833 switch (c) { 834 case 'j': 835 cfg_join = true; 836 cfg_mode = CFG_MODE_POLL; 837 cfg_wait = 400000; 838 break; 839 case 'r': 840 cfg_remove = true; 841 cfg_mode = CFG_MODE_POLL; 842 cfg_wait = 400000; 843 break; 844 case 'l': 845 listen_mode = true; 846 break; 847 case 'p': 848 cfg_port = optarg; 849 break; 850 case 's': 851 cfg_sock_proto = parse_proto(optarg); 852 break; 853 case 'h': 854 die_usage(); 855 break; 856 case 'u': 857 tcpulp_audit = true; 858 break; 859 case '6': 860 pf = AF_INET6; 861 break; 862 case 't': 863 poll_timeout = atoi(optarg) * 1000; 864 if (poll_timeout <= 0) 865 poll_timeout = -1; 866 break; 867 case 'm': 868 cfg_mode = parse_mode(optarg); 869 break; 870 case 'S': 871 cfg_sndbuf = parse_int(optarg); 872 break; 873 case 'R': 874 cfg_rcvbuf = parse_int(optarg); 875 break; 876 case 'w': 877 cfg_wait = atoi(optarg)*1000000; 878 break; 879 } 880 } 881 882 if (optind + 1 != argc) 883 die_usage(); 884 cfg_host = argv[optind]; 885 886 if (strchr(cfg_host, ':')) 887 pf = AF_INET6; 888 } 889 890 int main(int argc, char *argv[]) 891 { 892 init_rng(); 893 894 signal(SIGUSR1, handle_signal); 895 parse_opts(argc, argv); 896 897 if (tcpulp_audit) 898 return sock_test_tcpulp(cfg_host, cfg_port) ? 0 : 1; 899 900 if (listen_mode) { 901 int fd = sock_listen_mptcp(cfg_host, cfg_port); 902 903 if (fd < 0) 904 return 1; 905 906 if (cfg_rcvbuf) 907 set_rcvbuf(fd, cfg_rcvbuf); 908 if (cfg_sndbuf) 909 set_sndbuf(fd, cfg_sndbuf); 910 911 return main_loop_s(fd); 912 } 913 914 return main_loop(); 915 } 916