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 <signal.h> 17a951c980SBrian Tierney #include <unistd.h> 18a951c980SBrian Tierney #include <assert.h> 19a951c980SBrian Tierney #include <fcntl.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" 38a951c980SBrian Tierney #include "timer.h" 39a951c980SBrian Tierney #include "net.h" 40a951c980SBrian Tierney #include "units.h" 41a951c980SBrian Tierney #include "tcp_window_size.h" 42a951c980SBrian Tierney #include "uuid.h" 43a951c980SBrian Tierney #include "locale.h" 44a951c980SBrian Tierney 45465b565cSsethdelliott jmp_buf env; 46a3281a3dSBrian Tierney 47982c704aSsethdelliott int 48982c704aSsethdelliott iperf_server_listen(struct iperf_test *test) 49982c704aSsethdelliott { 50982c704aSsethdelliott char ubuf[UNIT_LEN]; 51982c704aSsethdelliott int x; 52982c704aSsethdelliott 53af70b448Ssethdelliott if((test->listener_tcp = netannounce(Ptcp, NULL, test->server_port)) < 0) { 54982c704aSsethdelliott // Needs to set some sort of error number/message 55af70b448Ssethdelliott perror("netannounce test->listener_tcp"); 56982c704aSsethdelliott 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 65982c704aSsethdelliott if ((x = get_tcp_windowsize(test->listener_sock_tcp, 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 73982c704aSsethdelliott if (test->protocol == Ptcp) { 74982c704aSsethdelliott if (test->default_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 } 81982c704aSsethdelliott printf("-----------------------------------------------------------\n"); 82982c704aSsethdelliott 83982c704aSsethdelliott FD_ZERO(&test->read_set); 84efdc02f7Ssethdelliott FD_ZERO(&test->write_set); 85af70b448Ssethdelliott FD_SET(test->listener_tcp, &test->read_set); 86af70b448Ssethdelliott test->max_fd = (test->listener_tcp > test->max_fd) ? test->listener_tcp : test->max_fd; 87982c704aSsethdelliott 88982c704aSsethdelliott return 0; 89982c704aSsethdelliott } 90982c704aSsethdelliott 91982c704aSsethdelliott int 92982c704aSsethdelliott iperf_accept(struct iperf_test *test) 93982c704aSsethdelliott { 94982c704aSsethdelliott int s; 95465b565cSsethdelliott int rbuf = ACCESS_DENIED; 96982c704aSsethdelliott char ipl[512], ipr[512]; 97af70b448Ssethdelliott char cookie[COOKIE_SIZE]; 98982c704aSsethdelliott socklen_t len; 99982c704aSsethdelliott struct sockaddr_in addr; 100465b565cSsethdelliott struct sockaddr_in temp1, temp2; 101af70b448Ssethdelliott struct iperf_stream *sp; 102af70b448Ssethdelliott static int streams_accepted; 103982c704aSsethdelliott 104af70b448Ssethdelliott if ((s = accept(test->listener_tcp, (struct sockaddr *) &addr, &len)) < 0) { 105982c704aSsethdelliott perror("accept"); 106982c704aSsethdelliott return -1; 107982c704aSsethdelliott } 108af70b448Ssethdelliott 109af70b448Ssethdelliott if (test->ctrl_sck == -1) { 110af70b448Ssethdelliott /* Server free, accept new client */ 111af70b448Ssethdelliott if (Nread(s, test->default_settings->cookie, COOKIE_SIZE, Ptcp) < 0) { 112af70b448Ssethdelliott perror("Nread COOKIE\n"); 113af70b448Ssethdelliott return -1; 114af70b448Ssethdelliott } 115af70b448Ssethdelliott 116465b565cSsethdelliott FD_SET(s, &test->read_set); 117465b565cSsethdelliott FD_SET(s, &test->write_set); 118465b565cSsethdelliott test->max_fd = (s > test->max_fd) ? s : test->max_fd; 119465b565cSsethdelliott test->ctrl_sck = s; 120af70b448Ssethdelliott streams_accepted = 0; 121982c704aSsethdelliott 122465b565cSsethdelliott len = sizeof(struct sockaddr_in); 123465b565cSsethdelliott if (getsockname(s, (struct sockaddr *) &temp1, &len) < 0) 124465b565cSsethdelliott perror("getsockname"); 125465b565cSsethdelliott if (getpeername(s, (struct sockaddr *) &temp2, &len) < 0) 126465b565cSsethdelliott perror("getpeername"); 127982c704aSsethdelliott 128465b565cSsethdelliott inet_ntop(AF_INET, (void *) &temp1.sin_addr, ipl, sizeof(ipl)); 129465b565cSsethdelliott inet_ntop(AF_INET, (void *) &temp2.sin_addr, ipr, sizeof(ipr)); 130465b565cSsethdelliott printf(report_peer, s, ipl, ntohs(temp1.sin_port), ipr, ntohs(temp2.sin_port)); 131465b565cSsethdelliott 132af70b448Ssethdelliott test->state = PARAM_EXCHANGE; 133af70b448Ssethdelliott if (Nwrite(test->ctrl_sck, &test->state, sizeof(char), Ptcp) < 0) { 134af70b448Ssethdelliott perror("Nwrite PARAM_EXCHANGE\n"); 135af70b448Ssethdelliott exit(1); 136af70b448Ssethdelliott } 137af70b448Ssethdelliott iperf_exchange_parameters(test); 138982c704aSsethdelliott } else { 139af70b448Ssethdelliott if (Nread(s, cookie, COOKIE_SIZE, Ptcp) < 0) { 140af70b448Ssethdelliott perror("Nread COOKIE\n"); 141465b565cSsethdelliott return -1; 142465b565cSsethdelliott } 143af70b448Ssethdelliott if ((strcmp(test->default_settings->cookie, cookie) == 0) && (test->state == CREATE_STREAMS)) { 144af70b448Ssethdelliott // XXX: CANNOT USE iperf_tcp_accept since stream is alread accepted at this point. New model needed! 145af70b448Ssethdelliott sp = test->new_stream(test); 146af70b448Ssethdelliott sp->socket = s; 147af70b448Ssethdelliott iperf_init_stream(sp, test); 148af70b448Ssethdelliott iperf_add_stream(test, sp); 149af70b448Ssethdelliott FD_SET(s, &test->read_set); 150af70b448Ssethdelliott FD_SET(s, &test->write_set); 151af70b448Ssethdelliott test->max_fd = (s > test->max_fd) ? s : test->max_fd; 152af70b448Ssethdelliott connect_msg(sp); 153af70b448Ssethdelliott 154af70b448Ssethdelliott ++streams_accepted; 155af70b448Ssethdelliott if (streams_accepted == test->num_streams) { 156af70b448Ssethdelliott test->state = TEST_START; 157af70b448Ssethdelliott if (Nwrite(test->ctrl_sck, &test->state, sizeof(char), Ptcp) < 0) { 158af70b448Ssethdelliott perror("Nwrite TEST_START\n"); 159af70b448Ssethdelliott return -1; 160af70b448Ssethdelliott } 161af70b448Ssethdelliott } 162af70b448Ssethdelliott } else { 163*efb7bbe4Ssethdelliott if (Nwrite(s, &rbuf, sizeof(int), Ptcp) < 0) { 164*efb7bbe4Ssethdelliott perror("Nwrite ACCESS_DENIED"); 165465b565cSsethdelliott return -1; 166465b565cSsethdelliott } 167465b565cSsethdelliott close(s); 168982c704aSsethdelliott } 169982c704aSsethdelliott } 170982c704aSsethdelliott 171af70b448Ssethdelliott return 0; 172af70b448Ssethdelliott } 173af70b448Ssethdelliott 174a951c980SBrian Tierney /**************************************************************************/ 175982c704aSsethdelliott int 176465b565cSsethdelliott iperf_handle_message_server(struct iperf_test *test) 177982c704aSsethdelliott { 178efdc02f7Ssethdelliott struct iperf_stream *sp; 179efdc02f7Ssethdelliott 18036d18d07Ssethdelliott if (read(test->ctrl_sck, &test->state, sizeof(char)) < 0) { 181ba2672a2Ssethdelliott // XXX: Needs to indicate read error 182982c704aSsethdelliott return -1; 183982c704aSsethdelliott } 184982c704aSsethdelliott 185982c704aSsethdelliott switch(test->state) { 186ba2672a2Ssethdelliott case TEST_START: 187ba2672a2Ssethdelliott break; 188465b565cSsethdelliott case TEST_RUNNING: 189465b565cSsethdelliott break; 190465b565cSsethdelliott case TEST_END: 191efdc02f7Ssethdelliott for (sp = test->streams; sp; sp = sp->next) { 192efdc02f7Ssethdelliott FD_CLR(sp->socket, &test->read_set); 193efdc02f7Ssethdelliott FD_CLR(sp->socket, &test->write_set); 194efdc02f7Ssethdelliott close(sp->socket); 195efdc02f7Ssethdelliott } 196181d61afSsethdelliott test->state = EXCHANGE_RESULTS; 197*efb7bbe4Ssethdelliott if (Nwrite(test->ctrl_sck, &test->state, sizeof(char), Ptcp) < 0) { 198*efb7bbe4Ssethdelliott perror("Nwrite EXCHANGE_RESULTS"); 199181d61afSsethdelliott exit(1); 200181d61afSsethdelliott } 201181d61afSsethdelliott iperf_exchange_results(test); 202873952e3Ssethdelliott test->state = DISPLAY_RESULTS; 203*efb7bbe4Ssethdelliott if (Nwrite(test->ctrl_sck, &test->state, sizeof(char), Ptcp) < 0) { 204*efb7bbe4Ssethdelliott perror("Nwrite DISPLAY_RESULTS"); 205465b565cSsethdelliott exit(1); 206465b565cSsethdelliott } 20732885561Ssethdelliott test->stats_callback(test); 208181d61afSsethdelliott test->reporter_callback(test); 209465b565cSsethdelliott break; 210873952e3Ssethdelliott case IPERF_DONE: 211873952e3Ssethdelliott break; 212465b565cSsethdelliott case CLIENT_TERMINATE: 213465b565cSsethdelliott fprintf(stderr, "The client has terminated. Exiting...\n"); 214ba2672a2Ssethdelliott exit(1); 215ba2672a2Ssethdelliott default: 216ba2672a2Ssethdelliott // XXX: This needs to be replaced by actual error handling 2177fa9f68fSsethdelliott fprintf(stderr, "How did you get here? test->state = %d\n", test->state); 218ba2672a2Ssethdelliott return -1; 219982c704aSsethdelliott } 220982c704aSsethdelliott 221982c704aSsethdelliott return 0; 222982c704aSsethdelliott } 223982c704aSsethdelliott 224efdc02f7Ssethdelliott void 225efdc02f7Ssethdelliott iperf_test_reset(struct iperf_test *test) 226efdc02f7Ssethdelliott { 227efdc02f7Ssethdelliott struct iperf_stream *sp, *np; 228efdc02f7Ssethdelliott 229efdc02f7Ssethdelliott close(test->ctrl_sck); 230efdc02f7Ssethdelliott 231efdc02f7Ssethdelliott /* Free streams */ 232efdc02f7Ssethdelliott for (sp = test->streams; sp; sp = np) { 233efdc02f7Ssethdelliott np = sp->next; 234efdc02f7Ssethdelliott iperf_free_stream(sp); 235efdc02f7Ssethdelliott } 236efdc02f7Ssethdelliott 23732885561Ssethdelliott test->streams = NULL; 23832885561Ssethdelliott 239efdc02f7Ssethdelliott test->role = 's'; 24032885561Ssethdelliott test->protocol = Ptcp; 24132885561Ssethdelliott test->duration = DURATION; 24232885561Ssethdelliott test->state = 0; 24332885561Ssethdelliott test->server_hostname = NULL; 24432885561Ssethdelliott 24532885561Ssethdelliott test->ctrl_sck = -1; 24632885561Ssethdelliott test->prot_listener = 0; 24732885561Ssethdelliott 24832885561Ssethdelliott test->reverse = 0; 24932885561Ssethdelliott // test->no_delay = 0 (Do we need this reset between tests?) 25032885561Ssethdelliott 251efdc02f7Ssethdelliott FD_ZERO(&test->read_set); 252efdc02f7Ssethdelliott FD_ZERO(&test->write_set); 253af70b448Ssethdelliott FD_SET(test->listener_tcp, &test->read_set); 254af70b448Ssethdelliott test->max_fd = test->listener_tcp; 25532885561Ssethdelliott 25632885561Ssethdelliott test->num_streams = 1; 25732885561Ssethdelliott test->default_settings->socket_bufsize = 0; 25832885561Ssethdelliott test->default_settings->blksize = DEFAULT_TCP_BLKSIZE; 25932885561Ssethdelliott test->default_settings->rate = RATE; /* UDP only */ 26032885561Ssethdelliott memset(test->default_settings->cookie, 0, COOKIE_SIZE); 261efdc02f7Ssethdelliott } 262efdc02f7Ssethdelliott 2637fa9f68fSsethdelliott int 264a951c980SBrian Tierney iperf_run_server(struct iperf_test *test) 265a951c980SBrian Tierney { 2667fa9f68fSsethdelliott int result; 267efdc02f7Ssethdelliott int streams_accepted; 268efdc02f7Ssethdelliott fd_set temp_read_set, temp_write_set; 2697fa9f68fSsethdelliott struct timeval tv; 270a951c980SBrian Tierney 271982c704aSsethdelliott // Open socket and listen 272982c704aSsethdelliott if (iperf_server_listen(test) < 0) { 273982c704aSsethdelliott // This needs to be replaced by more formal error handling 274982c704aSsethdelliott fprintf(stderr, "An error occurred. Exiting.\n"); 275982c704aSsethdelliott exit(1); 276e99faeaeSBrian Tierney } 277a951c980SBrian Tierney 278465b565cSsethdelliott signal(SIGINT, sig_handler); 279465b565cSsethdelliott if (setjmp(env)) { 280465b565cSsethdelliott fprintf(stderr, "Interrupt received. Exiting...\n"); 281465b565cSsethdelliott test->state = SERVER_TERMINATE; 282465b565cSsethdelliott if (test->ctrl_sck >= 0) { 283*efb7bbe4Ssethdelliott if (Nwrite(test->ctrl_sck, &test->state, sizeof(char), Ptcp) < 0) { 284465b565cSsethdelliott fprintf(stderr, "Unable to send SERVER_TERMINATE message to client\n"); 285465b565cSsethdelliott } 286465b565cSsethdelliott } 287465b565cSsethdelliott exit(1); 288465b565cSsethdelliott } 289465b565cSsethdelliott 290efdc02f7Ssethdelliott for (;;) { 291efdc02f7Ssethdelliott 292efdc02f7Ssethdelliott test->state = IPERF_START; 293efdc02f7Ssethdelliott streams_accepted = 0; 294a951c980SBrian Tierney 295873952e3Ssethdelliott while (test->state != IPERF_DONE) { 296982c704aSsethdelliott 297efdc02f7Ssethdelliott memcpy(&temp_read_set, &test->read_set, sizeof(fd_set)); 298efdc02f7Ssethdelliott memcpy(&temp_write_set, &test->write_set, sizeof(fd_set)); 299982c704aSsethdelliott tv.tv_sec = 15; 300982c704aSsethdelliott tv.tv_usec = 0; 301982c704aSsethdelliott 302efdc02f7Ssethdelliott result = select(test->max_fd + 1, &temp_read_set, &temp_write_set, NULL, &tv); 303982c704aSsethdelliott if (result < 0 && errno != EINTR) { 304982c704aSsethdelliott // Change the way this handles errors 305982c704aSsethdelliott perror("select"); 306982c704aSsethdelliott exit(1); 307982c704aSsethdelliott } else if (result > 0) { 308af70b448Ssethdelliott if (FD_ISSET(test->listener_tcp, &temp_read_set)) { 309465b565cSsethdelliott if (iperf_accept(test) < 0) { 310465b565cSsethdelliott fprintf(stderr, "iperf_accept: error accepting control socket. exiting...\n"); 311982c704aSsethdelliott exit(1); 312982c704aSsethdelliott } 313af70b448Ssethdelliott FD_CLR(test->listener_tcp, &temp_read_set); 314982c704aSsethdelliott } 315efdc02f7Ssethdelliott if (FD_ISSET(test->ctrl_sck, &temp_read_set)) { 316465b565cSsethdelliott iperf_handle_message_server(test); 317efdc02f7Ssethdelliott FD_CLR(test->ctrl_sck, &temp_read_set); 318982c704aSsethdelliott } 319465b565cSsethdelliott 320af70b448Ssethdelliott /* 321465b565cSsethdelliott if (test->state == CREATE_STREAMS) { 322efdc02f7Ssethdelliott if (FD_ISSET(test->prot_listener, &temp_read_set)) { 323982c704aSsethdelliott // Spawn new streams 324465b565cSsethdelliott // XXX: This works, but should it check cookies for authenticity 325465b565cSsethdelliott // Also, currently it uses 5202 for stream connections. 326465b565cSsethdelliott // Should this be fixed to use 5201 instead? 327ba2672a2Ssethdelliott iperf_tcp_accept(test); 328465b565cSsethdelliott ++streams_accepted; 329efdc02f7Ssethdelliott FD_CLR(test->prot_listener, &temp_read_set); 330982c704aSsethdelliott } 331465b565cSsethdelliott if (streams_accepted == test->num_streams) { 332465b565cSsethdelliott FD_CLR(test->prot_listener, &test->read_set); 333465b565cSsethdelliott close(test->prot_listener); 334465b565cSsethdelliott test->state = TEST_START; 33536d18d07Ssethdelliott if (write(test->ctrl_sck, &test->state, sizeof(char)) < 0) { 336465b565cSsethdelliott perror("write TEST_START"); 337465b565cSsethdelliott return -1; 338982c704aSsethdelliott } 339982c704aSsethdelliott } 340982c704aSsethdelliott } 341af70b448Ssethdelliott */ 342982c704aSsethdelliott 343465b565cSsethdelliott if (test->reverse) { 344465b565cSsethdelliott // Reverse mode. Server sends. 345465b565cSsethdelliott iperf_send(test); 346982c704aSsethdelliott } else { 347465b565cSsethdelliott // Regular mode. Server receives. 348465b565cSsethdelliott iperf_recv(test); 349e99faeaeSBrian Tierney } 350a951c980SBrian Tierney } 351982c704aSsethdelliott } 352e99faeaeSBrian Tierney 353efdc02f7Ssethdelliott /* Clean up the last test */ 354efdc02f7Ssethdelliott iperf_test_reset(test); 355efdc02f7Ssethdelliott printf("\n"); 356efdc02f7Ssethdelliott 357efdc02f7Ssethdelliott } 358efdc02f7Ssethdelliott 3594f431119Ssethdelliott // XXX: Need to put the above while loop into another control structure 3604f431119Ssethdelliott // that initiates a new test upon each connection rather than dying. 3615083c489SBrian Tierney 3627fa9f68fSsethdelliott return 0; 363a3281a3dSBrian Tierney } 364a3281a3dSBrian Tierney 365