xref: /iperf/src/iperf_server_api.c (revision f99bd3b2)
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 
55*f99bd3b2Ssethdelliott     if((test->listener = netannounce(test->settings->domain, Ptcp, test->bind_address, 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) {
76112a907cSsethdelliott         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;
99af70b448Ssethdelliott     char cookie[COOKIE_SIZE];
100982c704aSsethdelliott     socklen_t len;
101982c704aSsethdelliott     struct sockaddr_in addr;
102982c704aSsethdelliott 
103b68ac00eSsethdelliott     len = sizeof(addr);
10466ce7ad4Ssethdelliott     if ((s = accept(test->listener, (struct sockaddr *) &addr, &len)) < 0) {
105b60a49ddSsethdelliott         i_errno = IEACCEPT;
106b60a49ddSsethdelliott         return (-1);
107982c704aSsethdelliott     }
108af70b448Ssethdelliott 
109af70b448Ssethdelliott     if (test->ctrl_sck == -1) {
110af70b448Ssethdelliott         /* Server free, accept new client */
111112a907cSsethdelliott         if (Nread(s, test->cookie, COOKIE_SIZE, Ptcp) < 0) {
112b60a49ddSsethdelliott             i_errno = IERECVCOOKIE;
113b60a49ddSsethdelliott             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;
120982c704aSsethdelliott 
121af70b448Ssethdelliott         test->state = PARAM_EXCHANGE;
122af70b448Ssethdelliott         if (Nwrite(test->ctrl_sck, &test->state, sizeof(char), Ptcp) < 0) {
123b60a49ddSsethdelliott             i_errno = IESENDMESSAGE;
124b60a49ddSsethdelliott             return (-1);
125af70b448Ssethdelliott         }
126b60a49ddSsethdelliott         if (iperf_exchange_parameters(test) < 0) {
127b60a49ddSsethdelliott             return (-1);
128b60a49ddSsethdelliott         }
1294df383f6Ssethdelliott         if (test->on_connect) {
1304df383f6Ssethdelliott             test->on_connect(test);
1314df383f6Ssethdelliott         }
132982c704aSsethdelliott     } else {
133b60a49ddSsethdelliott         // XXX: Do we even need to receive cookie if we're just going to deny anyways?
134af70b448Ssethdelliott         if (Nread(s, cookie, COOKIE_SIZE, Ptcp) < 0) {
135b60a49ddSsethdelliott             i_errno = IERECVCOOKIE;
136b60a49ddSsethdelliott             return (-1);
137465b565cSsethdelliott         }
138efb7bbe4Ssethdelliott         if (Nwrite(s, &rbuf, sizeof(int), Ptcp) < 0) {
139b60a49ddSsethdelliott             i_errno = IESENDMESSAGE;
140b60a49ddSsethdelliott             return (-1);
141465b565cSsethdelliott         }
142465b565cSsethdelliott         close(s);
143982c704aSsethdelliott     }
144982c704aSsethdelliott 
145b60a49ddSsethdelliott     return (0);
146af70b448Ssethdelliott }
147af70b448Ssethdelliott 
148b68ac00eSsethdelliott 
149a951c980SBrian Tierney /**************************************************************************/
150982c704aSsethdelliott int
151465b565cSsethdelliott iperf_handle_message_server(struct iperf_test *test)
152982c704aSsethdelliott {
153b68ac00eSsethdelliott     int rval;
154efdc02f7Ssethdelliott     struct iperf_stream *sp;
155efdc02f7Ssethdelliott 
156b60a49ddSsethdelliott     // XXX: Need to rethink how this behaves to fit API
157b60a49ddSsethdelliott     if ((rval = Nread(test->ctrl_sck, &test->state, sizeof(char), Ptcp)) <= 0) {
158b68ac00eSsethdelliott         if (rval == 0) {
159b68ac00eSsethdelliott             fprintf(stderr, "The client has unexpectedly closed the connection.\n");
160b60a49ddSsethdelliott             i_errno = IECTRLCLOSE;
161b68ac00eSsethdelliott             test->state = IPERF_DONE;
162b60a49ddSsethdelliott             return (0);
163b68ac00eSsethdelliott         } else {
164b60a49ddSsethdelliott             i_errno = IERECVMESSAGE;
165b60a49ddSsethdelliott             return (-1);
166982c704aSsethdelliott         }
167b68ac00eSsethdelliott     }
168982c704aSsethdelliott 
169982c704aSsethdelliott     switch(test->state) {
170ba2672a2Ssethdelliott         case TEST_START:
171ba2672a2Ssethdelliott             break;
172465b565cSsethdelliott         case TEST_END:
173f1b3bd81Ssethdelliott             test->stats_callback(test);
174efdc02f7Ssethdelliott             for (sp = test->streams; sp; sp = sp->next) {
175efdc02f7Ssethdelliott                 FD_CLR(sp->socket, &test->read_set);
176efdc02f7Ssethdelliott                 FD_CLR(sp->socket, &test->write_set);
177efdc02f7Ssethdelliott                 close(sp->socket);
178efdc02f7Ssethdelliott             }
179181d61afSsethdelliott             test->state = EXCHANGE_RESULTS;
180efb7bbe4Ssethdelliott             if (Nwrite(test->ctrl_sck, &test->state, sizeof(char), Ptcp) < 0) {
181b60a49ddSsethdelliott                 i_errno = IESENDMESSAGE;
182b60a49ddSsethdelliott                 return (-1);
183181d61afSsethdelliott             }
184b60a49ddSsethdelliott             if (iperf_exchange_results(test) < 0)
185b60a49ddSsethdelliott                 return (-1);
186873952e3Ssethdelliott             test->state = DISPLAY_RESULTS;
187efb7bbe4Ssethdelliott             if (Nwrite(test->ctrl_sck, &test->state, sizeof(char), Ptcp) < 0) {
188b60a49ddSsethdelliott                 i_errno = IESENDMESSAGE;
189b60a49ddSsethdelliott                 return (-1);
190465b565cSsethdelliott             }
1914df383f6Ssethdelliott             if (test->on_test_finish)
1924df383f6Ssethdelliott                 test->on_test_finish(test);
193181d61afSsethdelliott             test->reporter_callback(test);
194465b565cSsethdelliott             break;
195873952e3Ssethdelliott         case IPERF_DONE:
196873952e3Ssethdelliott             break;
197465b565cSsethdelliott         case CLIENT_TERMINATE:
1981ac522ecSsethdelliott             fprintf(stderr, "The client has terminated.\n");
1991ac522ecSsethdelliott             for (sp = test->streams; sp; sp = sp->next) {
2001ac522ecSsethdelliott                 FD_CLR(sp->socket, &test->read_set);
2011ac522ecSsethdelliott                 FD_CLR(sp->socket, &test->write_set);
2021ac522ecSsethdelliott                 close(sp->socket);
2031ac522ecSsethdelliott             }
2041ac522ecSsethdelliott             test->state = IPERF_DONE;
2051ac522ecSsethdelliott             break;
206ba2672a2Ssethdelliott         default:
207b60a49ddSsethdelliott             i_errno = IEMESSAGE;
208b60a49ddSsethdelliott             return (-1);
209982c704aSsethdelliott     }
210982c704aSsethdelliott 
211b60a49ddSsethdelliott     return (0);
212982c704aSsethdelliott }
213982c704aSsethdelliott 
214efdc02f7Ssethdelliott void
215efdc02f7Ssethdelliott iperf_test_reset(struct iperf_test *test)
216efdc02f7Ssethdelliott {
217efdc02f7Ssethdelliott     struct iperf_stream *sp, *np;
218efdc02f7Ssethdelliott 
219efdc02f7Ssethdelliott     close(test->ctrl_sck);
220efdc02f7Ssethdelliott 
221efdc02f7Ssethdelliott     /* Free streams */
222efdc02f7Ssethdelliott     for (sp = test->streams; sp; sp = np) {
223efdc02f7Ssethdelliott         np = sp->next;
224efdc02f7Ssethdelliott         iperf_free_stream(sp);
225efdc02f7Ssethdelliott     }
226f1b3bd81Ssethdelliott     free_timer(test->timer);
227f1b3bd81Ssethdelliott     free_timer(test->stats_timer);
228f1b3bd81Ssethdelliott     free_timer(test->reporter_timer);
229f1b3bd81Ssethdelliott     test->timer = NULL;
230f1b3bd81Ssethdelliott     test->stats_timer = NULL;
231f1b3bd81Ssethdelliott     test->reporter_timer = NULL;
232efdc02f7Ssethdelliott 
23332885561Ssethdelliott     test->streams = NULL;
23432885561Ssethdelliott 
235efdc02f7Ssethdelliott     test->role = 's';
23666ce7ad4Ssethdelliott     set_protocol(test, Ptcp);
23732885561Ssethdelliott     test->duration = DURATION;
23832885561Ssethdelliott     test->state = 0;
23932885561Ssethdelliott     test->server_hostname = NULL;
24032885561Ssethdelliott 
24132885561Ssethdelliott     test->ctrl_sck = -1;
24266ce7ad4Ssethdelliott     test->prot_listener = -1;
24332885561Ssethdelliott 
244f1b3bd81Ssethdelliott     test->bytes_sent = 0;
245f1b3bd81Ssethdelliott 
24632885561Ssethdelliott     test->reverse = 0;
247b68ac00eSsethdelliott     test->no_delay = 0;
24832885561Ssethdelliott 
249efdc02f7Ssethdelliott     FD_ZERO(&test->read_set);
250efdc02f7Ssethdelliott     FD_ZERO(&test->write_set);
25166ce7ad4Ssethdelliott     FD_SET(test->listener, &test->read_set);
25266ce7ad4Ssethdelliott     test->max_fd = test->listener;
25332885561Ssethdelliott 
25432885561Ssethdelliott     test->num_streams = 1;
255112a907cSsethdelliott     test->settings->socket_bufsize = 0;
256112a907cSsethdelliott     test->settings->blksize = DEFAULT_TCP_BLKSIZE;
257112a907cSsethdelliott     test->settings->rate = RATE;   /* UDP only */
258112a907cSsethdelliott     test->settings->mss = 0;
259112a907cSsethdelliott     memset(test->cookie, 0, COOKIE_SIZE);
260efdc02f7Ssethdelliott }
261efdc02f7Ssethdelliott 
2627fa9f68fSsethdelliott int
263a951c980SBrian Tierney iperf_run_server(struct iperf_test *test)
264a951c980SBrian Tierney {
265112a907cSsethdelliott     int result, s, streams_accepted;
266efdc02f7Ssethdelliott     fd_set temp_read_set, temp_write_set;
2678a0b5a5dSsethdelliott     struct iperf_stream *sp;
2687fa9f68fSsethdelliott     struct timeval tv;
269a951c980SBrian Tierney 
270982c704aSsethdelliott     // Open socket and listen
271982c704aSsethdelliott     if (iperf_server_listen(test) < 0) {
272b60a49ddSsethdelliott         return (-1);
273e99faeaeSBrian Tierney     }
274a951c980SBrian Tierney 
275465b565cSsethdelliott     signal(SIGINT, sig_handler);
276465b565cSsethdelliott     if (setjmp(env)) {
277465b565cSsethdelliott         test->state = SERVER_TERMINATE;
278465b565cSsethdelliott         if (test->ctrl_sck >= 0) {
279efb7bbe4Ssethdelliott             if (Nwrite(test->ctrl_sck, &test->state, sizeof(char), Ptcp) < 0) {
280b60a49ddSsethdelliott                 i_errno = IESENDMESSAGE;
281b60a49ddSsethdelliott                 return (-1);
282465b565cSsethdelliott             }
283465b565cSsethdelliott         }
28466ce7ad4Ssethdelliott         return (0);
285465b565cSsethdelliott     }
286465b565cSsethdelliott 
287efdc02f7Ssethdelliott     for ( ; ; ) {
288efdc02f7Ssethdelliott 
289efdc02f7Ssethdelliott         test->state = IPERF_START;
290112a907cSsethdelliott         streams_accepted = 0;
291a951c980SBrian Tierney 
292873952e3Ssethdelliott         while (test->state != IPERF_DONE) {
293982c704aSsethdelliott 
294efdc02f7Ssethdelliott             memcpy(&temp_read_set, &test->read_set, sizeof(fd_set));
295efdc02f7Ssethdelliott             memcpy(&temp_write_set, &test->write_set, sizeof(fd_set));
296982c704aSsethdelliott             tv.tv_sec = 15;
297982c704aSsethdelliott             tv.tv_usec = 0;
298982c704aSsethdelliott 
299efdc02f7Ssethdelliott             result = select(test->max_fd + 1, &temp_read_set, &temp_write_set, NULL, &tv);
300982c704aSsethdelliott             if (result < 0 && errno != EINTR) {
301b60a49ddSsethdelliott                 i_errno = IESELECT;
302b60a49ddSsethdelliott                 return (-1);
303982c704aSsethdelliott             } else if (result > 0) {
30466ce7ad4Ssethdelliott                 if (FD_ISSET(test->listener, &temp_read_set)) {
305b68ac00eSsethdelliott                     if (test->state != CREATE_STREAMS) {
306465b565cSsethdelliott                         if (iperf_accept(test) < 0) {
307b60a49ddSsethdelliott                             return (-1);
308982c704aSsethdelliott                         }
30966ce7ad4Ssethdelliott                         FD_CLR(test->listener, &temp_read_set);
310982c704aSsethdelliott                     }
311b68ac00eSsethdelliott                 }
312efdc02f7Ssethdelliott                 if (FD_ISSET(test->ctrl_sck, &temp_read_set)) {
313b60a49ddSsethdelliott                     if (iperf_handle_message_server(test) < 0)
314b60a49ddSsethdelliott                         return (-1);
315efdc02f7Ssethdelliott                     FD_CLR(test->ctrl_sck, &temp_read_set);
316982c704aSsethdelliott                 }
317465b565cSsethdelliott 
318465b565cSsethdelliott                 if (test->state == CREATE_STREAMS) {
3198a0b5a5dSsethdelliott                     if (FD_ISSET(test->prot_listener, &temp_read_set)) {
3208a0b5a5dSsethdelliott 
32166ce7ad4Ssethdelliott                         if ((s = test->protocol->accept(test)) < 0)
322b60a49ddSsethdelliott                             return (-1);
3238a0b5a5dSsethdelliott 
3248a0b5a5dSsethdelliott                         if (!is_closed(s)) {
32566ce7ad4Ssethdelliott                             sp = iperf_new_stream(test, s);
3268a0b5a5dSsethdelliott                             if (!sp)
327b60a49ddSsethdelliott                                 return (-1);
32866ce7ad4Ssethdelliott 
3298a0b5a5dSsethdelliott                             FD_SET(s, &test->read_set);
3308a0b5a5dSsethdelliott                             FD_SET(s, &test->write_set);
3318a0b5a5dSsethdelliott                             test->max_fd = (s > test->max_fd) ? s : test->max_fd;
33266ce7ad4Ssethdelliott 
333112a907cSsethdelliott                             streams_accepted++;
3344df383f6Ssethdelliott                             if (test->on_new_stream)
3354df383f6Ssethdelliott                                 test->on_new_stream(sp);
336b68ac00eSsethdelliott                         }
3378a0b5a5dSsethdelliott                         FD_CLR(test->prot_listener, &temp_read_set);
338b68ac00eSsethdelliott                     }
3398a0b5a5dSsethdelliott 
340112a907cSsethdelliott                     if (streams_accepted == test->num_streams) {
34166ce7ad4Ssethdelliott                         if (test->protocol->id != Ptcp) {
3428a0b5a5dSsethdelliott                             FD_CLR(test->prot_listener, &test->read_set);
3438a0b5a5dSsethdelliott                             close(test->prot_listener);
34466ce7ad4Ssethdelliott                         } else {
345112a907cSsethdelliott                             if (test->no_delay || test->settings->mss) {
34666ce7ad4Ssethdelliott                                 FD_CLR(test->listener, &test->read_set);
34766ce7ad4Ssethdelliott                                 close(test->listener);
348*f99bd3b2Ssethdelliott                                 if ((s = netannounce(test->settings->domain, Ptcp, test->bind_address, test->server_port)) < 0) {
349b60a49ddSsethdelliott                                     i_errno = IELISTEN;
350c81883bfSsethdelliott                                     return (-1);
351c81883bfSsethdelliott                                 }
35266ce7ad4Ssethdelliott                                 test->listener = s;
353c81883bfSsethdelliott                                 test->max_fd = (s > test->max_fd ? s : test->max_fd);
35466ce7ad4Ssethdelliott                                 FD_SET(test->listener, &test->read_set);
355c81883bfSsethdelliott                             }
3568556db5dSsethdelliott                         }
3578a0b5a5dSsethdelliott                         test->prot_listener = -1;
358465b565cSsethdelliott                         test->state = TEST_START;
359b68ac00eSsethdelliott                         if (Nwrite(test->ctrl_sck, &test->state, sizeof(char), Ptcp) < 0) {
360b60a49ddSsethdelliott                             i_errno = IESENDMESSAGE;
361b60a49ddSsethdelliott                             return (-1);
362982c704aSsethdelliott                         }
363b60a49ddSsethdelliott                         if (iperf_init_test(test) < 0)
364b60a49ddSsethdelliott                             return (-1);
365f1b3bd81Ssethdelliott                         test->state = TEST_RUNNING;
366f1b3bd81Ssethdelliott                         if (Nwrite(test->ctrl_sck, &test->state, sizeof(char), Ptcp) < 0) {
367b60a49ddSsethdelliott                             i_errno = IESENDMESSAGE;
368b60a49ddSsethdelliott                             return (-1);
369f1b3bd81Ssethdelliott                         }
370982c704aSsethdelliott                     }
371982c704aSsethdelliott                 }
372982c704aSsethdelliott 
373f1b3bd81Ssethdelliott                 if (test->state == TEST_RUNNING) {
374465b565cSsethdelliott                     if (test->reverse) {
375465b565cSsethdelliott                         // Reverse mode. Server sends.
376b60a49ddSsethdelliott                         if (iperf_send(test) < 0)
377b60a49ddSsethdelliott                             return (-1);
378982c704aSsethdelliott                     } else {
379465b565cSsethdelliott                         // Regular mode. Server receives.
380b60a49ddSsethdelliott                         if (iperf_recv(test) < 0)
381b60a49ddSsethdelliott                             return (-1);
382e99faeaeSBrian Tierney                     }
383f1b3bd81Ssethdelliott 
384f1b3bd81Ssethdelliott                     /* Perform callbacks */
385f1b3bd81Ssethdelliott                     if (timer_expired(test->stats_timer)) {
386f1b3bd81Ssethdelliott                         test->stats_callback(test);
387b60a49ddSsethdelliott                         if (update_timer(test->stats_timer, test->stats_interval, 0) < 0)
388b60a49ddSsethdelliott                             return (-1);
389f1b3bd81Ssethdelliott                     }
390f1b3bd81Ssethdelliott                     if (timer_expired(test->reporter_timer)) {
391f1b3bd81Ssethdelliott                         test->reporter_callback(test);
392b60a49ddSsethdelliott                         if (update_timer(test->reporter_timer, test->reporter_interval, 0) < 0)
393b60a49ddSsethdelliott                             return (-1);
394f1b3bd81Ssethdelliott                     }
395f1b3bd81Ssethdelliott                 }
396a951c980SBrian Tierney             }
397982c704aSsethdelliott         }
398e99faeaeSBrian Tierney 
399efdc02f7Ssethdelliott         /* Clean up the last test */
400efdc02f7Ssethdelliott         iperf_test_reset(test);
401efdc02f7Ssethdelliott         printf("\n");
402efdc02f7Ssethdelliott 
403efdc02f7Ssethdelliott     }
404efdc02f7Ssethdelliott 
405b60a49ddSsethdelliott     return (0);
406a3281a3dSBrian Tierney }
407a3281a3dSBrian Tierney 
408