source: azure_iot_hub_f767zi/trunk/asp_baseplatform/lwip/lwip-2.1.2/src/apps/lwiperf/lwiperf.c@ 457

Last change on this file since 457 was 457, checked in by coas-nagasima, 4 years ago

ファイルを追加

  • Property svn:eol-style set to native
  • Property svn:mime-type set to text/x-csrc;charset=UTF-8
File size: 33.1 KB
Line 
1/**
2 * @file
3 * lwIP iPerf server implementation
4 */
5
6/**
7 * @defgroup iperf Iperf server
8 * @ingroup apps
9 *
10 * This is a simple performance measuring client/server to check your bandwith using
11 * iPerf2 on a PC as server/client.
12 * It is currently a minimal implementation providing a TCP client/server only.
13 *
14 * @todo:
15 * - implement UDP mode
16 * - protect combined sessions handling (via 'related_master_state') against reallocation
17 * (this is a pointer address, currently, so if the same memory is allocated again,
18 * session pairs (tx/rx) can be confused on reallocation)
19 */
20
21/*
22 * Copyright (c) 2014 Simon Goldschmidt
23 * All rights reserved.
24 *
25 * Redistribution and use in source and binary forms, with or without modification,
26 * are permitted provided that the following conditions are met:
27 *
28 * 1. Redistributions of source code must retain the above copyright notice,
29 * this list of conditions and the following disclaimer.
30 * 2. Redistributions in binary form must reproduce the above copyright notice,
31 * this list of conditions and the following disclaimer in the documentation
32 * and/or other materials provided with the distribution.
33 * 3. The name of the author may not be used to endorse or promote products
34 * derived from this software without specific prior written permission.
35 *
36 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
37 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
38 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
39 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
40 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
41 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
42 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
43 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
44 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
45 * OF SUCH DAMAGE.
46 *
47 * This file is part of the lwIP TCP/IP stack.
48 *
49 * Author: Simon Goldschmidt
50 */
51
52#include "lwip/apps/lwiperf.h"
53
54#include "lwip/tcp.h"
55#include "lwip/sys.h"
56
57#include <string.h>
58
59/* Currently, only TCP is implemented */
60#if LWIP_TCP && LWIP_CALLBACK_API
61
62/** Specify the idle timeout (in seconds) after that the test fails */
63#ifndef LWIPERF_TCP_MAX_IDLE_SEC
64#define LWIPERF_TCP_MAX_IDLE_SEC 10U
65#endif
66#if LWIPERF_TCP_MAX_IDLE_SEC > 255
67#error LWIPERF_TCP_MAX_IDLE_SEC must fit into an u8_t
68#endif
69
70/** Change this if you don't want to lwiperf to listen to any IP version */
71#ifndef LWIPERF_SERVER_IP_TYPE
72#define LWIPERF_SERVER_IP_TYPE IPADDR_TYPE_ANY
73#endif
74
75/* File internal memory allocation (struct lwiperf_*): this defaults to
76 the heap */
77#ifndef LWIPERF_ALLOC
78#define LWIPERF_ALLOC(type) mem_malloc(sizeof(type))
79#define LWIPERF_FREE(type, item) mem_free(item)
80#endif
81
82/** If this is 1, check that received data has the correct format */
83#ifndef LWIPERF_CHECK_RX_DATA
84#define LWIPERF_CHECK_RX_DATA 0
85#endif
86
87/** This is the Iperf settings struct sent from the client */
88typedef struct _lwiperf_settings {
89#define LWIPERF_FLAGS_ANSWER_TEST 0x80000000
90#define LWIPERF_FLAGS_ANSWER_NOW 0x00000001
91 u32_t flags;
92 u32_t num_threads; /* unused for now */
93 u32_t remote_port;
94 u32_t buffer_len; /* unused for now */
95 u32_t win_band; /* TCP window / UDP rate: unused for now */
96 u32_t amount; /* pos. value: bytes?; neg. values: time (unit is 10ms: 1/100 second) */
97} lwiperf_settings_t;
98
99/** Basic connection handle */
100struct _lwiperf_state_base;
101typedef struct _lwiperf_state_base lwiperf_state_base_t;
102struct _lwiperf_state_base {
103 /* linked list */
104 lwiperf_state_base_t *next;
105 /* 1=tcp, 0=udp */
106 u8_t tcp;
107 /* 1=server, 0=client */
108 u8_t server;
109 /* master state used to abort sessions (e.g. listener, main client) */
110 lwiperf_state_base_t *related_master_state;
111};
112
113/** Connection handle for a TCP iperf session */
114typedef struct _lwiperf_state_tcp {
115 lwiperf_state_base_t base;
116 struct tcp_pcb *server_pcb;
117 struct tcp_pcb *conn_pcb;
118 u32_t time_started;
119 lwiperf_report_fn report_fn;
120 void *report_arg;
121 u8_t poll_count;
122 u8_t next_num;
123 /* 1=start server when client is closed */
124 u8_t client_tradeoff_mode;
125 u32_t bytes_transferred;
126 lwiperf_settings_t settings;
127 u8_t have_settings_buf;
128 u8_t specific_remote;
129 ip_addr_t remote_addr;
130} lwiperf_state_tcp_t;
131
132/** List of active iperf sessions */
133static lwiperf_state_base_t *lwiperf_all_connections;
134/** A const buffer to send from: we want to measure sending, not copying! */
135static const u8_t lwiperf_txbuf_const[1600] = {
136 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
137 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
138 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
139 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
140 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
141 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
142 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
143 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
144 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
145 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
146 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
147 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
148 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
149 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
150 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
151 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
152 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
153 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
154 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
155 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
156 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
157 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
158 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
159 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
160 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
161 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
162 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
163 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
164 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
165 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
166 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
167 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
168 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
169 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
170 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
171 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
172 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
173 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
174 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
175 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
176};
177
178static err_t lwiperf_tcp_poll(void *arg, struct tcp_pcb *tpcb);
179static void lwiperf_tcp_err(void *arg, err_t err);
180static err_t lwiperf_start_tcp_server_impl(const ip_addr_t *local_addr, u16_t local_port,
181 lwiperf_report_fn report_fn, void *report_arg,
182 lwiperf_state_base_t *related_master_state, lwiperf_state_tcp_t **state);
183
184
185/** Add an iperf session to the 'active' list */
186static void
187lwiperf_list_add(lwiperf_state_base_t *item)
188{
189 item->next = lwiperf_all_connections;
190 lwiperf_all_connections = item;
191}
192
193/** Remove an iperf session from the 'active' list */
194static void
195lwiperf_list_remove(lwiperf_state_base_t *item)
196{
197 lwiperf_state_base_t *prev = NULL;
198 lwiperf_state_base_t *iter;
199 for (iter = lwiperf_all_connections; iter != NULL; prev = iter, iter = iter->next) {
200 if (iter == item) {
201 if (prev == NULL) {
202 lwiperf_all_connections = iter->next;
203 } else {
204 prev->next = iter->next;
205 }
206 /* @debug: ensure this item is listed only once */
207 for (iter = iter->next; iter != NULL; iter = iter->next) {
208 LWIP_ASSERT("duplicate entry", iter != item);
209 }
210 break;
211 }
212 }
213}
214
215static lwiperf_state_base_t *
216lwiperf_list_find(lwiperf_state_base_t *item)
217{
218 lwiperf_state_base_t *iter;
219 for (iter = lwiperf_all_connections; iter != NULL; iter = iter->next) {
220 if (iter == item) {
221 return item;
222 }
223 }
224 return NULL;
225}
226
227/** Call the report function of an iperf tcp session */
228static void
229lwip_tcp_conn_report(lwiperf_state_tcp_t *conn, enum lwiperf_report_type report_type)
230{
231 if ((conn != NULL) && (conn->report_fn != NULL)) {
232 u32_t now, duration_ms, bandwidth_kbitpsec;
233 now = sys_now();
234 duration_ms = now - conn->time_started;
235 if (duration_ms == 0) {
236 bandwidth_kbitpsec = 0;
237 } else {
238 bandwidth_kbitpsec = (conn->bytes_transferred / duration_ms) * 8U;
239 }
240 conn->report_fn(conn->report_arg, report_type,
241 &conn->conn_pcb->local_ip, conn->conn_pcb->local_port,
242 &conn->conn_pcb->remote_ip, conn->conn_pcb->remote_port,
243 conn->bytes_transferred, duration_ms, bandwidth_kbitpsec);
244 }
245}
246
247/** Close an iperf tcp session */
248static void
249lwiperf_tcp_close(lwiperf_state_tcp_t *conn, enum lwiperf_report_type report_type)
250{
251 err_t err;
252
253 lwiperf_list_remove(&conn->base);
254 lwip_tcp_conn_report(conn, report_type);
255 if (conn->conn_pcb != NULL) {
256 tcp_arg(conn->conn_pcb, NULL);
257 tcp_poll(conn->conn_pcb, NULL, 0);
258 tcp_sent(conn->conn_pcb, NULL);
259 tcp_recv(conn->conn_pcb, NULL);
260 tcp_err(conn->conn_pcb, NULL);
261 err = tcp_close(conn->conn_pcb);
262 if (err != ERR_OK) {
263 /* don't want to wait for free memory here... */
264 tcp_abort(conn->conn_pcb);
265 }
266 } else {
267 /* no conn pcb, this is the listener pcb */
268 err = tcp_close(conn->server_pcb);
269 LWIP_ASSERT("error", err == ERR_OK);
270 }
271 LWIPERF_FREE(lwiperf_state_tcp_t, conn);
272}
273
274/** Try to send more data on an iperf tcp session */
275static err_t
276lwiperf_tcp_client_send_more(lwiperf_state_tcp_t *conn)
277{
278 int send_more;
279 err_t err;
280 u16_t txlen;
281 u16_t txlen_max;
282 void *txptr;
283 u8_t apiflags;
284
285 LWIP_ASSERT("conn invalid", (conn != NULL) && conn->base.tcp && (conn->base.server == 0));
286
287 do {
288 send_more = 0;
289 if (conn->settings.amount & PP_HTONL(0x80000000)) {
290 /* this session is time-limited */
291 u32_t now = sys_now();
292 u32_t diff_ms = now - conn->time_started;
293 u32_t time = (u32_t) - (s32_t)lwip_htonl(conn->settings.amount);
294 u32_t time_ms = time * 10;
295 if (diff_ms >= time_ms) {
296 /* time specified by the client is over -> close the connection */
297 lwiperf_tcp_close(conn, LWIPERF_TCP_DONE_CLIENT);
298 return ERR_OK;
299 }
300 } else {
301 /* this session is byte-limited */
302 u32_t amount_bytes = lwip_htonl(conn->settings.amount);
303 /* @todo: this can send up to 1*MSS more than requested... */
304 if (amount_bytes >= conn->bytes_transferred) {
305 /* all requested bytes transferred -> close the connection */
306 lwiperf_tcp_close(conn, LWIPERF_TCP_DONE_CLIENT);
307 return ERR_OK;
308 }
309 }
310
311 if (conn->bytes_transferred < 24) {
312 /* transmit the settings a first time */
313 txptr = &((u8_t *)&conn->settings)[conn->bytes_transferred];
314 txlen_max = (u16_t)(24 - conn->bytes_transferred);
315 apiflags = TCP_WRITE_FLAG_COPY;
316 } else if (conn->bytes_transferred < 48) {
317 /* transmit the settings a second time */
318 txptr = &((u8_t *)&conn->settings)[conn->bytes_transferred - 24];
319 txlen_max = (u16_t)(48 - conn->bytes_transferred);
320 apiflags = TCP_WRITE_FLAG_COPY | TCP_WRITE_FLAG_MORE;
321 send_more = 1;
322 } else {
323 /* transmit data */
324 /* @todo: every x bytes, transmit the settings again */
325 txptr = LWIP_CONST_CAST(void *, &lwiperf_txbuf_const[conn->bytes_transferred % 10]);
326 txlen_max = TCP_MSS;
327 if (conn->bytes_transferred == 48) { /* @todo: fix this for intermediate settings, too */
328 txlen_max = TCP_MSS - 24;
329 }
330 apiflags = 0; /* no copying needed */
331 send_more = 1;
332 }
333 txlen = txlen_max;
334 do {
335 err = tcp_write(conn->conn_pcb, txptr, txlen, apiflags);
336 if (err == ERR_MEM) {
337 txlen /= 2;
338 }
339 } while ((err == ERR_MEM) && (txlen >= (TCP_MSS / 2)));
340
341 if (err == ERR_OK) {
342 conn->bytes_transferred += txlen;
343 } else {
344 send_more = 0;
345 }
346 } while (send_more);
347
348 tcp_output(conn->conn_pcb);
349 return ERR_OK;
350}
351
352/** TCP sent callback, try to send more data */
353static err_t
354lwiperf_tcp_client_sent(void *arg, struct tcp_pcb *tpcb, u16_t len)
355{
356 lwiperf_state_tcp_t *conn = (lwiperf_state_tcp_t *)arg;
357 /* @todo: check 'len' (e.g. to time ACK of all data)? for now, we just send more... */
358 LWIP_ASSERT("invalid conn", conn->conn_pcb == tpcb);
359 LWIP_UNUSED_ARG(tpcb);
360 LWIP_UNUSED_ARG(len);
361
362 conn->poll_count = 0;
363
364 return lwiperf_tcp_client_send_more(conn);
365}
366
367/** TCP connected callback (active connection), send data now */
368static err_t
369lwiperf_tcp_client_connected(void *arg, struct tcp_pcb *tpcb, err_t err)
370{
371 lwiperf_state_tcp_t *conn = (lwiperf_state_tcp_t *)arg;
372 LWIP_ASSERT("invalid conn", conn->conn_pcb == tpcb);
373 LWIP_UNUSED_ARG(tpcb);
374 if (err != ERR_OK) {
375 lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_REMOTE);
376 return ERR_OK;
377 }
378 conn->poll_count = 0;
379 conn->time_started = sys_now();
380 return lwiperf_tcp_client_send_more(conn);
381}
382
383/** Start TCP connection back to the client (either parallel or after the
384 * receive test has finished.
385 */
386static err_t
387lwiperf_tx_start_impl(const ip_addr_t *remote_ip, u16_t remote_port, lwiperf_settings_t *settings, lwiperf_report_fn report_fn,
388 void *report_arg, lwiperf_state_base_t *related_master_state, lwiperf_state_tcp_t **new_conn)
389{
390 err_t err;
391 lwiperf_state_tcp_t *client_conn;
392 struct tcp_pcb *newpcb;
393 ip_addr_t remote_addr;
394
395 LWIP_ASSERT("remote_ip != NULL", remote_ip != NULL);
396 LWIP_ASSERT("remote_ip != NULL", settings != NULL);
397 LWIP_ASSERT("new_conn != NULL", new_conn != NULL);
398 *new_conn = NULL;
399
400 client_conn = (lwiperf_state_tcp_t *)LWIPERF_ALLOC(lwiperf_state_tcp_t);
401 if (client_conn == NULL) {
402 return ERR_MEM;
403 }
404 newpcb = tcp_new_ip_type(IP_GET_TYPE(remote_ip));
405 if (newpcb == NULL) {
406 LWIPERF_FREE(lwiperf_state_tcp_t, client_conn);
407 return ERR_MEM;
408 }
409 memset(client_conn, 0, sizeof(lwiperf_state_tcp_t));
410 client_conn->base.tcp = 1;
411 client_conn->base.related_master_state = related_master_state;
412 client_conn->conn_pcb = newpcb;
413 client_conn->time_started = sys_now(); /* @todo: set this again on 'connected' */
414 client_conn->report_fn = report_fn;
415 client_conn->report_arg = report_arg;
416 client_conn->next_num = 4; /* initial nr is '4' since the header has 24 byte */
417 client_conn->bytes_transferred = 0;
418 memcpy(&client_conn->settings, settings, sizeof(*settings));
419 client_conn->have_settings_buf = 1;
420
421 tcp_arg(newpcb, client_conn);
422 tcp_sent(newpcb, lwiperf_tcp_client_sent);
423 tcp_poll(newpcb, lwiperf_tcp_poll, 2U);
424 tcp_err(newpcb, lwiperf_tcp_err);
425
426 ip_addr_copy(remote_addr, *remote_ip);
427
428 err = tcp_connect(newpcb, &remote_addr, remote_port, lwiperf_tcp_client_connected);
429 if (err != ERR_OK) {
430 lwiperf_tcp_close(client_conn, LWIPERF_TCP_ABORTED_LOCAL);
431 return err;
432 }
433 lwiperf_list_add(&client_conn->base);
434 *new_conn = client_conn;
435 return ERR_OK;
436}
437
438static err_t
439lwiperf_tx_start_passive(lwiperf_state_tcp_t *conn)
440{
441 err_t ret;
442 lwiperf_state_tcp_t *new_conn = NULL;
443 u16_t remote_port = (u16_t)lwip_htonl(conn->settings.remote_port);
444
445 ret = lwiperf_tx_start_impl(&conn->conn_pcb->remote_ip, remote_port, &conn->settings, conn->report_fn, conn->report_arg,
446 conn->base.related_master_state, &new_conn);
447 if (ret == ERR_OK) {
448 LWIP_ASSERT("new_conn != NULL", new_conn != NULL);
449 new_conn->settings.flags = 0; /* prevent the remote side starting back as client again */
450 }
451 return ret;
452}
453
454/** Receive data on an iperf tcp session */
455static err_t
456lwiperf_tcp_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
457{
458 u8_t tmp;
459 u16_t tot_len;
460 u32_t packet_idx;
461 struct pbuf *q;
462 lwiperf_state_tcp_t *conn = (lwiperf_state_tcp_t *)arg;
463
464 LWIP_ASSERT("pcb mismatch", conn->conn_pcb == tpcb);
465 LWIP_UNUSED_ARG(tpcb);
466
467 if (err != ERR_OK) {
468 lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_REMOTE);
469 return ERR_OK;
470 }
471 if (p == NULL) {
472 /* connection closed -> test done */
473 if (conn->settings.flags & PP_HTONL(LWIPERF_FLAGS_ANSWER_TEST)) {
474 if ((conn->settings.flags & PP_HTONL(LWIPERF_FLAGS_ANSWER_NOW)) == 0) {
475 /* client requested transmission after end of test */
476 lwiperf_tx_start_passive(conn);
477 }
478 }
479 lwiperf_tcp_close(conn, LWIPERF_TCP_DONE_SERVER);
480 return ERR_OK;
481 }
482 tot_len = p->tot_len;
483
484 conn->poll_count = 0;
485
486 if ((!conn->have_settings_buf) || ((conn->bytes_transferred - 24) % (1024 * 128) == 0)) {
487 /* wait for 24-byte header */
488 if (p->tot_len < sizeof(lwiperf_settings_t)) {
489 lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_LOCAL_DATAERROR);
490 pbuf_free(p);
491 return ERR_OK;
492 }
493 if (!conn->have_settings_buf) {
494 if (pbuf_copy_partial(p, &conn->settings, sizeof(lwiperf_settings_t), 0) != sizeof(lwiperf_settings_t)) {
495 lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_LOCAL);
496 pbuf_free(p);
497 return ERR_OK;
498 }
499 conn->have_settings_buf = 1;
500 if (conn->settings.flags & PP_HTONL(LWIPERF_FLAGS_ANSWER_TEST)) {
501 if (conn->settings.flags & PP_HTONL(LWIPERF_FLAGS_ANSWER_NOW)) {
502 /* client requested parallel transmission test */
503 err_t err2 = lwiperf_tx_start_passive(conn);
504 if (err2 != ERR_OK) {
505 lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_LOCAL_TXERROR);
506 pbuf_free(p);
507 return ERR_OK;
508 }
509 }
510 }
511 } else {
512 if (conn->settings.flags & PP_HTONL(LWIPERF_FLAGS_ANSWER_TEST)) {
513 if (pbuf_memcmp(p, 0, &conn->settings, sizeof(lwiperf_settings_t)) != 0) {
514 lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_LOCAL_DATAERROR);
515 pbuf_free(p);
516 return ERR_OK;
517 }
518 }
519 }
520 conn->bytes_transferred += sizeof(lwiperf_settings_t);
521 if (conn->bytes_transferred <= 24) {
522 conn->time_started = sys_now();
523 tcp_recved(tpcb, p->tot_len);
524 pbuf_free(p);
525 return ERR_OK;
526 }
527 conn->next_num = 4; /* 24 bytes received... */
528 tmp = pbuf_remove_header(p, 24);
529 LWIP_ASSERT("pbuf_remove_header failed", tmp == 0);
530 LWIP_UNUSED_ARG(tmp); /* for LWIP_NOASSERT */
531 }
532
533 packet_idx = 0;
534 for (q = p; q != NULL; q = q->next) {
535#if LWIPERF_CHECK_RX_DATA
536 const u8_t *payload = (const u8_t *)q->payload;
537 u16_t i;
538 for (i = 0; i < q->len; i++) {
539 u8_t val = payload[i];
540 u8_t num = val - '0';
541 if (num == conn->next_num) {
542 conn->next_num++;
543 if (conn->next_num == 10) {
544 conn->next_num = 0;
545 }
546 } else {
547 lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_LOCAL_DATAERROR);
548 pbuf_free(p);
549 return ERR_OK;
550 }
551 }
552#endif
553 packet_idx += q->len;
554 }
555 LWIP_ASSERT("count mismatch", packet_idx == p->tot_len);
556 conn->bytes_transferred += packet_idx;
557 tcp_recved(tpcb, tot_len);
558 pbuf_free(p);
559 return ERR_OK;
560}
561
562/** Error callback, iperf tcp session aborted */
563static void
564lwiperf_tcp_err(void *arg, err_t err)
565{
566 lwiperf_state_tcp_t *conn = (lwiperf_state_tcp_t *)arg;
567 LWIP_UNUSED_ARG(err);
568 lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_REMOTE);
569}
570
571/** TCP poll callback, try to send more data */
572static err_t
573lwiperf_tcp_poll(void *arg, struct tcp_pcb *tpcb)
574{
575 lwiperf_state_tcp_t *conn = (lwiperf_state_tcp_t *)arg;
576 LWIP_ASSERT("pcb mismatch", conn->conn_pcb == tpcb);
577 LWIP_UNUSED_ARG(tpcb);
578 if (++conn->poll_count >= LWIPERF_TCP_MAX_IDLE_SEC) {
579 lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_LOCAL);
580 return ERR_OK; /* lwiperf_tcp_close frees conn */
581 }
582
583 if (!conn->base.server) {
584 lwiperf_tcp_client_send_more(conn);
585 }
586
587 return ERR_OK;
588}
589
590/** This is called when a new client connects for an iperf tcp session */
591static err_t
592lwiperf_tcp_accept(void *arg, struct tcp_pcb *newpcb, err_t err)
593{
594 lwiperf_state_tcp_t *s, *conn;
595 if ((err != ERR_OK) || (newpcb == NULL) || (arg == NULL)) {
596 return ERR_VAL;
597 }
598
599 s = (lwiperf_state_tcp_t *)arg;
600 LWIP_ASSERT("invalid session", s->base.server);
601 LWIP_ASSERT("invalid listen pcb", s->server_pcb != NULL);
602 LWIP_ASSERT("invalid conn pcb", s->conn_pcb == NULL);
603 if (s->specific_remote) {
604 LWIP_ASSERT("s->base.related_master_state != NULL", s->base.related_master_state != NULL);
605 if (!ip_addr_cmp(&newpcb->remote_ip, &s->remote_addr)) {
606 /* this listener belongs to a client session, and this is not the correct remote */
607 return ERR_VAL;
608 }
609 } else {
610 LWIP_ASSERT("s->base.related_master_state == NULL", s->base.related_master_state == NULL);
611 }
612
613 conn = (lwiperf_state_tcp_t *)LWIPERF_ALLOC(lwiperf_state_tcp_t);
614 if (conn == NULL) {
615 return ERR_MEM;
616 }
617 memset(conn, 0, sizeof(lwiperf_state_tcp_t));
618 conn->base.tcp = 1;
619 conn->base.server = 1;
620 conn->base.related_master_state = &s->base;
621 conn->conn_pcb = newpcb;
622 conn->time_started = sys_now();
623 conn->report_fn = s->report_fn;
624 conn->report_arg = s->report_arg;
625
626 /* setup the tcp rx connection */
627 tcp_arg(newpcb, conn);
628 tcp_recv(newpcb, lwiperf_tcp_recv);
629 tcp_poll(newpcb, lwiperf_tcp_poll, 2U);
630 tcp_err(conn->conn_pcb, lwiperf_tcp_err);
631
632 if (s->specific_remote) {
633 /* this listener belongs to a client, so make the client the master of the newly created connection */
634 conn->base.related_master_state = s->base.related_master_state;
635 /* if dual mode or (tradeoff mode AND client is done): close the listener */
636 if (!s->client_tradeoff_mode || !lwiperf_list_find(s->base.related_master_state)) {
637 /* prevent report when closing: this is expected */
638 s->report_fn = NULL;
639 lwiperf_tcp_close(s, LWIPERF_TCP_ABORTED_LOCAL);
640 }
641 }
642 lwiperf_list_add(&conn->base);
643 return ERR_OK;
644}
645
646/**
647 * @ingroup iperf
648 * Start a TCP iperf server on the default TCP port (5001) and listen for
649 * incoming connections from iperf clients.
650 *
651 * @returns a connection handle that can be used to abort the server
652 * by calling @ref lwiperf_abort()
653 */
654void *
655lwiperf_start_tcp_server_default(lwiperf_report_fn report_fn, void *report_arg)
656{
657 return lwiperf_start_tcp_server(IP_ADDR_ANY, LWIPERF_TCP_PORT_DEFAULT,
658 report_fn, report_arg);
659}
660
661/**
662 * @ingroup iperf
663 * Start a TCP iperf server on a specific IP address and port and listen for
664 * incoming connections from iperf clients.
665 *
666 * @returns a connection handle that can be used to abort the server
667 * by calling @ref lwiperf_abort()
668 */
669void *
670lwiperf_start_tcp_server(const ip_addr_t *local_addr, u16_t local_port,
671 lwiperf_report_fn report_fn, void *report_arg)
672{
673 err_t err;
674 lwiperf_state_tcp_t *state = NULL;
675
676 err = lwiperf_start_tcp_server_impl(local_addr, local_port, report_fn, report_arg,
677 NULL, &state);
678 if (err == ERR_OK) {
679 return state;
680 }
681 return NULL;
682}
683
684static err_t lwiperf_start_tcp_server_impl(const ip_addr_t *local_addr, u16_t local_port,
685 lwiperf_report_fn report_fn, void *report_arg,
686 lwiperf_state_base_t *related_master_state, lwiperf_state_tcp_t **state)
687{
688 err_t err;
689 struct tcp_pcb *pcb;
690 lwiperf_state_tcp_t *s;
691
692 LWIP_ASSERT_CORE_LOCKED();
693
694 LWIP_ASSERT("state != NULL", state != NULL);
695
696 if (local_addr == NULL) {
697 return ERR_ARG;
698 }
699
700 s = (lwiperf_state_tcp_t *)LWIPERF_ALLOC(lwiperf_state_tcp_t);
701 if (s == NULL) {
702 return ERR_MEM;
703 }
704 memset(s, 0, sizeof(lwiperf_state_tcp_t));
705 s->base.tcp = 1;
706 s->base.server = 1;
707 s->base.related_master_state = related_master_state;
708 s->report_fn = report_fn;
709 s->report_arg = report_arg;
710
711 pcb = tcp_new_ip_type(LWIPERF_SERVER_IP_TYPE);
712 if (pcb == NULL) {
713 return ERR_MEM;
714 }
715 err = tcp_bind(pcb, local_addr, local_port);
716 if (err != ERR_OK) {
717 return err;
718 }
719 s->server_pcb = tcp_listen_with_backlog(pcb, 1);
720 if (s->server_pcb == NULL) {
721 if (pcb != NULL) {
722 tcp_close(pcb);
723 }
724 LWIPERF_FREE(lwiperf_state_tcp_t, s);
725 return ERR_MEM;
726 }
727 pcb = NULL;
728
729 tcp_arg(s->server_pcb, s);
730 tcp_accept(s->server_pcb, lwiperf_tcp_accept);
731
732 lwiperf_list_add(&s->base);
733 *state = s;
734 return ERR_OK;
735}
736
737/**
738 * @ingroup iperf
739 * Start a TCP iperf client to the default TCP port (5001).
740 *
741 * @returns a connection handle that can be used to abort the client
742 * by calling @ref lwiperf_abort()
743 */
744void* lwiperf_start_tcp_client_default(const ip_addr_t* remote_addr,
745 lwiperf_report_fn report_fn, void* report_arg)
746{
747 return lwiperf_start_tcp_client(remote_addr, LWIPERF_TCP_PORT_DEFAULT, LWIPERF_CLIENT,
748 report_fn, report_arg);
749}
750
751/**
752 * @ingroup iperf
753 * Start a TCP iperf client to a specific IP address and port.
754 *
755 * @returns a connection handle that can be used to abort the client
756 * by calling @ref lwiperf_abort()
757 */
758void* lwiperf_start_tcp_client(const ip_addr_t* remote_addr, u16_t remote_port,
759 enum lwiperf_client_type type, lwiperf_report_fn report_fn, void* report_arg)
760{
761 err_t ret;
762 lwiperf_settings_t settings;
763 lwiperf_state_tcp_t *state = NULL;
764
765 memset(&settings, 0, sizeof(settings));
766 switch (type) {
767 case LWIPERF_CLIENT:
768 /* Unidirectional tx only test */
769 settings.flags = 0;
770 break;
771 case LWIPERF_DUAL:
772 /* Do a bidirectional test simultaneously */
773 settings.flags = htonl(LWIPERF_FLAGS_ANSWER_TEST | LWIPERF_FLAGS_ANSWER_NOW);
774 break;
775 case LWIPERF_TRADEOFF:
776 /* Do a bidirectional test individually */
777 settings.flags = htonl(LWIPERF_FLAGS_ANSWER_TEST);
778 break;
779 default:
780 /* invalid argument */
781 return NULL;
782 }
783 settings.num_threads = htonl(1);
784 settings.remote_port = htonl(LWIPERF_TCP_PORT_DEFAULT);
785 /* TODO: implement passing duration/amount of bytes to transfer */
786 settings.amount = htonl((u32_t)-1000);
787
788 ret = lwiperf_tx_start_impl(remote_addr, remote_port, &settings, report_fn, report_arg, NULL, &state);
789 if (ret == ERR_OK) {
790 LWIP_ASSERT("state != NULL", state != NULL);
791 if (type != LWIPERF_CLIENT) {
792 /* start corresponding server now */
793 lwiperf_state_tcp_t *server = NULL;
794 ret = lwiperf_start_tcp_server_impl(&state->conn_pcb->local_ip, LWIPERF_TCP_PORT_DEFAULT,
795 report_fn, report_arg, (lwiperf_state_base_t *)state, &server);
796 if (ret != ERR_OK) {
797 /* starting server failed, abort client */
798 lwiperf_abort(state);
799 return NULL;
800 }
801 /* make this server accept one connection only */
802 server->specific_remote = 1;
803 server->remote_addr = state->conn_pcb->remote_ip;
804 if (type == LWIPERF_TRADEOFF) {
805 /* tradeoff means that the remote host connects only after the client is done,
806 so keep the listen pcb open until the client is done */
807 server->client_tradeoff_mode = 1;
808 }
809 }
810 return state;
811 }
812 return NULL;
813}
814
815/**
816 * @ingroup iperf
817 * Abort an iperf session (handle returned by lwiperf_start_tcp_server*())
818 */
819void
820lwiperf_abort(void *lwiperf_session)
821{
822 lwiperf_state_base_t *i, *dealloc, *last = NULL;
823
824 LWIP_ASSERT_CORE_LOCKED();
825
826 for (i = lwiperf_all_connections; i != NULL; ) {
827 if ((i == lwiperf_session) || (i->related_master_state == lwiperf_session)) {
828 dealloc = i;
829 i = i->next;
830 if (last != NULL) {
831 last->next = i;
832 }
833 LWIPERF_FREE(lwiperf_state_tcp_t, dealloc); /* @todo: type? */
834 } else {
835 last = i;
836 i = i->next;
837 }
838 }
839}
840
841#endif /* LWIP_TCP && LWIP_CALLBACK_API */
Note: See TracBrowser for help on using the repository browser.