1 /* 2 * iperf, Copyright (c) 2014-2018, The Regents of the University of 3 * California, through Lawrence Berkeley National Laboratory (subject 4 * to receipt of any required approvals from the U.S. Dept. of 5 * Energy). All rights reserved. 6 * 7 * If you have questions about your rights to use or distribute this 8 * software, please contact Berkeley Lab's Technology Transfer 9 * Department at [email protected]. 10 * 11 * NOTICE. This software is owned by the U.S. Department of Energy. 12 * As such, the U.S. Government has been granted for itself and others 13 * acting on its behalf a paid-up, nonexclusive, irrevocable, 14 * worldwide license in the Software to reproduce, prepare derivative 15 * works, and perform publicly and display publicly. Beginning five 16 * (5) years after the date permission to assert copyright is obtained 17 * from the U.S. Department of Energy, and subject to any subsequent 18 * five (5) year renewals, the U.S. Government is granted for itself 19 * and others acting on its behalf a paid-up, nonexclusive, 20 * irrevocable, worldwide license in the Software to reproduce, 21 * prepare derivative works, distribute copies to the public, perform 22 * publicly and display publicly, and to permit others to do so. 23 * 24 * This code is distributed under a BSD style license, see the LICENSE 25 * file for complete information. 26 */ 27 #include "iperf_config.h" 28 29 #include <stdio.h> 30 #include <stdlib.h> 31 #include <string.h> 32 #include <errno.h> 33 #include <unistd.h> 34 #include <sys/socket.h> 35 #include <sys/types.h> 36 #include <netinet/in.h> 37 #include <netdb.h> 38 #include <sys/time.h> 39 #include <sys/select.h> 40 41 #ifdef HAVE_NETINET_SCTP_H 42 #include <netinet/sctp.h> 43 #endif /* HAVE_NETINET_SCTP_H */ 44 45 #include "iperf.h" 46 #include "iperf_api.h" 47 #include "iperf_sctp.h" 48 #include "net.h" 49 50 51 52 /* iperf_sctp_recv 53 * 54 * receives the data for SCTP 55 */ 56 int 57 iperf_sctp_recv(struct iperf_stream *sp) 58 { 59 #if defined(HAVE_SCTP) 60 int r; 61 62 r = Nread(sp->socket, sp->buffer, sp->settings->blksize, Psctp); 63 if (r < 0) 64 return r; 65 66 /* Only count bytes received while we're in the correct state. */ 67 if (sp->test->state == TEST_RUNNING) { 68 sp->result->bytes_received += r; 69 sp->result->bytes_received_this_interval += r; 70 } 71 else { 72 if (sp->test->debug) 73 printf("Late receive, state = %d\n", sp->test->state); 74 } 75 76 return r; 77 #else 78 i_errno = IENOSCTP; 79 return -1; 80 #endif /* HAVE_SCTP */ 81 } 82 83 84 /* iperf_sctp_send 85 * 86 * sends the data for SCTP 87 */ 88 int 89 iperf_sctp_send(struct iperf_stream *sp) 90 { 91 #if defined(HAVE_SCTP) 92 int r; 93 94 r = Nwrite(sp->socket, sp->buffer, sp->settings->blksize, Psctp); 95 if (r < 0) 96 return r; 97 98 sp->result->bytes_sent += r; 99 sp->result->bytes_sent_this_interval += r; 100 101 return r; 102 #else 103 i_errno = IENOSCTP; 104 return -1; 105 #endif /* HAVE_SCTP */ 106 } 107 108 109 110 /* iperf_sctp_accept 111 * 112 * accept a new SCTP stream connection 113 */ 114 int 115 iperf_sctp_accept(struct iperf_test * test) 116 { 117 #if defined(HAVE_SCTP) 118 int s; 119 signed char rbuf = ACCESS_DENIED; 120 char cookie[COOKIE_SIZE]; 121 socklen_t len; 122 struct sockaddr_storage addr; 123 124 len = sizeof(addr); 125 s = accept(test->listener, (struct sockaddr *) &addr, &len); 126 if (s < 0) { 127 i_errno = IESTREAMCONNECT; 128 return -1; 129 } 130 131 if (Nread(s, cookie, COOKIE_SIZE, Psctp) < 0) { 132 i_errno = IERECVCOOKIE; 133 return -1; 134 } 135 136 if (strcmp(test->cookie, cookie) != 0) { 137 if (Nwrite(s, (char*) &rbuf, sizeof(rbuf), Psctp) < 0) { 138 i_errno = IESENDMESSAGE; 139 return -1; 140 } 141 close(s); 142 } 143 144 return s; 145 #else 146 i_errno = IENOSCTP; 147 return -1; 148 #endif /* HAVE_SCTP */ 149 } 150 151 152 /* iperf_sctp_listen 153 * 154 * start up a listener for SCTP stream connections 155 */ 156 int 157 iperf_sctp_listen(struct iperf_test *test) 158 { 159 #if defined(HAVE_SCTP) 160 struct addrinfo hints, *res; 161 char portstr[6]; 162 int s, opt, saved_errno; 163 164 close(test->listener); 165 166 snprintf(portstr, 6, "%d", test->server_port); 167 memset(&hints, 0, sizeof(hints)); 168 hints.ai_family = (test->settings->domain == AF_UNSPEC ? AF_INET6 : test->settings->domain); 169 hints.ai_socktype = SOCK_STREAM; 170 hints.ai_flags = AI_PASSIVE; 171 if (getaddrinfo(test->bind_address, portstr, &hints, &res) != 0) { 172 i_errno = IESTREAMLISTEN; 173 return -1; 174 } 175 176 if ((s = socket(res->ai_family, SOCK_STREAM, IPPROTO_SCTP)) < 0) { 177 freeaddrinfo(res); 178 i_errno = IESTREAMLISTEN; 179 return -1; 180 } 181 182 #if defined(IPV6_V6ONLY) && !defined(__OpenBSD__) 183 if (test->settings->domain == AF_UNSPEC || test->settings->domain == AF_INET6) { 184 if (test->settings->domain == AF_UNSPEC) 185 opt = 0; 186 else if (test->settings->domain == AF_INET6) 187 opt = 1; 188 if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, 189 (char *) &opt, sizeof(opt)) < 0) { 190 saved_errno = errno; 191 close(s); 192 freeaddrinfo(res); 193 errno = saved_errno; 194 i_errno = IEPROTOCOL; 195 return -1; 196 } 197 } 198 #endif /* IPV6_V6ONLY */ 199 200 opt = 1; 201 if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) { 202 saved_errno = errno; 203 close(s); 204 freeaddrinfo(res); 205 errno = saved_errno; 206 i_errno = IEREUSEADDR; 207 return -1; 208 } 209 210 /* servers must call sctp_bindx() _instead_ of bind() */ 211 if (!TAILQ_EMPTY(&test->xbind_addrs)) { 212 freeaddrinfo(res); 213 if (iperf_sctp_bindx(test, s, IPERF_SCTP_SERVER)) 214 return -1; 215 } else 216 if (bind(s, (struct sockaddr *) res->ai_addr, res->ai_addrlen) < 0) { 217 saved_errno = errno; 218 close(s); 219 freeaddrinfo(res); 220 errno = saved_errno; 221 i_errno = IESTREAMLISTEN; 222 return -1; 223 } 224 225 freeaddrinfo(res); 226 227 if (listen(s, 5) < 0) { 228 i_errno = IESTREAMLISTEN; 229 return -1; 230 } 231 232 test->listener = s; 233 234 return s; 235 #else 236 i_errno = IENOSCTP; 237 return -1; 238 #endif /* HAVE_SCTP */ 239 } 240 241 242 /* iperf_sctp_connect 243 * 244 * connect to a SCTP stream listener 245 */ 246 int 247 iperf_sctp_connect(struct iperf_test *test) 248 { 249 #if defined(HAVE_SCTP) 250 int s, opt, saved_errno; 251 char portstr[6]; 252 struct addrinfo hints, *local_res, *server_res; 253 254 if (test->bind_address) { 255 memset(&hints, 0, sizeof(hints)); 256 hints.ai_family = test->settings->domain; 257 hints.ai_socktype = SOCK_STREAM; 258 if (getaddrinfo(test->bind_address, NULL, &hints, &local_res) != 0) { 259 i_errno = IESTREAMCONNECT; 260 return -1; 261 } 262 } 263 264 memset(&hints, 0, sizeof(hints)); 265 hints.ai_family = test->settings->domain; 266 hints.ai_socktype = SOCK_STREAM; 267 snprintf(portstr, sizeof(portstr), "%d", test->server_port); 268 if (getaddrinfo(test->server_hostname, portstr, &hints, &server_res) != 0) { 269 if (test->bind_address) 270 freeaddrinfo(local_res); 271 i_errno = IESTREAMCONNECT; 272 return -1; 273 } 274 275 s = socket(server_res->ai_family, SOCK_STREAM, IPPROTO_SCTP); 276 if (s < 0) { 277 if (test->bind_address) 278 freeaddrinfo(local_res); 279 freeaddrinfo(server_res); 280 i_errno = IESTREAMCONNECT; 281 return -1; 282 } 283 284 /* 285 * Various ways to bind the local end of the connection. 286 * 1. --bind (with or without --cport). 287 */ 288 if (test->bind_address) { 289 struct sockaddr_in *lcladdr; 290 lcladdr = (struct sockaddr_in *)local_res->ai_addr; 291 lcladdr->sin_port = htons(test->bind_port); 292 293 if (bind(s, (struct sockaddr *) local_res->ai_addr, local_res->ai_addrlen) < 0) { 294 saved_errno = errno; 295 close(s); 296 freeaddrinfo(local_res); 297 freeaddrinfo(server_res); 298 errno = saved_errno; 299 i_errno = IESTREAMCONNECT; 300 return -1; 301 } 302 freeaddrinfo(local_res); 303 } 304 /* --cport, no --bind */ 305 else if (test->bind_port) { 306 size_t addrlen; 307 struct sockaddr_storage lcl; 308 309 /* IPv4 */ 310 if (server_res->ai_family == AF_INET) { 311 struct sockaddr_in *lcladdr = (struct sockaddr_in *) &lcl; 312 lcladdr->sin_family = AF_INET; 313 lcladdr->sin_port = htons(test->bind_port); 314 lcladdr->sin_addr.s_addr = INADDR_ANY; 315 addrlen = sizeof(struct sockaddr_in); 316 } 317 /* IPv6 */ 318 else if (server_res->ai_family == AF_INET6) { 319 struct sockaddr_in6 *lcladdr = (struct sockaddr_in6 *) &lcl; 320 lcladdr->sin6_family = AF_INET6; 321 lcladdr->sin6_port = htons(test->bind_port); 322 lcladdr->sin6_addr = in6addr_any; 323 addrlen = sizeof(struct sockaddr_in6); 324 } 325 /* Unknown protocol */ 326 else { 327 saved_errno = errno; 328 close(s); 329 freeaddrinfo(server_res); 330 errno = saved_errno; 331 i_errno = IEPROTOCOL; 332 return -1; 333 } 334 335 if (bind(s, (struct sockaddr *) &lcl, addrlen) < 0) { 336 saved_errno = errno; 337 close(s); 338 freeaddrinfo(server_res); 339 errno = saved_errno; 340 i_errno = IESTREAMCONNECT; 341 return -1; 342 } 343 } 344 345 if (test->no_delay != 0) { 346 opt = 1; 347 if (setsockopt(s, IPPROTO_SCTP, SCTP_NODELAY, &opt, sizeof(opt)) < 0) { 348 saved_errno = errno; 349 close(s); 350 freeaddrinfo(server_res); 351 errno = saved_errno; 352 i_errno = IESETNODELAY; 353 return -1; 354 } 355 } 356 357 if ((test->settings->mss >= 512 && test->settings->mss <= 131072)) { 358 359 /* 360 * Some platforms use a struct sctp_assoc_value as the 361 * argument to SCTP_MAXSEG. Other (older API implementations) 362 * take an int. FreeBSD 10 and CentOS 6 support SCTP_MAXSEG, 363 * but OpenSolaris 11 doesn't. 364 */ 365 #ifdef HAVE_STRUCT_SCTP_ASSOC_VALUE 366 struct sctp_assoc_value av; 367 368 /* 369 * Some platforms support SCTP_FUTURE_ASSOC, others need to 370 * (equivalently) do 0 here. FreeBSD 10 is an example of the 371 * former, CentOS 6 Linux is an example of the latter. 372 */ 373 #ifdef SCTP_FUTURE_ASSOC 374 av.assoc_id = SCTP_FUTURE_ASSOC; 375 #else 376 av.assoc_id = 0; 377 #endif 378 av.assoc_value = test->settings->mss; 379 380 if (setsockopt(s, IPPROTO_SCTP, SCTP_MAXSEG, &av, sizeof(av)) < 0) { 381 saved_errno = errno; 382 close(s); 383 freeaddrinfo(server_res); 384 errno = saved_errno; 385 i_errno = IESETMSS; 386 return -1; 387 } 388 #else 389 opt = test->settings->mss; 390 391 /* 392 * Solaris might not support this option. If it doesn't work, 393 * ignore the error (at least for now). 394 */ 395 if (setsockopt(s, IPPROTO_SCTP, SCTP_MAXSEG, &opt, sizeof(opt)) < 0 && 396 errno != ENOPROTOOPT) { 397 saved_errno = errno; 398 close(s); 399 freeaddrinfo(server_res); 400 errno = saved_errno; 401 i_errno = IESETMSS; 402 return -1; 403 } 404 #endif /* HAVE_STRUCT_SCTP_ASSOC_VALUE */ 405 } 406 407 if (test->settings->num_ostreams > 0) { 408 struct sctp_initmsg initmsg; 409 410 memset(&initmsg, 0, sizeof(struct sctp_initmsg)); 411 initmsg.sinit_num_ostreams = test->settings->num_ostreams; 412 413 if (setsockopt(s, IPPROTO_SCTP, SCTP_INITMSG, &initmsg, sizeof(struct sctp_initmsg)) < 0) { 414 saved_errno = errno; 415 close(s); 416 freeaddrinfo(server_res); 417 errno = saved_errno; 418 i_errno = IESETSCTPNSTREAM; 419 return -1; 420 } 421 } 422 423 /* clients must call bind() followed by sctp_bindx() before connect() */ 424 if (!TAILQ_EMPTY(&test->xbind_addrs)) { 425 if (iperf_sctp_bindx(test, s, IPERF_SCTP_CLIENT)) 426 return -1; 427 } 428 429 /* TODO support sctp_connectx() to avoid heartbeating. */ 430 if (connect(s, (struct sockaddr *) server_res->ai_addr, server_res->ai_addrlen) < 0 && errno != EINPROGRESS) { 431 saved_errno = errno; 432 close(s); 433 freeaddrinfo(server_res); 434 errno = saved_errno; 435 i_errno = IESTREAMCONNECT; 436 return -1; 437 } 438 freeaddrinfo(server_res); 439 440 /* Send cookie for verification */ 441 if (Nwrite(s, test->cookie, COOKIE_SIZE, Psctp) < 0) { 442 saved_errno = errno; 443 close(s); 444 errno = saved_errno; 445 i_errno = IESENDCOOKIE; 446 return -1; 447 } 448 449 /* 450 * We want to allow fragmentation. But there's at least one 451 * implementation (Solaris) that doesn't support this option, 452 * even though it defines SCTP_DISABLE_FRAGMENTS. So we have to 453 * try setting the option and ignore the error, if it doesn't 454 * work. 455 */ 456 opt = 0; 457 if (setsockopt(s, IPPROTO_SCTP, SCTP_DISABLE_FRAGMENTS, &opt, sizeof(opt)) < 0 && 458 errno != ENOPROTOOPT) { 459 saved_errno = errno; 460 close(s); 461 freeaddrinfo(server_res); 462 errno = saved_errno; 463 i_errno = IESETSCTPDISABLEFRAG; 464 return -1; 465 } 466 467 return s; 468 #else 469 i_errno = IENOSCTP; 470 return -1; 471 #endif /* HAVE_SCTP */ 472 } 473 474 475 476 int 477 iperf_sctp_init(struct iperf_test *test) 478 { 479 #if defined(HAVE_SCTP) 480 return 0; 481 #else 482 i_errno = IENOSCTP; 483 return -1; 484 #endif /* HAVE_SCTP */ 485 } 486 487 488 489 /* iperf_sctp_bindx 490 * 491 * handle binding to multiple endpoints (-X parameters) 492 */ 493 int 494 iperf_sctp_bindx(struct iperf_test *test, int s, int is_server) 495 { 496 #if defined(HAVE_SCTP) 497 struct addrinfo hints; 498 char portstr[6]; 499 char *servname; 500 struct addrinfo *ai, *ai0; 501 struct sockaddr *xaddrs; 502 struct xbind_entry *xbe, *xbe0; 503 char *bp; 504 size_t xaddrlen; 505 int nxaddrs; 506 int retval; 507 int domain; 508 int saved_errno; 509 510 domain = test->settings->domain; 511 xbe0 = NULL; 512 retval = 0; 513 514 if (TAILQ_EMPTY(&test->xbind_addrs)) 515 return retval; /* nothing to do */ 516 517 memset(&hints, 0, sizeof(hints)); 518 hints.ai_family = (domain == AF_UNSPEC ? AF_INET6 : domain); 519 hints.ai_socktype = SOCK_STREAM; 520 servname = NULL; 521 if (is_server) { 522 hints.ai_flags |= AI_PASSIVE; 523 snprintf(portstr, 6, "%d", test->server_port); 524 servname = portstr; 525 } 526 527 /* client: must pop first -X address and call bind(). 528 * sctp_bindx() must see the ephemeral port chosen by bind(). 529 * we deliberately ignore the -B argument in this case. 530 */ 531 if (!is_server) { 532 struct sockaddr *sa; 533 struct sockaddr_in *sin; 534 struct sockaddr_in6 *sin6; 535 int eport; 536 537 xbe0 = TAILQ_FIRST(&test->xbind_addrs); 538 TAILQ_REMOVE(&test->xbind_addrs, xbe0, link); 539 540 if (getaddrinfo(xbe0->name, servname, &hints, &xbe0->ai) != 0) { 541 i_errno = IESETSCTPBINDX; 542 retval = -1; 543 goto out; 544 } 545 546 ai = xbe0->ai; 547 if (domain != AF_UNSPEC && domain != ai->ai_family) { 548 i_errno = IESETSCTPBINDX; 549 retval = -1; 550 goto out; 551 } 552 if (bind(s, (struct sockaddr *)ai->ai_addr, ai->ai_addrlen) < 0) { 553 i_errno = IESETSCTPBINDX; 554 retval = -1; 555 goto out; 556 } 557 558 /* if only one -X argument, nothing more to do */ 559 if (TAILQ_EMPTY(&test->xbind_addrs)) 560 goto out; 561 562 sa = (struct sockaddr *)ai->ai_addr; 563 if (sa->sa_family == AF_INET) { 564 sin = (struct sockaddr_in *)ai->ai_addr; 565 eport = sin->sin_port; 566 } else if (sa->sa_family == AF_INET6) { 567 sin6 = (struct sockaddr_in6 *)ai->ai_addr; 568 eport = sin6->sin6_port; 569 } else { 570 i_errno = IESETSCTPBINDX; 571 retval = -1; 572 goto out; 573 } 574 snprintf(portstr, 6, "%d", ntohs(eport)); 575 servname = portstr; 576 } 577 578 /* pass 1: resolve and compute lengths. */ 579 nxaddrs = 0; 580 xaddrlen = 0; 581 TAILQ_FOREACH(xbe, &test->xbind_addrs, link) { 582 if (xbe->ai != NULL) 583 freeaddrinfo(xbe->ai); 584 if (getaddrinfo(xbe->name, servname, &hints, &xbe->ai) != 0) { 585 i_errno = IESETSCTPBINDX; 586 retval = -1; 587 goto out; 588 } 589 ai0 = xbe->ai; 590 for (ai = ai0; ai; ai = ai->ai_next) { 591 if (domain != AF_UNSPEC && domain != ai->ai_family) 592 continue; 593 xaddrlen += ai->ai_addrlen; 594 ++nxaddrs; 595 } 596 } 597 598 /* pass 2: copy into flat buffer. */ 599 xaddrs = (struct sockaddr *)malloc(xaddrlen); 600 if (!xaddrs) { 601 i_errno = IESETSCTPBINDX; 602 retval = -1; 603 goto out; 604 } 605 bp = (char *)xaddrs; 606 TAILQ_FOREACH(xbe, &test->xbind_addrs, link) { 607 ai0 = xbe->ai; 608 for (ai = ai0; ai; ai = ai->ai_next) { 609 if (domain != AF_UNSPEC && domain != ai->ai_family) 610 continue; 611 memcpy(bp, ai->ai_addr, ai->ai_addrlen); 612 bp += ai->ai_addrlen; 613 } 614 } 615 616 if (sctp_bindx(s, xaddrs, nxaddrs, SCTP_BINDX_ADD_ADDR) == -1) { 617 saved_errno = errno; 618 close(s); 619 free(xaddrs); 620 errno = saved_errno; 621 i_errno = IESETSCTPBINDX; 622 retval = -1; 623 goto out; 624 } 625 626 free(xaddrs); 627 retval = 0; 628 629 out: 630 /* client: put head node back. */ 631 if (!is_server && xbe0) 632 TAILQ_INSERT_HEAD(&test->xbind_addrs, xbe0, link); 633 634 TAILQ_FOREACH(xbe, &test->xbind_addrs, link) { 635 if (xbe->ai) { 636 freeaddrinfo(xbe->ai); 637 xbe->ai = NULL; 638 } 639 } 640 641 return retval; 642 #else 643 i_errno = IENOSCTP; 644 return -1; 645 #endif /* HAVE_SCTP */ 646 } 647