mirror of
https://github.com/lwip-tcpip/lwip.git
synced 2025-01-27 12:35:26 +00:00
Added a test for fast-rexmit
This commit is contained in:
parent
a444ec5111
commit
33a587d97e
@ -45,7 +45,7 @@ tcp_create_rx_segment(struct tcp_pcb* pcb, void* data, size_t data_len, u32_t se
|
|||||||
u32_t ackno_offset, u8_t headerflags)
|
u32_t ackno_offset, u8_t headerflags)
|
||||||
{
|
{
|
||||||
return tcp_create_segment(&pcb->remote_ip, &pcb->local_ip, pcb->remote_port, pcb->local_port,
|
return tcp_create_segment(&pcb->remote_ip, &pcb->local_ip, pcb->remote_port, pcb->local_port,
|
||||||
data, data_len, pcb->rcv_nxt + seqno_offset, pcb->snd_nxt + ackno_offset, headerflags);
|
data, data_len, pcb->rcv_nxt + seqno_offset, pcb->lastack + ackno_offset, headerflags);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Create a TCP segment usable for passing to tcp_input */
|
/** Create a TCP segment usable for passing to tcp_input */
|
||||||
@ -191,6 +191,7 @@ test_tcp_new_counters_pcb(struct test_tcp_counters* counters)
|
|||||||
tcp_arg(pcb, counters);
|
tcp_arg(pcb, counters);
|
||||||
tcp_recv(pcb, test_tcp_counters_recv);
|
tcp_recv(pcb, test_tcp_counters_recv);
|
||||||
tcp_err(pcb, test_tcp_counters_err);
|
tcp_err(pcb, test_tcp_counters_err);
|
||||||
|
pcb->snd_wnd = TCP_WND;
|
||||||
}
|
}
|
||||||
return pcb;
|
return pcb;
|
||||||
}
|
}
|
||||||
@ -211,3 +212,38 @@ void test_tcp_input(struct pbuf *p, struct netif *inp)
|
|||||||
current_netif = NULL;
|
current_netif = NULL;
|
||||||
current_header = NULL;
|
current_header = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static err_t test_tcp_netif_output(struct netif *netif, struct pbuf *p,
|
||||||
|
ip_addr_t *ipaddr)
|
||||||
|
{
|
||||||
|
struct test_tcp_txcounters *txcounters = (struct test_tcp_txcounters*)netif->state;
|
||||||
|
LWIP_UNUSED_ARG(ipaddr);
|
||||||
|
txcounters->num_tx_calls++;
|
||||||
|
txcounters->num_tx_bytes += p->tot_len;
|
||||||
|
/*if (txcounters->tx_packets == NULL) {
|
||||||
|
txcounters->tx_packets = p;
|
||||||
|
} else {
|
||||||
|
pbuf_cat(txcounters->tx_packets, p);
|
||||||
|
}*/
|
||||||
|
return ERR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_tcp_init_netif(struct netif *netif, struct test_tcp_txcounters *txcounters,
|
||||||
|
ip_addr_t *ip_addr, ip_addr_t *netmask)
|
||||||
|
{
|
||||||
|
struct netif *n;
|
||||||
|
memset(netif, 0, sizeof(struct netif));
|
||||||
|
memset(txcounters, 0, sizeof(struct test_tcp_txcounters));
|
||||||
|
netif->output = test_tcp_netif_output;
|
||||||
|
netif->state = txcounters;
|
||||||
|
netif->flags |= NETIF_FLAG_UP;
|
||||||
|
ip_addr_copy(netif->netmask, *netmask);
|
||||||
|
ip_addr_copy(netif->ip_addr, *ip_addr);
|
||||||
|
for (n = netif_list; n != NULL; n = n->next) {
|
||||||
|
if (n == netif) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
netif->next = NULL;
|
||||||
|
netif_list = netif;
|
||||||
|
}
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#include "../lwip_check.h"
|
#include "../lwip_check.h"
|
||||||
#include "lwip/arch.h"
|
#include "lwip/arch.h"
|
||||||
#include "lwip/tcp.h"
|
#include "lwip/tcp.h"
|
||||||
|
#include "lwip/netif.h"
|
||||||
|
|
||||||
/* counters used for test_tcp_counters_* callback functions */
|
/* counters used for test_tcp_counters_* callback functions */
|
||||||
struct test_tcp_counters {
|
struct test_tcp_counters {
|
||||||
@ -18,6 +19,11 @@ struct test_tcp_counters {
|
|||||||
u32_t expected_data_len;
|
u32_t expected_data_len;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct test_tcp_txcounters {
|
||||||
|
u32_t num_tx_calls;
|
||||||
|
u32_t num_tx_bytes;
|
||||||
|
};
|
||||||
|
|
||||||
/* Helper functions */
|
/* Helper functions */
|
||||||
void tcp_remove_all(void);
|
void tcp_remove_all(void);
|
||||||
|
|
||||||
@ -35,4 +41,8 @@ struct tcp_pcb* test_tcp_new_counters_pcb(struct test_tcp_counters* counters);
|
|||||||
|
|
||||||
void test_tcp_input(struct pbuf *p, struct netif *inp);
|
void test_tcp_input(struct pbuf *p, struct netif *inp);
|
||||||
|
|
||||||
|
void test_tcp_init_netif(struct netif *netif, struct test_tcp_txcounters *txcounters,
|
||||||
|
ip_addr_t *ip_addr, ip_addr_t *netmask);
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -19,6 +19,7 @@ tcp_setup(void)
|
|||||||
static void
|
static void
|
||||||
tcp_teardown(void)
|
tcp_teardown(void)
|
||||||
{
|
{
|
||||||
|
netif_list = NULL;
|
||||||
tcp_remove_all();
|
tcp_remove_all();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,6 +92,179 @@ START_TEST(test_tcp_recv_inseq)
|
|||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
|
/** Provoke fast retransmission by duplicate ACKs and then recover by ACKing all sent data.
|
||||||
|
* At the end, send more data. */
|
||||||
|
START_TEST(test_tcp_fast_retx_recover)
|
||||||
|
{
|
||||||
|
struct netif netif;
|
||||||
|
struct test_tcp_txcounters txcounters;
|
||||||
|
struct test_tcp_counters counters;
|
||||||
|
struct tcp_pcb* pcb;
|
||||||
|
struct pbuf* p;
|
||||||
|
char data1[] = { 1, 2, 3, 4};
|
||||||
|
char data2[] = { 5, 6, 7, 8};
|
||||||
|
char data3[] = { 9, 10, 11, 12};
|
||||||
|
char data4[] = {13, 14, 15, 16};
|
||||||
|
char data5[] = {17, 18, 19, 20};
|
||||||
|
char data6[] = {21, 22, 23, 24};
|
||||||
|
ip_addr_t remote_ip, local_ip, netmask;
|
||||||
|
u16_t remote_port = 0x100, local_port = 0x101;
|
||||||
|
err_t err;
|
||||||
|
LWIP_UNUSED_ARG(_i);
|
||||||
|
|
||||||
|
/* initialize local vars */
|
||||||
|
IP4_ADDR(&local_ip, 192, 168, 1, 1);
|
||||||
|
IP4_ADDR(&remote_ip, 192, 168, 1, 2);
|
||||||
|
IP4_ADDR(&netmask, 255, 255, 255, 0);
|
||||||
|
test_tcp_init_netif(&netif, &txcounters, &local_ip, &netmask);
|
||||||
|
memset(&counters, 0, sizeof(counters));
|
||||||
|
|
||||||
|
/* create and initialize the pcb */
|
||||||
|
pcb = test_tcp_new_counters_pcb(&counters);
|
||||||
|
EXPECT_RET(pcb != NULL);
|
||||||
|
tcp_set_state(pcb, ESTABLISHED, &local_ip, &remote_ip, local_port, remote_port);
|
||||||
|
pcb->mss = TCP_MSS;
|
||||||
|
/* disable initial congestion window (we don't send a SYN here...) */
|
||||||
|
pcb->cwnd = pcb->snd_wnd;
|
||||||
|
//tcp_nagle_disable(pcb);
|
||||||
|
|
||||||
|
/* send data1 */
|
||||||
|
err = tcp_write(pcb, data1, sizeof(data1), TCP_WRITE_FLAG_COPY);
|
||||||
|
EXPECT_RET(err == ERR_OK);
|
||||||
|
err = tcp_output(pcb);
|
||||||
|
EXPECT_RET(err == ERR_OK);
|
||||||
|
EXPECT_RET(txcounters.num_tx_calls == 1);
|
||||||
|
EXPECT_RET(txcounters.num_tx_bytes == sizeof(data1) + sizeof(struct tcp_hdr) + sizeof(struct ip_hdr));
|
||||||
|
memset(&txcounters, 0, sizeof(txcounters));
|
||||||
|
/* "recv" ACK for data1 */
|
||||||
|
p = tcp_create_rx_segment(pcb, NULL, 0, 0, 4, TCP_ACK);
|
||||||
|
EXPECT_RET(p != NULL);
|
||||||
|
test_tcp_input(p, &netif);
|
||||||
|
EXPECT_RET(txcounters.num_tx_calls == 0);
|
||||||
|
EXPECT_RET(pcb->unacked == NULL);
|
||||||
|
/* send data2 */
|
||||||
|
err = tcp_write(pcb, data2, sizeof(data2), TCP_WRITE_FLAG_COPY);
|
||||||
|
EXPECT_RET(err == ERR_OK);
|
||||||
|
err = tcp_output(pcb);
|
||||||
|
EXPECT_RET(err == ERR_OK);
|
||||||
|
EXPECT_RET(txcounters.num_tx_calls == 1);
|
||||||
|
EXPECT_RET(txcounters.num_tx_bytes == sizeof(data2) + sizeof(struct tcp_hdr) + sizeof(struct ip_hdr));
|
||||||
|
memset(&txcounters, 0, sizeof(txcounters));
|
||||||
|
/* duplicate ACK for data1 (data2 is lost) */
|
||||||
|
p = tcp_create_rx_segment(pcb, NULL, 0, 0, 0, TCP_ACK);
|
||||||
|
EXPECT_RET(p != NULL);
|
||||||
|
test_tcp_input(p, &netif);
|
||||||
|
EXPECT_RET(txcounters.num_tx_calls == 0);
|
||||||
|
EXPECT_RET(pcb->dupacks == 1);
|
||||||
|
/* send data3 */
|
||||||
|
err = tcp_write(pcb, data3, sizeof(data3), TCP_WRITE_FLAG_COPY);
|
||||||
|
EXPECT_RET(err == ERR_OK);
|
||||||
|
err = tcp_output(pcb);
|
||||||
|
EXPECT_RET(err == ERR_OK);
|
||||||
|
/* nagle enabled, no tx calls */
|
||||||
|
EXPECT_RET(txcounters.num_tx_calls == 0);
|
||||||
|
EXPECT_RET(txcounters.num_tx_bytes == 0);
|
||||||
|
memset(&txcounters, 0, sizeof(txcounters));
|
||||||
|
/* 2nd duplicate ACK for data1 (data2 and data3 are lost) */
|
||||||
|
p = tcp_create_rx_segment(pcb, NULL, 0, 0, 0, TCP_ACK);
|
||||||
|
EXPECT_RET(p != NULL);
|
||||||
|
test_tcp_input(p, &netif);
|
||||||
|
EXPECT_RET(txcounters.num_tx_calls == 0);
|
||||||
|
EXPECT_RET(pcb->dupacks == 2);
|
||||||
|
/* queue data4, don't send it (unsent-oversize is != 0) */
|
||||||
|
err = tcp_write(pcb, data4, sizeof(data4), TCP_WRITE_FLAG_COPY);
|
||||||
|
EXPECT_RET(err == ERR_OK);
|
||||||
|
/* 3nd duplicate ACK for data1 (data2 and data3 are lost) -> fast retransmission */
|
||||||
|
p = tcp_create_rx_segment(pcb, NULL, 0, 0, 0, TCP_ACK);
|
||||||
|
EXPECT_RET(p != NULL);
|
||||||
|
test_tcp_input(p, &netif);
|
||||||
|
//EXPECT_RET(txcounters.num_tx_calls == 1);
|
||||||
|
EXPECT_RET(pcb->dupacks == 3);
|
||||||
|
memset(&txcounters, 0, sizeof(txcounters));
|
||||||
|
// TODO: check expected data?
|
||||||
|
|
||||||
|
/* send data5, not output yet */
|
||||||
|
err = tcp_write(pcb, data5, sizeof(data5), TCP_WRITE_FLAG_COPY);
|
||||||
|
EXPECT_RET(err == ERR_OK);
|
||||||
|
//err = tcp_output(pcb);
|
||||||
|
//EXPECT_RET(err == ERR_OK);
|
||||||
|
EXPECT_RET(txcounters.num_tx_calls == 0);
|
||||||
|
EXPECT_RET(txcounters.num_tx_bytes == 0);
|
||||||
|
memset(&txcounters, 0, sizeof(txcounters));
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
err = tcp_write(pcb, data6, TCP_MSS, TCP_WRITE_FLAG_COPY);
|
||||||
|
i++;
|
||||||
|
}while(err == ERR_OK);
|
||||||
|
EXPECT_RET(err != ERR_OK);
|
||||||
|
}
|
||||||
|
//err = tcp_output(pcb);
|
||||||
|
//EXPECT_RET(err == ERR_OK);
|
||||||
|
EXPECT_RET(txcounters.num_tx_calls == 0);
|
||||||
|
EXPECT_RET(txcounters.num_tx_bytes == 0);
|
||||||
|
memset(&txcounters, 0, sizeof(txcounters));
|
||||||
|
|
||||||
|
/* send even more data */
|
||||||
|
err = tcp_write(pcb, data5, sizeof(data5), TCP_WRITE_FLAG_COPY);
|
||||||
|
EXPECT_RET(err == ERR_OK);
|
||||||
|
err = tcp_output(pcb);
|
||||||
|
EXPECT_RET(err == ERR_OK);
|
||||||
|
/* ...and even more data */
|
||||||
|
err = tcp_write(pcb, data5, sizeof(data5), TCP_WRITE_FLAG_COPY);
|
||||||
|
EXPECT_RET(err == ERR_OK);
|
||||||
|
err = tcp_output(pcb);
|
||||||
|
EXPECT_RET(err == ERR_OK);
|
||||||
|
/* ...and even more data */
|
||||||
|
err = tcp_write(pcb, data5, sizeof(data5), TCP_WRITE_FLAG_COPY);
|
||||||
|
EXPECT_RET(err == ERR_OK);
|
||||||
|
err = tcp_output(pcb);
|
||||||
|
EXPECT_RET(err == ERR_OK);
|
||||||
|
/* ...and even more data */
|
||||||
|
err = tcp_write(pcb, data5, sizeof(data5), TCP_WRITE_FLAG_COPY);
|
||||||
|
EXPECT_RET(err == ERR_OK);
|
||||||
|
err = tcp_output(pcb);
|
||||||
|
EXPECT_RET(err == ERR_OK);
|
||||||
|
|
||||||
|
/* send ACKs for data2 and data3 */
|
||||||
|
p = tcp_create_rx_segment(pcb, NULL, 0, 0, 12, TCP_ACK);
|
||||||
|
EXPECT_RET(p != NULL);
|
||||||
|
test_tcp_input(p, &netif);
|
||||||
|
EXPECT_RET(txcounters.num_tx_calls == 0);
|
||||||
|
|
||||||
|
/* ...and even more data */
|
||||||
|
err = tcp_write(pcb, data5, sizeof(data5), TCP_WRITE_FLAG_COPY);
|
||||||
|
EXPECT_RET(err == ERR_OK);
|
||||||
|
err = tcp_output(pcb);
|
||||||
|
EXPECT_RET(err == ERR_OK);
|
||||||
|
/* ...and even more data */
|
||||||
|
err = tcp_write(pcb, data5, sizeof(data5), TCP_WRITE_FLAG_COPY);
|
||||||
|
EXPECT_RET(err == ERR_OK);
|
||||||
|
err = tcp_output(pcb);
|
||||||
|
EXPECT_RET(err == ERR_OK);
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
/* create expected segment */
|
||||||
|
p1 = tcp_create_rx_segment(pcb, counters.expected_data, data_len, 0, 0, 0);
|
||||||
|
EXPECT_RET(p != NULL);
|
||||||
|
if (p != NULL) {
|
||||||
|
/* pass the segment to tcp_input */
|
||||||
|
test_tcp_input(p, &netif);
|
||||||
|
/* check if counters are as expected */
|
||||||
|
EXPECT_RET(counters.close_calls == 0);
|
||||||
|
EXPECT_RET(counters.recv_calls == 1);
|
||||||
|
EXPECT_RET(counters.recved_bytes == data_len);
|
||||||
|
EXPECT_RET(counters.err_calls == 0);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
/* make sure the pcb is freed */
|
||||||
|
EXPECT_RET(lwip_stats.memp[MEMP_TCP_PCB].used == 1);
|
||||||
|
tcp_abort(pcb);
|
||||||
|
EXPECT_RET(lwip_stats.memp[MEMP_TCP_PCB].used == 0);
|
||||||
|
}
|
||||||
|
END_TEST
|
||||||
|
|
||||||
|
|
||||||
/** Create the suite including all tests for this module */
|
/** Create the suite including all tests for this module */
|
||||||
Suite *
|
Suite *
|
||||||
@ -99,6 +273,7 @@ tcp_suite(void)
|
|||||||
TFun tests[] = {
|
TFun tests[] = {
|
||||||
test_tcp_new_abort,
|
test_tcp_new_abort,
|
||||||
test_tcp_recv_inseq,
|
test_tcp_recv_inseq,
|
||||||
|
test_tcp_fast_retx_recover,
|
||||||
};
|
};
|
||||||
return create_suite("TCP", tests, sizeof(tests)/sizeof(TFun), tcp_setup, tcp_teardown);
|
return create_suite("TCP", tests, sizeof(tests)/sizeof(TFun), tcp_setup, tcp_teardown);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user