xref: /iperf/src/iperf_server_api.c (revision f4e1c1d4)
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 <signal.h>
32a951c980SBrian Tierney #include <setjmp.h>
33a951c980SBrian Tierney 
34a951c980SBrian Tierney #include "iperf.h"
35a951c980SBrian Tierney #include "iperf_server_api.h"
36a951c980SBrian Tierney #include "iperf_api.h"
37a951c980SBrian Tierney #include "iperf_udp.h"
38a951c980SBrian Tierney #include "iperf_tcp.h"
39a951c980SBrian Tierney #include "timer.h"
40a951c980SBrian Tierney #include "net.h"
41a951c980SBrian Tierney #include "units.h"
42a951c980SBrian Tierney #include "tcp_window_size.h"
43a951c980SBrian Tierney #include "uuid.h"
44a951c980SBrian Tierney #include "locale.h"
45a951c980SBrian Tierney 
46a3281a3dSBrian Tierney void      handle_message(struct iperf_test * test, int m, struct iperf_stream * sp);
47a3281a3dSBrian Tierney 
48a951c980SBrian Tierney /*********************************************************************/
49a951c980SBrian Tierney /**
50a951c980SBrian Tierney  * param_received - handles the param_Exchange part for server
51a951c980SBrian Tierney  * returns state on success, -1 on failure
52a951c980SBrian Tierney  *
53a951c980SBrian Tierney  */
54a951c980SBrian Tierney 
55a951c980SBrian Tierney int
56a951c980SBrian Tierney param_received(struct iperf_stream * sp, struct param_exchange * param)
57a951c980SBrian Tierney {
58a951c980SBrian Tierney     int       result;
59a951c980SBrian Tierney     char     *buf = (char *) malloc(sizeof(struct param_exchange));
60a951c980SBrian Tierney 
61a951c980SBrian Tierney     if (sp->settings->cookie[0] == '\0' ||
628f4e66c9SBrian Tierney 	(strncmp(param->cookie, sp->settings->cookie, COOKIE_SIZE) == 0))
63a951c980SBrian Tierney     {
648f4e66c9SBrian Tierney 	strncpy(sp->settings->cookie, param->cookie, COOKIE_SIZE);
65a951c980SBrian Tierney 	sp->settings->blksize = param->blksize;
66a951c980SBrian Tierney 	sp->settings->socket_bufsize = param->recv_window;
67a951c980SBrian Tierney 	sp->settings->unit_format = param->format;
68a951c980SBrian Tierney 	printf("Got params from client: block size = %d, recv_window = %d cookie = %s\n",
69a951c980SBrian Tierney 	       sp->settings->blksize, sp->settings->socket_bufsize, sp->settings->cookie);
70a951c980SBrian Tierney 	param->state = TEST_START;
71a951c980SBrian Tierney 	buf[0] = TEST_START;
72a951c980SBrian Tierney 
73a951c980SBrian Tierney     } else
74a951c980SBrian Tierney     {
758f4e66c9SBrian Tierney 	fprintf(stderr, "Connection from new client denied.\n");
768f4e66c9SBrian Tierney 	printf("cookie still set to %s \n", sp->settings->cookie);
77a951c980SBrian Tierney 	param->state = ACCESS_DENIED;
78a951c980SBrian Tierney 	buf[0] = ACCESS_DENIED;
79a951c980SBrian Tierney     }
808f4e66c9SBrian Tierney     memcpy(sp->buffer, buf, sizeof(struct param_exchange));;
8197887027SBrian Tierney     //printf("param_received: Sending message (%d) back to client \n", sp->buffer[0]);
828f4e66c9SBrian Tierney     result = Nwrite(sp->socket, sp->buffer, sizeof(struct param_exchange), Ptcp);
83a951c980SBrian Tierney     if (result < 0)
84a951c980SBrian Tierney 	perror("param_received: Error sending param ack to client");
858f4e66c9SBrian Tierney     free(buf);
86a951c980SBrian Tierney     return param->state;
87a951c980SBrian Tierney }
88a951c980SBrian Tierney 
89a951c980SBrian Tierney /*************************************************************/
90a951c980SBrian Tierney 
91a951c980SBrian Tierney /**
92a951c980SBrian Tierney  * send_result_to_client - sends result string from server to client
93a951c980SBrian Tierney  */
94a951c980SBrian Tierney 
95a951c980SBrian Tierney void
96a951c980SBrian Tierney send_result_to_client(struct iperf_stream * sp)
97a951c980SBrian Tierney {
98a951c980SBrian Tierney     int       result;
99a951c980SBrian Tierney     int       size = sp->settings->blksize;
100a951c980SBrian Tierney 
101a951c980SBrian Tierney     char     *buf = (char *) malloc(size);
102a951c980SBrian Tierney 
103a951c980SBrian Tierney     if (!buf)
104a951c980SBrian Tierney     {
105a951c980SBrian Tierney 	perror("malloc: unable to allocate transmit buffer");
106a951c980SBrian Tierney     }
107a951c980SBrian Tierney     /* adding the string terminator to the message */
108a951c980SBrian Tierney     buf[strlen((char *) sp->data)] = '\0';
109a951c980SBrian Tierney 
110a951c980SBrian Tierney     memcpy(buf, sp->data, strlen((char *) sp->data));
111a951c980SBrian Tierney 
112a951c980SBrian Tierney     printf("send_result_to_client: sending %d bytes \n", (int) strlen((char *) sp->data));
113a951c980SBrian Tierney     result = send(sp->socket, buf, size, 0);
114a951c980SBrian Tierney     printf("RESULT SENT TO CLIENT\n");
115a951c980SBrian Tierney 
116a951c980SBrian Tierney     free(buf);
117a951c980SBrian Tierney }
118a951c980SBrian Tierney 
119a951c980SBrian Tierney /**************************************************************************/
120a951c980SBrian Tierney void
121a951c980SBrian Tierney iperf_run_server(struct iperf_test * test)
122a951c980SBrian Tierney {
123a951c980SBrian Tierney     struct timeval tv;
124a3281a3dSBrian Tierney     struct iperf_stream *np;
1255083c489SBrian Tierney     struct timer *stats_interval, *reporter_interval;
1265083c489SBrian Tierney     char     *result_string = NULL;
127a3281a3dSBrian Tierney     int       j = 0, result = 0, message = 0;
128a3281a3dSBrian Tierney     int       nfd = 0;
129a951c980SBrian Tierney 
13097887027SBrian Tierney     //printf("in iperf_run_server \n");
131150a09e9SBrian Tierney 
132a951c980SBrian Tierney     FD_ZERO(&test->read_set);
133e99faeaeSBrian Tierney     FD_ZERO(&test->temp_set);
134e99faeaeSBrian Tierney     if (test->protocol == Ptcp)
135e99faeaeSBrian Tierney     {
136e99faeaeSBrian Tierney 	/* add listener to the master set */
137a951c980SBrian Tierney 	FD_SET(test->listener_sock_tcp, &test->read_set);
138e99faeaeSBrian Tierney 	test->max_fd = test->listener_sock_tcp;
139e99faeaeSBrian Tierney     } else
140e99faeaeSBrian Tierney     {
141a951c980SBrian Tierney 	FD_SET(test->listener_sock_udp, &test->read_set);
142e99faeaeSBrian Tierney 	test->max_fd = test->listener_sock_udp;
143e99faeaeSBrian Tierney     }
144a951c980SBrian Tierney 
1458f4e66c9SBrian Tierney     //printf("iperf_run_server: max_fd set to %d \n", test->max_fd);
146a951c980SBrian Tierney 
147a951c980SBrian Tierney     test->num_streams = 0;
148a951c980SBrian Tierney     test->default_settings->state = TEST_RUNNING;
149a951c980SBrian Tierney 
1505083c489SBrian Tierney 
151a951c980SBrian Tierney     printf("iperf_run_server: Waiting for client connect.... \n");
152150a09e9SBrian Tierney 
153a951c980SBrian Tierney     while (test->default_settings->state != TEST_END)
154a951c980SBrian Tierney     {
155a951c980SBrian Tierney 	memcpy(&test->temp_set, &test->read_set, sizeof(test->read_set));
156a951c980SBrian Tierney 	tv.tv_sec = 15;
157a951c980SBrian Tierney 	tv.tv_usec = 0;
158a951c980SBrian Tierney 
159a951c980SBrian Tierney 	/* using select to check on multiple descriptors. */
160c27f38e5SBrian Tierney 	//printf("calling select.. sock = %d \n", test->max_fd + 1);
161a951c980SBrian Tierney 	result = select(test->max_fd + 1, &test->temp_set, NULL, NULL, &tv);
162a951c980SBrian Tierney 	if (result == 0)
163a951c980SBrian Tierney 	{
164a951c980SBrian Tierney 	    //printf("SERVER IDLE : %d sec\n", (int) tv.tv_sec);
165a951c980SBrian Tierney 	    continue;
166a951c980SBrian Tierney 	} else if (result < 0 && errno != EINTR)
167a951c980SBrian Tierney 	{
168c27f38e5SBrian Tierney 	    printf("Error in select(): %s, socket = %d\n", strerror(errno), test->max_fd + 1);
169a951c980SBrian Tierney 	    exit(0);
170a951c980SBrian Tierney 	} else if (result > 0)
171a951c980SBrian Tierney 	{
172e99faeaeSBrian Tierney 	    if (test->protocol == Ptcp)
173e99faeaeSBrian Tierney 	    {
174a951c980SBrian Tierney 		/* Accept a new TCP connection */
175a951c980SBrian Tierney 		if (FD_ISSET(test->listener_sock_tcp, &test->temp_set))
176a951c980SBrian Tierney 		{
177a951c980SBrian Tierney 		    test->protocol = Ptcp;
178a951c980SBrian Tierney 		    test->accept = iperf_tcp_accept;
179a3281a3dSBrian Tierney 		    if (test->accept < 0)
180a3281a3dSBrian Tierney 			return;
181a951c980SBrian Tierney 		    test->new_stream = iperf_new_tcp_stream;
182a951c980SBrian Tierney 		    test->accept(test);
183a951c980SBrian Tierney 		    test->default_settings->state = TEST_RUNNING;
184a951c980SBrian Tierney 		    FD_CLR(test->listener_sock_tcp, &test->temp_set);
1851882b9cbSBrian Tierney 		    //printf("iperf_run_server: accepted TCP connection \n");
186d44c04d4SBrian Tierney 		    test->num_streams++;
187a951c980SBrian Tierney 		}
188e99faeaeSBrian Tierney 	    } else
189e99faeaeSBrian Tierney 	    {
190a951c980SBrian Tierney 		/* Accept a new UDP connection */
191e99faeaeSBrian Tierney 		if (FD_ISSET(test->listener_sock_udp, &test->temp_set))
192a951c980SBrian Tierney 		{
193a951c980SBrian Tierney 		    test->protocol = Pudp;
194a951c980SBrian Tierney 		    test->accept = iperf_udp_accept;
195a3281a3dSBrian Tierney 		    if (test->accept < 0)
196a3281a3dSBrian Tierney 			return;
197a951c980SBrian Tierney 		    test->new_stream = iperf_new_udp_stream;
198a951c980SBrian Tierney 		    test->accept(test);
199a951c980SBrian Tierney 		    test->default_settings->state = TEST_RUNNING;
200a951c980SBrian Tierney 		    FD_CLR(test->listener_sock_udp, &test->temp_set);
201e99faeaeSBrian Tierney 		    printf("iperf_run_server: accepted UDP connection \n");
202e99faeaeSBrian Tierney 		}
203a951c980SBrian Tierney 	    }
204a951c980SBrian Tierney 	    /* Process the sockets for read operation */
205a3281a3dSBrian Tierney 	    nfd = test->max_fd + 1;
206a3281a3dSBrian Tierney 	    for (j = 0; j <= test->max_fd; j++)
207a951c980SBrian Tierney 	    {
208a3281a3dSBrian Tierney 		//printf("Checking socket %d \n", j);
209a951c980SBrian Tierney 		if (FD_ISSET(j, &test->temp_set))
210a951c980SBrian Tierney 		{
211a3281a3dSBrian Tierney 		    //printf("iperf_run_server: data ready on socket %d \n", j);
212e99faeaeSBrian Tierney 		    /* find the correct stream - possibly time consuming? */
213a951c980SBrian Tierney 		    np = find_stream_by_socket(test, j);
214c27f38e5SBrian Tierney 		    message = np->rcv(np);	/* get data from client using
215c27f38e5SBrian Tierney 						 * receiver callback  */
216a3281a3dSBrian Tierney 		    if (message < 0)
217a3281a3dSBrian Tierney 			goto done;
218a3281a3dSBrian Tierney 		    handle_message(test, message, np);
219a3281a3dSBrian Tierney 		    if (message == TEST_END)
220a3281a3dSBrian Tierney 			break;	/* test done, so break out of loop */
221e99faeaeSBrian Tierney 
222a3281a3dSBrian Tierney 		}		/* end if (FD_ISSET(j, &temp_set)) */
223a3281a3dSBrian Tierney 	    }			/* end for (j=0;...) */
2245083c489SBrian Tierney 
22597887027SBrian Tierney 	    if (message == PARAM_EXCHANGE)
22697887027SBrian Tierney 	    {
22797887027SBrian Tierney 		    /* start timer at end of PARAM_EXCHANGE */
22897887027SBrian Tierney     		    if (test->stats_interval != 0)
22997887027SBrian Tierney 			    stats_interval = new_timer(test->stats_interval, 0);
23097887027SBrian Tierney     		    if (test->reporter_interval != 0)
23197887027SBrian Tierney 			    reporter_interval = new_timer(test->reporter_interval, 0);
23297887027SBrian Tierney 	    }
23397887027SBrian Tierney 	    if ((message == STREAM_BEGIN) || (message == STREAM_RUNNING))
23497887027SBrian Tierney 	    {
23597887027SBrian Tierney 		/*
23697887027SBrian Tierney 		 * XXX: is this right? Might there be cases where we want
23797887027SBrian Tierney 		 * stats for while in another state?
23897887027SBrian Tierney 		 */
2395083c489SBrian Tierney 		if ((test->stats_interval != 0) && stats_interval->expired(stats_interval))
2405083c489SBrian Tierney 		{
2415083c489SBrian Tierney 		    test->stats_callback(test);
2425083c489SBrian Tierney 		    update_timer(stats_interval, test->stats_interval, 0);
2435083c489SBrian Tierney 		}
2445083c489SBrian Tierney 		if ((test->reporter_interval != 0) && reporter_interval->expired(reporter_interval))
2455083c489SBrian Tierney 		{
2465083c489SBrian Tierney 		    result_string = test->reporter_callback(test);
2475083c489SBrian Tierney 		    //printf("interval expired: printing results: \n");
2485083c489SBrian Tierney 		    puts(result_string);
2495083c489SBrian Tierney 		    update_timer(reporter_interval, test->reporter_interval, 0);
2505083c489SBrian Tierney 		}
25197887027SBrian Tierney 	    }
25297887027SBrian Tierney 	}			/* end else (result>0)   */
253a3281a3dSBrian Tierney     }				/* end while */
254a3281a3dSBrian Tierney 
255a3281a3dSBrian Tierney done:
256a3281a3dSBrian Tierney     printf("Test Complete. \n\n");
2575083c489SBrian Tierney 
258a3281a3dSBrian Tierney     /* reset cookie when client is finished */
259*f4e1c1d4SBrian Tierney     /* XXX: which cookie to reset, and why is it stored to 2 places? */
260*f4e1c1d4SBrian Tierney     //memset(test->streams->settings->cookie, '\0', COOKIE_SIZE);
261*f4e1c1d4SBrian Tierney     memset(test->default_settings->cookie, '\0', COOKIE_SIZE);
262a3281a3dSBrian Tierney     return;
263a3281a3dSBrian Tierney }
264a3281a3dSBrian Tierney 
265a3281a3dSBrian Tierney /********************************************************/
266a3281a3dSBrian Tierney 
267a3281a3dSBrian Tierney void
268a3281a3dSBrian Tierney handle_message(struct iperf_test * test, int message, struct iperf_stream * sp)
269a3281a3dSBrian Tierney {
270a3281a3dSBrian Tierney     char     *results_string = NULL;
271a3281a3dSBrian Tierney     struct iperf_stream *tp1 = NULL;
272a3281a3dSBrian Tierney     struct iperf_stream *tp2 = NULL;
273a3281a3dSBrian Tierney 
274a3281a3dSBrian Tierney     //printf("handle_message: %d \n", message);
275c27f38e5SBrian Tierney     if (message < 0)
276c27f38e5SBrian Tierney     {
277c27f38e5SBrian Tierney 	printf("Error reading data from client \n");
278a3281a3dSBrian Tierney 	close(sp->socket);
279c27f38e5SBrian Tierney 	return;
280c27f38e5SBrian Tierney     }
281a3281a3dSBrian Tierney     sp->settings->state = message;
282a951c980SBrian Tierney 
283a951c980SBrian Tierney     if (message == PARAM_EXCHANGE)
284a951c980SBrian Tierney     {
285a951c980SBrian Tierney 	/* copy the received settings into test */
286a951c980SBrian Tierney 	memcpy(test->default_settings, test->streams->settings, sizeof(struct iperf_settings));
287a951c980SBrian Tierney     }
288c27f38e5SBrian Tierney     if (message == ACCESS_DENIED)	/* this might get set by
289c27f38e5SBrian Tierney 					 * PARAM_EXCHANGE */
290a951c980SBrian Tierney     {
291a951c980SBrian Tierney 	/* XXX: test this! */
292a3281a3dSBrian Tierney 	close(sp->socket);
293a3281a3dSBrian Tierney 	FD_CLR(sp->socket, &test->read_set);
294a3281a3dSBrian Tierney 	iperf_free_stream(sp);
295a951c980SBrian Tierney     }
296a951c980SBrian Tierney     if (message == STREAM_END)
297a951c980SBrian Tierney     {
298a3281a3dSBrian Tierney 	/* get final timestamp for all streams */
299a3281a3dSBrian Tierney 	tp1 = test->streams;
300a3281a3dSBrian Tierney 	do
301a3281a3dSBrian Tierney 	{
302a3281a3dSBrian Tierney 	    gettimeofday(&tp1->result->end_time, NULL);
303a3281a3dSBrian Tierney 	    tp1 = tp1->next;
304a3281a3dSBrian Tierney 	} while (tp1 != NULL);
305a951c980SBrian Tierney     }
306a951c980SBrian Tierney     if (message == RESULT_REQUEST)
307a951c980SBrian Tierney     {
308a3281a3dSBrian Tierney 	sp->settings->state = RESULT_RESPOND;
309a951c980SBrian Tierney 	results_string = test->reporter_callback(test);
310a3281a3dSBrian Tierney 	sp->data = results_string;
311a3281a3dSBrian Tierney 	send_result_to_client(sp);
312a951c980SBrian Tierney     }
313a951c980SBrian Tierney     if (message == ALL_STREAMS_END)
314a951c980SBrian Tierney     {
3151882b9cbSBrian Tierney 	printf("Client done sending data. Printing final results. \n");
316a951c980SBrian Tierney 	/* print server results */
317a951c980SBrian Tierney 	results_string = test->reporter_callback(test);
318a951c980SBrian Tierney 	puts(results_string);	/* send to stdio */
319a951c980SBrian Tierney     }
320a951c980SBrian Tierney     if (message == TEST_END)
321a951c980SBrian Tierney     {
3221882b9cbSBrian Tierney 	//printf("client sent TEST_END message, shuting down sockets.. \n");
323a951c980SBrian Tierney 	/* FREE ALL STREAMS  */
324a3281a3dSBrian Tierney 	tp1 = test->streams;
325a951c980SBrian Tierney 	do
326a951c980SBrian Tierney 	{
327a3281a3dSBrian Tierney 	    tp2 = tp1;
3285083c489SBrian Tierney 	    //printf(" closing socket: %d \n", tp2->socket);
329a3281a3dSBrian Tierney 	    close(tp2->socket);
330a3281a3dSBrian Tierney 	    FD_CLR(tp2->socket, &test->read_set);
331a3281a3dSBrian Tierney 	    tp1 = tp2->next;	/* get next pointer before freeing */
332a3281a3dSBrian Tierney 	    iperf_free_stream(tp2);
333a3281a3dSBrian Tierney 	} while (tp1 != NULL);
334a951c980SBrian Tierney 
335a951c980SBrian Tierney 	test->default_settings->state = TEST_END;
3368f4e66c9SBrian Tierney 	memset(test->default_settings->cookie, '\0', COOKIE_SIZE);
337a951c980SBrian Tierney     }
338a951c980SBrian Tierney }
3395083c489SBrian Tierney 
3405083c489SBrian Tierney /**************************************************************************/
3415083c489SBrian Tierney 
3425083c489SBrian Tierney /**
3435083c489SBrian Tierney  * find_stream_by_socket -- finds the stream based on socket ID
3445083c489SBrian Tierney  *   simple sequential scan: not more effiecient, but should be fine
3455083c489SBrian Tierney  *	for a small number of streams.
3465083c489SBrian Tierney  *
3475083c489SBrian Tierney  *returns stream
3485083c489SBrian Tierney  *
3495083c489SBrian Tierney  */
3505083c489SBrian Tierney 
3515083c489SBrian Tierney struct iperf_stream *
3525083c489SBrian Tierney find_stream_by_socket(struct iperf_test * test, int sock)
3535083c489SBrian Tierney {
3545083c489SBrian Tierney     struct iperf_stream *n;
3555083c489SBrian Tierney 
3565083c489SBrian Tierney     n = test->streams;
3575083c489SBrian Tierney     while (1)
3585083c489SBrian Tierney     {
3595083c489SBrian Tierney 	if (n->socket == sock)
3605083c489SBrian Tierney 	    break;
3615083c489SBrian Tierney 	if (n->next == NULL)
3625083c489SBrian Tierney 	    break;
3635083c489SBrian Tierney 
3645083c489SBrian Tierney 	n = n->next;
3655083c489SBrian Tierney     }
3665083c489SBrian Tierney     return n;
3675083c489SBrian Tierney }
368