xref: /iperf/src/iperf_client_api.c (revision 56a97f93)
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 <stdlib.h>
13 #include <string.h>
14 #include <unistd.h>
15 #include <sys/types.h>
16 #include <sys/select.h>
17 #include <sys/uio.h>
18 
19 #include "iperf.h"
20 #include "iperf_api.h"
21 #include "iperf_client_api.h"
22 #include "iperf_error.h"
23 #include "iperf_util.h"
24 #include "net.h"
25 #include "timer.h"
26 
27 
28 int
29 iperf_create_streams(struct iperf_test *test)
30 {
31     int i, s;
32     struct iperf_stream *sp;
33 
34     for (i = 0; i < test->num_streams; ++i) {
35 
36         if ((s = test->protocol->connect(test)) < 0)
37             return (-1);
38 
39         FD_SET(s, &test->read_set);
40         FD_SET(s, &test->write_set);
41         test->max_fd = (test->max_fd < s) ? s : test->max_fd;
42 
43         sp = iperf_new_stream(test, s);
44         if (!sp)
45             return (-1);
46 
47         /* Perform the new stream callback */
48         if (test->on_new_stream)
49             test->on_new_stream(sp);
50     }
51 
52     return (0);
53 }
54 
55 int
56 iperf_handle_message_client(struct iperf_test *test)
57 {
58     int rval, perr;
59 
60     if ((rval = read(test->ctrl_sck, &test->state, sizeof(char))) <= 0) {
61         if (rval == 0) {
62             i_errno = IECTRLCLOSE;
63             return (-1);
64         } else {
65             i_errno = IERECVMESSAGE;
66             return (-1);
67         }
68     }
69 
70     switch (test->state) {
71         case PARAM_EXCHANGE:
72             if (iperf_exchange_parameters(test) < 0)
73                 return (-1);
74             if (test->on_connect)
75                 test->on_connect(test);
76             break;
77         case CREATE_STREAMS:
78             if (iperf_create_streams(test) < 0)
79                 return (-1);
80             break;
81         case TEST_START:
82             if (iperf_init_test(test) < 0)
83                 return (-1);
84             break;
85         case TEST_RUNNING:
86             break;
87         case EXCHANGE_RESULTS:
88             if (iperf_exchange_results(test) < 0)
89                 return (-1);
90             break;
91         case DISPLAY_RESULTS:
92             if (test->on_test_finish)
93                 test->on_test_finish(test);
94             iperf_client_end(test);
95             break;
96         case IPERF_DONE:
97             break;
98         case SERVER_TERMINATE:
99             i_errno = IESERVERTERM;
100             return (-1);
101         case ACCESS_DENIED:
102             i_errno = IEACCESSDENIED;
103             return (-1);
104         case SERVER_ERROR:
105             if (Nread(test->ctrl_sck, &i_errno, sizeof(i_errno), Ptcp) < 0) {
106                 i_errno = IECTRLREAD;
107                 return (-1);
108             }
109             i_errno = ntohl(i_errno);
110             if (Nread(test->ctrl_sck, &perr, sizeof(perr), Ptcp) < 0) {
111                 i_errno = IECTRLREAD;
112                 return (-1);
113             }
114             errno = ntohl(perr);
115             return (-1);
116         default:
117             i_errno = IEMESSAGE;
118             return (-1);
119     }
120 
121     return (0);
122 }
123 
124 
125 
126 /* iperf_connect -- client to server connection function */
127 int
128 iperf_connect(struct iperf_test *test)
129 {
130     FD_ZERO(&test->read_set);
131     FD_ZERO(&test->write_set);
132 
133     make_cookie(test->cookie);
134 
135     /* Create and connect the control channel */
136     test->ctrl_sck = netdial(test->settings->domain, Ptcp, test->bind_address, test->server_hostname, test->server_port);
137     if (test->ctrl_sck < 0) {
138         i_errno = IECONNECT;
139         return (-1);
140     }
141 
142     if (Nwrite(test->ctrl_sck, test->cookie, COOKIE_SIZE, Ptcp) < 0) {
143         i_errno = IESENDCOOKIE;
144         return (-1);
145     }
146 
147     FD_SET(test->ctrl_sck, &test->read_set);
148     FD_SET(test->ctrl_sck, &test->write_set);
149     test->max_fd = (test->ctrl_sck > test->max_fd) ? test->ctrl_sck : test->max_fd;
150 
151     return (0);
152 }
153 
154 
155 int
156 iperf_client_end(struct iperf_test *test)
157 {
158     struct iperf_stream *sp;
159 
160     /* Close all stream sockets */
161     SLIST_FOREACH(sp, &test->streams, streams) {
162         close(sp->socket);
163     }
164 
165     /* show final summary */
166     test->reporter_callback(test);
167 
168     test->state = IPERF_DONE;
169     if (Nwrite(test->ctrl_sck, &test->state, sizeof(char), Ptcp) < 0) {
170         i_errno = IESENDMESSAGE;
171         return (-1);
172     }
173 
174     return (0);
175 }
176 
177 
178 int
179 iperf_run_client(struct iperf_test * test)
180 {
181     int result;
182     fd_set temp_read_set, temp_write_set;
183     struct timeval tv;
184     time_t sec, usec;
185 
186     /* Start the client and connect to the server */
187     if (iperf_connect(test) < 0) {
188         return (-1);
189     }
190 
191     // Begin calculating CPU utilization
192     cpu_util(NULL);
193 
194     while (test->state != IPERF_DONE) {
195 
196         memcpy(&temp_read_set, &test->read_set, sizeof(fd_set));
197         memcpy(&temp_write_set, &test->write_set, sizeof(fd_set));
198         tv.tv_sec = 15;
199         tv.tv_usec = 0;
200 
201         result = select(test->max_fd + 1, &temp_read_set, &temp_write_set, NULL, &tv);
202         if (result < 0 && errno != EINTR) {
203             i_errno = IESELECT;
204             return (-1);
205         } else if (result > 0) {
206             if (FD_ISSET(test->ctrl_sck, &temp_read_set)) {
207                 if (iperf_handle_message_client(test) < 0)
208                     return (-1);
209                 FD_CLR(test->ctrl_sck, &temp_read_set);
210             }
211 
212             if (test->state == TEST_RUNNING) {
213                 if (test->reverse) {
214                     // Reverse mode. Client receives.
215                     if (iperf_recv(test) < 0)
216                         return (-1);
217                 } else {
218                     // Regular mode. Client sends.
219                     if (iperf_send(test) < 0)
220                         return (-1);
221                 }
222 
223                 /* Perform callbacks */
224                 if (timer_expired(test->stats_timer)) {
225                     test->stats_callback(test);
226                     sec = (time_t) test->stats_interval;
227                     usec = (test->stats_interval - sec) * SEC_TO_US;
228                     if (update_timer(test->stats_timer, sec, usec) < 0)
229                         return (-1);
230                 }
231                 if (timer_expired(test->reporter_timer)) {
232                     test->reporter_callback(test);
233                     sec = (time_t) test->reporter_interval;
234                     usec = (test->reporter_interval - sec) * SEC_TO_US;
235                     if (update_timer(test->reporter_timer, sec, usec) < 0)
236                         return (-1);
237                 }
238 
239                 /* Send TEST_END if all data has been sent or timer expired */
240                 if (all_data_sent(test) || timer_expired(test->timer)) {
241                     cpu_util(&test->cpu_util);
242                     test->stats_callback(test);
243                     test->state = TEST_END;
244                     if (Nwrite(test->ctrl_sck, &test->state, sizeof(char), Ptcp) < 0) {
245                         i_errno = IESENDMESSAGE;
246                         return (-1);
247                     }
248                 }
249             }
250         }
251     }
252 
253     return (0);
254 }
255