17d375156SJon Dugan /*
21e33e721SBruce A. Mah * iperf, Copyright (c) 2014-2022, The Regents of the University of
3da9f046fSBruce A. Mah * California, through Lawrence Berkeley National Laboratory (subject
4da9f046fSBruce A. Mah * to receipt of any required approvals from the U.S. Dept. of
5da9f046fSBruce A. Mah * Energy). All rights reserved.
67d375156SJon Dugan *
7da9f046fSBruce A. Mah * If you have questions about your rights to use or distribute this
8da9f046fSBruce A. Mah * software, please contact Berkeley Lab's Technology Transfer
9da9f046fSBruce A. Mah * Department at [email protected].
10da9f046fSBruce A. Mah *
11da9f046fSBruce A. Mah * NOTICE. This software is owned by the U.S. Department of Energy.
12da9f046fSBruce A. Mah * As such, the U.S. Government has been granted for itself and others
13da9f046fSBruce A. Mah * acting on its behalf a paid-up, nonexclusive, irrevocable,
14da9f046fSBruce A. Mah * worldwide license in the Software to reproduce, prepare derivative
15da9f046fSBruce A. Mah * works, and perform publicly and display publicly. Beginning five
16da9f046fSBruce A. Mah * (5) years after the date permission to assert copyright is obtained
17da9f046fSBruce A. Mah * from the U.S. Department of Energy, and subject to any subsequent
18da9f046fSBruce A. Mah * five (5) year renewals, the U.S. Government is granted for itself
19da9f046fSBruce A. Mah * and others acting on its behalf a paid-up, nonexclusive,
20da9f046fSBruce A. Mah * irrevocable, worldwide license in the Software to reproduce,
21da9f046fSBruce A. Mah * prepare derivative works, distribute copies to the public, perform
22da9f046fSBruce A. Mah * publicly and display publicly, and to permit others to do so.
23da9f046fSBruce A. Mah *
24da9f046fSBruce A. Mah * This code is distributed under a BSD style license, see the LICENSE
25da9f046fSBruce A. Mah * file for complete information.
267d375156SJon Dugan */
2718631aacSsethdelliott #include <errno.h>
2818631aacSsethdelliott #include <setjmp.h>
29e96ab740SJef Poskanzer #include <stdio.h>
3018631aacSsethdelliott #include <stdlib.h>
3118631aacSsethdelliott #include <string.h>
3218631aacSsethdelliott #include <unistd.h>
33a27f6534SJef Poskanzer #include <signal.h>
3418631aacSsethdelliott #include <sys/types.h>
357d413a50SBruce A. Mah #include <netinet/in.h>
3618631aacSsethdelliott #include <sys/select.h>
3718631aacSsethdelliott #include <sys/uio.h>
3898ce496bSjef #include <arpa/inet.h>
3918631aacSsethdelliott
4018631aacSsethdelliott #include "iperf.h"
4118631aacSsethdelliott #include "iperf_api.h"
4218631aacSsethdelliott #include "iperf_util.h"
43a1861d5fSBruce A. Mah #include "iperf_locale.h"
44cde81d76SBen Fox-Moore #include "iperf_time.h"
4518631aacSsethdelliott #include "net.h"
4618631aacSsethdelliott #include "timer.h"
4718631aacSsethdelliott
487eeaa1cbSBruce A. Mah #if defined(HAVE_TCP_CONGESTION)
497eeaa1cbSBruce A. Mah #if !defined(TCP_CA_NAME_MAX)
507eeaa1cbSBruce A. Mah #define TCP_CA_NAME_MAX 16
517eeaa1cbSBruce A. Mah #endif /* TCP_CA_NAME_MAX */
527eeaa1cbSBruce A. Mah #endif /* HAVE_TCP_CONGESTION */
5318631aacSsethdelliott
5418631aacSsethdelliott int
iperf_create_streams(struct iperf_test * test,int sender)550778f04cSBoris Okunev iperf_create_streams(struct iperf_test *test, int sender)
5618631aacSsethdelliott {
5724753fd7Sswlars if (NULL == test)
5824753fd7Sswlars {
5924753fd7Sswlars iperf_err(NULL, "No test\n");
6024753fd7Sswlars return -1;
6124753fd7Sswlars }
6219329249SBruce A. Mah int i, s;
6319329249SBruce A. Mah #if defined(HAVE_TCP_CONGESTION)
6419329249SBruce A. Mah int saved_errno;
6519329249SBruce A. Mah #endif /* HAVE_TCP_CONGESTION */
6618631aacSsethdelliott struct iperf_stream *sp;
6718631aacSsethdelliott
6851732113SKevin Constantine int orig_bind_port = test->bind_port;
6918631aacSsethdelliott for (i = 0; i < test->num_streams; ++i) {
7018631aacSsethdelliott
716bd4e258SBruce A. Mah test->bind_port = orig_bind_port;
726bd4e258SBruce A. Mah if (orig_bind_port) {
736bd4e258SBruce A. Mah test->bind_port += i;
7418631aacSsethdelliott // If Bidir make sure send and receive ports are different
75ec2d0670SJef Poskanzer if (!sender && test->mode == BIDIRECTIONAL)
7618631aacSsethdelliott test->bind_port += test->num_streams;
777eeaa1cbSBruce A. Mah }
787eeaa1cbSBruce A. Mah s = test->protocol->connect(test);
797eeaa1cbSBruce A. Mah test->bind_port = orig_bind_port;
807eeaa1cbSBruce A. Mah if (s < 0)
81b6072241STodd C. Miller return -1;
827eeaa1cbSBruce A. Mah
83b6072241STodd C. Miller #if defined(HAVE_TCP_CONGESTION)
847eeaa1cbSBruce A. Mah if (test->protocol->id == Ptcp) {
857eeaa1cbSBruce A. Mah if (test->congestion) {
867eeaa1cbSBruce A. Mah if (setsockopt(s, IPPROTO_TCP, TCP_CONGESTION, test->congestion, strlen(test->congestion)) < 0) {
877eeaa1cbSBruce A. Mah saved_errno = errno;
887eeaa1cbSBruce A. Mah close(s);
890fd60a36SBruce A. Mah errno = saved_errno;
907eeaa1cbSBruce A. Mah i_errno = IESETCONGESTION;
9153a68308SDavid Bar-On return -1;
9253a68308SDavid Bar-On }
9353a68308SDavid Bar-On }
94b6072241STodd C. Miller {
957eeaa1cbSBruce A. Mah socklen_t len = TCP_CA_NAME_MAX;
96b6072241STodd C. Miller char ca[TCP_CA_NAME_MAX + 1];
977eeaa1cbSBruce A. Mah int rc;
987eeaa1cbSBruce A. Mah rc = getsockopt(s, IPPROTO_TCP, TCP_CONGESTION, ca, &len);
997eeaa1cbSBruce A. Mah if (rc < 0 && test->congestion) {
10053a68308SDavid Bar-On saved_errno = errno;
10153a68308SDavid Bar-On close(s);
10253a68308SDavid Bar-On errno = saved_errno;
10353a68308SDavid Bar-On i_errno = IESETCONGESTION;
1047eeaa1cbSBruce A. Mah return -1;
1057eeaa1cbSBruce A. Mah }
1067eeaa1cbSBruce A. Mah // Set actual used congestion alg, or set to unknown if could not get it
1077eeaa1cbSBruce A. Mah if (rc < 0)
1087eeaa1cbSBruce A. Mah test->congestion_used = strdup("unknown");
1097eeaa1cbSBruce A. Mah else
1107eeaa1cbSBruce A. Mah test->congestion_used = strdup(ca);
1117eeaa1cbSBruce A. Mah if (test->debug) {
1120778f04cSBoris Okunev printf("Congestion algorithm is %s\n", test->congestion_used);
11318631aacSsethdelliott }
11496feeac5SJef Poskanzer }
11596feeac5SJef Poskanzer }
116082cbb0eSJef Poskanzer #endif /* HAVE_TCP_CONGESTION */
11718631aacSsethdelliott
1180778f04cSBoris Okunev if (sender)
11918631aacSsethdelliott FD_SET(s, &test->write_set);
120ec2d0670SJef Poskanzer else
12118631aacSsethdelliott FD_SET(s, &test->read_set);
12218631aacSsethdelliott if (s > test->max_fd) test->max_fd = s;
12318631aacSsethdelliott
12418631aacSsethdelliott sp = iperf_new_stream(test, s, sender);
12518631aacSsethdelliott if (!sp)
12618631aacSsethdelliott return -1;
127ec2d0670SJef Poskanzer
12818631aacSsethdelliott /* Perform the new stream callback */
12918631aacSsethdelliott if (test->on_new_stream)
130c9693599SJef Poskanzer test->on_new_stream(sp);
131cde81d76SBen Fox-Moore }
132c9693599SJef Poskanzer
133c9693599SJef Poskanzer return 0;
134c9693599SJef Poskanzer }
135c9693599SJef Poskanzer
136c9693599SJef Poskanzer static void
test_timer_proc(TimerClientData client_data,struct iperf_time * nowP)137c9693599SJef Poskanzer test_timer_proc(TimerClientData client_data, struct iperf_time *nowP)
138c9693599SJef Poskanzer {
139c9693599SJef Poskanzer struct iperf_test *test = client_data.p;
140cde81d76SBen Fox-Moore
141c9693599SJef Poskanzer test->timer = NULL;
142c9693599SJef Poskanzer test->done = 1;
143c9693599SJef Poskanzer }
144c9693599SJef Poskanzer
145c9693599SJef Poskanzer static void
client_stats_timer_proc(TimerClientData client_data,struct iperf_time * nowP)14638917b1fSJef Poskanzer client_stats_timer_proc(TimerClientData client_data, struct iperf_time *nowP)
147c9693599SJef Poskanzer {
148c9693599SJef Poskanzer struct iperf_test *test = client_data.p;
149c9693599SJef Poskanzer
150c9693599SJef Poskanzer if (test->done)
151cde81d76SBen Fox-Moore return;
152c9693599SJef Poskanzer if (test->stats_callback)
153c9693599SJef Poskanzer test->stats_callback(test);
154c9693599SJef Poskanzer }
155c9693599SJef Poskanzer
156c9693599SJef Poskanzer static void
client_reporter_timer_proc(TimerClientData client_data,struct iperf_time * nowP)15738917b1fSJef Poskanzer client_reporter_timer_proc(TimerClientData client_data, struct iperf_time *nowP)
158c9693599SJef Poskanzer {
159c9693599SJef Poskanzer struct iperf_test *test = client_data.p;
160c9693599SJef Poskanzer
161a953f5aaSJef Poskanzer if (test->done)
162a953f5aaSJef Poskanzer return;
163a953f5aaSJef Poskanzer if (test->reporter_callback)
164cde81d76SBen Fox-Moore test->reporter_callback(test);
165a953f5aaSJef Poskanzer }
16624753fd7Sswlars
16724753fd7Sswlars static int
create_client_timers(struct iperf_test * test)16824753fd7Sswlars create_client_timers(struct iperf_test * test)
16924753fd7Sswlars {
17024753fd7Sswlars struct iperf_time now;
17124753fd7Sswlars TimerClientData cd;
172a953f5aaSJef Poskanzer if (NULL == test)
173cde81d76SBen Fox-Moore {
174a953f5aaSJef Poskanzer iperf_err(NULL, "No test\n");
175a953f5aaSJef Poskanzer i_errno = IEINITTEST;
176a953f5aaSJef Poskanzer return -1;
177a953f5aaSJef Poskanzer }
178a953f5aaSJef Poskanzer
179f11d3fa3SJef Poskanzer if (iperf_time_now(&now) < 0) {
180a953f5aaSJef Poskanzer i_errno = IEINITTEST;
181a953f5aaSJef Poskanzer return -1;
182a953f5aaSJef Poskanzer }
183a953f5aaSJef Poskanzer cd.p = test;
184a953f5aaSJef Poskanzer test->timer = test->stats_timer = test->reporter_timer = NULL;
185a953f5aaSJef Poskanzer if (test->duration != 0) {
186a953f5aaSJef Poskanzer test->done = 0;
187a953f5aaSJef Poskanzer test->timer = tmr_create(&now, test_timer_proc, cd, ( test->duration + test->omit ) * SEC_TO_US, 0);
18838917b1fSJef Poskanzer if (test->timer == NULL) {
189a953f5aaSJef Poskanzer i_errno = IEINITTEST;
190a953f5aaSJef Poskanzer return -1;
191a953f5aaSJef Poskanzer }
192a953f5aaSJef Poskanzer }
193a953f5aaSJef Poskanzer if (test->stats_interval != 0) {
194a953f5aaSJef Poskanzer test->stats_timer = tmr_create(&now, client_stats_timer_proc, cd, test->stats_interval * SEC_TO_US, 1);
19538917b1fSJef Poskanzer if (test->stats_timer == NULL) {
196a953f5aaSJef Poskanzer i_errno = IEINITTEST;
197a953f5aaSJef Poskanzer return -1;
198a953f5aaSJef Poskanzer }
199a953f5aaSJef Poskanzer }
200a953f5aaSJef Poskanzer if (test->reporter_interval != 0) {
201a953f5aaSJef Poskanzer test->reporter_timer = tmr_create(&now, client_reporter_timer_proc, cd, test->reporter_interval * SEC_TO_US, 1);
202a953f5aaSJef Poskanzer if (test->reporter_timer == NULL) {
203a953f5aaSJef Poskanzer i_errno = IEINITTEST;
204c9693599SJef Poskanzer return -1;
205cde81d76SBen Fox-Moore }
206c9693599SJef Poskanzer }
207c9693599SJef Poskanzer return 0;
208c9693599SJef Poskanzer }
20957892604SJef Poskanzer
21057892604SJef Poskanzer static void
client_omit_timer_proc(TimerClientData client_data,struct iperf_time * nowP)211c9693599SJef Poskanzer client_omit_timer_proc(TimerClientData client_data, struct iperf_time *nowP)
212a953f5aaSJef Poskanzer {
213ad2a706fSNevo Hed struct iperf_test *test = client_data.p;
214c9693599SJef Poskanzer
215a953f5aaSJef Poskanzer test->omit_timer = NULL;
216a953f5aaSJef Poskanzer test->omitting = 0;
217a953f5aaSJef Poskanzer iperf_reset_stats(test);
218a953f5aaSJef Poskanzer if (test->verbose && !test->json_output && test->reporter_interval == 0)
219a953f5aaSJef Poskanzer iperf_printf(test, "%s", report_omit_done);
220c9693599SJef Poskanzer
221c9693599SJef Poskanzer /* Reset the timers. */
222c9693599SJef Poskanzer if (test->stats_timer != NULL)
22357892604SJef Poskanzer tmr_reset(nowP, test->stats_timer);
224c9693599SJef Poskanzer if (test->reporter_timer != NULL)
225cde81d76SBen Fox-Moore tmr_reset(nowP, test->reporter_timer);
226c9693599SJef Poskanzer }
22724753fd7Sswlars
22824753fd7Sswlars static int
create_client_omit_timer(struct iperf_test * test)22924753fd7Sswlars create_client_omit_timer(struct iperf_test * test)
23024753fd7Sswlars {
23124753fd7Sswlars struct iperf_time now;
232c9693599SJef Poskanzer TimerClientData cd;
233a953f5aaSJef Poskanzer if (NULL == test)
234a953f5aaSJef Poskanzer {
235a953f5aaSJef Poskanzer iperf_err(NULL, "No test\n");
236a953f5aaSJef Poskanzer return -1;
237cde81d76SBen Fox-Moore }
238c9693599SJef Poskanzer
239c9693599SJef Poskanzer if (test->omit == 0) {
240c9693599SJef Poskanzer test->omit_timer = NULL;
24157892604SJef Poskanzer test->omitting = 0;
242c9693599SJef Poskanzer } else {
24357892604SJef Poskanzer if (iperf_time_now(&now) < 0) {
24457892604SJef Poskanzer i_errno = IEINITTEST;
245c9693599SJef Poskanzer return -1;
246c9693599SJef Poskanzer }
247c9693599SJef Poskanzer test->omitting = 1;
248a953f5aaSJef Poskanzer cd.p = test;
249c9693599SJef Poskanzer test->omit_timer = tmr_create(&now, client_omit_timer_proc, cd, test->omit * SEC_TO_US, 0);
250c9693599SJef Poskanzer if (test->omit_timer == NULL) {
251c9693599SJef Poskanzer i_errno = IEINITTEST;
25218631aacSsethdelliott return -1;
25318631aacSsethdelliott }
25418631aacSsethdelliott }
255d2b9eb1aSJef Poskanzer return 0;
256d2b9eb1aSJef Poskanzer }
25718631aacSsethdelliott
25824753fd7Sswlars int
iperf_handle_message_client(struct iperf_test * test)25924753fd7Sswlars iperf_handle_message_client(struct iperf_test *test)
26024753fd7Sswlars {
26124753fd7Sswlars int rval;
26224753fd7Sswlars int32_t err;
26324753fd7Sswlars
264e35f2035SJef Poskanzer if (NULL == test)
26501943402SJef Poskanzer {
26618631aacSsethdelliott iperf_err(NULL, "No test\n");
26718631aacSsethdelliott i_errno = IEINITTEST;
268ec2d0670SJef Poskanzer return -1;
26918631aacSsethdelliott }
27018631aacSsethdelliott /*!!! Why is this read() and not Nread()? */
271ec2d0670SJef Poskanzer if ((rval = read(test->ctrl_sck, (char*) &test->state, sizeof(signed char))) <= 0) {
27218631aacSsethdelliott if (rval == 0) {
27318631aacSsethdelliott i_errno = IECTRLCLOSE;
27418631aacSsethdelliott return -1;
27518631aacSsethdelliott } else {
27618631aacSsethdelliott i_errno = IERECVMESSAGE;
277111645edSJef Poskanzer return -1;
278ec2d0670SJef Poskanzer }
27918631aacSsethdelliott }
28018631aacSsethdelliott
28118631aacSsethdelliott switch (test->state) {
28218631aacSsethdelliott case PARAM_EXCHANGE:
2830778f04cSBoris Okunev if (iperf_exchange_parameters(test) < 0)
2840778f04cSBoris Okunev return -1;
2850778f04cSBoris Okunev if (test->on_connect)
2860778f04cSBoris Okunev test->on_connect(test);
2870778f04cSBoris Okunev break;
2880778f04cSBoris Okunev case CREATE_STREAMS:
2890778f04cSBoris Okunev if (test->mode == BIDIRECTIONAL)
2900778f04cSBoris Okunev {
291ec2d0670SJef Poskanzer if (iperf_create_streams(test, 1) < 0)
29218631aacSsethdelliott return -1;
29318631aacSsethdelliott if (iperf_create_streams(test, 0) < 0)
294111645edSJef Poskanzer return -1;
295ec2d0670SJef Poskanzer }
296a953f5aaSJef Poskanzer else if (iperf_create_streams(test, test->mode) < 0)
297a953f5aaSJef Poskanzer return -1;
29857892604SJef Poskanzer break;
299c9693599SJef Poskanzer case TEST_START:
3000778f04cSBoris Okunev if (iperf_init_test(test) < 0)
3018bdc8fffSJef Poskanzer return -1;
302c9693599SJef Poskanzer if (create_client_timers(test) < 0)
30318631aacSsethdelliott return -1;
30418631aacSsethdelliott if (create_client_omit_timer(test) < 0)
30518631aacSsethdelliott return -1;
30618631aacSsethdelliott if (test->mode)
307111645edSJef Poskanzer if (iperf_create_send_timers(test) < 0)
308ec2d0670SJef Poskanzer return -1;
30918631aacSsethdelliott break;
31018631aacSsethdelliott case TEST_RUNNING:
31118631aacSsethdelliott break;
31218631aacSsethdelliott case EXCHANGE_RESULTS:
31318631aacSsethdelliott if (iperf_exchange_results(test) < 0)
31418631aacSsethdelliott return -1;
31518631aacSsethdelliott break;
31618631aacSsethdelliott case DISPLAY_RESULTS:
31718631aacSsethdelliott if (test->on_test_finish)
31818631aacSsethdelliott test->on_test_finish(test);
319c04bdcb9SBruce A. Mah iperf_client_end(test);
320c04bdcb9SBruce A. Mah break;
321c04bdcb9SBruce A. Mah case IPERF_DONE:
322c04bdcb9SBruce A. Mah break;
323c04bdcb9SBruce A. Mah case SERVER_TERMINATE:
324c04bdcb9SBruce A. Mah i_errno = IESERVERTERM;
325c04bdcb9SBruce A. Mah
326c04bdcb9SBruce A. Mah /*
327c04bdcb9SBruce A. Mah * Temporarily be in DISPLAY_RESULTS phase so we can get
328c04bdcb9SBruce A. Mah * ending summary statistics.
329ec2d0670SJef Poskanzer */
33018631aacSsethdelliott signed char oldstate = test->state;
33118631aacSsethdelliott cpu_util(test->cpu_util);
332ec2d0670SJef Poskanzer test->state = DISPLAY_RESULTS;
3332c206d54Ssethdelliott test->reporter_callback(test);
334d2b9eb1aSJef Poskanzer test->state = oldstate;
3352c206d54Ssethdelliott return -1;
336ec2d0670SJef Poskanzer case ACCESS_DENIED:
3372c206d54Ssethdelliott i_errno = IEACCESSDENIED;
338d2b9eb1aSJef Poskanzer return -1;
339d2b9eb1aSJef Poskanzer case SERVER_ERROR:
3402c206d54Ssethdelliott if (Nread(test->ctrl_sck, (char*) &err, sizeof(err), Ptcp) < 0) {
341ec2d0670SJef Poskanzer i_errno = IECTRLREAD;
3422c206d54Ssethdelliott return -1;
343d2b9eb1aSJef Poskanzer }
344ec2d0670SJef Poskanzer i_errno = ntohl(err);
34518631aacSsethdelliott if (Nread(test->ctrl_sck, (char*) &err, sizeof(err), Ptcp) < 0) {
34618631aacSsethdelliott i_errno = IECTRLREAD;
347ec2d0670SJef Poskanzer return -1;
34818631aacSsethdelliott }
34918631aacSsethdelliott errno = ntohl(err);
350ec2d0670SJef Poskanzer return -1;
35118631aacSsethdelliott default:
35218631aacSsethdelliott i_errno = IEMESSAGE;
35318631aacSsethdelliott return -1;
35418631aacSsethdelliott }
35518631aacSsethdelliott
35618631aacSsethdelliott return 0;
35718631aacSsethdelliott }
35818631aacSsethdelliott
359*7bdd5b0eSDavid Bar-On
360*7bdd5b0eSDavid Bar-On
361*7bdd5b0eSDavid Bar-On /* iperf_connect -- client to server connection function */
36224753fd7Sswlars int
iperf_connect(struct iperf_test * test)36324753fd7Sswlars iperf_connect(struct iperf_test *test)
36424753fd7Sswlars {
36524753fd7Sswlars int opt;
36624753fd7Sswlars socklen_t len;
36718631aacSsethdelliott
36818631aacSsethdelliott if (NULL == test)
36918631aacSsethdelliott {
3702ab386bfSjef iperf_err(NULL, "No test\n");
37118631aacSsethdelliott return -1;
37218631aacSsethdelliott }
373d7613a8eSJef Poskanzer FD_ZERO(&test->read_set);
374023fb45eSKevin Constantine FD_ZERO(&test->write_set);
37521581a72SBruce A. Mah
37618631aacSsethdelliott make_cookie(test->cookie);
37718631aacSsethdelliott
378ec2d0670SJef Poskanzer /* Create and connect the control channel */
37918631aacSsethdelliott if (test->ctrl_sck < 0)
38018631aacSsethdelliott // Create the control channel using an ephemeral port
38197a1d11aSBruce A. Mah test->ctrl_sck = netdial(test->settings->domain, Ptcp, test->bind_address, test->bind_dev, 0, test->server_hostname, test->server_port, test->settings->connect_timeout);
38297a1d11aSBruce A. Mah if (test->ctrl_sck < 0) {
38397a1d11aSBruce A. Mah i_errno = IECONNECT;
38497a1d11aSBruce A. Mah return -1;
38597a1d11aSBruce A. Mah }
38697a1d11aSBruce A. Mah
38797a1d11aSBruce A. Mah // set TCP_NODELAY for lower latency on control messages
388*7bdd5b0eSDavid Bar-On int flag = 1;
389*7bdd5b0eSDavid Bar-On if (setsockopt(test->ctrl_sck, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int))) {
390*7bdd5b0eSDavid Bar-On i_errno = IESETNODELAY;
391*7bdd5b0eSDavid Bar-On return -1;
392*7bdd5b0eSDavid Bar-On }
393*7bdd5b0eSDavid Bar-On
394*7bdd5b0eSDavid Bar-On #if defined(HAVE_TCP_USER_TIMEOUT)
395*7bdd5b0eSDavid Bar-On if ((opt = test->settings->snd_timeout)) {
396*7bdd5b0eSDavid Bar-On if (setsockopt(test->ctrl_sck, IPPROTO_TCP, TCP_USER_TIMEOUT, &opt, sizeof(opt)) < 0) {
39718631aacSsethdelliott i_errno = IESETUSERTIMEOUT;
39818631aacSsethdelliott return -1;
399ec2d0670SJef Poskanzer }
40018631aacSsethdelliott }
40118631aacSsethdelliott #endif /* HAVE_TCP_USER_TIMEOUT */
40218631aacSsethdelliott
403082cbb0eSJef Poskanzer if (Nwrite(test->ctrl_sck, test->cookie, COOKIE_SIZE, Ptcp) < 0) {
40418631aacSsethdelliott i_errno = IESENDCOOKIE;
4050fd60a36SBruce A. Mah return -1;
4060fd60a36SBruce A. Mah }
4070fd60a36SBruce A. Mah
4080fd60a36SBruce A. Mah FD_SET(test->ctrl_sck, &test->read_set);
4090fd60a36SBruce A. Mah if (test->ctrl_sck > test->max_fd) test->max_fd = test->ctrl_sck;
410d60f6234SBoris Okunev
4110fd60a36SBruce A. Mah len = sizeof(opt);
4120fd60a36SBruce A. Mah if (getsockopt(test->ctrl_sck, IPPROTO_TCP, TCP_MAXSEG, &opt, &len) < 0) {
413d60f6234SBoris Okunev test->ctrl_sck_mss = 0;
414805a4ce1SBruce A. Mah }
415d60f6234SBoris Okunev else {
416d60f6234SBoris Okunev if (opt > 0 && opt <= MAX_UDP_BLOCKSIZE) {
417d60f6234SBoris Okunev test->ctrl_sck_mss = opt;
418d60f6234SBoris Okunev }
419d60f6234SBoris Okunev else {
420d60f6234SBoris Okunev char str[WARN_STR_LEN];
421d60f6234SBoris Okunev snprintf(str, sizeof(str),
4220fd60a36SBruce A. Mah "Ignoring nonsense TCP MSS %d", opt);
4230fd60a36SBruce A. Mah warning(str);
4240fd60a36SBruce A. Mah
4250fd60a36SBruce A. Mah test->ctrl_sck_mss = 0;
4260fd60a36SBruce A. Mah }
4270fd60a36SBruce A. Mah }
4280fd60a36SBruce A. Mah
4290fd60a36SBruce A. Mah if (test->verbose) {
4300fd60a36SBruce A. Mah printf("Control connection MSS %d\n", test->ctrl_sck_mss);
4310fd60a36SBruce A. Mah }
4320fd60a36SBruce A. Mah
4330fd60a36SBruce A. Mah /*
4340fd60a36SBruce A. Mah * If we're doing a UDP test and the block size wasn't explicitly
4350fd60a36SBruce A. Mah * set, then use the known MSS of the control connection to pick
4360fd60a36SBruce A. Mah * an appropriate default. If we weren't able to get the
4370fd60a36SBruce A. Mah * MSS for some reason, then default to something that should
4380fd60a36SBruce A. Mah * work on non-jumbo-frame Ethernet networks. The goal is to
4390fd60a36SBruce A. Mah * pick a reasonable default that is large but should get from
4400fd60a36SBruce A. Mah * sender to receiver without any IP fragmentation.
4410fd60a36SBruce A. Mah *
4420fd60a36SBruce A. Mah * We assume that the control connection is routed the same as the
4430fd60a36SBruce A. Mah * data packets (thus has the same PMTU). Also in the case of
4440fd60a36SBruce A. Mah * --reverse tests, we assume that the MTU is the same in both
4450fd60a36SBruce A. Mah * directions. Note that even if the algorithm guesses wrong,
4460fd60a36SBruce A. Mah * the user always has the option to override.
4470fd60a36SBruce A. Mah */
4480fd60a36SBruce A. Mah if (test->protocol->id == Pudp) {
4490fd60a36SBruce A. Mah if (test->settings->blksize == 0) {
450f46e2e3eSBruce A. Mah if (test->ctrl_sck_mss) {
4510fd60a36SBruce A. Mah test->settings->blksize = test->ctrl_sck_mss;
4520fd60a36SBruce A. Mah }
453f46e2e3eSBruce A. Mah else {
4540fd60a36SBruce A. Mah test->settings->blksize = DEFAULT_UDP_BLKSIZE;
4550fd60a36SBruce A. Mah }
4560fd60a36SBruce A. Mah if (test->verbose) {
4570fd60a36SBruce A. Mah printf("Setting UDP block size to %d\n", test->settings->blksize);
4580fd60a36SBruce A. Mah }
459ca954c76SBruce A. Mah }
460ca954c76SBruce A. Mah
461805a4ce1SBruce A. Mah /*
462f46e2e3eSBruce A. Mah * Regardless of whether explicitly or implicitly set, if the
463d6a67517SBruce A. Mah * block size is larger than the MSS, print a warning.
464f46e2e3eSBruce A. Mah */
4650fd60a36SBruce A. Mah if (test->ctrl_sck_mss > 0 &&
4660fd60a36SBruce A. Mah test->settings->blksize > test->ctrl_sck_mss) {
4670fd60a36SBruce A. Mah char str[WARN_STR_LEN];
468ec2d0670SJef Poskanzer snprintf(str, sizeof(str),
46918631aacSsethdelliott "UDP block size %d exceeds TCP MSS %d, may result in fragmentation / drops", test->settings->blksize, test->ctrl_sck_mss);
47018631aacSsethdelliott warning(str);
47118631aacSsethdelliott }
47218631aacSsethdelliott }
47318631aacSsethdelliott
47418631aacSsethdelliott return 0;
47524753fd7Sswlars }
47624753fd7Sswlars
47724753fd7Sswlars
47824753fd7Sswlars int
iperf_client_end(struct iperf_test * test)47924753fd7Sswlars iperf_client_end(struct iperf_test *test)
4800bd8d9daSsethdelliott {
4810bd8d9daSsethdelliott if (NULL == test)
4820bd8d9daSsethdelliott {
4830bd8d9daSsethdelliott iperf_err(NULL, "No test\n");
4840bd8d9daSsethdelliott return -1;
4850bd8d9daSsethdelliott }
48618631aacSsethdelliott struct iperf_stream *sp;
48718631aacSsethdelliott
48818631aacSsethdelliott /* Close all stream sockets */
48918631aacSsethdelliott SLIST_FOREACH(sp, &test->streams, streams) {
4901e33e721SBruce A. Mah close(sp->socket);
4911e33e721SBruce A. Mah }
49246cb4b4bSBruce A. Mah
49346cb4b4bSBruce A. Mah /* show final summary */
4941e33e721SBruce A. Mah test->reporter_callback(test);
49546cb4b4bSBruce A. Mah
496946a5a0fSp0intR /* Send response only if no error in server */
4978464c3c2SBruce A. Mah if (test->state > 0) {
498946a5a0fSp0intR if (iperf_set_send_state(test, IPERF_DONE) != 0)
499946a5a0fSp0intR return -1;
500ec2d0670SJef Poskanzer }
50118631aacSsethdelliott
50218631aacSsethdelliott /* Close control socket */
50318631aacSsethdelliott if (test->ctrl_sck >= 0)
50418631aacSsethdelliott close(test->ctrl_sck);
50518631aacSsethdelliott
50618631aacSsethdelliott return 0;
507a27f6534SJef Poskanzer }
5087bcbb1f6SJef Poskanzer
5096177a7f1SJef Poskanzer
510cde81d76SBen Fox-Moore int
iperf_run_client(struct iperf_test * test)5117bcbb1f6SJef Poskanzer iperf_run_client(struct iperf_test * test)
512081ba8e4SBruce A. Mah {
5138ffe72e2SDavid Bar-On int startup;
5148ffe72e2SDavid Bar-On int result = 0;
5158ffe72e2SDavid Bar-On fd_set read_set, write_set;
5168ffe72e2SDavid Bar-On struct iperf_time now;
5178ffe72e2SDavid Bar-On struct timeval* timeout = NULL;
5188ffe72e2SDavid Bar-On struct iperf_stream *sp;
51918631aacSsethdelliott struct iperf_time last_receive_time;
52024753fd7Sswlars struct iperf_time diff_time;
52124753fd7Sswlars struct timeval used_timeout;
52224753fd7Sswlars int64_t t_usecs;
52324753fd7Sswlars int64_t timeout_us;
52424753fd7Sswlars int64_t rcv_timeout_us;
52524753fd7Sswlars
526255a9c71Ssrgnk if (NULL == test)
527255a9c71Ssrgnk {
528255a9c71Ssrgnk iperf_err(NULL, "No test\n");
529255a9c71Ssrgnk return -1;
5308a7e01ebSJef Poskanzer }
531deefb95fSBruce A. Mah
5328a7e01ebSJef Poskanzer if (test->logfile)
5338a7e01ebSJef Poskanzer if (iperf_open_logfile(test) < 0)
534e96ab740SJef Poskanzer return -1;
535e96ab740SJef Poskanzer
536e96ab740SJef Poskanzer if (test->affinity != -1)
537e96ab740SJef Poskanzer if (iperf_setaffinity(test, test->affinity) != 0)
538e96ab740SJef Poskanzer return -1;
539e96ab740SJef Poskanzer
540e96ab740SJef Poskanzer if (test->json_output)
541e96ab740SJef Poskanzer if (iperf_json_start(test) < 0)
542ad2a706fSNevo Hed return -1;
543ad2a706fSNevo Hed
544ad2a706fSNevo Hed if (test->json_output) {
5453fa8072eSBruce A. Mah cJSON_AddItemToObject(test->json_start, "version", cJSON_CreateString(version));
546e96ab740SJef Poskanzer cJSON_AddItemToObject(test->json_start, "system_info", cJSON_CreateString(get_system_info()));
547e96ab740SJef Poskanzer } else if (test->verbose) {
54818631aacSsethdelliott iperf_printf(test, "%s\n", version);
5498a7e01ebSJef Poskanzer iperf_printf(test, "%s", "");
55080b7c7b2SBruce A. Mah iperf_printf(test, "%s\n", get_system_info());
55118631aacSsethdelliott iflush(test);
552a27f6534SJef Poskanzer }
5533331e801Ssethdelliott
5548ffe72e2SDavid Bar-On /* Start the client and connect to the server */
5558ffe72e2SDavid Bar-On if (iperf_connect(test) < 0)
5568ffe72e2SDavid Bar-On goto cleanup_and_fail;
5578ffe72e2SDavid Bar-On
5583331e801Ssethdelliott /* Begin calculating CPU utilization */
559a27f6534SJef Poskanzer cpu_util(NULL);
56018631aacSsethdelliott if (test->mode != SENDER)
5616177a7f1SJef Poskanzer rcv_timeout_us = (test->settings->rcv_timeout.secs * SEC_TO_US) + test->settings->rcv_timeout.usecs;
5626177a7f1SJef Poskanzer else
563cde81d76SBen Fox-Moore rcv_timeout_us = 0;
5647bcbb1f6SJef Poskanzer
5658ffe72e2SDavid Bar-On startup = 1;
5668ffe72e2SDavid Bar-On while (test->state != IPERF_DONE) {
5678ffe72e2SDavid Bar-On memcpy(&read_set, &test->read_set, sizeof(fd_set));
5688ffe72e2SDavid Bar-On memcpy(&write_set, &test->write_set, sizeof(fd_set));
5698ffe72e2SDavid Bar-On iperf_time_now(&now);
5708ffe72e2SDavid Bar-On timeout = tmr_timeout(&now);
5718ffe72e2SDavid Bar-On
5728ffe72e2SDavid Bar-On // In reverse active mode client ensures data is received
5738ffe72e2SDavid Bar-On if (test->state == TEST_RUNNING && rcv_timeout_us > 0) {
5748ffe72e2SDavid Bar-On timeout_us = -1;
5758ffe72e2SDavid Bar-On if (timeout != NULL) {
5768ffe72e2SDavid Bar-On used_timeout.tv_sec = timeout->tv_sec;
5778ffe72e2SDavid Bar-On used_timeout.tv_usec = timeout->tv_usec;
5788ffe72e2SDavid Bar-On timeout_us = (timeout->tv_sec * SEC_TO_US) + timeout->tv_usec;
5798ffe72e2SDavid Bar-On }
5808ffe72e2SDavid Bar-On if (timeout_us < 0 || timeout_us > rcv_timeout_us) {
5817bcbb1f6SJef Poskanzer used_timeout.tv_sec = test->settings->rcv_timeout.secs;
58218631aacSsethdelliott used_timeout.tv_usec = test->settings->rcv_timeout.usecs;
58318631aacSsethdelliott }
58480b7c7b2SBruce A. Mah timeout = &used_timeout;
5858ffe72e2SDavid Bar-On }
5868ffe72e2SDavid Bar-On
5878ffe72e2SDavid Bar-On result = select(test->max_fd + 1, &read_set, &write_set, NULL, timeout);
5888ffe72e2SDavid Bar-On if (result < 0 && errno != EINTR) {
5898ffe72e2SDavid Bar-On i_errno = IESELECT;
5908ffe72e2SDavid Bar-On goto cleanup_and_fail;
5918ffe72e2SDavid Bar-On } else if (result == 0 && test->state == TEST_RUNNING && rcv_timeout_us > 0) {
5928ffe72e2SDavid Bar-On // If nothing was received in non-reverse running state then probably something got stack -
5938ffe72e2SDavid Bar-On // either client, server or network, and test should be terminated.
594ec2d0670SJef Poskanzer iperf_time_now(&now);
5958ffe72e2SDavid Bar-On if (iperf_time_diff(&now, &last_receive_time, &diff_time) == 0) {
5968ffe72e2SDavid Bar-On t_usecs = iperf_time_in_usecs(&diff_time);
5978ffe72e2SDavid Bar-On if (t_usecs > rcv_timeout_us) {
5988ffe72e2SDavid Bar-On i_errno = IENOMSG;
599ec2d0670SJef Poskanzer goto cleanup_and_fail;
6008ffe72e2SDavid Bar-On }
6018ffe72e2SDavid Bar-On
6028ffe72e2SDavid Bar-On }
6036177a7f1SJef Poskanzer }
60498ce496bSjef
60580b7c7b2SBruce A. Mah if (result > 0) {
60698ce496bSjef if (rcv_timeout_us > 0) {
6076177a7f1SJef Poskanzer iperf_time_now(&last_receive_time);
60818631aacSsethdelliott }
609a27f6534SJef Poskanzer if (FD_ISSET(test->ctrl_sck, &read_set)) {
61018631aacSsethdelliott if (iperf_handle_message_client(test) < 0) {
61118631aacSsethdelliott goto cleanup_and_fail;
612a27f6534SJef Poskanzer }
613c9693599SJef Poskanzer FD_CLR(test->ctrl_sck, &read_set);
614a953f5aaSJef Poskanzer }
615a27f6534SJef Poskanzer }
616ba123d5cSJef Poskanzer
6176b16244bSBruce A. Mah if (test->state == TEST_RUNNING) {
6186b16244bSBruce A. Mah
619081ba8e4SBruce A. Mah /* Is this our first time really running? */
620081ba8e4SBruce A. Mah if (startup) {
621081ba8e4SBruce A. Mah startup = 0;
622a27f6534SJef Poskanzer
623cbacc1d6SBruce A. Mah // Set non-blocking for non-UDP tests
624a27f6534SJef Poskanzer if (test->protocol->id != Pudp) {
6250778f04cSBoris Okunev SLIST_FOREACH(sp, &test->streams, streams) {
6260778f04cSBoris Okunev setnonblocking(sp->socket, 1);
6270778f04cSBoris Okunev }
6280778f04cSBoris Okunev }
62980b7c7b2SBruce A. Mah }
6307bcbb1f6SJef Poskanzer
63180b7c7b2SBruce A. Mah
6320778f04cSBoris Okunev if (test->mode == BIDIRECTIONAL)
63318631aacSsethdelliott {
634081ba8e4SBruce A. Mah if (iperf_send(test, &write_set) < 0)
63580b7c7b2SBruce A. Mah goto cleanup_and_fail;
6360778f04cSBoris Okunev if (iperf_recv(test, &read_set) < 0)
6370778f04cSBoris Okunev goto cleanup_and_fail;
6380778f04cSBoris Okunev } else if (test->mode == SENDER) {
63980b7c7b2SBruce A. Mah // Regular mode. Client sends.
64018631aacSsethdelliott if (iperf_send(test, &write_set) < 0)
64118631aacSsethdelliott goto cleanup_and_fail;
6420778f04cSBoris Okunev } else {
643ec2d0670SJef Poskanzer // Reverse mode. Client receives.
644cde81d76SBen Fox-Moore if (iperf_recv(test, &read_set) < 0)
645ec2d0670SJef Poskanzer goto cleanup_and_fail;
64618631aacSsethdelliott }
647b818ef51SBruce A. Mah
648b818ef51SBruce A. Mah
649b818ef51SBruce A. Mah /* Run the timers. */
650b818ef51SBruce A. Mah iperf_time_now(&now);
651b818ef51SBruce A. Mah tmr_run(&now);
652b818ef51SBruce A. Mah
653b818ef51SBruce A. Mah /*
654b818ef51SBruce A. Mah * Is the test done yet? We have to be out of omitting
655f11d3fa3SJef Poskanzer * mode, and then we have to have fulfilled one of the
65641089978SHamid Anvari * ending criteria, either by times, bytes, or blocks.
657b818ef51SBruce A. Mah * The bytes and blocks tests needs to handle both the
658b818ef51SBruce A. Mah * cases of the client being the sender and the client
659b818ef51SBruce A. Mah * being the receiver.
660b818ef51SBruce A. Mah */
661081ba8e4SBruce A. Mah if ((!test->omitting) &&
662f9820fd6SBruce A. Mah (test->done ||
6636b16244bSBruce A. Mah (test->settings->bytes != 0 && (test->bytes_sent >= test->settings->bytes ||
664081ba8e4SBruce A. Mah test->bytes_received >= test->settings->bytes)) ||
665081ba8e4SBruce A. Mah (test->settings->blocks != 0 && (test->blocks_sent >= test->settings->blocks ||
666081ba8e4SBruce A. Mah test->blocks_received >= test->settings->blocks)))) {
667cbacc1d6SBruce A. Mah
668081ba8e4SBruce A. Mah // Unset non-blocking for non-UDP tests
669fbaa2467SJef Poskanzer if (test->protocol->id != Pudp) {
67095360b20SJef Poskanzer SLIST_FOREACH(sp, &test->streams, streams) {
671056361fcSJef Poskanzer setnonblocking(sp->socket, 0);
67218631aacSsethdelliott }
673c9693599SJef Poskanzer }
67480b7c7b2SBruce A. Mah
67518631aacSsethdelliott /* Yes, done! Send TEST_END. */
67618631aacSsethdelliott test->done = 1;
6770fdda50cSBruce A. Mah cpu_util(test->cpu_util);
6780fdda50cSBruce A. Mah test->stats_callback(test);
6790fdda50cSBruce A. Mah if (iperf_set_send_state(test, TEST_END) != 0)
6800fdda50cSBruce A. Mah goto cleanup_and_fail;
6810fdda50cSBruce A. Mah }
6820778f04cSBoris Okunev }
6830fdda50cSBruce A. Mah // If we're in reverse mode, continue draining the data
68480b7c7b2SBruce A. Mah // connection(s) even if test is over. This prevents a
6850fdda50cSBruce A. Mah // deadlock where the server side fills up its pipe(s)
686f11d3fa3SJef Poskanzer // and gets blocked, so it can't receive state changes
68718631aacSsethdelliott // from the client side.
688e96ab740SJef Poskanzer else if (test->mode == RECEIVER && test->state == TEST_END) {
689e96ab740SJef Poskanzer if (iperf_recv(test, &read_set) < 0)
690e96ab740SJef Poskanzer goto cleanup_and_fail;
691d38ab4c8SJef Poskanzer }
692ad2a706fSNevo Hed }
693ad2a706fSNevo Hed
694d38ab4c8SJef Poskanzer if (test->json_output) {
695e96ab740SJef Poskanzer if (iperf_json_finish(test) < 0)
696aeb6938dSBruce A. Mah return -1;
697aeb6938dSBruce A. Mah } else {
698ec2d0670SJef Poskanzer iperf_printf(test, "\n");
69980b7c7b2SBruce A. Mah iperf_printf(test, "%s", report_done);
70080b7c7b2SBruce A. Mah }
70180b7c7b2SBruce A. Mah
7022ec43d12SDavid Bar-On iflush(test);
7031ed0239dSJoakim Sørensen
7041ed0239dSJoakim Sørensen return 0;
7051ed0239dSJoakim Sørensen
7060b15c449SSarah Larsen cleanup_and_fail:
7070b15c449SSarah Larsen iperf_client_end(test);
7081ed0239dSJoakim Sørensen if (test->json_output) {
7092ec43d12SDavid Bar-On cJSON_AddStringToObject(test->json_top, "error", iperf_strerror(i_errno));
71080b7c7b2SBruce A. Mah iperf_json_finish(test);
7111ed0239dSJoakim Sørensen iflush(test);
71218631aacSsethdelliott // Return 0 and not -1 since all terminating function were done here.
713 // Also prevents error message logging outside the already closed JSON output.
714 return 0;
715 }
716 iflush(test);
717 return -1;
718 }
719