| #include "test_tcp.h" |
| |
| #include "lwip/priv/tcp_priv.h" |
| #include "lwip/stats.h" |
| #include "tcp_helper.h" |
| #include "lwip/inet_chksum.h" |
| |
| #ifdef _MSC_VER |
| #pragma warning(disable: 4307) /* we explicitly wrap around TCP seqnos */ |
| #endif |
| |
| #if !LWIP_STATS || !TCP_STATS || !MEMP_STATS |
| #error "This tests needs TCP- and MEMP-statistics enabled" |
| #endif |
| #if TCP_SND_BUF <= TCP_WND |
| #error "This tests needs TCP_SND_BUF to be > TCP_WND" |
| #endif |
| |
| /* used with check_seqnos() */ |
| #define SEQNO1 (0xFFFFFF00 - TCP_MSS) |
| #define ISS 6510 |
| static u32_t seqnos[] = { |
| SEQNO1, |
| SEQNO1 + (1 * TCP_MSS), |
| SEQNO1 + (2 * TCP_MSS), |
| SEQNO1 + (3 * TCP_MSS), |
| SEQNO1 + (4 * TCP_MSS), |
| SEQNO1 + (5 * TCP_MSS) }; |
| |
| static u8_t test_tcp_timer; |
| |
| /* our own version of tcp_tmr so we can reset fast/slow timer state */ |
| static void |
| test_tcp_tmr(void) |
| { |
| tcp_fasttmr(); |
| if (++test_tcp_timer & 1) { |
| tcp_slowtmr(); |
| } |
| } |
| |
| /* Setups/teardown functions */ |
| static struct netif *old_netif_list; |
| static struct netif *old_netif_default; |
| |
| static void |
| tcp_setup(void) |
| { |
| struct tcp_pcb dummy_pcb; /* we need this for tcp_next_iss() only */ |
| |
| old_netif_list = netif_list; |
| old_netif_default = netif_default; |
| netif_list = NULL; |
| netif_default = NULL; |
| /* reset iss to default (6510) */ |
| tcp_ticks = 0; |
| tcp_ticks = 0 - (tcp_next_iss(&dummy_pcb) - 6510); |
| tcp_next_iss(&dummy_pcb); |
| tcp_ticks = 0; |
| |
| test_tcp_timer = 0; |
| tcp_remove_all(); |
| lwip_check_ensure_no_alloc(SKIP_POOL(MEMP_SYS_TIMEOUT)); |
| } |
| |
| static void |
| tcp_teardown(void) |
| { |
| netif_list = NULL; |
| netif_default = NULL; |
| tcp_remove_all(); |
| /* restore netif_list for next tests (e.g. loopif) */ |
| netif_list = old_netif_list; |
| netif_default = old_netif_default; |
| lwip_check_ensure_no_alloc(SKIP_POOL(MEMP_SYS_TIMEOUT)); |
| } |
| |
| |
| /* Test functions */ |
| |
| /** Call tcp_new() and tcp_abort() and test memp stats */ |
| START_TEST(test_tcp_new_abort) |
| { |
| struct tcp_pcb* pcb; |
| LWIP_UNUSED_ARG(_i); |
| |
| fail_unless(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0); |
| |
| pcb = tcp_new(); |
| fail_unless(pcb != NULL); |
| if (pcb != NULL) { |
| fail_unless(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1); |
| tcp_abort(pcb); |
| fail_unless(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0); |
| } |
| } |
| END_TEST |
| |
| /** Call tcp_new() and tcp_abort() and test memp stats */ |
| START_TEST(test_tcp_listen_passive_open) |
| { |
| struct tcp_pcb *pcb, *pcbl; |
| struct tcp_pcb_listen *lpcb; |
| struct netif netif; |
| struct test_tcp_txcounters txcounters; |
| struct test_tcp_counters counters; |
| struct pbuf *p; |
| ip_addr_t src_addr; |
| err_t err; |
| LWIP_UNUSED_ARG(_i); |
| |
| fail_unless(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0); |
| |
| test_tcp_init_netif(&netif, &txcounters, &test_local_ip, &test_netmask); |
| /* initialize counter struct */ |
| memset(&counters, 0, sizeof(counters)); |
| |
| pcb = tcp_new(); |
| EXPECT_RET(pcb != NULL); |
| err = tcp_bind(pcb, &netif.ip_addr, 1234); |
| EXPECT(err == ERR_OK); |
| pcbl = tcp_listen(pcb); |
| EXPECT_RET(pcbl != NULL); |
| EXPECT_RET(pcbl != pcb); |
| lpcb = (struct tcp_pcb_listen *)pcbl; |
| |
| ip_addr_set_ip4_u32_val(src_addr, lwip_htonl(lwip_ntohl(ip_addr_get_ip4_u32(&lpcb->local_ip)) + 1)); |
| |
| /* check correct syn packet */ |
| p = tcp_create_segment(&src_addr, &lpcb->local_ip, 12345, |
| lpcb->local_port, NULL, 0, 12345, 54321, TCP_SYN); |
| EXPECT(p != NULL); |
| if (p != NULL) { |
| /* pass the segment to tcp_input */ |
| test_tcp_input(p, &netif); |
| /* check if counters are as expected */ |
| EXPECT(txcounters.num_tx_calls == 1); |
| } |
| |
| /* check syn packet with short length */ |
| p = tcp_create_segment(&src_addr, &lpcb->local_ip, 12345, |
| lpcb->local_port, NULL, 0, 12345, 54321, TCP_SYN); |
| EXPECT(p != NULL); |
| EXPECT(p->next == NULL); |
| if ((p != NULL) && (p->next == NULL)) { |
| p->len -= 2; |
| p->tot_len -= 2; |
| /* pass the segment to tcp_input */ |
| test_tcp_input(p, &netif); |
| /* check if counters are as expected */ |
| EXPECT(txcounters.num_tx_calls == 1); |
| } |
| |
| tcp_close(pcbl); |
| } |
| END_TEST |
| |
| /** Create an ESTABLISHED pcb and check if receive callback is called */ |
| START_TEST(test_tcp_recv_inseq) |
| { |
| struct test_tcp_counters counters; |
| struct tcp_pcb* pcb; |
| struct pbuf* p; |
| char data[] = {1, 2, 3, 4}; |
| u16_t data_len; |
| struct netif netif; |
| struct test_tcp_txcounters txcounters; |
| LWIP_UNUSED_ARG(_i); |
| |
| /* initialize local vars */ |
| test_tcp_init_netif(&netif, &txcounters, &test_local_ip, &test_netmask); |
| data_len = sizeof(data); |
| /* initialize counter struct */ |
| memset(&counters, 0, sizeof(counters)); |
| counters.expected_data_len = data_len; |
| counters.expected_data = data; |
| |
| /* create and initialize the pcb */ |
| pcb = test_tcp_new_counters_pcb(&counters); |
| EXPECT_RET(pcb != NULL); |
| tcp_set_state(pcb, ESTABLISHED, &test_local_ip, &test_remote_ip, TEST_LOCAL_PORT, TEST_REMOTE_PORT); |
| |
| /* create a segment */ |
| p = tcp_create_rx_segment(pcb, counters.expected_data, data_len, 0, 0, 0); |
| EXPECT(p != NULL); |
| if (p != NULL) { |
| /* pass the segment to tcp_input */ |
| test_tcp_input(p, &netif); |
| /* check if counters are as expected */ |
| EXPECT(counters.close_calls == 0); |
| EXPECT(counters.recv_calls == 1); |
| EXPECT(counters.recved_bytes == data_len); |
| EXPECT(counters.err_calls == 0); |
| } |
| |
| /* make sure the pcb is freed */ |
| EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1); |
| tcp_abort(pcb); |
| EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0); |
| } |
| END_TEST |
| |
| /** Create an ESTABLISHED pcb and check if receive callback is called if a segment |
| * overlapping rcv_nxt is received */ |
| START_TEST(test_tcp_recv_inseq_trim) |
| { |
| struct test_tcp_counters counters; |
| struct tcp_pcb* pcb; |
| struct pbuf* p; |
| char data[PBUF_POOL_BUFSIZE*2]; |
| u16_t data_len; |
| struct netif netif; |
| struct test_tcp_txcounters txcounters; |
| const u32_t new_data_len = 40; |
| LWIP_UNUSED_ARG(_i); |
| |
| /* initialize local vars */ |
| test_tcp_init_netif(&netif, &txcounters, &test_local_ip, &test_netmask); |
| data_len = sizeof(data); |
| memset(data, 0, sizeof(data)); |
| /* initialize counter struct */ |
| memset(&counters, 0, sizeof(counters)); |
| counters.expected_data_len = data_len; |
| counters.expected_data = data; |
| |
| /* create and initialize the pcb */ |
| pcb = test_tcp_new_counters_pcb(&counters); |
| EXPECT_RET(pcb != NULL); |
| tcp_set_state(pcb, ESTABLISHED, &test_local_ip, &test_remote_ip, TEST_LOCAL_PORT, TEST_REMOTE_PORT); |
| |
| /* create a segment (with an overlapping/old seqno so that the new data begins in the 2nd pbuf) */ |
| p = tcp_create_rx_segment(pcb, counters.expected_data, data_len, (u32_t)(0-(data_len-new_data_len)), 0, 0); |
| EXPECT(p != NULL); |
| if (p != NULL) { |
| EXPECT(p->next != NULL); |
| if (p->next != NULL) { |
| EXPECT(p->next->next != NULL); |
| } |
| } |
| if ((p != NULL) && (p->next != NULL) && (p->next->next != NULL)) { |
| /* pass the segment to tcp_input */ |
| test_tcp_input(p, &netif); |
| /* check if counters are as expected */ |
| EXPECT(counters.close_calls == 0); |
| EXPECT(counters.recv_calls == 1); |
| EXPECT(counters.recved_bytes == new_data_len); |
| EXPECT(counters.err_calls == 0); |
| } |
| |
| /* make sure the pcb is freed */ |
| EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1); |
| tcp_abort(pcb); |
| EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0); |
| } |
| END_TEST |
| |
| static err_t test_tcp_recv_expect1byte(void* arg, struct tcp_pcb* pcb, struct pbuf* p, err_t err); |
| |
| static err_t |
| test_tcp_recv_expectclose(void* arg, struct tcp_pcb* pcb, struct pbuf* p, err_t err) |
| { |
| EXPECT_RETX(pcb != NULL, ERR_OK); |
| EXPECT_RETX(err == ERR_OK, ERR_OK); |
| LWIP_UNUSED_ARG(arg); |
| |
| if (p != NULL) { |
| fail(); |
| } else { |
| /* correct: FIN received; close our end, too */ |
| err_t err2 = tcp_close(pcb); |
| fail_unless(err2 == ERR_OK); |
| /* set back to some other rx function, just to not get here again */ |
| tcp_recv(pcb, test_tcp_recv_expect1byte); |
| } |
| return ERR_OK; |
| } |
| |
| static err_t |
| test_tcp_recv_expect1byte(void* arg, struct tcp_pcb* pcb, struct pbuf* p, err_t err) |
| { |
| EXPECT_RETX(pcb != NULL, ERR_OK); |
| EXPECT_RETX(err == ERR_OK, ERR_OK); |
| LWIP_UNUSED_ARG(arg); |
| |
| if (p != NULL) { |
| if ((p->len == 1) && (p->tot_len == 1)) { |
| tcp_recv(pcb, test_tcp_recv_expectclose); |
| } else { |
| fail(); |
| } |
| pbuf_free(p); |
| } else { |
| fail(); |
| } |
| return ERR_OK; |
| } |
| |
| START_TEST(test_tcp_passive_close) |
| { |
| struct test_tcp_counters counters; |
| struct tcp_pcb* pcb; |
| struct pbuf* p; |
| char data = 0x0f; |
| struct netif netif; |
| struct test_tcp_txcounters txcounters; |
| LWIP_UNUSED_ARG(_i); |
| |
| /* initialize local vars */ |
| test_tcp_init_netif(&netif, &txcounters, &test_local_ip, &test_netmask); |
| |
| /* initialize counter struct */ |
| memset(&counters, 0, sizeof(counters)); |
| counters.expected_data_len = 1; |
| counters.expected_data = &data; |
| |
| /* create and initialize the pcb */ |
| pcb = test_tcp_new_counters_pcb(&counters); |
| EXPECT_RET(pcb != NULL); |
| tcp_set_state(pcb, ESTABLISHED, &test_local_ip, &test_remote_ip, TEST_LOCAL_PORT, TEST_REMOTE_PORT); |
| |
| /* create a segment without data */ |
| p = tcp_create_rx_segment(pcb, &data, 1, 0, 0, TCP_FIN); |
| EXPECT(p != NULL); |
| if (p != NULL) { |
| tcp_recv(pcb, test_tcp_recv_expect1byte); |
| /* pass the segment to tcp_input */ |
| test_tcp_input(p, &netif); |
| } |
| /* don't free the pcb here (part of the test!) */ |
| } |
| END_TEST |
| |
| START_TEST(test_tcp_active_abort) |
| { |
| struct test_tcp_counters counters; |
| struct tcp_pcb* pcb; |
| char data = 0x0f; |
| struct netif netif; |
| struct test_tcp_txcounters txcounters; |
| LWIP_UNUSED_ARG(_i); |
| |
| memset(&txcounters, 0, sizeof(txcounters)); |
| |
| /* initialize local vars */ |
| test_tcp_init_netif(&netif, &txcounters, &test_local_ip, &test_netmask); |
| |
| /* initialize counter struct */ |
| memset(&counters, 0, sizeof(counters)); |
| counters.expected_data_len = 1; |
| counters.expected_data = &data; |
| |
| /* create and initialize the pcb */ |
| pcb = test_tcp_new_counters_pcb(&counters); |
| EXPECT_RET(pcb != NULL); |
| tcp_set_state(pcb, ESTABLISHED, &test_local_ip, &test_remote_ip, TEST_LOCAL_PORT, TEST_REMOTE_PORT); |
| |
| /* abort the pcb */ |
| EXPECT_RET(txcounters.num_tx_calls == 0); |
| txcounters.copy_tx_packets = 1; |
| tcp_abort(pcb); |
| txcounters.copy_tx_packets = 0; |
| EXPECT(txcounters.num_tx_calls == 1); |
| EXPECT(txcounters.num_tx_bytes == 40U); |
| EXPECT(txcounters.tx_packets != NULL); |
| if (txcounters.tx_packets != NULL) { |
| u16_t ret; |
| struct tcp_hdr tcphdr; |
| ret = pbuf_copy_partial(txcounters.tx_packets, &tcphdr, 20, 20); |
| EXPECT(ret == 20); |
| EXPECT(tcphdr.dest == PP_HTONS(TEST_REMOTE_PORT)); |
| EXPECT(tcphdr.src == PP_HTONS(TEST_LOCAL_PORT)); |
| pbuf_free(txcounters.tx_packets); |
| txcounters.tx_packets = NULL; |
| } |
| |
| /* don't free the pcb here (part of the test!) */ |
| } |
| END_TEST |
| |
| /** Check that we handle malformed tcp headers, and discard the pbuf(s) */ |
| START_TEST(test_tcp_malformed_header) |
| { |
| struct test_tcp_counters counters; |
| struct tcp_pcb* pcb; |
| struct pbuf* p; |
| char data[] = {1, 2, 3, 4}; |
| u16_t data_len, chksum; |
| struct netif netif; |
| struct test_tcp_txcounters txcounters; |
| struct tcp_hdr *hdr; |
| LWIP_UNUSED_ARG(_i); |
| |
| /* initialize local vars */ |
| test_tcp_init_netif(&netif, &txcounters, &test_local_ip, &test_netmask); |
| data_len = sizeof(data); |
| /* initialize counter struct */ |
| memset(&counters, 0, sizeof(counters)); |
| counters.expected_data_len = data_len; |
| counters.expected_data = data; |
| |
| /* create and initialize the pcb */ |
| pcb = test_tcp_new_counters_pcb(&counters); |
| EXPECT_RET(pcb != NULL); |
| tcp_set_state(pcb, ESTABLISHED, &test_local_ip, &test_remote_ip, TEST_LOCAL_PORT, TEST_REMOTE_PORT); |
| |
| /* create a segment */ |
| p = tcp_create_rx_segment(pcb, counters.expected_data, data_len, 0, 0, 0); |
| |
| pbuf_header(p, -(s16_t)sizeof(struct ip_hdr)); |
| |
| hdr = (struct tcp_hdr *)p->payload; |
| TCPH_HDRLEN_FLAGS_SET(hdr, 15, 0x3d1); |
| |
| hdr->chksum = 0; |
| |
| chksum = ip_chksum_pseudo(p, IP_PROTO_TCP, p->tot_len, |
| &test_remote_ip, &test_local_ip); |
| |
| hdr->chksum = chksum; |
| |
| pbuf_header(p, sizeof(struct ip_hdr)); |
| |
| EXPECT(p != NULL); |
| EXPECT(p->next == NULL); |
| if (p != NULL) { |
| /* pass the segment to tcp_input */ |
| test_tcp_input(p, &netif); |
| /* check if counters are as expected */ |
| EXPECT(counters.close_calls == 0); |
| EXPECT(counters.recv_calls == 0); |
| EXPECT(counters.recved_bytes == 0); |
| EXPECT(counters.err_calls == 0); |
| } |
| |
| /* make sure the pcb is freed */ |
| EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1); |
| tcp_abort(pcb); |
| EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0); |
| } |
| 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[TCP_MSS] = {21, 22, 23, 24}; |
| err_t err; |
| LWIP_UNUSED_ARG(_i); |
| |
| /* initialize local vars */ |
| test_tcp_init_netif(&netif, &txcounters, &test_local_ip, &test_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, &test_local_ip, &test_remote_ip, TEST_LOCAL_PORT, TEST_REMOTE_PORT); |
| pcb->mss = TCP_MSS; |
| /* disable initial congestion window (we don't send a SYN here...) */ |
| pcb->cwnd = pcb->snd_wnd; |
| |
| /* 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(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1); |
| tcp_abort(pcb); |
| EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0); |
| } |
| END_TEST |
| |
| static u8_t tx_data[TCP_WND*2]; |
| |
| static void |
| check_seqnos(struct tcp_seg *segs, int num_expected, u32_t *seqnos_expected) |
| { |
| struct tcp_seg *s = segs; |
| int i; |
| for (i = 0; i < num_expected; i++, s = s->next) { |
| EXPECT_RET(s != NULL); |
| EXPECT(s->tcphdr->seqno == htonl(seqnos_expected[i])); |
| } |
| EXPECT(s == NULL); |
| } |
| |
| /** Send data with sequence numbers that wrap around the u32_t range. |
| * Then, provoke fast retransmission by duplicate ACKs and check that all |
| * segment lists are still properly sorted. */ |
| START_TEST(test_tcp_fast_rexmit_wraparound) |
| { |
| struct netif netif; |
| struct test_tcp_txcounters txcounters; |
| struct test_tcp_counters counters; |
| struct tcp_pcb* pcb; |
| struct pbuf* p; |
| err_t err; |
| size_t i; |
| u16_t sent_total = 0; |
| LWIP_UNUSED_ARG(_i); |
| |
| for (i = 0; i < sizeof(tx_data); i++) { |
| tx_data[i] = (u8_t)i; |
| } |
| |
| /* initialize local vars */ |
| test_tcp_init_netif(&netif, &txcounters, &test_local_ip, &test_netmask); |
| memset(&counters, 0, sizeof(counters)); |
| |
| /* create and initialize the pcb */ |
| tcp_ticks = SEQNO1 - ISS; |
| pcb = test_tcp_new_counters_pcb(&counters); |
| EXPECT_RET(pcb != NULL); |
| tcp_set_state(pcb, ESTABLISHED, &test_local_ip, &test_remote_ip, TEST_LOCAL_PORT, TEST_REMOTE_PORT); |
| pcb->mss = TCP_MSS; |
| /* disable initial congestion window (we don't send a SYN here...) */ |
| pcb->cwnd = 2*TCP_MSS; |
| /* start in congestion advoidance */ |
| pcb->ssthresh = pcb->cwnd; |
| |
| /* send 6 mss-sized segments */ |
| for (i = 0; i < 6; i++) { |
| err = tcp_write(pcb, &tx_data[sent_total], TCP_MSS, TCP_WRITE_FLAG_COPY); |
| EXPECT_RET(err == ERR_OK); |
| sent_total += TCP_MSS; |
| } |
| check_seqnos(pcb->unsent, 6, seqnos); |
| EXPECT(pcb->unacked == NULL); |
| err = tcp_output(pcb); |
| EXPECT(txcounters.num_tx_calls == 2); |
| EXPECT(txcounters.num_tx_bytes == 2 * (TCP_MSS + 40U)); |
| memset(&txcounters, 0, sizeof(txcounters)); |
| |
| check_seqnos(pcb->unacked, 2, seqnos); |
| check_seqnos(pcb->unsent, 4, &seqnos[2]); |
| |
| /* ACK the first segment */ |
| p = tcp_create_rx_segment(pcb, NULL, 0, 0, TCP_MSS, TCP_ACK); |
| test_tcp_input(p, &netif); |
| /* ensure this didn't trigger a retransmission. Only one |
| segment should be transmitted because cwnd opened up by |
| TCP_MSS and a fraction since we are in congestion avoidance */ |
| EXPECT(txcounters.num_tx_calls == 1); |
| EXPECT(txcounters.num_tx_bytes == TCP_MSS + 40U); |
| memset(&txcounters, 0, sizeof(txcounters)); |
| check_seqnos(pcb->unacked, 2, &seqnos[1]); |
| check_seqnos(pcb->unsent, 3, &seqnos[3]); |
| |
| /* 3 dupacks */ |
| EXPECT(pcb->dupacks == 0); |
| p = tcp_create_rx_segment(pcb, NULL, 0, 0, 0, TCP_ACK); |
| test_tcp_input(p, &netif); |
| EXPECT(txcounters.num_tx_calls == 0); |
| EXPECT(pcb->dupacks == 1); |
| p = tcp_create_rx_segment(pcb, NULL, 0, 0, 0, TCP_ACK); |
| test_tcp_input(p, &netif); |
| EXPECT(txcounters.num_tx_calls == 0); |
| EXPECT(pcb->dupacks == 2); |
| /* 3rd dupack -> fast rexmit */ |
| p = tcp_create_rx_segment(pcb, NULL, 0, 0, 0, TCP_ACK); |
| test_tcp_input(p, &netif); |
| EXPECT(pcb->dupacks == 3); |
| EXPECT(txcounters.num_tx_calls == 4); |
| memset(&txcounters, 0, sizeof(txcounters)); |
| EXPECT(pcb->unsent == NULL); |
| check_seqnos(pcb->unacked, 5, &seqnos[1]); |
| |
| /* make sure the pcb is freed */ |
| EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1); |
| tcp_abort(pcb); |
| EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0); |
| } |
| END_TEST |
| |
| /** Send data with sequence numbers that wrap around the u32_t range. |
| * Then, provoke RTO retransmission and check that all |
| * segment lists are still properly sorted. */ |
| START_TEST(test_tcp_rto_rexmit_wraparound) |
| { |
| struct netif netif; |
| struct test_tcp_txcounters txcounters; |
| struct test_tcp_counters counters; |
| struct tcp_pcb* pcb; |
| struct tcp_pcb dummy_pcb_for_iss; /* we need this for tcp_next_iss() only */ |
| err_t err; |
| size_t i; |
| u16_t sent_total = 0; |
| LWIP_UNUSED_ARG(_i); |
| |
| for (i = 0; i < sizeof(tx_data); i++) { |
| tx_data[i] = (u8_t)i; |
| } |
| |
| /* initialize local vars */ |
| test_tcp_init_netif(&netif, &txcounters, &test_local_ip, &test_netmask); |
| memset(&counters, 0, sizeof(counters)); |
| |
| /* create and initialize the pcb */ |
| tcp_ticks = 0; |
| tcp_ticks = 0 - tcp_next_iss(&dummy_pcb_for_iss); |
| tcp_ticks = SEQNO1 - tcp_next_iss(&dummy_pcb_for_iss); |
| pcb = test_tcp_new_counters_pcb(&counters); |
| EXPECT_RET(pcb != NULL); |
| tcp_set_state(pcb, ESTABLISHED, &test_local_ip, &test_remote_ip, TEST_LOCAL_PORT, TEST_REMOTE_PORT); |
| pcb->mss = TCP_MSS; |
| /* disable initial congestion window (we don't send a SYN here...) */ |
| pcb->cwnd = 2*TCP_MSS; |
| |
| /* send 6 mss-sized segments */ |
| for (i = 0; i < 6; i++) { |
| err = tcp_write(pcb, &tx_data[sent_total], TCP_MSS, TCP_WRITE_FLAG_COPY); |
| EXPECT_RET(err == ERR_OK); |
| sent_total += TCP_MSS; |
| } |
| check_seqnos(pcb->unsent, 6, seqnos); |
| EXPECT(pcb->unacked == NULL); |
| err = tcp_output(pcb); |
| EXPECT(txcounters.num_tx_calls == 2); |
| EXPECT(txcounters.num_tx_bytes == 2 * (TCP_MSS + 40U)); |
| memset(&txcounters, 0, sizeof(txcounters)); |
| |
| check_seqnos(pcb->unacked, 2, seqnos); |
| check_seqnos(pcb->unsent, 4, &seqnos[2]); |
| |
| /* call the tcp timer some times */ |
| for (i = 0; i < 10; i++) { |
| test_tcp_tmr(); |
| EXPECT(txcounters.num_tx_calls == 0); |
| } |
| /* 11th call to tcp_tmr: RTO rexmit fires */ |
| test_tcp_tmr(); |
| EXPECT(txcounters.num_tx_calls == 1); |
| check_seqnos(pcb->unacked, 1, seqnos); |
| check_seqnos(pcb->unsent, 5, &seqnos[1]); |
| |
| /* fake greater cwnd */ |
| pcb->cwnd = pcb->snd_wnd; |
| /* send more data */ |
| err = tcp_output(pcb); |
| EXPECT(err == ERR_OK); |
| /* check queues are sorted */ |
| EXPECT(pcb->unsent == NULL); |
| check_seqnos(pcb->unacked, 6, seqnos); |
| |
| /* make sure the pcb is freed */ |
| EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1); |
| tcp_abort(pcb); |
| EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0); |
| } |
| END_TEST |
| |
| /** Provoke fast retransmission by duplicate ACKs and then recover by ACKing all sent data. |
| * At the end, send more data. */ |
| static void test_tcp_tx_full_window_lost(u8_t zero_window_probe_from_unsent) |
| { |
| struct netif netif; |
| struct test_tcp_txcounters txcounters; |
| struct test_tcp_counters counters; |
| struct tcp_pcb* pcb; |
| struct pbuf *p; |
| err_t err; |
| size_t i; |
| u16_t sent_total; |
| u8_t expected = 0xFE; |
| |
| for (i = 0; i < sizeof(tx_data); i++) { |
| u8_t d = (u8_t)i; |
| if (d == 0xFE) { |
| d = 0xF0; |
| } |
| tx_data[i] = d; |
| } |
| if (zero_window_probe_from_unsent) { |
| tx_data[TCP_WND] = expected; |
| } else { |
| tx_data[0] = expected; |
| } |
| |
| /* initialize local vars */ |
| test_tcp_init_netif(&netif, &txcounters, &test_local_ip, &test_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, &test_local_ip, &test_remote_ip, TEST_LOCAL_PORT, TEST_REMOTE_PORT); |
| pcb->mss = TCP_MSS; |
| /* disable initial congestion window (we don't send a SYN here...) */ |
| pcb->cwnd = pcb->snd_wnd; |
| |
| /* send a full window (minus 1 packets) of TCP data in MSS-sized chunks */ |
| sent_total = 0; |
| if ((TCP_WND - TCP_MSS) % TCP_MSS != 0) { |
| u16_t initial_data_len = (TCP_WND - TCP_MSS) % TCP_MSS; |
| err = tcp_write(pcb, &tx_data[sent_total], initial_data_len, TCP_WRITE_FLAG_COPY); |
| EXPECT_RET(err == ERR_OK); |
| err = tcp_output(pcb); |
| EXPECT_RET(err == ERR_OK); |
| EXPECT(txcounters.num_tx_calls == 1); |
| EXPECT(txcounters.num_tx_bytes == initial_data_len + 40U); |
| memset(&txcounters, 0, sizeof(txcounters)); |
| sent_total += initial_data_len; |
| } |
| for (; sent_total < (TCP_WND - TCP_MSS); sent_total += TCP_MSS) { |
| err = tcp_write(pcb, &tx_data[sent_total], TCP_MSS, TCP_WRITE_FLAG_COPY); |
| EXPECT_RET(err == ERR_OK); |
| err = tcp_output(pcb); |
| EXPECT_RET(err == ERR_OK); |
| EXPECT(txcounters.num_tx_calls == 1); |
| EXPECT(txcounters.num_tx_bytes == TCP_MSS + 40U); |
| memset(&txcounters, 0, sizeof(txcounters)); |
| } |
| EXPECT(sent_total == (TCP_WND - TCP_MSS)); |
| |
| /* now ACK the packet before the first */ |
| p = tcp_create_rx_segment(pcb, NULL, 0, 0, 0, TCP_ACK); |
| test_tcp_input(p, &netif); |
| /* ensure this didn't trigger a retransmission */ |
| EXPECT(txcounters.num_tx_calls == 0); |
| EXPECT(txcounters.num_tx_bytes == 0); |
| |
| EXPECT(pcb->persist_backoff == 0); |
| /* send the last packet, now a complete window has been sent */ |
| err = tcp_write(pcb, &tx_data[sent_total], TCP_MSS, TCP_WRITE_FLAG_COPY); |
| sent_total += TCP_MSS; |
| EXPECT_RET(err == ERR_OK); |
| err = tcp_output(pcb); |
| EXPECT_RET(err == ERR_OK); |
| EXPECT(txcounters.num_tx_calls == 1); |
| EXPECT(txcounters.num_tx_bytes == TCP_MSS + 40U); |
| memset(&txcounters, 0, sizeof(txcounters)); |
| EXPECT(pcb->persist_backoff == 0); |
| |
| if (zero_window_probe_from_unsent) { |
| /* ACK all data but close the TX window */ |
| p = tcp_create_rx_segment_wnd(pcb, NULL, 0, 0, TCP_WND, TCP_ACK, 0); |
| test_tcp_input(p, &netif); |
| /* ensure this didn't trigger any transmission */ |
| EXPECT(txcounters.num_tx_calls == 0); |
| EXPECT(txcounters.num_tx_bytes == 0); |
| /* window is completely full, but persist timer is off since send buffer is empty */ |
| EXPECT(pcb->snd_wnd == 0); |
| EXPECT(pcb->persist_backoff == 0); |
| } |
| |
| /* send one byte more (out of window) -> persist timer starts */ |
| err = tcp_write(pcb, &tx_data[sent_total], 1, TCP_WRITE_FLAG_COPY); |
| EXPECT_RET(err == ERR_OK); |
| err = tcp_output(pcb); |
| EXPECT_RET(err == ERR_OK); |
| EXPECT(txcounters.num_tx_calls == 0); |
| EXPECT(txcounters.num_tx_bytes == 0); |
| memset(&txcounters, 0, sizeof(txcounters)); |
| if (!zero_window_probe_from_unsent) { |
| /* no persist timer unless a zero window announcement has been received */ |
| EXPECT(pcb->persist_backoff == 0); |
| } else { |
| EXPECT(pcb->persist_backoff == 1); |
| |
| /* call tcp_timer some more times to let persist timer count up */ |
| for (i = 0; i < 4; i++) { |
| test_tcp_tmr(); |
| EXPECT(txcounters.num_tx_calls == 0); |
| EXPECT(txcounters.num_tx_bytes == 0); |
| } |
| |
| /* this should trigger the zero-window-probe */ |
| txcounters.copy_tx_packets = 1; |
| test_tcp_tmr(); |
| txcounters.copy_tx_packets = 0; |
| EXPECT(txcounters.num_tx_calls == 1); |
| EXPECT(txcounters.num_tx_bytes == 1 + 40U); |
| EXPECT(txcounters.tx_packets != NULL); |
| if (txcounters.tx_packets != NULL) { |
| u8_t sent; |
| u16_t ret; |
| ret = pbuf_copy_partial(txcounters.tx_packets, &sent, 1, 40U); |
| EXPECT(ret == 1); |
| EXPECT(sent == expected); |
| } |
| if (txcounters.tx_packets != NULL) { |
| pbuf_free(txcounters.tx_packets); |
| txcounters.tx_packets = NULL; |
| } |
| } |
| |
| /* make sure the pcb is freed */ |
| EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1); |
| tcp_abort(pcb); |
| EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0); |
| } |
| |
| START_TEST(test_tcp_tx_full_window_lost_from_unsent) |
| { |
| LWIP_UNUSED_ARG(_i); |
| test_tcp_tx_full_window_lost(1); |
| } |
| END_TEST |
| |
| START_TEST(test_tcp_tx_full_window_lost_from_unacked) |
| { |
| LWIP_UNUSED_ARG(_i); |
| test_tcp_tx_full_window_lost(0); |
| } |
| END_TEST |
| |
| /** Send data, provoke retransmission and then add data to a segment |
| * that already has been sent before. */ |
| START_TEST(test_tcp_retx_add_to_sent) |
| { |
| struct netif netif; |
| struct test_tcp_txcounters txcounters; |
| struct test_tcp_counters counters; |
| struct tcp_pcb* pcb; |
| struct pbuf* p; |
| char data1a[] = { 1, 2, 3}; |
| char data1b[] = { 4}; |
| char data2a[] = { 5, 6, 7, 8}; |
| char data2b[] = { 5, 6, 7}; |
| char data3[] = { 9, 10, 11, 12, 12}; |
| char data4[] = { 13, 14, 15, 16,17}; |
| err_t err; |
| int i; |
| LWIP_UNUSED_ARG(_i); |
| |
| /* initialize local vars */ |
| test_tcp_init_netif(&netif, &txcounters, &test_local_ip, &test_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, &test_local_ip, &test_remote_ip, TEST_LOCAL_PORT, TEST_REMOTE_PORT); |
| pcb->mss = TCP_MSS; |
| /* disable initial congestion window (we don't send a SYN here...) */ |
| pcb->cwnd = pcb->snd_wnd; |
| |
| /* send data1 */ |
| err = tcp_write(pcb, data1a, sizeof(data1a), TCP_WRITE_FLAG_COPY); |
| EXPECT_RET(err == ERR_OK); |
| err = tcp_write(pcb, data1b, sizeof(data1b), 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(data1a) + sizeof(data1b) + 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, data2a, sizeof(data2a), TCP_WRITE_FLAG_COPY); |
| EXPECT_RET(err == ERR_OK); |
| err = tcp_write(pcb, data2b, sizeof(data2b), 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(data2a) + sizeof(data2b) + sizeof(struct tcp_hdr) + sizeof(struct ip_hdr)); |
| memset(&txcounters, 0, sizeof(txcounters)); |
| /* 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); |
| EXPECT_RET(txcounters.num_tx_calls == 0); |
| EXPECT_RET(txcounters.num_tx_bytes == 0); |
| memset(&txcounters, 0, sizeof(txcounters)); |
| |
| /* data3 not sent yet (nagle) */ |
| EXPECT_RET(pcb->unacked != NULL); |
| EXPECT_RET(pcb->unsent != NULL); |
| |
| /* disable nagle for this test so data to sent segment can be added below... */ |
| tcp_nagle_disable(pcb); |
| |
| /* call the tcp timer some times */ |
| for (i = 0; i < 20; i++) { |
| test_tcp_tmr(); |
| if (txcounters.num_tx_calls != 0) { |
| break; |
| } |
| } |
| /* data3 sent */ |
| EXPECT_RET(txcounters.num_tx_calls == 1); |
| EXPECT_RET(txcounters.num_tx_bytes == sizeof(data3) + sizeof(struct tcp_hdr) + sizeof(struct ip_hdr)); |
| EXPECT_RET(pcb->unacked != NULL); |
| EXPECT_RET(pcb->unsent == NULL); |
| memset(&txcounters, 0, sizeof(txcounters)); |
| |
| tcp_nagle_enable(pcb); |
| |
| /* call the tcp timer some times */ |
| for (i = 0; i < 20; i++) { |
| test_tcp_tmr(); |
| if (txcounters.num_tx_calls != 0) { |
| break; |
| } |
| } |
| /* RTO: rexmit of data2 */ |
| EXPECT_RET(txcounters.num_tx_calls == 1); |
| EXPECT_RET(txcounters.num_tx_bytes == sizeof(data2a) + sizeof(data2b) + sizeof(struct tcp_hdr) + sizeof(struct ip_hdr)); |
| EXPECT_RET(pcb->unacked != NULL); |
| EXPECT_RET(pcb->unsent != NULL); |
| memset(&txcounters, 0, sizeof(txcounters)); |
| |
| /* send data4 */ |
| err = tcp_write(pcb, data4, sizeof(data4), TCP_WRITE_FLAG_COPY); |
| EXPECT_RET(err == ERR_OK); |
| /* disable nagle for this test so data to transmit without further ACKs... */ |
| tcp_nagle_disable(pcb); |
| err = tcp_output(pcb); |
| EXPECT_RET(err == ERR_OK); |
| /* nagle enabled, no tx calls */ |
| EXPECT_RET(txcounters.num_tx_calls == 1); |
| EXPECT_RET(txcounters.num_tx_bytes == sizeof(data3) + sizeof(data4) + sizeof(struct tcp_hdr) + sizeof(struct ip_hdr)); |
| memset(&txcounters, 0, sizeof(txcounters)); |
| /* make sure the pcb is freed */ |
| EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1); |
| tcp_abort(pcb); |
| EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0); |
| } |
| END_TEST |
| |
| START_TEST(test_tcp_rto_tracking) |
| { |
| struct netif netif; |
| struct test_tcp_txcounters txcounters; |
| struct test_tcp_counters counters; |
| struct tcp_pcb* pcb; |
| struct pbuf* p; |
| err_t err; |
| size_t i; |
| u16_t sent_total = 0; |
| LWIP_UNUSED_ARG(_i); |
| |
| for (i = 0; i < sizeof(tx_data); i++) { |
| tx_data[i] = (u8_t)i; |
| } |
| |
| /* initialize local vars */ |
| test_tcp_init_netif(&netif, &txcounters, &test_local_ip, &test_netmask); |
| memset(&counters, 0, sizeof(counters)); |
| |
| /* create and initialize the pcb */ |
| tcp_ticks = SEQNO1 - ISS; |
| pcb = test_tcp_new_counters_pcb(&counters); |
| EXPECT_RET(pcb != NULL); |
| tcp_set_state(pcb, ESTABLISHED, &test_local_ip, &test_remote_ip, TEST_LOCAL_PORT, TEST_REMOTE_PORT); |
| pcb->mss = TCP_MSS; |
| /* Set congestion window large enough to send all our segments */ |
| pcb->cwnd = 5*TCP_MSS; |
| |
| /* send 5 mss-sized segments */ |
| for (i = 0; i < 5; i++) { |
| err = tcp_write(pcb, &tx_data[sent_total], TCP_MSS, TCP_WRITE_FLAG_COPY); |
| EXPECT_RET(err == ERR_OK); |
| sent_total += TCP_MSS; |
| } |
| check_seqnos(pcb->unsent, 5, seqnos); |
| EXPECT(pcb->unacked == NULL); |
| err = tcp_output(pcb); |
| EXPECT(txcounters.num_tx_calls == 5); |
| EXPECT(txcounters.num_tx_bytes == 5 * (TCP_MSS + 40U)); |
| memset(&txcounters, 0, sizeof(txcounters)); |
| /* Check all 5 are in-flight */ |
| EXPECT(pcb->unsent == NULL); |
| check_seqnos(pcb->unacked, 5, seqnos); |
| |
| /* Force us into retransmisson timeout */ |
| while (!(pcb->flags & TF_RTO)) { |
| test_tcp_tmr(); |
| } |
| /* Ensure 4 remaining segments are back on unsent, ready for retransmission */ |
| check_seqnos(pcb->unsent, 4, &seqnos[1]); |
| /* Ensure 1st segment is on unacked (already retransmitted) */ |
| check_seqnos(pcb->unacked, 1, seqnos); |
| EXPECT(txcounters.num_tx_calls == 1); |
| EXPECT(txcounters.num_tx_bytes == TCP_MSS + 40U); |
| memset(&txcounters, 0, sizeof(txcounters)); |
| /* Ensure rto_end points to next byte */ |
| EXPECT(pcb->rto_end == seqnos[5]); |
| EXPECT(pcb->rto_end == pcb->snd_nxt); |
| /* Check cwnd was reset */ |
| EXPECT(pcb->cwnd == pcb->mss); |
| |
| /* Add another segment to send buffer which is outside of RTO */ |
| err = tcp_write(pcb, &tx_data[sent_total], TCP_MSS, TCP_WRITE_FLAG_COPY); |
| EXPECT_RET(err == ERR_OK); |
| sent_total += TCP_MSS; |
| check_seqnos(pcb->unsent, 5, &seqnos[1]); |
| /* Ensure no new data was sent */ |
| EXPECT(txcounters.num_tx_calls == 0); |
| EXPECT(txcounters.num_tx_bytes == 0); |
| EXPECT(pcb->rto_end == pcb->snd_nxt); |
| |
| /* ACK first segment */ |
| p = tcp_create_rx_segment(pcb, NULL, 0, 0, TCP_MSS, TCP_ACK); |
| test_tcp_input(p, &netif); |
| /* Next two retranmissions should go out, due to cwnd in slow start */ |
| EXPECT(txcounters.num_tx_calls == 2); |
| EXPECT(txcounters.num_tx_bytes == 2 * (TCP_MSS + 40U)); |
| memset(&txcounters, 0, sizeof(txcounters)); |
| check_seqnos(pcb->unacked, 2, &seqnos[1]); |
| check_seqnos(pcb->unsent, 3, &seqnos[3]); |
| /* RTO should still be marked */ |
| EXPECT(pcb->flags & TF_RTO); |
| /* cwnd should have only grown by 1 MSS */ |
| EXPECT(pcb->cwnd == (tcpwnd_size_t)(2 * pcb->mss)); |
| /* Ensure no new data was sent */ |
| EXPECT(pcb->rto_end == pcb->snd_nxt); |
| |
| /* ACK the next two segments */ |
| p = tcp_create_rx_segment(pcb, NULL, 0, 0, 2*TCP_MSS, TCP_ACK); |
| test_tcp_input(p, &netif); |
| /* Final 2 retransmissions and 1 new data should go out */ |
| EXPECT(txcounters.num_tx_calls == 3); |
| EXPECT(txcounters.num_tx_bytes == 3 * (TCP_MSS + 40U)); |
| memset(&txcounters, 0, sizeof(txcounters)); |
| check_seqnos(pcb->unacked, 3, &seqnos[3]); |
| EXPECT(pcb->unsent == NULL); |
| /* RTO should still be marked */ |
| EXPECT(pcb->flags & TF_RTO); |
| /* cwnd should have only grown by 1 MSS */ |
| EXPECT(pcb->cwnd == (tcpwnd_size_t)(3 * pcb->mss)); |
| /* snd_nxt should have been advanced past rto_end */ |
| EXPECT(TCP_SEQ_GT(pcb->snd_nxt, pcb->rto_end)); |
| |
| /* ACK the next two segments, finishing our RTO, leaving new segment unacked */ |
| p = tcp_create_rx_segment(pcb, NULL, 0, 0, 2*TCP_MSS, TCP_ACK); |
| test_tcp_input(p, &netif); |
| EXPECT(!(pcb->flags & TF_RTO)); |
| check_seqnos(pcb->unacked, 1, &seqnos[5]); |
| /* We should be in ABC congestion avoidance, so no change in cwnd */ |
| EXPECT(pcb->cwnd == (tcpwnd_size_t)(3 * pcb->mss)); |
| EXPECT(pcb->cwnd >= pcb->ssthresh); |
| /* Ensure ABC congestion avoidance is tracking bytes acked */ |
| EXPECT(pcb->bytes_acked == (tcpwnd_size_t)(2 * pcb->mss)); |
| |
| /* make sure the pcb is freed */ |
| EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1); |
| tcp_abort(pcb); |
| EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0); |
| } |
| END_TEST |
| |
| static void test_tcp_rto_timeout_impl(int link_down) |
| { |
| struct netif netif; |
| struct test_tcp_txcounters txcounters; |
| struct test_tcp_counters counters; |
| struct tcp_pcb *pcb, *cur; |
| err_t err; |
| size_t i; |
| const size_t max_wait_ctr = 1024 * 1024; |
| |
| /* Setup data for a single segment */ |
| for (i = 0; i < TCP_MSS; i++) { |
| tx_data[i] = (u8_t)i; |
| } |
| |
| /* initialize local vars */ |
| test_tcp_init_netif(&netif, &txcounters, &test_local_ip, &test_netmask); |
| memset(&counters, 0, sizeof(counters)); |
| |
| /* create and initialize the pcb */ |
| tcp_ticks = SEQNO1 - ISS; |
| pcb = test_tcp_new_counters_pcb(&counters); |
| EXPECT_RET(pcb != NULL); |
| tcp_set_state(pcb, ESTABLISHED, &test_local_ip, &test_remote_ip, TEST_LOCAL_PORT, TEST_REMOTE_PORT); |
| pcb->mss = TCP_MSS; |
| pcb->cwnd = TCP_MSS; |
| |
| /* send our segment */ |
| err = tcp_write(pcb, &tx_data[0], TCP_MSS, TCP_WRITE_FLAG_COPY); |
| EXPECT_RET(err == ERR_OK); |
| err = tcp_output(pcb); |
| EXPECT(txcounters.num_tx_calls == 1); |
| EXPECT(txcounters.num_tx_bytes == 1 * (TCP_MSS + 40U)); |
| memset(&txcounters, 0, sizeof(txcounters)); |
| |
| /* ensure no errors have been recorded */ |
| EXPECT(counters.err_calls == 0); |
| EXPECT(counters.last_err == ERR_OK); |
| |
| /* Force us into retransmisson timeout */ |
| for (i = 0; !(pcb->flags & TF_RTO) && i < max_wait_ctr; i++) { |
| test_tcp_tmr(); |
| } |
| EXPECT(i < max_wait_ctr); |
| |
| /* check first rexmit */ |
| EXPECT(pcb->nrtx == 1); |
| EXPECT(txcounters.num_tx_calls == 1); |
| EXPECT(txcounters.num_tx_bytes == 1 * (TCP_MSS + 40U)); |
| |
| /* still no error expected */ |
| EXPECT(counters.err_calls == 0); |
| EXPECT(counters.last_err == ERR_OK); |
| |
| if (link_down) { |
| netif_set_link_down(&netif); |
| } |
| |
| /* keep running the timer till we hit our maximum RTO */ |
| for (i = 0; counters.last_err == ERR_OK && i < max_wait_ctr; i++) { |
| test_tcp_tmr(); |
| } |
| EXPECT(i < max_wait_ctr); |
| |
| /* check number of retransmissions */ |
| if (link_down) { |
| EXPECT(txcounters.num_tx_calls == 1); |
| EXPECT(txcounters.num_tx_bytes == 1 * (TCP_MSS + 40U)); |
| } else { |
| EXPECT(txcounters.num_tx_calls == TCP_MAXRTX); |
| EXPECT(txcounters.num_tx_bytes == TCP_MAXRTX * (TCP_MSS + 40U)); |
| } |
| |
| /* check the connection (pcb) has been aborted */ |
| EXPECT(counters.err_calls == 1); |
| EXPECT(counters.last_err == ERR_ABRT); |
| /* check our pcb is no longer active */ |
| for (cur = tcp_active_pcbs; cur != NULL; cur = cur->next) { |
| EXPECT(cur != pcb); |
| } |
| EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0); |
| } |
| |
| START_TEST(test_tcp_rto_timeout) |
| { |
| LWIP_UNUSED_ARG(_i); |
| test_tcp_rto_timeout_impl(0); |
| } |
| END_TEST |
| |
| START_TEST(test_tcp_rto_timeout_link_down) |
| { |
| LWIP_UNUSED_ARG(_i); |
| test_tcp_rto_timeout_impl(1); |
| } |
| END_TEST |
| |
| static void test_tcp_rto_timeout_syn_sent_impl(int link_down) |
| { |
| struct netif netif; |
| struct test_tcp_txcounters txcounters; |
| struct test_tcp_counters counters; |
| struct tcp_pcb *pcb, *cur; |
| err_t err; |
| size_t i; |
| const size_t max_wait_ctr = 1024 * 1024; |
| const u16_t tcp_syn_opts_len = LWIP_TCP_OPT_LENGTH(TF_SEG_OPTS_MSS|TF_SEG_OPTS_WND_SCALE|TF_SEG_OPTS_SACK_PERM|TF_SEG_OPTS_TS); |
| |
| /* Setup data for a single segment */ |
| for (i = 0; i < TCP_MSS; i++) { |
| tx_data[i] = (u8_t)i; |
| } |
| |
| /* initialize local vars */ |
| test_tcp_init_netif(&netif, &txcounters, &test_local_ip, &test_netmask); |
| memset(&counters, 0, sizeof(counters)); |
| |
| /* create and initialize the pcb */ |
| tcp_ticks = SEQNO1 - ISS; |
| pcb = test_tcp_new_counters_pcb(&counters); |
| EXPECT_RET(pcb != NULL); |
| err = tcp_connect(pcb, &netif.gw, 123, NULL); |
| EXPECT_RET(err == ERR_OK); |
| EXPECT_RET(pcb->state == SYN_SENT); |
| EXPECT(txcounters.num_tx_calls == 1); |
| EXPECT(txcounters.num_tx_bytes == 40U + tcp_syn_opts_len); |
| |
| /* ensure no errors have been recorded */ |
| EXPECT(counters.err_calls == 0); |
| EXPECT(counters.last_err == ERR_OK); |
| |
| txcounters.num_tx_calls = 0; |
| txcounters.num_tx_bytes = 0; |
| |
| /* Force us into retransmisson timeout */ |
| for (i = 0; !(pcb->flags & TF_RTO) && i < max_wait_ctr; i++) { |
| test_tcp_tmr(); |
| } |
| EXPECT(i < max_wait_ctr); |
| |
| /* check first rexmit */ |
| EXPECT(pcb->nrtx == 1); |
| EXPECT(txcounters.num_tx_calls == 1); |
| EXPECT(txcounters.num_tx_bytes == 40U + tcp_syn_opts_len); /* 40: headers; >=: options */ |
| |
| /* still no error expected */ |
| EXPECT(counters.err_calls == 0); |
| EXPECT(counters.last_err == ERR_OK); |
| |
| if (link_down) { |
| /* set link down and check what happens to the RTO counter */ |
| netif_set_link_down(&netif); |
| } |
| |
| /* keep running the timer till we hit our maximum RTO */ |
| for (i = 0; counters.last_err == ERR_OK && i < max_wait_ctr; i++) { |
| test_tcp_tmr(); |
| } |
| EXPECT(i < max_wait_ctr); |
| |
| /* check number of retransmissions */ |
| if (link_down) { |
| EXPECT(txcounters.num_tx_calls == 1); |
| EXPECT(txcounters.num_tx_bytes == 40U + tcp_syn_opts_len); |
| } else { |
| EXPECT(txcounters.num_tx_calls == TCP_SYNMAXRTX); |
| EXPECT(txcounters.num_tx_bytes == TCP_SYNMAXRTX * (tcp_syn_opts_len + 40U)); |
| } |
| |
| /* check the connection (pcb) has been aborted */ |
| EXPECT(counters.err_calls == 1); |
| EXPECT(counters.last_err == ERR_ABRT); |
| /* check our pcb is no longer active */ |
| for (cur = tcp_active_pcbs; cur != NULL; cur = cur->next) { |
| EXPECT(cur != pcb); |
| } |
| EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0); |
| } |
| |
| START_TEST(test_tcp_rto_timeout_syn_sent) |
| { |
| LWIP_UNUSED_ARG(_i); |
| test_tcp_rto_timeout_syn_sent_impl(0); |
| } |
| END_TEST |
| |
| START_TEST(test_tcp_rto_timeout_syn_sent_link_down) |
| { |
| LWIP_UNUSED_ARG(_i); |
| test_tcp_rto_timeout_syn_sent_impl(1); |
| } |
| END_TEST |
| |
| static void test_tcp_zwp_timeout_impl(int link_down) |
| { |
| struct netif netif; |
| struct test_tcp_txcounters txcounters; |
| struct test_tcp_counters counters; |
| struct tcp_pcb *pcb, *cur; |
| struct pbuf* p; |
| err_t err; |
| size_t i; |
| |
| /* Setup data for two segments */ |
| for (i = 0; i < 2*TCP_MSS; i++) { |
| tx_data[i] = (u8_t)i; |
| } |
| |
| /* initialize local vars */ |
| test_tcp_init_netif(&netif, &txcounters, &test_local_ip, &test_netmask); |
| memset(&counters, 0, sizeof(counters)); |
| |
| /* create and initialize the pcb */ |
| tcp_ticks = SEQNO1 - ISS; |
| pcb = test_tcp_new_counters_pcb(&counters); |
| EXPECT_RET(pcb != NULL); |
| tcp_set_state(pcb, ESTABLISHED, &test_local_ip, &test_remote_ip, TEST_LOCAL_PORT, TEST_REMOTE_PORT); |
| pcb->mss = TCP_MSS; |
| pcb->cwnd = TCP_MSS; |
| |
| /* send first segment */ |
| err = tcp_write(pcb, &tx_data[0], TCP_MSS, TCP_WRITE_FLAG_COPY); |
| EXPECT(err == ERR_OK); |
| err = tcp_output(pcb); |
| EXPECT(err == ERR_OK); |
| |
| /* verify segment is in-flight */ |
| EXPECT(pcb->unsent == NULL); |
| check_seqnos(pcb->unacked, 1, seqnos); |
| EXPECT(txcounters.num_tx_calls == 1); |
| EXPECT(txcounters.num_tx_bytes == 1 * (TCP_MSS + 40U)); |
| memset(&txcounters, 0, sizeof(txcounters)); |
| |
| /* ACK the segment and close the TX window */ |
| p = tcp_create_rx_segment_wnd(pcb, NULL, 0, 0, TCP_MSS, TCP_ACK, 0); |
| test_tcp_input(p, &netif); |
| EXPECT(pcb->unacked == NULL); |
| EXPECT(pcb->unsent == NULL); |
| /* send buffer empty, persist should be off */ |
| EXPECT(pcb->persist_backoff == 0); |
| EXPECT(pcb->snd_wnd == 0); |
| |
| /* send second segment, should be buffered */ |
| err = tcp_write(pcb, &tx_data[TCP_MSS], TCP_MSS, TCP_WRITE_FLAG_COPY); |
| EXPECT(err == ERR_OK); |
| err = tcp_output(pcb); |
| EXPECT(err == ERR_OK); |
| |
| /* ensure it is buffered and persist timer started */ |
| EXPECT(pcb->unacked == NULL); |
| check_seqnos(pcb->unsent, 1, &seqnos[1]); |
| EXPECT(txcounters.num_tx_calls == 0); |
| EXPECT(txcounters.num_tx_bytes == 0); |
| EXPECT(pcb->persist_backoff == 1); |
| |
| /* ensure no errors have been recorded */ |
| EXPECT(counters.err_calls == 0); |
| EXPECT(counters.last_err == ERR_OK); |
| |
| /* run timer till first probe */ |
| EXPECT(pcb->persist_probe == 0); |
| while (pcb->persist_probe == 0) { |
| test_tcp_tmr(); |
| } |
| EXPECT(txcounters.num_tx_calls == 1); |
| EXPECT(txcounters.num_tx_bytes == (1 + 40U)); |
| memset(&txcounters, 0, sizeof(txcounters)); |
| |
| /* respond to probe with remote's current SEQ, ACK, and zero-window */ |
| p = tcp_create_rx_segment_wnd(pcb, NULL, 0, 0, 0, TCP_ACK, 0); |
| test_tcp_input(p, &netif); |
| /* ensure zero-window is still active, but probe count reset */ |
| EXPECT(pcb->persist_backoff > 1); |
| EXPECT(pcb->persist_probe == 0); |
| EXPECT(pcb->snd_wnd == 0); |
| |
| /* ensure no errors have been recorded */ |
| EXPECT(counters.err_calls == 0); |
| EXPECT(counters.last_err == ERR_OK); |
| |
| if (link_down) { |
| netif_set_link_down(&netif); |
| } |
| |
| /* now run the timer till we hit our maximum probe count */ |
| while (counters.last_err == ERR_OK) { |
| test_tcp_tmr(); |
| } |
| |
| if (link_down) { |
| EXPECT(txcounters.num_tx_calls == 0); |
| EXPECT(txcounters.num_tx_bytes == 0); |
| } else { |
| /* check maximum number of 1 byte probes were sent */ |
| EXPECT(txcounters.num_tx_calls == TCP_MAXRTX); |
| EXPECT(txcounters.num_tx_bytes == TCP_MAXRTX * (1 + 40U)); |
| } |
| |
| /* check the connection (pcb) has been aborted */ |
| EXPECT(counters.err_calls == 1); |
| EXPECT(counters.last_err == ERR_ABRT); |
| /* check our pcb is no longer active */ |
| for (cur = tcp_active_pcbs; cur != NULL; cur = cur->next) { |
| EXPECT(cur != pcb); |
| } |
| EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0); |
| } |
| |
| START_TEST(test_tcp_zwp_timeout) |
| { |
| LWIP_UNUSED_ARG(_i); |
| test_tcp_zwp_timeout_impl(0); |
| } |
| END_TEST |
| |
| START_TEST(test_tcp_zwp_timeout_link_down) |
| { |
| LWIP_UNUSED_ARG(_i); |
| test_tcp_zwp_timeout_impl(1); |
| } |
| END_TEST |
| |
| START_TEST(test_tcp_persist_split) |
| { |
| struct netif netif; |
| struct test_tcp_txcounters txcounters; |
| struct test_tcp_counters counters; |
| struct tcp_pcb *pcb; |
| struct pbuf* p; |
| err_t err; |
| size_t i; |
| LWIP_UNUSED_ARG(_i); |
| |
| /* Setup data for four segments */ |
| for (i = 0; i < 4 * TCP_MSS; i++) { |
| tx_data[i] = (u8_t)i; |
| } |
| |
| /* initialize local vars */ |
| test_tcp_init_netif(&netif, &txcounters, &test_local_ip, &test_netmask); |
| memset(&counters, 0, sizeof(counters)); |
| |
| /* create and initialize the pcb */ |
| tcp_ticks = SEQNO1 - ISS; |
| pcb = test_tcp_new_counters_pcb(&counters); |
| EXPECT_RET(pcb != NULL); |
| tcp_set_state(pcb, ESTABLISHED, &test_local_ip, &test_remote_ip, TEST_LOCAL_PORT, TEST_REMOTE_PORT); |
| pcb->mss = TCP_MSS; |
| /* set window to three segments */ |
| pcb->cwnd = 3 * TCP_MSS; |
| pcb->snd_wnd = 3 * TCP_MSS; |
| pcb->snd_wnd_max = 3 * TCP_MSS; |
| |
| /* send four segments. Fourth should stay buffered and is a 3/4 MSS segment to |
| get coverage on the oversized segment case */ |
| err = tcp_write(pcb, &tx_data[0], (3 * TCP_MSS) + (TCP_MSS - (TCP_MSS / 4)), TCP_WRITE_FLAG_COPY); |
| EXPECT(err == ERR_OK); |
| err = tcp_output(pcb); |
| EXPECT(err == ERR_OK); |
| |
| /* verify 3 segments are in-flight */ |
| EXPECT(pcb->unacked != NULL); |
| check_seqnos(pcb->unacked, 3, seqnos); |
| EXPECT(txcounters.num_tx_calls == 3); |
| EXPECT(txcounters.num_tx_bytes == 3 * (TCP_MSS + 40U)); |
| memset(&txcounters, 0, sizeof(txcounters)); |
| /* verify 4th segment is on unsent */ |
| EXPECT(pcb->unsent != NULL); |
| EXPECT(pcb->unsent->len == TCP_MSS - (TCP_MSS / 4)); |
| check_seqnos(pcb->unsent, 1, &seqnos[3]); |
| #if TCP_OVERSIZE |
| EXPECT(pcb->unsent_oversize == TCP_MSS / 4); |
| #if TCP_OVERSIZE_DBGCHECK |
| EXPECT(pcb->unsent->oversize_left == pcb->unsent_oversize); |
| #endif /* TCP_OVERSIZE_DBGCHECK */ |
| #endif /* TCP_OVERSIZE */ |
| |
| /* ACK the 3 segments and update the window to only 1/2 TCP_MSS. |
| 4th segment should stay on unsent because it's bigger than 1/2 MSS */ |
| p = tcp_create_rx_segment_wnd(pcb, NULL, 0, 0, 3 * TCP_MSS, TCP_ACK, TCP_MSS / 2); |
| test_tcp_input(p, &netif); |
| EXPECT(pcb->unacked == NULL); |
| EXPECT(pcb->snd_wnd == TCP_MSS / 2); |
| EXPECT(pcb->unsent != NULL); |
| check_seqnos(pcb->unsent, 1, &seqnos[3]); |
| EXPECT(txcounters.num_tx_calls == 0); |
| EXPECT(txcounters.num_tx_bytes == 0); |
| /* persist timer should be started since 4th segment is stuck waiting on snd_wnd */ |
| EXPECT(pcb->persist_backoff == 1); |
| |
| /* ensure no errors have been recorded */ |
| EXPECT(counters.err_calls == 0); |
| EXPECT(counters.last_err == ERR_OK); |
| |
| /* call tcp_timer some more times to let persist timer count up */ |
| for (i = 0; i < 4; i++) { |
| test_tcp_tmr(); |
| EXPECT(txcounters.num_tx_calls == 0); |
| EXPECT(txcounters.num_tx_bytes == 0); |
| } |
| |
| /* this should be the first timer shot, which should split the |
| * segment and send a runt (of the remaining window size) */ |
| txcounters.copy_tx_packets = 1; |
| test_tcp_tmr(); |
| txcounters.copy_tx_packets = 0; |
| /* persist will be disabled as RTO timer takes over */ |
| EXPECT(pcb->persist_backoff == 0); |
| EXPECT(txcounters.num_tx_calls == 1); |
| EXPECT(txcounters.num_tx_bytes == ((TCP_MSS /2) + 40U)); |
| /* verify 1/2 MSS segment sent, 1/4 MSS still buffered */ |
| EXPECT(pcb->unsent != NULL); |
| EXPECT(pcb->unsent->len == TCP_MSS / 4); |
| EXPECT(pcb->unacked != NULL); |
| EXPECT(pcb->unacked->len == TCP_MSS / 2); |
| #if TCP_OVERSIZE |
| /* verify there is no oversized remaining since during the |
| segment split, the remainder pbuf is always the exact length */ |
| EXPECT(pcb->unsent_oversize == 0); |
| #if TCP_OVERSIZE_DBGCHECK |
| /* Split segment already transmitted, should be at 0 */ |
| EXPECT(pcb->unacked->oversize_left == 0); |
| /* Remainder segement should match pcb value (which is 0) */ |
| EXPECT(pcb->unsent->oversize_left == pcb->unsent_oversize); |
| #endif /* TCP_OVERSIZE_DBGCHECK */ |
| #endif /* TCP_OVERSIZE */ |
| |
| /* verify first half segment */ |
| EXPECT(txcounters.tx_packets != NULL); |
| if (txcounters.tx_packets != NULL) { |
| u8_t sent[TCP_MSS / 2]; |
| u16_t ret; |
| ret = pbuf_copy_partial(txcounters.tx_packets, &sent, TCP_MSS / 2, 40U); |
| EXPECT(ret == TCP_MSS / 2); |
| EXPECT(memcmp(sent, &tx_data[3 * TCP_MSS], TCP_MSS / 2) == 0); |
| } |
| if (txcounters.tx_packets != NULL) { |
| pbuf_free(txcounters.tx_packets); |
| txcounters.tx_packets = NULL; |
| } |
| memset(&txcounters, 0, sizeof(txcounters)); |
| |
| /* ACK the half segment, leave window at half segment */ |
| p = tcp_create_rx_segment_wnd(pcb, NULL, 0, 0, TCP_MSS / 2, TCP_ACK, TCP_MSS / 2); |
| txcounters.copy_tx_packets = 1; |
| test_tcp_input(p, &netif); |
| txcounters.copy_tx_packets = 0; |
| /* ensure remaining segment was sent */ |
| EXPECT(txcounters.num_tx_calls == 1); |
| EXPECT(txcounters.num_tx_bytes == ((TCP_MSS / 4) + 40U)); |
| EXPECT(pcb->unsent == NULL); |
| EXPECT(pcb->unacked != NULL); |
| EXPECT(pcb->unacked->len == TCP_MSS / 4); |
| EXPECT(pcb->snd_wnd == TCP_MSS / 2); |
| |
| /* verify remainder segment */ |
| EXPECT(txcounters.tx_packets != NULL); |
| if (txcounters.tx_packets != NULL) { |
| u8_t sent[TCP_MSS / 4]; |
| u16_t ret; |
| ret = pbuf_copy_partial(txcounters.tx_packets, &sent, TCP_MSS / 4, 40U); |
| EXPECT(ret == TCP_MSS / 4); |
| EXPECT(memcmp(sent, &tx_data[(3 * TCP_MSS) + TCP_MSS / 2], TCP_MSS / 4) == 0); |
| } |
| if (txcounters.tx_packets != NULL) { |
| pbuf_free(txcounters.tx_packets); |
| txcounters.tx_packets = NULL; |
| } |
| |
| /* ensure no errors have been recorded */ |
| EXPECT(counters.err_calls == 0); |
| EXPECT(counters.last_err == ERR_OK); |
| |
| /* make sure the pcb is freed */ |
| EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1); |
| tcp_abort(pcb); |
| EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0); |
| } |
| END_TEST |
| |
| /** Create the suite including all tests for this module */ |
| Suite * |
| tcp_suite(void) |
| { |
| testfunc tests[] = { |
| TESTFUNC(test_tcp_new_abort), |
| TESTFUNC(test_tcp_listen_passive_open), |
| TESTFUNC(test_tcp_recv_inseq), |
| TESTFUNC(test_tcp_recv_inseq_trim), |
| TESTFUNC(test_tcp_passive_close), |
| TESTFUNC(test_tcp_active_abort), |
| TESTFUNC(test_tcp_malformed_header), |
| TESTFUNC(test_tcp_fast_retx_recover), |
| TESTFUNC(test_tcp_fast_rexmit_wraparound), |
| TESTFUNC(test_tcp_rto_rexmit_wraparound), |
| TESTFUNC(test_tcp_tx_full_window_lost_from_unacked), |
| TESTFUNC(test_tcp_tx_full_window_lost_from_unsent), |
| TESTFUNC(test_tcp_retx_add_to_sent), |
| TESTFUNC(test_tcp_rto_tracking), |
| TESTFUNC(test_tcp_rto_timeout), |
| TESTFUNC(test_tcp_rto_timeout_link_down), |
| TESTFUNC(test_tcp_rto_timeout_syn_sent), |
| TESTFUNC(test_tcp_rto_timeout_syn_sent_link_down), |
| TESTFUNC(test_tcp_zwp_timeout), |
| TESTFUNC(test_tcp_zwp_timeout_link_down), |
| TESTFUNC(test_tcp_persist_split) |
| }; |
| return create_suite("TCP", tests, sizeof(tests)/sizeof(testfunc), tcp_setup, tcp_teardown); |
| } |