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 47465b565cSsethdelliott /* send_result_to_client - sends result string from server to client */ 48a951c980SBrian Tierney void 49a951c980SBrian Tierney send_result_to_client(struct iperf_stream * sp) 50a951c980SBrian Tierney { 51a951c980SBrian Tierney int result; 52a951c980SBrian Tierney int size = sp->settings->blksize; 53a951c980SBrian Tierney 54a951c980SBrian Tierney char *buf = (char *) malloc(size); 55a951c980SBrian Tierney 56a951c980SBrian Tierney if (!buf) 57a951c980SBrian Tierney { 58a951c980SBrian Tierney perror("malloc: unable to allocate transmit buffer"); 59a951c980SBrian Tierney } 60a951c980SBrian Tierney /* adding the string terminator to the message */ 61a951c980SBrian Tierney buf[strlen((char *) sp->data)] = '\0'; 62a951c980SBrian Tierney 63a951c980SBrian Tierney memcpy(buf, sp->data, strlen((char *) sp->data)); 64a951c980SBrian Tierney 65a951c980SBrian Tierney printf("send_result_to_client: sending %d bytes \n", (int) strlen((char *) sp->data)); 66a951c980SBrian Tierney result = send(sp->socket, buf, size, 0); 67a951c980SBrian Tierney printf("RESULT SENT TO CLIENT\n"); 68a951c980SBrian Tierney 69a951c980SBrian Tierney free(buf); 70a951c980SBrian Tierney } 71a951c980SBrian Tierney 72982c704aSsethdelliott int 73982c704aSsethdelliott iperf_server_listen(struct iperf_test *test) 74982c704aSsethdelliott { 75982c704aSsethdelliott char ubuf[UNIT_LEN]; 76982c704aSsethdelliott int x; 77982c704aSsethdelliott 78982c704aSsethdelliott if((test->listener = netannounce(Ptcp, NULL, test->server_port)) < 0) { 79982c704aSsethdelliott // Needs to set some sort of error number/message 80465b565cSsethdelliott perror("netannounce test->listener"); 81982c704aSsethdelliott return -1; 82982c704aSsethdelliott } 83982c704aSsethdelliott setnonblocking(test->listener); 84982c704aSsethdelliott 85982c704aSsethdelliott printf("-----------------------------------------------------------\n"); 86982c704aSsethdelliott printf("Server listening on %d\n", test->server_port); 87982c704aSsethdelliott 88982c704aSsethdelliott // This needs to be changed to reflect if client has different window size 89982c704aSsethdelliott // make sure we got what we asked for 90ba2672a2Ssethdelliott /* XXX: This needs to be moved to the stream listener 91982c704aSsethdelliott if ((x = get_tcp_windowsize(test->listener_sock_tcp, SO_RCVBUF)) < 0) { 92982c704aSsethdelliott // Needs to set some sort of error number/message 93982c704aSsethdelliott perror("SO_RCVBUF"); 94982c704aSsethdelliott return -1; 95982c704aSsethdelliott } 96ba2672a2Ssethdelliott */ 97982c704aSsethdelliott 98ba2672a2Ssethdelliott // XXX: This code needs to be moved to after parameter exhange 99ba2672a2Ssethdelliott // XXX: Last thing I was working on 100982c704aSsethdelliott if (test->protocol == Ptcp) { 101982c704aSsethdelliott if (test->default_settings->socket_bufsize > 0) { 102982c704aSsethdelliott unit_snprintf(ubuf, UNIT_LEN, (double) x, 'A'); 103982c704aSsethdelliott printf("TCP window size: %s\n", ubuf); 104982c704aSsethdelliott } else { 105982c704aSsethdelliott printf("Using TCP Autotuning\n"); 106982c704aSsethdelliott } 107982c704aSsethdelliott } 108982c704aSsethdelliott printf("-----------------------------------------------------------\n"); 109982c704aSsethdelliott 110982c704aSsethdelliott FD_ZERO(&test->read_set); 111982c704aSsethdelliott FD_ZERO(&test->temp_set); 112982c704aSsethdelliott FD_SET(test->listener, &test->read_set); 113465b565cSsethdelliott test->max_fd = (test->listener > test->max_fd) ? test->listener : test->max_fd; 114982c704aSsethdelliott 115982c704aSsethdelliott return 0; 116982c704aSsethdelliott } 117982c704aSsethdelliott 118982c704aSsethdelliott int 119982c704aSsethdelliott iperf_accept(struct iperf_test *test) 120982c704aSsethdelliott { 121982c704aSsethdelliott int s; 122465b565cSsethdelliott int rbuf = ACCESS_DENIED; 123982c704aSsethdelliott char ipl[512], ipr[512]; 124982c704aSsethdelliott socklen_t len; 125982c704aSsethdelliott struct sockaddr_in addr; 126465b565cSsethdelliott struct sockaddr_in temp1, temp2; 127982c704aSsethdelliott 128465b565cSsethdelliott if (test->ctrl_sck == -1) { 129982c704aSsethdelliott if ((s = accept(test->listener, (struct sockaddr *) &addr, &len)) < 0) { 130982c704aSsethdelliott perror("accept"); 131982c704aSsethdelliott return -1; 132982c704aSsethdelliott } 133465b565cSsethdelliott FD_SET(s, &test->read_set); 134465b565cSsethdelliott FD_SET(s, &test->write_set); 135465b565cSsethdelliott test->max_fd = (s > test->max_fd) ? s : test->max_fd; 136465b565cSsethdelliott test->ctrl_sck = s; 137982c704aSsethdelliott 138465b565cSsethdelliott len = sizeof(struct sockaddr_in); 139465b565cSsethdelliott if (getsockname(s, (struct sockaddr *) &temp1, &len) < 0) 140465b565cSsethdelliott perror("getsockname"); 141465b565cSsethdelliott if (getpeername(s, (struct sockaddr *) &temp2, &len) < 0) 142465b565cSsethdelliott perror("getpeername"); 143982c704aSsethdelliott 144465b565cSsethdelliott inet_ntop(AF_INET, (void *) &temp1.sin_addr, ipl, sizeof(ipl)); 145465b565cSsethdelliott inet_ntop(AF_INET, (void *) &temp2.sin_addr, ipr, sizeof(ipr)); 146465b565cSsethdelliott printf(report_peer, s, ipl, ntohs(temp1.sin_port), ipr, ntohs(temp2.sin_port)); 147465b565cSsethdelliott 148465b565cSsethdelliott return 0; 149465b565cSsethdelliott 150982c704aSsethdelliott } else { 151982c704aSsethdelliott // This message needs to be sent to the client 152465b565cSsethdelliott if ((s = accept(test->listener, (struct sockaddr *) &addr, &len)) < 0) { 153465b565cSsethdelliott perror("accept"); 154465b565cSsethdelliott return -1; 155465b565cSsethdelliott } 156465b565cSsethdelliott if (write(s, &rbuf, sizeof(int)) < 0) { 157465b565cSsethdelliott perror("write"); 158465b565cSsethdelliott return -1; 159465b565cSsethdelliott } 160465b565cSsethdelliott close(s); 161982c704aSsethdelliott return 0; 162982c704aSsethdelliott } 163982c704aSsethdelliott } 164982c704aSsethdelliott 165a951c980SBrian Tierney /**************************************************************************/ 166982c704aSsethdelliott int 167465b565cSsethdelliott iperf_handle_message_server(struct iperf_test *test) 168982c704aSsethdelliott { 169982c704aSsethdelliott if (read(test->ctrl_sck, &test->state, sizeof(int)) < 0) { 170ba2672a2Ssethdelliott // XXX: Needs to indicate read error 171982c704aSsethdelliott return -1; 172982c704aSsethdelliott } 173982c704aSsethdelliott 174982c704aSsethdelliott switch(test->state) { 175982c704aSsethdelliott case PARAM_EXCHANGE: 176982c704aSsethdelliott iperf_exchange_parameters(test); 177982c704aSsethdelliott break; 178ba2672a2Ssethdelliott case TEST_START: 179ba2672a2Ssethdelliott break; 180465b565cSsethdelliott case TEST_RUNNING: 181465b565cSsethdelliott break; 182465b565cSsethdelliott case TEST_END: 183873952e3Ssethdelliott test->state = DISPLAY_RESULTS; 184465b565cSsethdelliott if (write(test->ctrl_sck, &test->state, sizeof(int)) < 0) { 185873952e3Ssethdelliott perror("write DISPLAY_RESULTS"); 186465b565cSsethdelliott exit(1); 187465b565cSsethdelliott } 188465b565cSsethdelliott break; 189873952e3Ssethdelliott case IPERF_DONE: 190873952e3Ssethdelliott break; 191465b565cSsethdelliott case CLIENT_TERMINATE: 192465b565cSsethdelliott fprintf(stderr, "The client has terminated. Exiting...\n"); 193ba2672a2Ssethdelliott exit(1); 194ba2672a2Ssethdelliott default: 195ba2672a2Ssethdelliott // XXX: This needs to be replaced by actual error handling 1967fa9f68fSsethdelliott fprintf(stderr, "How did you get here? test->state = %d\n", test->state); 197ba2672a2Ssethdelliott return -1; 198982c704aSsethdelliott } 199982c704aSsethdelliott 200982c704aSsethdelliott return 0; 201982c704aSsethdelliott } 202982c704aSsethdelliott 2037fa9f68fSsethdelliott int 204a951c980SBrian Tierney iperf_run_server(struct iperf_test *test) 205a951c980SBrian Tierney { 2067fa9f68fSsethdelliott int result; 207465b565cSsethdelliott int streams_accepted = 0; 2087fa9f68fSsethdelliott struct timeval tv; 209a951c980SBrian Tierney 210982c704aSsethdelliott // Open socket and listen 211982c704aSsethdelliott if (iperf_server_listen(test) < 0) { 212982c704aSsethdelliott // This needs to be replaced by more formal error handling 213982c704aSsethdelliott fprintf(stderr, "An error occurred. Exiting.\n"); 214982c704aSsethdelliott exit(1); 215e99faeaeSBrian Tierney } 216a951c980SBrian Tierney 217465b565cSsethdelliott signal(SIGINT, sig_handler); 218465b565cSsethdelliott if (setjmp(env)) { 219465b565cSsethdelliott fprintf(stderr, "Interrupt received. Exiting...\n"); 220465b565cSsethdelliott test->state = SERVER_TERMINATE; 221465b565cSsethdelliott if (test->ctrl_sck >= 0) { 222465b565cSsethdelliott if (write(test->ctrl_sck, &test->state, sizeof(int)) < 0) { 223465b565cSsethdelliott fprintf(stderr, "Unable to send SERVER_TERMINATE message to client\n"); 224465b565cSsethdelliott } 225465b565cSsethdelliott } 226465b565cSsethdelliott exit(1); 227465b565cSsethdelliott } 228465b565cSsethdelliott 229a951c980SBrian Tierney test->default_settings->state = TEST_RUNNING; 230a951c980SBrian Tierney 231a951c980SBrian Tierney printf("iperf_run_server: Waiting for client connect.... \n"); 232150a09e9SBrian Tierney 233873952e3Ssethdelliott while (test->state != IPERF_DONE) { 234982c704aSsethdelliott 235*59aaeda7Ssethdelliott // XXX: Move test->temp_set over to local fd_set 236*59aaeda7Ssethdelliott memcpy(&test->temp_set, &test->read_set, sizeof(fd_set)); 237982c704aSsethdelliott tv.tv_sec = 15; 238982c704aSsethdelliott tv.tv_usec = 0; 239982c704aSsethdelliott 240982c704aSsethdelliott result = select(test->max_fd + 1, &test->temp_set, NULL, NULL, &tv); 241982c704aSsethdelliott if (result < 0 && errno != EINTR) { 242982c704aSsethdelliott // Change the way this handles errors 243982c704aSsethdelliott perror("select"); 244982c704aSsethdelliott exit(1); 245982c704aSsethdelliott } else if (result > 0) { 246982c704aSsethdelliott if (FD_ISSET(test->listener, &test->temp_set)) { 247465b565cSsethdelliott if (iperf_accept(test) < 0) { 248465b565cSsethdelliott fprintf(stderr, "iperf_accept: error accepting control socket. exiting...\n"); 249982c704aSsethdelliott exit(1); 250982c704aSsethdelliott } 251982c704aSsethdelliott FD_CLR(test->listener, &test->temp_set); 252982c704aSsethdelliott } 253982c704aSsethdelliott if (FD_ISSET(test->ctrl_sck, &test->temp_set)) { 254982c704aSsethdelliott // Handle control messages 255465b565cSsethdelliott iperf_handle_message_server(test); 256982c704aSsethdelliott FD_CLR(test->ctrl_sck, &test->temp_set); 257982c704aSsethdelliott } 258465b565cSsethdelliott 259465b565cSsethdelliott if (test->state == CREATE_STREAMS) { 260982c704aSsethdelliott if (FD_ISSET(test->prot_listener, &test->temp_set)) { 261982c704aSsethdelliott // Spawn new streams 262465b565cSsethdelliott // XXX: This works, but should it check cookies for authenticity 263465b565cSsethdelliott // Also, currently it uses 5202 for stream connections. 264465b565cSsethdelliott // Should this be fixed to use 5201 instead? 265ba2672a2Ssethdelliott iperf_tcp_accept(test); 266465b565cSsethdelliott ++streams_accepted; 267465b565cSsethdelliott FD_CLR(test->prot_listener, &test->temp_set); 268982c704aSsethdelliott } 269465b565cSsethdelliott if (streams_accepted == test->num_streams) { 270465b565cSsethdelliott FD_CLR(test->prot_listener, &test->read_set); 271465b565cSsethdelliott close(test->prot_listener); 272465b565cSsethdelliott test->state = TEST_START; 273465b565cSsethdelliott if (write(test->ctrl_sck, &test->state, sizeof(int)) < 0) { 274465b565cSsethdelliott perror("write TEST_START"); 275465b565cSsethdelliott return -1; 276982c704aSsethdelliott } 277982c704aSsethdelliott } 278982c704aSsethdelliott } 279982c704aSsethdelliott 280465b565cSsethdelliott if (test->reverse) { 281465b565cSsethdelliott // Reverse mode. Server sends. 282465b565cSsethdelliott iperf_send(test); 283982c704aSsethdelliott } else { 284465b565cSsethdelliott // Regular mode. Server receives. 285465b565cSsethdelliott iperf_recv(test); 286e99faeaeSBrian Tierney } 287a951c980SBrian Tierney } 288982c704aSsethdelliott } 289e99faeaeSBrian Tierney 290a3281a3dSBrian Tierney printf("Test Complete. \n\n"); 2915083c489SBrian Tierney 292a3281a3dSBrian Tierney /* reset cookie when client is finished */ 293f4e1c1d4SBrian Tierney /* XXX: which cookie to reset, and why is it stored to 2 places? */ 294f4e1c1d4SBrian Tierney //memset(test->streams->settings->cookie, '\0', COOKIE_SIZE); 295465b565cSsethdelliott /* All memory for the previous run needs to be freed here */ 296f4e1c1d4SBrian Tierney memset(test->default_settings->cookie, '\0', COOKIE_SIZE); 2977fa9f68fSsethdelliott return 0; 298a3281a3dSBrian Tierney } 299a3281a3dSBrian Tierney 300