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> 19a951c980SBrian Tierney #include <sys/socket.h> 20a951c980SBrian Tierney #include <sys/types.h> 21a951c980SBrian Tierney #include <netinet/in.h> 22a951c980SBrian Tierney #include <arpa/inet.h> 23a951c980SBrian Tierney #include <netdb.h> 24a951c980SBrian Tierney #include <pthread.h> 25a951c980SBrian Tierney #include <stdint.h> 26a951c980SBrian Tierney #include <netinet/tcp.h> 27a951c980SBrian Tierney #include <sys/time.h> 28a951c980SBrian Tierney #include <sys/resource.h> 29a951c980SBrian Tierney #include <sched.h> 30a951c980SBrian Tierney #include <setjmp.h> 31a951c980SBrian Tierney 32a951c980SBrian Tierney #include "iperf.h" 33a951c980SBrian Tierney #include "iperf_server_api.h" 34a951c980SBrian Tierney #include "iperf_api.h" 35a951c980SBrian Tierney #include "iperf_udp.h" 36a951c980SBrian Tierney #include "iperf_tcp.h" 37b60a49ddSsethdelliott #include "iperf_error.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 { 50982c704aSsethdelliott char ubuf[UNIT_LEN]; 51982c704aSsethdelliott int x; 52982c704aSsethdelliott 53f99bd3b2Ssethdelliott if((test->listener = netannounce(test->settings->domain, Ptcp, test->bind_address, test->server_port)) < 0) { 54b60a49ddSsethdelliott i_errno = IELISTEN; 55b60a49ddSsethdelliott return (-1); 56982c704aSsethdelliott } 57982c704aSsethdelliott 58982c704aSsethdelliott printf("-----------------------------------------------------------\n"); 59982c704aSsethdelliott printf("Server listening on %d\n", test->server_port); 60982c704aSsethdelliott 61982c704aSsethdelliott // This needs to be changed to reflect if client has different window size 62982c704aSsethdelliott // make sure we got what we asked for 63ba2672a2Ssethdelliott /* XXX: This needs to be moved to the stream listener 6466ce7ad4Ssethdelliott if ((x = get_tcp_windowsize(test->listener, SO_RCVBUF)) < 0) { 65982c704aSsethdelliott // Needs to set some sort of error number/message 66982c704aSsethdelliott perror("SO_RCVBUF"); 67982c704aSsethdelliott return -1; 68982c704aSsethdelliott } 69ba2672a2Ssethdelliott */ 70982c704aSsethdelliott 71ba2672a2Ssethdelliott // XXX: This code needs to be moved to after parameter exhange 72c81883bfSsethdelliott /* 7366ce7ad4Ssethdelliott if (test->protocol->id == Ptcp) { 74112a907cSsethdelliott if (test->settings->socket_bufsize > 0) { 75982c704aSsethdelliott unit_snprintf(ubuf, UNIT_LEN, (double) x, 'A'); 76982c704aSsethdelliott printf("TCP window size: %s\n", ubuf); 77982c704aSsethdelliott } else { 78982c704aSsethdelliott printf("Using TCP Autotuning\n"); 79982c704aSsethdelliott } 80982c704aSsethdelliott } 81c81883bfSsethdelliott */ 82982c704aSsethdelliott printf("-----------------------------------------------------------\n"); 83982c704aSsethdelliott 84982c704aSsethdelliott FD_ZERO(&test->read_set); 85efdc02f7Ssethdelliott FD_ZERO(&test->write_set); 8666ce7ad4Ssethdelliott FD_SET(test->listener, &test->read_set); 8766ce7ad4Ssethdelliott test->max_fd = (test->listener > test->max_fd) ? test->listener : test->max_fd; 88982c704aSsethdelliott 89982c704aSsethdelliott return 0; 90982c704aSsethdelliott } 91982c704aSsethdelliott 92982c704aSsethdelliott int 93982c704aSsethdelliott iperf_accept(struct iperf_test *test) 94982c704aSsethdelliott { 95982c704aSsethdelliott int s; 96465b565cSsethdelliott int rbuf = ACCESS_DENIED; 97af70b448Ssethdelliott char cookie[COOKIE_SIZE]; 98982c704aSsethdelliott socklen_t len; 99982c704aSsethdelliott struct sockaddr_in addr; 100982c704aSsethdelliott 101b68ac00eSsethdelliott len = sizeof(addr); 10266ce7ad4Ssethdelliott if ((s = accept(test->listener, (struct sockaddr *) &addr, &len)) < 0) { 103b60a49ddSsethdelliott i_errno = IEACCEPT; 104b60a49ddSsethdelliott return (-1); 105982c704aSsethdelliott } 106af70b448Ssethdelliott 107af70b448Ssethdelliott if (test->ctrl_sck == -1) { 108af70b448Ssethdelliott /* Server free, accept new client */ 109112a907cSsethdelliott if (Nread(s, test->cookie, COOKIE_SIZE, Ptcp) < 0) { 110b60a49ddSsethdelliott i_errno = IERECVCOOKIE; 111b60a49ddSsethdelliott return (-1); 112af70b448Ssethdelliott } 113af70b448Ssethdelliott 114465b565cSsethdelliott FD_SET(s, &test->read_set); 115465b565cSsethdelliott FD_SET(s, &test->write_set); 116465b565cSsethdelliott test->max_fd = (s > test->max_fd) ? s : test->max_fd; 117465b565cSsethdelliott test->ctrl_sck = s; 118982c704aSsethdelliott 119af70b448Ssethdelliott test->state = PARAM_EXCHANGE; 120af70b448Ssethdelliott if (Nwrite(test->ctrl_sck, &test->state, sizeof(char), Ptcp) < 0) { 121b60a49ddSsethdelliott i_errno = IESENDMESSAGE; 122b60a49ddSsethdelliott return (-1); 123af70b448Ssethdelliott } 124b60a49ddSsethdelliott if (iperf_exchange_parameters(test) < 0) { 125b60a49ddSsethdelliott return (-1); 126b60a49ddSsethdelliott } 1274df383f6Ssethdelliott if (test->on_connect) { 1284df383f6Ssethdelliott test->on_connect(test); 1294df383f6Ssethdelliott } 130982c704aSsethdelliott } else { 131b60a49ddSsethdelliott // XXX: Do we even need to receive cookie if we're just going to deny anyways? 132af70b448Ssethdelliott if (Nread(s, cookie, COOKIE_SIZE, Ptcp) < 0) { 133b60a49ddSsethdelliott i_errno = IERECVCOOKIE; 134b60a49ddSsethdelliott return (-1); 135465b565cSsethdelliott } 136efb7bbe4Ssethdelliott if (Nwrite(s, &rbuf, sizeof(int), Ptcp) < 0) { 137b60a49ddSsethdelliott i_errno = IESENDMESSAGE; 138b60a49ddSsethdelliott return (-1); 139465b565cSsethdelliott } 140465b565cSsethdelliott close(s); 141982c704aSsethdelliott } 142982c704aSsethdelliott 143b60a49ddSsethdelliott return (0); 144af70b448Ssethdelliott } 145af70b448Ssethdelliott 146b68ac00eSsethdelliott 147a951c980SBrian Tierney /**************************************************************************/ 148982c704aSsethdelliott int 149465b565cSsethdelliott iperf_handle_message_server(struct iperf_test *test) 150982c704aSsethdelliott { 151b68ac00eSsethdelliott int rval; 152efdc02f7Ssethdelliott struct iperf_stream *sp; 153efdc02f7Ssethdelliott 154b60a49ddSsethdelliott // XXX: Need to rethink how this behaves to fit API 155b60a49ddSsethdelliott if ((rval = Nread(test->ctrl_sck, &test->state, sizeof(char), Ptcp)) <= 0) { 156b68ac00eSsethdelliott if (rval == 0) { 157b68ac00eSsethdelliott fprintf(stderr, "The client has unexpectedly closed the connection.\n"); 158b60a49ddSsethdelliott i_errno = IECTRLCLOSE; 159b68ac00eSsethdelliott test->state = IPERF_DONE; 160b60a49ddSsethdelliott return (0); 161b68ac00eSsethdelliott } else { 162b60a49ddSsethdelliott i_errno = IERECVMESSAGE; 163b60a49ddSsethdelliott return (-1); 164982c704aSsethdelliott } 165b68ac00eSsethdelliott } 166982c704aSsethdelliott 167982c704aSsethdelliott switch(test->state) { 168ba2672a2Ssethdelliott case TEST_START: 169ba2672a2Ssethdelliott break; 170465b565cSsethdelliott case TEST_END: 171*3331e801Ssethdelliott cpu_util(&test->cpu_util); 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 280*3331e801Ssethdelliott // Begin calculating CPU utilization 281*3331e801Ssethdelliott cpu_util(NULL); 282*3331e801Ssethdelliott 283efdc02f7Ssethdelliott test->state = IPERF_START; 284112a907cSsethdelliott streams_accepted = 0; 285a951c980SBrian Tierney 286873952e3Ssethdelliott while (test->state != IPERF_DONE) { 287982c704aSsethdelliott 288efdc02f7Ssethdelliott memcpy(&temp_read_set, &test->read_set, sizeof(fd_set)); 289efdc02f7Ssethdelliott memcpy(&temp_write_set, &test->write_set, sizeof(fd_set)); 290982c704aSsethdelliott tv.tv_sec = 15; 291982c704aSsethdelliott tv.tv_usec = 0; 292982c704aSsethdelliott 293efdc02f7Ssethdelliott result = select(test->max_fd + 1, &temp_read_set, &temp_write_set, NULL, &tv); 294982c704aSsethdelliott if (result < 0 && errno != EINTR) { 295b60a49ddSsethdelliott i_errno = IESELECT; 296b60a49ddSsethdelliott return (-1); 297982c704aSsethdelliott } else if (result > 0) { 29866ce7ad4Ssethdelliott if (FD_ISSET(test->listener, &temp_read_set)) { 299b68ac00eSsethdelliott if (test->state != CREATE_STREAMS) { 300465b565cSsethdelliott if (iperf_accept(test) < 0) { 301b60a49ddSsethdelliott return (-1); 302982c704aSsethdelliott } 30366ce7ad4Ssethdelliott FD_CLR(test->listener, &temp_read_set); 304982c704aSsethdelliott } 305b68ac00eSsethdelliott } 306efdc02f7Ssethdelliott if (FD_ISSET(test->ctrl_sck, &temp_read_set)) { 307b60a49ddSsethdelliott if (iperf_handle_message_server(test) < 0) 308b60a49ddSsethdelliott return (-1); 309efdc02f7Ssethdelliott FD_CLR(test->ctrl_sck, &temp_read_set); 310982c704aSsethdelliott } 311465b565cSsethdelliott 312465b565cSsethdelliott if (test->state == CREATE_STREAMS) { 3138a0b5a5dSsethdelliott if (FD_ISSET(test->prot_listener, &temp_read_set)) { 3148a0b5a5dSsethdelliott 31566ce7ad4Ssethdelliott if ((s = test->protocol->accept(test)) < 0) 316b60a49ddSsethdelliott return (-1); 3178a0b5a5dSsethdelliott 3188a0b5a5dSsethdelliott if (!is_closed(s)) { 31966ce7ad4Ssethdelliott sp = iperf_new_stream(test, s); 3208a0b5a5dSsethdelliott if (!sp) 321b60a49ddSsethdelliott return (-1); 32266ce7ad4Ssethdelliott 3238a0b5a5dSsethdelliott FD_SET(s, &test->read_set); 3248a0b5a5dSsethdelliott FD_SET(s, &test->write_set); 3258a0b5a5dSsethdelliott test->max_fd = (s > test->max_fd) ? s : test->max_fd; 32666ce7ad4Ssethdelliott 327112a907cSsethdelliott streams_accepted++; 3284df383f6Ssethdelliott if (test->on_new_stream) 3294df383f6Ssethdelliott test->on_new_stream(sp); 330b68ac00eSsethdelliott } 3318a0b5a5dSsethdelliott FD_CLR(test->prot_listener, &temp_read_set); 332b68ac00eSsethdelliott } 3338a0b5a5dSsethdelliott 334112a907cSsethdelliott if (streams_accepted == test->num_streams) { 33566ce7ad4Ssethdelliott if (test->protocol->id != Ptcp) { 3368a0b5a5dSsethdelliott FD_CLR(test->prot_listener, &test->read_set); 3378a0b5a5dSsethdelliott close(test->prot_listener); 33866ce7ad4Ssethdelliott } else { 3390bd8d9daSsethdelliott if (test->no_delay || test->settings->mss || test->settings->socket_bufsize) { 34066ce7ad4Ssethdelliott FD_CLR(test->listener, &test->read_set); 34166ce7ad4Ssethdelliott close(test->listener); 342f99bd3b2Ssethdelliott if ((s = netannounce(test->settings->domain, Ptcp, test->bind_address, test->server_port)) < 0) { 343b60a49ddSsethdelliott i_errno = IELISTEN; 344c81883bfSsethdelliott return (-1); 345c81883bfSsethdelliott } 34666ce7ad4Ssethdelliott test->listener = s; 347c81883bfSsethdelliott test->max_fd = (s > test->max_fd ? s : test->max_fd); 34866ce7ad4Ssethdelliott FD_SET(test->listener, &test->read_set); 349c81883bfSsethdelliott } 3508556db5dSsethdelliott } 3518a0b5a5dSsethdelliott test->prot_listener = -1; 352465b565cSsethdelliott test->state = TEST_START; 353b68ac00eSsethdelliott if (Nwrite(test->ctrl_sck, &test->state, sizeof(char), Ptcp) < 0) { 354b60a49ddSsethdelliott i_errno = IESENDMESSAGE; 355b60a49ddSsethdelliott return (-1); 356982c704aSsethdelliott } 357b60a49ddSsethdelliott if (iperf_init_test(test) < 0) 358b60a49ddSsethdelliott return (-1); 359f1b3bd81Ssethdelliott test->state = TEST_RUNNING; 360f1b3bd81Ssethdelliott if (Nwrite(test->ctrl_sck, &test->state, sizeof(char), Ptcp) < 0) { 361b60a49ddSsethdelliott i_errno = IESENDMESSAGE; 362b60a49ddSsethdelliott return (-1); 363f1b3bd81Ssethdelliott } 364982c704aSsethdelliott } 365982c704aSsethdelliott } 366982c704aSsethdelliott 367f1b3bd81Ssethdelliott if (test->state == TEST_RUNNING) { 368465b565cSsethdelliott if (test->reverse) { 369465b565cSsethdelliott // Reverse mode. Server sends. 370b60a49ddSsethdelliott if (iperf_send(test) < 0) 371b60a49ddSsethdelliott return (-1); 372982c704aSsethdelliott } else { 373465b565cSsethdelliott // Regular mode. Server receives. 374b60a49ddSsethdelliott if (iperf_recv(test) < 0) 375b60a49ddSsethdelliott return (-1); 376e99faeaeSBrian Tierney } 377f1b3bd81Ssethdelliott 378f1b3bd81Ssethdelliott /* Perform callbacks */ 379f1b3bd81Ssethdelliott if (timer_expired(test->stats_timer)) { 380f1b3bd81Ssethdelliott test->stats_callback(test); 3810bd8d9daSsethdelliott sec = (time_t) test->stats_interval; 3820bd8d9daSsethdelliott usec = (test->stats_interval - sec) * SEC_TO_US; 3830bd8d9daSsethdelliott if (update_timer(test->stats_timer, sec, usec) < 0) 384b60a49ddSsethdelliott return (-1); 385f1b3bd81Ssethdelliott } 386f1b3bd81Ssethdelliott if (timer_expired(test->reporter_timer)) { 387f1b3bd81Ssethdelliott test->reporter_callback(test); 3880bd8d9daSsethdelliott sec = (time_t) test->reporter_interval; 3890bd8d9daSsethdelliott usec = (test->reporter_interval - sec) * SEC_TO_US; 3900bd8d9daSsethdelliott if (update_timer(test->reporter_timer, sec, usec) < 0) 391b60a49ddSsethdelliott return (-1); 392f1b3bd81Ssethdelliott } 393f1b3bd81Ssethdelliott } 394a951c980SBrian Tierney } 395982c704aSsethdelliott } 396e99faeaeSBrian Tierney 39702a35f07Ssethdelliott /* Close open test sockets */ 3980bd8d9daSsethdelliott close(test->ctrl_sck); 3990bd8d9daSsethdelliott close(test->listener); 400efdc02f7Ssethdelliott 401b60a49ddSsethdelliott return (0); 402a3281a3dSBrian Tierney } 403a3281a3dSBrian Tierney 404