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