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 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); 85982c704aSsethdelliott FD_SET(test->listener, &test->read_set); 86465b565cSsethdelliott test->max_fd = (test->listener > test->max_fd) ? test->listener : 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]; 97982c704aSsethdelliott socklen_t len; 98982c704aSsethdelliott struct sockaddr_in addr; 99465b565cSsethdelliott struct sockaddr_in temp1, temp2; 100982c704aSsethdelliott 101465b565cSsethdelliott if (test->ctrl_sck == -1) { 102982c704aSsethdelliott if ((s = accept(test->listener, (struct sockaddr *) &addr, &len)) < 0) { 103982c704aSsethdelliott perror("accept"); 104982c704aSsethdelliott return -1; 105982c704aSsethdelliott } 106465b565cSsethdelliott FD_SET(s, &test->read_set); 107465b565cSsethdelliott FD_SET(s, &test->write_set); 108465b565cSsethdelliott test->max_fd = (s > test->max_fd) ? s : test->max_fd; 109465b565cSsethdelliott test->ctrl_sck = s; 110982c704aSsethdelliott 111465b565cSsethdelliott len = sizeof(struct sockaddr_in); 112465b565cSsethdelliott if (getsockname(s, (struct sockaddr *) &temp1, &len) < 0) 113465b565cSsethdelliott perror("getsockname"); 114465b565cSsethdelliott if (getpeername(s, (struct sockaddr *) &temp2, &len) < 0) 115465b565cSsethdelliott perror("getpeername"); 116982c704aSsethdelliott 117465b565cSsethdelliott inet_ntop(AF_INET, (void *) &temp1.sin_addr, ipl, sizeof(ipl)); 118465b565cSsethdelliott inet_ntop(AF_INET, (void *) &temp2.sin_addr, ipr, sizeof(ipr)); 119465b565cSsethdelliott printf(report_peer, s, ipl, ntohs(temp1.sin_port), ipr, ntohs(temp2.sin_port)); 120465b565cSsethdelliott 121465b565cSsethdelliott return 0; 122465b565cSsethdelliott 123982c704aSsethdelliott } else { 124982c704aSsethdelliott // This message needs to be sent to the client 125465b565cSsethdelliott if ((s = accept(test->listener, (struct sockaddr *) &addr, &len)) < 0) { 126465b565cSsethdelliott perror("accept"); 127465b565cSsethdelliott return -1; 128465b565cSsethdelliott } 129465b565cSsethdelliott if (write(s, &rbuf, sizeof(int)) < 0) { 130465b565cSsethdelliott perror("write"); 131465b565cSsethdelliott return -1; 132465b565cSsethdelliott } 133465b565cSsethdelliott close(s); 134982c704aSsethdelliott return 0; 135982c704aSsethdelliott } 136982c704aSsethdelliott } 137982c704aSsethdelliott 138a951c980SBrian Tierney /**************************************************************************/ 139982c704aSsethdelliott int 140465b565cSsethdelliott iperf_handle_message_server(struct iperf_test *test) 141982c704aSsethdelliott { 142efdc02f7Ssethdelliott struct iperf_stream *sp; 143efdc02f7Ssethdelliott 14436d18d07Ssethdelliott if (read(test->ctrl_sck, &test->state, sizeof(char)) < 0) { 145ba2672a2Ssethdelliott // XXX: Needs to indicate read error 146982c704aSsethdelliott return -1; 147982c704aSsethdelliott } 148982c704aSsethdelliott 149982c704aSsethdelliott switch(test->state) { 150982c704aSsethdelliott case PARAM_EXCHANGE: 151982c704aSsethdelliott iperf_exchange_parameters(test); 152982c704aSsethdelliott break; 153ba2672a2Ssethdelliott case TEST_START: 154ba2672a2Ssethdelliott break; 155465b565cSsethdelliott case TEST_RUNNING: 156465b565cSsethdelliott break; 157465b565cSsethdelliott case TEST_END: 158efdc02f7Ssethdelliott for (sp = test->streams; sp; sp = sp->next) { 159efdc02f7Ssethdelliott FD_CLR(sp->socket, &test->read_set); 160efdc02f7Ssethdelliott FD_CLR(sp->socket, &test->write_set); 161efdc02f7Ssethdelliott close(sp->socket); 162efdc02f7Ssethdelliott } 163181d61afSsethdelliott test->state = EXCHANGE_RESULTS; 164181d61afSsethdelliott if (write(test->ctrl_sck, &test->state, sizeof(char)) < 0) { 165181d61afSsethdelliott perror("write EXCHANGE_RESULTS"); 166181d61afSsethdelliott exit(1); 167181d61afSsethdelliott } 168181d61afSsethdelliott iperf_exchange_results(test); 169873952e3Ssethdelliott test->state = DISPLAY_RESULTS; 17036d18d07Ssethdelliott if (write(test->ctrl_sck, &test->state, sizeof(char)) < 0) { 171873952e3Ssethdelliott perror("write DISPLAY_RESULTS"); 172465b565cSsethdelliott exit(1); 173465b565cSsethdelliott } 174*32885561Ssethdelliott test->stats_callback(test); 175181d61afSsethdelliott test->reporter_callback(test); 176465b565cSsethdelliott break; 177873952e3Ssethdelliott case IPERF_DONE: 178873952e3Ssethdelliott break; 179465b565cSsethdelliott case CLIENT_TERMINATE: 180465b565cSsethdelliott fprintf(stderr, "The client has terminated. Exiting...\n"); 181ba2672a2Ssethdelliott exit(1); 182ba2672a2Ssethdelliott default: 183ba2672a2Ssethdelliott // XXX: This needs to be replaced by actual error handling 1847fa9f68fSsethdelliott fprintf(stderr, "How did you get here? test->state = %d\n", test->state); 185ba2672a2Ssethdelliott return -1; 186982c704aSsethdelliott } 187982c704aSsethdelliott 188982c704aSsethdelliott return 0; 189982c704aSsethdelliott } 190982c704aSsethdelliott 191efdc02f7Ssethdelliott void 192efdc02f7Ssethdelliott iperf_test_reset(struct iperf_test *test) 193efdc02f7Ssethdelliott { 194efdc02f7Ssethdelliott struct iperf_stream *sp, *np; 195efdc02f7Ssethdelliott 196efdc02f7Ssethdelliott close(test->ctrl_sck); 197efdc02f7Ssethdelliott 198efdc02f7Ssethdelliott /* Free streams */ 199efdc02f7Ssethdelliott for (sp = test->streams; sp; sp = np) { 200efdc02f7Ssethdelliott np = sp->next; 201efdc02f7Ssethdelliott iperf_free_stream(sp); 202efdc02f7Ssethdelliott } 203efdc02f7Ssethdelliott 204*32885561Ssethdelliott test->streams = NULL; 205*32885561Ssethdelliott 206efdc02f7Ssethdelliott test->role = 's'; 207*32885561Ssethdelliott test->protocol = Ptcp; 208*32885561Ssethdelliott test->duration = DURATION; 209*32885561Ssethdelliott test->state = 0; 210*32885561Ssethdelliott test->server_hostname = NULL; 211*32885561Ssethdelliott 212*32885561Ssethdelliott test->ctrl_sck = -1; 213*32885561Ssethdelliott test->prot_listener = 0; 214*32885561Ssethdelliott 215*32885561Ssethdelliott test->reverse = 0; 216*32885561Ssethdelliott // test->no_delay = 0 (Do we need this reset between tests?) 217*32885561Ssethdelliott 218efdc02f7Ssethdelliott FD_ZERO(&test->read_set); 219efdc02f7Ssethdelliott FD_ZERO(&test->write_set); 220efdc02f7Ssethdelliott FD_SET(test->listener, &test->read_set); 221*32885561Ssethdelliott test->max_fd = test->listener; 222*32885561Ssethdelliott 223*32885561Ssethdelliott test->num_streams = 1; 224*32885561Ssethdelliott test->default_settings->socket_bufsize = 0; 225*32885561Ssethdelliott test->default_settings->blksize = DEFAULT_TCP_BLKSIZE; 226*32885561Ssethdelliott test->default_settings->rate = RATE; /* UDP only */ 227*32885561Ssethdelliott memset(test->default_settings->cookie, 0, COOKIE_SIZE); 228efdc02f7Ssethdelliott } 229efdc02f7Ssethdelliott 2307fa9f68fSsethdelliott int 231a951c980SBrian Tierney iperf_run_server(struct iperf_test *test) 232a951c980SBrian Tierney { 2337fa9f68fSsethdelliott int result; 234efdc02f7Ssethdelliott int streams_accepted; 235efdc02f7Ssethdelliott fd_set temp_read_set, temp_write_set; 2367fa9f68fSsethdelliott struct timeval tv; 237a951c980SBrian Tierney 238982c704aSsethdelliott // Open socket and listen 239982c704aSsethdelliott if (iperf_server_listen(test) < 0) { 240982c704aSsethdelliott // This needs to be replaced by more formal error handling 241982c704aSsethdelliott fprintf(stderr, "An error occurred. Exiting.\n"); 242982c704aSsethdelliott exit(1); 243e99faeaeSBrian Tierney } 244a951c980SBrian Tierney 245465b565cSsethdelliott signal(SIGINT, sig_handler); 246465b565cSsethdelliott if (setjmp(env)) { 247465b565cSsethdelliott fprintf(stderr, "Interrupt received. Exiting...\n"); 248465b565cSsethdelliott test->state = SERVER_TERMINATE; 249465b565cSsethdelliott if (test->ctrl_sck >= 0) { 25036d18d07Ssethdelliott if (write(test->ctrl_sck, &test->state, sizeof(char)) < 0) { 251465b565cSsethdelliott fprintf(stderr, "Unable to send SERVER_TERMINATE message to client\n"); 252465b565cSsethdelliott } 253465b565cSsethdelliott } 254465b565cSsethdelliott exit(1); 255465b565cSsethdelliott } 256465b565cSsethdelliott 257efdc02f7Ssethdelliott for (;;) { 258efdc02f7Ssethdelliott 259efdc02f7Ssethdelliott test->state = IPERF_START; 260efdc02f7Ssethdelliott streams_accepted = 0; 261a951c980SBrian Tierney 262873952e3Ssethdelliott while (test->state != IPERF_DONE) { 263982c704aSsethdelliott 264efdc02f7Ssethdelliott memcpy(&temp_read_set, &test->read_set, sizeof(fd_set)); 265efdc02f7Ssethdelliott memcpy(&temp_write_set, &test->write_set, sizeof(fd_set)); 266982c704aSsethdelliott tv.tv_sec = 15; 267982c704aSsethdelliott tv.tv_usec = 0; 268982c704aSsethdelliott 269efdc02f7Ssethdelliott result = select(test->max_fd + 1, &temp_read_set, &temp_write_set, NULL, &tv); 270982c704aSsethdelliott if (result < 0 && errno != EINTR) { 271982c704aSsethdelliott // Change the way this handles errors 272982c704aSsethdelliott perror("select"); 273982c704aSsethdelliott exit(1); 274982c704aSsethdelliott } else if (result > 0) { 275efdc02f7Ssethdelliott if (FD_ISSET(test->listener, &temp_read_set)) { 276465b565cSsethdelliott if (iperf_accept(test) < 0) { 277465b565cSsethdelliott fprintf(stderr, "iperf_accept: error accepting control socket. exiting...\n"); 278982c704aSsethdelliott exit(1); 279982c704aSsethdelliott } 280efdc02f7Ssethdelliott FD_CLR(test->listener, &temp_read_set); 281982c704aSsethdelliott } 282efdc02f7Ssethdelliott if (FD_ISSET(test->ctrl_sck, &temp_read_set)) { 283982c704aSsethdelliott // Handle control messages 284465b565cSsethdelliott iperf_handle_message_server(test); 285efdc02f7Ssethdelliott FD_CLR(test->ctrl_sck, &temp_read_set); 286982c704aSsethdelliott } 287465b565cSsethdelliott 288465b565cSsethdelliott if (test->state == CREATE_STREAMS) { 289efdc02f7Ssethdelliott if (FD_ISSET(test->prot_listener, &temp_read_set)) { 290982c704aSsethdelliott // Spawn new streams 291465b565cSsethdelliott // XXX: This works, but should it check cookies for authenticity 292465b565cSsethdelliott // Also, currently it uses 5202 for stream connections. 293465b565cSsethdelliott // Should this be fixed to use 5201 instead? 294ba2672a2Ssethdelliott iperf_tcp_accept(test); 295465b565cSsethdelliott ++streams_accepted; 296efdc02f7Ssethdelliott FD_CLR(test->prot_listener, &temp_read_set); 297982c704aSsethdelliott } 298465b565cSsethdelliott if (streams_accepted == test->num_streams) { 299465b565cSsethdelliott FD_CLR(test->prot_listener, &test->read_set); 300465b565cSsethdelliott close(test->prot_listener); 301465b565cSsethdelliott test->state = TEST_START; 30236d18d07Ssethdelliott if (write(test->ctrl_sck, &test->state, sizeof(char)) < 0) { 303465b565cSsethdelliott perror("write TEST_START"); 304465b565cSsethdelliott return -1; 305982c704aSsethdelliott } 306982c704aSsethdelliott } 307982c704aSsethdelliott } 308982c704aSsethdelliott 309465b565cSsethdelliott if (test->reverse) { 310465b565cSsethdelliott // Reverse mode. Server sends. 311465b565cSsethdelliott iperf_send(test); 312982c704aSsethdelliott } else { 313465b565cSsethdelliott // Regular mode. Server receives. 314465b565cSsethdelliott iperf_recv(test); 315e99faeaeSBrian Tierney } 316a951c980SBrian Tierney } 317982c704aSsethdelliott } 318e99faeaeSBrian Tierney 319efdc02f7Ssethdelliott /* Clean up the last test */ 320efdc02f7Ssethdelliott iperf_test_reset(test); 321efdc02f7Ssethdelliott printf("\n"); 322efdc02f7Ssethdelliott 323efdc02f7Ssethdelliott } 324efdc02f7Ssethdelliott 3254f431119Ssethdelliott // XXX: Need to put the above while loop into another control structure 3264f431119Ssethdelliott // that initiates a new test upon each connection rather than dying. 3275083c489SBrian Tierney 3287fa9f68fSsethdelliott return 0; 329a3281a3dSBrian Tierney } 330a3281a3dSBrian Tierney 331