Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependents: TYBLE16_simple_data_logger TYBLE16_MP3_Air
lwip_lwiperf.c
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 */
Generated on Tue Jul 12 2022 13:54:29 by
