Kenji Arai / mbed-os_TYBLE16

Dependents:   TYBLE16_simple_data_logger TYBLE16_MP3_Air

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers lwip_lwiperf.c Source File

lwip_lwiperf.c

Go to the documentation of this file.
00001 /**
00002  * @file
00003  * lwIP iPerf server implementation
00004  */
00005 
00006 /**
00007  * @defgroup iperf Iperf server
00008  * @ingroup apps
00009  *
00010  * This is a simple performance measuring client/server to check your bandwith using
00011  * iPerf2 on a PC as server/client.
00012  * It is currently a minimal implementation providing a TCP client/server only.
00013  *
00014  * @todo:
00015  * - implement UDP mode
00016  * - protect combined sessions handling (via 'related_master_state') against reallocation
00017  *   (this is a pointer address, currently, so if the same memory is allocated again,
00018  *    session pairs (tx/rx) can be confused on reallocation)
00019  */
00020 
00021 /*
00022  * Copyright (c) 2014 Simon Goldschmidt
00023  * All rights reserved.
00024  *
00025  * Redistribution and use in source and binary forms, with or without modification,
00026  * are permitted provided that the following conditions are met:
00027  *
00028  * 1. Redistributions of source code must retain the above copyright notice,
00029  *    this list of conditions and the following disclaimer.
00030  * 2. Redistributions in binary form must reproduce the above copyright notice,
00031  *    this list of conditions and the following disclaimer in the documentation
00032  *    and/or other materials provided with the distribution.
00033  * 3. The name of the author may not be used to endorse or promote products
00034  *    derived from this software without specific prior written permission.
00035  *
00036  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
00037  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
00038  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
00039  * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
00040  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
00041  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00042  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00043  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
00044  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
00045  * OF SUCH DAMAGE.
00046  *
00047  * This file is part of the lwIP TCP/IP stack.
00048  *
00049  * Author: Simon Goldschmidt
00050  */
00051 
00052 #include "lwip/apps/lwiperf.h"
00053 
00054 #include "lwip/tcp.h"
00055 #include "lwip/sys.h"
00056 
00057 #include <string.h>
00058 
00059 /* Currently, only TCP is implemented */
00060 #if LWIP_TCP && LWIP_CALLBACK_API
00061 
00062 /** Specify the idle timeout (in seconds) after that the test fails */
00063 #ifndef LWIPERF_TCP_MAX_IDLE_SEC
00064 #define LWIPERF_TCP_MAX_IDLE_SEC    10U
00065 #endif
00066 #if LWIPERF_TCP_MAX_IDLE_SEC > 255
00067 #error LWIPERF_TCP_MAX_IDLE_SEC must fit into an u8_t
00068 #endif
00069 
00070 /** Change this if you don't want to lwiperf to listen to any IP version */
00071 #ifndef LWIPERF_SERVER_IP_TYPE
00072 #define LWIPERF_SERVER_IP_TYPE      IPADDR_TYPE_ANY
00073 #endif
00074 
00075 /* File internal memory allocation (struct lwiperf_*): this defaults to
00076    the heap */
00077 #ifndef LWIPERF_ALLOC
00078 #define LWIPERF_ALLOC(type)         mem_malloc(sizeof(type))
00079 #define LWIPERF_FREE(type, item)    mem_free(item)
00080 #endif
00081 
00082 /** If this is 1, check that received data has the correct format */
00083 #ifndef LWIPERF_CHECK_RX_DATA
00084 #define LWIPERF_CHECK_RX_DATA       0
00085 #endif
00086 
00087 /** This is the Iperf settings struct sent from the client */
00088 typedef struct _lwiperf_settings {
00089 #define LWIPERF_FLAGS_ANSWER_TEST 0x80000000
00090 #define LWIPERF_FLAGS_ANSWER_NOW  0x00000001
00091   u32_t flags;
00092   u32_t num_threads; /* unused for now */
00093   u32_t remote_port;
00094   u32_t buffer_len; /* unused for now */
00095   u32_t win_band; /* TCP window / UDP rate: unused for now */
00096   u32_t amount; /* pos. value: bytes?; neg. values: time (unit is 10ms: 1/100 second) */
00097 } lwiperf_settings_t;
00098 
00099 /** Basic connection handle */
00100 struct _lwiperf_state_base;
00101 typedef struct _lwiperf_state_base lwiperf_state_base_t;
00102 struct _lwiperf_state_base {
00103   /* linked list */
00104   lwiperf_state_base_t *next;
00105   /* 1=tcp, 0=udp */
00106   u8_t tcp;
00107   /* 1=server, 0=client */
00108   u8_t server;
00109   /* master state used to abort sessions (e.g. listener, main client) */
00110   lwiperf_state_base_t *related_master_state;
00111 };
00112 
00113 /** Connection handle for a TCP iperf session */
00114 typedef struct _lwiperf_state_tcp {
00115   lwiperf_state_base_t base;
00116   struct tcp_pcb *server_pcb;
00117   struct tcp_pcb *conn_pcb;
00118   u32_t time_started;
00119   lwiperf_report_fn report_fn;
00120   void *report_arg;
00121   u8_t poll_count;
00122   u8_t next_num;
00123   /* 1=start server when client is closed */
00124   u8_t client_tradeoff_mode;
00125   u32_t bytes_transferred;
00126   lwiperf_settings_t settings;
00127   u8_t have_settings_buf;
00128   u8_t specific_remote;
00129   ip_addr_t remote_addr;
00130 } lwiperf_state_tcp_t;
00131 
00132 /** List of active iperf sessions */
00133 static lwiperf_state_base_t *lwiperf_all_connections;
00134 /** A const buffer to send from: we want to measure sending, not copying! */
00135 static const u8_t lwiperf_txbuf_const[1600] = {
00136   '0', '1', '2', '3', '4', '5', '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',
00137   '0', '1', '2', '3', '4', '5', '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',
00138   '0', '1', '2', '3', '4', '5', '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',
00139   '0', '1', '2', '3', '4', '5', '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',
00140   '0', '1', '2', '3', '4', '5', '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',
00141   '0', '1', '2', '3', '4', '5', '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',
00142   '0', '1', '2', '3', '4', '5', '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',
00143   '0', '1', '2', '3', '4', '5', '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',
00144   '0', '1', '2', '3', '4', '5', '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',
00145   '0', '1', '2', '3', '4', '5', '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',
00146   '0', '1', '2', '3', '4', '5', '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',
00147   '0', '1', '2', '3', '4', '5', '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',
00148   '0', '1', '2', '3', '4', '5', '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',
00149   '0', '1', '2', '3', '4', '5', '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',
00150   '0', '1', '2', '3', '4', '5', '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',
00151   '0', '1', '2', '3', '4', '5', '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',
00152   '0', '1', '2', '3', '4', '5', '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',
00153   '0', '1', '2', '3', '4', '5', '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',
00154   '0', '1', '2', '3', '4', '5', '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',
00155   '0', '1', '2', '3', '4', '5', '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',
00156   '0', '1', '2', '3', '4', '5', '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',
00157   '0', '1', '2', '3', '4', '5', '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',
00158   '0', '1', '2', '3', '4', '5', '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',
00159   '0', '1', '2', '3', '4', '5', '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',
00160   '0', '1', '2', '3', '4', '5', '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',
00161   '0', '1', '2', '3', '4', '5', '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',
00162   '0', '1', '2', '3', '4', '5', '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',
00163   '0', '1', '2', '3', '4', '5', '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',
00164   '0', '1', '2', '3', '4', '5', '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',
00165   '0', '1', '2', '3', '4', '5', '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',
00166   '0', '1', '2', '3', '4', '5', '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',
00167   '0', '1', '2', '3', '4', '5', '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',
00168   '0', '1', '2', '3', '4', '5', '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',
00169   '0', '1', '2', '3', '4', '5', '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',
00170   '0', '1', '2', '3', '4', '5', '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',
00171   '0', '1', '2', '3', '4', '5', '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',
00172   '0', '1', '2', '3', '4', '5', '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',
00173   '0', '1', '2', '3', '4', '5', '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',
00174   '0', '1', '2', '3', '4', '5', '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',
00175   '0', '1', '2', '3', '4', '5', '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',
00176 };
00177 
00178 static err_t lwiperf_tcp_poll(void *arg, struct tcp_pcb *tpcb);
00179 static void lwiperf_tcp_err(void *arg, err_t err);
00180 static err_t lwiperf_start_tcp_server_impl(const ip_addr_t *local_addr, u16_t local_port,
00181                                            lwiperf_report_fn report_fn, void *report_arg,
00182                                            lwiperf_state_base_t *related_master_state, lwiperf_state_tcp_t **state);
00183 
00184 
00185 /** Add an iperf session to the 'active' list */
00186 static void
00187 lwiperf_list_add(lwiperf_state_base_t *item)
00188 {
00189   item->next = lwiperf_all_connections;
00190   lwiperf_all_connections = item;
00191 }
00192 
00193 /** Remove an iperf session from the 'active' list */
00194 static void
00195 lwiperf_list_remove(lwiperf_state_base_t *item)
00196 {
00197   lwiperf_state_base_t *prev = NULL;
00198   lwiperf_state_base_t *iter;
00199   for (iter = lwiperf_all_connections; iter != NULL; prev = iter, iter = iter->next) {
00200     if (iter == item) {
00201       if (prev == NULL) {
00202         lwiperf_all_connections = iter->next;
00203       } else {
00204         prev->next = iter->next;
00205       }
00206       /* @debug: ensure this item is listed only once */
00207       for (iter = iter->next; iter != NULL; iter = iter->next) {
00208         LWIP_ASSERT("duplicate entry", iter != item);
00209       }
00210       break;
00211     }
00212   }
00213 }
00214 
00215 static lwiperf_state_base_t *
00216 lwiperf_list_find(lwiperf_state_base_t *item)
00217 {
00218   lwiperf_state_base_t *iter;
00219   for (iter = lwiperf_all_connections; iter != NULL; iter = iter->next) {
00220     if (iter == item) {
00221       return item;
00222     }
00223   }
00224   return NULL;
00225 }
00226 
00227 /** Call the report function of an iperf tcp session */
00228 static void
00229 lwip_tcp_conn_report(lwiperf_state_tcp_t *conn, enum lwiperf_report_type report_type)
00230 {
00231   if ((conn != NULL) && (conn->report_fn != NULL)) {
00232     u32_t now, duration_ms, bandwidth_kbitpsec;
00233     now = sys_now();
00234     duration_ms = now - conn->time_started;
00235     if (duration_ms == 0) {
00236       bandwidth_kbitpsec = 0;
00237     } else {
00238       bandwidth_kbitpsec = (conn->bytes_transferred / duration_ms) * 8U;
00239     }
00240     conn->report_fn(conn->report_arg, report_type,
00241                     &conn->conn_pcb->local_ip, conn->conn_pcb->local_port,
00242                     &conn->conn_pcb->remote_ip, conn->conn_pcb->remote_port,
00243                     conn->bytes_transferred, duration_ms, bandwidth_kbitpsec);
00244   }
00245 }
00246 
00247 /** Close an iperf tcp session */
00248 static void
00249 lwiperf_tcp_close(lwiperf_state_tcp_t *conn, enum lwiperf_report_type report_type)
00250 {
00251   err_t err;
00252 
00253   lwiperf_list_remove(&conn->base);
00254   lwip_tcp_conn_report(conn, report_type);
00255   if (conn->conn_pcb != NULL) {
00256     tcp_arg(conn->conn_pcb, NULL);
00257     tcp_poll(conn->conn_pcb, NULL, 0);
00258     tcp_sent(conn->conn_pcb, NULL);
00259     tcp_recv(conn->conn_pcb, NULL);
00260     tcp_err(conn->conn_pcb, NULL);
00261     err = tcp_close(conn->conn_pcb);
00262     if (err != ERR_OK) {
00263       /* don't want to wait for free memory here... */
00264       tcp_abort(conn->conn_pcb);
00265     }
00266   } else {
00267     /* no conn pcb, this is the listener pcb */
00268     err = tcp_close(conn->server_pcb);
00269     LWIP_ASSERT("error", err == ERR_OK);
00270   }
00271   LWIPERF_FREE(lwiperf_state_tcp_t, conn);
00272 }
00273 
00274 /** Try to send more data on an iperf tcp session */
00275 static err_t
00276 lwiperf_tcp_client_send_more(lwiperf_state_tcp_t *conn)
00277 {
00278   int send_more;
00279   err_t err;
00280   u16_t txlen;
00281   u16_t txlen_max;
00282   void *txptr;
00283   u8_t apiflags;
00284 
00285   LWIP_ASSERT("conn invalid", (conn != NULL) && conn->base.tcp && (conn->base.server == 0));
00286 
00287   do {
00288     send_more = 0;
00289     if (conn->settings.amount & PP_HTONL(0x80000000)) {
00290       /* this session is time-limited */
00291       u32_t now = sys_now();
00292       u32_t diff_ms = now - conn->time_started;
00293       u32_t time = (u32_t) - (s32_t)lwip_htonl(conn->settings.amount);
00294       u32_t time_ms = time * 10;
00295       if (diff_ms >= time_ms) {
00296         /* time specified by the client is over -> close the connection */
00297         lwiperf_tcp_close(conn, LWIPERF_TCP_DONE_CLIENT);
00298         return ERR_OK;
00299       }
00300     } else {
00301       /* this session is byte-limited */
00302       u32_t amount_bytes = lwip_htonl(conn->settings.amount);
00303       /* @todo: this can send up to 1*MSS more than requested... */
00304       if (amount_bytes >= conn->bytes_transferred) {
00305         /* all requested bytes transferred -> close the connection */
00306         lwiperf_tcp_close(conn, LWIPERF_TCP_DONE_CLIENT);
00307         return ERR_OK;
00308       }
00309     }
00310 
00311     if (conn->bytes_transferred < 24) {
00312       /* transmit the settings a first time */
00313       txptr = &((u8_t *)&conn->settings)[conn->bytes_transferred];
00314       txlen_max = (u16_t)(24 - conn->bytes_transferred);
00315       apiflags = TCP_WRITE_FLAG_COPY;
00316     } else if (conn->bytes_transferred < 48) {
00317       /* transmit the settings a second time */
00318       txptr = &((u8_t *)&conn->settings)[conn->bytes_transferred - 24];
00319       txlen_max = (u16_t)(48 - conn->bytes_transferred);
00320       apiflags = TCP_WRITE_FLAG_COPY | TCP_WRITE_FLAG_MORE;
00321       send_more = 1;
00322     } else {
00323       /* transmit data */
00324       /* @todo: every x bytes, transmit the settings again */
00325       txptr = LWIP_CONST_CAST(void *, &lwiperf_txbuf_const[conn->bytes_transferred % 10]);
00326       txlen_max = TCP_MSS;
00327       if (conn->bytes_transferred == 48) { /* @todo: fix this for intermediate settings, too */
00328         txlen_max = TCP_MSS - 24;
00329       }
00330       apiflags = 0; /* no copying needed */
00331       send_more = 1;
00332     }
00333     txlen = txlen_max;
00334     do {
00335       err = tcp_write(conn->conn_pcb, txptr, txlen, apiflags);
00336       if (err ==  ERR_MEM) {
00337         txlen /= 2;
00338       }
00339     } while ((err == ERR_MEM) && (txlen >= (TCP_MSS / 2)));
00340 
00341     if (err == ERR_OK) {
00342       conn->bytes_transferred += txlen;
00343     } else {
00344       send_more = 0;
00345     }
00346   } while (send_more);
00347 
00348   tcp_output(conn->conn_pcb);
00349   return ERR_OK;
00350 }
00351 
00352 /** TCP sent callback, try to send more data */
00353 static err_t
00354 lwiperf_tcp_client_sent(void *arg, struct tcp_pcb *tpcb, u16_t len)
00355 {
00356   lwiperf_state_tcp_t *conn = (lwiperf_state_tcp_t *)arg;
00357   /* @todo: check 'len' (e.g. to time ACK of all data)? for now, we just send more... */
00358   LWIP_ASSERT("invalid conn", conn->conn_pcb == tpcb);
00359   LWIP_UNUSED_ARG(tpcb);
00360   LWIP_UNUSED_ARG(len);
00361 
00362   conn->poll_count = 0;
00363 
00364   return lwiperf_tcp_client_send_more(conn);
00365 }
00366 
00367 /** TCP connected callback (active connection), send data now */
00368 static err_t
00369 lwiperf_tcp_client_connected(void *arg, struct tcp_pcb *tpcb, err_t err)
00370 {
00371   lwiperf_state_tcp_t *conn = (lwiperf_state_tcp_t *)arg;
00372   LWIP_ASSERT("invalid conn", conn->conn_pcb == tpcb);
00373   LWIP_UNUSED_ARG(tpcb);
00374   if (err != ERR_OK) {
00375     lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_REMOTE);
00376     return ERR_OK;
00377   }
00378   conn->poll_count = 0;
00379   conn->time_started = sys_now();
00380   return lwiperf_tcp_client_send_more(conn);
00381 }
00382 
00383 /** Start TCP connection back to the client (either parallel or after the
00384  * receive test has finished.
00385  */
00386 static err_t
00387 lwiperf_tx_start_impl(const ip_addr_t *remote_ip, u16_t remote_port, lwiperf_settings_t *settings, lwiperf_report_fn report_fn,
00388                       void *report_arg, lwiperf_state_base_t *related_master_state, lwiperf_state_tcp_t **new_conn)
00389 {
00390   err_t err;
00391   lwiperf_state_tcp_t *client_conn;
00392   struct tcp_pcb *newpcb;
00393   ip_addr_t remote_addr;
00394 
00395   LWIP_ASSERT("remote_ip != NULL", remote_ip != NULL);
00396   LWIP_ASSERT("remote_ip != NULL", settings != NULL);
00397   LWIP_ASSERT("new_conn != NULL", new_conn != NULL);
00398   *new_conn = NULL;
00399 
00400   client_conn = (lwiperf_state_tcp_t *)LWIPERF_ALLOC(lwiperf_state_tcp_t);
00401   if (client_conn == NULL) {
00402     return ERR_MEM;
00403   }
00404   newpcb = tcp_new_ip_type(IP_GET_TYPE(remote_ip));
00405   if (newpcb == NULL) {
00406     LWIPERF_FREE(lwiperf_state_tcp_t, client_conn);
00407     return ERR_MEM;
00408   }
00409   memset(client_conn, 0, sizeof(lwiperf_state_tcp_t));
00410   client_conn->base.tcp = 1;
00411   client_conn->base.related_master_state = related_master_state;
00412   client_conn->conn_pcb = newpcb;
00413   client_conn->time_started = sys_now(); /* @todo: set this again on 'connected' */
00414   client_conn->report_fn = report_fn;
00415   client_conn->report_arg = report_arg;
00416   client_conn->next_num = 4; /* initial nr is '4' since the header has 24 byte */
00417   client_conn->bytes_transferred = 0;
00418   memcpy(&client_conn->settings, settings, sizeof(*settings));
00419   client_conn->have_settings_buf = 1;
00420 
00421   tcp_arg(newpcb, client_conn);
00422   tcp_sent(newpcb, lwiperf_tcp_client_sent);
00423   tcp_poll(newpcb, lwiperf_tcp_poll, 2U);
00424   tcp_err(newpcb, lwiperf_tcp_err);
00425 
00426   ip_addr_copy(remote_addr, *remote_ip);
00427 
00428   err = tcp_connect(newpcb, &remote_addr, remote_port, lwiperf_tcp_client_connected);
00429   if (err != ERR_OK) {
00430     lwiperf_tcp_close(client_conn, LWIPERF_TCP_ABORTED_LOCAL);
00431     return err;
00432   }
00433   lwiperf_list_add(&client_conn->base);
00434   *new_conn = client_conn;
00435   return ERR_OK;
00436 }
00437 
00438 static err_t
00439 lwiperf_tx_start_passive(lwiperf_state_tcp_t *conn)
00440 {
00441   err_t ret;
00442   lwiperf_state_tcp_t *new_conn = NULL;
00443   u16_t remote_port = (u16_t)lwip_htonl(conn->settings.remote_port);
00444 
00445   ret = lwiperf_tx_start_impl(&conn->conn_pcb->remote_ip, remote_port, &conn->settings, conn->report_fn, conn->report_arg,
00446     conn->base.related_master_state, &new_conn);
00447   if (ret == ERR_OK) {
00448     LWIP_ASSERT("new_conn != NULL", new_conn != NULL);
00449     new_conn->settings.flags = 0; /* prevent the remote side starting back as client again */
00450   }
00451   return ret;
00452 }
00453 
00454 /** Receive data on an iperf tcp session */
00455 static err_t
00456 lwiperf_tcp_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
00457 {
00458   u8_t tmp;
00459   u16_t tot_len;
00460   u32_t packet_idx;
00461   struct pbuf *q;
00462   lwiperf_state_tcp_t *conn = (lwiperf_state_tcp_t *)arg;
00463 
00464   LWIP_ASSERT("pcb mismatch", conn->conn_pcb == tpcb);
00465   LWIP_UNUSED_ARG(tpcb);
00466 
00467   if (err != ERR_OK) {
00468     lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_REMOTE);
00469     return ERR_OK;
00470   }
00471   if (p == NULL) {
00472     /* connection closed -> test done */
00473     if (conn->settings.flags & PP_HTONL(LWIPERF_FLAGS_ANSWER_TEST)) {
00474       if ((conn->settings.flags & PP_HTONL(LWIPERF_FLAGS_ANSWER_NOW)) == 0) {
00475         /* client requested transmission after end of test */
00476         lwiperf_tx_start_passive(conn);
00477       }
00478     }
00479     lwiperf_tcp_close(conn, LWIPERF_TCP_DONE_SERVER);
00480     return ERR_OK;
00481   }
00482   tot_len = p->tot_len;
00483 
00484   conn->poll_count = 0;
00485 
00486   if ((!conn->have_settings_buf) || ((conn->bytes_transferred - 24) % (1024 * 128) == 0)) {
00487     /* wait for 24-byte header */
00488     if (p->tot_len < sizeof(lwiperf_settings_t)) {
00489       lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_LOCAL_DATAERROR);
00490       pbuf_free(p);
00491       return ERR_OK;
00492     }
00493     if (!conn->have_settings_buf) {
00494       if (pbuf_copy_partial(p, &conn->settings, sizeof(lwiperf_settings_t), 0) != sizeof(lwiperf_settings_t)) {
00495         lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_LOCAL);
00496         pbuf_free(p);
00497         return ERR_OK;
00498       }
00499       conn->have_settings_buf = 1;
00500       if (conn->settings.flags & PP_HTONL(LWIPERF_FLAGS_ANSWER_TEST)) {
00501         if (conn->settings.flags & PP_HTONL(LWIPERF_FLAGS_ANSWER_NOW)) {
00502           /* client requested parallel transmission test */
00503           err_t err2 = lwiperf_tx_start_passive(conn);
00504           if (err2 != ERR_OK) {
00505             lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_LOCAL_TXERROR);
00506             pbuf_free(p);
00507             return ERR_OK;
00508           }
00509         }
00510       }
00511     } else {
00512       if (conn->settings.flags & PP_HTONL(LWIPERF_FLAGS_ANSWER_TEST)) {
00513         if (pbuf_memcmp(p, 0, &conn->settings, sizeof(lwiperf_settings_t)) != 0) {
00514           lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_LOCAL_DATAERROR);
00515           pbuf_free(p);
00516           return ERR_OK;
00517         }
00518       }
00519     }
00520     conn->bytes_transferred += sizeof(lwiperf_settings_t);
00521     if (conn->bytes_transferred <= 24) {
00522       conn->time_started = sys_now();
00523       tcp_recved(tpcb, p->tot_len);
00524       pbuf_free(p);
00525       return ERR_OK;
00526     }
00527     conn->next_num = 4; /* 24 bytes received... */
00528     tmp = pbuf_remove_header(p, 24);
00529     LWIP_ASSERT("pbuf_remove_header failed", tmp == 0);
00530     LWIP_UNUSED_ARG(tmp); /* for LWIP_NOASSERT */
00531   }
00532 
00533   packet_idx = 0;
00534   for (q = p; q != NULL; q = q->next) {
00535 #if LWIPERF_CHECK_RX_DATA
00536     const u8_t *payload = (const u8_t *)q->payload;
00537     u16_t i;
00538     for (i = 0; i < q->len; i++) {
00539       u8_t val = payload[i];
00540       u8_t num = val - '0';
00541       if (num == conn->next_num) {
00542         conn->next_num++;
00543         if (conn->next_num == 10) {
00544           conn->next_num = 0;
00545         }
00546       } else {
00547         lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_LOCAL_DATAERROR);
00548         pbuf_free(p);
00549         return ERR_OK;
00550       }
00551     }
00552 #endif
00553     packet_idx += q->len;
00554   }
00555   LWIP_ASSERT("count mismatch", packet_idx == p->tot_len);
00556   conn->bytes_transferred += packet_idx;
00557   tcp_recved(tpcb, tot_len);
00558   pbuf_free(p);
00559   return ERR_OK;
00560 }
00561 
00562 /** Error callback, iperf tcp session aborted */
00563 static void
00564 lwiperf_tcp_err(void *arg, err_t err)
00565 {
00566   lwiperf_state_tcp_t *conn = (lwiperf_state_tcp_t *)arg;
00567   LWIP_UNUSED_ARG(err);
00568   lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_REMOTE);
00569 }
00570 
00571 /** TCP poll callback, try to send more data */
00572 static err_t
00573 lwiperf_tcp_poll(void *arg, struct tcp_pcb *tpcb)
00574 {
00575   lwiperf_state_tcp_t *conn = (lwiperf_state_tcp_t *)arg;
00576   LWIP_ASSERT("pcb mismatch", conn->conn_pcb == tpcb);
00577   LWIP_UNUSED_ARG(tpcb);
00578   if (++conn->poll_count >= LWIPERF_TCP_MAX_IDLE_SEC) {
00579     lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_LOCAL);
00580     return ERR_OK; /* lwiperf_tcp_close frees conn */
00581   }
00582 
00583   if (!conn->base.server) {
00584     lwiperf_tcp_client_send_more(conn);
00585   }
00586 
00587   return ERR_OK;
00588 }
00589 
00590 /** This is called when a new client connects for an iperf tcp session */
00591 static err_t
00592 lwiperf_tcp_accept(void *arg, struct tcp_pcb *newpcb, err_t err)
00593 {
00594   lwiperf_state_tcp_t *s, *conn;
00595   if ((err != ERR_OK) || (newpcb == NULL) || (arg == NULL)) {
00596     return ERR_VAL;
00597   }
00598 
00599   s = (lwiperf_state_tcp_t *)arg;
00600   LWIP_ASSERT("invalid session", s->base.server);
00601   LWIP_ASSERT("invalid listen pcb", s->server_pcb != NULL);
00602   LWIP_ASSERT("invalid conn pcb", s->conn_pcb == NULL);
00603   if (s->specific_remote) {
00604     LWIP_ASSERT("s->base.related_master_state != NULL", s->base.related_master_state != NULL);
00605     if (!ip_addr_cmp(&newpcb->remote_ip, &s->remote_addr)) {
00606       /* this listener belongs to a client session, and this is not the correct remote */
00607       return ERR_VAL;
00608     }
00609   } else {
00610     LWIP_ASSERT("s->base.related_master_state == NULL", s->base.related_master_state == NULL);
00611   }
00612 
00613   conn = (lwiperf_state_tcp_t *)LWIPERF_ALLOC(lwiperf_state_tcp_t);
00614   if (conn == NULL) {
00615     return ERR_MEM;
00616   }
00617   memset(conn, 0, sizeof(lwiperf_state_tcp_t));
00618   conn->base.tcp = 1;
00619   conn->base.server = 1;
00620   conn->base.related_master_state = &s->base;
00621   conn->conn_pcb = newpcb;
00622   conn->time_started = sys_now();
00623   conn->report_fn = s->report_fn;
00624   conn->report_arg = s->report_arg;
00625 
00626   /* setup the tcp rx connection */
00627   tcp_arg(newpcb, conn);
00628   tcp_recv(newpcb, lwiperf_tcp_recv);
00629   tcp_poll(newpcb, lwiperf_tcp_poll, 2U);
00630   tcp_err(conn->conn_pcb, lwiperf_tcp_err);
00631 
00632   if (s->specific_remote) {
00633     /* this listener belongs to a client, so make the client the master of the newly created connection */
00634     conn->base.related_master_state = s->base.related_master_state;
00635     /* if dual mode or (tradeoff mode AND client is done): close the listener */
00636     if (!s->client_tradeoff_mode || !lwiperf_list_find(s->base.related_master_state)) {
00637       /* prevent report when closing: this is expected */
00638       s->report_fn = NULL;
00639       lwiperf_tcp_close(s, LWIPERF_TCP_ABORTED_LOCAL);
00640     }
00641   }
00642   lwiperf_list_add(&conn->base);
00643   return ERR_OK;
00644 }
00645 
00646 /**
00647  * @ingroup iperf
00648  * Start a TCP iperf server on the default TCP port (5001) and listen for
00649  * incoming connections from iperf clients.
00650  *
00651  * @returns a connection handle that can be used to abort the server
00652  *          by calling @ref lwiperf_abort()
00653  */
00654 void *
00655 lwiperf_start_tcp_server_default(lwiperf_report_fn report_fn, void *report_arg)
00656 {
00657   return lwiperf_start_tcp_server(IP_ADDR_ANY, LWIPERF_TCP_PORT_DEFAULT,
00658                                   report_fn, report_arg);
00659 }
00660 
00661 /**
00662  * @ingroup iperf
00663  * Start a TCP iperf server on a specific IP address and port and listen for
00664  * incoming connections from iperf clients.
00665  *
00666  * @returns a connection handle that can be used to abort the server
00667  *          by calling @ref lwiperf_abort()
00668  */
00669 void *
00670 lwiperf_start_tcp_server(const ip_addr_t *local_addr, u16_t local_port,
00671                          lwiperf_report_fn report_fn, void *report_arg)
00672 {
00673   err_t err;
00674   lwiperf_state_tcp_t *state = NULL;
00675 
00676   err = lwiperf_start_tcp_server_impl(local_addr, local_port, report_fn, report_arg,
00677     NULL, &state);
00678   if (err == ERR_OK) {
00679     return state;
00680   }
00681   return NULL;
00682 }
00683 
00684 static err_t lwiperf_start_tcp_server_impl(const ip_addr_t *local_addr, u16_t local_port,
00685                                            lwiperf_report_fn report_fn, void *report_arg,
00686                                            lwiperf_state_base_t *related_master_state, lwiperf_state_tcp_t **state)
00687 {
00688   err_t err;
00689   struct tcp_pcb *pcb;
00690   lwiperf_state_tcp_t *s;
00691 
00692   LWIP_ASSERT_CORE_LOCKED();
00693 
00694   LWIP_ASSERT("state != NULL", state != NULL);
00695 
00696   if (local_addr == NULL) {
00697     return ERR_ARG;
00698   }
00699 
00700   s = (lwiperf_state_tcp_t *)LWIPERF_ALLOC(lwiperf_state_tcp_t);
00701   if (s == NULL) {
00702     return ERR_MEM;
00703   }
00704   memset(s, 0, sizeof(lwiperf_state_tcp_t));
00705   s->base.tcp = 1;
00706   s->base.server = 1;
00707   s->base.related_master_state = related_master_state;
00708   s->report_fn = report_fn;
00709   s->report_arg = report_arg;
00710 
00711   pcb = tcp_new_ip_type(LWIPERF_SERVER_IP_TYPE);
00712   if (pcb == NULL) {
00713     return ERR_MEM;
00714   }
00715   err = tcp_bind(pcb, local_addr, local_port);
00716   if (err != ERR_OK) {
00717     return err;
00718   }
00719   s->server_pcb = tcp_listen_with_backlog(pcb, 1);
00720   if (s->server_pcb == NULL) {
00721     if (pcb != NULL) {
00722       tcp_close(pcb);
00723     }
00724     LWIPERF_FREE(lwiperf_state_tcp_t, s);
00725     return ERR_MEM;
00726   }
00727   pcb = NULL;
00728 
00729   tcp_arg(s->server_pcb, s);
00730   tcp_accept(s->server_pcb, lwiperf_tcp_accept);
00731 
00732   lwiperf_list_add(&s->base);
00733   *state = s;
00734   return ERR_OK;
00735 }
00736 
00737 /**
00738  * @ingroup iperf
00739  * Start a TCP iperf client to the default TCP port (5001).
00740  *
00741  * @returns a connection handle that can be used to abort the client
00742  *          by calling @ref lwiperf_abort()
00743  */
00744 void* lwiperf_start_tcp_client_default(const ip_addr_t* remote_addr,
00745                                lwiperf_report_fn report_fn, void* report_arg)
00746 {
00747   return lwiperf_start_tcp_client(remote_addr, LWIPERF_TCP_PORT_DEFAULT, LWIPERF_CLIENT,
00748                                   report_fn, report_arg);
00749 }
00750 
00751 /**
00752  * @ingroup iperf
00753  * Start a TCP iperf client to a specific IP address and port.
00754  *
00755  * @returns a connection handle that can be used to abort the client
00756  *          by calling @ref lwiperf_abort()
00757  */
00758 void* lwiperf_start_tcp_client(const ip_addr_t* remote_addr, u16_t remote_port,
00759   enum lwiperf_client_type type, lwiperf_report_fn report_fn, void* report_arg)
00760 {
00761   err_t ret;
00762   lwiperf_settings_t settings;
00763   lwiperf_state_tcp_t *state = NULL;
00764 
00765   memset(&settings, 0, sizeof(settings));
00766   switch (type) {
00767   case LWIPERF_CLIENT:
00768     /* Unidirectional tx only test */
00769     settings.flags = 0;
00770     break;
00771   case LWIPERF_DUAL:
00772     /* Do a bidirectional test simultaneously */
00773     settings.flags = htonl(LWIPERF_FLAGS_ANSWER_TEST | LWIPERF_FLAGS_ANSWER_NOW);
00774     break;
00775   case LWIPERF_TRADEOFF:
00776     /* Do a bidirectional test individually */
00777     settings.flags = htonl(LWIPERF_FLAGS_ANSWER_TEST);
00778     break;
00779   default:
00780     /* invalid argument */
00781     return NULL;
00782   }
00783   settings.num_threads = htonl(1);
00784   settings.remote_port = htonl(LWIPERF_TCP_PORT_DEFAULT);
00785   /* TODO: implement passing duration/amount of bytes to transfer */
00786   settings.amount = htonl((u32_t)-1000);
00787 
00788   ret = lwiperf_tx_start_impl(remote_addr, remote_port, &settings, report_fn, report_arg, NULL, &state);
00789   if (ret == ERR_OK) {
00790     LWIP_ASSERT("state != NULL", state != NULL);
00791     if (type != LWIPERF_CLIENT) {
00792       /* start corresponding server now */
00793       lwiperf_state_tcp_t *server = NULL;
00794       ret = lwiperf_start_tcp_server_impl(&state->conn_pcb->local_ip, LWIPERF_TCP_PORT_DEFAULT,
00795         report_fn, report_arg, (lwiperf_state_base_t *)state, &server);
00796       if (ret != ERR_OK) {
00797         /* starting server failed, abort client */
00798         lwiperf_abort(state);
00799         return NULL;
00800       }
00801       /* make this server accept one connection only */
00802       server->specific_remote = 1;
00803       server->remote_addr = state->conn_pcb->remote_ip;
00804       if (type == LWIPERF_TRADEOFF) {
00805         /* tradeoff means that the remote host connects only after the client is done,
00806            so keep the listen pcb open until the client is done */
00807         server->client_tradeoff_mode = 1;
00808       }
00809     }
00810     return state;
00811   }
00812   return NULL;
00813 }
00814 
00815 /**
00816  * @ingroup iperf
00817  * Abort an iperf session (handle returned by lwiperf_start_tcp_server*())
00818  */
00819 void
00820 lwiperf_abort(void *lwiperf_session)
00821 {
00822   lwiperf_state_base_t *i, *dealloc, *last = NULL;
00823 
00824   LWIP_ASSERT_CORE_LOCKED();
00825 
00826   for (i = lwiperf_all_connections; i != NULL; ) {
00827     if ((i == lwiperf_session) || (i->related_master_state == lwiperf_session)) {
00828       dealloc = i;
00829       i = i->next;
00830       if (last != NULL) {
00831         last->next = i;
00832       }
00833       LWIPERF_FREE(lwiperf_state_tcp_t, dealloc); /* @todo: type? */
00834     } else {
00835       last = i;
00836       i = i->next;
00837     }
00838   }
00839 }
00840 
00841 #endif /* LWIP_TCP && LWIP_CALLBACK_API */