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