1a951c980SBrian Tierney 2a951c980SBrian Tierney /* 3a951c980SBrian Tierney * Copyright (c) 2009, The Regents of the University of California, through 4a951c980SBrian Tierney * Lawrence Berkeley National Laboratory (subject to receipt of any required 5a951c980SBrian Tierney * approvals from the U.S. Dept. of Energy). All rights reserved. 6a951c980SBrian Tierney */ 7a951c980SBrian Tierney 8a951c980SBrian Tierney /* iperf_server_api.c: Functions to be used by an iperf server 9a951c980SBrian Tierney */ 10a951c980SBrian Tierney 11a951c980SBrian Tierney #include <stdio.h> 12a951c980SBrian Tierney #include <stdlib.h> 13a951c980SBrian Tierney #include <string.h> 14a951c980SBrian Tierney #include <getopt.h> 15a951c980SBrian Tierney #include <errno.h> 16a951c980SBrian Tierney #include <unistd.h> 17a951c980SBrian Tierney #include <assert.h> 18a951c980SBrian Tierney #include <fcntl.h> 198a0b5a5dSsethdelliott #include <sys/queue.h> 20a951c980SBrian Tierney #include <sys/socket.h> 21a951c980SBrian Tierney #include <sys/types.h> 22a951c980SBrian Tierney #include <netinet/in.h> 23a951c980SBrian Tierney #include <arpa/inet.h> 24a951c980SBrian Tierney #include <netdb.h> 25a951c980SBrian Tierney #include <pthread.h> 26a951c980SBrian Tierney #include <stdint.h> 27a951c980SBrian Tierney #include <netinet/tcp.h> 28a951c980SBrian Tierney #include <sys/time.h> 29a951c980SBrian Tierney #include <sys/resource.h> 30a951c980SBrian Tierney #include <sched.h> 31a951c980SBrian Tierney #include <setjmp.h> 32a951c980SBrian Tierney 33a951c980SBrian Tierney #include "iperf.h" 34a951c980SBrian Tierney #include "iperf_server_api.h" 35a951c980SBrian Tierney #include "iperf_api.h" 36a951c980SBrian Tierney #include "iperf_udp.h" 37a951c980SBrian Tierney #include "iperf_tcp.h" 38b60a49ddSsethdelliott #include "iperf_error.h" 398a0b5a5dSsethdelliott #include "iperf_util.h" 40a951c980SBrian Tierney #include "timer.h" 41a951c980SBrian Tierney #include "net.h" 42a951c980SBrian Tierney #include "units.h" 43a951c980SBrian Tierney #include "tcp_window_size.h" 44b0b16b86SJon Dugan #include "iperf_util.h" 45a951c980SBrian Tierney #include "locale.h" 46a951c980SBrian Tierney 47a3281a3dSBrian Tierney 48982c704aSsethdelliott int 49982c704aSsethdelliott iperf_server_listen(struct iperf_test *test) 50982c704aSsethdelliott { 51982c704aSsethdelliott char ubuf[UNIT_LEN]; 52982c704aSsethdelliott int x; 53982c704aSsethdelliott 54f99bd3b2Ssethdelliott if((test->listener = netannounce(test->settings->domain, Ptcp, test->bind_address, test->server_port)) < 0) { 55b60a49ddSsethdelliott i_errno = IELISTEN; 56b60a49ddSsethdelliott return (-1); 57982c704aSsethdelliott } 58982c704aSsethdelliott 59982c704aSsethdelliott printf("-----------------------------------------------------------\n"); 60982c704aSsethdelliott printf("Server listening on %d\n", test->server_port); 61982c704aSsethdelliott 62982c704aSsethdelliott // This needs to be changed to reflect if client has different window size 63982c704aSsethdelliott // make sure we got what we asked for 64ba2672a2Ssethdelliott /* XXX: This needs to be moved to the stream listener 6566ce7ad4Ssethdelliott if ((x = get_tcp_windowsize(test->listener, SO_RCVBUF)) < 0) { 66982c704aSsethdelliott // Needs to set some sort of error number/message 67982c704aSsethdelliott perror("SO_RCVBUF"); 68982c704aSsethdelliott return -1; 69982c704aSsethdelliott } 70ba2672a2Ssethdelliott */ 71982c704aSsethdelliott 72ba2672a2Ssethdelliott // XXX: This code needs to be moved to after parameter exhange 73c81883bfSsethdelliott /* 7466ce7ad4Ssethdelliott if (test->protocol->id == Ptcp) { 75112a907cSsethdelliott if (test->settings->socket_bufsize > 0) { 76982c704aSsethdelliott unit_snprintf(ubuf, UNIT_LEN, (double) x, 'A'); 77982c704aSsethdelliott printf("TCP window size: %s\n", ubuf); 78982c704aSsethdelliott } else { 79982c704aSsethdelliott printf("Using TCP Autotuning\n"); 80982c704aSsethdelliott } 81982c704aSsethdelliott } 82c81883bfSsethdelliott */ 83982c704aSsethdelliott printf("-----------------------------------------------------------\n"); 84982c704aSsethdelliott 85982c704aSsethdelliott FD_ZERO(&test->read_set); 86efdc02f7Ssethdelliott FD_ZERO(&test->write_set); 8766ce7ad4Ssethdelliott FD_SET(test->listener, &test->read_set); 8866ce7ad4Ssethdelliott test->max_fd = (test->listener > test->max_fd) ? test->listener : test->max_fd; 89982c704aSsethdelliott 90982c704aSsethdelliott return 0; 91982c704aSsethdelliott } 92982c704aSsethdelliott 93982c704aSsethdelliott int 94982c704aSsethdelliott iperf_accept(struct iperf_test *test) 95982c704aSsethdelliott { 96982c704aSsethdelliott int s; 97465b565cSsethdelliott int rbuf = ACCESS_DENIED; 98af70b448Ssethdelliott char cookie[COOKIE_SIZE]; 99982c704aSsethdelliott socklen_t len; 100982c704aSsethdelliott struct sockaddr_in addr; 101982c704aSsethdelliott 102b68ac00eSsethdelliott len = sizeof(addr); 10366ce7ad4Ssethdelliott if ((s = accept(test->listener, (struct sockaddr *) &addr, &len)) < 0) { 104b60a49ddSsethdelliott i_errno = IEACCEPT; 105b60a49ddSsethdelliott return (-1); 106982c704aSsethdelliott } 107af70b448Ssethdelliott 108af70b448Ssethdelliott if (test->ctrl_sck == -1) { 109af70b448Ssethdelliott /* Server free, accept new client */ 110112a907cSsethdelliott if (Nread(s, test->cookie, COOKIE_SIZE, Ptcp) < 0) { 111b60a49ddSsethdelliott i_errno = IERECVCOOKIE; 112b60a49ddSsethdelliott return (-1); 113af70b448Ssethdelliott } 114af70b448Ssethdelliott 115465b565cSsethdelliott FD_SET(s, &test->read_set); 116465b565cSsethdelliott FD_SET(s, &test->write_set); 117465b565cSsethdelliott test->max_fd = (s > test->max_fd) ? s : test->max_fd; 118465b565cSsethdelliott test->ctrl_sck = s; 119982c704aSsethdelliott 120af70b448Ssethdelliott test->state = PARAM_EXCHANGE; 121af70b448Ssethdelliott if (Nwrite(test->ctrl_sck, &test->state, sizeof(char), Ptcp) < 0) { 122b60a49ddSsethdelliott i_errno = IESENDMESSAGE; 123b60a49ddSsethdelliott return (-1); 124af70b448Ssethdelliott } 125b60a49ddSsethdelliott if (iperf_exchange_parameters(test) < 0) { 126b60a49ddSsethdelliott return (-1); 127b60a49ddSsethdelliott } 1284df383f6Ssethdelliott if (test->on_connect) { 1294df383f6Ssethdelliott test->on_connect(test); 1304df383f6Ssethdelliott } 131982c704aSsethdelliott } else { 132b60a49ddSsethdelliott // XXX: Do we even need to receive cookie if we're just going to deny anyways? 133af70b448Ssethdelliott if (Nread(s, cookie, COOKIE_SIZE, Ptcp) < 0) { 134b60a49ddSsethdelliott i_errno = IERECVCOOKIE; 135b60a49ddSsethdelliott return (-1); 136465b565cSsethdelliott } 137efb7bbe4Ssethdelliott if (Nwrite(s, &rbuf, sizeof(int), Ptcp) < 0) { 138b60a49ddSsethdelliott i_errno = IESENDMESSAGE; 139b60a49ddSsethdelliott return (-1); 140465b565cSsethdelliott } 141465b565cSsethdelliott close(s); 142982c704aSsethdelliott } 143982c704aSsethdelliott 144b60a49ddSsethdelliott return (0); 145af70b448Ssethdelliott } 146af70b448Ssethdelliott 147b68ac00eSsethdelliott 148a951c980SBrian Tierney /**************************************************************************/ 149982c704aSsethdelliott int 150465b565cSsethdelliott iperf_handle_message_server(struct iperf_test *test) 151982c704aSsethdelliott { 152b68ac00eSsethdelliott int rval; 153efdc02f7Ssethdelliott struct iperf_stream *sp; 154efdc02f7Ssethdelliott 155b60a49ddSsethdelliott // XXX: Need to rethink how this behaves to fit API 156b60a49ddSsethdelliott if ((rval = Nread(test->ctrl_sck, &test->state, sizeof(char), Ptcp)) <= 0) { 157b68ac00eSsethdelliott if (rval == 0) { 158b68ac00eSsethdelliott fprintf(stderr, "The client has unexpectedly closed the connection.\n"); 159b60a49ddSsethdelliott i_errno = IECTRLCLOSE; 160b68ac00eSsethdelliott test->state = IPERF_DONE; 161b60a49ddSsethdelliott return (0); 162b68ac00eSsethdelliott } else { 163b60a49ddSsethdelliott i_errno = IERECVMESSAGE; 164b60a49ddSsethdelliott return (-1); 165982c704aSsethdelliott } 166b68ac00eSsethdelliott } 167982c704aSsethdelliott 168982c704aSsethdelliott switch(test->state) { 169ba2672a2Ssethdelliott case TEST_START: 170ba2672a2Ssethdelliott break; 171465b565cSsethdelliott case TEST_END: 172f1b3bd81Ssethdelliott test->stats_callback(test); 173dd4bc008Ssethdelliott SLIST_FOREACH(sp, &test->streams, streams) { 174efdc02f7Ssethdelliott FD_CLR(sp->socket, &test->read_set); 175efdc02f7Ssethdelliott FD_CLR(sp->socket, &test->write_set); 176efdc02f7Ssethdelliott close(sp->socket); 177efdc02f7Ssethdelliott } 178181d61afSsethdelliott test->state = EXCHANGE_RESULTS; 179efb7bbe4Ssethdelliott if (Nwrite(test->ctrl_sck, &test->state, sizeof(char), Ptcp) < 0) { 180b60a49ddSsethdelliott i_errno = IESENDMESSAGE; 181b60a49ddSsethdelliott return (-1); 182181d61afSsethdelliott } 183b60a49ddSsethdelliott if (iperf_exchange_results(test) < 0) 184b60a49ddSsethdelliott return (-1); 185873952e3Ssethdelliott test->state = DISPLAY_RESULTS; 186efb7bbe4Ssethdelliott if (Nwrite(test->ctrl_sck, &test->state, sizeof(char), Ptcp) < 0) { 187b60a49ddSsethdelliott i_errno = IESENDMESSAGE; 188b60a49ddSsethdelliott return (-1); 189465b565cSsethdelliott } 1904df383f6Ssethdelliott if (test->on_test_finish) 1914df383f6Ssethdelliott test->on_test_finish(test); 192181d61afSsethdelliott test->reporter_callback(test); 193465b565cSsethdelliott break; 194873952e3Ssethdelliott case IPERF_DONE: 195873952e3Ssethdelliott break; 196465b565cSsethdelliott case CLIENT_TERMINATE: 197ffdcc7d4Ssethdelliott i_errno = IECLIENTTERM; 198ffdcc7d4Ssethdelliott 199ffdcc7d4Ssethdelliott // XXX: Remove this line below! 2001ac522ecSsethdelliott fprintf(stderr, "The client has terminated.\n"); 201dd4bc008Ssethdelliott SLIST_FOREACH(sp, &test->streams, streams) { 2021ac522ecSsethdelliott FD_CLR(sp->socket, &test->read_set); 2031ac522ecSsethdelliott FD_CLR(sp->socket, &test->write_set); 2041ac522ecSsethdelliott close(sp->socket); 2051ac522ecSsethdelliott } 2061ac522ecSsethdelliott test->state = IPERF_DONE; 2071ac522ecSsethdelliott break; 208ba2672a2Ssethdelliott default: 209b60a49ddSsethdelliott i_errno = IEMESSAGE; 210b60a49ddSsethdelliott return (-1); 211982c704aSsethdelliott } 212982c704aSsethdelliott 213b60a49ddSsethdelliott return (0); 214982c704aSsethdelliott } 215982c704aSsethdelliott 2160bd8d9daSsethdelliott /* XXX: This function is not used anymore */ 217efdc02f7Ssethdelliott void 218efdc02f7Ssethdelliott iperf_test_reset(struct iperf_test *test) 219efdc02f7Ssethdelliott { 2200bd8d9daSsethdelliott struct iperf_stream *sp; 221efdc02f7Ssethdelliott 222efdc02f7Ssethdelliott close(test->ctrl_sck); 223efdc02f7Ssethdelliott 224efdc02f7Ssethdelliott /* Free streams */ 225dd4bc008Ssethdelliott while (!SLIST_EMPTY(&test->streams)) { 226dd4bc008Ssethdelliott sp = SLIST_FIRST(&test->streams); 227dd4bc008Ssethdelliott SLIST_REMOVE_HEAD(&test->streams, streams); 228dd4bc008Ssethdelliott iperf_free_stream(sp); 229dd4bc008Ssethdelliott } 230f1b3bd81Ssethdelliott free_timer(test->timer); 231f1b3bd81Ssethdelliott free_timer(test->stats_timer); 232f1b3bd81Ssethdelliott free_timer(test->reporter_timer); 233f1b3bd81Ssethdelliott test->timer = NULL; 234f1b3bd81Ssethdelliott test->stats_timer = NULL; 235f1b3bd81Ssethdelliott test->reporter_timer = NULL; 236efdc02f7Ssethdelliott 237dd4bc008Ssethdelliott SLIST_INIT(&test->streams); 23832885561Ssethdelliott 239efdc02f7Ssethdelliott test->role = 's'; 24066ce7ad4Ssethdelliott set_protocol(test, Ptcp); 24132885561Ssethdelliott test->duration = DURATION; 24232885561Ssethdelliott test->state = 0; 24332885561Ssethdelliott test->server_hostname = NULL; 24432885561Ssethdelliott 24532885561Ssethdelliott test->ctrl_sck = -1; 24666ce7ad4Ssethdelliott test->prot_listener = -1; 24732885561Ssethdelliott 248f1b3bd81Ssethdelliott test->bytes_sent = 0; 249f1b3bd81Ssethdelliott 25032885561Ssethdelliott test->reverse = 0; 251b68ac00eSsethdelliott test->no_delay = 0; 25232885561Ssethdelliott 253efdc02f7Ssethdelliott FD_ZERO(&test->read_set); 254efdc02f7Ssethdelliott FD_ZERO(&test->write_set); 25566ce7ad4Ssethdelliott FD_SET(test->listener, &test->read_set); 25666ce7ad4Ssethdelliott test->max_fd = test->listener; 25732885561Ssethdelliott 25832885561Ssethdelliott test->num_streams = 1; 259112a907cSsethdelliott test->settings->socket_bufsize = 0; 260112a907cSsethdelliott test->settings->blksize = DEFAULT_TCP_BLKSIZE; 261112a907cSsethdelliott test->settings->rate = RATE; /* UDP only */ 262112a907cSsethdelliott test->settings->mss = 0; 263112a907cSsethdelliott memset(test->cookie, 0, COOKIE_SIZE); 264efdc02f7Ssethdelliott } 265efdc02f7Ssethdelliott 2667fa9f68fSsethdelliott int 267a951c980SBrian Tierney iperf_run_server(struct iperf_test *test) 268a951c980SBrian Tierney { 269112a907cSsethdelliott int result, s, streams_accepted; 270efdc02f7Ssethdelliott fd_set temp_read_set, temp_write_set; 2718a0b5a5dSsethdelliott struct iperf_stream *sp; 2727fa9f68fSsethdelliott struct timeval tv; 2730bd8d9daSsethdelliott time_t sec, usec; 274a951c980SBrian Tierney 275982c704aSsethdelliott // Open socket and listen 276982c704aSsethdelliott if (iperf_server_listen(test) < 0) { 277b60a49ddSsethdelliott return (-1); 278e99faeaeSBrian Tierney } 279a951c980SBrian Tierney 280efdc02f7Ssethdelliott test->state = IPERF_START; 281112a907cSsethdelliott streams_accepted = 0; 282a951c980SBrian Tierney 283873952e3Ssethdelliott while (test->state != IPERF_DONE) { 284982c704aSsethdelliott 285efdc02f7Ssethdelliott memcpy(&temp_read_set, &test->read_set, sizeof(fd_set)); 286efdc02f7Ssethdelliott memcpy(&temp_write_set, &test->write_set, sizeof(fd_set)); 287982c704aSsethdelliott tv.tv_sec = 15; 288982c704aSsethdelliott tv.tv_usec = 0; 289982c704aSsethdelliott 290efdc02f7Ssethdelliott result = select(test->max_fd + 1, &temp_read_set, &temp_write_set, NULL, &tv); 291982c704aSsethdelliott if (result < 0 && errno != EINTR) { 292b60a49ddSsethdelliott i_errno = IESELECT; 293b60a49ddSsethdelliott return (-1); 294982c704aSsethdelliott } else if (result > 0) { 29566ce7ad4Ssethdelliott if (FD_ISSET(test->listener, &temp_read_set)) { 296b68ac00eSsethdelliott if (test->state != CREATE_STREAMS) { 297465b565cSsethdelliott if (iperf_accept(test) < 0) { 298b60a49ddSsethdelliott return (-1); 299982c704aSsethdelliott } 30066ce7ad4Ssethdelliott FD_CLR(test->listener, &temp_read_set); 301982c704aSsethdelliott } 302b68ac00eSsethdelliott } 303efdc02f7Ssethdelliott if (FD_ISSET(test->ctrl_sck, &temp_read_set)) { 304b60a49ddSsethdelliott if (iperf_handle_message_server(test) < 0) 305b60a49ddSsethdelliott return (-1); 306efdc02f7Ssethdelliott FD_CLR(test->ctrl_sck, &temp_read_set); 307982c704aSsethdelliott } 308465b565cSsethdelliott 309465b565cSsethdelliott if (test->state == CREATE_STREAMS) { 3108a0b5a5dSsethdelliott if (FD_ISSET(test->prot_listener, &temp_read_set)) { 3118a0b5a5dSsethdelliott 31266ce7ad4Ssethdelliott if ((s = test->protocol->accept(test)) < 0) 313b60a49ddSsethdelliott return (-1); 3148a0b5a5dSsethdelliott 3158a0b5a5dSsethdelliott if (!is_closed(s)) { 31666ce7ad4Ssethdelliott sp = iperf_new_stream(test, s); 3178a0b5a5dSsethdelliott if (!sp) 318b60a49ddSsethdelliott return (-1); 31966ce7ad4Ssethdelliott 3208a0b5a5dSsethdelliott FD_SET(s, &test->read_set); 3218a0b5a5dSsethdelliott FD_SET(s, &test->write_set); 3228a0b5a5dSsethdelliott test->max_fd = (s > test->max_fd) ? s : test->max_fd; 32366ce7ad4Ssethdelliott 324112a907cSsethdelliott streams_accepted++; 3254df383f6Ssethdelliott if (test->on_new_stream) 3264df383f6Ssethdelliott test->on_new_stream(sp); 327b68ac00eSsethdelliott } 3288a0b5a5dSsethdelliott FD_CLR(test->prot_listener, &temp_read_set); 329b68ac00eSsethdelliott } 3308a0b5a5dSsethdelliott 331112a907cSsethdelliott if (streams_accepted == test->num_streams) { 33266ce7ad4Ssethdelliott if (test->protocol->id != Ptcp) { 3338a0b5a5dSsethdelliott FD_CLR(test->prot_listener, &test->read_set); 3348a0b5a5dSsethdelliott close(test->prot_listener); 33566ce7ad4Ssethdelliott } else { 3360bd8d9daSsethdelliott if (test->no_delay || test->settings->mss || test->settings->socket_bufsize) { 33766ce7ad4Ssethdelliott FD_CLR(test->listener, &test->read_set); 33866ce7ad4Ssethdelliott close(test->listener); 339f99bd3b2Ssethdelliott if ((s = netannounce(test->settings->domain, Ptcp, test->bind_address, test->server_port)) < 0) { 340b60a49ddSsethdelliott i_errno = IELISTEN; 341c81883bfSsethdelliott return (-1); 342c81883bfSsethdelliott } 34366ce7ad4Ssethdelliott test->listener = s; 344c81883bfSsethdelliott test->max_fd = (s > test->max_fd ? s : test->max_fd); 34566ce7ad4Ssethdelliott FD_SET(test->listener, &test->read_set); 346c81883bfSsethdelliott } 3478556db5dSsethdelliott } 3488a0b5a5dSsethdelliott test->prot_listener = -1; 349465b565cSsethdelliott test->state = TEST_START; 350b68ac00eSsethdelliott if (Nwrite(test->ctrl_sck, &test->state, sizeof(char), Ptcp) < 0) { 351b60a49ddSsethdelliott i_errno = IESENDMESSAGE; 352b60a49ddSsethdelliott return (-1); 353982c704aSsethdelliott } 354b60a49ddSsethdelliott if (iperf_init_test(test) < 0) 355b60a49ddSsethdelliott return (-1); 356f1b3bd81Ssethdelliott test->state = TEST_RUNNING; 357f1b3bd81Ssethdelliott if (Nwrite(test->ctrl_sck, &test->state, sizeof(char), Ptcp) < 0) { 358b60a49ddSsethdelliott i_errno = IESENDMESSAGE; 359b60a49ddSsethdelliott return (-1); 360f1b3bd81Ssethdelliott } 361982c704aSsethdelliott } 362982c704aSsethdelliott } 363982c704aSsethdelliott 364f1b3bd81Ssethdelliott if (test->state == TEST_RUNNING) { 365465b565cSsethdelliott if (test->reverse) { 366465b565cSsethdelliott // Reverse mode. Server sends. 367b60a49ddSsethdelliott if (iperf_send(test) < 0) 368b60a49ddSsethdelliott return (-1); 369982c704aSsethdelliott } else { 370465b565cSsethdelliott // Regular mode. Server receives. 371b60a49ddSsethdelliott if (iperf_recv(test) < 0) 372b60a49ddSsethdelliott return (-1); 373e99faeaeSBrian Tierney } 374f1b3bd81Ssethdelliott 375f1b3bd81Ssethdelliott /* Perform callbacks */ 376f1b3bd81Ssethdelliott if (timer_expired(test->stats_timer)) { 377f1b3bd81Ssethdelliott test->stats_callback(test); 3780bd8d9daSsethdelliott sec = (time_t) test->stats_interval; 3790bd8d9daSsethdelliott usec = (test->stats_interval - sec) * SEC_TO_US; 3800bd8d9daSsethdelliott if (update_timer(test->stats_timer, sec, usec) < 0) 381b60a49ddSsethdelliott return (-1); 382f1b3bd81Ssethdelliott } 383f1b3bd81Ssethdelliott if (timer_expired(test->reporter_timer)) { 384f1b3bd81Ssethdelliott test->reporter_callback(test); 3850bd8d9daSsethdelliott sec = (time_t) test->reporter_interval; 3860bd8d9daSsethdelliott usec = (test->reporter_interval - sec) * SEC_TO_US; 3870bd8d9daSsethdelliott if (update_timer(test->reporter_timer, sec, usec) < 0) 388b60a49ddSsethdelliott return (-1); 389f1b3bd81Ssethdelliott } 390f1b3bd81Ssethdelliott } 391a951c980SBrian Tierney } 392982c704aSsethdelliott } 393e99faeaeSBrian Tierney 394*02a35f07Ssethdelliott /* Close open test sockets */ 3950bd8d9daSsethdelliott close(test->ctrl_sck); 3960bd8d9daSsethdelliott close(test->listener); 397efdc02f7Ssethdelliott 398b60a49ddSsethdelliott return (0); 399a3281a3dSBrian Tierney } 400a3281a3dSBrian Tierney 401