Annotation of embedaddon/iperf/src/iperf_client_api.c, revision 1.1.1.1
1.1 misho 1: /*
2: * iperf, Copyright (c) 2014, 2015, 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 TTD@lbl.gov.
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;
57: if (orig_bind_port)
58: test->bind_port += i;
59: if ((s = test->protocol->connect(test)) < 0)
60: return -1;
61:
62: if (test->sender)
63: FD_SET(s, &test->write_set);
64: else
65: FD_SET(s, &test->read_set);
66: if (s > test->max_fd) test->max_fd = s;
67:
68: sp = iperf_new_stream(test, s);
69: if (!sp)
70: return -1;
71:
72: /* Perform the new stream callback */
73: if (test->on_new_stream)
74: test->on_new_stream(sp);
75: }
76:
77: return 0;
78: }
79:
80: static void
81: test_timer_proc(TimerClientData client_data, struct timeval *nowP)
82: {
83: struct iperf_test *test = client_data.p;
84:
85: test->timer = NULL;
86: test->done = 1;
87: }
88:
89: static void
90: client_stats_timer_proc(TimerClientData client_data, struct timeval *nowP)
91: {
92: struct iperf_test *test = client_data.p;
93:
94: if (test->done)
95: return;
96: if (test->stats_callback)
97: test->stats_callback(test);
98: }
99:
100: static void
101: client_reporter_timer_proc(TimerClientData client_data, struct timeval *nowP)
102: {
103: struct iperf_test *test = client_data.p;
104:
105: if (test->done)
106: return;
107: if (test->reporter_callback)
108: test->reporter_callback(test);
109: }
110:
111: static int
112: create_client_timers(struct iperf_test * test)
113: {
114: struct timeval now;
115: TimerClientData cd;
116:
117: if (gettimeofday(&now, NULL) < 0) {
118: i_errno = IEINITTEST;
119: return -1;
120: }
121: cd.p = test;
122: test->timer = test->stats_timer = test->reporter_timer = NULL;
123: if (test->duration != 0) {
124: test->done = 0;
125: test->timer = tmr_create(&now, test_timer_proc, cd, ( test->duration + test->omit ) * SEC_TO_US, 0);
126: if (test->timer == NULL) {
127: i_errno = IEINITTEST;
128: return -1;
129: }
130: }
131: if (test->stats_interval != 0) {
132: test->stats_timer = tmr_create(&now, client_stats_timer_proc, cd, test->stats_interval * SEC_TO_US, 1);
133: if (test->stats_timer == NULL) {
134: i_errno = IEINITTEST;
135: return -1;
136: }
137: }
138: if (test->reporter_interval != 0) {
139: test->reporter_timer = tmr_create(&now, client_reporter_timer_proc, cd, test->reporter_interval * SEC_TO_US, 1);
140: if (test->reporter_timer == NULL) {
141: i_errno = IEINITTEST;
142: return -1;
143: }
144: }
145: return 0;
146: }
147:
148: static void
149: client_omit_timer_proc(TimerClientData client_data, struct timeval *nowP)
150: {
151: struct iperf_test *test = client_data.p;
152:
153: test->omit_timer = NULL;
154: test->omitting = 0;
155: iperf_reset_stats(test);
156: if (test->verbose && !test->json_output && test->reporter_interval == 0)
157: iprintf(test, "%s", report_omit_done);
158:
159: /* Reset the timers. */
160: if (test->stats_timer != NULL)
161: tmr_reset(nowP, test->stats_timer);
162: if (test->reporter_timer != NULL)
163: tmr_reset(nowP, test->reporter_timer);
164: }
165:
166: static int
167: create_client_omit_timer(struct iperf_test * test)
168: {
169: struct timeval now;
170: TimerClientData cd;
171:
172: if (test->omit == 0) {
173: test->omit_timer = NULL;
174: test->omitting = 0;
175: } else {
176: if (gettimeofday(&now, NULL) < 0) {
177: i_errno = IEINITTEST;
178: return -1;
179: }
180: test->omitting = 1;
181: cd.p = test;
182: test->omit_timer = tmr_create(&now, client_omit_timer_proc, cd, test->omit * SEC_TO_US, 0);
183: if (test->omit_timer == NULL) {
184: i_errno = IEINITTEST;
185: return -1;
186: }
187: }
188: return 0;
189: }
190:
191: int
192: iperf_handle_message_client(struct iperf_test *test)
193: {
194: int rval;
195: int32_t err;
196:
197: /*!!! Why is this read() and not Nread()? */
198: if ((rval = read(test->ctrl_sck, (char*) &test->state, sizeof(signed char))) <= 0) {
199: if (rval == 0) {
200: i_errno = IECTRLCLOSE;
201: return -1;
202: } else {
203: i_errno = IERECVMESSAGE;
204: return -1;
205: }
206: }
207:
208: switch (test->state) {
209: case PARAM_EXCHANGE:
210: if (iperf_exchange_parameters(test) < 0)
211: return -1;
212: if (test->on_connect)
213: test->on_connect(test);
214: break;
215: case CREATE_STREAMS:
216: if (iperf_create_streams(test) < 0)
217: return -1;
218: break;
219: case TEST_START:
220: if (iperf_init_test(test) < 0)
221: return -1;
222: if (create_client_timers(test) < 0)
223: return -1;
224: if (create_client_omit_timer(test) < 0)
225: return -1;
226: if (!test->reverse)
227: if (iperf_create_send_timers(test) < 0)
228: return -1;
229: break;
230: case TEST_RUNNING:
231: break;
232: case EXCHANGE_RESULTS:
233: if (iperf_exchange_results(test) < 0)
234: return -1;
235: break;
236: case DISPLAY_RESULTS:
237: if (test->on_test_finish)
238: test->on_test_finish(test);
239: iperf_client_end(test);
240: break;
241: case IPERF_DONE:
242: break;
243: case SERVER_TERMINATE:
244: i_errno = IESERVERTERM;
245:
246: /*
247: * Temporarily be in DISPLAY_RESULTS phase so we can get
248: * ending summary statistics.
249: */
250: signed char oldstate = test->state;
251: cpu_util(test->cpu_util);
252: test->state = DISPLAY_RESULTS;
253: test->reporter_callback(test);
254: test->state = oldstate;
255: return -1;
256: case ACCESS_DENIED:
257: i_errno = IEACCESSDENIED;
258: return -1;
259: case SERVER_ERROR:
260: if (Nread(test->ctrl_sck, (char*) &err, sizeof(err), Ptcp) < 0) {
261: i_errno = IECTRLREAD;
262: return -1;
263: }
264: i_errno = ntohl(err);
265: if (Nread(test->ctrl_sck, (char*) &err, sizeof(err), Ptcp) < 0) {
266: i_errno = IECTRLREAD;
267: return -1;
268: }
269: errno = ntohl(err);
270: return -1;
271: default:
272: i_errno = IEMESSAGE;
273: return -1;
274: }
275:
276: return 0;
277: }
278:
279:
280:
281: /* iperf_connect -- client to server connection function */
282: int
283: iperf_connect(struct iperf_test *test)
284: {
285: FD_ZERO(&test->read_set);
286: FD_ZERO(&test->write_set);
287:
288: make_cookie(test->cookie);
289:
290: /* Create and connect the control channel */
291: if (test->ctrl_sck < 0)
292: // Create the control channel using an ephemeral port
293: test->ctrl_sck = netdial(test->settings->domain, Ptcp, test->bind_address, 0, test->server_hostname, test->server_port);
294: if (test->ctrl_sck < 0) {
295: i_errno = IECONNECT;
296: return -1;
297: }
298:
299: if (Nwrite(test->ctrl_sck, test->cookie, COOKIE_SIZE, Ptcp) < 0) {
300: i_errno = IESENDCOOKIE;
301: return -1;
302: }
303:
304: FD_SET(test->ctrl_sck, &test->read_set);
305: if (test->ctrl_sck > test->max_fd) test->max_fd = test->ctrl_sck;
306:
307: return 0;
308: }
309:
310:
311: int
312: iperf_client_end(struct iperf_test *test)
313: {
314: struct iperf_stream *sp;
315:
316: /* Close all stream sockets */
317: SLIST_FOREACH(sp, &test->streams, streams) {
318: close(sp->socket);
319: }
320:
321: /* show final summary */
322: test->reporter_callback(test);
323:
324: if (iperf_set_send_state(test, IPERF_DONE) != 0)
325: return -1;
326:
327: return 0;
328: }
329:
330:
331: int
332: iperf_run_client(struct iperf_test * test)
333: {
334: int startup;
335: int result = 0;
336: fd_set read_set, write_set;
337: struct timeval now;
338: struct timeval* timeout = NULL;
339: struct iperf_stream *sp;
340:
341: if (test->affinity != -1)
342: if (iperf_setaffinity(test, test->affinity) != 0)
343: return -1;
344:
345: if (test->json_output)
346: if (iperf_json_start(test) < 0)
347: return -1;
348:
349: if (test->json_output) {
350: cJSON_AddItemToObject(test->json_start, "version", cJSON_CreateString(version));
351: cJSON_AddItemToObject(test->json_start, "system_info", cJSON_CreateString(get_system_info()));
352: } else if (test->verbose) {
353: iprintf(test, "%s\n", version);
354: iprintf(test, "%s", "");
355: iprintf(test, "%s\n", get_system_info());
356: iflush(test);
357: }
358:
359: /* Start the client and connect to the server */
360: if (iperf_connect(test) < 0)
361: return -1;
362:
363: /* Begin calculating CPU utilization */
364: cpu_util(NULL);
365:
366: startup = 1;
367: while (test->state != IPERF_DONE) {
368: memcpy(&read_set, &test->read_set, sizeof(fd_set));
369: memcpy(&write_set, &test->write_set, sizeof(fd_set));
370: (void) gettimeofday(&now, NULL);
371: timeout = tmr_timeout(&now);
372: result = select(test->max_fd + 1, &read_set, &write_set, NULL, timeout);
373: if (result < 0 && errno != EINTR) {
374: i_errno = IESELECT;
375: return -1;
376: }
377: if (result > 0) {
378: if (FD_ISSET(test->ctrl_sck, &read_set)) {
379: if (iperf_handle_message_client(test) < 0) {
380: return -1;
381: }
382: FD_CLR(test->ctrl_sck, &read_set);
383: }
384: }
385:
386: if (test->state == TEST_RUNNING) {
387:
388: /* Is this our first time really running? */
389: if (startup) {
390: startup = 0;
391:
392: // Set non-blocking for non-UDP tests
393: if (test->protocol->id != Pudp) {
394: SLIST_FOREACH(sp, &test->streams, streams) {
395: setnonblocking(sp->socket, 1);
396: }
397: }
398: }
399:
400: if (test->reverse) {
401: // Reverse mode. Client receives.
402: if (iperf_recv(test, &read_set) < 0)
403: return -1;
404: } else {
405: // Regular mode. Client sends.
406: if (iperf_send(test, &write_set) < 0)
407: return -1;
408: }
409:
410: /* Run the timers. */
411: (void) gettimeofday(&now, NULL);
412: tmr_run(&now);
413:
414: /* Is the test done yet? */
415: if ((!test->omitting) &&
416: ((test->duration != 0 && test->done) ||
417: (test->settings->bytes != 0 && test->bytes_sent >= test->settings->bytes) ||
418: (test->settings->blocks != 0 && test->blocks_sent >= test->settings->blocks))) {
419:
420: // Unset non-blocking for non-UDP tests
421: if (test->protocol->id != Pudp) {
422: SLIST_FOREACH(sp, &test->streams, streams) {
423: setnonblocking(sp->socket, 0);
424: }
425: }
426:
427: /* Yes, done! Send TEST_END. */
428: test->done = 1;
429: cpu_util(test->cpu_util);
430: test->stats_callback(test);
431: if (iperf_set_send_state(test, TEST_END) != 0)
432: return -1;
433: }
434: }
435: // If we're in reverse mode, continue draining the data
436: // connection(s) even if test is over. This prevents a
437: // deadlock where the server side fills up its pipe(s)
438: // and gets blocked, so it can't receive state changes
439: // from the client side.
440: else if (test->reverse && test->state == TEST_END) {
441: if (iperf_recv(test, &read_set) < 0)
442: return -1;
443: }
444: }
445:
446: if (test->json_output) {
447: if (iperf_json_finish(test) < 0)
448: return -1;
449: } else {
450: iprintf(test, "\n");
451: iprintf(test, "%s", report_done);
452: }
453:
454: iflush(test);
455:
456: return 0;
457: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>