1a951c980SBrian Tierney /* 27d375156SJon Dugan * Copyright (c) 2009-2011, The Regents of the University of California, 37d375156SJon Dugan * through Lawrence Berkeley National Laboratory (subject to receipt of any 47d375156SJon Dugan * required approvals from the U.S. Dept. of Energy). All rights reserved. 57d375156SJon Dugan * 67d375156SJon Dugan * This code is distributed under a BSD style license, see the LICENSE file 77d375156SJon Dugan * for complete information. 8a951c980SBrian Tierney */ 9a951c980SBrian Tierney 10a951c980SBrian Tierney /* iperf_server_api.c: Functions to be used by an iperf server 11a951c980SBrian Tierney */ 12a951c980SBrian Tierney 13a951c980SBrian Tierney #include <stdio.h> 14a951c980SBrian Tierney #include <stdlib.h> 15a951c980SBrian Tierney #include <string.h> 16a951c980SBrian Tierney #include <getopt.h> 17a951c980SBrian Tierney #include <errno.h> 18a951c980SBrian Tierney #include <unistd.h> 19a951c980SBrian Tierney #include <assert.h> 20a951c980SBrian Tierney #include <fcntl.h> 21a951c980SBrian Tierney #include <sys/socket.h> 22a951c980SBrian Tierney #include <sys/types.h> 23a951c980SBrian Tierney #include <netinet/in.h> 24a951c980SBrian Tierney #include <arpa/inet.h> 25a951c980SBrian Tierney #include <netdb.h> 26a951c980SBrian Tierney #include <pthread.h> 27a951c980SBrian Tierney #include <stdint.h> 28a951c980SBrian Tierney #include <netinet/tcp.h> 29a951c980SBrian Tierney #include <sys/time.h> 30a951c980SBrian Tierney #include <sys/resource.h> 31a951c980SBrian Tierney #include <sched.h> 32a951c980SBrian Tierney #include <setjmp.h> 33a951c980SBrian Tierney 34a951c980SBrian Tierney #include "iperf.h" 35a951c980SBrian Tierney #include "iperf_api.h" 36a951c980SBrian Tierney #include "iperf_udp.h" 37a951c980SBrian Tierney #include "iperf_tcp.h" 388a0b5a5dSsethdelliott #include "iperf_util.h" 39a951c980SBrian Tierney #include "timer.h" 40a951c980SBrian Tierney #include "net.h" 41a951c980SBrian Tierney #include "units.h" 42a951c980SBrian Tierney #include "tcp_window_size.h" 43b0b16b86SJon Dugan #include "iperf_util.h" 44a951c980SBrian Tierney #include "locale.h" 45a951c980SBrian Tierney 46a3281a3dSBrian Tierney 47982c704aSsethdelliott int 48982c704aSsethdelliott iperf_server_listen(struct iperf_test *test) 49982c704aSsethdelliott { 50f99bd3b2Ssethdelliott if((test->listener = netannounce(test->settings->domain, Ptcp, test->bind_address, test->server_port)) < 0) { 51b60a49ddSsethdelliott i_errno = IELISTEN; 52ec2d0670SJef Poskanzer return -1; 53982c704aSsethdelliott } 54982c704aSsethdelliott 559bfb54c0SJef Poskanzer if (!test->json_output) { 56982c704aSsethdelliott printf("-----------------------------------------------------------\n"); 57982c704aSsethdelliott printf("Server listening on %d\n", test->server_port); 589bfb54c0SJef Poskanzer } 59982c704aSsethdelliott 60982c704aSsethdelliott // This needs to be changed to reflect if client has different window size 61982c704aSsethdelliott // make sure we got what we asked for 62ba2672a2Ssethdelliott /* XXX: This needs to be moved to the stream listener 6366ce7ad4Ssethdelliott if ((x = get_tcp_windowsize(test->listener, SO_RCVBUF)) < 0) { 64982c704aSsethdelliott // Needs to set some sort of error number/message 65982c704aSsethdelliott perror("SO_RCVBUF"); 66982c704aSsethdelliott return -1; 67982c704aSsethdelliott } 68ba2672a2Ssethdelliott */ 69982c704aSsethdelliott 70ba2672a2Ssethdelliott // XXX: This code needs to be moved to after parameter exhange 71c81883bfSsethdelliott /* 7298ce496bSjef char ubuf[UNIT_LEN]; 7398ce496bSjef int x; 7498ce496bSjef 7566ce7ad4Ssethdelliott if (test->protocol->id == Ptcp) { 76112a907cSsethdelliott if (test->settings->socket_bufsize > 0) { 77982c704aSsethdelliott unit_snprintf(ubuf, UNIT_LEN, (double) x, 'A'); 789bfb54c0SJef Poskanzer if (test->json_output) 79982c704aSsethdelliott printf("TCP window size: %s\n", ubuf); 80982c704aSsethdelliott } else { 819bfb54c0SJef Poskanzer if (test->json_output) 82982c704aSsethdelliott printf("Using TCP Autotuning\n"); 83982c704aSsethdelliott } 84982c704aSsethdelliott } 85c81883bfSsethdelliott */ 869bfb54c0SJef Poskanzer if (!test->json_output) 87982c704aSsethdelliott printf("-----------------------------------------------------------\n"); 88982c704aSsethdelliott 89982c704aSsethdelliott FD_ZERO(&test->read_set); 90efdc02f7Ssethdelliott FD_ZERO(&test->write_set); 9166ce7ad4Ssethdelliott FD_SET(test->listener, &test->read_set); 9266ce7ad4Ssethdelliott test->max_fd = (test->listener > test->max_fd) ? test->listener : test->max_fd; 93982c704aSsethdelliott 94982c704aSsethdelliott return 0; 95982c704aSsethdelliott } 96982c704aSsethdelliott 97982c704aSsethdelliott int 98982c704aSsethdelliott iperf_accept(struct iperf_test *test) 99982c704aSsethdelliott { 100982c704aSsethdelliott int s; 101465b565cSsethdelliott int rbuf = ACCESS_DENIED; 102af70b448Ssethdelliott char cookie[COOKIE_SIZE]; 103982c704aSsethdelliott socklen_t len; 104982c704aSsethdelliott struct sockaddr_in addr; 105982c704aSsethdelliott 106b68ac00eSsethdelliott len = sizeof(addr); 10766ce7ad4Ssethdelliott if ((s = accept(test->listener, (struct sockaddr *) &addr, &len)) < 0) { 108b60a49ddSsethdelliott i_errno = IEACCEPT; 109ec2d0670SJef Poskanzer return -1; 110982c704aSsethdelliott } 111af70b448Ssethdelliott 112af70b448Ssethdelliott if (test->ctrl_sck == -1) { 113af70b448Ssethdelliott /* Server free, accept new client */ 114112a907cSsethdelliott if (Nread(s, test->cookie, COOKIE_SIZE, Ptcp) < 0) { 115b60a49ddSsethdelliott i_errno = IERECVCOOKIE; 116ec2d0670SJef Poskanzer return -1; 117af70b448Ssethdelliott } 118af70b448Ssethdelliott 119465b565cSsethdelliott FD_SET(s, &test->read_set); 120465b565cSsethdelliott FD_SET(s, &test->write_set); 121465b565cSsethdelliott test->max_fd = (s > test->max_fd) ? s : test->max_fd; 122465b565cSsethdelliott test->ctrl_sck = s; 123982c704aSsethdelliott 124af70b448Ssethdelliott test->state = PARAM_EXCHANGE; 125af70b448Ssethdelliott if (Nwrite(test->ctrl_sck, &test->state, sizeof(char), Ptcp) < 0) { 126b60a49ddSsethdelliott i_errno = IESENDMESSAGE; 127ec2d0670SJef Poskanzer return -1; 128af70b448Ssethdelliott } 129b60a49ddSsethdelliott if (iperf_exchange_parameters(test) < 0) { 130ec2d0670SJef Poskanzer return -1; 131b60a49ddSsethdelliott } 1324df383f6Ssethdelliott if (test->on_connect) { 1334df383f6Ssethdelliott test->on_connect(test); 1344df383f6Ssethdelliott } 135982c704aSsethdelliott } else { 136b60a49ddSsethdelliott // XXX: Do we even need to receive cookie if we're just going to deny anyways? 137af70b448Ssethdelliott if (Nread(s, cookie, COOKIE_SIZE, Ptcp) < 0) { 138b60a49ddSsethdelliott i_errno = IERECVCOOKIE; 139ec2d0670SJef Poskanzer return -1; 140465b565cSsethdelliott } 141*987b4323SJef Poskanzer if (Nwrite(s, (char*) &rbuf, sizeof(int), Ptcp) < 0) { 142b60a49ddSsethdelliott i_errno = IESENDMESSAGE; 143ec2d0670SJef Poskanzer return -1; 144465b565cSsethdelliott } 145465b565cSsethdelliott close(s); 146982c704aSsethdelliott } 147982c704aSsethdelliott 148ec2d0670SJef Poskanzer return 0; 149af70b448Ssethdelliott } 150af70b448Ssethdelliott 151b68ac00eSsethdelliott 152a951c980SBrian Tierney /**************************************************************************/ 153982c704aSsethdelliott int 154465b565cSsethdelliott iperf_handle_message_server(struct iperf_test *test) 155982c704aSsethdelliott { 156b68ac00eSsethdelliott int rval; 157efdc02f7Ssethdelliott struct iperf_stream *sp; 158efdc02f7Ssethdelliott 159b60a49ddSsethdelliott // XXX: Need to rethink how this behaves to fit API 160b60a49ddSsethdelliott if ((rval = Nread(test->ctrl_sck, &test->state, sizeof(char), Ptcp)) <= 0) { 161b68ac00eSsethdelliott if (rval == 0) { 162b5e0751fSJef Poskanzer iperf_err(test, "the client has unexpectedly closed the connection"); 163b60a49ddSsethdelliott i_errno = IECTRLCLOSE; 164b68ac00eSsethdelliott test->state = IPERF_DONE; 165ec2d0670SJef Poskanzer return 0; 166b68ac00eSsethdelliott } else { 167b60a49ddSsethdelliott i_errno = IERECVMESSAGE; 168ec2d0670SJef Poskanzer return -1; 169982c704aSsethdelliott } 170b68ac00eSsethdelliott } 171982c704aSsethdelliott 172982c704aSsethdelliott switch(test->state) { 173ba2672a2Ssethdelliott case TEST_START: 174ba2672a2Ssethdelliott break; 175465b565cSsethdelliott case TEST_END: 1763331e801Ssethdelliott cpu_util(&test->cpu_util); 177f1b3bd81Ssethdelliott test->stats_callback(test); 178dd4bc008Ssethdelliott SLIST_FOREACH(sp, &test->streams, streams) { 179efdc02f7Ssethdelliott FD_CLR(sp->socket, &test->read_set); 180efdc02f7Ssethdelliott FD_CLR(sp->socket, &test->write_set); 181efdc02f7Ssethdelliott close(sp->socket); 182efdc02f7Ssethdelliott } 183181d61afSsethdelliott test->state = EXCHANGE_RESULTS; 184efb7bbe4Ssethdelliott if (Nwrite(test->ctrl_sck, &test->state, sizeof(char), Ptcp) < 0) { 185b60a49ddSsethdelliott i_errno = IESENDMESSAGE; 186ec2d0670SJef Poskanzer return -1; 187181d61afSsethdelliott } 188b60a49ddSsethdelliott if (iperf_exchange_results(test) < 0) 189ec2d0670SJef Poskanzer return -1; 190873952e3Ssethdelliott test->state = DISPLAY_RESULTS; 191efb7bbe4Ssethdelliott if (Nwrite(test->ctrl_sck, &test->state, sizeof(char), Ptcp) < 0) { 192b60a49ddSsethdelliott i_errno = IESENDMESSAGE; 193ec2d0670SJef Poskanzer return -1; 194465b565cSsethdelliott } 1954df383f6Ssethdelliott if (test->on_test_finish) 1964df383f6Ssethdelliott test->on_test_finish(test); 197181d61afSsethdelliott test->reporter_callback(test); 198465b565cSsethdelliott break; 199873952e3Ssethdelliott case IPERF_DONE: 200873952e3Ssethdelliott break; 201465b565cSsethdelliott case CLIENT_TERMINATE: 202ffdcc7d4Ssethdelliott i_errno = IECLIENTTERM; 203ffdcc7d4Ssethdelliott 204ffdcc7d4Ssethdelliott // XXX: Remove this line below! 205b5e0751fSJef Poskanzer iperf_err(test, "the client has terminated"); 206dd4bc008Ssethdelliott SLIST_FOREACH(sp, &test->streams, streams) { 2071ac522ecSsethdelliott FD_CLR(sp->socket, &test->read_set); 2081ac522ecSsethdelliott FD_CLR(sp->socket, &test->write_set); 2091ac522ecSsethdelliott close(sp->socket); 2101ac522ecSsethdelliott } 2111ac522ecSsethdelliott test->state = IPERF_DONE; 2121ac522ecSsethdelliott break; 213ba2672a2Ssethdelliott default: 214b60a49ddSsethdelliott i_errno = IEMESSAGE; 215ec2d0670SJef Poskanzer return -1; 216982c704aSsethdelliott } 217982c704aSsethdelliott 218ec2d0670SJef Poskanzer return 0; 219982c704aSsethdelliott } 220982c704aSsethdelliott 2210bd8d9daSsethdelliott /* XXX: This function is not used anymore */ 222efdc02f7Ssethdelliott void 223efdc02f7Ssethdelliott iperf_test_reset(struct iperf_test *test) 224efdc02f7Ssethdelliott { 2250bd8d9daSsethdelliott struct iperf_stream *sp; 226efdc02f7Ssethdelliott 227efdc02f7Ssethdelliott close(test->ctrl_sck); 228efdc02f7Ssethdelliott 229efdc02f7Ssethdelliott /* Free streams */ 230dd4bc008Ssethdelliott while (!SLIST_EMPTY(&test->streams)) { 231dd4bc008Ssethdelliott sp = SLIST_FIRST(&test->streams); 232dd4bc008Ssethdelliott SLIST_REMOVE_HEAD(&test->streams, streams); 233dd4bc008Ssethdelliott iperf_free_stream(sp); 234dd4bc008Ssethdelliott } 235ec2d0670SJef Poskanzer if (test->timer != NULL) { 236ec2d0670SJef Poskanzer tmr_cancel(test->timer); 237f1b3bd81Ssethdelliott test->timer = NULL; 238ec2d0670SJef Poskanzer } 239ec2d0670SJef Poskanzer if (test->stats_timer != NULL) { 240ec2d0670SJef Poskanzer tmr_cancel(test->stats_timer); 241f1b3bd81Ssethdelliott test->stats_timer = NULL; 242ec2d0670SJef Poskanzer } 243ec2d0670SJef Poskanzer if (test->reporter_timer != NULL) { 244ec2d0670SJef Poskanzer tmr_cancel(test->reporter_timer); 245f1b3bd81Ssethdelliott test->reporter_timer = NULL; 246ec2d0670SJef Poskanzer } 247efdc02f7Ssethdelliott 248dd4bc008Ssethdelliott SLIST_INIT(&test->streams); 24932885561Ssethdelliott 250efdc02f7Ssethdelliott test->role = 's'; 25166ce7ad4Ssethdelliott set_protocol(test, Ptcp); 25232885561Ssethdelliott test->duration = DURATION; 25332885561Ssethdelliott test->state = 0; 25432885561Ssethdelliott test->server_hostname = NULL; 25532885561Ssethdelliott 25632885561Ssethdelliott test->ctrl_sck = -1; 25766ce7ad4Ssethdelliott test->prot_listener = -1; 25832885561Ssethdelliott 259f1b3bd81Ssethdelliott test->bytes_sent = 0; 260f1b3bd81Ssethdelliott 26132885561Ssethdelliott test->reverse = 0; 262b68ac00eSsethdelliott test->no_delay = 0; 26332885561Ssethdelliott 264efdc02f7Ssethdelliott FD_ZERO(&test->read_set); 265efdc02f7Ssethdelliott FD_ZERO(&test->write_set); 26666ce7ad4Ssethdelliott FD_SET(test->listener, &test->read_set); 26766ce7ad4Ssethdelliott test->max_fd = test->listener; 26832885561Ssethdelliott 26932885561Ssethdelliott test->num_streams = 1; 270112a907cSsethdelliott test->settings->socket_bufsize = 0; 271112a907cSsethdelliott test->settings->blksize = DEFAULT_TCP_BLKSIZE; 272112a907cSsethdelliott test->settings->rate = RATE; /* UDP only */ 273112a907cSsethdelliott test->settings->mss = 0; 274112a907cSsethdelliott memset(test->cookie, 0, COOKIE_SIZE); 275efdc02f7Ssethdelliott } 276efdc02f7Ssethdelliott 2777fa9f68fSsethdelliott int 278a951c980SBrian Tierney iperf_run_server(struct iperf_test *test) 279a951c980SBrian Tierney { 280112a907cSsethdelliott int result, s, streams_accepted; 2816177a7f1SJef Poskanzer fd_set read_set, write_set; 2828a0b5a5dSsethdelliott struct iperf_stream *sp; 283ec2d0670SJef Poskanzer struct timeval now; 284a951c980SBrian Tierney 285982c704aSsethdelliott // Open socket and listen 286982c704aSsethdelliott if (iperf_server_listen(test) < 0) { 287ec2d0670SJef Poskanzer return -1; 288e99faeaeSBrian Tierney } 289a951c980SBrian Tierney 2903331e801Ssethdelliott // Begin calculating CPU utilization 2913331e801Ssethdelliott cpu_util(NULL); 2923331e801Ssethdelliott 293efdc02f7Ssethdelliott test->state = IPERF_START; 294112a907cSsethdelliott streams_accepted = 0; 295a951c980SBrian Tierney 296ec2d0670SJef Poskanzer (void) gettimeofday(&now, NULL); 297873952e3Ssethdelliott while (test->state != IPERF_DONE) { 298982c704aSsethdelliott 2996177a7f1SJef Poskanzer memcpy(&read_set, &test->read_set, sizeof(fd_set)); 3006177a7f1SJef Poskanzer memcpy(&write_set, &test->write_set, sizeof(fd_set)); 301982c704aSsethdelliott 3026177a7f1SJef Poskanzer result = select(test->max_fd + 1, &read_set, &write_set, NULL, tmr_timeout(&now)); 303982c704aSsethdelliott if (result < 0 && errno != EINTR) { 304b60a49ddSsethdelliott i_errno = IESELECT; 305ec2d0670SJef Poskanzer return -1; 306ec2d0670SJef Poskanzer } 307ec2d0670SJef Poskanzer if (result > 0) { 3086177a7f1SJef Poskanzer if (FD_ISSET(test->listener, &read_set)) { 309b68ac00eSsethdelliott if (test->state != CREATE_STREAMS) { 310465b565cSsethdelliott if (iperf_accept(test) < 0) { 311ec2d0670SJef Poskanzer return -1; 312982c704aSsethdelliott } 3136177a7f1SJef Poskanzer FD_CLR(test->listener, &read_set); 314982c704aSsethdelliott } 315b68ac00eSsethdelliott } 3166177a7f1SJef Poskanzer if (FD_ISSET(test->ctrl_sck, &read_set)) { 317b60a49ddSsethdelliott if (iperf_handle_message_server(test) < 0) 318ec2d0670SJef Poskanzer return -1; 3196177a7f1SJef Poskanzer FD_CLR(test->ctrl_sck, &read_set); 320982c704aSsethdelliott } 321465b565cSsethdelliott 322465b565cSsethdelliott if (test->state == CREATE_STREAMS) { 3236177a7f1SJef Poskanzer if (FD_ISSET(test->prot_listener, &read_set)) { 3248a0b5a5dSsethdelliott 32566ce7ad4Ssethdelliott if ((s = test->protocol->accept(test)) < 0) 326ec2d0670SJef Poskanzer return -1; 3278a0b5a5dSsethdelliott 3288a0b5a5dSsethdelliott if (!is_closed(s)) { 32966ce7ad4Ssethdelliott sp = iperf_new_stream(test, s); 3308a0b5a5dSsethdelliott if (!sp) 331ec2d0670SJef Poskanzer return -1; 33266ce7ad4Ssethdelliott 3338a0b5a5dSsethdelliott FD_SET(s, &test->read_set); 3348a0b5a5dSsethdelliott FD_SET(s, &test->write_set); 3358a0b5a5dSsethdelliott test->max_fd = (s > test->max_fd) ? s : test->max_fd; 33666ce7ad4Ssethdelliott 337112a907cSsethdelliott streams_accepted++; 3384df383f6Ssethdelliott if (test->on_new_stream) 3394df383f6Ssethdelliott test->on_new_stream(sp); 340b68ac00eSsethdelliott } 3416177a7f1SJef Poskanzer FD_CLR(test->prot_listener, &read_set); 342b68ac00eSsethdelliott } 3438a0b5a5dSsethdelliott 344112a907cSsethdelliott if (streams_accepted == test->num_streams) { 34566ce7ad4Ssethdelliott if (test->protocol->id != Ptcp) { 3468a0b5a5dSsethdelliott FD_CLR(test->prot_listener, &test->read_set); 3478a0b5a5dSsethdelliott close(test->prot_listener); 34866ce7ad4Ssethdelliott } else { 3490bd8d9daSsethdelliott if (test->no_delay || test->settings->mss || test->settings->socket_bufsize) { 35066ce7ad4Ssethdelliott FD_CLR(test->listener, &test->read_set); 35166ce7ad4Ssethdelliott close(test->listener); 352f99bd3b2Ssethdelliott if ((s = netannounce(test->settings->domain, Ptcp, test->bind_address, test->server_port)) < 0) { 353b60a49ddSsethdelliott i_errno = IELISTEN; 354ec2d0670SJef Poskanzer return -1; 355c81883bfSsethdelliott } 35666ce7ad4Ssethdelliott test->listener = s; 357c81883bfSsethdelliott test->max_fd = (s > test->max_fd ? s : test->max_fd); 35866ce7ad4Ssethdelliott FD_SET(test->listener, &test->read_set); 359c81883bfSsethdelliott } 3608556db5dSsethdelliott } 3618a0b5a5dSsethdelliott test->prot_listener = -1; 362465b565cSsethdelliott test->state = TEST_START; 363b68ac00eSsethdelliott if (Nwrite(test->ctrl_sck, &test->state, sizeof(char), Ptcp) < 0) { 364b60a49ddSsethdelliott i_errno = IESENDMESSAGE; 365ec2d0670SJef Poskanzer return -1; 366982c704aSsethdelliott } 367b60a49ddSsethdelliott if (iperf_init_test(test) < 0) 368ec2d0670SJef Poskanzer return -1; 369f1b3bd81Ssethdelliott test->state = TEST_RUNNING; 370f1b3bd81Ssethdelliott if (Nwrite(test->ctrl_sck, &test->state, sizeof(char), Ptcp) < 0) { 371b60a49ddSsethdelliott i_errno = IESENDMESSAGE; 372ec2d0670SJef Poskanzer return -1; 373f1b3bd81Ssethdelliott } 374982c704aSsethdelliott } 375982c704aSsethdelliott } 376982c704aSsethdelliott 377f1b3bd81Ssethdelliott if (test->state == TEST_RUNNING) { 378465b565cSsethdelliott if (test->reverse) { 379465b565cSsethdelliott // Reverse mode. Server sends. 3806177a7f1SJef Poskanzer if (iperf_send(test, &write_set) < 0) 381ec2d0670SJef Poskanzer return -1; 382982c704aSsethdelliott } else { 383465b565cSsethdelliott // Regular mode. Server receives. 3846177a7f1SJef Poskanzer if (iperf_recv(test, &read_set) < 0) 385ec2d0670SJef Poskanzer return -1; 386e99faeaeSBrian Tierney } 387f1b3bd81Ssethdelliott 388ec2d0670SJef Poskanzer /* Run the timers. */ 389ec2d0670SJef Poskanzer (void) gettimeofday(&now, NULL); 390ec2d0670SJef Poskanzer tmr_run(&now); 391f1b3bd81Ssethdelliott } 392a951c980SBrian Tierney } 393982c704aSsethdelliott } 394e99faeaeSBrian Tierney 39502a35f07Ssethdelliott /* Close open test sockets */ 3960bd8d9daSsethdelliott close(test->ctrl_sck); 3970bd8d9daSsethdelliott close(test->listener); 398efdc02f7Ssethdelliott 399ec2d0670SJef Poskanzer return 0; 400a3281a3dSBrian Tierney } 401