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 53982c704aSsethdelliott if((test->listener = netannounce(Ptcp, NULL, test->server_port)) < 0) { 54982c704aSsethdelliott // Needs to set some sort of error number/message 55465b565cSsethdelliott perror("netannounce test->listener"); 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 73ba2672a2Ssethdelliott // XXX: Last thing I was working on 74982c704aSsethdelliott if (test->protocol == Ptcp) { 75982c704aSsethdelliott if (test->default_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 } 82982c704aSsethdelliott printf("-----------------------------------------------------------\n"); 83982c704aSsethdelliott 84982c704aSsethdelliott FD_ZERO(&test->read_set); 85982c704aSsethdelliott FD_ZERO(&test->temp_set); 86982c704aSsethdelliott FD_SET(test->listener, &test->read_set); 87465b565cSsethdelliott 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; 97982c704aSsethdelliott char ipl[512], ipr[512]; 98982c704aSsethdelliott socklen_t len; 99982c704aSsethdelliott struct sockaddr_in addr; 100465b565cSsethdelliott struct sockaddr_in temp1, temp2; 101982c704aSsethdelliott 102465b565cSsethdelliott if (test->ctrl_sck == -1) { 103982c704aSsethdelliott if ((s = accept(test->listener, (struct sockaddr *) &addr, &len)) < 0) { 104982c704aSsethdelliott perror("accept"); 105982c704aSsethdelliott return -1; 106982c704aSsethdelliott } 107465b565cSsethdelliott FD_SET(s, &test->read_set); 108465b565cSsethdelliott FD_SET(s, &test->write_set); 109465b565cSsethdelliott test->max_fd = (s > test->max_fd) ? s : test->max_fd; 110465b565cSsethdelliott test->ctrl_sck = s; 111982c704aSsethdelliott 112465b565cSsethdelliott len = sizeof(struct sockaddr_in); 113465b565cSsethdelliott if (getsockname(s, (struct sockaddr *) &temp1, &len) < 0) 114465b565cSsethdelliott perror("getsockname"); 115465b565cSsethdelliott if (getpeername(s, (struct sockaddr *) &temp2, &len) < 0) 116465b565cSsethdelliott perror("getpeername"); 117982c704aSsethdelliott 118465b565cSsethdelliott inet_ntop(AF_INET, (void *) &temp1.sin_addr, ipl, sizeof(ipl)); 119465b565cSsethdelliott inet_ntop(AF_INET, (void *) &temp2.sin_addr, ipr, sizeof(ipr)); 120465b565cSsethdelliott printf(report_peer, s, ipl, ntohs(temp1.sin_port), ipr, ntohs(temp2.sin_port)); 121465b565cSsethdelliott 122465b565cSsethdelliott return 0; 123465b565cSsethdelliott 124982c704aSsethdelliott } else { 125982c704aSsethdelliott // This message needs to be sent to the client 126465b565cSsethdelliott if ((s = accept(test->listener, (struct sockaddr *) &addr, &len)) < 0) { 127465b565cSsethdelliott perror("accept"); 128465b565cSsethdelliott return -1; 129465b565cSsethdelliott } 130465b565cSsethdelliott if (write(s, &rbuf, sizeof(int)) < 0) { 131465b565cSsethdelliott perror("write"); 132465b565cSsethdelliott return -1; 133465b565cSsethdelliott } 134465b565cSsethdelliott close(s); 135982c704aSsethdelliott return 0; 136982c704aSsethdelliott } 137982c704aSsethdelliott } 138982c704aSsethdelliott 139a951c980SBrian Tierney /**************************************************************************/ 140982c704aSsethdelliott int 141465b565cSsethdelliott iperf_handle_message_server(struct iperf_test *test) 142982c704aSsethdelliott { 14336d18d07Ssethdelliott if (read(test->ctrl_sck, &test->state, sizeof(char)) < 0) { 144ba2672a2Ssethdelliott // XXX: Needs to indicate read error 145982c704aSsethdelliott return -1; 146982c704aSsethdelliott } 147982c704aSsethdelliott 148982c704aSsethdelliott switch(test->state) { 149982c704aSsethdelliott case PARAM_EXCHANGE: 150982c704aSsethdelliott iperf_exchange_parameters(test); 151982c704aSsethdelliott break; 152ba2672a2Ssethdelliott case TEST_START: 153ba2672a2Ssethdelliott break; 154465b565cSsethdelliott case TEST_RUNNING: 155465b565cSsethdelliott break; 156465b565cSsethdelliott case TEST_END: 157*181d61afSsethdelliott test->state = EXCHANGE_RESULTS; 158*181d61afSsethdelliott if (write(test->ctrl_sck, &test->state, sizeof(char)) < 0) { 159*181d61afSsethdelliott perror("write EXCHANGE_RESULTS"); 160*181d61afSsethdelliott exit(1); 161*181d61afSsethdelliott } 162*181d61afSsethdelliott iperf_exchange_results(test); 163873952e3Ssethdelliott test->state = DISPLAY_RESULTS; 16436d18d07Ssethdelliott if (write(test->ctrl_sck, &test->state, sizeof(char)) < 0) { 165873952e3Ssethdelliott perror("write DISPLAY_RESULTS"); 166465b565cSsethdelliott exit(1); 167465b565cSsethdelliott } 168*181d61afSsethdelliott test->reporter_callback(test); 169465b565cSsethdelliott break; 170873952e3Ssethdelliott case IPERF_DONE: 171873952e3Ssethdelliott break; 172465b565cSsethdelliott case CLIENT_TERMINATE: 173465b565cSsethdelliott fprintf(stderr, "The client has terminated. Exiting...\n"); 174ba2672a2Ssethdelliott exit(1); 175ba2672a2Ssethdelliott default: 176ba2672a2Ssethdelliott // XXX: This needs to be replaced by actual error handling 1777fa9f68fSsethdelliott fprintf(stderr, "How did you get here? test->state = %d\n", test->state); 178ba2672a2Ssethdelliott return -1; 179982c704aSsethdelliott } 180982c704aSsethdelliott 181982c704aSsethdelliott return 0; 182982c704aSsethdelliott } 183982c704aSsethdelliott 1847fa9f68fSsethdelliott int 185a951c980SBrian Tierney iperf_run_server(struct iperf_test *test) 186a951c980SBrian Tierney { 1877fa9f68fSsethdelliott int result; 188465b565cSsethdelliott int streams_accepted = 0; 1897fa9f68fSsethdelliott struct timeval tv; 190a951c980SBrian Tierney 191982c704aSsethdelliott // Open socket and listen 192982c704aSsethdelliott if (iperf_server_listen(test) < 0) { 193982c704aSsethdelliott // This needs to be replaced by more formal error handling 194982c704aSsethdelliott fprintf(stderr, "An error occurred. Exiting.\n"); 195982c704aSsethdelliott exit(1); 196e99faeaeSBrian Tierney } 197a951c980SBrian Tierney 198465b565cSsethdelliott signal(SIGINT, sig_handler); 199465b565cSsethdelliott if (setjmp(env)) { 200465b565cSsethdelliott fprintf(stderr, "Interrupt received. Exiting...\n"); 201465b565cSsethdelliott test->state = SERVER_TERMINATE; 202465b565cSsethdelliott if (test->ctrl_sck >= 0) { 20336d18d07Ssethdelliott if (write(test->ctrl_sck, &test->state, sizeof(char)) < 0) { 204465b565cSsethdelliott fprintf(stderr, "Unable to send SERVER_TERMINATE message to client\n"); 205465b565cSsethdelliott } 206465b565cSsethdelliott } 207465b565cSsethdelliott exit(1); 208465b565cSsethdelliott } 209465b565cSsethdelliott 210a951c980SBrian Tierney test->default_settings->state = TEST_RUNNING; 211a951c980SBrian Tierney 212873952e3Ssethdelliott while (test->state != IPERF_DONE) { 213982c704aSsethdelliott 21459aaeda7Ssethdelliott // XXX: Move test->temp_set over to local fd_set 21559aaeda7Ssethdelliott memcpy(&test->temp_set, &test->read_set, sizeof(fd_set)); 216982c704aSsethdelliott tv.tv_sec = 15; 217982c704aSsethdelliott tv.tv_usec = 0; 218982c704aSsethdelliott 219982c704aSsethdelliott result = select(test->max_fd + 1, &test->temp_set, NULL, NULL, &tv); 220982c704aSsethdelliott if (result < 0 && errno != EINTR) { 221982c704aSsethdelliott // Change the way this handles errors 222982c704aSsethdelliott perror("select"); 223982c704aSsethdelliott exit(1); 224982c704aSsethdelliott } else if (result > 0) { 225982c704aSsethdelliott if (FD_ISSET(test->listener, &test->temp_set)) { 226465b565cSsethdelliott if (iperf_accept(test) < 0) { 227465b565cSsethdelliott fprintf(stderr, "iperf_accept: error accepting control socket. exiting...\n"); 228982c704aSsethdelliott exit(1); 229982c704aSsethdelliott } 230982c704aSsethdelliott FD_CLR(test->listener, &test->temp_set); 231982c704aSsethdelliott } 232982c704aSsethdelliott if (FD_ISSET(test->ctrl_sck, &test->temp_set)) { 233982c704aSsethdelliott // Handle control messages 234465b565cSsethdelliott iperf_handle_message_server(test); 235982c704aSsethdelliott FD_CLR(test->ctrl_sck, &test->temp_set); 236982c704aSsethdelliott } 237465b565cSsethdelliott 238465b565cSsethdelliott if (test->state == CREATE_STREAMS) { 239982c704aSsethdelliott if (FD_ISSET(test->prot_listener, &test->temp_set)) { 240982c704aSsethdelliott // Spawn new streams 241465b565cSsethdelliott // XXX: This works, but should it check cookies for authenticity 242465b565cSsethdelliott // Also, currently it uses 5202 for stream connections. 243465b565cSsethdelliott // Should this be fixed to use 5201 instead? 244ba2672a2Ssethdelliott iperf_tcp_accept(test); 245465b565cSsethdelliott ++streams_accepted; 246465b565cSsethdelliott FD_CLR(test->prot_listener, &test->temp_set); 247982c704aSsethdelliott } 248465b565cSsethdelliott if (streams_accepted == test->num_streams) { 249465b565cSsethdelliott FD_CLR(test->prot_listener, &test->read_set); 250465b565cSsethdelliott close(test->prot_listener); 251465b565cSsethdelliott test->state = TEST_START; 25236d18d07Ssethdelliott if (write(test->ctrl_sck, &test->state, sizeof(char)) < 0) { 253465b565cSsethdelliott perror("write TEST_START"); 254465b565cSsethdelliott return -1; 255982c704aSsethdelliott } 256982c704aSsethdelliott } 257982c704aSsethdelliott } 258982c704aSsethdelliott 259465b565cSsethdelliott if (test->reverse) { 260465b565cSsethdelliott // Reverse mode. Server sends. 261465b565cSsethdelliott iperf_send(test); 262982c704aSsethdelliott } else { 263465b565cSsethdelliott // Regular mode. Server receives. 264465b565cSsethdelliott iperf_recv(test); 265e99faeaeSBrian Tierney } 266a951c980SBrian Tierney } 267982c704aSsethdelliott } 268e99faeaeSBrian Tierney 2694f431119Ssethdelliott // XXX: Need to put the above while loop into another control structure 2704f431119Ssethdelliott // that initiates a new test upon each connection rather than dying. 2715083c489SBrian Tierney 272a3281a3dSBrian Tierney /* reset cookie when client is finished */ 273f4e1c1d4SBrian Tierney /* XXX: which cookie to reset, and why is it stored to 2 places? */ 274f4e1c1d4SBrian Tierney //memset(test->streams->settings->cookie, '\0', COOKIE_SIZE); 275465b565cSsethdelliott /* All memory for the previous run needs to be freed here */ 276f4e1c1d4SBrian Tierney memset(test->default_settings->cookie, '\0', COOKIE_SIZE); 2777fa9f68fSsethdelliott return 0; 278a3281a3dSBrian Tierney } 279a3281a3dSBrian Tierney 280