xref: /iperf/src/iperf_client_api.c (revision 4874c4a8)
1 /*
2  * iperf, Copyright (c) 2014, The Regents of the University of
3  * California, through Lawrence Berkeley National Laboratory (subject
4  * to receipt of any required approvals from the U.S. Dept. of
5  * Energy).  All rights reserved.
6  *
7  * If you have questions about your rights to use or distribute this
8  * software, please contact Berkeley Lab's Technology Transfer
9  * Department at [email protected].
10  *
11  * NOTICE.  This software is owned by the U.S. Department of Energy.
12  * As such, the U.S. Government has been granted for itself and others
13  * acting on its behalf a paid-up, nonexclusive, irrevocable,
14  * worldwide license in the Software to reproduce, prepare derivative
15  * works, and perform publicly and display publicly.  Beginning five
16  * (5) years after the date permission to assert copyright is obtained
17  * from the U.S. Department of Energy, and subject to any subsequent
18  * five (5) year renewals, the U.S. Government is granted for itself
19  * and others acting on its behalf a paid-up, nonexclusive,
20  * irrevocable, worldwide license in the Software to reproduce,
21  * prepare derivative works, distribute copies to the public, perform
22  * publicly and display publicly, and to permit others to do so.
23  *
24  * This code is distributed under a BSD style license, see the LICENSE
25  * file for complete information.
26  */
27 #include <errno.h>
28 #include <setjmp.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <unistd.h>
33 #include <signal.h>
34 #include <sys/types.h>
35 #include <sys/select.h>
36 #include <sys/uio.h>
37 #include <arpa/inet.h>
38 
39 #include "iperf.h"
40 #include "iperf_api.h"
41 #include "iperf_util.h"
42 #include "iperf_locale.h"
43 #include "net.h"
44 #include "timer.h"
45 
46 
47 int
48 iperf_create_streams(struct iperf_test *test)
49 {
50     int i, s;
51     struct iperf_stream *sp;
52 
53     int orig_bind_port = test->bind_port;
54     for (i = 0; i < test->num_streams; ++i) {
55 
56         test->bind_port = orig_bind_port + i;
57         if ((s = test->protocol->connect(test)) < 0)
58             return -1;
59 
60 	if (test->sender)
61 	    FD_SET(s, &test->write_set);
62 	else
63 	    FD_SET(s, &test->read_set);
64 	if (s > test->max_fd) test->max_fd = s;
65 
66         sp = iperf_new_stream(test, s);
67         if (!sp)
68             return -1;
69 
70         /* Perform the new stream callback */
71         if (test->on_new_stream)
72             test->on_new_stream(sp);
73     }
74 
75     return 0;
76 }
77 
78 static void
79 test_timer_proc(TimerClientData client_data, struct timeval *nowP)
80 {
81     struct iperf_test *test = client_data.p;
82 
83     test->timer = NULL;
84     test->done = 1;
85 }
86 
87 static void
88 client_stats_timer_proc(TimerClientData client_data, struct timeval *nowP)
89 {
90     struct iperf_test *test = client_data.p;
91 
92     if (test->done)
93         return;
94     if (test->stats_callback)
95 	test->stats_callback(test);
96 }
97 
98 static void
99 client_reporter_timer_proc(TimerClientData client_data, struct timeval *nowP)
100 {
101     struct iperf_test *test = client_data.p;
102 
103     if (test->done)
104         return;
105     if (test->reporter_callback)
106 	test->reporter_callback(test);
107 }
108 
109 static int
110 create_client_timers(struct iperf_test * test)
111 {
112     struct timeval now;
113     TimerClientData cd;
114 
115     if (gettimeofday(&now, NULL) < 0) {
116 	i_errno = IEINITTEST;
117 	return -1;
118     }
119     cd.p = test;
120     test->timer = test->stats_timer = test->reporter_timer = NULL;
121     if (test->duration != 0) {
122 	test->done = 0;
123         test->timer = tmr_create(&now, test_timer_proc, cd, ( test->duration + test->omit ) * SEC_TO_US, 0);
124         if (test->timer == NULL) {
125             i_errno = IEINITTEST;
126             return -1;
127 	}
128     }
129     if (test->stats_interval != 0) {
130         test->stats_timer = tmr_create(&now, client_stats_timer_proc, cd, test->stats_interval * SEC_TO_US, 1);
131         if (test->stats_timer == NULL) {
132             i_errno = IEINITTEST;
133             return -1;
134 	}
135     }
136     if (test->reporter_interval != 0) {
137         test->reporter_timer = tmr_create(&now, client_reporter_timer_proc, cd, test->reporter_interval * SEC_TO_US, 1);
138         if (test->reporter_timer == NULL) {
139             i_errno = IEINITTEST;
140             return -1;
141 	}
142     }
143     return 0;
144 }
145 
146 static void
147 client_omit_timer_proc(TimerClientData client_data, struct timeval *nowP)
148 {
149     struct iperf_test *test = client_data.p;
150 
151     test->omit_timer = NULL;
152     test->omitting = 0;
153     iperf_reset_stats(test);
154     if (test->verbose && !test->json_output && test->reporter_interval == 0)
155         iprintf(test, "%s", report_omit_done);
156 
157     /* Reset the timers. */
158     if (test->stats_timer != NULL)
159         tmr_reset(nowP, test->stats_timer);
160     if (test->reporter_timer != NULL)
161         tmr_reset(nowP, test->reporter_timer);
162 }
163 
164 static int
165 create_client_omit_timer(struct iperf_test * test)
166 {
167     struct timeval now;
168     TimerClientData cd;
169 
170     if (test->omit == 0) {
171 	test->omit_timer = NULL;
172         test->omitting = 0;
173     } else {
174 	if (gettimeofday(&now, NULL) < 0) {
175 	    i_errno = IEINITTEST;
176 	    return -1;
177 	}
178 	test->omitting = 1;
179 	cd.p = test;
180 	test->omit_timer = tmr_create(&now, client_omit_timer_proc, cd, test->omit * SEC_TO_US, 0);
181 	if (test->omit_timer == NULL) {
182 	    i_errno = IEINITTEST;
183 	    return -1;
184 	}
185     }
186     return 0;
187 }
188 
189 int
190 iperf_handle_message_client(struct iperf_test *test)
191 {
192     int rval;
193     int32_t err;
194 
195     /*!!! Why is this read() and not Nread()? */
196     if ((rval = read(test->ctrl_sck, (char*) &test->state, sizeof(signed char))) <= 0) {
197         if (rval == 0) {
198             i_errno = IECTRLCLOSE;
199             return -1;
200         } else {
201             i_errno = IERECVMESSAGE;
202             return -1;
203         }
204     }
205 
206     switch (test->state) {
207         case PARAM_EXCHANGE:
208             if (iperf_exchange_parameters(test) < 0)
209                 return -1;
210             if (test->on_connect)
211                 test->on_connect(test);
212             break;
213         case CREATE_STREAMS:
214             if (iperf_create_streams(test) < 0)
215                 return -1;
216             break;
217         case TEST_START:
218             if (iperf_init_test(test) < 0)
219                 return -1;
220             if (create_client_timers(test) < 0)
221                 return -1;
222             if (create_client_omit_timer(test) < 0)
223                 return -1;
224 	    if (!test->reverse)
225 		if (iperf_create_send_timers(test) < 0)
226 		    return -1;
227             break;
228         case TEST_RUNNING:
229             break;
230         case EXCHANGE_RESULTS:
231             if (iperf_exchange_results(test) < 0)
232                 return -1;
233             break;
234         case DISPLAY_RESULTS:
235             if (test->on_test_finish)
236                 test->on_test_finish(test);
237             iperf_client_end(test);
238             break;
239         case IPERF_DONE:
240             break;
241         case SERVER_TERMINATE:
242             i_errno = IESERVERTERM;
243 
244 	    /*
245 	     * Temporarily be in DISPLAY_RESULTS phase so we can get
246 	     * ending summary statistics.
247 	     */
248 	    signed char oldstate = test->state;
249 	    cpu_util(test->cpu_util);
250 	    test->state = DISPLAY_RESULTS;
251 	    test->reporter_callback(test);
252 	    test->state = oldstate;
253             return -1;
254         case ACCESS_DENIED:
255             i_errno = IEACCESSDENIED;
256             return -1;
257         case SERVER_ERROR:
258             if (Nread(test->ctrl_sck, (char*) &err, sizeof(err), Ptcp) < 0) {
259                 i_errno = IECTRLREAD;
260                 return -1;
261             }
262 	    i_errno = ntohl(err);
263             if (Nread(test->ctrl_sck, (char*) &err, sizeof(err), Ptcp) < 0) {
264                 i_errno = IECTRLREAD;
265                 return -1;
266             }
267             errno = ntohl(err);
268             return -1;
269         default:
270             i_errno = IEMESSAGE;
271             return -1;
272     }
273 
274     return 0;
275 }
276 
277 
278 
279 /* iperf_connect -- client to server connection function */
280 int
281 iperf_connect(struct iperf_test *test)
282 {
283     FD_ZERO(&test->read_set);
284     FD_ZERO(&test->write_set);
285 
286     make_cookie(test->cookie);
287 
288     /* Create and connect the control channel */
289     if (test->ctrl_sck < 0)
290 	// Create the control channel using an ephemeral port
291 	test->ctrl_sck = netdial(test->settings->domain, Ptcp, test->bind_address, 0, test->server_hostname, test->server_port);
292     if (test->ctrl_sck < 0) {
293         i_errno = IECONNECT;
294         return -1;
295     }
296 
297     if (Nwrite(test->ctrl_sck, test->cookie, COOKIE_SIZE, Ptcp) < 0) {
298         i_errno = IESENDCOOKIE;
299         return -1;
300     }
301 
302     FD_SET(test->ctrl_sck, &test->read_set);
303     if (test->ctrl_sck > test->max_fd) test->max_fd = test->ctrl_sck;
304 
305     return 0;
306 }
307 
308 
309 int
310 iperf_client_end(struct iperf_test *test)
311 {
312     struct iperf_stream *sp;
313 
314     /* Close all stream sockets */
315     SLIST_FOREACH(sp, &test->streams, streams) {
316         close(sp->socket);
317     }
318 
319     /* show final summary */
320     test->reporter_callback(test);
321 
322     if (iperf_set_send_state(test, IPERF_DONE) != 0)
323         return -1;
324 
325     return 0;
326 }
327 
328 
329 static jmp_buf sigend_jmp_buf;
330 
331 static void
332 sigend_handler(int sig)
333 {
334     longjmp(sigend_jmp_buf, 1);
335 }
336 
337 
338 int
339 iperf_run_client(struct iperf_test * test)
340 {
341     int startup;
342     int result = 0;
343     fd_set read_set, write_set;
344     struct timeval now;
345     struct timeval* timeout = NULL;
346     struct iperf_stream *sp;
347 
348     /* Termination signals. */
349     iperf_catch_sigend(sigend_handler);
350     if (setjmp(sigend_jmp_buf))
351 	iperf_got_sigend(test);
352 
353     if (test->affinity != -1)
354 	if (iperf_setaffinity(test, test->affinity) != 0)
355 	    return -1;
356 
357     if (test->json_output)
358 	if (iperf_json_start(test) < 0)
359 	    return -1;
360 
361     if (test->json_output) {
362 	cJSON_AddItemToObject(test->json_start, "version", cJSON_CreateString(version));
363 	cJSON_AddItemToObject(test->json_start, "system_info", cJSON_CreateString(get_system_info()));
364     } else if (test->verbose) {
365 	iprintf(test, "%s\n", version);
366 	iprintf(test, "%s", "");
367 	iprintf(test, "%s\n", get_system_info());
368 	iflush(test);
369     }
370 
371     /* Start the client and connect to the server */
372     if (iperf_connect(test) < 0)
373         return -1;
374 
375     /* Begin calculating CPU utilization */
376     cpu_util(NULL);
377 
378     startup = 1;
379     while (test->state != IPERF_DONE) {
380 	memcpy(&read_set, &test->read_set, sizeof(fd_set));
381 	memcpy(&write_set, &test->write_set, sizeof(fd_set));
382 	(void) gettimeofday(&now, NULL);
383 	timeout = tmr_timeout(&now);
384 	result = select(test->max_fd + 1, &read_set, &write_set, NULL, timeout);
385 	if (result < 0 && errno != EINTR) {
386   	    i_errno = IESELECT;
387 	    return -1;
388 	}
389 	if (result > 0) {
390 	    if (FD_ISSET(test->ctrl_sck, &read_set)) {
391  	        if (iperf_handle_message_client(test) < 0) {
392 		    return -1;
393 		}
394 		FD_CLR(test->ctrl_sck, &read_set);
395 	    }
396 	}
397 
398 	if (test->state == TEST_RUNNING) {
399 
400 	    /* Is this our first time really running? */
401 	    if (startup) {
402 	        startup = 0;
403 
404 		// Set non-blocking for non-UDP tests
405 		if (test->protocol->id != Pudp) {
406 		    SLIST_FOREACH(sp, &test->streams, streams) {
407 			setnonblocking(sp->socket, 1);
408 		    }
409 		}
410 	    }
411 
412 	    if (test->reverse) {
413 		// Reverse mode. Client receives.
414 		if (iperf_recv(test, &read_set) < 0)
415 		    return -1;
416 	    } else {
417 		// Regular mode. Client sends.
418 		if (iperf_send(test, &write_set) < 0)
419 		    return -1;
420 	    }
421 
422             /* Run the timers. */
423             (void) gettimeofday(&now, NULL);
424             tmr_run(&now);
425 
426 	    /* Is the test done yet? */
427 	    if ((!test->omitting) &&
428 	        ((test->duration != 0 && test->done) ||
429 	         (test->settings->bytes != 0 && test->bytes_sent >= test->settings->bytes) ||
430 	         (test->settings->blocks != 0 && test->blocks_sent >= test->settings->blocks))) {
431 
432 		// Unset non-blocking for non-UDP tests
433 		if (test->protocol->id != Pudp) {
434 		    SLIST_FOREACH(sp, &test->streams, streams) {
435 			setnonblocking(sp->socket, 0);
436 		    }
437 		}
438 
439 		/* Yes, done!  Send TEST_END. */
440 		test->done = 1;
441 		cpu_util(test->cpu_util);
442 		test->stats_callback(test);
443 		if (iperf_set_send_state(test, TEST_END) != 0)
444 		    return -1;
445 	    }
446 	}
447 	// If we're in reverse mode, continue draining the data
448 	// connection(s) even if test is over.  This prevents a
449 	// deadlock where the server side fills up its pipe(s)
450 	// and gets blocked, so it can't receive state changes
451 	// from the client side.
452 	else if (test->reverse && test->state == TEST_END) {
453 	    if (iperf_recv(test, &read_set) < 0)
454 		return -1;
455 	}
456     }
457 
458     if (test->json_output) {
459 	if (iperf_json_finish(test) < 0)
460 	    return -1;
461     } else {
462 	iprintf(test, "\n");
463 	iprintf(test, "%s", report_done);
464     }
465 
466     iflush(test);
467 
468     return 0;
469 }
470