• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 */
88 typedef 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 */
100 struct _lwiperf_state_base;
101 typedef struct _lwiperf_state_base lwiperf_state_base_t;
102 struct _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 */
114 typedef 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 */
133 static lwiperf_state_base_t *lwiperf_all_connections;
134 /** A const buffer to send from: we want to measure sending, not copying! */
135 static 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 
178 static err_t lwiperf_tcp_poll(void *arg, struct tcp_pcb *tpcb);
179 static void lwiperf_tcp_err(void *arg, err_t err);
180 static 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 */
186 static void
lwiperf_list_add(lwiperf_state_base_t * item)187 lwiperf_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 */
194 static void
lwiperf_list_remove(lwiperf_state_base_t * item)195 lwiperf_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 
215 static lwiperf_state_base_t *
lwiperf_list_find(lwiperf_state_base_t * item)216 lwiperf_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 */
228 static void
lwip_tcp_conn_report(lwiperf_state_tcp_t * conn,enum lwiperf_report_type report_type)229 lwip_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 */
248 static void
lwiperf_tcp_close(lwiperf_state_tcp_t * conn,enum lwiperf_report_type report_type)249 lwiperf_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 */
275 static err_t
lwiperf_tcp_client_send_more(lwiperf_state_tcp_t * conn)276 lwiperf_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 */
353 static err_t
lwiperf_tcp_client_sent(void * arg,struct tcp_pcb * tpcb,u16_t len)354 lwiperf_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 */
368 static err_t
lwiperf_tcp_client_connected(void * arg,struct tcp_pcb * tpcb,err_t err)369 lwiperf_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  */
386 static err_t
lwiperf_tx_start_impl(const ip_addr_t * remote_ip,u16_t remote_port,lwiperf_settings_t * settings,lwiperf_report_fn report_fn,void * report_arg,lwiperf_state_base_t * related_master_state,lwiperf_state_tcp_t ** new_conn)387 lwiperf_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 
438 static err_t
lwiperf_tx_start_passive(lwiperf_state_tcp_t * conn)439 lwiperf_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 */
455 static err_t
lwiperf_tcp_recv(void * arg,struct tcp_pcb * tpcb,struct pbuf * p,err_t err)456 lwiperf_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 */
563 static void
lwiperf_tcp_err(void * arg,err_t err)564 lwiperf_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 */
572 static err_t
lwiperf_tcp_poll(void * arg,struct tcp_pcb * tpcb)573 lwiperf_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 */
591 static err_t
lwiperf_tcp_accept(void * arg,struct tcp_pcb * newpcb,err_t err)592 lwiperf_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  */
654 void *
lwiperf_start_tcp_server_default(lwiperf_report_fn report_fn,void * report_arg)655 lwiperf_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  */
669 void *
lwiperf_start_tcp_server(const ip_addr_t * local_addr,u16_t local_port,lwiperf_report_fn report_fn,void * report_arg)670 lwiperf_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 
lwiperf_start_tcp_server_impl(const ip_addr_t * local_addr,u16_t local_port,lwiperf_report_fn report_fn,void * report_arg,lwiperf_state_base_t * related_master_state,lwiperf_state_tcp_t ** state)684 static 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  */
lwiperf_start_tcp_client_default(const ip_addr_t * remote_addr,lwiperf_report_fn report_fn,void * report_arg)744 void* 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  */
lwiperf_start_tcp_client(const ip_addr_t * remote_addr,u16_t remote_port,enum lwiperf_client_type type,lwiperf_report_fn report_fn,void * report_arg)758 void* 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  */
819 void
lwiperf_abort(void * lwiperf_session)820 lwiperf_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 */
842