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 server to check your bandwith using 11 * iPerf2 on a PC as client. 12 * It is currently a minimal implementation providing an IPv4 TCP server only. 13 * 14 * @todo: implement UDP mode and IPv6 15 */ 16 17/* 18 * Copyright (c) 2014 Simon Goldschmidt 19 * All rights reserved. 20 * 21 * Redistribution and use in source and binary forms, with or without modification, 22 * are permitted provided that the following conditions are met: 23 * 24 * 1. Redistributions of source code must retain the above copyright notice, 25 * this list of conditions and the following disclaimer. 26 * 2. Redistributions in binary form must reproduce the above copyright notice, 27 * this list of conditions and the following disclaimer in the documentation 28 * and/or other materials provided with the distribution. 29 * 3. The name of the author may not be used to endorse or promote products 30 * derived from this software without specific prior written permission. 31 * 32 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 33 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 34 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 35 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 36 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 37 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 38 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 39 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 40 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 41 * OF SUCH DAMAGE. 42 * 43 * This file is part of the lwIP TCP/IP stack. 44 * 45 * Author: Simon Goldschmidt 46 */ 47 48#include "lwip/apps/lwiperf.h" 49 50#include "lwip/tcp.h" 51#include "lwip/sys.h" 52 53#include <string.h> 54 55/* Currently, only TCP-over-IPv4 is implemented (does iperf support IPv6 anyway?) */ 56#if LWIP_IPV4 && LWIP_TCP && LWIP_CALLBACK_API 57 58/** Specify the idle timeout (in seconds) after that the test fails */ 59#ifndef LWIPERF_TCP_MAX_IDLE_SEC 60#define LWIPERF_TCP_MAX_IDLE_SEC 10U 61#endif 62#if LWIPERF_TCP_MAX_IDLE_SEC > 255 63#error LWIPERF_TCP_MAX_IDLE_SEC must fit into an u8_t 64#endif 65 66/* File internal memory allocation (struct lwiperf_*): this defaults to 67 the heap */ 68#ifndef LWIPERF_ALLOC 69#define LWIPERF_ALLOC(type) mem_malloc(sizeof(type)) 70#define LWIPERF_FREE(type, item) mem_free(item) 71#endif 72 73/** If this is 1, check that received data has the correct format */ 74#ifndef LWIPERF_CHECK_RX_DATA 75#define LWIPERF_CHECK_RX_DATA 0 76#endif 77 78/** This is the Iperf settings struct sent from the client */ 79typedef struct _lwiperf_settings { 80#define LWIPERF_FLAGS_ANSWER_TEST 0x80000000 81#define LWIPERF_FLAGS_ANSWER_NOW 0x00000001 82 u32_t flags; 83 u32_t num_threads; /* unused for now */ 84 u32_t remote_port; 85 u32_t buffer_len; /* unused for now */ 86 u32_t win_band; /* TCP window / UDP rate: unused for now */ 87 u32_t amount; /* pos. value: bytes?; neg. values: time (unit is 10ms: 1/100 second) */ 88} lwiperf_settings_t; 89 90/** Basic connection handle */ 91struct _lwiperf_state_base; 92typedef struct _lwiperf_state_base lwiperf_state_base_t; 93struct _lwiperf_state_base { 94 /* 1=tcp, 0=udp */ 95 u8_t tcp; 96 /* 1=server, 0=client */ 97 u8_t server; 98 lwiperf_state_base_t* next; 99 lwiperf_state_base_t* related_server_state; 100}; 101 102/** Connection handle for a TCP iperf session */ 103typedef struct _lwiperf_state_tcp { 104 lwiperf_state_base_t base; 105 struct tcp_pcb* server_pcb; 106 struct tcp_pcb* conn_pcb; 107 u32_t time_started; 108 lwiperf_report_fn report_fn; 109 void* report_arg; 110 u8_t poll_count; 111 u8_t next_num; 112 u32_t bytes_transferred; 113 lwiperf_settings_t settings; 114 u8_t have_settings_buf; 115} lwiperf_state_tcp_t; 116 117/** List of active iperf sessions */ 118static lwiperf_state_base_t* lwiperf_all_connections; 119/** A const buffer to send from: we want to measure sending, not copying! */ 120static const u8_t lwiperf_txbuf_const[1600] = { 121 '0','1','2','3','4','5','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', 122 '0','1','2','3','4','5','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', 123 '0','1','2','3','4','5','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', 124 '0','1','2','3','4','5','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', 125 '0','1','2','3','4','5','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', 126 '0','1','2','3','4','5','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', 127 '0','1','2','3','4','5','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', 128 '0','1','2','3','4','5','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', 129 '0','1','2','3','4','5','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', 130 '0','1','2','3','4','5','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', 131 '0','1','2','3','4','5','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', 132 '0','1','2','3','4','5','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', 133 '0','1','2','3','4','5','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', 134 '0','1','2','3','4','5','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', 135 '0','1','2','3','4','5','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', 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}; 162 163static err_t lwiperf_tcp_poll(void *arg, struct tcp_pcb *tpcb); 164static void lwiperf_tcp_err(void *arg, err_t err); 165 166/** Add an iperf session to the 'active' list */ 167static void 168lwiperf_list_add(lwiperf_state_base_t* item) 169{ 170 if (lwiperf_all_connections == NULL) { 171 lwiperf_all_connections = item; 172 } else { 173 item = lwiperf_all_connections; 174 } 175} 176 177/** Remove an iperf session from the 'active' list */ 178static void 179lwiperf_list_remove(lwiperf_state_base_t* item) 180{ 181 lwiperf_state_base_t* prev = NULL; 182 lwiperf_state_base_t* iter; 183 for (iter = lwiperf_all_connections; iter != NULL; prev = iter, iter = iter->next) { 184 if (iter == item) { 185 if (prev == NULL) { 186 lwiperf_all_connections = iter->next; 187 } else { 188 prev->next = item; 189 } 190 /* @debug: ensure this item is listed only once */ 191 for (iter = iter->next; iter != NULL; iter = iter->next) { 192 LWIP_ASSERT("duplicate entry", iter != item); 193 } 194 break; 195 } 196 } 197} 198 199/** Call the report function of an iperf tcp session */ 200static void 201lwip_tcp_conn_report(lwiperf_state_tcp_t* conn, enum lwiperf_report_type report_type) 202{ 203 if ((conn != NULL) && (conn->report_fn != NULL)) { 204 u32_t now, duration_ms, bandwidth_kbitpsec; 205 now = sys_now(); 206 duration_ms = now - conn->time_started; 207 if (duration_ms == 0) { 208 bandwidth_kbitpsec = 0; 209 } else { 210 bandwidth_kbitpsec = (conn->bytes_transferred / duration_ms) * 8U; 211 } 212 conn->report_fn(conn->report_arg, report_type, 213 &conn->conn_pcb->local_ip, conn->conn_pcb->local_port, 214 &conn->conn_pcb->remote_ip, conn->conn_pcb->remote_port, 215 conn->bytes_transferred, duration_ms, bandwidth_kbitpsec); 216 } 217} 218 219/** Close an iperf tcp session */ 220static void 221lwiperf_tcp_close(lwiperf_state_tcp_t* conn, enum lwiperf_report_type report_type) 222{ 223 err_t err; 224 225 lwip_tcp_conn_report(conn, report_type); 226 lwiperf_list_remove(&conn->base); 227 if (conn->conn_pcb != NULL) { 228 tcp_arg(conn->conn_pcb, NULL); 229 tcp_poll(conn->conn_pcb, NULL, 0); 230 tcp_sent(conn->conn_pcb, NULL); 231 tcp_recv(conn->conn_pcb, NULL); 232 tcp_err(conn->conn_pcb, NULL); 233 err = tcp_close(conn->conn_pcb); 234 if (err != ERR_OK) { 235 /* don't want to wait for free memory here... */ 236 tcp_abort(conn->conn_pcb); 237 } 238 } else { 239 /* no conn pcb, this is the server pcb */ 240 err = tcp_close(conn->server_pcb); 241 LWIP_ASSERT("error", err != ERR_OK); 242 } 243 LWIPERF_FREE(lwiperf_state_tcp_t, conn); 244} 245 246/** Try to send more data on an iperf tcp session */ 247static err_t 248lwiperf_tcp_client_send_more(lwiperf_state_tcp_t* conn) 249{ 250 int send_more; 251 err_t err; 252 u16_t txlen; 253 u16_t txlen_max; 254 void* txptr; 255 u8_t apiflags; 256 257 LWIP_ASSERT("conn invalid", (conn != NULL) && conn->base.tcp && (conn->base.server == 0)); 258 259 do { 260 send_more = 0; 261 if (conn->settings.amount & PP_HTONL(0x80000000)) { 262 /* this session is time-limited */ 263 u32_t now = sys_now(); 264 u32_t diff_ms = now - conn->time_started; 265 u32_t time = (u32_t)-(s32_t)lwip_htonl(conn->settings.amount); 266 u32_t time_ms = time * 10; 267 if (diff_ms >= time_ms) { 268 /* time specified by the client is over -> close the connection */ 269 lwiperf_tcp_close(conn, LWIPERF_TCP_DONE_CLIENT); 270 return ERR_OK; 271 } 272 } else { 273 /* this session is byte-limited */ 274 u32_t amount_bytes = lwip_htonl(conn->settings.amount); 275 /* @todo: this can send up to 1*MSS more than requested... */ 276 if (amount_bytes >= conn->bytes_transferred) { 277 /* all requested bytes transferred -> close the connection */ 278 lwiperf_tcp_close(conn, LWIPERF_TCP_DONE_CLIENT); 279 return ERR_OK; 280 } 281 } 282 283 if (conn->bytes_transferred < 24) { 284 /* transmit the settings a first time */ 285 txptr = &((u8_t*)&conn->settings)[conn->bytes_transferred]; 286 txlen_max = (u16_t)(24 - conn->bytes_transferred); 287 apiflags = TCP_WRITE_FLAG_COPY; 288 } else if (conn->bytes_transferred < 48) { 289 /* transmit the settings a second time */ 290 txptr = &((u8_t*)&conn->settings)[conn->bytes_transferred - 24]; 291 txlen_max = (u16_t)(48 - conn->bytes_transferred); 292 apiflags = TCP_WRITE_FLAG_COPY | TCP_WRITE_FLAG_MORE; 293 send_more = 1; 294 } else { 295 /* transmit data */ 296 /* @todo: every x bytes, transmit the settings again */ 297 txptr = LWIP_CONST_CAST(void*, &lwiperf_txbuf_const[conn->bytes_transferred % 10]); 298 txlen_max = TCP_MSS; 299 if (conn->bytes_transferred == 48) { /* @todo: fix this for intermediate settings, too */ 300 txlen_max = TCP_MSS - 24; 301 } 302 apiflags = 0; /* no copying needed */ 303 send_more = 1; 304 } 305 txlen = txlen_max; 306 do { 307 err = tcp_write(conn->conn_pcb, txptr, txlen, apiflags); 308 if (err == ERR_MEM) { 309 txlen /= 2; 310 } 311 } while ((err == ERR_MEM) && (txlen >= (TCP_MSS/2))); 312 313 if (err == ERR_OK) { 314 conn->bytes_transferred += txlen; 315 } else { 316 send_more = 0; 317 } 318 } while(send_more); 319 320 tcp_output(conn->conn_pcb); 321 return ERR_OK; 322} 323 324/** TCP sent callback, try to send more data */ 325static err_t 326lwiperf_tcp_client_sent(void *arg, struct tcp_pcb *tpcb, u16_t len) 327{ 328 lwiperf_state_tcp_t* conn = (lwiperf_state_tcp_t*)arg; 329 /* @todo: check 'len' (e.g. to time ACK of all data)? for now, we just send more... */ 330 LWIP_ASSERT("invalid conn", conn->conn_pcb == tpcb); 331 LWIP_UNUSED_ARG(tpcb); 332 LWIP_UNUSED_ARG(len); 333 334 conn->poll_count = 0; 335 336 return lwiperf_tcp_client_send_more(conn); 337} 338 339/** TCP connected callback (active connection), send data now */ 340static err_t 341lwiperf_tcp_client_connected(void *arg, struct tcp_pcb *tpcb, err_t err) 342{ 343 lwiperf_state_tcp_t* conn = (lwiperf_state_tcp_t*)arg; 344 LWIP_ASSERT("invalid conn", conn->conn_pcb == tpcb); 345 LWIP_UNUSED_ARG(tpcb); 346 if (err != ERR_OK) { 347 lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_REMOTE); 348 return ERR_OK; 349 } 350 conn->poll_count = 0; 351 conn->time_started = sys_now(); 352 return lwiperf_tcp_client_send_more(conn); 353} 354 355/** Start TCP connection back to the client (either parallel or after the 356 * receive test has finished. 357 */ 358static err_t 359lwiperf_tx_start(lwiperf_state_tcp_t* conn) 360{ 361 err_t err; 362 lwiperf_state_tcp_t* client_conn; 363 struct tcp_pcb* newpcb; 364 ip_addr_t remote_addr; 365 u16_t remote_port; 366 367 client_conn = (lwiperf_state_tcp_t*)LWIPERF_ALLOC(lwiperf_state_tcp_t); 368 if (client_conn == NULL) { 369 return ERR_MEM; 370 } 371 newpcb = tcp_new(); 372 if (newpcb == NULL) { 373 LWIPERF_FREE(lwiperf_state_tcp_t, client_conn); 374 return ERR_MEM; 375 } 376 377 MEMCPY(client_conn, conn, sizeof(lwiperf_state_tcp_t)); 378 client_conn->base.server = 0; 379 client_conn->server_pcb = NULL; 380 client_conn->conn_pcb = newpcb; 381 client_conn->time_started = sys_now(); /* @todo: set this again on 'connected' */ 382 client_conn->poll_count = 0; 383 client_conn->next_num = 4; /* initial nr is '4' since the header has 24 byte */ 384 client_conn->bytes_transferred = 0; 385 client_conn->settings.flags = 0; /* prevent the remote side starting back as client again */ 386 387 tcp_arg(newpcb, client_conn); 388 tcp_sent(newpcb, lwiperf_tcp_client_sent); 389 tcp_poll(newpcb, lwiperf_tcp_poll, 2U); 390 tcp_err(newpcb, lwiperf_tcp_err); 391 392 ip_addr_copy(remote_addr, conn->conn_pcb->remote_ip); 393 remote_port = (u16_t)lwip_htonl(client_conn->settings.remote_port); 394 395 err = tcp_connect(newpcb, &remote_addr, remote_port, lwiperf_tcp_client_connected); 396 if (err != ERR_OK) { 397 lwiperf_tcp_close(client_conn, LWIPERF_TCP_ABORTED_LOCAL); 398 return err; 399 } 400 lwiperf_list_add(&client_conn->base); 401 return ERR_OK; 402} 403 404/** Receive data on an iperf tcp session */ 405static err_t 406lwiperf_tcp_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) 407{ 408 u8_t tmp; 409 u16_t tot_len; 410 u32_t packet_idx; 411 struct pbuf* q; 412 lwiperf_state_tcp_t* conn = (lwiperf_state_tcp_t*)arg; 413 414 LWIP_ASSERT("pcb mismatch", conn->conn_pcb == tpcb); 415 LWIP_UNUSED_ARG(tpcb); 416 417 if (err != ERR_OK) { 418 lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_REMOTE); 419 return ERR_OK; 420 } 421 if (p == NULL) { 422 /* connection closed -> test done */ 423 if ((conn->settings.flags & PP_HTONL(LWIPERF_FLAGS_ANSWER_TEST|LWIPERF_FLAGS_ANSWER_NOW)) == 424 PP_HTONL(LWIPERF_FLAGS_ANSWER_TEST)) { 425 /* client requested transmission after end of test */ 426 lwiperf_tx_start(conn); 427 } 428 lwiperf_tcp_close(conn, LWIPERF_TCP_DONE_SERVER); 429 return ERR_OK; 430 } 431 tot_len = p->tot_len; 432 433 conn->poll_count = 0; 434 435 if ((!conn->have_settings_buf) || ((conn->bytes_transferred -24) % (1024*128) == 0)) { 436 /* wait for 24-byte header */ 437 if (p->tot_len < sizeof(lwiperf_settings_t)) { 438 lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_LOCAL_DATAERROR); 439 pbuf_free(p); 440 return ERR_VAL; 441 } 442 if (!conn->have_settings_buf) { 443 if (pbuf_copy_partial(p, &conn->settings, sizeof(lwiperf_settings_t), 0) != sizeof(lwiperf_settings_t)) { 444 lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_LOCAL); 445 pbuf_free(p); 446 return ERR_VAL; 447 } 448 conn->have_settings_buf = 1; 449 if ((conn->settings.flags & PP_HTONL(LWIPERF_FLAGS_ANSWER_TEST|LWIPERF_FLAGS_ANSWER_NOW)) == 450 PP_HTONL(LWIPERF_FLAGS_ANSWER_TEST|LWIPERF_FLAGS_ANSWER_NOW)) { 451 /* client requested parallel transmission test */ 452 err_t err2 = lwiperf_tx_start(conn); 453 if (err2 != ERR_OK) { 454 lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_LOCAL_TXERROR); 455 pbuf_free(p); 456 return err2; 457 } 458 } 459 } else { 460 if (pbuf_memcmp(p, 0, &conn->settings, sizeof(lwiperf_settings_t)) != 0) { 461 lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_LOCAL_DATAERROR); 462 pbuf_free(p); 463 return ERR_VAL; 464 } 465 } 466 conn->bytes_transferred += sizeof(lwiperf_settings_t); 467 if (conn->bytes_transferred <= 24) { 468 conn->time_started = sys_now(); 469 tcp_recved(tpcb, p->tot_len); 470 pbuf_free(p); 471 return ERR_OK; 472 } 473 conn->next_num = 4; /* 24 bytes received... */ 474 tmp = pbuf_header(p, -24); 475 LWIP_ASSERT("pbuf_header failed", tmp == 0); 476 } 477 478 packet_idx = 0; 479 for (q = p; q != NULL; q = q->next) { 480#if LWIPERF_CHECK_RX_DATA 481 const u8_t* payload = (const u8_t*)q->payload; 482 u16_t i; 483 for (i = 0; i < q->len; i++) { 484 u8_t val = payload[i]; 485 u8_t num = val - '0'; 486 if (num == conn->next_num) { 487 conn->next_num++; 488 if (conn->next_num == 10) { 489 conn->next_num = 0; 490 } 491 } else { 492 lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_LOCAL_DATAERROR); 493 pbuf_free(p); 494 return ERR_VAL; 495 } 496 } 497#endif 498 packet_idx += q->len; 499 } 500 LWIP_ASSERT("count mismatch", packet_idx == p->tot_len); 501 conn->bytes_transferred += packet_idx; 502 tcp_recved(tpcb, tot_len); 503 pbuf_free(p); 504 return ERR_OK; 505} 506 507/** Error callback, iperf tcp session aborted */ 508static void 509lwiperf_tcp_err(void *arg, err_t err) 510{ 511 lwiperf_state_tcp_t* conn = (lwiperf_state_tcp_t*)arg; 512 LWIP_UNUSED_ARG(err); 513 lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_REMOTE); 514} 515 516/** TCP poll callback, try to send more data */ 517static err_t 518lwiperf_tcp_poll(void *arg, struct tcp_pcb *tpcb) 519{ 520 lwiperf_state_tcp_t* conn = (lwiperf_state_tcp_t*)arg; 521 LWIP_ASSERT("pcb mismatch", conn->conn_pcb == tpcb); 522 LWIP_UNUSED_ARG(tpcb); 523 if (++conn->poll_count >= LWIPERF_TCP_MAX_IDLE_SEC) { 524 lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_LOCAL); 525 return ERR_OK; /* lwiperf_tcp_close frees conn */ 526 } 527 528 if (!conn->base.server) { 529 lwiperf_tcp_client_send_more(conn); 530 } 531 532 return ERR_OK; 533} 534 535/** This is called when a new client connects for an iperf tcp session */ 536static err_t 537lwiperf_tcp_accept(void *arg, struct tcp_pcb *newpcb, err_t err) 538{ 539 lwiperf_state_tcp_t *s, *conn; 540 if ((err != ERR_OK) || (newpcb == NULL) || (arg == NULL)) { 541 return ERR_VAL; 542 } 543 544 s = (lwiperf_state_tcp_t*)arg; 545 conn = (lwiperf_state_tcp_t*)LWIPERF_ALLOC(lwiperf_state_tcp_t); 546 if (conn == NULL) { 547 return ERR_MEM; 548 } 549 memset(conn, 0, sizeof(lwiperf_state_tcp_t)); 550 conn->base.tcp = 1; 551 conn->base.server = 1; 552 conn->base.related_server_state = &s->base; 553 conn->server_pcb = s->server_pcb; 554 conn->conn_pcb = newpcb; 555 conn->time_started = sys_now(); 556 conn->report_fn = s->report_fn; 557 conn->report_arg = s->report_arg; 558 559 /* setup the tcp rx connection */ 560 tcp_arg(newpcb, conn); 561 tcp_recv(newpcb, lwiperf_tcp_recv); 562 tcp_poll(newpcb, lwiperf_tcp_poll, 2U); 563 tcp_err(conn->conn_pcb, lwiperf_tcp_err); 564 565 lwiperf_list_add(&conn->base); 566 return ERR_OK; 567} 568 569/** 570 * @ingroup iperf 571 * Start a TCP iperf server on the default TCP port (5001) and listen for 572 * incoming connections from iperf clients. 573 * 574 * @returns a connection handle that can be used to abort the server 575 * by calling @ref lwiperf_abort() 576 */ 577void* 578lwiperf_start_tcp_server_default(lwiperf_report_fn report_fn, void* report_arg) 579{ 580 return lwiperf_start_tcp_server(IP_ADDR_ANY, LWIPERF_TCP_PORT_DEFAULT, 581 report_fn, report_arg); 582} 583 584/** 585 * @ingroup iperf 586 * Start a TCP iperf server on a specific IP address and port and listen for 587 * incoming connections from iperf clients. 588 * 589 * @returns a connection handle that can be used to abort the server 590 * by calling @ref lwiperf_abort() 591 */ 592void* 593lwiperf_start_tcp_server(const ip_addr_t* local_addr, u16_t local_port, 594 lwiperf_report_fn report_fn, void* report_arg) 595{ 596 err_t err; 597 struct tcp_pcb* pcb; 598 lwiperf_state_tcp_t* s; 599 600 if (local_addr == NULL) { 601 return NULL; 602 } 603 604 s = (lwiperf_state_tcp_t*)LWIPERF_ALLOC(lwiperf_state_tcp_t); 605 if (s == NULL) { 606 return NULL; 607 } 608 memset(s, 0, sizeof(lwiperf_state_tcp_t)); 609 s->base.tcp = 1; 610 s->base.server = 1; 611 s->report_fn = report_fn; 612 s->report_arg = report_arg; 613 614 pcb = tcp_new(); 615 if (pcb != NULL) { 616 err = tcp_bind(pcb, local_addr, local_port); 617 if (err == ERR_OK) { 618 s->server_pcb = tcp_listen_with_backlog(pcb, 1); 619 } 620 } 621 if (s->server_pcb == NULL) { 622 if (pcb != NULL) { 623 tcp_close(pcb); 624 } 625 LWIPERF_FREE(lwiperf_state_tcp_t, s); 626 return NULL; 627 } 628 pcb = NULL; 629 630 tcp_arg(s->server_pcb, s); 631 tcp_accept(s->server_pcb, lwiperf_tcp_accept); 632 633 lwiperf_list_add(&s->base); 634 return s; 635} 636 637/** 638 * @ingroup iperf 639 * Abort an iperf session (handle returned by lwiperf_start_tcp_server*()) 640 */ 641void 642lwiperf_abort(void* lwiperf_session) 643{ 644 lwiperf_state_base_t* i, *dealloc, *last = NULL; 645 646 for (i = lwiperf_all_connections; i != NULL; ) { 647 if ((i == lwiperf_session) || (i->related_server_state == lwiperf_session)) { 648 dealloc = i; 649 i = i->next; 650 if (last != NULL) { 651 last->next = i; 652 } 653 LWIPERF_FREE(lwiperf_state_tcp_t, dealloc); /* @todo: type? */ 654 } else { 655 last = i; 656 i = i->next; 657 } 658 } 659} 660 661#endif /* LWIP_IPV4 && LWIP_TCP && LWIP_CALLBACK_API */ 662