xref: /iperf/src/iperf_server_api.c (revision 59aaeda7)
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