xref: /minix3/minix/lib/liblwip/dist/test/unit/tcp/test_tcp.c (revision 5d5fbe79c1b60734f34c69330aec5496644e8651)
1*5d5fbe79SDavid van Moolenbroek #include "test_tcp.h"
2*5d5fbe79SDavid van Moolenbroek 
3*5d5fbe79SDavid van Moolenbroek #include "lwip/priv/tcp_priv.h"
4*5d5fbe79SDavid van Moolenbroek #include "lwip/stats.h"
5*5d5fbe79SDavid van Moolenbroek #include "tcp_helper.h"
6*5d5fbe79SDavid van Moolenbroek #include "lwip/inet_chksum.h"
7*5d5fbe79SDavid van Moolenbroek 
8*5d5fbe79SDavid van Moolenbroek #ifdef _MSC_VER
9*5d5fbe79SDavid van Moolenbroek #pragma warning(disable: 4307) /* we explicitly wrap around TCP seqnos */
10*5d5fbe79SDavid van Moolenbroek #endif
11*5d5fbe79SDavid van Moolenbroek 
12*5d5fbe79SDavid van Moolenbroek #if !LWIP_STATS || !TCP_STATS || !MEMP_STATS
13*5d5fbe79SDavid van Moolenbroek #error "This tests needs TCP- and MEMP-statistics enabled"
14*5d5fbe79SDavid van Moolenbroek #endif
15*5d5fbe79SDavid van Moolenbroek #if TCP_SND_BUF <= TCP_WND
16*5d5fbe79SDavid van Moolenbroek #error "This tests needs TCP_SND_BUF to be > TCP_WND"
17*5d5fbe79SDavid van Moolenbroek #endif
18*5d5fbe79SDavid van Moolenbroek 
19*5d5fbe79SDavid van Moolenbroek static u8_t test_tcp_timer;
20*5d5fbe79SDavid van Moolenbroek 
21*5d5fbe79SDavid van Moolenbroek /* our own version of tcp_tmr so we can reset fast/slow timer state */
22*5d5fbe79SDavid van Moolenbroek static void
test_tcp_tmr(void)23*5d5fbe79SDavid van Moolenbroek test_tcp_tmr(void)
24*5d5fbe79SDavid van Moolenbroek {
25*5d5fbe79SDavid van Moolenbroek   tcp_fasttmr();
26*5d5fbe79SDavid van Moolenbroek   if (++test_tcp_timer & 1) {
27*5d5fbe79SDavid van Moolenbroek     tcp_slowtmr();
28*5d5fbe79SDavid van Moolenbroek   }
29*5d5fbe79SDavid van Moolenbroek }
30*5d5fbe79SDavid van Moolenbroek 
31*5d5fbe79SDavid van Moolenbroek /* Setups/teardown functions */
32*5d5fbe79SDavid van Moolenbroek 
33*5d5fbe79SDavid van Moolenbroek static void
tcp_setup(void)34*5d5fbe79SDavid van Moolenbroek tcp_setup(void)
35*5d5fbe79SDavid van Moolenbroek {
36*5d5fbe79SDavid van Moolenbroek   /* reset iss to default (6510) */
37*5d5fbe79SDavid van Moolenbroek   tcp_ticks = 0;
38*5d5fbe79SDavid van Moolenbroek   tcp_ticks = 0 - (tcp_next_iss(NULL) - 6510);
39*5d5fbe79SDavid van Moolenbroek   tcp_next_iss(NULL);
40*5d5fbe79SDavid van Moolenbroek   tcp_ticks = 0;
41*5d5fbe79SDavid van Moolenbroek 
42*5d5fbe79SDavid van Moolenbroek   test_tcp_timer = 0;
43*5d5fbe79SDavid van Moolenbroek   tcp_remove_all();
44*5d5fbe79SDavid van Moolenbroek }
45*5d5fbe79SDavid van Moolenbroek 
46*5d5fbe79SDavid van Moolenbroek static void
tcp_teardown(void)47*5d5fbe79SDavid van Moolenbroek tcp_teardown(void)
48*5d5fbe79SDavid van Moolenbroek {
49*5d5fbe79SDavid van Moolenbroek   tcp_remove_all();
50*5d5fbe79SDavid van Moolenbroek   netif_list = NULL;
51*5d5fbe79SDavid van Moolenbroek   netif_default = NULL;
52*5d5fbe79SDavid van Moolenbroek }
53*5d5fbe79SDavid van Moolenbroek 
54*5d5fbe79SDavid van Moolenbroek 
55*5d5fbe79SDavid van Moolenbroek /* Test functions */
56*5d5fbe79SDavid van Moolenbroek 
57*5d5fbe79SDavid van Moolenbroek /** Call tcp_new() and tcp_abort() and test memp stats */
START_TEST(test_tcp_new_abort)58*5d5fbe79SDavid van Moolenbroek START_TEST(test_tcp_new_abort)
59*5d5fbe79SDavid van Moolenbroek {
60*5d5fbe79SDavid van Moolenbroek   struct tcp_pcb* pcb;
61*5d5fbe79SDavid van Moolenbroek   LWIP_UNUSED_ARG(_i);
62*5d5fbe79SDavid van Moolenbroek 
63*5d5fbe79SDavid van Moolenbroek   fail_unless(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0);
64*5d5fbe79SDavid van Moolenbroek 
65*5d5fbe79SDavid van Moolenbroek   pcb = tcp_new();
66*5d5fbe79SDavid van Moolenbroek   fail_unless(pcb != NULL);
67*5d5fbe79SDavid van Moolenbroek   if (pcb != NULL) {
68*5d5fbe79SDavid van Moolenbroek     fail_unless(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1);
69*5d5fbe79SDavid van Moolenbroek     tcp_abort(pcb);
70*5d5fbe79SDavid van Moolenbroek     fail_unless(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0);
71*5d5fbe79SDavid van Moolenbroek   }
72*5d5fbe79SDavid van Moolenbroek }
73*5d5fbe79SDavid van Moolenbroek END_TEST
74*5d5fbe79SDavid van Moolenbroek 
75*5d5fbe79SDavid van Moolenbroek /** Create an ESTABLISHED pcb and check if receive callback is called */
START_TEST(test_tcp_recv_inseq)76*5d5fbe79SDavid van Moolenbroek START_TEST(test_tcp_recv_inseq)
77*5d5fbe79SDavid van Moolenbroek {
78*5d5fbe79SDavid van Moolenbroek   struct test_tcp_counters counters;
79*5d5fbe79SDavid van Moolenbroek   struct tcp_pcb* pcb;
80*5d5fbe79SDavid van Moolenbroek   struct pbuf* p;
81*5d5fbe79SDavid van Moolenbroek   char data[] = {1, 2, 3, 4};
82*5d5fbe79SDavid van Moolenbroek   ip_addr_t remote_ip, local_ip, netmask;
83*5d5fbe79SDavid van Moolenbroek   u16_t data_len;
84*5d5fbe79SDavid van Moolenbroek   u16_t remote_port = 0x100, local_port = 0x101;
85*5d5fbe79SDavid van Moolenbroek   struct netif netif;
86*5d5fbe79SDavid van Moolenbroek   struct test_tcp_txcounters txcounters;
87*5d5fbe79SDavid van Moolenbroek   LWIP_UNUSED_ARG(_i);
88*5d5fbe79SDavid van Moolenbroek 
89*5d5fbe79SDavid van Moolenbroek   /* initialize local vars */
90*5d5fbe79SDavid van Moolenbroek   memset(&netif, 0, sizeof(netif));
91*5d5fbe79SDavid van Moolenbroek   IP_ADDR4(&local_ip, 192, 168, 1, 1);
92*5d5fbe79SDavid van Moolenbroek   IP_ADDR4(&remote_ip, 192, 168, 1, 2);
93*5d5fbe79SDavid van Moolenbroek   IP_ADDR4(&netmask,   255, 255, 255, 0);
94*5d5fbe79SDavid van Moolenbroek   test_tcp_init_netif(&netif, &txcounters, &local_ip, &netmask);
95*5d5fbe79SDavid van Moolenbroek   data_len = sizeof(data);
96*5d5fbe79SDavid van Moolenbroek   /* initialize counter struct */
97*5d5fbe79SDavid van Moolenbroek   memset(&counters, 0, sizeof(counters));
98*5d5fbe79SDavid van Moolenbroek   counters.expected_data_len = data_len;
99*5d5fbe79SDavid van Moolenbroek   counters.expected_data = data;
100*5d5fbe79SDavid van Moolenbroek 
101*5d5fbe79SDavid van Moolenbroek   /* create and initialize the pcb */
102*5d5fbe79SDavid van Moolenbroek   pcb = test_tcp_new_counters_pcb(&counters);
103*5d5fbe79SDavid van Moolenbroek   EXPECT_RET(pcb != NULL);
104*5d5fbe79SDavid van Moolenbroek   tcp_set_state(pcb, ESTABLISHED, &local_ip, &remote_ip, local_port, remote_port);
105*5d5fbe79SDavid van Moolenbroek 
106*5d5fbe79SDavid van Moolenbroek   /* create a segment */
107*5d5fbe79SDavid van Moolenbroek   p = tcp_create_rx_segment(pcb, counters.expected_data, data_len, 0, 0, 0);
108*5d5fbe79SDavid van Moolenbroek   EXPECT(p != NULL);
109*5d5fbe79SDavid van Moolenbroek   if (p != NULL) {
110*5d5fbe79SDavid van Moolenbroek     /* pass the segment to tcp_input */
111*5d5fbe79SDavid van Moolenbroek     test_tcp_input(p, &netif);
112*5d5fbe79SDavid van Moolenbroek     /* check if counters are as expected */
113*5d5fbe79SDavid van Moolenbroek     EXPECT(counters.close_calls == 0);
114*5d5fbe79SDavid van Moolenbroek     EXPECT(counters.recv_calls == 1);
115*5d5fbe79SDavid van Moolenbroek     EXPECT(counters.recved_bytes == data_len);
116*5d5fbe79SDavid van Moolenbroek     EXPECT(counters.err_calls == 0);
117*5d5fbe79SDavid van Moolenbroek   }
118*5d5fbe79SDavid van Moolenbroek 
119*5d5fbe79SDavid van Moolenbroek   /* make sure the pcb is freed */
120*5d5fbe79SDavid van Moolenbroek   EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1);
121*5d5fbe79SDavid van Moolenbroek   tcp_abort(pcb);
122*5d5fbe79SDavid van Moolenbroek   EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0);
123*5d5fbe79SDavid van Moolenbroek }
124*5d5fbe79SDavid van Moolenbroek END_TEST
125*5d5fbe79SDavid van Moolenbroek 
126*5d5fbe79SDavid van Moolenbroek /** Check that we handle malformed tcp headers, and discard the pbuf(s) */
START_TEST(test_tcp_malformed_header)127*5d5fbe79SDavid van Moolenbroek START_TEST(test_tcp_malformed_header)
128*5d5fbe79SDavid van Moolenbroek {
129*5d5fbe79SDavid van Moolenbroek   struct test_tcp_counters counters;
130*5d5fbe79SDavid van Moolenbroek   struct tcp_pcb* pcb;
131*5d5fbe79SDavid van Moolenbroek   struct pbuf* p;
132*5d5fbe79SDavid van Moolenbroek   char data[] = {1, 2, 3, 4};
133*5d5fbe79SDavid van Moolenbroek   ip_addr_t remote_ip, local_ip, netmask;
134*5d5fbe79SDavid van Moolenbroek   u16_t data_len, chksum;
135*5d5fbe79SDavid van Moolenbroek   u16_t remote_port = 0x100, local_port = 0x101;
136*5d5fbe79SDavid van Moolenbroek   struct netif netif;
137*5d5fbe79SDavid van Moolenbroek   struct test_tcp_txcounters txcounters;
138*5d5fbe79SDavid van Moolenbroek   struct tcp_hdr *hdr;
139*5d5fbe79SDavid van Moolenbroek   LWIP_UNUSED_ARG(_i);
140*5d5fbe79SDavid van Moolenbroek 
141*5d5fbe79SDavid van Moolenbroek   /* initialize local vars */
142*5d5fbe79SDavid van Moolenbroek   memset(&netif, 0, sizeof(netif));
143*5d5fbe79SDavid van Moolenbroek   IP_ADDR4(&local_ip, 192, 168, 1, 1);
144*5d5fbe79SDavid van Moolenbroek   IP_ADDR4(&remote_ip, 192, 168, 1, 2);
145*5d5fbe79SDavid van Moolenbroek   IP_ADDR4(&netmask,   255, 255, 255, 0);
146*5d5fbe79SDavid van Moolenbroek   test_tcp_init_netif(&netif, &txcounters, &local_ip, &netmask);
147*5d5fbe79SDavid van Moolenbroek   data_len = sizeof(data);
148*5d5fbe79SDavid van Moolenbroek   /* initialize counter struct */
149*5d5fbe79SDavid van Moolenbroek   memset(&counters, 0, sizeof(counters));
150*5d5fbe79SDavid van Moolenbroek   counters.expected_data_len = data_len;
151*5d5fbe79SDavid van Moolenbroek   counters.expected_data = data;
152*5d5fbe79SDavid van Moolenbroek 
153*5d5fbe79SDavid van Moolenbroek   /* create and initialize the pcb */
154*5d5fbe79SDavid van Moolenbroek   pcb = test_tcp_new_counters_pcb(&counters);
155*5d5fbe79SDavid van Moolenbroek   EXPECT_RET(pcb != NULL);
156*5d5fbe79SDavid van Moolenbroek   tcp_set_state(pcb, ESTABLISHED, &local_ip, &remote_ip, local_port, remote_port);
157*5d5fbe79SDavid van Moolenbroek 
158*5d5fbe79SDavid van Moolenbroek   /* create a segment */
159*5d5fbe79SDavid van Moolenbroek   p = tcp_create_rx_segment(pcb, counters.expected_data, data_len, 0, 0, 0);
160*5d5fbe79SDavid van Moolenbroek 
161*5d5fbe79SDavid van Moolenbroek   pbuf_header(p, -(s16_t)sizeof(struct ip_hdr));
162*5d5fbe79SDavid van Moolenbroek 
163*5d5fbe79SDavid van Moolenbroek   hdr = (struct tcp_hdr *)p->payload;
164*5d5fbe79SDavid van Moolenbroek   TCPH_HDRLEN_FLAGS_SET(hdr, 15, 0x3d1);
165*5d5fbe79SDavid van Moolenbroek 
166*5d5fbe79SDavid van Moolenbroek   hdr->chksum = 0;
167*5d5fbe79SDavid van Moolenbroek 
168*5d5fbe79SDavid van Moolenbroek   chksum = ip_chksum_pseudo(p, IP_PROTO_TCP, p->tot_len,
169*5d5fbe79SDavid van Moolenbroek                              &remote_ip, &local_ip);
170*5d5fbe79SDavid van Moolenbroek 
171*5d5fbe79SDavid van Moolenbroek   hdr->chksum = chksum;
172*5d5fbe79SDavid van Moolenbroek 
173*5d5fbe79SDavid van Moolenbroek   pbuf_header(p, sizeof(struct ip_hdr));
174*5d5fbe79SDavid van Moolenbroek 
175*5d5fbe79SDavid van Moolenbroek   EXPECT(p != NULL);
176*5d5fbe79SDavid van Moolenbroek   EXPECT(p->next == NULL);
177*5d5fbe79SDavid van Moolenbroek   if (p != NULL) {
178*5d5fbe79SDavid van Moolenbroek     /* pass the segment to tcp_input */
179*5d5fbe79SDavid van Moolenbroek     test_tcp_input(p, &netif);
180*5d5fbe79SDavid van Moolenbroek     /* check if counters are as expected */
181*5d5fbe79SDavid van Moolenbroek     EXPECT(counters.close_calls == 0);
182*5d5fbe79SDavid van Moolenbroek     EXPECT(counters.recv_calls == 0);
183*5d5fbe79SDavid van Moolenbroek     EXPECT(counters.recved_bytes == 0);
184*5d5fbe79SDavid van Moolenbroek     EXPECT(counters.err_calls == 0);
185*5d5fbe79SDavid van Moolenbroek   }
186*5d5fbe79SDavid van Moolenbroek 
187*5d5fbe79SDavid van Moolenbroek   /* make sure the pcb is freed */
188*5d5fbe79SDavid van Moolenbroek   EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1);
189*5d5fbe79SDavid van Moolenbroek   tcp_abort(pcb);
190*5d5fbe79SDavid van Moolenbroek   EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0);
191*5d5fbe79SDavid van Moolenbroek }
192*5d5fbe79SDavid van Moolenbroek END_TEST
193*5d5fbe79SDavid van Moolenbroek 
194*5d5fbe79SDavid van Moolenbroek 
195*5d5fbe79SDavid van Moolenbroek /** Provoke fast retransmission by duplicate ACKs and then recover by ACKing all sent data.
196*5d5fbe79SDavid van Moolenbroek  * At the end, send more data. */
START_TEST(test_tcp_fast_retx_recover)197*5d5fbe79SDavid van Moolenbroek START_TEST(test_tcp_fast_retx_recover)
198*5d5fbe79SDavid van Moolenbroek {
199*5d5fbe79SDavid van Moolenbroek   struct netif netif;
200*5d5fbe79SDavid van Moolenbroek   struct test_tcp_txcounters txcounters;
201*5d5fbe79SDavid van Moolenbroek   struct test_tcp_counters counters;
202*5d5fbe79SDavid van Moolenbroek   struct tcp_pcb* pcb;
203*5d5fbe79SDavid van Moolenbroek   struct pbuf* p;
204*5d5fbe79SDavid van Moolenbroek   char data1[] = { 1,  2,  3,  4};
205*5d5fbe79SDavid van Moolenbroek   char data2[] = { 5,  6,  7,  8};
206*5d5fbe79SDavid van Moolenbroek   char data3[] = { 9, 10, 11, 12};
207*5d5fbe79SDavid van Moolenbroek   char data4[] = {13, 14, 15, 16};
208*5d5fbe79SDavid van Moolenbroek   char data5[] = {17, 18, 19, 20};
209*5d5fbe79SDavid van Moolenbroek   char data6[TCP_MSS] = {21, 22, 23, 24};
210*5d5fbe79SDavid van Moolenbroek   ip_addr_t remote_ip, local_ip, netmask;
211*5d5fbe79SDavid van Moolenbroek   u16_t remote_port = 0x100, local_port = 0x101;
212*5d5fbe79SDavid van Moolenbroek   err_t err;
213*5d5fbe79SDavid van Moolenbroek   LWIP_UNUSED_ARG(_i);
214*5d5fbe79SDavid van Moolenbroek 
215*5d5fbe79SDavid van Moolenbroek   /* initialize local vars */
216*5d5fbe79SDavid van Moolenbroek   IP_ADDR4(&local_ip,  192, 168,   1, 1);
217*5d5fbe79SDavid van Moolenbroek   IP_ADDR4(&remote_ip, 192, 168,   1, 2);
218*5d5fbe79SDavid van Moolenbroek   IP_ADDR4(&netmask,   255, 255, 255, 0);
219*5d5fbe79SDavid van Moolenbroek   test_tcp_init_netif(&netif, &txcounters, &local_ip, &netmask);
220*5d5fbe79SDavid van Moolenbroek   memset(&counters, 0, sizeof(counters));
221*5d5fbe79SDavid van Moolenbroek 
222*5d5fbe79SDavid van Moolenbroek   /* create and initialize the pcb */
223*5d5fbe79SDavid van Moolenbroek   pcb = test_tcp_new_counters_pcb(&counters);
224*5d5fbe79SDavid van Moolenbroek   EXPECT_RET(pcb != NULL);
225*5d5fbe79SDavid van Moolenbroek   tcp_set_state(pcb, ESTABLISHED, &local_ip, &remote_ip, local_port, remote_port);
226*5d5fbe79SDavid van Moolenbroek   pcb->mss = TCP_MSS;
227*5d5fbe79SDavid van Moolenbroek   /* disable initial congestion window (we don't send a SYN here...) */
228*5d5fbe79SDavid van Moolenbroek   pcb->cwnd = pcb->snd_wnd;
229*5d5fbe79SDavid van Moolenbroek 
230*5d5fbe79SDavid van Moolenbroek   /* send data1 */
231*5d5fbe79SDavid van Moolenbroek   err = tcp_write(pcb, data1, sizeof(data1), TCP_WRITE_FLAG_COPY);
232*5d5fbe79SDavid van Moolenbroek   EXPECT_RET(err == ERR_OK);
233*5d5fbe79SDavid van Moolenbroek   err = tcp_output(pcb);
234*5d5fbe79SDavid van Moolenbroek   EXPECT_RET(err == ERR_OK);
235*5d5fbe79SDavid van Moolenbroek   EXPECT_RET(txcounters.num_tx_calls == 1);
236*5d5fbe79SDavid van Moolenbroek   EXPECT_RET(txcounters.num_tx_bytes == sizeof(data1) + sizeof(struct tcp_hdr) + sizeof(struct ip_hdr));
237*5d5fbe79SDavid van Moolenbroek   memset(&txcounters, 0, sizeof(txcounters));
238*5d5fbe79SDavid van Moolenbroek  /* "recv" ACK for data1 */
239*5d5fbe79SDavid van Moolenbroek   p = tcp_create_rx_segment(pcb, NULL, 0, 0, 4, TCP_ACK);
240*5d5fbe79SDavid van Moolenbroek   EXPECT_RET(p != NULL);
241*5d5fbe79SDavid van Moolenbroek   test_tcp_input(p, &netif);
242*5d5fbe79SDavid van Moolenbroek   EXPECT_RET(txcounters.num_tx_calls == 0);
243*5d5fbe79SDavid van Moolenbroek   EXPECT_RET(pcb->unacked == NULL);
244*5d5fbe79SDavid van Moolenbroek   /* send data2 */
245*5d5fbe79SDavid van Moolenbroek   err = tcp_write(pcb, data2, sizeof(data2), TCP_WRITE_FLAG_COPY);
246*5d5fbe79SDavid van Moolenbroek   EXPECT_RET(err == ERR_OK);
247*5d5fbe79SDavid van Moolenbroek   err = tcp_output(pcb);
248*5d5fbe79SDavid van Moolenbroek   EXPECT_RET(err == ERR_OK);
249*5d5fbe79SDavid van Moolenbroek   EXPECT_RET(txcounters.num_tx_calls == 1);
250*5d5fbe79SDavid van Moolenbroek   EXPECT_RET(txcounters.num_tx_bytes == sizeof(data2) + sizeof(struct tcp_hdr) + sizeof(struct ip_hdr));
251*5d5fbe79SDavid van Moolenbroek   memset(&txcounters, 0, sizeof(txcounters));
252*5d5fbe79SDavid van Moolenbroek   /* duplicate ACK for data1 (data2 is lost) */
253*5d5fbe79SDavid van Moolenbroek   p = tcp_create_rx_segment(pcb, NULL, 0, 0, 0, TCP_ACK);
254*5d5fbe79SDavid van Moolenbroek   EXPECT_RET(p != NULL);
255*5d5fbe79SDavid van Moolenbroek   test_tcp_input(p, &netif);
256*5d5fbe79SDavid van Moolenbroek   EXPECT_RET(txcounters.num_tx_calls == 0);
257*5d5fbe79SDavid van Moolenbroek   EXPECT_RET(pcb->dupacks == 1);
258*5d5fbe79SDavid van Moolenbroek   /* send data3 */
259*5d5fbe79SDavid van Moolenbroek   err = tcp_write(pcb, data3, sizeof(data3), TCP_WRITE_FLAG_COPY);
260*5d5fbe79SDavid van Moolenbroek   EXPECT_RET(err == ERR_OK);
261*5d5fbe79SDavid van Moolenbroek   err = tcp_output(pcb);
262*5d5fbe79SDavid van Moolenbroek   EXPECT_RET(err == ERR_OK);
263*5d5fbe79SDavid van Moolenbroek   /* nagle enabled, no tx calls */
264*5d5fbe79SDavid van Moolenbroek   EXPECT_RET(txcounters.num_tx_calls == 0);
265*5d5fbe79SDavid van Moolenbroek   EXPECT_RET(txcounters.num_tx_bytes == 0);
266*5d5fbe79SDavid van Moolenbroek   memset(&txcounters, 0, sizeof(txcounters));
267*5d5fbe79SDavid van Moolenbroek   /* 2nd duplicate ACK for data1 (data2 and data3 are lost) */
268*5d5fbe79SDavid van Moolenbroek   p = tcp_create_rx_segment(pcb, NULL, 0, 0, 0, TCP_ACK);
269*5d5fbe79SDavid van Moolenbroek   EXPECT_RET(p != NULL);
270*5d5fbe79SDavid van Moolenbroek   test_tcp_input(p, &netif);
271*5d5fbe79SDavid van Moolenbroek   EXPECT_RET(txcounters.num_tx_calls == 0);
272*5d5fbe79SDavid van Moolenbroek   EXPECT_RET(pcb->dupacks == 2);
273*5d5fbe79SDavid van Moolenbroek   /* queue data4, don't send it (unsent-oversize is != 0) */
274*5d5fbe79SDavid van Moolenbroek   err = tcp_write(pcb, data4, sizeof(data4), TCP_WRITE_FLAG_COPY);
275*5d5fbe79SDavid van Moolenbroek   EXPECT_RET(err == ERR_OK);
276*5d5fbe79SDavid van Moolenbroek   /* 3nd duplicate ACK for data1 (data2 and data3 are lost) -> fast retransmission */
277*5d5fbe79SDavid van Moolenbroek   p = tcp_create_rx_segment(pcb, NULL, 0, 0, 0, TCP_ACK);
278*5d5fbe79SDavid van Moolenbroek   EXPECT_RET(p != NULL);
279*5d5fbe79SDavid van Moolenbroek   test_tcp_input(p, &netif);
280*5d5fbe79SDavid van Moolenbroek   /*EXPECT_RET(txcounters.num_tx_calls == 1);*/
281*5d5fbe79SDavid van Moolenbroek   EXPECT_RET(pcb->dupacks == 3);
282*5d5fbe79SDavid van Moolenbroek   memset(&txcounters, 0, sizeof(txcounters));
283*5d5fbe79SDavid van Moolenbroek   /* @todo: check expected data?*/
284*5d5fbe79SDavid van Moolenbroek 
285*5d5fbe79SDavid van Moolenbroek   /* send data5, not output yet */
286*5d5fbe79SDavid van Moolenbroek   err = tcp_write(pcb, data5, sizeof(data5), TCP_WRITE_FLAG_COPY);
287*5d5fbe79SDavid van Moolenbroek   EXPECT_RET(err == ERR_OK);
288*5d5fbe79SDavid van Moolenbroek   /*err = tcp_output(pcb);
289*5d5fbe79SDavid van Moolenbroek   EXPECT_RET(err == ERR_OK);*/
290*5d5fbe79SDavid van Moolenbroek   EXPECT_RET(txcounters.num_tx_calls == 0);
291*5d5fbe79SDavid van Moolenbroek   EXPECT_RET(txcounters.num_tx_bytes == 0);
292*5d5fbe79SDavid van Moolenbroek   memset(&txcounters, 0, sizeof(txcounters));
293*5d5fbe79SDavid van Moolenbroek   {
294*5d5fbe79SDavid van Moolenbroek     int i = 0;
295*5d5fbe79SDavid van Moolenbroek     do
296*5d5fbe79SDavid van Moolenbroek     {
297*5d5fbe79SDavid van Moolenbroek       err = tcp_write(pcb, data6, TCP_MSS, TCP_WRITE_FLAG_COPY);
298*5d5fbe79SDavid van Moolenbroek       i++;
299*5d5fbe79SDavid van Moolenbroek     }while(err == ERR_OK);
300*5d5fbe79SDavid van Moolenbroek     EXPECT_RET(err != ERR_OK);
301*5d5fbe79SDavid van Moolenbroek   }
302*5d5fbe79SDavid van Moolenbroek   err = tcp_output(pcb);
303*5d5fbe79SDavid van Moolenbroek   EXPECT_RET(err == ERR_OK);
304*5d5fbe79SDavid van Moolenbroek   /*EXPECT_RET(txcounters.num_tx_calls == 0);
305*5d5fbe79SDavid van Moolenbroek   EXPECT_RET(txcounters.num_tx_bytes == 0);*/
306*5d5fbe79SDavid van Moolenbroek   memset(&txcounters, 0, sizeof(txcounters));
307*5d5fbe79SDavid van Moolenbroek 
308*5d5fbe79SDavid van Moolenbroek   /* send even more data */
309*5d5fbe79SDavid van Moolenbroek   err = tcp_write(pcb, data5, sizeof(data5), TCP_WRITE_FLAG_COPY);
310*5d5fbe79SDavid van Moolenbroek   EXPECT_RET(err == ERR_OK);
311*5d5fbe79SDavid van Moolenbroek   err = tcp_output(pcb);
312*5d5fbe79SDavid van Moolenbroek   EXPECT_RET(err == ERR_OK);
313*5d5fbe79SDavid van Moolenbroek   /* ...and even more data */
314*5d5fbe79SDavid van Moolenbroek   err = tcp_write(pcb, data5, sizeof(data5), TCP_WRITE_FLAG_COPY);
315*5d5fbe79SDavid van Moolenbroek   EXPECT_RET(err == ERR_OK);
316*5d5fbe79SDavid van Moolenbroek   err = tcp_output(pcb);
317*5d5fbe79SDavid van Moolenbroek   EXPECT_RET(err == ERR_OK);
318*5d5fbe79SDavid van Moolenbroek   /* ...and even more data */
319*5d5fbe79SDavid van Moolenbroek   err = tcp_write(pcb, data5, sizeof(data5), TCP_WRITE_FLAG_COPY);
320*5d5fbe79SDavid van Moolenbroek   EXPECT_RET(err == ERR_OK);
321*5d5fbe79SDavid van Moolenbroek   err = tcp_output(pcb);
322*5d5fbe79SDavid van Moolenbroek   EXPECT_RET(err == ERR_OK);
323*5d5fbe79SDavid van Moolenbroek   /* ...and even more data */
324*5d5fbe79SDavid van Moolenbroek   err = tcp_write(pcb, data5, sizeof(data5), TCP_WRITE_FLAG_COPY);
325*5d5fbe79SDavid van Moolenbroek   EXPECT_RET(err == ERR_OK);
326*5d5fbe79SDavid van Moolenbroek   err = tcp_output(pcb);
327*5d5fbe79SDavid van Moolenbroek   EXPECT_RET(err == ERR_OK);
328*5d5fbe79SDavid van Moolenbroek 
329*5d5fbe79SDavid van Moolenbroek   /* send ACKs for data2 and data3 */
330*5d5fbe79SDavid van Moolenbroek   p = tcp_create_rx_segment(pcb, NULL, 0, 0, 12, TCP_ACK);
331*5d5fbe79SDavid van Moolenbroek   EXPECT_RET(p != NULL);
332*5d5fbe79SDavid van Moolenbroek   test_tcp_input(p, &netif);
333*5d5fbe79SDavid van Moolenbroek   /*EXPECT_RET(txcounters.num_tx_calls == 0);*/
334*5d5fbe79SDavid van Moolenbroek 
335*5d5fbe79SDavid van Moolenbroek   /* ...and even more data */
336*5d5fbe79SDavid van Moolenbroek   err = tcp_write(pcb, data5, sizeof(data5), TCP_WRITE_FLAG_COPY);
337*5d5fbe79SDavid van Moolenbroek   EXPECT_RET(err == ERR_OK);
338*5d5fbe79SDavid van Moolenbroek   err = tcp_output(pcb);
339*5d5fbe79SDavid van Moolenbroek   EXPECT_RET(err == ERR_OK);
340*5d5fbe79SDavid van Moolenbroek   /* ...and even more data */
341*5d5fbe79SDavid van Moolenbroek   err = tcp_write(pcb, data5, sizeof(data5), TCP_WRITE_FLAG_COPY);
342*5d5fbe79SDavid van Moolenbroek   EXPECT_RET(err == ERR_OK);
343*5d5fbe79SDavid van Moolenbroek   err = tcp_output(pcb);
344*5d5fbe79SDavid van Moolenbroek   EXPECT_RET(err == ERR_OK);
345*5d5fbe79SDavid van Moolenbroek 
346*5d5fbe79SDavid van Moolenbroek #if 0
347*5d5fbe79SDavid van Moolenbroek   /* create expected segment */
348*5d5fbe79SDavid van Moolenbroek   p1 = tcp_create_rx_segment(pcb, counters.expected_data, data_len, 0, 0, 0);
349*5d5fbe79SDavid van Moolenbroek   EXPECT_RET(p != NULL);
350*5d5fbe79SDavid van Moolenbroek   if (p != NULL) {
351*5d5fbe79SDavid van Moolenbroek     /* pass the segment to tcp_input */
352*5d5fbe79SDavid van Moolenbroek     test_tcp_input(p, &netif);
353*5d5fbe79SDavid van Moolenbroek     /* check if counters are as expected */
354*5d5fbe79SDavid van Moolenbroek     EXPECT_RET(counters.close_calls == 0);
355*5d5fbe79SDavid van Moolenbroek     EXPECT_RET(counters.recv_calls == 1);
356*5d5fbe79SDavid van Moolenbroek     EXPECT_RET(counters.recved_bytes == data_len);
357*5d5fbe79SDavid van Moolenbroek     EXPECT_RET(counters.err_calls == 0);
358*5d5fbe79SDavid van Moolenbroek   }
359*5d5fbe79SDavid van Moolenbroek #endif
360*5d5fbe79SDavid van Moolenbroek   /* make sure the pcb is freed */
361*5d5fbe79SDavid van Moolenbroek   EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1);
362*5d5fbe79SDavid van Moolenbroek   tcp_abort(pcb);
363*5d5fbe79SDavid van Moolenbroek   EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0);
364*5d5fbe79SDavid van Moolenbroek }
365*5d5fbe79SDavid van Moolenbroek END_TEST
366*5d5fbe79SDavid van Moolenbroek 
367*5d5fbe79SDavid van Moolenbroek static u8_t tx_data[TCP_WND*2];
368*5d5fbe79SDavid van Moolenbroek 
369*5d5fbe79SDavid van Moolenbroek static void
check_seqnos(struct tcp_seg * segs,int num_expected,u32_t * seqnos_expected)370*5d5fbe79SDavid van Moolenbroek check_seqnos(struct tcp_seg *segs, int num_expected, u32_t *seqnos_expected)
371*5d5fbe79SDavid van Moolenbroek {
372*5d5fbe79SDavid van Moolenbroek   struct tcp_seg *s = segs;
373*5d5fbe79SDavid van Moolenbroek   int i;
374*5d5fbe79SDavid van Moolenbroek   for (i = 0; i < num_expected; i++, s = s->next) {
375*5d5fbe79SDavid van Moolenbroek     EXPECT_RET(s != NULL);
376*5d5fbe79SDavid van Moolenbroek     EXPECT(s->tcphdr->seqno == htonl(seqnos_expected[i]));
377*5d5fbe79SDavid van Moolenbroek   }
378*5d5fbe79SDavid van Moolenbroek   EXPECT(s == NULL);
379*5d5fbe79SDavid van Moolenbroek }
380*5d5fbe79SDavid van Moolenbroek 
381*5d5fbe79SDavid van Moolenbroek /** Send data with sequence numbers that wrap around the u32_t range.
382*5d5fbe79SDavid van Moolenbroek  * Then, provoke fast retransmission by duplicate ACKs and check that all
383*5d5fbe79SDavid van Moolenbroek  * segment lists are still properly sorted. */
START_TEST(test_tcp_fast_rexmit_wraparound)384*5d5fbe79SDavid van Moolenbroek START_TEST(test_tcp_fast_rexmit_wraparound)
385*5d5fbe79SDavid van Moolenbroek {
386*5d5fbe79SDavid van Moolenbroek   struct netif netif;
387*5d5fbe79SDavid van Moolenbroek   struct test_tcp_txcounters txcounters;
388*5d5fbe79SDavid van Moolenbroek   struct test_tcp_counters counters;
389*5d5fbe79SDavid van Moolenbroek   struct tcp_pcb* pcb;
390*5d5fbe79SDavid van Moolenbroek   struct pbuf* p;
391*5d5fbe79SDavid van Moolenbroek   ip_addr_t remote_ip, local_ip, netmask;
392*5d5fbe79SDavid van Moolenbroek   u16_t remote_port = 0x100, local_port = 0x101;
393*5d5fbe79SDavid van Moolenbroek   err_t err;
394*5d5fbe79SDavid van Moolenbroek #define SEQNO1 (0xFFFFFF00 - TCP_MSS)
395*5d5fbe79SDavid van Moolenbroek #define ISS    6510
396*5d5fbe79SDavid van Moolenbroek   u16_t i, sent_total = 0;
397*5d5fbe79SDavid van Moolenbroek   u32_t seqnos[] = {
398*5d5fbe79SDavid van Moolenbroek     SEQNO1,
399*5d5fbe79SDavid van Moolenbroek     SEQNO1 + (1 * TCP_MSS),
400*5d5fbe79SDavid van Moolenbroek     SEQNO1 + (2 * TCP_MSS),
401*5d5fbe79SDavid van Moolenbroek     SEQNO1 + (3 * TCP_MSS),
402*5d5fbe79SDavid van Moolenbroek     SEQNO1 + (4 * TCP_MSS),
403*5d5fbe79SDavid van Moolenbroek     SEQNO1 + (5 * TCP_MSS)};
404*5d5fbe79SDavid van Moolenbroek   LWIP_UNUSED_ARG(_i);
405*5d5fbe79SDavid van Moolenbroek 
406*5d5fbe79SDavid van Moolenbroek   for (i = 0; i < sizeof(tx_data); i++) {
407*5d5fbe79SDavid van Moolenbroek     tx_data[i] = (u8_t)i;
408*5d5fbe79SDavid van Moolenbroek   }
409*5d5fbe79SDavid van Moolenbroek 
410*5d5fbe79SDavid van Moolenbroek   /* initialize local vars */
411*5d5fbe79SDavid van Moolenbroek   IP_ADDR4(&local_ip,  192, 168,   1, 1);
412*5d5fbe79SDavid van Moolenbroek   IP_ADDR4(&remote_ip, 192, 168,   1, 2);
413*5d5fbe79SDavid van Moolenbroek   IP_ADDR4(&netmask,   255, 255, 255, 0);
414*5d5fbe79SDavid van Moolenbroek   test_tcp_init_netif(&netif, &txcounters, &local_ip, &netmask);
415*5d5fbe79SDavid van Moolenbroek   memset(&counters, 0, sizeof(counters));
416*5d5fbe79SDavid van Moolenbroek 
417*5d5fbe79SDavid van Moolenbroek   /* create and initialize the pcb */
418*5d5fbe79SDavid van Moolenbroek   tcp_ticks = SEQNO1 - ISS;
419*5d5fbe79SDavid van Moolenbroek   pcb = test_tcp_new_counters_pcb(&counters);
420*5d5fbe79SDavid van Moolenbroek   EXPECT_RET(pcb != NULL);
421*5d5fbe79SDavid van Moolenbroek   tcp_set_state(pcb, ESTABLISHED, &local_ip, &remote_ip, local_port, remote_port);
422*5d5fbe79SDavid van Moolenbroek   pcb->mss = TCP_MSS;
423*5d5fbe79SDavid van Moolenbroek   /* disable initial congestion window (we don't send a SYN here...) */
424*5d5fbe79SDavid van Moolenbroek   pcb->cwnd = 2*TCP_MSS;
425*5d5fbe79SDavid van Moolenbroek 
426*5d5fbe79SDavid van Moolenbroek   /* send 6 mss-sized segments */
427*5d5fbe79SDavid van Moolenbroek   for (i = 0; i < 6; i++) {
428*5d5fbe79SDavid van Moolenbroek     err = tcp_write(pcb, &tx_data[sent_total], TCP_MSS, TCP_WRITE_FLAG_COPY);
429*5d5fbe79SDavid van Moolenbroek     EXPECT_RET(err == ERR_OK);
430*5d5fbe79SDavid van Moolenbroek     sent_total += TCP_MSS;
431*5d5fbe79SDavid van Moolenbroek   }
432*5d5fbe79SDavid van Moolenbroek   check_seqnos(pcb->unsent, 6, seqnos);
433*5d5fbe79SDavid van Moolenbroek   EXPECT(pcb->unacked == NULL);
434*5d5fbe79SDavid van Moolenbroek   err = tcp_output(pcb);
435*5d5fbe79SDavid van Moolenbroek   EXPECT(txcounters.num_tx_calls == 2);
436*5d5fbe79SDavid van Moolenbroek   EXPECT(txcounters.num_tx_bytes == 2 * (TCP_MSS + 40U));
437*5d5fbe79SDavid van Moolenbroek   memset(&txcounters, 0, sizeof(txcounters));
438*5d5fbe79SDavid van Moolenbroek 
439*5d5fbe79SDavid van Moolenbroek   check_seqnos(pcb->unacked, 2, seqnos);
440*5d5fbe79SDavid van Moolenbroek   check_seqnos(pcb->unsent, 4, &seqnos[2]);
441*5d5fbe79SDavid van Moolenbroek 
442*5d5fbe79SDavid van Moolenbroek   /* ACK the first segment */
443*5d5fbe79SDavid van Moolenbroek   p = tcp_create_rx_segment(pcb, NULL, 0, 0, TCP_MSS, TCP_ACK);
444*5d5fbe79SDavid van Moolenbroek   test_tcp_input(p, &netif);
445*5d5fbe79SDavid van Moolenbroek   /* ensure this didn't trigger a retransmission */
446*5d5fbe79SDavid van Moolenbroek   EXPECT(txcounters.num_tx_calls == 1);
447*5d5fbe79SDavid van Moolenbroek   EXPECT(txcounters.num_tx_bytes == TCP_MSS + 40U);
448*5d5fbe79SDavid van Moolenbroek   memset(&txcounters, 0, sizeof(txcounters));
449*5d5fbe79SDavid van Moolenbroek   check_seqnos(pcb->unacked, 2, &seqnos[1]);
450*5d5fbe79SDavid van Moolenbroek   check_seqnos(pcb->unsent, 3, &seqnos[3]);
451*5d5fbe79SDavid van Moolenbroek 
452*5d5fbe79SDavid van Moolenbroek   /* 3 dupacks */
453*5d5fbe79SDavid van Moolenbroek   EXPECT(pcb->dupacks == 0);
454*5d5fbe79SDavid van Moolenbroek   p = tcp_create_rx_segment(pcb, NULL, 0, 0, 0, TCP_ACK);
455*5d5fbe79SDavid van Moolenbroek   test_tcp_input(p, &netif);
456*5d5fbe79SDavid van Moolenbroek   EXPECT(txcounters.num_tx_calls == 0);
457*5d5fbe79SDavid van Moolenbroek   EXPECT(pcb->dupacks == 1);
458*5d5fbe79SDavid van Moolenbroek   p = tcp_create_rx_segment(pcb, NULL, 0, 0, 0, TCP_ACK);
459*5d5fbe79SDavid van Moolenbroek   test_tcp_input(p, &netif);
460*5d5fbe79SDavid van Moolenbroek   EXPECT(txcounters.num_tx_calls == 0);
461*5d5fbe79SDavid van Moolenbroek   EXPECT(pcb->dupacks == 2);
462*5d5fbe79SDavid van Moolenbroek   /* 3rd dupack -> fast rexmit */
463*5d5fbe79SDavid van Moolenbroek   p = tcp_create_rx_segment(pcb, NULL, 0, 0, 0, TCP_ACK);
464*5d5fbe79SDavid van Moolenbroek   test_tcp_input(p, &netif);
465*5d5fbe79SDavid van Moolenbroek   EXPECT(pcb->dupacks == 3);
466*5d5fbe79SDavid van Moolenbroek   EXPECT(txcounters.num_tx_calls == 4);
467*5d5fbe79SDavid van Moolenbroek   memset(&txcounters, 0, sizeof(txcounters));
468*5d5fbe79SDavid van Moolenbroek   EXPECT(pcb->unsent == NULL);
469*5d5fbe79SDavid van Moolenbroek   check_seqnos(pcb->unacked, 5, &seqnos[1]);
470*5d5fbe79SDavid van Moolenbroek 
471*5d5fbe79SDavid van Moolenbroek   /* make sure the pcb is freed */
472*5d5fbe79SDavid van Moolenbroek   EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1);
473*5d5fbe79SDavid van Moolenbroek   tcp_abort(pcb);
474*5d5fbe79SDavid van Moolenbroek   EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0);
475*5d5fbe79SDavid van Moolenbroek }
476*5d5fbe79SDavid van Moolenbroek END_TEST
477*5d5fbe79SDavid van Moolenbroek 
478*5d5fbe79SDavid van Moolenbroek /** Send data with sequence numbers that wrap around the u32_t range.
479*5d5fbe79SDavid van Moolenbroek  * Then, provoke RTO retransmission and check that all
480*5d5fbe79SDavid van Moolenbroek  * segment lists are still properly sorted. */
START_TEST(test_tcp_rto_rexmit_wraparound)481*5d5fbe79SDavid van Moolenbroek START_TEST(test_tcp_rto_rexmit_wraparound)
482*5d5fbe79SDavid van Moolenbroek {
483*5d5fbe79SDavid van Moolenbroek   struct netif netif;
484*5d5fbe79SDavid van Moolenbroek   struct test_tcp_txcounters txcounters;
485*5d5fbe79SDavid van Moolenbroek   struct test_tcp_counters counters;
486*5d5fbe79SDavid van Moolenbroek   struct tcp_pcb* pcb;
487*5d5fbe79SDavid van Moolenbroek   ip_addr_t remote_ip, local_ip, netmask;
488*5d5fbe79SDavid van Moolenbroek   u16_t remote_port = 0x100, local_port = 0x101;
489*5d5fbe79SDavid van Moolenbroek   err_t err;
490*5d5fbe79SDavid van Moolenbroek #define SEQNO1 (0xFFFFFF00 - TCP_MSS)
491*5d5fbe79SDavid van Moolenbroek #define ISS    6510
492*5d5fbe79SDavid van Moolenbroek   u16_t i, sent_total = 0;
493*5d5fbe79SDavid van Moolenbroek   u32_t seqnos[] = {
494*5d5fbe79SDavid van Moolenbroek     SEQNO1,
495*5d5fbe79SDavid van Moolenbroek     SEQNO1 + (1 * TCP_MSS),
496*5d5fbe79SDavid van Moolenbroek     SEQNO1 + (2 * TCP_MSS),
497*5d5fbe79SDavid van Moolenbroek     SEQNO1 + (3 * TCP_MSS),
498*5d5fbe79SDavid van Moolenbroek     SEQNO1 + (4 * TCP_MSS),
499*5d5fbe79SDavid van Moolenbroek     SEQNO1 + (5 * TCP_MSS)};
500*5d5fbe79SDavid van Moolenbroek   LWIP_UNUSED_ARG(_i);
501*5d5fbe79SDavid van Moolenbroek 
502*5d5fbe79SDavid van Moolenbroek   for (i = 0; i < sizeof(tx_data); i++) {
503*5d5fbe79SDavid van Moolenbroek     tx_data[i] = (u8_t)i;
504*5d5fbe79SDavid van Moolenbroek   }
505*5d5fbe79SDavid van Moolenbroek 
506*5d5fbe79SDavid van Moolenbroek   /* initialize local vars */
507*5d5fbe79SDavid van Moolenbroek   IP_ADDR4(&local_ip,  192, 168,   1, 1);
508*5d5fbe79SDavid van Moolenbroek   IP_ADDR4(&remote_ip, 192, 168,   1, 2);
509*5d5fbe79SDavid van Moolenbroek   IP_ADDR4(&netmask,   255, 255, 255, 0);
510*5d5fbe79SDavid van Moolenbroek   test_tcp_init_netif(&netif, &txcounters, &local_ip, &netmask);
511*5d5fbe79SDavid van Moolenbroek   memset(&counters, 0, sizeof(counters));
512*5d5fbe79SDavid van Moolenbroek 
513*5d5fbe79SDavid van Moolenbroek   /* create and initialize the pcb */
514*5d5fbe79SDavid van Moolenbroek   tcp_ticks = 0;
515*5d5fbe79SDavid van Moolenbroek   tcp_ticks = 0 - tcp_next_iss(NULL);
516*5d5fbe79SDavid van Moolenbroek   tcp_ticks = SEQNO1 - tcp_next_iss(NULL);
517*5d5fbe79SDavid van Moolenbroek   pcb = test_tcp_new_counters_pcb(&counters);
518*5d5fbe79SDavid van Moolenbroek   EXPECT_RET(pcb != NULL);
519*5d5fbe79SDavid van Moolenbroek   tcp_set_state(pcb, ESTABLISHED, &local_ip, &remote_ip, local_port, remote_port);
520*5d5fbe79SDavid van Moolenbroek   pcb->mss = TCP_MSS;
521*5d5fbe79SDavid van Moolenbroek   /* disable initial congestion window (we don't send a SYN here...) */
522*5d5fbe79SDavid van Moolenbroek   pcb->cwnd = 2*TCP_MSS;
523*5d5fbe79SDavid van Moolenbroek 
524*5d5fbe79SDavid van Moolenbroek   /* send 6 mss-sized segments */
525*5d5fbe79SDavid van Moolenbroek   for (i = 0; i < 6; i++) {
526*5d5fbe79SDavid van Moolenbroek     err = tcp_write(pcb, &tx_data[sent_total], TCP_MSS, TCP_WRITE_FLAG_COPY);
527*5d5fbe79SDavid van Moolenbroek     EXPECT_RET(err == ERR_OK);
528*5d5fbe79SDavid van Moolenbroek     sent_total += TCP_MSS;
529*5d5fbe79SDavid van Moolenbroek   }
530*5d5fbe79SDavid van Moolenbroek   check_seqnos(pcb->unsent, 6, seqnos);
531*5d5fbe79SDavid van Moolenbroek   EXPECT(pcb->unacked == NULL);
532*5d5fbe79SDavid van Moolenbroek   err = tcp_output(pcb);
533*5d5fbe79SDavid van Moolenbroek   EXPECT(txcounters.num_tx_calls == 2);
534*5d5fbe79SDavid van Moolenbroek   EXPECT(txcounters.num_tx_bytes == 2 * (TCP_MSS + 40U));
535*5d5fbe79SDavid van Moolenbroek   memset(&txcounters, 0, sizeof(txcounters));
536*5d5fbe79SDavid van Moolenbroek 
537*5d5fbe79SDavid van Moolenbroek   check_seqnos(pcb->unacked, 2, seqnos);
538*5d5fbe79SDavid van Moolenbroek   check_seqnos(pcb->unsent, 4, &seqnos[2]);
539*5d5fbe79SDavid van Moolenbroek 
540*5d5fbe79SDavid van Moolenbroek   /* call the tcp timer some times */
541*5d5fbe79SDavid van Moolenbroek   for (i = 0; i < 10; i++) {
542*5d5fbe79SDavid van Moolenbroek     test_tcp_tmr();
543*5d5fbe79SDavid van Moolenbroek     EXPECT(txcounters.num_tx_calls == 0);
544*5d5fbe79SDavid van Moolenbroek   }
545*5d5fbe79SDavid van Moolenbroek   /* 11th call to tcp_tmr: RTO rexmit fires */
546*5d5fbe79SDavid van Moolenbroek   test_tcp_tmr();
547*5d5fbe79SDavid van Moolenbroek   EXPECT(txcounters.num_tx_calls == 1);
548*5d5fbe79SDavid van Moolenbroek   check_seqnos(pcb->unacked, 1, seqnos);
549*5d5fbe79SDavid van Moolenbroek   check_seqnos(pcb->unsent, 5, &seqnos[1]);
550*5d5fbe79SDavid van Moolenbroek 
551*5d5fbe79SDavid van Moolenbroek   /* fake greater cwnd */
552*5d5fbe79SDavid van Moolenbroek   pcb->cwnd = pcb->snd_wnd;
553*5d5fbe79SDavid van Moolenbroek   /* send more data */
554*5d5fbe79SDavid van Moolenbroek   err = tcp_output(pcb);
555*5d5fbe79SDavid van Moolenbroek   EXPECT(err == ERR_OK);
556*5d5fbe79SDavid van Moolenbroek   /* check queues are sorted */
557*5d5fbe79SDavid van Moolenbroek   EXPECT(pcb->unsent == NULL);
558*5d5fbe79SDavid van Moolenbroek   check_seqnos(pcb->unacked, 6, seqnos);
559*5d5fbe79SDavid van Moolenbroek 
560*5d5fbe79SDavid van Moolenbroek   /* make sure the pcb is freed */
561*5d5fbe79SDavid van Moolenbroek   EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1);
562*5d5fbe79SDavid van Moolenbroek   tcp_abort(pcb);
563*5d5fbe79SDavid van Moolenbroek   EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0);
564*5d5fbe79SDavid van Moolenbroek }
565*5d5fbe79SDavid van Moolenbroek END_TEST
566*5d5fbe79SDavid van Moolenbroek 
567*5d5fbe79SDavid van Moolenbroek /** Provoke fast retransmission by duplicate ACKs and then recover by ACKing all sent data.
568*5d5fbe79SDavid van Moolenbroek  * At the end, send more data. */
test_tcp_tx_full_window_lost(u8_t zero_window_probe_from_unsent)569*5d5fbe79SDavid van Moolenbroek static void test_tcp_tx_full_window_lost(u8_t zero_window_probe_from_unsent)
570*5d5fbe79SDavid van Moolenbroek {
571*5d5fbe79SDavid van Moolenbroek   struct netif netif;
572*5d5fbe79SDavid van Moolenbroek   struct test_tcp_txcounters txcounters;
573*5d5fbe79SDavid van Moolenbroek   struct test_tcp_counters counters;
574*5d5fbe79SDavid van Moolenbroek   struct tcp_pcb* pcb;
575*5d5fbe79SDavid van Moolenbroek   struct pbuf *p;
576*5d5fbe79SDavid van Moolenbroek   ip_addr_t remote_ip, local_ip, netmask;
577*5d5fbe79SDavid van Moolenbroek   u16_t remote_port = 0x100, local_port = 0x101;
578*5d5fbe79SDavid van Moolenbroek   err_t err;
579*5d5fbe79SDavid van Moolenbroek   u16_t sent_total, i;
580*5d5fbe79SDavid van Moolenbroek   u8_t expected = 0xFE;
581*5d5fbe79SDavid van Moolenbroek 
582*5d5fbe79SDavid van Moolenbroek   for (i = 0; i < sizeof(tx_data); i++) {
583*5d5fbe79SDavid van Moolenbroek     u8_t d = (u8_t)i;
584*5d5fbe79SDavid van Moolenbroek     if (d == 0xFE) {
585*5d5fbe79SDavid van Moolenbroek       d = 0xF0;
586*5d5fbe79SDavid van Moolenbroek     }
587*5d5fbe79SDavid van Moolenbroek     tx_data[i] = d;
588*5d5fbe79SDavid van Moolenbroek   }
589*5d5fbe79SDavid van Moolenbroek   if (zero_window_probe_from_unsent) {
590*5d5fbe79SDavid van Moolenbroek     tx_data[TCP_WND] = expected;
591*5d5fbe79SDavid van Moolenbroek   } else {
592*5d5fbe79SDavid van Moolenbroek     tx_data[0] = expected;
593*5d5fbe79SDavid van Moolenbroek   }
594*5d5fbe79SDavid van Moolenbroek 
595*5d5fbe79SDavid van Moolenbroek   /* initialize local vars */
596*5d5fbe79SDavid van Moolenbroek   IP_ADDR4(&local_ip,  192, 168,   1, 1);
597*5d5fbe79SDavid van Moolenbroek   IP_ADDR4(&remote_ip, 192, 168,   1, 2);
598*5d5fbe79SDavid van Moolenbroek   IP_ADDR4(&netmask,   255, 255, 255, 0);
599*5d5fbe79SDavid van Moolenbroek   test_tcp_init_netif(&netif, &txcounters, &local_ip, &netmask);
600*5d5fbe79SDavid van Moolenbroek   memset(&counters, 0, sizeof(counters));
601*5d5fbe79SDavid van Moolenbroek   memset(&txcounters, 0, sizeof(txcounters));
602*5d5fbe79SDavid van Moolenbroek 
603*5d5fbe79SDavid van Moolenbroek   /* create and initialize the pcb */
604*5d5fbe79SDavid van Moolenbroek   pcb = test_tcp_new_counters_pcb(&counters);
605*5d5fbe79SDavid van Moolenbroek   EXPECT_RET(pcb != NULL);
606*5d5fbe79SDavid van Moolenbroek   tcp_set_state(pcb, ESTABLISHED, &local_ip, &remote_ip, local_port, remote_port);
607*5d5fbe79SDavid van Moolenbroek   pcb->mss = TCP_MSS;
608*5d5fbe79SDavid van Moolenbroek   /* disable initial congestion window (we don't send a SYN here...) */
609*5d5fbe79SDavid van Moolenbroek   pcb->cwnd = pcb->snd_wnd;
610*5d5fbe79SDavid van Moolenbroek 
611*5d5fbe79SDavid van Moolenbroek   /* send a full window (minus 1 packets) of TCP data in MSS-sized chunks */
612*5d5fbe79SDavid van Moolenbroek   sent_total = 0;
613*5d5fbe79SDavid van Moolenbroek   if ((TCP_WND - TCP_MSS) % TCP_MSS != 0) {
614*5d5fbe79SDavid van Moolenbroek     u16_t initial_data_len = (TCP_WND - TCP_MSS) % TCP_MSS;
615*5d5fbe79SDavid van Moolenbroek     err = tcp_write(pcb, &tx_data[sent_total], initial_data_len, TCP_WRITE_FLAG_COPY);
616*5d5fbe79SDavid van Moolenbroek     EXPECT_RET(err == ERR_OK);
617*5d5fbe79SDavid van Moolenbroek     err = tcp_output(pcb);
618*5d5fbe79SDavid van Moolenbroek     EXPECT_RET(err == ERR_OK);
619*5d5fbe79SDavid van Moolenbroek     EXPECT(txcounters.num_tx_calls == 1);
620*5d5fbe79SDavid van Moolenbroek     EXPECT(txcounters.num_tx_bytes == initial_data_len + 40U);
621*5d5fbe79SDavid van Moolenbroek     memset(&txcounters, 0, sizeof(txcounters));
622*5d5fbe79SDavid van Moolenbroek     sent_total += initial_data_len;
623*5d5fbe79SDavid van Moolenbroek   }
624*5d5fbe79SDavid van Moolenbroek   for (; sent_total < (TCP_WND - TCP_MSS); sent_total += TCP_MSS) {
625*5d5fbe79SDavid van Moolenbroek     err = tcp_write(pcb, &tx_data[sent_total], TCP_MSS, TCP_WRITE_FLAG_COPY);
626*5d5fbe79SDavid van Moolenbroek     EXPECT_RET(err == ERR_OK);
627*5d5fbe79SDavid van Moolenbroek     err = tcp_output(pcb);
628*5d5fbe79SDavid van Moolenbroek     EXPECT_RET(err == ERR_OK);
629*5d5fbe79SDavid van Moolenbroek     EXPECT(txcounters.num_tx_calls == 1);
630*5d5fbe79SDavid van Moolenbroek     EXPECT(txcounters.num_tx_bytes == TCP_MSS + 40U);
631*5d5fbe79SDavid van Moolenbroek     memset(&txcounters, 0, sizeof(txcounters));
632*5d5fbe79SDavid van Moolenbroek   }
633*5d5fbe79SDavid van Moolenbroek   EXPECT(sent_total == (TCP_WND - TCP_MSS));
634*5d5fbe79SDavid van Moolenbroek 
635*5d5fbe79SDavid van Moolenbroek   /* now ACK the packet before the first */
636*5d5fbe79SDavid van Moolenbroek   p = tcp_create_rx_segment(pcb, NULL, 0, 0, 0, TCP_ACK);
637*5d5fbe79SDavid van Moolenbroek   test_tcp_input(p, &netif);
638*5d5fbe79SDavid van Moolenbroek   /* ensure this didn't trigger a retransmission */
639*5d5fbe79SDavid van Moolenbroek   EXPECT(txcounters.num_tx_calls == 0);
640*5d5fbe79SDavid van Moolenbroek   EXPECT(txcounters.num_tx_bytes == 0);
641*5d5fbe79SDavid van Moolenbroek 
642*5d5fbe79SDavid van Moolenbroek   EXPECT(pcb->persist_backoff == 0);
643*5d5fbe79SDavid van Moolenbroek   /* send the last packet, now a complete window has been sent */
644*5d5fbe79SDavid van Moolenbroek   err = tcp_write(pcb, &tx_data[sent_total], TCP_MSS, TCP_WRITE_FLAG_COPY);
645*5d5fbe79SDavid van Moolenbroek   sent_total += TCP_MSS;
646*5d5fbe79SDavid van Moolenbroek   EXPECT_RET(err == ERR_OK);
647*5d5fbe79SDavid van Moolenbroek   err = tcp_output(pcb);
648*5d5fbe79SDavid van Moolenbroek   EXPECT_RET(err == ERR_OK);
649*5d5fbe79SDavid van Moolenbroek   EXPECT(txcounters.num_tx_calls == 1);
650*5d5fbe79SDavid van Moolenbroek   EXPECT(txcounters.num_tx_bytes == TCP_MSS + 40U);
651*5d5fbe79SDavid van Moolenbroek   memset(&txcounters, 0, sizeof(txcounters));
652*5d5fbe79SDavid van Moolenbroek   EXPECT(pcb->persist_backoff == 0);
653*5d5fbe79SDavid van Moolenbroek 
654*5d5fbe79SDavid van Moolenbroek   if (zero_window_probe_from_unsent) {
655*5d5fbe79SDavid van Moolenbroek     /* ACK all data but close the TX window */
656*5d5fbe79SDavid van Moolenbroek     p = tcp_create_rx_segment_wnd(pcb, NULL, 0, 0, TCP_WND, TCP_ACK, 0);
657*5d5fbe79SDavid van Moolenbroek     test_tcp_input(p, &netif);
658*5d5fbe79SDavid van Moolenbroek     /* ensure this didn't trigger any transmission */
659*5d5fbe79SDavid van Moolenbroek     EXPECT(txcounters.num_tx_calls == 0);
660*5d5fbe79SDavid van Moolenbroek     EXPECT(txcounters.num_tx_bytes == 0);
661*5d5fbe79SDavid van Moolenbroek     EXPECT(pcb->persist_backoff == 1);
662*5d5fbe79SDavid van Moolenbroek   }
663*5d5fbe79SDavid van Moolenbroek 
664*5d5fbe79SDavid van Moolenbroek   /* send one byte more (out of window) -> persist timer starts */
665*5d5fbe79SDavid van Moolenbroek   err = tcp_write(pcb, &tx_data[sent_total], 1, TCP_WRITE_FLAG_COPY);
666*5d5fbe79SDavid van Moolenbroek   EXPECT_RET(err == ERR_OK);
667*5d5fbe79SDavid van Moolenbroek   err = tcp_output(pcb);
668*5d5fbe79SDavid van Moolenbroek   EXPECT_RET(err == ERR_OK);
669*5d5fbe79SDavid van Moolenbroek   EXPECT(txcounters.num_tx_calls == 0);
670*5d5fbe79SDavid van Moolenbroek   EXPECT(txcounters.num_tx_bytes == 0);
671*5d5fbe79SDavid van Moolenbroek   memset(&txcounters, 0, sizeof(txcounters));
672*5d5fbe79SDavid van Moolenbroek   if (!zero_window_probe_from_unsent) {
673*5d5fbe79SDavid van Moolenbroek     /* no persist timer unless a zero window announcement has been received */
674*5d5fbe79SDavid van Moolenbroek     EXPECT(pcb->persist_backoff == 0);
675*5d5fbe79SDavid van Moolenbroek   } else {
676*5d5fbe79SDavid van Moolenbroek     EXPECT(pcb->persist_backoff == 1);
677*5d5fbe79SDavid van Moolenbroek 
678*5d5fbe79SDavid van Moolenbroek     /* call tcp_timer some more times to let persist timer count up */
679*5d5fbe79SDavid van Moolenbroek     for (i = 0; i < 4; i++) {
680*5d5fbe79SDavid van Moolenbroek       test_tcp_tmr();
681*5d5fbe79SDavid van Moolenbroek       EXPECT(txcounters.num_tx_calls == 0);
682*5d5fbe79SDavid van Moolenbroek       EXPECT(txcounters.num_tx_bytes == 0);
683*5d5fbe79SDavid van Moolenbroek     }
684*5d5fbe79SDavid van Moolenbroek 
685*5d5fbe79SDavid van Moolenbroek     /* this should trigger the zero-window-probe */
686*5d5fbe79SDavid van Moolenbroek     txcounters.copy_tx_packets = 1;
687*5d5fbe79SDavid van Moolenbroek     test_tcp_tmr();
688*5d5fbe79SDavid van Moolenbroek     txcounters.copy_tx_packets = 0;
689*5d5fbe79SDavid van Moolenbroek     EXPECT(txcounters.num_tx_calls == 1);
690*5d5fbe79SDavid van Moolenbroek     EXPECT(txcounters.num_tx_bytes == 1 + 40U);
691*5d5fbe79SDavid van Moolenbroek     EXPECT(txcounters.tx_packets != NULL);
692*5d5fbe79SDavid van Moolenbroek     if (txcounters.tx_packets != NULL) {
693*5d5fbe79SDavid van Moolenbroek       u8_t sent;
694*5d5fbe79SDavid van Moolenbroek       u16_t ret;
695*5d5fbe79SDavid van Moolenbroek       ret = pbuf_copy_partial(txcounters.tx_packets, &sent, 1, 40U);
696*5d5fbe79SDavid van Moolenbroek       EXPECT(ret == 1);
697*5d5fbe79SDavid van Moolenbroek       EXPECT(sent == expected);
698*5d5fbe79SDavid van Moolenbroek     }
699*5d5fbe79SDavid van Moolenbroek     if (txcounters.tx_packets != NULL) {
700*5d5fbe79SDavid van Moolenbroek       pbuf_free(txcounters.tx_packets);
701*5d5fbe79SDavid van Moolenbroek       txcounters.tx_packets = NULL;
702*5d5fbe79SDavid van Moolenbroek     }
703*5d5fbe79SDavid van Moolenbroek   }
704*5d5fbe79SDavid van Moolenbroek 
705*5d5fbe79SDavid van Moolenbroek   /* make sure the pcb is freed */
706*5d5fbe79SDavid van Moolenbroek   EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1);
707*5d5fbe79SDavid van Moolenbroek   tcp_abort(pcb);
708*5d5fbe79SDavid van Moolenbroek   EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0);
709*5d5fbe79SDavid van Moolenbroek }
710*5d5fbe79SDavid van Moolenbroek 
START_TEST(test_tcp_tx_full_window_lost_from_unsent)711*5d5fbe79SDavid van Moolenbroek START_TEST(test_tcp_tx_full_window_lost_from_unsent)
712*5d5fbe79SDavid van Moolenbroek {
713*5d5fbe79SDavid van Moolenbroek   LWIP_UNUSED_ARG(_i);
714*5d5fbe79SDavid van Moolenbroek   test_tcp_tx_full_window_lost(1);
715*5d5fbe79SDavid van Moolenbroek }
716*5d5fbe79SDavid van Moolenbroek END_TEST
717*5d5fbe79SDavid van Moolenbroek 
START_TEST(test_tcp_tx_full_window_lost_from_unacked)718*5d5fbe79SDavid van Moolenbroek START_TEST(test_tcp_tx_full_window_lost_from_unacked)
719*5d5fbe79SDavid van Moolenbroek {
720*5d5fbe79SDavid van Moolenbroek   LWIP_UNUSED_ARG(_i);
721*5d5fbe79SDavid van Moolenbroek   test_tcp_tx_full_window_lost(0);
722*5d5fbe79SDavid van Moolenbroek }
723*5d5fbe79SDavid van Moolenbroek END_TEST
724*5d5fbe79SDavid van Moolenbroek 
725*5d5fbe79SDavid van Moolenbroek /** Create the suite including all tests for this module */
726*5d5fbe79SDavid van Moolenbroek Suite *
tcp_suite(void)727*5d5fbe79SDavid van Moolenbroek tcp_suite(void)
728*5d5fbe79SDavid van Moolenbroek {
729*5d5fbe79SDavid van Moolenbroek   testfunc tests[] = {
730*5d5fbe79SDavid van Moolenbroek     TESTFUNC(test_tcp_new_abort),
731*5d5fbe79SDavid van Moolenbroek     TESTFUNC(test_tcp_recv_inseq),
732*5d5fbe79SDavid van Moolenbroek     TESTFUNC(test_tcp_malformed_header),
733*5d5fbe79SDavid van Moolenbroek     TESTFUNC(test_tcp_fast_retx_recover),
734*5d5fbe79SDavid van Moolenbroek     TESTFUNC(test_tcp_fast_rexmit_wraparound),
735*5d5fbe79SDavid van Moolenbroek     TESTFUNC(test_tcp_rto_rexmit_wraparound),
736*5d5fbe79SDavid van Moolenbroek     TESTFUNC(test_tcp_tx_full_window_lost_from_unacked),
737*5d5fbe79SDavid van Moolenbroek     TESTFUNC(test_tcp_tx_full_window_lost_from_unsent)
738*5d5fbe79SDavid van Moolenbroek   };
739*5d5fbe79SDavid van Moolenbroek   return create_suite("TCP", tests, sizeof(tests)/sizeof(testfunc), tcp_setup, tcp_teardown);
740*5d5fbe79SDavid van Moolenbroek }
741