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