xref: /iperf/src/iperf_server_api.c (revision 112a907c)
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>
208a0b5a5dSsethdelliott #include <sys/queue.h>
21a951c980SBrian Tierney #include <sys/socket.h>
22a951c980SBrian Tierney #include <sys/types.h>
23a951c980SBrian Tierney #include <netinet/in.h>
24a951c980SBrian Tierney #include <arpa/inet.h>
25a951c980SBrian Tierney #include <netdb.h>
26a951c980SBrian Tierney #include <pthread.h>
27a951c980SBrian Tierney #include <stdint.h>
28a951c980SBrian Tierney #include <netinet/tcp.h>
29a951c980SBrian Tierney #include <sys/time.h>
30a951c980SBrian Tierney #include <sys/resource.h>
31a951c980SBrian Tierney #include <sched.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"
39b60a49ddSsethdelliott #include "iperf_error.h"
408a0b5a5dSsethdelliott #include "iperf_util.h"
41a951c980SBrian Tierney #include "timer.h"
42a951c980SBrian Tierney #include "net.h"
43a951c980SBrian Tierney #include "units.h"
44a951c980SBrian Tierney #include "tcp_window_size.h"
45b0b16b86SJon Dugan #include "iperf_util.h"
46a951c980SBrian Tierney #include "locale.h"
47a951c980SBrian Tierney 
48a3281a3dSBrian Tierney 
49982c704aSsethdelliott int
50982c704aSsethdelliott iperf_server_listen(struct iperf_test *test)
51982c704aSsethdelliott {
52982c704aSsethdelliott     char ubuf[UNIT_LEN];
53982c704aSsethdelliott     int x;
54982c704aSsethdelliott 
5566ce7ad4Ssethdelliott     if((test->listener = netannounce(Ptcp, NULL, test->server_port)) < 0) {
56b60a49ddSsethdelliott         i_errno = IELISTEN;
57b60a49ddSsethdelliott         return (-1);
58982c704aSsethdelliott     }
59982c704aSsethdelliott 
60982c704aSsethdelliott     printf("-----------------------------------------------------------\n");
61982c704aSsethdelliott     printf("Server listening on %d\n", test->server_port);
62982c704aSsethdelliott 
63982c704aSsethdelliott     // This needs to be changed to reflect if client has different window size
64982c704aSsethdelliott     // make sure we got what we asked for
65ba2672a2Ssethdelliott     /* XXX: This needs to be moved to the stream listener
6666ce7ad4Ssethdelliott     if ((x = get_tcp_windowsize(test->listener, SO_RCVBUF)) < 0) {
67982c704aSsethdelliott         // Needs to set some sort of error number/message
68982c704aSsethdelliott         perror("SO_RCVBUF");
69982c704aSsethdelliott         return -1;
70982c704aSsethdelliott     }
71ba2672a2Ssethdelliott     */
72982c704aSsethdelliott 
73ba2672a2Ssethdelliott     // XXX: This code needs to be moved to after parameter exhange
74c81883bfSsethdelliott     /*
7566ce7ad4Ssethdelliott     if (test->protocol->id == Ptcp) {
76*112a907cSsethdelliott         if (test->settings->socket_bufsize > 0) {
77982c704aSsethdelliott             unit_snprintf(ubuf, UNIT_LEN, (double) x, 'A');
78982c704aSsethdelliott             printf("TCP window size: %s\n", ubuf);
79982c704aSsethdelliott         } else {
80982c704aSsethdelliott             printf("Using TCP Autotuning\n");
81982c704aSsethdelliott         }
82982c704aSsethdelliott     }
83c81883bfSsethdelliott     */
84982c704aSsethdelliott     printf("-----------------------------------------------------------\n");
85982c704aSsethdelliott 
86982c704aSsethdelliott     FD_ZERO(&test->read_set);
87efdc02f7Ssethdelliott     FD_ZERO(&test->write_set);
8866ce7ad4Ssethdelliott     FD_SET(test->listener, &test->read_set);
8966ce7ad4Ssethdelliott     test->max_fd = (test->listener > test->max_fd) ? test->listener : test->max_fd;
90982c704aSsethdelliott 
91982c704aSsethdelliott     return 0;
92982c704aSsethdelliott }
93982c704aSsethdelliott 
94982c704aSsethdelliott int
95982c704aSsethdelliott iperf_accept(struct iperf_test *test)
96982c704aSsethdelliott {
97982c704aSsethdelliott     int s;
98465b565cSsethdelliott     int rbuf = ACCESS_DENIED;
99b60a49ddSsethdelliott     char ipl[512], ipr[512];    // XXX: This is overkill. Max length of IPv6 address = 46 (INET6_ADDRSTRLEN)
100af70b448Ssethdelliott     char cookie[COOKIE_SIZE];
101982c704aSsethdelliott     socklen_t len;
102982c704aSsethdelliott     struct sockaddr_in addr;
103465b565cSsethdelliott     struct sockaddr_in temp1, temp2;
104982c704aSsethdelliott 
105b68ac00eSsethdelliott     len = sizeof(addr);
10666ce7ad4Ssethdelliott     if ((s = accept(test->listener, (struct sockaddr *) &addr, &len)) < 0) {
107b60a49ddSsethdelliott         i_errno = IEACCEPT;
108b60a49ddSsethdelliott         return (-1);
109982c704aSsethdelliott     }
110af70b448Ssethdelliott 
111af70b448Ssethdelliott     if (test->ctrl_sck == -1) {
112af70b448Ssethdelliott         /* Server free, accept new client */
113*112a907cSsethdelliott         if (Nread(s, test->cookie, COOKIE_SIZE, Ptcp) < 0) {
114b60a49ddSsethdelliott             i_errno = IERECVCOOKIE;
115b60a49ddSsethdelliott             return (-1);
116af70b448Ssethdelliott         }
117af70b448Ssethdelliott 
118465b565cSsethdelliott         FD_SET(s, &test->read_set);
119465b565cSsethdelliott         FD_SET(s, &test->write_set);
120465b565cSsethdelliott         test->max_fd = (s > test->max_fd) ? s : test->max_fd;
121465b565cSsethdelliott         test->ctrl_sck = s;
122982c704aSsethdelliott 
123465b565cSsethdelliott         len = sizeof(struct sockaddr_in);
124b60a49ddSsethdelliott         if (getsockname(s, (struct sockaddr *) &temp1, &len) < 0) {
125b60a49ddSsethdelliott             i_errno = IEACCEPT;
126b60a49ddSsethdelliott             return (-1);
127b60a49ddSsethdelliott         }
128b60a49ddSsethdelliott         if (getpeername(s, (struct sockaddr *) &temp2, &len) < 0) {
129b60a49ddSsethdelliott             i_errno = IEACCEPT;
130b60a49ddSsethdelliott             return (-1);
131b60a49ddSsethdelliott         }
132982c704aSsethdelliott 
133b60a49ddSsethdelliott         // XXX: Check inet_ntop for errors?
134465b565cSsethdelliott         inet_ntop(AF_INET, (void *) &temp1.sin_addr, ipl, sizeof(ipl));
135465b565cSsethdelliott         inet_ntop(AF_INET, (void *) &temp2.sin_addr, ipr, sizeof(ipr));
136b60a49ddSsethdelliott 
137465b565cSsethdelliott         printf(report_peer, s, ipl, ntohs(temp1.sin_port), ipr, ntohs(temp2.sin_port));
138465b565cSsethdelliott 
139af70b448Ssethdelliott         test->state = PARAM_EXCHANGE;
140af70b448Ssethdelliott         if (Nwrite(test->ctrl_sck, &test->state, sizeof(char), Ptcp) < 0) {
141b60a49ddSsethdelliott             i_errno = IESENDMESSAGE;
142b60a49ddSsethdelliott             return (-1);
143af70b448Ssethdelliott         }
144b60a49ddSsethdelliott         if (iperf_exchange_parameters(test) < 0) {
145b60a49ddSsethdelliott             return (-1);
146b60a49ddSsethdelliott         }
147982c704aSsethdelliott     } else {
148b60a49ddSsethdelliott         // XXX: Do we even need to receive cookie if we're just going to deny anyways?
149af70b448Ssethdelliott         if (Nread(s, cookie, COOKIE_SIZE, Ptcp) < 0) {
150b60a49ddSsethdelliott             i_errno = IERECVCOOKIE;
151b60a49ddSsethdelliott             return (-1);
152465b565cSsethdelliott         }
153efb7bbe4Ssethdelliott         if (Nwrite(s, &rbuf, sizeof(int), Ptcp) < 0) {
154b60a49ddSsethdelliott             i_errno = IESENDMESSAGE;
155b60a49ddSsethdelliott             return (-1);
156465b565cSsethdelliott         }
157465b565cSsethdelliott         close(s);
158982c704aSsethdelliott     }
159982c704aSsethdelliott 
160b60a49ddSsethdelliott     return (0);
161af70b448Ssethdelliott }
162af70b448Ssethdelliott 
163b68ac00eSsethdelliott 
164a951c980SBrian Tierney /**************************************************************************/
165982c704aSsethdelliott int
166465b565cSsethdelliott iperf_handle_message_server(struct iperf_test *test)
167982c704aSsethdelliott {
168b68ac00eSsethdelliott     int rval;
169efdc02f7Ssethdelliott     struct iperf_stream *sp;
170efdc02f7Ssethdelliott 
171b60a49ddSsethdelliott     // XXX: Need to rethink how this behaves to fit API
172b60a49ddSsethdelliott     if ((rval = Nread(test->ctrl_sck, &test->state, sizeof(char), Ptcp)) <= 0) {
173b68ac00eSsethdelliott         if (rval == 0) {
174b68ac00eSsethdelliott             fprintf(stderr, "The client has unexpectedly closed the connection.\n");
175b60a49ddSsethdelliott             i_errno = IECTRLCLOSE;
176b68ac00eSsethdelliott             test->state = IPERF_DONE;
177b60a49ddSsethdelliott             return (0);
178b68ac00eSsethdelliott         } else {
179b60a49ddSsethdelliott             i_errno = IERECVMESSAGE;
180b60a49ddSsethdelliott             return (-1);
181982c704aSsethdelliott         }
182b68ac00eSsethdelliott     }
183982c704aSsethdelliott 
184982c704aSsethdelliott     switch(test->state) {
185ba2672a2Ssethdelliott         case TEST_START:
186ba2672a2Ssethdelliott             break;
187465b565cSsethdelliott         case TEST_END:
188f1b3bd81Ssethdelliott             test->stats_callback(test);
189efdc02f7Ssethdelliott             for (sp = test->streams; sp; sp = sp->next) {
190efdc02f7Ssethdelliott                 FD_CLR(sp->socket, &test->read_set);
191efdc02f7Ssethdelliott                 FD_CLR(sp->socket, &test->write_set);
192efdc02f7Ssethdelliott                 close(sp->socket);
193efdc02f7Ssethdelliott             }
194181d61afSsethdelliott             test->state = EXCHANGE_RESULTS;
195efb7bbe4Ssethdelliott             if (Nwrite(test->ctrl_sck, &test->state, sizeof(char), Ptcp) < 0) {
196b60a49ddSsethdelliott                 i_errno = IESENDMESSAGE;
197b60a49ddSsethdelliott                 return (-1);
198181d61afSsethdelliott             }
199b60a49ddSsethdelliott             if (iperf_exchange_results(test) < 0)
200b60a49ddSsethdelliott                 return (-1);
201873952e3Ssethdelliott             test->state = DISPLAY_RESULTS;
202efb7bbe4Ssethdelliott             if (Nwrite(test->ctrl_sck, &test->state, sizeof(char), Ptcp) < 0) {
203b60a49ddSsethdelliott                 i_errno = IESENDMESSAGE;
204b60a49ddSsethdelliott                 return (-1);
205465b565cSsethdelliott             }
206181d61afSsethdelliott             test->reporter_callback(test);
207465b565cSsethdelliott             break;
208873952e3Ssethdelliott         case IPERF_DONE:
209873952e3Ssethdelliott             break;
210465b565cSsethdelliott         case CLIENT_TERMINATE:
2111ac522ecSsethdelliott             fprintf(stderr, "The client has terminated.\n");
2121ac522ecSsethdelliott             for (sp = test->streams; sp; sp = sp->next) {
2131ac522ecSsethdelliott                 FD_CLR(sp->socket, &test->read_set);
2141ac522ecSsethdelliott                 FD_CLR(sp->socket, &test->write_set);
2151ac522ecSsethdelliott                 close(sp->socket);
2161ac522ecSsethdelliott             }
2171ac522ecSsethdelliott             test->state = IPERF_DONE;
2181ac522ecSsethdelliott             break;
219ba2672a2Ssethdelliott         default:
220b60a49ddSsethdelliott             i_errno = IEMESSAGE;
221b60a49ddSsethdelliott             return (-1);
222982c704aSsethdelliott     }
223982c704aSsethdelliott 
224b60a49ddSsethdelliott     return (0);
225982c704aSsethdelliott }
226982c704aSsethdelliott 
227efdc02f7Ssethdelliott void
228efdc02f7Ssethdelliott iperf_test_reset(struct iperf_test *test)
229efdc02f7Ssethdelliott {
230efdc02f7Ssethdelliott     struct iperf_stream *sp, *np;
231efdc02f7Ssethdelliott 
232efdc02f7Ssethdelliott     close(test->ctrl_sck);
233efdc02f7Ssethdelliott 
234efdc02f7Ssethdelliott     /* Free streams */
235efdc02f7Ssethdelliott     for (sp = test->streams; sp; sp = np) {
236efdc02f7Ssethdelliott         np = sp->next;
237efdc02f7Ssethdelliott         iperf_free_stream(sp);
238efdc02f7Ssethdelliott     }
239f1b3bd81Ssethdelliott     free_timer(test->timer);
240f1b3bd81Ssethdelliott     free_timer(test->stats_timer);
241f1b3bd81Ssethdelliott     free_timer(test->reporter_timer);
242f1b3bd81Ssethdelliott     test->timer = NULL;
243f1b3bd81Ssethdelliott     test->stats_timer = NULL;
244f1b3bd81Ssethdelliott     test->reporter_timer = NULL;
245efdc02f7Ssethdelliott 
24632885561Ssethdelliott     test->streams = NULL;
24732885561Ssethdelliott 
248efdc02f7Ssethdelliott     test->role = 's';
24966ce7ad4Ssethdelliott     set_protocol(test, Ptcp);
25032885561Ssethdelliott     test->duration = DURATION;
25132885561Ssethdelliott     test->state = 0;
25232885561Ssethdelliott     test->server_hostname = NULL;
25332885561Ssethdelliott 
25432885561Ssethdelliott     test->ctrl_sck = -1;
25566ce7ad4Ssethdelliott     test->prot_listener = -1;
25632885561Ssethdelliott 
257f1b3bd81Ssethdelliott     test->bytes_sent = 0;
258f1b3bd81Ssethdelliott 
25932885561Ssethdelliott     test->reverse = 0;
260b68ac00eSsethdelliott     test->no_delay = 0;
26132885561Ssethdelliott 
262efdc02f7Ssethdelliott     FD_ZERO(&test->read_set);
263efdc02f7Ssethdelliott     FD_ZERO(&test->write_set);
26466ce7ad4Ssethdelliott     FD_SET(test->listener, &test->read_set);
26566ce7ad4Ssethdelliott     test->max_fd = test->listener;
26632885561Ssethdelliott 
26732885561Ssethdelliott     test->num_streams = 1;
268*112a907cSsethdelliott     test->settings->socket_bufsize = 0;
269*112a907cSsethdelliott     test->settings->blksize = DEFAULT_TCP_BLKSIZE;
270*112a907cSsethdelliott     test->settings->rate = RATE;   /* UDP only */
271*112a907cSsethdelliott     test->settings->mss = 0;
272*112a907cSsethdelliott     memset(test->cookie, 0, COOKIE_SIZE);
273efdc02f7Ssethdelliott }
274efdc02f7Ssethdelliott 
2757fa9f68fSsethdelliott int
276a951c980SBrian Tierney iperf_run_server(struct iperf_test *test)
277a951c980SBrian Tierney {
278*112a907cSsethdelliott     int result, s, streams_accepted;
279efdc02f7Ssethdelliott     fd_set temp_read_set, temp_write_set;
2808a0b5a5dSsethdelliott     struct iperf_stream *sp;
2817fa9f68fSsethdelliott     struct timeval tv;
282a951c980SBrian Tierney 
283982c704aSsethdelliott     // Open socket and listen
284982c704aSsethdelliott     if (iperf_server_listen(test) < 0) {
285b60a49ddSsethdelliott         return (-1);
286e99faeaeSBrian Tierney     }
287a951c980SBrian Tierney 
288465b565cSsethdelliott     signal(SIGINT, sig_handler);
289465b565cSsethdelliott     if (setjmp(env)) {
290465b565cSsethdelliott         test->state = SERVER_TERMINATE;
291465b565cSsethdelliott         if (test->ctrl_sck >= 0) {
292efb7bbe4Ssethdelliott             if (Nwrite(test->ctrl_sck, &test->state, sizeof(char), Ptcp) < 0) {
293b60a49ddSsethdelliott                 i_errno = IESENDMESSAGE;
294b60a49ddSsethdelliott                 return (-1);
295465b565cSsethdelliott             }
296465b565cSsethdelliott         }
29766ce7ad4Ssethdelliott         return (0);
298465b565cSsethdelliott     }
299465b565cSsethdelliott 
300efdc02f7Ssethdelliott     for ( ; ; ) {
301efdc02f7Ssethdelliott 
302efdc02f7Ssethdelliott         test->state = IPERF_START;
303*112a907cSsethdelliott         streams_accepted = 0;
304a951c980SBrian Tierney 
305873952e3Ssethdelliott         while (test->state != IPERF_DONE) {
306982c704aSsethdelliott 
307efdc02f7Ssethdelliott             memcpy(&temp_read_set, &test->read_set, sizeof(fd_set));
308efdc02f7Ssethdelliott             memcpy(&temp_write_set, &test->write_set, sizeof(fd_set));
309982c704aSsethdelliott             tv.tv_sec = 15;
310982c704aSsethdelliott             tv.tv_usec = 0;
311982c704aSsethdelliott 
312efdc02f7Ssethdelliott             result = select(test->max_fd + 1, &temp_read_set, &temp_write_set, NULL, &tv);
313982c704aSsethdelliott             if (result < 0 && errno != EINTR) {
314b60a49ddSsethdelliott                 i_errno = IESELECT;
315b60a49ddSsethdelliott                 return (-1);
316982c704aSsethdelliott             } else if (result > 0) {
31766ce7ad4Ssethdelliott                 if (FD_ISSET(test->listener, &temp_read_set)) {
318b68ac00eSsethdelliott                     if (test->state != CREATE_STREAMS) {
319465b565cSsethdelliott                         if (iperf_accept(test) < 0) {
320b60a49ddSsethdelliott                             return (-1);
321982c704aSsethdelliott                         }
32266ce7ad4Ssethdelliott                         FD_CLR(test->listener, &temp_read_set);
323982c704aSsethdelliott                     }
324b68ac00eSsethdelliott                 }
325efdc02f7Ssethdelliott                 if (FD_ISSET(test->ctrl_sck, &temp_read_set)) {
326b60a49ddSsethdelliott                     if (iperf_handle_message_server(test) < 0)
327b60a49ddSsethdelliott                         return (-1);
328efdc02f7Ssethdelliott                     FD_CLR(test->ctrl_sck, &temp_read_set);
329982c704aSsethdelliott                 }
330465b565cSsethdelliott 
331465b565cSsethdelliott                 if (test->state == CREATE_STREAMS) {
3328a0b5a5dSsethdelliott                     if (FD_ISSET(test->prot_listener, &temp_read_set)) {
3338a0b5a5dSsethdelliott 
33466ce7ad4Ssethdelliott                         if ((s = test->protocol->accept(test)) < 0)
335b60a49ddSsethdelliott                             return (-1);
3368a0b5a5dSsethdelliott 
3378a0b5a5dSsethdelliott                         if (!is_closed(s)) {
33866ce7ad4Ssethdelliott                             sp = iperf_new_stream(test, s);
3398a0b5a5dSsethdelliott                             if (!sp)
340b60a49ddSsethdelliott                                 return (-1);
34166ce7ad4Ssethdelliott 
3428a0b5a5dSsethdelliott                             FD_SET(s, &test->read_set);
3438a0b5a5dSsethdelliott                             FD_SET(s, &test->write_set);
3448a0b5a5dSsethdelliott                             test->max_fd = (s > test->max_fd) ? s : test->max_fd;
34566ce7ad4Ssethdelliott 
346*112a907cSsethdelliott                             streams_accepted++;
3478a0b5a5dSsethdelliott                             connect_msg(sp);
348b68ac00eSsethdelliott                         }
3498a0b5a5dSsethdelliott                         FD_CLR(test->prot_listener, &temp_read_set);
350b68ac00eSsethdelliott                     }
3518a0b5a5dSsethdelliott 
352*112a907cSsethdelliott                     if (streams_accepted == test->num_streams) {
35366ce7ad4Ssethdelliott                         if (test->protocol->id != Ptcp) {
3548a0b5a5dSsethdelliott                             FD_CLR(test->prot_listener, &test->read_set);
3558a0b5a5dSsethdelliott                             close(test->prot_listener);
35666ce7ad4Ssethdelliott                         } else {
357*112a907cSsethdelliott                             if (test->no_delay || test->settings->mss) {
35866ce7ad4Ssethdelliott                                 FD_CLR(test->listener, &test->read_set);
35966ce7ad4Ssethdelliott                                 close(test->listener);
360c81883bfSsethdelliott                                 if ((s = netannounce(Ptcp, NULL, test->server_port)) < 0) {
361b60a49ddSsethdelliott                                     i_errno = IELISTEN;
362c81883bfSsethdelliott                                     return (-1);
363c81883bfSsethdelliott                                 }
36466ce7ad4Ssethdelliott                                 test->listener = s;
365c81883bfSsethdelliott                                 test->max_fd = (s > test->max_fd ? s : test->max_fd);
36666ce7ad4Ssethdelliott                                 FD_SET(test->listener, &test->read_set);
367c81883bfSsethdelliott                             }
3688556db5dSsethdelliott                         }
3698a0b5a5dSsethdelliott                         test->prot_listener = -1;
370465b565cSsethdelliott                         test->state = TEST_START;
371b68ac00eSsethdelliott                         if (Nwrite(test->ctrl_sck, &test->state, sizeof(char), Ptcp) < 0) {
372b60a49ddSsethdelliott                             i_errno = IESENDMESSAGE;
373b60a49ddSsethdelliott                             return (-1);
374982c704aSsethdelliott                         }
375b60a49ddSsethdelliott                         if (iperf_init_test(test) < 0)
376b60a49ddSsethdelliott                             return (-1);
377f1b3bd81Ssethdelliott                         test->state = TEST_RUNNING;
378f1b3bd81Ssethdelliott                         if (Nwrite(test->ctrl_sck, &test->state, sizeof(char), Ptcp) < 0) {
379b60a49ddSsethdelliott                             i_errno = IESENDMESSAGE;
380b60a49ddSsethdelliott                             return (-1);
381f1b3bd81Ssethdelliott                         }
382982c704aSsethdelliott                     }
383982c704aSsethdelliott                 }
384982c704aSsethdelliott 
385f1b3bd81Ssethdelliott                 if (test->state == TEST_RUNNING) {
386465b565cSsethdelliott                     if (test->reverse) {
387465b565cSsethdelliott                         // Reverse mode. Server sends.
388b60a49ddSsethdelliott                         if (iperf_send(test) < 0)
389b60a49ddSsethdelliott                             return (-1);
390982c704aSsethdelliott                     } else {
391465b565cSsethdelliott                         // Regular mode. Server receives.
392b60a49ddSsethdelliott                         if (iperf_recv(test) < 0)
393b60a49ddSsethdelliott                             return (-1);
394e99faeaeSBrian Tierney                     }
395f1b3bd81Ssethdelliott 
396f1b3bd81Ssethdelliott                     /* Perform callbacks */
397f1b3bd81Ssethdelliott                     if (timer_expired(test->stats_timer)) {
398f1b3bd81Ssethdelliott                         test->stats_callback(test);
399b60a49ddSsethdelliott                         if (update_timer(test->stats_timer, test->stats_interval, 0) < 0)
400b60a49ddSsethdelliott                             return (-1);
401f1b3bd81Ssethdelliott                     }
402f1b3bd81Ssethdelliott                     if (timer_expired(test->reporter_timer)) {
403f1b3bd81Ssethdelliott                         test->reporter_callback(test);
404b60a49ddSsethdelliott                         if (update_timer(test->reporter_timer, test->reporter_interval, 0) < 0)
405b60a49ddSsethdelliott                             return (-1);
406f1b3bd81Ssethdelliott                     }
407f1b3bd81Ssethdelliott                 }
408a951c980SBrian Tierney             }
409982c704aSsethdelliott         }
410e99faeaeSBrian Tierney 
411efdc02f7Ssethdelliott         /* Clean up the last test */
412efdc02f7Ssethdelliott         iperf_test_reset(test);
413efdc02f7Ssethdelliott         printf("\n");
414efdc02f7Ssethdelliott 
415efdc02f7Ssethdelliott     }
416efdc02f7Ssethdelliott 
417b60a49ddSsethdelliott     return (0);
418a3281a3dSBrian Tierney }
419a3281a3dSBrian Tierney 
420