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