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 */
842