diff options
Diffstat (limited to 'ext/lwip/test')
-rw-r--r-- | ext/lwip/test/unit/core/test_mem.c | 73 | ||||
-rw-r--r-- | ext/lwip/test/unit/core/test_mem.h | 8 | ||||
-rw-r--r-- | ext/lwip/test/unit/etharp/test_etharp.c | 262 | ||||
-rw-r--r-- | ext/lwip/test/unit/etharp/test_etharp.h | 8 | ||||
-rw-r--r-- | ext/lwip/test/unit/lwip_check.h | 37 | ||||
-rw-r--r-- | ext/lwip/test/unit/lwip_unittests.c | 45 | ||||
-rw-r--r-- | ext/lwip/test/unit/lwipopts.h | 50 | ||||
-rw-r--r-- | ext/lwip/test/unit/tcp/tcp_helper.c | 294 | ||||
-rw-r--r-- | ext/lwip/test/unit/tcp/tcp_helper.h | 52 | ||||
-rw-r--r-- | ext/lwip/test/unit/tcp/test_tcp.c | 667 | ||||
-rw-r--r-- | ext/lwip/test/unit/tcp/test_tcp.h | 8 | ||||
-rw-r--r-- | ext/lwip/test/unit/tcp/test_tcp_oos.c | 944 | ||||
-rw-r--r-- | ext/lwip/test/unit/tcp/test_tcp_oos.h | 8 | ||||
-rw-r--r-- | ext/lwip/test/unit/udp/test_udp.c | 68 | ||||
-rw-r--r-- | ext/lwip/test/unit/udp/test_udp.h | 8 |
15 files changed, 2532 insertions, 0 deletions
diff --git a/ext/lwip/test/unit/core/test_mem.c b/ext/lwip/test/unit/core/test_mem.c new file mode 100644 index 00000000..d3a5d540 --- /dev/null +++ b/ext/lwip/test/unit/core/test_mem.c @@ -0,0 +1,73 @@ +#include "test_mem.h" + +#include "lwip/mem.h" +#include "lwip/stats.h" + +#if !LWIP_STATS || !MEM_STATS +#error "This tests needs MEM-statistics enabled" +#endif +#if LWIP_DNS +#error "This test needs DNS turned off (as it mallocs on init)" +#endif + +/* Setups/teardown functions */ + +static void +mem_setup(void) +{ +} + +static void +mem_teardown(void) +{ +} + + +/* Test functions */ + +/** Call mem_malloc, mem_free and mem_trim and check stats */ +START_TEST(test_mem_one) +{ +#define SIZE1 16 +#define SIZE1_2 12 +#define SIZE2 16 + void *p1, *p2; + mem_size_t s1, s2; + LWIP_UNUSED_ARG(_i); + +#if LWIP_DNS + fail("This test needs DNS turned off (as it mallocs on init)"); +#endif + + fail_unless(lwip_stats.mem.used == 0); + + p1 = mem_malloc(SIZE1); + fail_unless(p1 != NULL); + fail_unless(lwip_stats.mem.used >= SIZE1); + s1 = lwip_stats.mem.used; + + p2 = mem_malloc(SIZE2); + fail_unless(p2 != NULL); + fail_unless(lwip_stats.mem.used >= SIZE2 + s1); + s2 = lwip_stats.mem.used; + + mem_trim(p1, SIZE1_2); + + mem_free(p2); + fail_unless(lwip_stats.mem.used <= s2 - SIZE2); + + mem_free(p1); + fail_unless(lwip_stats.mem.used == 0); +} +END_TEST + + +/** Create the suite including all tests for this module */ +Suite * +mem_suite(void) +{ + TFun tests[] = { + test_mem_one + }; + return create_suite("MEM", tests, sizeof(tests)/sizeof(TFun), mem_setup, mem_teardown); +} diff --git a/ext/lwip/test/unit/core/test_mem.h b/ext/lwip/test/unit/core/test_mem.h new file mode 100644 index 00000000..13803edc --- /dev/null +++ b/ext/lwip/test/unit/core/test_mem.h @@ -0,0 +1,8 @@ +#ifndef __TEST_MEM_H__ +#define __TEST_MEM_H__ + +#include "../lwip_check.h" + +Suite *mem_suite(void); + +#endif diff --git a/ext/lwip/test/unit/etharp/test_etharp.c b/ext/lwip/test/unit/etharp/test_etharp.c new file mode 100644 index 00000000..cbbc9502 --- /dev/null +++ b/ext/lwip/test/unit/etharp/test_etharp.c @@ -0,0 +1,262 @@ +#include "test_etharp.h" + +#include "lwip/udp.h" +#include "netif/etharp.h" +#include "lwip/stats.h" + +#if !LWIP_STATS || !UDP_STATS || !MEMP_STATS || !ETHARP_STATS +#error "This tests needs UDP-, MEMP- and ETHARP-statistics enabled" +#endif +#if !ETHARP_SUPPORT_STATIC_ENTRIES +#error "This test needs ETHARP_SUPPORT_STATIC_ENTRIES enabled" +#endif + +static struct netif test_netif; +static ip_addr_t test_ipaddr, test_netmask, test_gw; +struct eth_addr test_ethaddr = {1,1,1,1,1,1}; +struct eth_addr test_ethaddr2 = {1,1,1,1,1,2}; +struct eth_addr test_ethaddr3 = {1,1,1,1,1,3}; +struct eth_addr test_ethaddr4 = {1,1,1,1,1,4}; +static int linkoutput_ctr; + +/* Helper functions */ +static void +etharp_remove_all(void) +{ + int i; + /* call etharp_tmr often enough to have all entries cleaned */ + for(i = 0; i < 0xff; i++) { + etharp_tmr(); + } +} + +static err_t +default_netif_linkoutput(struct netif *netif, struct pbuf *p) +{ + fail_unless(netif == &test_netif); + fail_unless(p != NULL); + linkoutput_ctr++; + return ERR_OK; +} + +static err_t +default_netif_init(struct netif *netif) +{ + fail_unless(netif != NULL); + netif->linkoutput = default_netif_linkoutput; + netif->output = etharp_output; + netif->mtu = 1500; + netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP; + netif->hwaddr_len = ETHARP_HWADDR_LEN; + return ERR_OK; +} + +static void +default_netif_add(void) +{ + IP4_ADDR(&test_gw, 192,168,0,1); + IP4_ADDR(&test_ipaddr, 192,168,0,1); + IP4_ADDR(&test_netmask, 255,255,0,0); + + fail_unless(netif_default == NULL); + netif_set_default(netif_add(&test_netif, &test_ipaddr, &test_netmask, + &test_gw, NULL, default_netif_init, NULL)); + netif_set_up(&test_netif); +} + +static void +default_netif_remove(void) +{ + fail_unless(netif_default == &test_netif); + netif_remove(&test_netif); +} + +static void +create_arp_response(ip_addr_t *adr) +{ + int k; + struct eth_hdr *ethhdr; + struct etharp_hdr *etharphdr; + struct pbuf *p = pbuf_alloc(PBUF_RAW, sizeof(struct eth_hdr) + sizeof(struct etharp_hdr), PBUF_RAM); + if(p == NULL) { + FAIL_RET(); + } + ethhdr = (struct eth_hdr*)p->payload; + etharphdr = (struct etharp_hdr*)(ethhdr + 1); + + ethhdr->dest = test_ethaddr; + ethhdr->src = test_ethaddr2; + ethhdr->type = htons(ETHTYPE_ARP); + + etharphdr->hwtype = htons(/*HWTYPE_ETHERNET*/ 1); + etharphdr->proto = htons(ETHTYPE_IP); + etharphdr->hwlen = ETHARP_HWADDR_LEN; + etharphdr->protolen = sizeof(ip_addr_t); + etharphdr->opcode = htons(ARP_REPLY); + + SMEMCPY(ðarphdr->sipaddr, adr, sizeof(ip_addr_t)); + SMEMCPY(ðarphdr->dipaddr, &test_ipaddr, sizeof(ip_addr_t)); + + k = 6; + while(k > 0) { + k--; + /* Write the ARP MAC-Addresses */ + etharphdr->shwaddr.addr[k] = test_ethaddr2.addr[k]; + etharphdr->dhwaddr.addr[k] = test_ethaddr.addr[k]; + /* Write the Ethernet MAC-Addresses */ + ethhdr->dest.addr[k] = test_ethaddr.addr[k]; + ethhdr->src.addr[k] = test_ethaddr2.addr[k]; + } + + ethernet_input(p, &test_netif); +} + +/* Setups/teardown functions */ + +static void +etharp_setup(void) +{ + etharp_remove_all(); + default_netif_add(); +} + +static void +etharp_teardown(void) +{ + etharp_remove_all(); + default_netif_remove(); +} + + +/* Test functions */ + +START_TEST(test_etharp_table) +{ +#if ETHARP_SUPPORT_STATIC_ENTRIES + err_t err; +#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ + s8_t idx; + ip_addr_t *unused_ipaddr; + struct eth_addr *unused_ethaddr; + struct udp_pcb* pcb; + LWIP_UNUSED_ARG(_i); + + if (netif_default != &test_netif) { + fail("This test needs a default netif"); + } + + linkoutput_ctr = 0; + + pcb = udp_new(); + fail_unless(pcb != NULL); + if (pcb != NULL) { + ip_addr_t adrs[ARP_TABLE_SIZE + 2]; + int i; + for(i = 0; i < ARP_TABLE_SIZE + 2; i++) { + IP4_ADDR(&adrs[i], 192,168,0,i+2); + } + /* fill ARP-table with dynamic entries */ + for(i = 0; i < ARP_TABLE_SIZE; i++) { + struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, 10, PBUF_RAM); + fail_unless(p != NULL); + if (p != NULL) { + err_t err = udp_sendto(pcb, p, &adrs[i], 123); + fail_unless(err == ERR_OK); + /* etharp request sent? */ + fail_unless(linkoutput_ctr == (2*i) + 1); + pbuf_free(p); + + /* create an ARP response */ + create_arp_response(&adrs[i]); + /* queued UDP packet sent? */ + fail_unless(linkoutput_ctr == (2*i) + 2); + + idx = etharp_find_addr(NULL, &adrs[i], &unused_ethaddr, &unused_ipaddr); + fail_unless(idx == i); + etharp_tmr(); + } + } + linkoutput_ctr = 0; +#if ETHARP_SUPPORT_STATIC_ENTRIES + /* create one static entry */ + err = etharp_add_static_entry(&adrs[ARP_TABLE_SIZE], &test_ethaddr3); + fail_unless(err == ERR_OK); + idx = etharp_find_addr(NULL, &adrs[ARP_TABLE_SIZE], &unused_ethaddr, &unused_ipaddr); + fail_unless(idx == 0); + fail_unless(linkoutput_ctr == 0); +#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ + + linkoutput_ctr = 0; + /* fill ARP-table with dynamic entries */ + for(i = 0; i < ARP_TABLE_SIZE; i++) { + struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, 10, PBUF_RAM); + fail_unless(p != NULL); + if (p != NULL) { + err_t err = udp_sendto(pcb, p, &adrs[i], 123); + fail_unless(err == ERR_OK); + /* etharp request sent? */ + fail_unless(linkoutput_ctr == (2*i) + 1); + pbuf_free(p); + + /* create an ARP response */ + create_arp_response(&adrs[i]); + /* queued UDP packet sent? */ + fail_unless(linkoutput_ctr == (2*i) + 2); + + idx = etharp_find_addr(NULL, &adrs[i], &unused_ethaddr, &unused_ipaddr); + if (i < ARP_TABLE_SIZE - 1) { + fail_unless(idx == i+1); + } else { + /* the last entry must not overwrite the static entry! */ + fail_unless(idx == 1); + } + etharp_tmr(); + } + } +#if ETHARP_SUPPORT_STATIC_ENTRIES + /* create a second static entry */ + err = etharp_add_static_entry(&adrs[ARP_TABLE_SIZE+1], &test_ethaddr4); + fail_unless(err == ERR_OK); + idx = etharp_find_addr(NULL, &adrs[ARP_TABLE_SIZE], &unused_ethaddr, &unused_ipaddr); + fail_unless(idx == 0); + idx = etharp_find_addr(NULL, &adrs[ARP_TABLE_SIZE+1], &unused_ethaddr, &unused_ipaddr); + fail_unless(idx == 2); + /* and remove it again */ + err = etharp_remove_static_entry(&adrs[ARP_TABLE_SIZE+1]); + fail_unless(err == ERR_OK); + idx = etharp_find_addr(NULL, &adrs[ARP_TABLE_SIZE], &unused_ethaddr, &unused_ipaddr); + fail_unless(idx == 0); + idx = etharp_find_addr(NULL, &adrs[ARP_TABLE_SIZE+1], &unused_ethaddr, &unused_ipaddr); + fail_unless(idx == -1); +#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ + + /* check that static entries don't time out */ + etharp_remove_all(); + idx = etharp_find_addr(NULL, &adrs[ARP_TABLE_SIZE], &unused_ethaddr, &unused_ipaddr); + fail_unless(idx == 0); + +#if ETHARP_SUPPORT_STATIC_ENTRIES + /* remove the first static entry */ + err = etharp_remove_static_entry(&adrs[ARP_TABLE_SIZE]); + fail_unless(err == ERR_OK); + idx = etharp_find_addr(NULL, &adrs[ARP_TABLE_SIZE], &unused_ethaddr, &unused_ipaddr); + fail_unless(idx == -1); + idx = etharp_find_addr(NULL, &adrs[ARP_TABLE_SIZE+1], &unused_ethaddr, &unused_ipaddr); + fail_unless(idx == -1); +#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ + + udp_remove(pcb); + } +} +END_TEST + + +/** Create the suite including all tests for this module */ +Suite * +etharp_suite(void) +{ + TFun tests[] = { + test_etharp_table + }; + return create_suite("ETHARP", tests, sizeof(tests)/sizeof(TFun), etharp_setup, etharp_teardown); +} diff --git a/ext/lwip/test/unit/etharp/test_etharp.h b/ext/lwip/test/unit/etharp/test_etharp.h new file mode 100644 index 00000000..96e00c3b --- /dev/null +++ b/ext/lwip/test/unit/etharp/test_etharp.h @@ -0,0 +1,8 @@ +#ifndef __TEST_ETHARP_H__ +#define __TEST_ETHARP_H__ + +#include "../lwip_check.h" + +Suite* etharp_suite(void); + +#endif diff --git a/ext/lwip/test/unit/lwip_check.h b/ext/lwip/test/unit/lwip_check.h new file mode 100644 index 00000000..e27f55ae --- /dev/null +++ b/ext/lwip/test/unit/lwip_check.h @@ -0,0 +1,37 @@ +#ifndef __LWIP_CHECK_H__ +#define __LWIP_CHECK_H__ + +/* Common header file for lwIP unit tests using the check framework */ + +#include <config.h> +#include <check.h> +#include <stdlib.h> + +#define FAIL_RET() do { fail(); return; } while(0) +#define EXPECT(x) fail_unless(x) +#define EXPECT_RET(x) do { fail_unless(x); if(!(x)) { return; }} while(0) +#define EXPECT_RETX(x, y) do { fail_unless(x); if(!(x)) { return y; }} while(0) +#define EXPECT_RETNULL(x) EXPECT_RETX(x, NULL) + +/** typedef for a function returning a test suite */ +typedef Suite* (suite_getter_fn)(void); + +/** Create a test suite */ +static Suite* create_suite(const char* name, TFun *tests, size_t num_tests, SFun setup, SFun teardown) +{ + size_t i; + Suite *s = suite_create(name); + + for(i = 0; i < num_tests; i++) { + /* Core test case */ + TCase *tc_core = tcase_create("Core"); + if ((setup != NULL) || (teardown != NULL)) { + tcase_add_checked_fixture(tc_core, setup, teardown); + } + tcase_add_test(tc_core, tests[i]); + suite_add_tcase(s, tc_core); + } + return s; +} + +#endif /* __LWIP_CHECK_H__ */ diff --git a/ext/lwip/test/unit/lwip_unittests.c b/ext/lwip/test/unit/lwip_unittests.c new file mode 100644 index 00000000..4f537e66 --- /dev/null +++ b/ext/lwip/test/unit/lwip_unittests.c @@ -0,0 +1,45 @@ +#include "lwip_check.h" + +#include "udp/test_udp.h" +#include "tcp/test_tcp.h" +#include "tcp/test_tcp_oos.h" +#include "core/test_mem.h" +#include "etharp/test_etharp.h" + +#include "lwip/init.h" + + +int main() +{ + int number_failed; + SRunner *sr; + size_t i; + suite_getter_fn* suites[] = { + udp_suite, + tcp_suite, + tcp_oos_suite, + mem_suite, + etharp_suite + }; + size_t num = sizeof(suites)/sizeof(void*); + LWIP_ASSERT("No suites defined", num > 0); + + lwip_init(); + + sr = srunner_create((suites[0])()); + for(i = 1; i < num; i++) { + srunner_add_suite(sr, ((suite_getter_fn*)suites[i])()); + } + +#ifdef LWIP_UNITTESTS_NOFORK + srunner_set_fork_status(sr, CK_NOFORK); +#endif +#ifdef LWIP_UNITTESTS_FORK + srunner_set_fork_status(sr, CK_FORK); +#endif + + srunner_run_all(sr, CK_NORMAL); + number_failed = srunner_ntests_failed(sr); + srunner_free(sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/ext/lwip/test/unit/lwipopts.h b/ext/lwip/test/unit/lwipopts.h new file mode 100644 index 00000000..88e76d7a --- /dev/null +++ b/ext/lwip/test/unit/lwipopts.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2001-2003 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Simon Goldschmidt + * + */ +#ifndef __LWIPOPTS_H__ +#define __LWIPOPTS_H__ + +/* Prevent having to link sys_arch.c (we don't test the API layers in unit tests) */ +#define NO_SYS 1 +#define LWIP_NETCONN 0 +#define LWIP_SOCKET 0 + +/* Minimal changes to opt.h required for tcp unit tests: */ +#define MEM_SIZE 16000 +#define TCP_SND_QUEUELEN 40 +#define MEMP_NUM_TCP_SEG TCP_SND_QUEUELEN +#define TCP_SND_BUF (12 * TCP_MSS) +#define TCP_WND (10 * TCP_MSS) + +/* Minimal changes to opt.h required for etharp unit tests: */ +#define ETHARP_SUPPORT_STATIC_ENTRIES 1 + +#endif /* __LWIPOPTS_H__ */ diff --git a/ext/lwip/test/unit/tcp/tcp_helper.c b/ext/lwip/test/unit/tcp/tcp_helper.c new file mode 100644 index 00000000..dd550f1b --- /dev/null +++ b/ext/lwip/test/unit/tcp/tcp_helper.c @@ -0,0 +1,294 @@ +#include "tcp_helper.h" + +#include "lwip/tcp_impl.h" +#include "lwip/stats.h" +#include "lwip/pbuf.h" +#include "lwip/inet_chksum.h" + +#if !LWIP_STATS || !TCP_STATS || !MEMP_STATS +#error "This tests needs TCP- and MEMP-statistics enabled" +#endif + +/** Remove all pcbs on the given list. */ +static void +tcp_remove(struct tcp_pcb* pcb_list) +{ + struct tcp_pcb *pcb = pcb_list; + struct tcp_pcb *pcb2; + + while(pcb != NULL) { + pcb2 = pcb; + pcb = pcb->next; + tcp_abort(pcb2); + } +} + +/** Remove all pcbs on listen-, active- and time-wait-list (bound- isn't exported). */ +void +tcp_remove_all(void) +{ + tcp_remove(tcp_listen_pcbs.pcbs); + tcp_remove(tcp_active_pcbs); + tcp_remove(tcp_tw_pcbs); + fail_unless(lwip_stats.memp[MEMP_TCP_PCB].used == 0); + fail_unless(lwip_stats.memp[MEMP_TCP_PCB_LISTEN].used == 0); + fail_unless(lwip_stats.memp[MEMP_TCP_SEG].used == 0); + fail_unless(lwip_stats.memp[MEMP_PBUF_POOL].used == 0); +} + +/** Create a TCP segment usable for passing to tcp_input */ +static struct pbuf* +tcp_create_segment_wnd(ip_addr_t* src_ip, ip_addr_t* dst_ip, + u16_t src_port, u16_t dst_port, void* data, size_t data_len, + u32_t seqno, u32_t ackno, u8_t headerflags, u16_t wnd) +{ + struct pbuf *p, *q; + struct ip_hdr* iphdr; + struct tcp_hdr* tcphdr; + u16_t pbuf_len = (u16_t)(sizeof(struct ip_hdr) + sizeof(struct tcp_hdr) + data_len); + + p = pbuf_alloc(PBUF_RAW, pbuf_len, PBUF_POOL); + EXPECT_RETNULL(p != NULL); + /* first pbuf must be big enough to hold the headers */ + EXPECT_RETNULL(p->len >= (sizeof(struct ip_hdr) + sizeof(struct tcp_hdr))); + if (data_len > 0) { + /* first pbuf must be big enough to hold at least 1 data byte, too */ + EXPECT_RETNULL(p->len > (sizeof(struct ip_hdr) + sizeof(struct tcp_hdr))); + } + + for(q = p; q != NULL; q = q->next) { + memset(q->payload, 0, q->len); + } + + iphdr = p->payload; + /* fill IP header */ + iphdr->dest.addr = dst_ip->addr; + iphdr->src.addr = src_ip->addr; + IPH_VHL_SET(iphdr, 4, IP_HLEN / 4); + IPH_TOS_SET(iphdr, 0); + IPH_LEN_SET(iphdr, htons(p->tot_len)); + IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN)); + + /* let p point to TCP header */ + pbuf_header(p, -(s16_t)sizeof(struct ip_hdr)); + + tcphdr = p->payload; + tcphdr->src = htons(src_port); + tcphdr->dest = htons(dst_port); + tcphdr->seqno = htonl(seqno); + tcphdr->ackno = htonl(ackno); + TCPH_HDRLEN_SET(tcphdr, sizeof(struct tcp_hdr)/4); + TCPH_FLAGS_SET(tcphdr, headerflags); + tcphdr->wnd = htons(wnd); + + if (data_len > 0) { + /* let p point to TCP data */ + pbuf_header(p, -(s16_t)sizeof(struct tcp_hdr)); + /* copy data */ + pbuf_take(p, data, data_len); + /* let p point to TCP header again */ + pbuf_header(p, sizeof(struct tcp_hdr)); + } + + /* calculate checksum */ + + tcphdr->chksum = inet_chksum_pseudo(p, src_ip, dst_ip, + IP_PROTO_TCP, p->tot_len); + + pbuf_header(p, sizeof(struct ip_hdr)); + + return p; +} + +/** Create a TCP segment usable for passing to tcp_input */ +struct pbuf* +tcp_create_segment(ip_addr_t* src_ip, ip_addr_t* dst_ip, + u16_t src_port, u16_t dst_port, void* data, size_t data_len, + u32_t seqno, u32_t ackno, u8_t headerflags) +{ + return tcp_create_segment_wnd(src_ip, dst_ip, src_port, dst_port, data, + data_len, seqno, ackno, headerflags, TCP_WND); +} + +/** Create a TCP segment usable for passing to tcp_input + * - IP-addresses, ports, seqno and ackno are taken from pcb + * - seqno and ackno can be altered with an offset + */ +struct pbuf* +tcp_create_rx_segment(struct tcp_pcb* pcb, void* data, size_t data_len, u32_t seqno_offset, + u32_t ackno_offset, u8_t headerflags) +{ + 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->lastack + ackno_offset, headerflags); +} + +/** Create a TCP segment usable for passing to tcp_input + * - IP-addresses, ports, seqno and ackno are taken from pcb + * - seqno and ackno can be altered with an offset + * - TCP window can be adjusted + */ +struct pbuf* tcp_create_rx_segment_wnd(struct tcp_pcb* pcb, void* data, size_t data_len, + u32_t seqno_offset, u32_t ackno_offset, u8_t headerflags, u16_t wnd) +{ + return tcp_create_segment_wnd(&pcb->remote_ip, &pcb->local_ip, pcb->remote_port, pcb->local_port, + data, data_len, pcb->rcv_nxt + seqno_offset, pcb->lastack + ackno_offset, headerflags, wnd); +} + +/** Safely bring a tcp_pcb into the requested state */ +void +tcp_set_state(struct tcp_pcb* pcb, enum tcp_state state, ip_addr_t* local_ip, + ip_addr_t* remote_ip, u16_t local_port, u16_t remote_port) +{ + /* @todo: are these all states? */ + /* @todo: remove from previous list */ + pcb->state = state; + if (state == ESTABLISHED) { + TCP_REG(&tcp_active_pcbs, pcb); + pcb->local_ip.addr = local_ip->addr; + pcb->local_port = local_port; + pcb->remote_ip.addr = remote_ip->addr; + pcb->remote_port = remote_port; + } else if(state == LISTEN) { + TCP_REG(&tcp_listen_pcbs.pcbs, pcb); + pcb->local_ip.addr = local_ip->addr; + pcb->local_port = local_port; + } else if(state == TIME_WAIT) { + TCP_REG(&tcp_tw_pcbs, pcb); + pcb->local_ip.addr = local_ip->addr; + pcb->local_port = local_port; + pcb->remote_ip.addr = remote_ip->addr; + pcb->remote_port = remote_port; + } else { + fail(); + } +} + +void +test_tcp_counters_err(void* arg, err_t err) +{ + struct test_tcp_counters* counters = arg; + EXPECT_RET(arg != NULL); + counters->err_calls++; + counters->last_err = err; +} + +static void +test_tcp_counters_check_rxdata(struct test_tcp_counters* counters, struct pbuf* p) +{ + struct pbuf* q; + u32_t i, received; + if(counters->expected_data == NULL) { + /* no data to compare */ + return; + } + EXPECT_RET(counters->recved_bytes + p->tot_len <= counters->expected_data_len); + received = counters->recved_bytes; + for(q = p; q != NULL; q = q->next) { + char *data = q->payload; + for(i = 0; i < q->len; i++) { + EXPECT_RET(data[i] == counters->expected_data[received]); + received++; + } + } + EXPECT(received == counters->recved_bytes + p->tot_len); +} + +err_t +test_tcp_counters_recv(void* arg, struct tcp_pcb* pcb, struct pbuf* p, err_t err) +{ + struct test_tcp_counters* counters = arg; + EXPECT_RETX(arg != NULL, ERR_OK); + EXPECT_RETX(pcb != NULL, ERR_OK); + EXPECT_RETX(err == ERR_OK, ERR_OK); + + if (p != NULL) { + if (counters->close_calls == 0) { + counters->recv_calls++; + test_tcp_counters_check_rxdata(counters, p); + counters->recved_bytes += p->tot_len; + } else { + counters->recv_calls_after_close++; + counters->recved_bytes_after_close += p->tot_len; + } + pbuf_free(p); + } else { + counters->close_calls++; + } + EXPECT(counters->recv_calls_after_close == 0 && counters->recved_bytes_after_close == 0); + return ERR_OK; +} + +/** Allocate a pcb and set up the test_tcp_counters_* callbacks */ +struct tcp_pcb* +test_tcp_new_counters_pcb(struct test_tcp_counters* counters) +{ + struct tcp_pcb* pcb = tcp_new(); + if (pcb != NULL) { + /* set up args and callbacks */ + tcp_arg(pcb, counters); + tcp_recv(pcb, test_tcp_counters_recv); + tcp_err(pcb, test_tcp_counters_err); + pcb->snd_wnd = TCP_WND; + pcb->snd_wnd_max = TCP_WND; + } + return pcb; +} + +/** Calls tcp_input() after adjusting current_iphdr_dest */ +void test_tcp_input(struct pbuf *p, struct netif *inp) +{ + struct ip_hdr *iphdr = (struct ip_hdr*)p->payload; + ip_addr_copy(current_iphdr_dest, iphdr->dest); + ip_addr_copy(current_iphdr_src, iphdr->src); + current_netif = inp; + current_header = iphdr; + + tcp_input(p, inp); + + current_iphdr_dest.addr = 0; + current_iphdr_src.addr = 0; + current_netif = 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->copy_tx_packets) { + struct pbuf *p_copy = pbuf_alloc(PBUF_LINK, p->tot_len, PBUF_RAM); + err_t err; + EXPECT(p_copy != NULL); + err = pbuf_copy(p_copy, p); + EXPECT(err == ERR_OK); + if (txcounters->tx_packets == NULL) { + txcounters->tx_packets = p_copy; + } else { + pbuf_cat(txcounters->tx_packets, p_copy); + } + } + 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; +} diff --git a/ext/lwip/test/unit/tcp/tcp_helper.h b/ext/lwip/test/unit/tcp/tcp_helper.h new file mode 100644 index 00000000..4a72c935 --- /dev/null +++ b/ext/lwip/test/unit/tcp/tcp_helper.h @@ -0,0 +1,52 @@ +#ifndef __TCP_HELPER_H__ +#define __TCP_HELPER_H__ + +#include "../lwip_check.h" +#include "lwip/arch.h" +#include "lwip/tcp.h" +#include "lwip/netif.h" + +/* counters used for test_tcp_counters_* callback functions */ +struct test_tcp_counters { + u32_t recv_calls; + u32_t recved_bytes; + u32_t recv_calls_after_close; + u32_t recved_bytes_after_close; + u32_t close_calls; + u32_t err_calls; + err_t last_err; + char* expected_data; + u32_t expected_data_len; +}; + +struct test_tcp_txcounters { + u32_t num_tx_calls; + u32_t num_tx_bytes; + u8_t copy_tx_packets; + struct pbuf *tx_packets; +}; + +/* Helper functions */ +void tcp_remove_all(void); + +struct pbuf* tcp_create_segment(ip_addr_t* src_ip, ip_addr_t* dst_ip, + u16_t src_port, u16_t dst_port, void* data, size_t data_len, + u32_t seqno, u32_t ackno, u8_t headerflags); +struct pbuf* tcp_create_rx_segment(struct tcp_pcb* pcb, void* data, size_t data_len, + u32_t seqno_offset, u32_t ackno_offset, u8_t headerflags); +struct pbuf* tcp_create_rx_segment_wnd(struct tcp_pcb* pcb, void* data, size_t data_len, + u32_t seqno_offset, u32_t ackno_offset, u8_t headerflags, u16_t wnd); +void tcp_set_state(struct tcp_pcb* pcb, enum tcp_state state, ip_addr_t* local_ip, + ip_addr_t* remote_ip, u16_t local_port, u16_t remote_port); +void test_tcp_counters_err(void* arg, err_t err); +err_t test_tcp_counters_recv(void* arg, struct tcp_pcb* pcb, struct pbuf* p, err_t err); + +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_init_netif(struct netif *netif, struct test_tcp_txcounters *txcounters, + ip_addr_t *ip_addr, ip_addr_t *netmask); + + +#endif diff --git a/ext/lwip/test/unit/tcp/test_tcp.c b/ext/lwip/test/unit/tcp/test_tcp.c new file mode 100644 index 00000000..6fd5be50 --- /dev/null +++ b/ext/lwip/test/unit/tcp/test_tcp.c @@ -0,0 +1,667 @@ +#include "test_tcp.h" + +#include "lwip/tcp_impl.h" +#include "lwip/stats.h" +#include "tcp_helper.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 + +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 void +tcp_setup(void) +{ + /* reset iss to default (6510) */ + tcp_ticks = 0; + tcp_ticks = 0 - (tcp_next_iss() - 6510); + tcp_next_iss(); + tcp_ticks = 0; + + test_tcp_timer = 0; + tcp_remove_all(); +} + +static void +tcp_teardown(void) +{ + netif_list = NULL; + tcp_remove_all(); +} + + +/* 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(lwip_stats.memp[MEMP_TCP_PCB].used == 0); + + pcb = tcp_new(); + fail_unless(pcb != NULL); + if (pcb != NULL) { + fail_unless(lwip_stats.memp[MEMP_TCP_PCB].used == 1); + tcp_abort(pcb); + fail_unless(lwip_stats.memp[MEMP_TCP_PCB].used == 0); + } +} +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}; + ip_addr_t remote_ip, local_ip; + u16_t data_len; + u16_t remote_port = 0x100, local_port = 0x101; + struct netif netif; + LWIP_UNUSED_ARG(_i); + + /* initialize local vars */ + memset(&netif, 0, sizeof(netif)); + IP4_ADDR(&local_ip, 192, 168, 1, 1); + IP4_ADDR(&remote_ip, 192, 168, 1, 2); + 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, &local_ip, &remote_ip, local_port, 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(lwip_stats.memp[MEMP_TCP_PCB].used == 1); + tcp_abort(pcb); + EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 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[] = {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; + + /* 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 + +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; + ip_addr_t remote_ip, local_ip, netmask; + u16_t remote_port = 0x100, local_port = 0x101; + err_t err; +#define SEQNO1 (0xFFFFFF00 - TCP_MSS) +#define ISS 6510 + u16_t i, sent_total = 0; + u32_t seqnos[] = { + SEQNO1, + SEQNO1 + (1 * TCP_MSS), + SEQNO1 + (2 * TCP_MSS), + SEQNO1 + (3 * TCP_MSS), + SEQNO1 + (4 * TCP_MSS), + SEQNO1 + (5 * TCP_MSS)}; + LWIP_UNUSED_ARG(_i); + + for (i = 0; i < sizeof(tx_data); i++) { + tx_data[i] = (u8_t)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 */ + tcp_ticks = SEQNO1 - ISS; + pcb = test_tcp_new_counters_pcb(&counters); + EXPECT_RET(pcb != NULL); + EXPECT(pcb->lastack == SEQNO1); + 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 = 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]); + + /* 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 */ + 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(lwip_stats.memp[MEMP_TCP_PCB].used == 1); + tcp_abort(pcb); + EXPECT_RET(lwip_stats.memp[MEMP_TCP_PCB].used == 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; + ip_addr_t remote_ip, local_ip, netmask; + u16_t remote_port = 0x100, local_port = 0x101; + err_t err; +#define SEQNO1 (0xFFFFFF00 - TCP_MSS) +#define ISS 6510 + u16_t i, sent_total = 0; + u32_t seqnos[] = { + SEQNO1, + SEQNO1 + (1 * TCP_MSS), + SEQNO1 + (2 * TCP_MSS), + SEQNO1 + (3 * TCP_MSS), + SEQNO1 + (4 * TCP_MSS), + SEQNO1 + (5 * TCP_MSS)}; + LWIP_UNUSED_ARG(_i); + + for (i = 0; i < sizeof(tx_data); i++) { + tx_data[i] = (u8_t)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 */ + tcp_ticks = 0; + tcp_ticks = 0 - tcp_next_iss(); + tcp_ticks = SEQNO1 - tcp_next_iss(); + pcb = test_tcp_new_counters_pcb(&counters); + EXPECT_RET(pcb != NULL); + EXPECT(pcb->lastack == SEQNO1); + 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 = 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(lwip_stats.memp[MEMP_TCP_PCB].used == 1); + tcp_abort(pcb); + EXPECT_RET(lwip_stats.memp[MEMP_TCP_PCB].used == 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; + ip_addr_t remote_ip, local_ip, netmask; + u16_t remote_port = 0x100, local_port = 0x101; + err_t err; + u16_t sent_total, i; + 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 */ + 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)); + memset(&txcounters, 0, sizeof(txcounters)); + + /* 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; + + /* 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); + EXPECT(pcb->persist_backoff == 1); + } + + /* 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(lwip_stats.memp[MEMP_TCP_PCB].used == 1); + tcp_abort(pcb); + EXPECT_RET(lwip_stats.memp[MEMP_TCP_PCB].used == 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 + +/** Create the suite including all tests for this module */ +Suite * +tcp_suite(void) +{ + TFun tests[] = { + test_tcp_new_abort, + test_tcp_recv_inseq, + test_tcp_fast_retx_recover, + test_tcp_fast_rexmit_wraparound, + test_tcp_rto_rexmit_wraparound, + test_tcp_tx_full_window_lost_from_unacked, + test_tcp_tx_full_window_lost_from_unsent + }; + return create_suite("TCP", tests, sizeof(tests)/sizeof(TFun), tcp_setup, tcp_teardown); +} diff --git a/ext/lwip/test/unit/tcp/test_tcp.h b/ext/lwip/test/unit/tcp/test_tcp.h new file mode 100644 index 00000000..f1c4a463 --- /dev/null +++ b/ext/lwip/test/unit/tcp/test_tcp.h @@ -0,0 +1,8 @@ +#ifndef __TEST_TCP_H__ +#define __TEST_TCP_H__ + +#include "../lwip_check.h" + +Suite *tcp_suite(void); + +#endif diff --git a/ext/lwip/test/unit/tcp/test_tcp_oos.c b/ext/lwip/test/unit/tcp/test_tcp_oos.c new file mode 100644 index 00000000..764de1c4 --- /dev/null +++ b/ext/lwip/test/unit/tcp/test_tcp_oos.c @@ -0,0 +1,944 @@ +#include "test_tcp_oos.h" + +#include "lwip/tcp_impl.h" +#include "lwip/stats.h" +#include "tcp_helper.h" + +#if !LWIP_STATS || !TCP_STATS || !MEMP_STATS +#error "This tests needs TCP- and MEMP-statistics enabled" +#endif +#if !TCP_QUEUE_OOSEQ +#error "This tests needs TCP_QUEUE_OOSEQ enabled" +#endif + +/** CHECK_SEGMENTS_ON_OOSEQ: + * 1: check count, seqno and len of segments on pcb->ooseq (strict) + * 0: only check that bytes are received in correct order (less strict) */ +#define CHECK_SEGMENTS_ON_OOSEQ 1 + +#if CHECK_SEGMENTS_ON_OOSEQ +#define EXPECT_OOSEQ(x) EXPECT(x) +#else +#define EXPECT_OOSEQ(x) +#endif + +/* helper functions */ + +/** Get the numbers of segments on the ooseq list */ +static int tcp_oos_count(struct tcp_pcb* pcb) +{ + int num = 0; + struct tcp_seg* seg = pcb->ooseq; + while(seg != NULL) { + num++; + seg = seg->next; + } + return num; +} + +/** Get the numbers of pbufs on the ooseq list */ +static int tcp_oos_pbuf_count(struct tcp_pcb* pcb) +{ + int num = 0; + struct tcp_seg* seg = pcb->ooseq; + while(seg != NULL) { + num += pbuf_clen(seg->p); + seg = seg->next; + } + return num; +} + +/** Get the seqno of a segment (by index) on the ooseq list + * + * @param pcb the pcb to check for ooseq segments + * @param seg_index index of the segment on the ooseq list + * @return seqno of the segment + */ +static u32_t +tcp_oos_seg_seqno(struct tcp_pcb* pcb, int seg_index) +{ + int num = 0; + struct tcp_seg* seg = pcb->ooseq; + + /* then check the actual segment */ + while(seg != NULL) { + if(num == seg_index) { + return seg->tcphdr->seqno; + } + num++; + seg = seg->next; + } + fail(); + return 0; +} + +/** Get the tcplen (datalen + SYN/FIN) of a segment (by index) on the ooseq list + * + * @param pcb the pcb to check for ooseq segments + * @param seg_index index of the segment on the ooseq list + * @return tcplen of the segment + */ +static int +tcp_oos_seg_tcplen(struct tcp_pcb* pcb, int seg_index) +{ + int num = 0; + struct tcp_seg* seg = pcb->ooseq; + + /* then check the actual segment */ + while(seg != NULL) { + if(num == seg_index) { + return TCP_TCPLEN(seg); + } + num++; + seg = seg->next; + } + fail(); + return -1; +} + +/** Get the tcplen (datalen + SYN/FIN) of all segments on the ooseq list + * + * @param pcb the pcb to check for ooseq segments + * @return tcplen of all segment + */ +static int +tcp_oos_tcplen(struct tcp_pcb* pcb) +{ + int len = 0; + struct tcp_seg* seg = pcb->ooseq; + + /* then check the actual segment */ + while(seg != NULL) { + len += TCP_TCPLEN(seg); + seg = seg->next; + } + return len; +} + +/* Setup/teardown functions */ + +static void +tcp_oos_setup(void) +{ + tcp_remove_all(); +} + +static void +tcp_oos_teardown(void) +{ + tcp_remove_all(); +} + + + +/* Test functions */ + +/** create multiple segments and pass them to tcp_input in a wrong + * order to see if ooseq-caching works correctly + * FIN is received in out-of-sequence segments only */ +START_TEST(test_tcp_recv_ooseq_FIN_OOSEQ) +{ + struct test_tcp_counters counters; + struct tcp_pcb* pcb; + struct pbuf *p_8_9, *p_4_8, *p_4_10, *p_2_14, *p_fin, *pinseq; + char data[] = { + 1, 2, 3, 4, + 5, 6, 7, 8, + 9, 10, 11, 12, + 13, 14, 15, 16}; + ip_addr_t remote_ip, local_ip; + u16_t data_len; + u16_t remote_port = 0x100, local_port = 0x101; + struct netif netif; + LWIP_UNUSED_ARG(_i); + + /* initialize local vars */ + memset(&netif, 0, sizeof(netif)); + IP4_ADDR(&local_ip, 192, 168, 1, 1); + IP4_ADDR(&remote_ip, 192, 168, 1, 2); + 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, &local_ip, &remote_ip, local_port, remote_port); + + /* create segments */ + /* pinseq is sent as last segment! */ + pinseq = tcp_create_rx_segment(pcb, &data[0], 4, 0, 0, TCP_ACK); + /* p1: 8 bytes before FIN */ + /* seqno: 8..16 */ + p_8_9 = tcp_create_rx_segment(pcb, &data[8], 8, 8, 0, TCP_ACK|TCP_FIN); + /* p2: 4 bytes before p1, including the first 4 bytes of p1 (partly duplicate) */ + /* seqno: 4..11 */ + p_4_8 = tcp_create_rx_segment(pcb, &data[4], 8, 4, 0, TCP_ACK); + /* p3: same as p2 but 2 bytes longer */ + /* seqno: 4..13 */ + p_4_10 = tcp_create_rx_segment(pcb, &data[4], 10, 4, 0, TCP_ACK); + /* p4: 14 bytes before FIN, includes data from p1 and p2, plus partly from pinseq */ + /* seqno: 2..15 */ + p_2_14 = tcp_create_rx_segment(pcb, &data[2], 14, 2, 0, TCP_ACK); + /* FIN, seqno 16 */ + p_fin = tcp_create_rx_segment(pcb, NULL, 0,16, 0, TCP_ACK|TCP_FIN); + EXPECT(pinseq != NULL); + EXPECT(p_8_9 != NULL); + EXPECT(p_4_8 != NULL); + EXPECT(p_4_10 != NULL); + EXPECT(p_2_14 != NULL); + EXPECT(p_fin != NULL); + if ((pinseq != NULL) && (p_8_9 != NULL) && (p_4_8 != NULL) && (p_4_10 != NULL) && (p_2_14 != NULL) && (p_fin != NULL)) { + /* pass the segment to tcp_input */ + test_tcp_input(p_8_9, &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); + /* check ooseq queue */ + EXPECT_OOSEQ(tcp_oos_count(pcb) == 1); + EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 0) == 8); + EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 0) == 9); /* includes FIN */ + + /* pass the segment to tcp_input */ + test_tcp_input(p_4_8, &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); + /* check ooseq queue */ + EXPECT_OOSEQ(tcp_oos_count(pcb) == 2); + EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 0) == 4); + EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 0) == 4); + EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 1) == 8); + EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 1) == 9); /* includes FIN */ + + /* pass the segment to tcp_input */ + test_tcp_input(p_4_10, &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); + /* ooseq queue: unchanged */ + EXPECT_OOSEQ(tcp_oos_count(pcb) == 2); + EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 0) == 4); + EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 0) == 4); + EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 1) == 8); + EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 1) == 9); /* includes FIN */ + + /* pass the segment to tcp_input */ + test_tcp_input(p_2_14, &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); + /* check ooseq queue */ + EXPECT_OOSEQ(tcp_oos_count(pcb) == 1); + EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 0) == 2); + EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 0) == 15); /* includes FIN */ + + /* pass the segment to tcp_input */ + test_tcp_input(p_fin, &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); + /* ooseq queue: unchanged */ + EXPECT_OOSEQ(tcp_oos_count(pcb) == 1); + EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 0) == 2); + EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 0) == 15); /* includes FIN */ + + /* pass the segment to tcp_input */ + test_tcp_input(pinseq, &netif); + /* check if counters are as expected */ + EXPECT(counters.close_calls == 1); + EXPECT(counters.recv_calls == 1); + EXPECT(counters.recved_bytes == data_len); + EXPECT(counters.err_calls == 0); + EXPECT(pcb->ooseq == NULL); + } + + /* make sure the pcb is freed */ + EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 1); + tcp_abort(pcb); + EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 0); +} +END_TEST + + +/** create multiple segments and pass them to tcp_input in a wrong + * order to see if ooseq-caching works correctly + * FIN is received IN-SEQUENCE at the end */ +START_TEST(test_tcp_recv_ooseq_FIN_INSEQ) +{ + struct test_tcp_counters counters; + struct tcp_pcb* pcb; + struct pbuf *p_1_2, *p_4_8, *p_3_11, *p_2_12, *p_15_1, *p_15_1a, *pinseq, *pinseqFIN; + char data[] = { + 1, 2, 3, 4, + 5, 6, 7, 8, + 9, 10, 11, 12, + 13, 14, 15, 16}; + ip_addr_t remote_ip, local_ip; + u16_t data_len; + u16_t remote_port = 0x100, local_port = 0x101; + struct netif netif; + LWIP_UNUSED_ARG(_i); + + /* initialize local vars */ + memset(&netif, 0, sizeof(netif)); + IP4_ADDR(&local_ip, 192, 168, 1, 1); + IP4_ADDR(&remote_ip, 192, 168, 1, 2); + 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, &local_ip, &remote_ip, local_port, remote_port); + + /* create segments */ + /* p1: 7 bytes - 2 before FIN */ + /* seqno: 1..2 */ + p_1_2 = tcp_create_rx_segment(pcb, &data[1], 2, 1, 0, TCP_ACK); + /* p2: 4 bytes before p1, including the first 4 bytes of p1 (partly duplicate) */ + /* seqno: 4..11 */ + p_4_8 = tcp_create_rx_segment(pcb, &data[4], 8, 4, 0, TCP_ACK); + /* p3: same as p2 but 2 bytes longer and one byte more at the front */ + /* seqno: 3..13 */ + p_3_11 = tcp_create_rx_segment(pcb, &data[3], 11, 3, 0, TCP_ACK); + /* p4: 13 bytes - 2 before FIN - should be ignored as contained in p1 and p3 */ + /* seqno: 2..13 */ + p_2_12 = tcp_create_rx_segment(pcb, &data[2], 12, 2, 0, TCP_ACK); + /* pinseq is the first segment that is held back to create ooseq! */ + /* seqno: 0..3 */ + pinseq = tcp_create_rx_segment(pcb, &data[0], 4, 0, 0, TCP_ACK); + /* p5: last byte before FIN */ + /* seqno: 15 */ + p_15_1 = tcp_create_rx_segment(pcb, &data[15], 1, 15, 0, TCP_ACK); + /* p6: same as p5, should be ignored */ + p_15_1a= tcp_create_rx_segment(pcb, &data[15], 1, 15, 0, TCP_ACK); + /* pinseqFIN: last 2 bytes plus FIN */ + /* only segment containing seqno 14 and FIN */ + pinseqFIN = tcp_create_rx_segment(pcb, &data[14], 2, 14, 0, TCP_ACK|TCP_FIN); + EXPECT(pinseq != NULL); + EXPECT(p_1_2 != NULL); + EXPECT(p_4_8 != NULL); + EXPECT(p_3_11 != NULL); + EXPECT(p_2_12 != NULL); + EXPECT(p_15_1 != NULL); + EXPECT(p_15_1a != NULL); + EXPECT(pinseqFIN != NULL); + if ((pinseq != NULL) && (p_1_2 != NULL) && (p_4_8 != NULL) && (p_3_11 != NULL) && (p_2_12 != NULL) + && (p_15_1 != NULL) && (p_15_1a != NULL) && (pinseqFIN != NULL)) { + /* pass the segment to tcp_input */ + test_tcp_input(p_1_2, &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); + /* check ooseq queue */ + EXPECT_OOSEQ(tcp_oos_count(pcb) == 1); + EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 0) == 1); + EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 0) == 2); + + /* pass the segment to tcp_input */ + test_tcp_input(p_4_8, &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); + /* check ooseq queue */ + EXPECT_OOSEQ(tcp_oos_count(pcb) == 2); + EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 0) == 1); + EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 0) == 2); + EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 1) == 4); + EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 1) == 8); + + /* pass the segment to tcp_input */ + test_tcp_input(p_3_11, &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); + /* check ooseq queue */ + EXPECT_OOSEQ(tcp_oos_count(pcb) == 2); + EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 0) == 1); + EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 0) == 2); + /* p_3_11 has removed p_4_8 from ooseq */ + EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 1) == 3); + EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 1) == 11); + + /* pass the segment to tcp_input */ + test_tcp_input(p_2_12, &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); + /* check ooseq queue */ + EXPECT_OOSEQ(tcp_oos_count(pcb) == 2); + EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 0) == 1); + EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 0) == 1); + EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 1) == 2); + EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 1) == 12); + + /* pass the segment to tcp_input */ + test_tcp_input(pinseq, &netif); + /* check if counters are as expected */ + EXPECT(counters.close_calls == 0); + EXPECT(counters.recv_calls == 1); + EXPECT(counters.recved_bytes == 14); + EXPECT(counters.err_calls == 0); + EXPECT(pcb->ooseq == NULL); + + /* pass the segment to tcp_input */ + test_tcp_input(p_15_1, &netif); + /* check if counters are as expected */ + EXPECT(counters.close_calls == 0); + EXPECT(counters.recv_calls == 1); + EXPECT(counters.recved_bytes == 14); + EXPECT(counters.err_calls == 0); + /* check ooseq queue */ + EXPECT_OOSEQ(tcp_oos_count(pcb) == 1); + EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 0) == 15); + EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 0) == 1); + + /* pass the segment to tcp_input */ + test_tcp_input(p_15_1a, &netif); + /* check if counters are as expected */ + EXPECT(counters.close_calls == 0); + EXPECT(counters.recv_calls == 1); + EXPECT(counters.recved_bytes == 14); + EXPECT(counters.err_calls == 0); + /* check ooseq queue: unchanged */ + EXPECT_OOSEQ(tcp_oos_count(pcb) == 1); + EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 0) == 15); + EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 0) == 1); + + /* pass the segment to tcp_input */ + test_tcp_input(pinseqFIN, &netif); + /* check if counters are as expected */ + EXPECT(counters.close_calls == 1); + EXPECT(counters.recv_calls == 2); + EXPECT(counters.recved_bytes == data_len); + EXPECT(counters.err_calls == 0); + EXPECT(pcb->ooseq == NULL); + } + + /* make sure the pcb is freed */ + EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 1); + tcp_abort(pcb); + EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 0); +} +END_TEST + +static char data_full_wnd[TCP_WND]; + +/** create multiple segments and pass them to tcp_input with the first segment missing + * to simulate overruning the rxwin with ooseq queueing enabled */ +START_TEST(test_tcp_recv_ooseq_overrun_rxwin) +{ +#if !TCP_OOSEQ_MAX_BYTES && !TCP_OOSEQ_MAX_PBUFS + int i, k; + struct test_tcp_counters counters; + struct tcp_pcb* pcb; + struct pbuf *pinseq, *p_ovr; + ip_addr_t remote_ip, local_ip; + u16_t remote_port = 0x100, local_port = 0x101; + struct netif netif; + int datalen = 0; + int datalen2; + + for(i = 0; i < sizeof(data_full_wnd); i++) { + data_full_wnd[i] = (char)i; + } + + /* initialize local vars */ + memset(&netif, 0, sizeof(netif)); + IP4_ADDR(&local_ip, 192, 168, 1, 1); + IP4_ADDR(&remote_ip, 192, 168, 1, 2); + /* initialize counter struct */ + memset(&counters, 0, sizeof(counters)); + counters.expected_data_len = TCP_WND; + counters.expected_data = data_full_wnd; + + /* 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->rcv_nxt = 0x8000; + + /* create segments */ + /* pinseq is sent as last segment! */ + pinseq = tcp_create_rx_segment(pcb, &data_full_wnd[0], TCP_MSS, 0, 0, TCP_ACK); + + for(i = TCP_MSS, k = 0; i < TCP_WND; i += TCP_MSS, k++) { + int count, expected_datalen; + struct pbuf *p = tcp_create_rx_segment(pcb, &data_full_wnd[TCP_MSS*(k+1)], + TCP_MSS, TCP_MSS*(k+1), 0, TCP_ACK); + EXPECT_RET(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); + /* check ooseq queue */ + count = tcp_oos_count(pcb); + EXPECT_OOSEQ(count == k+1); + datalen = tcp_oos_tcplen(pcb); + if (i + TCP_MSS < TCP_WND) { + expected_datalen = (k+1)*TCP_MSS; + } else { + expected_datalen = TCP_WND - TCP_MSS; + } + if (datalen != expected_datalen) { + EXPECT_OOSEQ(datalen == expected_datalen); + } + } + + /* pass in one more segment, cleary overrunning the rxwin */ + p_ovr = tcp_create_rx_segment(pcb, &data_full_wnd[TCP_MSS*(k+1)], TCP_MSS, TCP_MSS*(k+1), 0, TCP_ACK); + EXPECT_RET(p_ovr != NULL); + /* pass the segment to tcp_input */ + test_tcp_input(p_ovr, &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); + /* check ooseq queue */ + EXPECT_OOSEQ(tcp_oos_count(pcb) == k); + datalen2 = tcp_oos_tcplen(pcb); + EXPECT_OOSEQ(datalen == datalen2); + + /* now pass inseq */ + test_tcp_input(pinseq, &netif); + EXPECT(pcb->ooseq == NULL); + + /* make sure the pcb is freed */ + EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 1); + tcp_abort(pcb); + EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 0); +#endif /* !TCP_OOSEQ_MAX_BYTES && !TCP_OOSEQ_MAX_PBUFS */ + LWIP_UNUSED_ARG(_i); +} +END_TEST + +START_TEST(test_tcp_recv_ooseq_max_bytes) +{ +#if TCP_OOSEQ_MAX_BYTES && (TCP_OOSEQ_MAX_BYTES < (TCP_WND + 1)) && (PBUF_POOL_BUFSIZE >= (TCP_MSS + PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN)) + int i, k; + struct test_tcp_counters counters; + struct tcp_pcb* pcb; + struct pbuf *p_ovr; + ip_addr_t remote_ip, local_ip; + u16_t remote_port = 0x100, local_port = 0x101; + struct netif netif; + int datalen = 0; + int datalen2; + + for(i = 0; i < sizeof(data_full_wnd); i++) { + data_full_wnd[i] = (char)i; + } + + /* initialize local vars */ + memset(&netif, 0, sizeof(netif)); + IP4_ADDR(&local_ip, 192, 168, 1, 1); + IP4_ADDR(&remote_ip, 192, 168, 1, 2); + /* initialize counter struct */ + memset(&counters, 0, sizeof(counters)); + counters.expected_data_len = TCP_WND; + counters.expected_data = data_full_wnd; + + /* 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->rcv_nxt = 0x8000; + + /* don't 'recv' the first segment (1 byte) so that all other segments will be ooseq */ + + /* create segments and 'recv' them */ + for(k = 1, i = 1; k < TCP_OOSEQ_MAX_BYTES; k += TCP_MSS, i++) { + int count; + struct pbuf *p = tcp_create_rx_segment(pcb, &data_full_wnd[k], + TCP_MSS, k, 0, TCP_ACK); + EXPECT_RET(p != NULL); + EXPECT_RET(p->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 == 0); + EXPECT(counters.recved_bytes == 0); + EXPECT(counters.err_calls == 0); + /* check ooseq queue */ + count = tcp_oos_pbuf_count(pcb); + EXPECT_OOSEQ(count == i); + datalen = tcp_oos_tcplen(pcb); + EXPECT_OOSEQ(datalen == (i * TCP_MSS)); + } + + /* pass in one more segment, overrunning the limit */ + p_ovr = tcp_create_rx_segment(pcb, &data_full_wnd[k+1], 1, k+1, 0, TCP_ACK); + EXPECT_RET(p_ovr != NULL); + /* pass the segment to tcp_input */ + test_tcp_input(p_ovr, &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); + /* check ooseq queue (ensure the new segment was not accepted) */ + EXPECT_OOSEQ(tcp_oos_count(pcb) == (i-1)); + datalen2 = tcp_oos_tcplen(pcb); + EXPECT_OOSEQ(datalen2 == ((i-1) * TCP_MSS)); + + /* make sure the pcb is freed */ + EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 1); + tcp_abort(pcb); + EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 0); +#endif /* TCP_OOSEQ_MAX_BYTES && (TCP_OOSEQ_MAX_BYTES < (TCP_WND + 1)) && (PBUF_POOL_BUFSIZE >= (TCP_MSS + PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN)) */ + LWIP_UNUSED_ARG(_i); +} +END_TEST + +START_TEST(test_tcp_recv_ooseq_max_pbufs) +{ +#if TCP_OOSEQ_MAX_PBUFS && (TCP_OOSEQ_MAX_PBUFS < ((TCP_WND / TCP_MSS) + 1)) && (PBUF_POOL_BUFSIZE >= (TCP_MSS + PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN)) + int i; + struct test_tcp_counters counters; + struct tcp_pcb* pcb; + struct pbuf *p_ovr; + ip_addr_t remote_ip, local_ip; + u16_t remote_port = 0x100, local_port = 0x101; + struct netif netif; + int datalen = 0; + int datalen2; + + for(i = 0; i < sizeof(data_full_wnd); i++) { + data_full_wnd[i] = (char)i; + } + + /* initialize local vars */ + memset(&netif, 0, sizeof(netif)); + IP4_ADDR(&local_ip, 192, 168, 1, 1); + IP4_ADDR(&remote_ip, 192, 168, 1, 2); + /* initialize counter struct */ + memset(&counters, 0, sizeof(counters)); + counters.expected_data_len = TCP_WND; + counters.expected_data = data_full_wnd; + + /* 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->rcv_nxt = 0x8000; + + /* don't 'recv' the first segment (1 byte) so that all other segments will be ooseq */ + + /* create segments and 'recv' them */ + for(i = 1; i <= TCP_OOSEQ_MAX_PBUFS; i++) { + int count; + struct pbuf *p = tcp_create_rx_segment(pcb, &data_full_wnd[i], + 1, i, 0, TCP_ACK); + EXPECT_RET(p != NULL); + EXPECT_RET(p->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 == 0); + EXPECT(counters.recved_bytes == 0); + EXPECT(counters.err_calls == 0); + /* check ooseq queue */ + count = tcp_oos_pbuf_count(pcb); + EXPECT_OOSEQ(count == i); + datalen = tcp_oos_tcplen(pcb); + EXPECT_OOSEQ(datalen == i); + } + + /* pass in one more segment, overrunning the limit */ + p_ovr = tcp_create_rx_segment(pcb, &data_full_wnd[i+1], 1, i+1, 0, TCP_ACK); + EXPECT_RET(p_ovr != NULL); + /* pass the segment to tcp_input */ + test_tcp_input(p_ovr, &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); + /* check ooseq queue (ensure the new segment was not accepted) */ + EXPECT_OOSEQ(tcp_oos_count(pcb) == (i-1)); + datalen2 = tcp_oos_tcplen(pcb); + EXPECT_OOSEQ(datalen2 == (i-1)); + + /* make sure the pcb is freed */ + EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 1); + tcp_abort(pcb); + EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 0); +#endif /* TCP_OOSEQ_MAX_PBUFS && (TCP_OOSEQ_MAX_BYTES < (TCP_WND + 1)) && (PBUF_POOL_BUFSIZE >= (TCP_MSS + PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN)) */ + LWIP_UNUSED_ARG(_i); +} +END_TEST + +static void +check_rx_counters(struct tcp_pcb *pcb, struct test_tcp_counters *counters, u32_t exp_close_calls, u32_t exp_rx_calls, + u32_t exp_rx_bytes, u32_t exp_err_calls, int exp_oos_count, int exp_oos_len) +{ + int oos_len; + EXPECT(counters->close_calls == exp_close_calls); + EXPECT(counters->recv_calls == exp_rx_calls); + EXPECT(counters->recved_bytes == exp_rx_bytes); + EXPECT(counters->err_calls == exp_err_calls); + /* check that pbuf is queued in ooseq */ + EXPECT_OOSEQ(tcp_oos_count(pcb) == exp_oos_count); + oos_len = tcp_oos_tcplen(pcb); + EXPECT_OOSEQ(exp_oos_len == oos_len); +} + +/* this test uses 4 packets: + * - data (len=TCP_MSS) + * - FIN + * - data after FIN (len=1) (invalid) + * - 2nd FIN (invalid) + * + * the parameter 'delay_packet' is a bitmask that choses which on these packets is ooseq + */ +static void test_tcp_recv_ooseq_double_FINs(int delay_packet) +{ + int i, k; + struct test_tcp_counters counters; + struct tcp_pcb* pcb; + struct pbuf *p_normal_fin, *p_data_after_fin, *p, *p_2nd_fin_ooseq; + ip_addr_t remote_ip, local_ip; + u16_t remote_port = 0x100, local_port = 0x101; + struct netif netif; + u32_t exp_rx_calls = 0, exp_rx_bytes = 0, exp_close_calls = 0, exp_oos_pbufs = 0, exp_oos_tcplen = 0; + int first_dropped = 0xff; + int last_dropped = 0; + + for(i = 0; i < sizeof(data_full_wnd); i++) { + data_full_wnd[i] = (char)i; + } + + /* initialize local vars */ + memset(&netif, 0, sizeof(netif)); + IP4_ADDR(&local_ip, 192, 168, 1, 1); + IP4_ADDR(&remote_ip, 192, 168, 1, 2); + /* initialize counter struct */ + memset(&counters, 0, sizeof(counters)); + counters.expected_data_len = TCP_WND; + counters.expected_data = data_full_wnd; + + /* 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->rcv_nxt = 0x8000; + + /* create segments */ + p = tcp_create_rx_segment(pcb, &data_full_wnd[0], TCP_MSS, 0, 0, TCP_ACK); + p_normal_fin = tcp_create_rx_segment(pcb, NULL, 0, TCP_MSS, 0, TCP_ACK|TCP_FIN); + k = 1; + p_data_after_fin = tcp_create_rx_segment(pcb, &data_full_wnd[TCP_MSS+1], k, TCP_MSS+1, 0, TCP_ACK); + p_2nd_fin_ooseq = tcp_create_rx_segment(pcb, NULL, 0, TCP_MSS+1+k, 0, TCP_ACK|TCP_FIN); + + if(delay_packet & 1) { + /* drop normal data */ + first_dropped = 1; + last_dropped = 1; + } else { + /* send normal data */ + test_tcp_input(p, &netif); + exp_rx_calls++; + exp_rx_bytes += TCP_MSS; + } + /* check if counters are as expected */ + check_rx_counters(pcb, &counters, exp_close_calls, exp_rx_calls, exp_rx_bytes, 0, exp_oos_pbufs, exp_oos_tcplen); + + if(delay_packet & 2) { + /* drop FIN */ + if(first_dropped > 2) { + first_dropped = 2; + } + last_dropped = 2; + } else { + /* send FIN */ + test_tcp_input(p_normal_fin, &netif); + if (first_dropped < 2) { + /* already dropped packets, this one is ooseq */ + exp_oos_pbufs++; + exp_oos_tcplen++; + } else { + /* inseq */ + exp_close_calls++; + } + } + /* check if counters are as expected */ + check_rx_counters(pcb, &counters, exp_close_calls, exp_rx_calls, exp_rx_bytes, 0, exp_oos_pbufs, exp_oos_tcplen); + + if(delay_packet & 4) { + /* drop data-after-FIN */ + if(first_dropped > 3) { + first_dropped = 3; + } + last_dropped = 3; + } else { + /* send data-after-FIN */ + test_tcp_input(p_data_after_fin, &netif); + if (first_dropped < 3) { + /* already dropped packets, this one is ooseq */ + if (delay_packet & 2) { + /* correct FIN was ooseq */ + exp_oos_pbufs++; + exp_oos_tcplen += k; + } + } else { + /* inseq: no change */ + } + } + /* check if counters are as expected */ + check_rx_counters(pcb, &counters, exp_close_calls, exp_rx_calls, exp_rx_bytes, 0, exp_oos_pbufs, exp_oos_tcplen); + + if(delay_packet & 8) { + /* drop 2nd-FIN */ + if(first_dropped > 4) { + first_dropped = 4; + } + last_dropped = 4; + } else { + /* send 2nd-FIN */ + test_tcp_input(p_2nd_fin_ooseq, &netif); + if (first_dropped < 3) { + /* already dropped packets, this one is ooseq */ + if (delay_packet & 2) { + /* correct FIN was ooseq */ + exp_oos_pbufs++; + exp_oos_tcplen++; + } + } else { + /* inseq: no change */ + } + } + /* check if counters are as expected */ + check_rx_counters(pcb, &counters, exp_close_calls, exp_rx_calls, exp_rx_bytes, 0, exp_oos_pbufs, exp_oos_tcplen); + + if(delay_packet & 1) { + /* dropped normal data before */ + test_tcp_input(p, &netif); + exp_rx_calls++; + exp_rx_bytes += TCP_MSS; + if((delay_packet & 2) == 0) { + /* normal FIN was NOT delayed */ + exp_close_calls++; + exp_oos_pbufs = exp_oos_tcplen = 0; + } + } + /* check if counters are as expected */ + check_rx_counters(pcb, &counters, exp_close_calls, exp_rx_calls, exp_rx_bytes, 0, exp_oos_pbufs, exp_oos_tcplen); + + if(delay_packet & 2) { + /* dropped normal FIN before */ + test_tcp_input(p_normal_fin, &netif); + exp_close_calls++; + exp_oos_pbufs = exp_oos_tcplen = 0; + } + /* check if counters are as expected */ + check_rx_counters(pcb, &counters, exp_close_calls, exp_rx_calls, exp_rx_bytes, 0, exp_oos_pbufs, exp_oos_tcplen); + + if(delay_packet & 4) { + /* dropped data-after-FIN before */ + test_tcp_input(p_data_after_fin, &netif); + } + /* check if counters are as expected */ + check_rx_counters(pcb, &counters, exp_close_calls, exp_rx_calls, exp_rx_bytes, 0, exp_oos_pbufs, exp_oos_tcplen); + + if(delay_packet & 8) { + /* dropped 2nd-FIN before */ + test_tcp_input(p_2nd_fin_ooseq, &netif); + } + /* check if counters are as expected */ + check_rx_counters(pcb, &counters, exp_close_calls, exp_rx_calls, exp_rx_bytes, 0, exp_oos_pbufs, exp_oos_tcplen); + + /* check that ooseq data has been dumped */ + EXPECT(pcb->ooseq == NULL); + + /* make sure the pcb is freed */ + EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 1); + tcp_abort(pcb); + EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 0); +} + +/** create multiple segments and pass them to tcp_input with the first segment missing + * to simulate overruning the rxwin with ooseq queueing enabled */ +#define FIN_TEST(name, num) \ + START_TEST(name) \ + { \ + LWIP_UNUSED_ARG(_i); \ + test_tcp_recv_ooseq_double_FINs(num); \ + } \ + END_TEST +FIN_TEST(test_tcp_recv_ooseq_double_FIN_0, 0) +FIN_TEST(test_tcp_recv_ooseq_double_FIN_1, 1) +FIN_TEST(test_tcp_recv_ooseq_double_FIN_2, 2) +FIN_TEST(test_tcp_recv_ooseq_double_FIN_3, 3) +FIN_TEST(test_tcp_recv_ooseq_double_FIN_4, 4) +FIN_TEST(test_tcp_recv_ooseq_double_FIN_5, 5) +FIN_TEST(test_tcp_recv_ooseq_double_FIN_6, 6) +FIN_TEST(test_tcp_recv_ooseq_double_FIN_7, 7) +FIN_TEST(test_tcp_recv_ooseq_double_FIN_8, 8) +FIN_TEST(test_tcp_recv_ooseq_double_FIN_9, 9) +FIN_TEST(test_tcp_recv_ooseq_double_FIN_10, 10) +FIN_TEST(test_tcp_recv_ooseq_double_FIN_11, 11) +FIN_TEST(test_tcp_recv_ooseq_double_FIN_12, 12) +FIN_TEST(test_tcp_recv_ooseq_double_FIN_13, 13) +FIN_TEST(test_tcp_recv_ooseq_double_FIN_14, 14) +FIN_TEST(test_tcp_recv_ooseq_double_FIN_15, 15) + + +/** Create the suite including all tests for this module */ +Suite * +tcp_oos_suite(void) +{ + TFun tests[] = { + test_tcp_recv_ooseq_FIN_OOSEQ, + test_tcp_recv_ooseq_FIN_INSEQ, + test_tcp_recv_ooseq_overrun_rxwin, + test_tcp_recv_ooseq_max_bytes, + test_tcp_recv_ooseq_max_pbufs, + test_tcp_recv_ooseq_double_FIN_0, + test_tcp_recv_ooseq_double_FIN_1, + test_tcp_recv_ooseq_double_FIN_2, + test_tcp_recv_ooseq_double_FIN_3, + test_tcp_recv_ooseq_double_FIN_4, + test_tcp_recv_ooseq_double_FIN_5, + test_tcp_recv_ooseq_double_FIN_6, + test_tcp_recv_ooseq_double_FIN_7, + test_tcp_recv_ooseq_double_FIN_8, + test_tcp_recv_ooseq_double_FIN_9, + test_tcp_recv_ooseq_double_FIN_10, + test_tcp_recv_ooseq_double_FIN_11, + test_tcp_recv_ooseq_double_FIN_12, + test_tcp_recv_ooseq_double_FIN_13, + test_tcp_recv_ooseq_double_FIN_14, + test_tcp_recv_ooseq_double_FIN_15 + }; + return create_suite("TCP_OOS", tests, sizeof(tests)/sizeof(TFun), tcp_oos_setup, tcp_oos_teardown); +} diff --git a/ext/lwip/test/unit/tcp/test_tcp_oos.h b/ext/lwip/test/unit/tcp/test_tcp_oos.h new file mode 100644 index 00000000..5e411f00 --- /dev/null +++ b/ext/lwip/test/unit/tcp/test_tcp_oos.h @@ -0,0 +1,8 @@ +#ifndef __TEST_TCP_OOS_H__ +#define __TEST_TCP_OOS_H__ + +#include "../lwip_check.h" + +Suite *tcp_oos_suite(void); + +#endif diff --git a/ext/lwip/test/unit/udp/test_udp.c b/ext/lwip/test/unit/udp/test_udp.c new file mode 100644 index 00000000..a2f02af0 --- /dev/null +++ b/ext/lwip/test/unit/udp/test_udp.c @@ -0,0 +1,68 @@ +#include "test_udp.h" + +#include "lwip/udp.h" +#include "lwip/stats.h" + +#if !LWIP_STATS || !UDP_STATS || !MEMP_STATS +#error "This tests needs UDP- and MEMP-statistics enabled" +#endif + +/* Helper functions */ +static void +udp_remove_all(void) +{ + struct udp_pcb *pcb = udp_pcbs; + struct udp_pcb *pcb2; + + while(pcb != NULL) { + pcb2 = pcb; + pcb = pcb->next; + udp_remove(pcb2); + } + fail_unless(lwip_stats.memp[MEMP_UDP_PCB].used == 0); +} + +/* Setups/teardown functions */ + +static void +udp_setup(void) +{ + udp_remove_all(); +} + +static void +udp_teardown(void) +{ + udp_remove_all(); +} + + +/* Test functions */ + +START_TEST(test_udp_new_remove) +{ + struct udp_pcb* pcb; + LWIP_UNUSED_ARG(_i); + + fail_unless(lwip_stats.memp[MEMP_UDP_PCB].used == 0); + + pcb = udp_new(); + fail_unless(pcb != NULL); + if (pcb != NULL) { + fail_unless(lwip_stats.memp[MEMP_UDP_PCB].used == 1); + udp_remove(pcb); + fail_unless(lwip_stats.memp[MEMP_UDP_PCB].used == 0); + } +} +END_TEST + + +/** Create the suite including all tests for this module */ +Suite * +udp_suite(void) +{ + TFun tests[] = { + test_udp_new_remove, + }; + return create_suite("UDP", tests, sizeof(tests)/sizeof(TFun), udp_setup, udp_teardown); +} diff --git a/ext/lwip/test/unit/udp/test_udp.h b/ext/lwip/test/unit/udp/test_udp.h new file mode 100644 index 00000000..93353682 --- /dev/null +++ b/ext/lwip/test/unit/udp/test_udp.h @@ -0,0 +1,8 @@ +#ifndef __TEST_UDP_H__ +#define __TEST_UDP_H__ + +#include "../lwip_check.h" + +Suite* udp_suite(void); + +#endif |