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