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: 157873952e3Ssethdelliott test->state = DISPLAY_RESULTS; 15836d18d07Ssethdelliott if (write(test->ctrl_sck, &test->state, sizeof(char)) < 0) { 159873952e3Ssethdelliott perror("write DISPLAY_RESULTS"); 160465b565cSsethdelliott exit(1); 161465b565cSsethdelliott } 162465b565cSsethdelliott break; 163873952e3Ssethdelliott case IPERF_DONE: 164873952e3Ssethdelliott break; 165465b565cSsethdelliott case CLIENT_TERMINATE: 166465b565cSsethdelliott fprintf(stderr, "The client has terminated. Exiting...\n"); 167ba2672a2Ssethdelliott exit(1); 168ba2672a2Ssethdelliott default: 169ba2672a2Ssethdelliott // XXX: This needs to be replaced by actual error handling 1707fa9f68fSsethdelliott fprintf(stderr, "How did you get here? test->state = %d\n", test->state); 171ba2672a2Ssethdelliott return -1; 172982c704aSsethdelliott } 173982c704aSsethdelliott 174982c704aSsethdelliott return 0; 175982c704aSsethdelliott } 176982c704aSsethdelliott 1777fa9f68fSsethdelliott int 178a951c980SBrian Tierney iperf_run_server(struct iperf_test *test) 179a951c980SBrian Tierney { 1807fa9f68fSsethdelliott int result; 181465b565cSsethdelliott int streams_accepted = 0; 1827fa9f68fSsethdelliott struct timeval tv; 183a951c980SBrian Tierney 184982c704aSsethdelliott // Open socket and listen 185982c704aSsethdelliott if (iperf_server_listen(test) < 0) { 186982c704aSsethdelliott // This needs to be replaced by more formal error handling 187982c704aSsethdelliott fprintf(stderr, "An error occurred. Exiting.\n"); 188982c704aSsethdelliott exit(1); 189e99faeaeSBrian Tierney } 190a951c980SBrian Tierney 191465b565cSsethdelliott signal(SIGINT, sig_handler); 192465b565cSsethdelliott if (setjmp(env)) { 193465b565cSsethdelliott fprintf(stderr, "Interrupt received. Exiting...\n"); 194465b565cSsethdelliott test->state = SERVER_TERMINATE; 195465b565cSsethdelliott if (test->ctrl_sck >= 0) { 19636d18d07Ssethdelliott if (write(test->ctrl_sck, &test->state, sizeof(char)) < 0) { 197465b565cSsethdelliott fprintf(stderr, "Unable to send SERVER_TERMINATE message to client\n"); 198465b565cSsethdelliott } 199465b565cSsethdelliott } 200465b565cSsethdelliott exit(1); 201465b565cSsethdelliott } 202465b565cSsethdelliott 203a951c980SBrian Tierney test->default_settings->state = TEST_RUNNING; 204a951c980SBrian Tierney 205a951c980SBrian Tierney printf("iperf_run_server: Waiting for client connect.... \n"); 206150a09e9SBrian Tierney 207873952e3Ssethdelliott while (test->state != IPERF_DONE) { 208982c704aSsethdelliott 20959aaeda7Ssethdelliott // XXX: Move test->temp_set over to local fd_set 21059aaeda7Ssethdelliott memcpy(&test->temp_set, &test->read_set, sizeof(fd_set)); 211982c704aSsethdelliott tv.tv_sec = 15; 212982c704aSsethdelliott tv.tv_usec = 0; 213982c704aSsethdelliott 214982c704aSsethdelliott result = select(test->max_fd + 1, &test->temp_set, NULL, NULL, &tv); 215982c704aSsethdelliott if (result < 0 && errno != EINTR) { 216982c704aSsethdelliott // Change the way this handles errors 217982c704aSsethdelliott perror("select"); 218982c704aSsethdelliott exit(1); 219982c704aSsethdelliott } else if (result > 0) { 220982c704aSsethdelliott if (FD_ISSET(test->listener, &test->temp_set)) { 221465b565cSsethdelliott if (iperf_accept(test) < 0) { 222465b565cSsethdelliott fprintf(stderr, "iperf_accept: error accepting control socket. exiting...\n"); 223982c704aSsethdelliott exit(1); 224982c704aSsethdelliott } 225982c704aSsethdelliott FD_CLR(test->listener, &test->temp_set); 226982c704aSsethdelliott } 227982c704aSsethdelliott if (FD_ISSET(test->ctrl_sck, &test->temp_set)) { 228982c704aSsethdelliott // Handle control messages 229465b565cSsethdelliott iperf_handle_message_server(test); 230982c704aSsethdelliott FD_CLR(test->ctrl_sck, &test->temp_set); 231982c704aSsethdelliott } 232465b565cSsethdelliott 233465b565cSsethdelliott if (test->state == CREATE_STREAMS) { 234982c704aSsethdelliott if (FD_ISSET(test->prot_listener, &test->temp_set)) { 235982c704aSsethdelliott // Spawn new streams 236465b565cSsethdelliott // XXX: This works, but should it check cookies for authenticity 237465b565cSsethdelliott // Also, currently it uses 5202 for stream connections. 238465b565cSsethdelliott // Should this be fixed to use 5201 instead? 239ba2672a2Ssethdelliott iperf_tcp_accept(test); 240465b565cSsethdelliott ++streams_accepted; 241465b565cSsethdelliott FD_CLR(test->prot_listener, &test->temp_set); 242982c704aSsethdelliott } 243465b565cSsethdelliott if (streams_accepted == test->num_streams) { 244465b565cSsethdelliott FD_CLR(test->prot_listener, &test->read_set); 245465b565cSsethdelliott close(test->prot_listener); 246465b565cSsethdelliott test->state = TEST_START; 24736d18d07Ssethdelliott if (write(test->ctrl_sck, &test->state, sizeof(char)) < 0) { 248465b565cSsethdelliott perror("write TEST_START"); 249465b565cSsethdelliott return -1; 250982c704aSsethdelliott } 251982c704aSsethdelliott } 252982c704aSsethdelliott } 253982c704aSsethdelliott 254465b565cSsethdelliott if (test->reverse) { 255465b565cSsethdelliott // Reverse mode. Server sends. 256465b565cSsethdelliott iperf_send(test); 257982c704aSsethdelliott } else { 258465b565cSsethdelliott // Regular mode. Server receives. 259465b565cSsethdelliott iperf_recv(test); 260e99faeaeSBrian Tierney } 261a951c980SBrian Tierney } 262982c704aSsethdelliott } 263e99faeaeSBrian Tierney 264*4f431119Ssethdelliott // XXX: Need to put the above while loop into another control structure 265*4f431119Ssethdelliott // that initiates a new test upon each connection rather than dying. 2665083c489SBrian Tierney 267a3281a3dSBrian Tierney /* reset cookie when client is finished */ 268f4e1c1d4SBrian Tierney /* XXX: which cookie to reset, and why is it stored to 2 places? */ 269f4e1c1d4SBrian Tierney //memset(test->streams->settings->cookie, '\0', COOKIE_SIZE); 270465b565cSsethdelliott /* All memory for the previous run needs to be freed here */ 271f4e1c1d4SBrian Tierney memset(test->default_settings->cookie, '\0', COOKIE_SIZE); 2727fa9f68fSsethdelliott return 0; 273a3281a3dSBrian Tierney } 274a3281a3dSBrian Tierney 275