Annotation of embedaddon/iperf/src/iperf_server_api.c, revision 1.1
1.1 ! misho 1: /*
! 2: * iperf, Copyright (c) 2014, 2015, 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 TTD@lbl.gov.
! 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: /* iperf_server_api.c: Functions to be used by an iperf server
! 28: */
! 29:
! 30: #include <stdio.h>
! 31: #include <stdlib.h>
! 32: #include <string.h>
! 33: #include <getopt.h>
! 34: #include <errno.h>
! 35: #include <unistd.h>
! 36: #include <assert.h>
! 37: #include <fcntl.h>
! 38: #include <sys/socket.h>
! 39: #include <sys/types.h>
! 40: #include <netinet/in.h>
! 41: #include <arpa/inet.h>
! 42: #include <netdb.h>
! 43: #include <pthread.h>
! 44: #ifdef HAVE_STDINT_H
! 45: #include <stdint.h>
! 46: #endif
! 47: #include <netinet/tcp.h>
! 48: #include <sys/time.h>
! 49: #include <sys/resource.h>
! 50: #include <sched.h>
! 51: #include <setjmp.h>
! 52:
! 53: #include "iperf.h"
! 54: #include "iperf_api.h"
! 55: #include "iperf_udp.h"
! 56: #include "iperf_tcp.h"
! 57: #include "iperf_util.h"
! 58: #include "timer.h"
! 59: #include "net.h"
! 60: #include "units.h"
! 61: #include "tcp_window_size.h"
! 62: #include "iperf_util.h"
! 63: #include "iperf_locale.h"
! 64:
! 65:
! 66: int
! 67: iperf_server_listen(struct iperf_test *test)
! 68: {
! 69: retry:
! 70: if((test->listener = netannounce(test->settings->domain, Ptcp, test->bind_address, test->server_port)) < 0) {
! 71: if (errno == EAFNOSUPPORT && (test->settings->domain == AF_INET6 || test->settings->domain == AF_UNSPEC)) {
! 72: /* If we get "Address family not supported by protocol", that
! 73: ** probably means we were compiled with IPv6 but the running
! 74: ** kernel does not actually do IPv6. This is not too unusual,
! 75: ** v6 support is and perhaps always will be spotty.
! 76: */
! 77: warning("this system does not seem to support IPv6 - trying IPv4");
! 78: test->settings->domain = AF_INET;
! 79: goto retry;
! 80: } else {
! 81: i_errno = IELISTEN;
! 82: return -1;
! 83: }
! 84: }
! 85:
! 86: if (!test->json_output) {
! 87: iprintf(test, "-----------------------------------------------------------\n");
! 88: iprintf(test, "Server listening on %d\n", test->server_port);
! 89: }
! 90:
! 91: // This needs to be changed to reflect if client has different window size
! 92: // make sure we got what we asked for
! 93: /* XXX: This needs to be moved to the stream listener
! 94: if ((x = get_tcp_windowsize(test->listener, SO_RCVBUF)) < 0) {
! 95: // Needs to set some sort of error number/message
! 96: perror("SO_RCVBUF");
! 97: return -1;
! 98: }
! 99: */
! 100:
! 101: // XXX: This code needs to be moved to after parameter exhange
! 102: /*
! 103: char ubuf[UNIT_LEN];
! 104: int x;
! 105:
! 106: if (test->protocol->id == Ptcp) {
! 107: if (test->settings->socket_bufsize > 0) {
! 108: unit_snprintf(ubuf, UNIT_LEN, (double) x, 'A');
! 109: if (!test->json_output)
! 110: iprintf(test, report_window, ubuf);
! 111: } else {
! 112: if (!test->json_output)
! 113: iprintf(test, "%s", report_autotune);
! 114: }
! 115: }
! 116: */
! 117: if (!test->json_output)
! 118: iprintf(test, "-----------------------------------------------------------\n");
! 119:
! 120: FD_ZERO(&test->read_set);
! 121: FD_ZERO(&test->write_set);
! 122: FD_SET(test->listener, &test->read_set);
! 123: if (test->listener > test->max_fd) test->max_fd = test->listener;
! 124:
! 125: return 0;
! 126: }
! 127:
! 128: int
! 129: iperf_accept(struct iperf_test *test)
! 130: {
! 131: int s;
! 132: signed char rbuf = ACCESS_DENIED;
! 133: socklen_t len;
! 134: struct sockaddr_storage addr;
! 135:
! 136: len = sizeof(addr);
! 137: if ((s = accept(test->listener, (struct sockaddr *) &addr, &len)) < 0) {
! 138: i_errno = IEACCEPT;
! 139: return -1;
! 140: }
! 141:
! 142: if (test->ctrl_sck == -1) {
! 143: /* Server free, accept new client */
! 144: test->ctrl_sck = s;
! 145: if (Nread(test->ctrl_sck, test->cookie, COOKIE_SIZE, Ptcp) < 0) {
! 146: i_errno = IERECVCOOKIE;
! 147: return -1;
! 148: }
! 149: FD_SET(test->ctrl_sck, &test->read_set);
! 150: if (test->ctrl_sck > test->max_fd) test->max_fd = test->ctrl_sck;
! 151:
! 152: if (iperf_set_send_state(test, PARAM_EXCHANGE) != 0)
! 153: return -1;
! 154: if (iperf_exchange_parameters(test) < 0)
! 155: return -1;
! 156: if (test->server_affinity != -1)
! 157: if (iperf_setaffinity(test, test->server_affinity) != 0)
! 158: return -1;
! 159: if (test->on_connect)
! 160: test->on_connect(test);
! 161: } else {
! 162: /*
! 163: * Don't try to read from the socket. It could block an ongoing test.
! 164: * Just send ACCESS_DENIED.
! 165: */
! 166: if (Nwrite(s, (char*) &rbuf, sizeof(rbuf), Ptcp) < 0) {
! 167: i_errno = IESENDMESSAGE;
! 168: return -1;
! 169: }
! 170: close(s);
! 171: }
! 172:
! 173: return 0;
! 174: }
! 175:
! 176:
! 177: /**************************************************************************/
! 178: int
! 179: iperf_handle_message_server(struct iperf_test *test)
! 180: {
! 181: int rval;
! 182: struct iperf_stream *sp;
! 183:
! 184: // XXX: Need to rethink how this behaves to fit API
! 185: if ((rval = Nread(test->ctrl_sck, (char*) &test->state, sizeof(signed char), Ptcp)) <= 0) {
! 186: if (rval == 0) {
! 187: iperf_err(test, "the client has unexpectedly closed the connection");
! 188: i_errno = IECTRLCLOSE;
! 189: test->state = IPERF_DONE;
! 190: return 0;
! 191: } else {
! 192: i_errno = IERECVMESSAGE;
! 193: return -1;
! 194: }
! 195: }
! 196:
! 197: switch(test->state) {
! 198: case TEST_START:
! 199: break;
! 200: case TEST_END:
! 201: test->done = 1;
! 202: cpu_util(test->cpu_util);
! 203: test->stats_callback(test);
! 204: SLIST_FOREACH(sp, &test->streams, streams) {
! 205: FD_CLR(sp->socket, &test->read_set);
! 206: FD_CLR(sp->socket, &test->write_set);
! 207: close(sp->socket);
! 208: }
! 209: test->reporter_callback(test);
! 210: if (iperf_set_send_state(test, EXCHANGE_RESULTS) != 0)
! 211: return -1;
! 212: if (iperf_exchange_results(test) < 0)
! 213: return -1;
! 214: if (iperf_set_send_state(test, DISPLAY_RESULTS) != 0)
! 215: return -1;
! 216: if (test->on_test_finish)
! 217: test->on_test_finish(test);
! 218: break;
! 219: case IPERF_DONE:
! 220: break;
! 221: case CLIENT_TERMINATE:
! 222: i_errno = IECLIENTTERM;
! 223:
! 224: // Temporarily be in DISPLAY_RESULTS phase so we can get
! 225: // ending summary statistics.
! 226: signed char oldstate = test->state;
! 227: cpu_util(test->cpu_util);
! 228: test->state = DISPLAY_RESULTS;
! 229: test->reporter_callback(test);
! 230: test->state = oldstate;
! 231:
! 232: // XXX: Remove this line below!
! 233: iperf_err(test, "the client has terminated");
! 234: SLIST_FOREACH(sp, &test->streams, streams) {
! 235: FD_CLR(sp->socket, &test->read_set);
! 236: FD_CLR(sp->socket, &test->write_set);
! 237: close(sp->socket);
! 238: }
! 239: test->state = IPERF_DONE;
! 240: break;
! 241: default:
! 242: i_errno = IEMESSAGE;
! 243: return -1;
! 244: }
! 245:
! 246: return 0;
! 247: }
! 248:
! 249: /* XXX: This function is not used anymore */
! 250: void
! 251: iperf_test_reset(struct iperf_test *test)
! 252: {
! 253: struct iperf_stream *sp;
! 254:
! 255: close(test->ctrl_sck);
! 256:
! 257: /* Free streams */
! 258: while (!SLIST_EMPTY(&test->streams)) {
! 259: sp = SLIST_FIRST(&test->streams);
! 260: SLIST_REMOVE_HEAD(&test->streams, streams);
! 261: iperf_free_stream(sp);
! 262: }
! 263: if (test->timer != NULL) {
! 264: tmr_cancel(test->timer);
! 265: test->timer = NULL;
! 266: }
! 267: if (test->stats_timer != NULL) {
! 268: tmr_cancel(test->stats_timer);
! 269: test->stats_timer = NULL;
! 270: }
! 271: if (test->reporter_timer != NULL) {
! 272: tmr_cancel(test->reporter_timer);
! 273: test->reporter_timer = NULL;
! 274: }
! 275: test->done = 0;
! 276:
! 277: SLIST_INIT(&test->streams);
! 278:
! 279: test->role = 's';
! 280: set_protocol(test, Ptcp);
! 281: test->omit = OMIT;
! 282: test->duration = DURATION;
! 283: test->diskfile_name = (char*) 0;
! 284: test->affinity = -1;
! 285: test->server_affinity = -1;
! 286: test->title = NULL;
! 287: test->congestion = NULL;
! 288: test->state = 0;
! 289: test->server_hostname = NULL;
! 290:
! 291: test->ctrl_sck = -1;
! 292: test->prot_listener = -1;
! 293:
! 294: test->bytes_sent = 0;
! 295:
! 296: test->reverse = 0;
! 297: test->sender = 0;
! 298: test->sender_has_retransmits = 0;
! 299: test->no_delay = 0;
! 300:
! 301: FD_ZERO(&test->read_set);
! 302: FD_ZERO(&test->write_set);
! 303: FD_SET(test->listener, &test->read_set);
! 304: test->max_fd = test->listener;
! 305:
! 306: test->num_streams = 1;
! 307: test->settings->socket_bufsize = 0;
! 308: test->settings->blksize = DEFAULT_TCP_BLKSIZE;
! 309: test->settings->rate = 0;
! 310: test->settings->mss = 0;
! 311: memset(test->cookie, 0, COOKIE_SIZE);
! 312: }
! 313:
! 314: static void
! 315: server_stats_timer_proc(TimerClientData client_data, struct timeval *nowP)
! 316: {
! 317: struct iperf_test *test = client_data.p;
! 318:
! 319: if (test->done)
! 320: return;
! 321: if (test->stats_callback)
! 322: test->stats_callback(test);
! 323: }
! 324:
! 325: static void
! 326: server_reporter_timer_proc(TimerClientData client_data, struct timeval *nowP)
! 327: {
! 328: struct iperf_test *test = client_data.p;
! 329:
! 330: if (test->done)
! 331: return;
! 332: if (test->reporter_callback)
! 333: test->reporter_callback(test);
! 334: }
! 335:
! 336: static int
! 337: create_server_timers(struct iperf_test * test)
! 338: {
! 339: struct timeval now;
! 340: TimerClientData cd;
! 341:
! 342: if (gettimeofday(&now, NULL) < 0) {
! 343: i_errno = IEINITTEST;
! 344: return -1;
! 345: }
! 346: cd.p = test;
! 347: test->stats_timer = test->reporter_timer = NULL;
! 348: if (test->stats_interval != 0) {
! 349: test->stats_timer = tmr_create(&now, server_stats_timer_proc, cd, test->stats_interval * SEC_TO_US, 1);
! 350: if (test->stats_timer == NULL) {
! 351: i_errno = IEINITTEST;
! 352: return -1;
! 353: }
! 354: }
! 355: if (test->reporter_interval != 0) {
! 356: test->reporter_timer = tmr_create(&now, server_reporter_timer_proc, cd, test->reporter_interval * SEC_TO_US, 1);
! 357: if (test->reporter_timer == NULL) {
! 358: i_errno = IEINITTEST;
! 359: return -1;
! 360: }
! 361: }
! 362: return 0;
! 363: }
! 364:
! 365: static void
! 366: server_omit_timer_proc(TimerClientData client_data, struct timeval *nowP)
! 367: {
! 368: struct iperf_test *test = client_data.p;
! 369:
! 370: test->omit_timer = NULL;
! 371: test->omitting = 0;
! 372: iperf_reset_stats(test);
! 373: if (test->verbose && !test->json_output && test->reporter_interval == 0)
! 374: iprintf(test, "%s", report_omit_done);
! 375:
! 376: /* Reset the timers. */
! 377: if (test->stats_timer != NULL)
! 378: tmr_reset(nowP, test->stats_timer);
! 379: if (test->reporter_timer != NULL)
! 380: tmr_reset(nowP, test->reporter_timer);
! 381: }
! 382:
! 383: static int
! 384: create_server_omit_timer(struct iperf_test * test)
! 385: {
! 386: struct timeval now;
! 387: TimerClientData cd;
! 388:
! 389: if (test->omit == 0) {
! 390: test->omit_timer = NULL;
! 391: test->omitting = 0;
! 392: } else {
! 393: if (gettimeofday(&now, NULL) < 0) {
! 394: i_errno = IEINITTEST;
! 395: return -1;
! 396: }
! 397: test->omitting = 1;
! 398: cd.p = test;
! 399: test->omit_timer = tmr_create(&now, server_omit_timer_proc, cd, test->omit * SEC_TO_US, 0);
! 400: if (test->omit_timer == NULL) {
! 401: i_errno = IEINITTEST;
! 402: return -1;
! 403: }
! 404: }
! 405:
! 406: return 0;
! 407: }
! 408:
! 409: static void
! 410: cleanup_server(struct iperf_test *test)
! 411: {
! 412: /* Close open test sockets */
! 413: close(test->ctrl_sck);
! 414: close(test->listener);
! 415:
! 416: /* Cancel any remaining timers. */
! 417: if (test->stats_timer != NULL) {
! 418: tmr_cancel(test->stats_timer);
! 419: test->stats_timer = NULL;
! 420: }
! 421: if (test->reporter_timer != NULL) {
! 422: tmr_cancel(test->reporter_timer);
! 423: test->reporter_timer = NULL;
! 424: }
! 425: if (test->omit_timer != NULL) {
! 426: tmr_cancel(test->omit_timer);
! 427: test->omit_timer = NULL;
! 428: }
! 429: }
! 430:
! 431:
! 432: int
! 433: iperf_run_server(struct iperf_test *test)
! 434: {
! 435: int result, s, streams_accepted;
! 436: fd_set read_set, write_set;
! 437: struct iperf_stream *sp;
! 438: struct timeval now;
! 439: struct timeval* timeout;
! 440:
! 441: if (test->affinity != -1)
! 442: if (iperf_setaffinity(test, test->affinity) != 0)
! 443: return -2;
! 444:
! 445: if (test->json_output)
! 446: if (iperf_json_start(test) < 0)
! 447: return -2;
! 448:
! 449: if (test->json_output) {
! 450: cJSON_AddItemToObject(test->json_start, "version", cJSON_CreateString(version));
! 451: cJSON_AddItemToObject(test->json_start, "system_info", cJSON_CreateString(get_system_info()));
! 452: } else if (test->verbose) {
! 453: iprintf(test, "%s\n", version);
! 454: iprintf(test, "%s", "");
! 455: iprintf(test, "%s\n", get_system_info());
! 456: iflush(test);
! 457: }
! 458:
! 459: // Open socket and listen
! 460: if (iperf_server_listen(test) < 0) {
! 461: return -2;
! 462: }
! 463:
! 464: // Begin calculating CPU utilization
! 465: cpu_util(NULL);
! 466:
! 467: test->state = IPERF_START;
! 468: streams_accepted = 0;
! 469:
! 470: while (test->state != IPERF_DONE) {
! 471:
! 472: memcpy(&read_set, &test->read_set, sizeof(fd_set));
! 473: memcpy(&write_set, &test->write_set, sizeof(fd_set));
! 474:
! 475: (void) gettimeofday(&now, NULL);
! 476: timeout = tmr_timeout(&now);
! 477: result = select(test->max_fd + 1, &read_set, &write_set, NULL, timeout);
! 478: if (result < 0 && errno != EINTR) {
! 479: cleanup_server(test);
! 480: i_errno = IESELECT;
! 481: return -1;
! 482: }
! 483: if (result > 0) {
! 484: if (FD_ISSET(test->listener, &read_set)) {
! 485: if (test->state != CREATE_STREAMS) {
! 486: if (iperf_accept(test) < 0) {
! 487: cleanup_server(test);
! 488: return -1;
! 489: }
! 490: FD_CLR(test->listener, &read_set);
! 491: }
! 492: }
! 493: if (FD_ISSET(test->ctrl_sck, &read_set)) {
! 494: if (iperf_handle_message_server(test) < 0) {
! 495: cleanup_server(test);
! 496: return -1;
! 497: }
! 498: FD_CLR(test->ctrl_sck, &read_set);
! 499: }
! 500:
! 501: if (test->state == CREATE_STREAMS) {
! 502: if (FD_ISSET(test->prot_listener, &read_set)) {
! 503:
! 504: if ((s = test->protocol->accept(test)) < 0) {
! 505: cleanup_server(test);
! 506: return -1;
! 507: }
! 508:
! 509: if (!is_closed(s)) {
! 510: sp = iperf_new_stream(test, s);
! 511: if (!sp) {
! 512: cleanup_server(test);
! 513: return -1;
! 514: }
! 515:
! 516: if (test->sender)
! 517: FD_SET(s, &test->write_set);
! 518: else
! 519: FD_SET(s, &test->read_set);
! 520: if (s > test->max_fd) test->max_fd = s;
! 521:
! 522: /*
! 523: * If the protocol isn't UDP, or even if it is but
! 524: * we're the receiver, set nonblocking sockets.
! 525: * We need this to allow a server receiver to
! 526: * maintain interactivity with the control channel.
! 527: */
! 528: if (test->protocol->id != Pudp ||
! 529: !test->sender) {
! 530: setnonblocking(s, 1);
! 531: }
! 532:
! 533: streams_accepted++;
! 534: if (test->on_new_stream)
! 535: test->on_new_stream(sp);
! 536: }
! 537: FD_CLR(test->prot_listener, &read_set);
! 538: }
! 539:
! 540: if (streams_accepted == test->num_streams) {
! 541: if (test->protocol->id != Ptcp) {
! 542: FD_CLR(test->prot_listener, &test->read_set);
! 543: close(test->prot_listener);
! 544: } else {
! 545: if (test->no_delay || test->settings->mss || test->settings->socket_bufsize) {
! 546: FD_CLR(test->listener, &test->read_set);
! 547: close(test->listener);
! 548: if ((s = netannounce(test->settings->domain, Ptcp, test->bind_address, test->server_port)) < 0) {
! 549: cleanup_server(test);
! 550: i_errno = IELISTEN;
! 551: return -1;
! 552: }
! 553: test->listener = s;
! 554: FD_SET(test->listener, &test->read_set);
! 555: if (test->listener > test->max_fd) test->max_fd = test->listener;
! 556: }
! 557: }
! 558: test->prot_listener = -1;
! 559: if (iperf_set_send_state(test, TEST_START) != 0) {
! 560: cleanup_server(test);
! 561: return -1;
! 562: }
! 563: if (iperf_init_test(test) < 0) {
! 564: cleanup_server(test);
! 565: return -1;
! 566: }
! 567: if (create_server_timers(test) < 0) {
! 568: cleanup_server(test);
! 569: return -1;
! 570: }
! 571: if (create_server_omit_timer(test) < 0) {
! 572: cleanup_server(test);
! 573: return -1;
! 574: }
! 575: if (test->reverse)
! 576: if (iperf_create_send_timers(test) < 0) {
! 577: cleanup_server(test);
! 578: return -1;
! 579: }
! 580: if (iperf_set_send_state(test, TEST_RUNNING) != 0) {
! 581: cleanup_server(test);
! 582: return -1;
! 583: }
! 584: }
! 585: }
! 586:
! 587: if (test->state == TEST_RUNNING) {
! 588: if (test->reverse) {
! 589: // Reverse mode. Server sends.
! 590: if (iperf_send(test, &write_set) < 0) {
! 591: cleanup_server(test);
! 592: return -1;
! 593: }
! 594: } else {
! 595: // Regular mode. Server receives.
! 596: if (iperf_recv(test, &read_set) < 0) {
! 597: cleanup_server(test);
! 598: return -1;
! 599: }
! 600: }
! 601: }
! 602: }
! 603:
! 604: if (result == 0 ||
! 605: (timeout != NULL && timeout->tv_sec == 0 && timeout->tv_usec == 0)) {
! 606: /* Run the timers. */
! 607: (void) gettimeofday(&now, NULL);
! 608: tmr_run(&now);
! 609: }
! 610: }
! 611:
! 612: cleanup_server(test);
! 613:
! 614: if (test->json_output) {
! 615: if (iperf_json_finish(test) < 0)
! 616: return -1;
! 617: }
! 618:
! 619: iflush(test);
! 620:
! 621: if (test->server_affinity != -1)
! 622: if (iperf_clearaffinity(test) != 0)
! 623: return -1;
! 624:
! 625: return 0;
! 626: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>