xref: /iperf/src/iperf_server_api.c (revision efb7bbe4)
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 
53af70b448Ssethdelliott     if((test->listener_tcp = netannounce(Ptcp, NULL, test->server_port)) < 0) {
54982c704aSsethdelliott         // Needs to set some sort of error number/message
55af70b448Ssethdelliott         perror("netannounce test->listener_tcp");
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);
85af70b448Ssethdelliott     FD_SET(test->listener_tcp, &test->read_set);
86af70b448Ssethdelliott     test->max_fd = (test->listener_tcp > test->max_fd) ? test->listener_tcp : 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];
97af70b448Ssethdelliott     char cookie[COOKIE_SIZE];
98982c704aSsethdelliott     socklen_t len;
99982c704aSsethdelliott     struct sockaddr_in addr;
100465b565cSsethdelliott     struct sockaddr_in temp1, temp2;
101af70b448Ssethdelliott     struct iperf_stream *sp;
102af70b448Ssethdelliott     static int streams_accepted;
103982c704aSsethdelliott 
104af70b448Ssethdelliott     if ((s = accept(test->listener_tcp, (struct sockaddr *) &addr, &len)) < 0) {
105982c704aSsethdelliott         perror("accept");
106982c704aSsethdelliott         return -1;
107982c704aSsethdelliott     }
108af70b448Ssethdelliott 
109af70b448Ssethdelliott     if (test->ctrl_sck == -1) {
110af70b448Ssethdelliott         /* Server free, accept new client */
111af70b448Ssethdelliott         if (Nread(s, test->default_settings->cookie, COOKIE_SIZE, Ptcp) < 0) {
112af70b448Ssethdelliott             perror("Nread COOKIE\n");
113af70b448Ssethdelliott             return -1;
114af70b448Ssethdelliott         }
115af70b448Ssethdelliott 
116465b565cSsethdelliott         FD_SET(s, &test->read_set);
117465b565cSsethdelliott         FD_SET(s, &test->write_set);
118465b565cSsethdelliott         test->max_fd = (s > test->max_fd) ? s : test->max_fd;
119465b565cSsethdelliott         test->ctrl_sck = s;
120af70b448Ssethdelliott         streams_accepted = 0;
121982c704aSsethdelliott 
122465b565cSsethdelliott         len = sizeof(struct sockaddr_in);
123465b565cSsethdelliott         if (getsockname(s, (struct sockaddr *) &temp1, &len) < 0)
124465b565cSsethdelliott             perror("getsockname");
125465b565cSsethdelliott         if (getpeername(s, (struct sockaddr *) &temp2, &len) < 0)
126465b565cSsethdelliott             perror("getpeername");
127982c704aSsethdelliott 
128465b565cSsethdelliott         inet_ntop(AF_INET, (void *) &temp1.sin_addr, ipl, sizeof(ipl));
129465b565cSsethdelliott         inet_ntop(AF_INET, (void *) &temp2.sin_addr, ipr, sizeof(ipr));
130465b565cSsethdelliott         printf(report_peer, s, ipl, ntohs(temp1.sin_port), ipr, ntohs(temp2.sin_port));
131465b565cSsethdelliott 
132af70b448Ssethdelliott         test->state = PARAM_EXCHANGE;
133af70b448Ssethdelliott         if (Nwrite(test->ctrl_sck, &test->state, sizeof(char), Ptcp) < 0) {
134af70b448Ssethdelliott             perror("Nwrite PARAM_EXCHANGE\n");
135af70b448Ssethdelliott             exit(1);
136af70b448Ssethdelliott         }
137af70b448Ssethdelliott         iperf_exchange_parameters(test);
138982c704aSsethdelliott     } else {
139af70b448Ssethdelliott         if (Nread(s, cookie, COOKIE_SIZE, Ptcp) < 0) {
140af70b448Ssethdelliott             perror("Nread COOKIE\n");
141465b565cSsethdelliott             return -1;
142465b565cSsethdelliott         }
143af70b448Ssethdelliott         if ((strcmp(test->default_settings->cookie, cookie) == 0) && (test->state == CREATE_STREAMS)) {
144af70b448Ssethdelliott             // XXX: CANNOT USE iperf_tcp_accept since stream is alread accepted at this point. New model needed!
145af70b448Ssethdelliott             sp = test->new_stream(test);
146af70b448Ssethdelliott             sp->socket = s;
147af70b448Ssethdelliott             iperf_init_stream(sp, test);
148af70b448Ssethdelliott             iperf_add_stream(test, sp);
149af70b448Ssethdelliott             FD_SET(s, &test->read_set);
150af70b448Ssethdelliott             FD_SET(s, &test->write_set);
151af70b448Ssethdelliott             test->max_fd = (s > test->max_fd) ? s : test->max_fd;
152af70b448Ssethdelliott             connect_msg(sp);
153af70b448Ssethdelliott 
154af70b448Ssethdelliott             ++streams_accepted;
155af70b448Ssethdelliott             if (streams_accepted == test->num_streams) {
156af70b448Ssethdelliott                 test->state = TEST_START;
157af70b448Ssethdelliott                 if (Nwrite(test->ctrl_sck, &test->state, sizeof(char), Ptcp) < 0) {
158af70b448Ssethdelliott                     perror("Nwrite TEST_START\n");
159af70b448Ssethdelliott                     return -1;
160af70b448Ssethdelliott                 }
161af70b448Ssethdelliott             }
162af70b448Ssethdelliott         } else {
163*efb7bbe4Ssethdelliott             if (Nwrite(s, &rbuf, sizeof(int), Ptcp) < 0) {
164*efb7bbe4Ssethdelliott                 perror("Nwrite ACCESS_DENIED");
165465b565cSsethdelliott                 return -1;
166465b565cSsethdelliott             }
167465b565cSsethdelliott             close(s);
168982c704aSsethdelliott         }
169982c704aSsethdelliott     }
170982c704aSsethdelliott 
171af70b448Ssethdelliott     return 0;
172af70b448Ssethdelliott }
173af70b448Ssethdelliott 
174a951c980SBrian Tierney /**************************************************************************/
175982c704aSsethdelliott int
176465b565cSsethdelliott iperf_handle_message_server(struct iperf_test *test)
177982c704aSsethdelliott {
178efdc02f7Ssethdelliott     struct iperf_stream *sp;
179efdc02f7Ssethdelliott 
18036d18d07Ssethdelliott     if (read(test->ctrl_sck, &test->state, sizeof(char)) < 0) {
181ba2672a2Ssethdelliott         // XXX: Needs to indicate read error
182982c704aSsethdelliott         return -1;
183982c704aSsethdelliott     }
184982c704aSsethdelliott 
185982c704aSsethdelliott     switch(test->state) {
186ba2672a2Ssethdelliott         case TEST_START:
187ba2672a2Ssethdelliott             break;
188465b565cSsethdelliott         case TEST_RUNNING:
189465b565cSsethdelliott             break;
190465b565cSsethdelliott         case TEST_END:
191efdc02f7Ssethdelliott             for (sp = test->streams; sp; sp = sp->next) {
192efdc02f7Ssethdelliott                 FD_CLR(sp->socket, &test->read_set);
193efdc02f7Ssethdelliott                 FD_CLR(sp->socket, &test->write_set);
194efdc02f7Ssethdelliott                 close(sp->socket);
195efdc02f7Ssethdelliott             }
196181d61afSsethdelliott             test->state = EXCHANGE_RESULTS;
197*efb7bbe4Ssethdelliott             if (Nwrite(test->ctrl_sck, &test->state, sizeof(char), Ptcp) < 0) {
198*efb7bbe4Ssethdelliott                 perror("Nwrite EXCHANGE_RESULTS");
199181d61afSsethdelliott                 exit(1);
200181d61afSsethdelliott             }
201181d61afSsethdelliott             iperf_exchange_results(test);
202873952e3Ssethdelliott             test->state = DISPLAY_RESULTS;
203*efb7bbe4Ssethdelliott             if (Nwrite(test->ctrl_sck, &test->state, sizeof(char), Ptcp) < 0) {
204*efb7bbe4Ssethdelliott                 perror("Nwrite DISPLAY_RESULTS");
205465b565cSsethdelliott                 exit(1);
206465b565cSsethdelliott             }
20732885561Ssethdelliott             test->stats_callback(test);
208181d61afSsethdelliott             test->reporter_callback(test);
209465b565cSsethdelliott             break;
210873952e3Ssethdelliott         case IPERF_DONE:
211873952e3Ssethdelliott             break;
212465b565cSsethdelliott         case CLIENT_TERMINATE:
213465b565cSsethdelliott             fprintf(stderr, "The client has terminated. Exiting...\n");
214ba2672a2Ssethdelliott             exit(1);
215ba2672a2Ssethdelliott         default:
216ba2672a2Ssethdelliott             // XXX: This needs to be replaced by actual error handling
2177fa9f68fSsethdelliott             fprintf(stderr, "How did you get here? test->state = %d\n", test->state);
218ba2672a2Ssethdelliott             return -1;
219982c704aSsethdelliott     }
220982c704aSsethdelliott 
221982c704aSsethdelliott     return 0;
222982c704aSsethdelliott }
223982c704aSsethdelliott 
224efdc02f7Ssethdelliott void
225efdc02f7Ssethdelliott iperf_test_reset(struct iperf_test *test)
226efdc02f7Ssethdelliott {
227efdc02f7Ssethdelliott     struct iperf_stream *sp, *np;
228efdc02f7Ssethdelliott 
229efdc02f7Ssethdelliott     close(test->ctrl_sck);
230efdc02f7Ssethdelliott 
231efdc02f7Ssethdelliott     /* Free streams */
232efdc02f7Ssethdelliott     for (sp = test->streams; sp; sp = np) {
233efdc02f7Ssethdelliott         np = sp->next;
234efdc02f7Ssethdelliott         iperf_free_stream(sp);
235efdc02f7Ssethdelliott     }
236efdc02f7Ssethdelliott 
23732885561Ssethdelliott     test->streams = NULL;
23832885561Ssethdelliott 
239efdc02f7Ssethdelliott     test->role = 's';
24032885561Ssethdelliott     test->protocol = Ptcp;
24132885561Ssethdelliott     test->duration = DURATION;
24232885561Ssethdelliott     test->state = 0;
24332885561Ssethdelliott     test->server_hostname = NULL;
24432885561Ssethdelliott 
24532885561Ssethdelliott     test->ctrl_sck = -1;
24632885561Ssethdelliott     test->prot_listener = 0;
24732885561Ssethdelliott 
24832885561Ssethdelliott     test->reverse = 0;
24932885561Ssethdelliott     // test->no_delay = 0 (Do we need this reset between tests?)
25032885561Ssethdelliott 
251efdc02f7Ssethdelliott     FD_ZERO(&test->read_set);
252efdc02f7Ssethdelliott     FD_ZERO(&test->write_set);
253af70b448Ssethdelliott     FD_SET(test->listener_tcp, &test->read_set);
254af70b448Ssethdelliott     test->max_fd = test->listener_tcp;
25532885561Ssethdelliott 
25632885561Ssethdelliott     test->num_streams = 1;
25732885561Ssethdelliott     test->default_settings->socket_bufsize = 0;
25832885561Ssethdelliott     test->default_settings->blksize = DEFAULT_TCP_BLKSIZE;
25932885561Ssethdelliott     test->default_settings->rate = RATE;   /* UDP only */
26032885561Ssethdelliott     memset(test->default_settings->cookie, 0, COOKIE_SIZE);
261efdc02f7Ssethdelliott }
262efdc02f7Ssethdelliott 
2637fa9f68fSsethdelliott int
264a951c980SBrian Tierney iperf_run_server(struct iperf_test *test)
265a951c980SBrian Tierney {
2667fa9f68fSsethdelliott     int result;
267efdc02f7Ssethdelliott     int streams_accepted;
268efdc02f7Ssethdelliott     fd_set temp_read_set, temp_write_set;
2697fa9f68fSsethdelliott     struct timeval tv;
270a951c980SBrian Tierney 
271982c704aSsethdelliott     // Open socket and listen
272982c704aSsethdelliott     if (iperf_server_listen(test) < 0) {
273982c704aSsethdelliott         // This needs to be replaced by more formal error handling
274982c704aSsethdelliott         fprintf(stderr, "An error occurred. Exiting.\n");
275982c704aSsethdelliott         exit(1);
276e99faeaeSBrian Tierney     }
277a951c980SBrian Tierney 
278465b565cSsethdelliott     signal(SIGINT, sig_handler);
279465b565cSsethdelliott     if (setjmp(env)) {
280465b565cSsethdelliott         fprintf(stderr, "Interrupt received. Exiting...\n");
281465b565cSsethdelliott         test->state = SERVER_TERMINATE;
282465b565cSsethdelliott         if (test->ctrl_sck >= 0) {
283*efb7bbe4Ssethdelliott             if (Nwrite(test->ctrl_sck, &test->state, sizeof(char), Ptcp) < 0) {
284465b565cSsethdelliott                 fprintf(stderr, "Unable to send SERVER_TERMINATE message to client\n");
285465b565cSsethdelliott             }
286465b565cSsethdelliott         }
287465b565cSsethdelliott         exit(1);
288465b565cSsethdelliott     }
289465b565cSsethdelliott 
290efdc02f7Ssethdelliott     for (;;) {
291efdc02f7Ssethdelliott 
292efdc02f7Ssethdelliott         test->state = IPERF_START;
293efdc02f7Ssethdelliott         streams_accepted = 0;
294a951c980SBrian Tierney 
295873952e3Ssethdelliott         while (test->state != IPERF_DONE) {
296982c704aSsethdelliott 
297efdc02f7Ssethdelliott             memcpy(&temp_read_set, &test->read_set, sizeof(fd_set));
298efdc02f7Ssethdelliott             memcpy(&temp_write_set, &test->write_set, sizeof(fd_set));
299982c704aSsethdelliott             tv.tv_sec = 15;
300982c704aSsethdelliott             tv.tv_usec = 0;
301982c704aSsethdelliott 
302efdc02f7Ssethdelliott             result = select(test->max_fd + 1, &temp_read_set, &temp_write_set, NULL, &tv);
303982c704aSsethdelliott             if (result < 0 && errno != EINTR) {
304982c704aSsethdelliott                 // Change the way this handles errors
305982c704aSsethdelliott                 perror("select");
306982c704aSsethdelliott                 exit(1);
307982c704aSsethdelliott             } else if (result > 0) {
308af70b448Ssethdelliott                 if (FD_ISSET(test->listener_tcp, &temp_read_set)) {
309465b565cSsethdelliott                     if (iperf_accept(test) < 0) {
310465b565cSsethdelliott                         fprintf(stderr, "iperf_accept: error accepting control socket. exiting...\n");
311982c704aSsethdelliott                         exit(1);
312982c704aSsethdelliott                     }
313af70b448Ssethdelliott                     FD_CLR(test->listener_tcp, &temp_read_set);
314982c704aSsethdelliott                 }
315efdc02f7Ssethdelliott                 if (FD_ISSET(test->ctrl_sck, &temp_read_set)) {
316465b565cSsethdelliott                     iperf_handle_message_server(test);
317efdc02f7Ssethdelliott                     FD_CLR(test->ctrl_sck, &temp_read_set);
318982c704aSsethdelliott                 }
319465b565cSsethdelliott 
320af70b448Ssethdelliott /*
321465b565cSsethdelliott                 if (test->state == CREATE_STREAMS) {
322efdc02f7Ssethdelliott                     if (FD_ISSET(test->prot_listener, &temp_read_set)) {
323982c704aSsethdelliott                         // Spawn new streams
324465b565cSsethdelliott                         // XXX: This works, but should it check cookies for authenticity
325465b565cSsethdelliott                         //      Also, currently it uses 5202 for stream connections.
326465b565cSsethdelliott                         //      Should this be fixed to use 5201 instead?
327ba2672a2Ssethdelliott                         iperf_tcp_accept(test);
328465b565cSsethdelliott                         ++streams_accepted;
329efdc02f7Ssethdelliott                         FD_CLR(test->prot_listener, &temp_read_set);
330982c704aSsethdelliott                     }
331465b565cSsethdelliott                     if (streams_accepted == test->num_streams) {
332465b565cSsethdelliott                         FD_CLR(test->prot_listener, &test->read_set);
333465b565cSsethdelliott                         close(test->prot_listener);
334465b565cSsethdelliott                         test->state = TEST_START;
33536d18d07Ssethdelliott                         if (write(test->ctrl_sck, &test->state, sizeof(char)) < 0) {
336465b565cSsethdelliott                             perror("write TEST_START");
337465b565cSsethdelliott                             return -1;
338982c704aSsethdelliott                         }
339982c704aSsethdelliott                     }
340982c704aSsethdelliott                 }
341af70b448Ssethdelliott */
342982c704aSsethdelliott 
343465b565cSsethdelliott                 if (test->reverse) {
344465b565cSsethdelliott                     // Reverse mode. Server sends.
345465b565cSsethdelliott                     iperf_send(test);
346982c704aSsethdelliott                 } else {
347465b565cSsethdelliott                     // Regular mode. Server receives.
348465b565cSsethdelliott                     iperf_recv(test);
349e99faeaeSBrian Tierney                 }
350a951c980SBrian Tierney             }
351982c704aSsethdelliott         }
352e99faeaeSBrian Tierney 
353efdc02f7Ssethdelliott         /* Clean up the last test */
354efdc02f7Ssethdelliott         iperf_test_reset(test);
355efdc02f7Ssethdelliott         printf("\n");
356efdc02f7Ssethdelliott 
357efdc02f7Ssethdelliott     }
358efdc02f7Ssethdelliott 
3594f431119Ssethdelliott     // XXX: Need to put the above while loop into another control structure
3604f431119Ssethdelliott     //      that initiates a new test upon each connection rather than dying.
3615083c489SBrian Tierney 
3627fa9f68fSsethdelliott     return 0;
363a3281a3dSBrian Tierney }
364a3281a3dSBrian Tierney 
365