1 | #include "tcp_helper.h"
|
---|
2 |
|
---|
3 | #include "lwip/priv/tcp_priv.h"
|
---|
4 | #include "lwip/stats.h"
|
---|
5 | #include "lwip/pbuf.h"
|
---|
6 | #include "lwip/inet_chksum.h"
|
---|
7 | #include "lwip/ip_addr.h"
|
---|
8 |
|
---|
9 | #if !LWIP_STATS || !TCP_STATS || !MEMP_STATS
|
---|
10 | #error "This tests needs TCP- and MEMP-statistics enabled"
|
---|
11 | #endif
|
---|
12 |
|
---|
13 | const ip_addr_t test_local_ip = IPADDR4_INIT_BYTES(192, 168, 1, 1);
|
---|
14 | const ip_addr_t test_remote_ip = IPADDR4_INIT_BYTES(192, 168, 1, 2);
|
---|
15 | const ip_addr_t test_netmask = IPADDR4_INIT_BYTES(255, 255, 255, 0);
|
---|
16 |
|
---|
17 | /** Remove all pcbs on the given list. */
|
---|
18 | static void
|
---|
19 | tcp_remove(struct tcp_pcb* pcb_list)
|
---|
20 | {
|
---|
21 | struct tcp_pcb *pcb = pcb_list;
|
---|
22 | struct tcp_pcb *pcb2;
|
---|
23 |
|
---|
24 | while(pcb != NULL) {
|
---|
25 | pcb2 = pcb;
|
---|
26 | pcb = pcb->next;
|
---|
27 | tcp_abort(pcb2);
|
---|
28 | }
|
---|
29 | }
|
---|
30 |
|
---|
31 | /** Remove all pcbs on listen-, active- and time-wait-list (bound- isn't exported). */
|
---|
32 | void
|
---|
33 | tcp_remove_all(void)
|
---|
34 | {
|
---|
35 | tcp_remove(tcp_listen_pcbs.pcbs);
|
---|
36 | tcp_remove(tcp_bound_pcbs);
|
---|
37 | tcp_remove(tcp_active_pcbs);
|
---|
38 | tcp_remove(tcp_tw_pcbs);
|
---|
39 | fail_unless(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0);
|
---|
40 | fail_unless(MEMP_STATS_GET(used, MEMP_TCP_PCB_LISTEN) == 0);
|
---|
41 | fail_unless(MEMP_STATS_GET(used, MEMP_TCP_SEG) == 0);
|
---|
42 | fail_unless(MEMP_STATS_GET(used, MEMP_PBUF_POOL) == 0);
|
---|
43 | }
|
---|
44 |
|
---|
45 | /** Create a TCP segment usable for passing to tcp_input */
|
---|
46 | static struct pbuf*
|
---|
47 | tcp_create_segment_wnd(ip_addr_t* src_ip, ip_addr_t* dst_ip,
|
---|
48 | u16_t src_port, u16_t dst_port, void* data, size_t data_len,
|
---|
49 | u32_t seqno, u32_t ackno, u8_t headerflags, u16_t wnd)
|
---|
50 | {
|
---|
51 | struct pbuf *p, *q;
|
---|
52 | struct ip_hdr* iphdr;
|
---|
53 | struct tcp_hdr* tcphdr;
|
---|
54 | u16_t pbuf_len = (u16_t)(sizeof(struct ip_hdr) + sizeof(struct tcp_hdr) + data_len);
|
---|
55 | LWIP_ASSERT("data_len too big", data_len <= 0xFFFF);
|
---|
56 |
|
---|
57 | p = pbuf_alloc(PBUF_RAW, pbuf_len, PBUF_POOL);
|
---|
58 | EXPECT_RETNULL(p != NULL);
|
---|
59 | /* first pbuf must be big enough to hold the headers */
|
---|
60 | EXPECT_RETNULL(p->len >= (sizeof(struct ip_hdr) + sizeof(struct tcp_hdr)));
|
---|
61 | if (data_len > 0) {
|
---|
62 | /* first pbuf must be big enough to hold at least 1 data byte, too */
|
---|
63 | EXPECT_RETNULL(p->len > (sizeof(struct ip_hdr) + sizeof(struct tcp_hdr)));
|
---|
64 | }
|
---|
65 |
|
---|
66 | for(q = p; q != NULL; q = q->next) {
|
---|
67 | memset(q->payload, 0, q->len);
|
---|
68 | }
|
---|
69 |
|
---|
70 | iphdr = (struct ip_hdr*)p->payload;
|
---|
71 | /* fill IP header */
|
---|
72 | iphdr->dest.addr = ip_2_ip4(dst_ip)->addr;
|
---|
73 | iphdr->src.addr = ip_2_ip4(src_ip)->addr;
|
---|
74 | IPH_VHL_SET(iphdr, 4, IP_HLEN / 4);
|
---|
75 | IPH_TOS_SET(iphdr, 0);
|
---|
76 | IPH_LEN_SET(iphdr, htons(p->tot_len));
|
---|
77 | IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN));
|
---|
78 |
|
---|
79 | /* let p point to TCP header */
|
---|
80 | pbuf_header(p, -(s16_t)sizeof(struct ip_hdr));
|
---|
81 |
|
---|
82 | tcphdr = (struct tcp_hdr*)p->payload;
|
---|
83 | tcphdr->src = htons(src_port);
|
---|
84 | tcphdr->dest = htons(dst_port);
|
---|
85 | tcphdr->seqno = htonl(seqno);
|
---|
86 | tcphdr->ackno = htonl(ackno);
|
---|
87 | TCPH_HDRLEN_SET(tcphdr, sizeof(struct tcp_hdr)/4);
|
---|
88 | TCPH_FLAGS_SET(tcphdr, headerflags);
|
---|
89 | tcphdr->wnd = htons(wnd);
|
---|
90 |
|
---|
91 | if (data_len > 0) {
|
---|
92 | /* let p point to TCP data */
|
---|
93 | pbuf_header(p, -(s16_t)sizeof(struct tcp_hdr));
|
---|
94 | /* copy data */
|
---|
95 | pbuf_take(p, data, (u16_t)data_len);
|
---|
96 | /* let p point to TCP header again */
|
---|
97 | pbuf_header(p, sizeof(struct tcp_hdr));
|
---|
98 | }
|
---|
99 |
|
---|
100 | /* calculate checksum */
|
---|
101 |
|
---|
102 | tcphdr->chksum = ip_chksum_pseudo(p,
|
---|
103 | IP_PROTO_TCP, p->tot_len, src_ip, dst_ip);
|
---|
104 |
|
---|
105 | pbuf_header(p, sizeof(struct ip_hdr));
|
---|
106 |
|
---|
107 | return p;
|
---|
108 | }
|
---|
109 |
|
---|
110 | /** Create a TCP segment usable for passing to tcp_input */
|
---|
111 | struct pbuf*
|
---|
112 | tcp_create_segment(ip_addr_t* src_ip, ip_addr_t* dst_ip,
|
---|
113 | u16_t src_port, u16_t dst_port, void* data, size_t data_len,
|
---|
114 | u32_t seqno, u32_t ackno, u8_t headerflags)
|
---|
115 | {
|
---|
116 | return tcp_create_segment_wnd(src_ip, dst_ip, src_port, dst_port, data,
|
---|
117 | data_len, seqno, ackno, headerflags, TCP_WND);
|
---|
118 | }
|
---|
119 |
|
---|
120 | /** Create a TCP segment usable for passing to tcp_input
|
---|
121 | * - IP-addresses, ports, seqno and ackno are taken from pcb
|
---|
122 | * - seqno and ackno can be altered with an offset
|
---|
123 | */
|
---|
124 | struct pbuf*
|
---|
125 | tcp_create_rx_segment(struct tcp_pcb* pcb, void* data, size_t data_len, u32_t seqno_offset,
|
---|
126 | u32_t ackno_offset, u8_t headerflags)
|
---|
127 | {
|
---|
128 | return tcp_create_segment(&pcb->remote_ip, &pcb->local_ip, pcb->remote_port, pcb->local_port,
|
---|
129 | data, data_len, pcb->rcv_nxt + seqno_offset, pcb->lastack + ackno_offset, headerflags);
|
---|
130 | }
|
---|
131 |
|
---|
132 | /** Create a TCP segment usable for passing to tcp_input
|
---|
133 | * - IP-addresses, ports, seqno and ackno are taken from pcb
|
---|
134 | * - seqno and ackno can be altered with an offset
|
---|
135 | * - TCP window can be adjusted
|
---|
136 | */
|
---|
137 | struct pbuf* tcp_create_rx_segment_wnd(struct tcp_pcb* pcb, void* data, size_t data_len,
|
---|
138 | u32_t seqno_offset, u32_t ackno_offset, u8_t headerflags, u16_t wnd)
|
---|
139 | {
|
---|
140 | return tcp_create_segment_wnd(&pcb->remote_ip, &pcb->local_ip, pcb->remote_port, pcb->local_port,
|
---|
141 | data, data_len, pcb->rcv_nxt + seqno_offset, pcb->lastack + ackno_offset, headerflags, wnd);
|
---|
142 | }
|
---|
143 |
|
---|
144 | /** Safely bring a tcp_pcb into the requested state */
|
---|
145 | void
|
---|
146 | tcp_set_state(struct tcp_pcb* pcb, enum tcp_state state, const ip_addr_t* local_ip,
|
---|
147 | const ip_addr_t* remote_ip, u16_t local_port, u16_t remote_port)
|
---|
148 | {
|
---|
149 | u32_t iss;
|
---|
150 |
|
---|
151 | /* @todo: are these all states? */
|
---|
152 | /* @todo: remove from previous list */
|
---|
153 | pcb->state = state;
|
---|
154 |
|
---|
155 | iss = tcp_next_iss(pcb);
|
---|
156 | pcb->snd_wl2 = iss;
|
---|
157 | pcb->snd_nxt = iss;
|
---|
158 | pcb->lastack = iss;
|
---|
159 | pcb->snd_lbb = iss;
|
---|
160 |
|
---|
161 | if (state == ESTABLISHED) {
|
---|
162 | TCP_REG(&tcp_active_pcbs, pcb);
|
---|
163 | ip_addr_copy(pcb->local_ip, *local_ip);
|
---|
164 | pcb->local_port = local_port;
|
---|
165 | ip_addr_copy(pcb->remote_ip, *remote_ip);
|
---|
166 | pcb->remote_port = remote_port;
|
---|
167 | } else if(state == LISTEN) {
|
---|
168 | TCP_REG(&tcp_listen_pcbs.pcbs, pcb);
|
---|
169 | ip_addr_copy(pcb->local_ip, *local_ip);
|
---|
170 | pcb->local_port = local_port;
|
---|
171 | } else if(state == TIME_WAIT) {
|
---|
172 | TCP_REG(&tcp_tw_pcbs, pcb);
|
---|
173 | ip_addr_copy(pcb->local_ip, *local_ip);
|
---|
174 | pcb->local_port = local_port;
|
---|
175 | ip_addr_copy(pcb->remote_ip, *remote_ip);
|
---|
176 | pcb->remote_port = remote_port;
|
---|
177 | } else {
|
---|
178 | fail();
|
---|
179 | }
|
---|
180 | }
|
---|
181 |
|
---|
182 | void
|
---|
183 | test_tcp_counters_err(void* arg, err_t err)
|
---|
184 | {
|
---|
185 | struct test_tcp_counters* counters = (struct test_tcp_counters*)arg;
|
---|
186 | EXPECT_RET(arg != NULL);
|
---|
187 | counters->err_calls++;
|
---|
188 | counters->last_err = err;
|
---|
189 | }
|
---|
190 |
|
---|
191 | static void
|
---|
192 | test_tcp_counters_check_rxdata(struct test_tcp_counters* counters, struct pbuf* p)
|
---|
193 | {
|
---|
194 | struct pbuf* q;
|
---|
195 | u32_t i, received;
|
---|
196 | if(counters->expected_data == NULL) {
|
---|
197 | /* no data to compare */
|
---|
198 | return;
|
---|
199 | }
|
---|
200 | EXPECT_RET(counters->recved_bytes + p->tot_len <= counters->expected_data_len);
|
---|
201 | received = counters->recved_bytes;
|
---|
202 | for(q = p; q != NULL; q = q->next) {
|
---|
203 | char *data = (char*)q->payload;
|
---|
204 | for(i = 0; i < q->len; i++) {
|
---|
205 | EXPECT_RET(data[i] == counters->expected_data[received]);
|
---|
206 | received++;
|
---|
207 | }
|
---|
208 | }
|
---|
209 | EXPECT(received == counters->recved_bytes + p->tot_len);
|
---|
210 | }
|
---|
211 |
|
---|
212 | err_t
|
---|
213 | test_tcp_counters_recv(void* arg, struct tcp_pcb* pcb, struct pbuf* p, err_t err)
|
---|
214 | {
|
---|
215 | struct test_tcp_counters* counters = (struct test_tcp_counters*)arg;
|
---|
216 | EXPECT_RETX(arg != NULL, ERR_OK);
|
---|
217 | EXPECT_RETX(pcb != NULL, ERR_OK);
|
---|
218 | EXPECT_RETX(err == ERR_OK, ERR_OK);
|
---|
219 |
|
---|
220 | if (p != NULL) {
|
---|
221 | if (counters->close_calls == 0) {
|
---|
222 | counters->recv_calls++;
|
---|
223 | test_tcp_counters_check_rxdata(counters, p);
|
---|
224 | counters->recved_bytes += p->tot_len;
|
---|
225 | } else {
|
---|
226 | counters->recv_calls_after_close++;
|
---|
227 | counters->recved_bytes_after_close += p->tot_len;
|
---|
228 | }
|
---|
229 | pbuf_free(p);
|
---|
230 | } else {
|
---|
231 | counters->close_calls++;
|
---|
232 | }
|
---|
233 | EXPECT(counters->recv_calls_after_close == 0 && counters->recved_bytes_after_close == 0);
|
---|
234 | return ERR_OK;
|
---|
235 | }
|
---|
236 |
|
---|
237 | /** Allocate a pcb and set up the test_tcp_counters_* callbacks */
|
---|
238 | struct tcp_pcb*
|
---|
239 | test_tcp_new_counters_pcb(struct test_tcp_counters* counters)
|
---|
240 | {
|
---|
241 | struct tcp_pcb* pcb = tcp_new();
|
---|
242 | if (pcb != NULL) {
|
---|
243 | /* set up args and callbacks */
|
---|
244 | tcp_arg(pcb, counters);
|
---|
245 | tcp_recv(pcb, test_tcp_counters_recv);
|
---|
246 | tcp_err(pcb, test_tcp_counters_err);
|
---|
247 | pcb->snd_wnd = TCP_WND;
|
---|
248 | pcb->snd_wnd_max = TCP_WND;
|
---|
249 | }
|
---|
250 | return pcb;
|
---|
251 | }
|
---|
252 |
|
---|
253 | /** Calls tcp_input() after adjusting current_iphdr_dest */
|
---|
254 | void test_tcp_input(struct pbuf *p, struct netif *inp)
|
---|
255 | {
|
---|
256 | struct ip_hdr *iphdr = (struct ip_hdr*)p->payload;
|
---|
257 | /* these lines are a hack, don't use them as an example :-) */
|
---|
258 | ip_addr_copy_from_ip4(*ip_current_dest_addr(), iphdr->dest);
|
---|
259 | ip_addr_copy_from_ip4(*ip_current_src_addr(), iphdr->src);
|
---|
260 | ip_current_netif() = inp;
|
---|
261 | ip_data.current_ip4_header = iphdr;
|
---|
262 |
|
---|
263 | /* since adding IPv6, p->payload must point to tcp header, not ip header */
|
---|
264 | pbuf_header(p, -(s16_t)sizeof(struct ip_hdr));
|
---|
265 |
|
---|
266 | tcp_input(p, inp);
|
---|
267 |
|
---|
268 | ip_addr_set_zero(ip_current_dest_addr());
|
---|
269 | ip_addr_set_zero(ip_current_src_addr());
|
---|
270 | ip_current_netif() = NULL;
|
---|
271 | ip_data.current_ip4_header = NULL;
|
---|
272 | }
|
---|
273 |
|
---|
274 | static err_t test_tcp_netif_output(struct netif *netif, struct pbuf *p,
|
---|
275 | const ip4_addr_t *ipaddr)
|
---|
276 | {
|
---|
277 | struct test_tcp_txcounters *txcounters = (struct test_tcp_txcounters*)netif->state;
|
---|
278 | LWIP_UNUSED_ARG(ipaddr);
|
---|
279 | if (txcounters != NULL)
|
---|
280 | {
|
---|
281 | txcounters->num_tx_calls++;
|
---|
282 | txcounters->num_tx_bytes += p->tot_len;
|
---|
283 | if (txcounters->copy_tx_packets) {
|
---|
284 | struct pbuf *p_copy = pbuf_alloc(PBUF_LINK, p->tot_len, PBUF_RAM);
|
---|
285 | err_t err;
|
---|
286 | EXPECT(p_copy != NULL);
|
---|
287 | err = pbuf_copy(p_copy, p);
|
---|
288 | EXPECT(err == ERR_OK);
|
---|
289 | if (txcounters->tx_packets == NULL) {
|
---|
290 | txcounters->tx_packets = p_copy;
|
---|
291 | } else {
|
---|
292 | pbuf_cat(txcounters->tx_packets, p_copy);
|
---|
293 | }
|
---|
294 | }
|
---|
295 | }
|
---|
296 | return ERR_OK;
|
---|
297 | }
|
---|
298 |
|
---|
299 | void test_tcp_init_netif(struct netif *netif, struct test_tcp_txcounters *txcounters,
|
---|
300 | const ip_addr_t *ip_addr, const ip_addr_t *netmask)
|
---|
301 | {
|
---|
302 | struct netif *n;
|
---|
303 | memset(netif, 0, sizeof(struct netif));
|
---|
304 | if (txcounters != NULL) {
|
---|
305 | memset(txcounters, 0, sizeof(struct test_tcp_txcounters));
|
---|
306 | netif->state = txcounters;
|
---|
307 | }
|
---|
308 | netif->output = test_tcp_netif_output;
|
---|
309 | netif->flags |= NETIF_FLAG_UP | NETIF_FLAG_LINK_UP;
|
---|
310 | ip_addr_copy_from_ip4(netif->netmask, *ip_2_ip4(netmask));
|
---|
311 | ip_addr_copy_from_ip4(netif->ip_addr, *ip_2_ip4(ip_addr));
|
---|
312 | for (n = netif_list; n != NULL; n = n->next) {
|
---|
313 | if (n == netif) {
|
---|
314 | return;
|
---|
315 | }
|
---|
316 | }
|
---|
317 | netif->next = NULL;
|
---|
318 | netif_list = netif;
|
---|
319 | }
|
---|