Release v2.2.0_20250106
diff --git a/test/unit/Filelists.cmake b/test/unit/Filelists.cmake index e1f40bc..b3db893 100644 --- a/test/unit/Filelists.cmake +++ b/test/unit/Filelists.cmake
@@ -5,15 +5,20 @@ # # This file is NOT designed (on purpose) to be used as cmake # subdir via add_subdirectory() -# The intention is to provide greater flexibility to users to +# The intention is to provide greater flexibility to users to # create their own targets using the *_SRCS variables. +if(NOT ${CMAKE_VERSION} VERSION_LESS "3.10.0") + include_guard(GLOBAL) +endif() + set(LWIP_TESTDIR ${LWIP_DIR}/test/unit) set(LWIP_TESTFILES ${LWIP_TESTDIR}/lwip_unittests.c ${LWIP_TESTDIR}/api/test_sockets.c ${LWIP_TESTDIR}/arch/sys_arch.c ${LWIP_TESTDIR}/core/test_def.c + ${LWIP_TESTDIR}/core/test_dns.c ${LWIP_TESTDIR}/core/test_mem.c ${LWIP_TESTDIR}/core/test_netif.c ${LWIP_TESTDIR}/core/test_pbuf.c @@ -26,6 +31,8 @@ ${LWIP_TESTDIR}/mqtt/test_mqtt.c ${LWIP_TESTDIR}/tcp/tcp_helper.c ${LWIP_TESTDIR}/tcp/test_tcp_oos.c + ${LWIP_TESTDIR}/tcp/test_tcp_state.c ${LWIP_TESTDIR}/tcp/test_tcp.c ${LWIP_TESTDIR}/udp/test_udp.c + ${LWIP_TESTDIR}/ppp/test_pppos.c )
diff --git a/test/unit/Filelists.mk b/test/unit/Filelists.mk index 4b77078..c33e1b0 100644 --- a/test/unit/Filelists.mk +++ b/test/unit/Filelists.mk
@@ -34,6 +34,7 @@ $(TESTDIR)/api/test_sockets.c \ $(TESTDIR)/arch/sys_arch.c \ $(TESTDIR)/core/test_def.c \ + $(TESTDIR)/core/test_dns.c \ $(TESTDIR)/core/test_mem.c \ $(TESTDIR)/core/test_netif.c \ $(TESTDIR)/core/test_pbuf.c \ @@ -46,6 +47,8 @@ $(TESTDIR)/mqtt/test_mqtt.c \ $(TESTDIR)/tcp/tcp_helper.c \ $(TESTDIR)/tcp/test_tcp_oos.c \ + $(TESTDIR)/tcp/test_tcp_state.c \ $(TESTDIR)/tcp/test_tcp.c \ - $(TESTDIR)/udp/test_udp.c + $(TESTDIR)/udp/test_udp.c \ + $(TESTDIR)/ppp/test_pppos.c
diff --git a/test/unit/Makefile b/test/unit/Makefile new file mode 100644 index 0000000..a27c72c --- /dev/null +++ b/test/unit/Makefile
@@ -0,0 +1,11 @@ +# Shortcuts to building and running tests on unix platforms. +# Output files will be written to the directory listed below. + +all: + cd ../../contrib/ports/unix/check/ && $(MAKE) + +check: + cd ../../contrib/ports/unix/check/ && $(MAKE) check + +clean: + cd ../../contrib/ports/unix/check/ && $(MAKE) clean
diff --git a/test/unit/api/test_sockets.c b/test/unit/api/test_sockets.c index 472fa48..3faa5ff 100644 --- a/test/unit/api/test_sockets.c +++ b/test/unit/api/test_sockets.c
@@ -153,7 +153,7 @@ fail_unless(errno == EISCONN); /* write from server to client */ - ret = write(s3, "test", 4); + ret = lwip_write(s3, "test", 4); fail_unless(ret == 4); ret = lwip_shutdown(s3, SHUT_WR); @@ -250,11 +250,11 @@ static void test_sockets_msgapi_update_iovs(struct msghdr *msg, size_t bytes) { - int i; + msg_iovlen_t i; /* note: this modifies the underyling iov_base and iov_len for a partial read for an individual vector. This updates the msg->msg_iov pointer - to skip fully consumed vecotrs */ + to skip fully consumed vectors */ /* process fully consumed vectors */ for (i = 0; i < msg->msg_iovlen; i++) { @@ -330,7 +330,7 @@ /* set s2 to non-blocking, not inherited from listener */ opt = lwip_fcntl(s2, F_GETFL, 0); - fail_unless(opt == 6); + fail_unless(opt == O_RDWR); opt = O_NONBLOCK; ret = lwip_fcntl(s2, F_SETFL, opt); fail_unless(ret == 0);
diff --git a/test/unit/core/test_dns.c b/test/unit/core/test_dns.c new file mode 100644 index 0000000..6789d24 --- /dev/null +++ b/test/unit/core/test_dns.c
@@ -0,0 +1,52 @@ +#include "test_dns.h" + +#include "lwip/dns.h" + +/* Setups/teardown functions */ + +static void +dns_setup(void) +{ +} + +static void +dns_teardown(void) +{ +} + +/* Test functions */ + +START_TEST(test_dns_set_get_server) +{ + int n; + LWIP_UNUSED_ARG(_i); + + for (n = 0; n < 256; n++) { + u8_t i = (u8_t)n; + ip_addr_t server; + /* Should return a zeroed address for any index */ + fail_unless(dns_getserver(i)); + fail_unless(ip_addr_isany(dns_getserver(i))); + + /* Should accept setting address for any index, and ignore if out of range */ + IP_ADDR4(&server, 10, 0, 0, i); + dns_setserver(i, &server); + fail_unless(dns_getserver(i)); + if (i < DNS_MAX_SERVERS) { + fail_unless(ip_addr_eq(dns_getserver(i), &server) == 1); + } else { + fail_unless(ip_addr_isany(dns_getserver(i))); + } + } +} +END_TEST + +/** Create the suite including all tests for this module */ +Suite * +dns_suite(void) +{ + testfunc tests[] = { + TESTFUNC(test_dns_set_get_server) + }; + return create_suite("DNS", tests, sizeof(tests)/sizeof(testfunc), dns_setup, dns_teardown); +}
diff --git a/test/unit/core/test_dns.h b/test/unit/core/test_dns.h new file mode 100644 index 0000000..eaad0ca --- /dev/null +++ b/test/unit/core/test_dns.h
@@ -0,0 +1,8 @@ +#ifndef LWIP_HDR_TEST_DNS_H +#define LWIP_HDR_TEST_DNS_H + +#include "../lwip_check.h" + +Suite *dns_suite(void); + +#endif
diff --git a/test/unit/core/test_mem.c b/test/unit/core/test_mem.c index 2aeb20c..601bfc7 100644 --- a/test/unit/core/test_mem.c +++ b/test/unit/core/test_mem.c
@@ -6,9 +6,6 @@ #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 */
diff --git a/test/unit/core/test_netif.c b/test/unit/core/test_netif.c index c5fa75d..a51a479 100644 --- a/test/unit/core/test_netif.c +++ b/test/unit/core/test_netif.c
@@ -166,7 +166,8 @@ IP4_ADDR(&netmask, 255, 255, 255, 0); IP4_ADDR(&gw, 1, 2, 3, 254); expected_reasons = (netif_nsc_reason_t)(LWIP_NSC_IPV4_ADDRESS_CHANGED | LWIP_NSC_IPV4_NETMASK_CHANGED | - LWIP_NSC_IPV4_GATEWAY_CHANGED | LWIP_NSC_IPV4_SETTINGS_CHANGED); + LWIP_NSC_IPV4_GATEWAY_CHANGED | LWIP_NSC_IPV4_SETTINGS_CHANGED | + LWIP_NSC_IPV4_ADDR_VALID); callback_ctr = 0; netif_set_addr(&net_test, &addr, &netmask, &gw); fail_unless(callback_ctr == 1); @@ -185,13 +186,16 @@ netif_set_gw(&net_test, &gw); fail_unless(callback_ctr == 0); + /* netif_set_addr() always issues at least LWIP_NSC_IPV4_ADDR_VALID */ + expected_reasons = LWIP_NSC_IPV4_ADDR_VALID; callback_ctr = 0; netif_set_addr(&net_test, &addr, &netmask, &gw); - fail_unless(callback_ctr == 0); + fail_unless(callback_ctr == 1); /* check for single-events */ IP4_ADDR(&addr, 1, 2, 3, 5); - expected_reasons = (netif_nsc_reason_t)(LWIP_NSC_IPV4_ADDRESS_CHANGED | LWIP_NSC_IPV4_SETTINGS_CHANGED); + expected_reasons = (netif_nsc_reason_t)(LWIP_NSC_IPV4_ADDRESS_CHANGED | LWIP_NSC_IPV4_SETTINGS_CHANGED | + LWIP_NSC_IPV4_ADDR_VALID); callback_ctr = 0; netif_set_addr(&net_test, &addr, &netmask, &gw); fail_unless(callback_ctr == 1);
diff --git a/test/unit/core/test_pbuf.c b/test/unit/core/test_pbuf.c index 57087d2..6163e4f 100644 --- a/test/unit/core/test_pbuf.c +++ b/test/unit/core/test_pbuf.c
@@ -6,9 +6,6 @@ #if !LWIP_STATS || !MEM_STATS ||!MEMP_STATS #error "This tests needs MEM- and MEMP-statistics enabled" #endif -#if LWIP_DNS -#error "This test needs DNS turned off (as it mallocs on init)" -#endif #if !LWIP_TCP || !TCP_QUEUE_OOSEQ || !LWIP_WND_SCALE #error "This test needs TCP OOSEQ queueing and window scaling enabled" #endif @@ -77,9 +74,6 @@ err_t err; LWIP_UNUSED_ARG(_i); - fail_unless(lwip_stats.mem.used == 0); - fail_unless(MEMP_STATS_GET(used, MEMP_PBUF_POOL) == 0); - p1 = pbuf_alloc(PBUF_RAW, 1024, PBUF_RAM); fail_unless(p1 != NULL); fail_unless(p1->ref == 1); @@ -99,10 +93,102 @@ pbuf_free(p1); pbuf_free(p3); - fail_unless(lwip_stats.mem.used == 0); +} +END_TEST - fail_unless(lwip_stats.mem.used == 0); - fail_unless(MEMP_STATS_GET(used, MEMP_PBUF_POOL) == 0); +/** Call pbuf_copy on pbufs with chains of different sizes */ +START_TEST(test_pbuf_copy_unmatched_chains) +{ + uint16_t i, j; + err_t err; + struct pbuf *source, *dest, *p; + LWIP_UNUSED_ARG(_i); + + source = NULL; + /* Build source pbuf from linked 16 byte parts, + * with payload bytes containing their offset */ + for (i = 0; i < 8; i++) { + p = pbuf_alloc(PBUF_RAW, 16, PBUF_RAM); + fail_unless(p != NULL); + for (j = 0; j < p->len; j++) { + ((u8_t*)p->payload)[j] = (u8_t)((i << 4) | j); + } + if (source) { + pbuf_cat(source, p); + } else { + source = p; + } + } + for (i = 0; i < source->tot_len; i++) { + fail_unless(pbuf_get_at(source, i) == i); + } + + /* Build dest pbuf from other lengths */ + dest = pbuf_alloc(PBUF_RAW, 35, PBUF_RAM); + fail_unless(dest != NULL); + p = pbuf_alloc(PBUF_RAW, 81, PBUF_RAM); + fail_unless(p != NULL); + pbuf_cat(dest, p); + p = pbuf_alloc(PBUF_RAW, 27, PBUF_RAM); + fail_unless(p != NULL); + pbuf_cat(dest, p); + + /* Copy contents and verify data */ + err = pbuf_copy(dest, source); + fail_unless(err == ERR_OK); + for (i = 0; i < source->tot_len; i++) { + fail_unless(pbuf_get_at(dest, i) == i); + } + + pbuf_free(source); + pbuf_free(dest); +} +END_TEST + +START_TEST(test_pbuf_copy_partial_pbuf) +{ + struct pbuf *a, *b, *dest; + char lwip[] = "lwip "; + char packet[] = "packet"; + err_t err; + LWIP_UNUSED_ARG(_i); + + a = pbuf_alloc(PBUF_RAW, 5, PBUF_REF); + fail_unless(a != NULL); + a->payload = lwip; + b = pbuf_alloc(PBUF_RAW, 7, PBUF_REF); + fail_unless(b != NULL); + b->payload = packet; + pbuf_cat(a, b); + dest = pbuf_alloc(PBUF_RAW, 14, PBUF_RAM); + memset(dest->payload, 0, dest->len); + fail_unless(dest != NULL); + + /* Don't copy if data will not fit */ + err = pbuf_copy_partial_pbuf(dest, a, a->tot_len, 4); + fail_unless(err == ERR_ARG); + /* Don't copy if length is longer than source */ + err = pbuf_copy_partial_pbuf(dest, a, a->tot_len + 1, 0); + fail_unless(err == ERR_ARG); + /* Normal copy */ + err = pbuf_copy_partial_pbuf(dest, a, a->tot_len, 0); + fail_unless(err == ERR_OK); + fail_unless(strcmp("lwip packet", (char*)dest->payload) == 0); + /* Copy at offset */ + err = pbuf_copy_partial_pbuf(dest, a, a->tot_len, 1); + fail_unless(err == ERR_OK); + fail_unless(strcmp("llwip packet", (char*)dest->payload) == 0); + /* Copy at offset with shorter length */ + err = pbuf_copy_partial_pbuf(dest, a, 6, 6); + fail_unless(err == ERR_OK); + fail_unless(strcmp("llwip lwip p", (char*)dest->payload) == 0); + /* Copy with shorter length */ + err = pbuf_copy_partial_pbuf(dest, a, 5, 0); + fail_unless(err == ERR_OK); + fail_unless(strcmp("lwip lwip p", (char*)dest->payload) == 0); + + pbuf_free(dest); + pbuf_free(a); } END_TEST @@ -262,6 +348,8 @@ testfunc tests[] = { TESTFUNC(test_pbuf_alloc_zero_pbufs), TESTFUNC(test_pbuf_copy_zero_pbuf), + TESTFUNC(test_pbuf_copy_unmatched_chains), + TESTFUNC(test_pbuf_copy_partial_pbuf), TESTFUNC(test_pbuf_split_64k_on_small_pbufs), TESTFUNC(test_pbuf_queueing_bigger_than_64k), TESTFUNC(test_pbuf_take_at_edge),
diff --git a/test/unit/dhcp/test_dhcp.c b/test/unit/dhcp/test_dhcp.c index d84900d..ac8a219 100644 --- a/test/unit/dhcp/test_dhcp.c +++ b/test/unit/dhcp/test_dhcp.c
@@ -4,8 +4,20 @@ #include "lwip/dhcp.h" #include "lwip/prot/dhcp.h" #include "lwip/etharp.h" +#include "lwip/inet.h" #include "netif/ethernet.h" +#if LWIP_ACD +#if LWIP_DHCP_DOES_ACD_CHECK +#define DHCP_TEST_NUM_ARP_FRAMES 5 +#else +#define DHCP_TEST_NUM_ARP_FRAMES 0 +#endif +#else +#define DHCP_TEST_NUM_ARP_FRAMES 1 +#endif + + static struct netif net_test; static const u8_t broadcast[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; @@ -132,6 +144,9 @@ static void tick_lwip(void) { tick++; +#if LWIP_DHCP_DOES_ACD_CHECK + acd_tmr(); +#endif if (tick % 5 == 0) { dhcp_fine_tmr(); } @@ -282,7 +297,7 @@ check_pkt(p, 278, magic_cookie, sizeof(magic_cookie)); - /* Check dchp message type, can be at different positions */ + /* Check dhcp message type, can be at different positions */ if (txpacket == 1) { u8_t dhcp_discover_opt[] = { 0x35, 0x01, 0x01 }; check_pkt_fuzzy(p, 282, dhcp_discover_opt, sizeof(dhcp_discover_opt)); @@ -295,9 +310,20 @@ } break; } +#if DHCP_TEST_NUM_ARP_FRAMES > 0 case 3: +#if DHCP_TEST_NUM_ARP_FRAMES > 1 case 4: +#if DHCP_TEST_NUM_ARP_FRAMES > 2 case 5: +#if DHCP_TEST_NUM_ARP_FRAMES > 3 + case 6: +#if DHCP_TEST_NUM_ARP_FRAMES > 4 + case 7: +#endif +#endif +#endif +#endif { const u8_t arpproto[] = { 0x08, 0x06 }; @@ -307,7 +333,8 @@ check_pkt(p, 12, arpproto, sizeof(arpproto)); /* eth level proto: ip */ break; } - default: +#endif + default: fail(); break; } @@ -363,7 +390,7 @@ check_pkt(p, 278, magic_cookie, sizeof(magic_cookie)); - /* Check dchp message type, can be at different positions */ + /* Check dhcp message type, can be at different positions */ if (txpacket == 1) { u8_t dhcp_discover_opt[] = { 0x35, 0x01, 0x01 }; check_pkt_fuzzy(p, 282, dhcp_discover_opt, sizeof(dhcp_discover_opt)); @@ -377,9 +404,20 @@ break; } case 3: +#if DHCP_TEST_NUM_ARP_FRAMES > 0 case 4: +#if DHCP_TEST_NUM_ARP_FRAMES > 1 case 5: +#if DHCP_TEST_NUM_ARP_FRAMES > 2 case 6: +#if DHCP_TEST_NUM_ARP_FRAMES > 3 + case 7: +#if DHCP_TEST_NUM_ARP_FRAMES > 4 + case 8: +#endif +#endif +#endif +#endif { const u8_t arpproto[] = { 0x08, 0x06 }; @@ -389,7 +427,8 @@ check_pkt(p, 12, arpproto, sizeof(arpproto)); /* eth level proto: ip */ break; } - case 7: +#endif + case 4 + DHCP_TEST_NUM_ARP_FRAMES: { const u8_t fake_arp[6] = { 0x12, 0x34, 0x56, 0x78, 0x9a, 0xab }; const u8_t ipproto[] = { 0x08, 0x00 }; @@ -410,7 +449,7 @@ check_pkt(p, 278, magic_cookie, sizeof(magic_cookie)); - /* Check dchp message type, can be at different positions */ + /* Check dhcp message type, can be at different positions */ check_pkt_fuzzy(p, 282, dhcp_request_opt, sizeof(dhcp_request_opt)); break; } @@ -478,10 +517,12 @@ memcpy(&dhcp_ack[46], &xid, 4); /* insert transaction id */ send_pkt(&net_test, dhcp_ack, sizeof(dhcp_ack)); - for (i = 0; i < 20; i++) { + fail_unless(txpacket == 2); + + for (i = 0; i < 200; i++) { tick_lwip(); } - fail_unless(txpacket == 5, "TX %d packets, expected 5", txpacket); /* ARP requests sent */ + fail_unless(txpacket == (2 + DHCP_TEST_NUM_ARP_FRAMES), "TX %d packets, expected %d", txpacket, (2 + DHCP_TEST_NUM_ARP_FRAMES)); /* Interface up */ fail_unless(netif_is_up(&net_test)); @@ -551,7 +592,12 @@ memcpy(&dhcp_ack[46], &xid, 4); /* insert transaction id */ send_pkt(&net_test, dhcp_ack, sizeof(dhcp_ack)); - fail_unless(txpacket == 3); /* ARP request sent */ + fail_unless(txpacket == 2); /* ARP request sent */ + + while (txpacket == 2) { + tick_lwip(); + } + fail_unless(txpacket == 3); tcase = TEST_LWIP_DHCP_NAK; /* Switch testcase */ @@ -766,10 +812,10 @@ memcpy(&relay_ack1[46], &xid, 4); /* insert transaction id */ send_pkt(&net_test, relay_ack1, sizeof(relay_ack1)); - for (i = 0; i < 25; i++) { + for (i = 0; i < 200; i++) { tick_lwip(); } - fail_unless(txpacket == 5, "txpkt should be 5, is %d", txpacket); /* ARP requests sent */ + fail_unless(txpacket == (2 + DHCP_TEST_NUM_ARP_FRAMES), "TX %d packets, expected %d", txpacket, (2 + DHCP_TEST_NUM_ARP_FRAMES)); /* Interface up */ fail_unless(netif_is_up(&net_test)); @@ -782,20 +828,20 @@ fail_if(memcmp(&netmask, &net_test.netmask, sizeof(ip4_addr_t))); fail_if(memcmp(&gw, &net_test.gw, sizeof(ip4_addr_t))); - fail_unless(txpacket == 5, "txpacket = %d", txpacket); + fail_unless(txpacket == (2 + DHCP_TEST_NUM_ARP_FRAMES), "TX %d packets, expected %d", txpacket, (2 + DHCP_TEST_NUM_ARP_FRAMES)); for (i = 0; i < 108000 - 25; i++) { tick_lwip(); } fail_unless(netif_is_up(&net_test)); - fail_unless(txpacket == 6, "txpacket = %d", txpacket); + fail_unless(txpacket == (3 + DHCP_TEST_NUM_ARP_FRAMES), "TX %d packets, expected %d", txpacket, (3 + DHCP_TEST_NUM_ARP_FRAMES)); /* We need to send arp response here.. */ send_pkt(&net_test, arp_resp, sizeof(arp_resp)); - fail_unless(txpacket == 7, "txpacket = %d", txpacket); + fail_unless(txpacket == (4 + DHCP_TEST_NUM_ARP_FRAMES), "TX %d packets, expected %d", txpacket, (4 + DHCP_TEST_NUM_ARP_FRAMES)); fail_unless(netif_is_up(&net_test)); xid = htonl(netif_dhcp_data(&net_test)->xid); /* xid updated */ @@ -806,7 +852,7 @@ tick_lwip(); } - fail_unless(txpacket == 7, "txpacket = %d", txpacket); + fail_unless(txpacket == (4 + DHCP_TEST_NUM_ARP_FRAMES), "TX %d packets, expected %d", txpacket, (5 + DHCP_TEST_NUM_ARP_FRAMES)); tcase = TEST_NONE; dhcp_stop(&net_test);
diff --git a/test/unit/etharp/test_etharp.c b/test/unit/etharp/test_etharp.c index 8eb506c..aeaacb2 100644 --- a/test/unit/etharp/test_etharp.c +++ b/test/unit/etharp/test_etharp.c
@@ -2,6 +2,7 @@ #include "lwip/udp.h" #include "lwip/etharp.h" +#include "lwip/inet.h" #include "netif/ethernet.h" #include "lwip/stats.h" #include "lwip/prot/iana.h"
diff --git a/test/unit/ip4/test_ip4.c b/test/unit/ip4/test_ip4.c index a5a7fd9..af715da 100644 --- a/test/unit/ip4/test_ip4.c +++ b/test/unit/ip4/test_ip4.c
@@ -1,6 +1,8 @@ #include "test_ip4.h" +#include "lwip/icmp.h" #include "lwip/ip4.h" +#include "lwip/etharp.h" #include "lwip/inet_chksum.h" #include "lwip/stats.h" #include "lwip/prot/ip.h" @@ -12,6 +14,61 @@ #error "This tests needs LWIP_IPV4, IP_REASSEMBLY; MIB2- and IPFRAG-statistics enabled" #endif +static struct netif test_netif; +static ip4_addr_t test_ipaddr, test_netmask, test_gw; +static int linkoutput_ctr; +static int linkoutput_byte_ctr; +static u16_t linkoutput_pkt_len; +static u8_t linkoutput_pkt[100]; + +/* reference internal lwip variable in netif.c */ + +static err_t +test_netif_linkoutput(struct netif *netif, struct pbuf *p) +{ + fail_unless(netif == &test_netif); + fail_unless(p != NULL); + linkoutput_ctr++; + linkoutput_byte_ctr += p->tot_len; + /* Copy start of packet into buffer */ + linkoutput_pkt_len = pbuf_copy_partial(p, linkoutput_pkt, sizeof(linkoutput_pkt), 0); + return ERR_OK; +} + +static err_t +test_netif_init(struct netif *netif) +{ + fail_unless(netif != NULL); + netif->linkoutput = test_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 +test_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_add(&test_netif, &test_ipaddr, &test_netmask, &test_gw, + NULL, test_netif_init, NULL); + netif_set_default(&test_netif); + netif_set_up(&test_netif); +} + +static void +test_netif_remove(void) +{ + if (netif_default == &test_netif) { + netif_remove(&test_netif); + } +} + /* Helper functions */ static void create_ip4_input_fragment(u16_t ip_id, u16_t start, u16_t len, int last) @@ -52,6 +109,12 @@ } } +static err_t arpless_output(struct netif *netif, struct pbuf *p, + const ip4_addr_t *ipaddr) { + LWIP_UNUSED_ARG(ipaddr); + return netif->linkoutput(netif, p); +} + /* Setups/teardown functions */ static void @@ -71,10 +134,33 @@ /* poll until all memory is released... */ tcpip_thread_poll_one(); lwip_check_ensure_no_alloc(SKIP_POOL(MEMP_SYS_TIMEOUT)); + test_netif_remove(); + netif_set_up(netif_get_loopif()); } - /* Test functions */ +START_TEST(test_ip4_frag) +{ + struct pbuf *data = pbuf_alloc(PBUF_IP, 8000, PBUF_RAM); + ip_addr_t peer_ip = IPADDR4_INIT_BYTES(192,168,0,5); + err_t err; + LWIP_UNUSED_ARG(_i); + + linkoutput_ctr = 0; + + /* Verify that 8000 byte payload is split into six packets */ + fail_unless(data != NULL); + test_netif_add(); + test_netif.output = arpless_output; + err = ip4_output_if_src(data, &test_ipaddr, ip_2_ip4(&peer_ip), + 16, 0, IP_PROTO_UDP, &test_netif); + fail_unless(err == ERR_OK); + fail_unless(linkoutput_ctr == 6); + fail_unless(linkoutput_byte_ctr == (8000 + (6 * IP_HLEN))); + pbuf_free(data); + test_netif_remove(); +} +END_TEST START_TEST(test_ip4_reass) { @@ -148,13 +234,109 @@ } END_TEST +/* packets to 127.0.0.1 shall not be sent out to netif_default */ +START_TEST(test_127_0_0_1) +{ + ip4_addr_t localhost; + struct pbuf* p; + LWIP_UNUSED_ARG(_i); + + linkoutput_ctr = 0; + + test_netif_add(); + netif_set_down(netif_get_loopif()); + + IP4_ADDR(&localhost, 127, 0, 0, 1); + p = pbuf_alloc(PBUF_IP, 10, PBUF_POOL); + + if(ip4_output(p, netif_ip4_addr(netif_default), &localhost, 0, 0, IP_PROTO_UDP) != ERR_OK) { + pbuf_free(p); + } + fail_unless(linkoutput_ctr == 0); +} +END_TEST + +START_TEST(test_ip4addr_aton) +{ + ip4_addr_t ip_addr; + + LWIP_UNUSED_ARG(_i); + + fail_unless(ip4addr_aton("192.168.0.1", &ip_addr) == 1); + fail_unless(ip4addr_aton("192.168.0.0001", &ip_addr) == 1); + fail_unless(ip4addr_aton("192.168.0.zzz", &ip_addr) == 0); + fail_unless(ip4addr_aton("192.168.1", &ip_addr) == 1); + fail_unless(ip4addr_aton("192.168.0xd3", &ip_addr) == 1); + fail_unless(ip4addr_aton("192.168.0xz5", &ip_addr) == 0); + fail_unless(ip4addr_aton("192.168.095", &ip_addr) == 0); +} +END_TEST + +/* Test for bug #59364 */ +START_TEST(test_ip4_icmp_replylen_short) +{ + /* IP packet to 192.168.0.1 using proto 0x22 and 1 byte payload */ + const u8_t unknown_proto[] = { + 0x45, 0x00, 0x00, 0x15, 0xd4, 0x31, 0x00, 0x00, 0xff, 0x22, + 0x66, 0x41, 0xc0, 0xa8, 0x00, 0x02, 0xc0, 0xa8, 0x00, 0x01, + 0xaa }; + struct pbuf *p; + const int icmp_len = IP_HLEN + sizeof(struct icmp_hdr); + LWIP_UNUSED_ARG(_i); + + linkoutput_ctr = 0; + + test_netif_add(); + test_netif.output = arpless_output; + p = pbuf_alloc(PBUF_IP, sizeof(unknown_proto), PBUF_RAM); + pbuf_take(p, unknown_proto, sizeof(unknown_proto)); + fail_unless(ip4_input(p, &test_netif) == ERR_OK); + + fail_unless(linkoutput_ctr == 1); + /* Verify outgoing ICMP packet has no extra data */ + fail_unless(linkoutput_pkt_len == icmp_len + sizeof(unknown_proto)); + fail_if(memcmp(&linkoutput_pkt[icmp_len], unknown_proto, sizeof(unknown_proto))); +} +END_TEST + +START_TEST(test_ip4_icmp_replylen_first_8) +{ + /* IP packet to 192.168.0.1 using proto 0x22 and 11 bytes payload */ + const u8_t unknown_proto[] = { + 0x45, 0x00, 0x00, 0x1f, 0xd4, 0x31, 0x00, 0x00, 0xff, 0x22, + 0x66, 0x37, 0xc0, 0xa8, 0x00, 0x02, 0xc0, 0xa8, 0x00, 0x01, + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, + 0xaa }; + struct pbuf *p; + const int icmp_len = IP_HLEN + sizeof(struct icmp_hdr); + const int unreach_len = IP_HLEN + 8; + LWIP_UNUSED_ARG(_i); + + linkoutput_ctr = 0; + + test_netif_add(); + test_netif.output = arpless_output; + p = pbuf_alloc(PBUF_IP, sizeof(unknown_proto), PBUF_RAM); + pbuf_take(p, unknown_proto, sizeof(unknown_proto)); + fail_unless(ip4_input(p, &test_netif) == ERR_OK); + + fail_unless(linkoutput_ctr == 1); + fail_unless(linkoutput_pkt_len == icmp_len + unreach_len); + fail_if(memcmp(&linkoutput_pkt[icmp_len], unknown_proto, unreach_len)); +} +END_TEST /** Create the suite including all tests for this module */ Suite * ip4_suite(void) { testfunc tests[] = { + TESTFUNC(test_ip4_frag), TESTFUNC(test_ip4_reass), + TESTFUNC(test_127_0_0_1), + TESTFUNC(test_ip4addr_aton), + TESTFUNC(test_ip4_icmp_replylen_short), + TESTFUNC(test_ip4_icmp_replylen_first_8), }; return create_suite("IPv4", tests, sizeof(tests)/sizeof(testfunc), ip4_setup, ip4_teardown); }
diff --git a/test/unit/ip6/test_ip6.c b/test/unit/ip6/test_ip6.c index 7303741..43ffcf7 100644 --- a/test/unit/ip6/test_ip6.c +++ b/test/unit/ip6/test_ip6.c
@@ -2,6 +2,7 @@ #include "lwip/ethip6.h" #include "lwip/ip6.h" +#include "lwip/icmp6.h" #include "lwip/inet_chksum.h" #include "lwip/nd6.h" #include "lwip/stats.h" @@ -15,6 +16,7 @@ static struct netif test_netif6; static int linkoutput_ctr; +static int linkoutput_byte_ctr; static err_t default_netif_linkoutput(struct netif *netif, struct pbuf *p) @@ -22,6 +24,7 @@ fail_unless(netif == &test_netif6); fail_unless(p != NULL); linkoutput_ctr++; + linkoutput_byte_ctr += p->tot_len; return ERR_OK; } @@ -132,6 +135,8 @@ { LWIP_UNUSED_ARG(_i); + linkoutput_ctr = 0; + /* test without link-local address */ test_ip6_ll_addr_iter(0, 0); @@ -287,6 +292,73 @@ } END_TEST +static struct pbuf *cloned_pbuf = NULL; +static err_t clone_output(struct netif *netif, struct pbuf *p, const ip6_addr_t *addr) { + LWIP_UNUSED_ARG(netif); + LWIP_UNUSED_ARG(addr); + cloned_pbuf = pbuf_clone(PBUF_RAW, PBUF_RAM, p); + return ERR_OK; +} + +/* Reproduces bug #58553 */ +START_TEST(test_ip6_dest_unreachable_chained_pbuf) +{ + + ip_addr_t my_addr = IPADDR6_INIT_HOST(0x20010db8, 0x0, 0x0, 0x1); + ip_addr_t peer_addr = IPADDR6_INIT_HOST(0x20010db8, 0x0, 0x0, 0x4); + /* Create chained pbuf with UDP data that will get destination unreachable */ + u8_t udp_hdr[] = { + 0x60, 0x00, 0x27, 0x03, 0x00, 0x2d, 0x11, 0x40, + 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, + 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0xff, 0x03, 0xff, 0x00, 0x2d, 0x00, 0x00, + }; + struct pbuf *header = pbuf_alloc(PBUF_RAW, sizeof(udp_hdr), PBUF_ROM); + u8_t udp_payload[] = "abcdefghijklmnopqrstuvwxyz0123456789"; + struct pbuf *data = pbuf_alloc(PBUF_RAW, sizeof(udp_payload), PBUF_ROM); + u8_t *icmpptr; + struct ip6_hdr *outhdr; + struct icmp6_hdr *icmp6hdr; + LWIP_UNUSED_ARG(_i); + + fail_unless(header); + header->payload = udp_hdr; + fail_unless(data); + data->payload = udp_payload; + pbuf_cat(header, data); + data = NULL; + + /* Configure and enable local address */ + netif_set_up(&test_netif6); + netif_ip6_addr_set(&test_netif6, 0, ip_2_ip6(&my_addr)); + netif_ip6_addr_set_state(&test_netif6, 0, IP6_ADDR_VALID); + test_netif6.output_ip6 = clone_output; + + /* Process packet and send ICMPv6 reply for unreachable UDP port */ + ip6_input(header, &test_netif6); + header = NULL; + + /* Verify ICMP reply packet contents */ + fail_unless(cloned_pbuf); + fail_unless(cloned_pbuf->len == IP6_HLEN + ICMP6_HLEN + sizeof(udp_hdr) + sizeof(udp_payload)); + outhdr = (struct ip6_hdr*) cloned_pbuf->payload; + fail_unless(ip6_addr_packed_eq(ip_2_ip6(&my_addr), &outhdr->src, IP6_NO_ZONE)); + fail_unless(ip6_addr_packed_eq(ip_2_ip6(&peer_addr), &outhdr->dest, IP6_NO_ZONE)); + icmpptr = &((u8_t*)cloned_pbuf->payload)[IP6_HLEN]; + icmp6hdr = (struct icmp6_hdr*) icmpptr; + fail_unless(icmp6hdr->type == ICMP6_TYPE_DUR); + fail_unless(icmp6hdr->code == ICMP6_DUR_PORT); + fail_unless(icmp6hdr->data == lwip_htonl(0)); + icmpptr += ICMP6_HLEN; + fail_unless(memcmp(icmpptr, udp_hdr, sizeof(udp_hdr)) == 0, "mismatch in copied ip6/udp header"); + icmpptr += sizeof(udp_hdr); + fail_unless(memcmp(icmpptr, udp_payload, sizeof(udp_payload)) == 0, "mismatch in copied udp payload"); + pbuf_free(cloned_pbuf); +} +END_TEST + /* Reproduces bug #57374 */ START_TEST(test_ip6_frag_pbuf_len_assert) { @@ -295,6 +367,7 @@ struct pbuf *payload, *hdr; err_t err; int i; + LWIP_UNUSED_ARG(_i); /* Configure and enable local address */ test_netif6.mtu = 1500; @@ -324,6 +397,41 @@ } END_TEST +static err_t direct_output(struct netif *netif, struct pbuf *p, const ip6_addr_t *addr) { + LWIP_UNUSED_ARG(addr); + return netif->linkoutput(netif, p); +} + +START_TEST(test_ip6_frag) +{ + ip_addr_t my_addr = IPADDR6_INIT_HOST(0x20010db8, 0x0, 0x0, 0x1); + ip_addr_t peer_addr = IPADDR6_INIT_HOST(0x20010db8, 0x0, 0x0, 0x4); + struct pbuf *data; + err_t err; + LWIP_UNUSED_ARG(_i); + + /* Configure and enable local address */ + test_netif6.mtu = 1500; + netif_set_up(&test_netif6); + netif_ip6_addr_set(&test_netif6, 0, ip_2_ip6(&my_addr)); + netif_ip6_addr_set_state(&test_netif6, 0, IP6_ADDR_VALID); + test_netif6.output_ip6 = direct_output; + /* Reset counters after multicast traffic */ + linkoutput_ctr = 0; + linkoutput_byte_ctr = 0; + + /* Verify that 8000 byte payload is split into six packets */ + data = pbuf_alloc(PBUF_IP, 8000, PBUF_RAM); + fail_unless(data != NULL); + err = ip6_output_if_src(data, ip_2_ip6(&my_addr), ip_2_ip6(&peer_addr), + 15, 0, IP_PROTO_UDP, &test_netif6); + fail_unless(err == ERR_OK); + fail_unless(linkoutput_ctr == 6); + fail_unless(linkoutput_byte_ctr == (8000 + (6 * (IP6_HLEN + IP6_FRAG_HLEN)))); + pbuf_free(data); +} +END_TEST + /** Create the suite including all tests for this module */ Suite * ip6_suite(void) @@ -334,7 +442,9 @@ TESTFUNC(test_ip6_ntoa_ipv4mapped), TESTFUNC(test_ip6_ntoa), TESTFUNC(test_ip6_lladdr), - TESTFUNC(test_ip6_frag_pbuf_len_assert) + TESTFUNC(test_ip6_dest_unreachable_chained_pbuf), + TESTFUNC(test_ip6_frag_pbuf_len_assert), + TESTFUNC(test_ip6_frag) }; return create_suite("IPv6", tests, sizeof(tests)/sizeof(testfunc), ip6_setup, ip6_teardown); }
diff --git a/test/unit/lwip_unittests.c b/test/unit/lwip_unittests.c index 497a44b..8d47e45 100644 --- a/test/unit/lwip_unittests.c +++ b/test/unit/lwip_unittests.c
@@ -5,7 +5,9 @@ #include "udp/test_udp.h" #include "tcp/test_tcp.h" #include "tcp/test_tcp_oos.h" +#include "tcp/test_tcp_state.h" #include "core/test_def.h" +#include "core/test_dns.h" #include "core/test_mem.h" #include "core/test_netif.h" #include "core/test_pbuf.h" @@ -15,12 +17,20 @@ #include "mdns/test_mdns.h" #include "mqtt/test_mqtt.h" #include "api/test_sockets.h" +#include "ppp/test_pppos.h" #include "lwip/init.h" #if !NO_SYS #include "lwip/tcpip.h" #endif +/* This function is used for LWIP_RAND by some ports... */ +unsigned int +lwip_port_rand(void) +{ + return (unsigned int)rand(); +} + Suite* create_suite(const char* name, testfunc *tests, size_t num_tests, SFun setup, SFun teardown) { size_t i; @@ -43,11 +53,14 @@ unsigned int mask; if (!(skip & SKIP_HEAP)) { - fail_unless(lwip_stats.mem.used == 0); + fail_unless(lwip_stats.mem.used == 0, + "mem heap still has %d bytes allocated", lwip_stats.mem.used); } for (i = 0, mask = 1; i < MEMP_MAX; i++, mask <<= 1) { if (!(skip & mask)) { - fail_unless(lwip_stats.memp[i]->used == 0); + fail_unless(lwip_stats.memp[i]->used == 0, + "memp pool '%s' still has %d entries allocated", + lwip_stats.memp[i]->name, lwip_stats.memp[i]->used); } } } @@ -67,7 +80,9 @@ udp_suite, tcp_suite, tcp_oos_suite, + tcp_state_suite, def_suite, + dns_suite, mem_suite, netif_suite, pbuf_suite, @@ -77,6 +92,9 @@ mdns_suite, mqtt_suite, sockets_suite +#if PPP_SUPPORT && PPPOS_SUPPORT + , pppos_suite +#endif /* PPP_SUPPORT && PPPOS_SUPPORT */ }; size_t num = sizeof(suites)/sizeof(void*); LWIP_ASSERT("No suites defined", num > 0);
diff --git a/test/unit/lwipopts.h b/test/unit/lwipopts.h index e4523fc..0ee09df 100644 --- a/test/unit/lwipopts.h +++ b/test/unit/lwipopts.h
@@ -51,9 +51,13 @@ #define LWIP_HAVE_LOOPIF 1 #define TCPIP_THREAD_TEST -/* Enable DHCP to test it, disable UDP checksum to easier inject packets */ +/* Enable DHCP to test it */ #define LWIP_DHCP 1 +/* Enable DNS, with random source port to avoid alloc in dns_init */ +#define LWIP_DNS 1 +#define LWIP_DNS_SECURE (LWIP_DNS_SECURE_RAND_XID | LWIP_DNS_SECURE_RAND_SRC_PORT) + /* Minimal changes to opt.h required for tcp unit tests: */ #define MEM_SIZE 16000 #define TCP_SND_QUEUELEN 40 @@ -69,6 +73,10 @@ #define LWIP_MDNS_RESPONDER 1 #define LWIP_NUM_NETIF_CLIENT_DATA (LWIP_MDNS_RESPONDER) +/* Enable PPP and PPPOS support for PPPOS test suites */ +#define PPP_SUPPORT 1 +#define PPPOS_SUPPORT 1 + /* Minimal changes to opt.h required for etharp unit tests: */ #define ETHARP_SUPPORT_STATIC_ENTRIES 1
diff --git a/test/unit/mdns/test_mdns.c b/test/unit/mdns/test_mdns.c index 6385163..01434b5 100644 --- a/test/unit/mdns/test_mdns.c +++ b/test/unit/mdns/test_mdns.c
@@ -34,6 +34,7 @@ #include "lwip/pbuf.h" #include "lwip/apps/mdns.h" +#include "lwip/apps/mdns_domain.h" #include "lwip/apps/mdns_priv.h" START_TEST(readname_basic) @@ -829,7 +830,7 @@ offset = 0x20; length = mdns_compress_domain(p, &offset, &domain); - /* Dont compress if jump would be to a jump */ + /* Don't compress if jump would be to a jump */ fail_unless(length == domain.length); offset = 0x10;
diff --git a/test/unit/ppp/test_pppos.c b/test/unit/ppp/test_pppos.c new file mode 100644 index 0000000..2b9aee8 --- /dev/null +++ b/test/unit/ppp/test_pppos.c
@@ -0,0 +1,67 @@ +#include "test_pppos.h" + +#include "lwip/netif.h" +#include "netif/ppp/pppos.h" +#include "netif/ppp/ppp.h" + +#if PPP_SUPPORT && PPPOS_SUPPORT +static struct netif pppos_netif; +static ppp_pcb *ppp; + +static u32_t ppp_output_cb(ppp_pcb *pcb, const void *data, u32_t len, void *ctx) +{ + LWIP_UNUSED_ARG(pcb); + LWIP_UNUSED_ARG(data); + LWIP_UNUSED_ARG(len); + LWIP_UNUSED_ARG(ctx); + + return 0; +} + +static void ppp_link_status_cb(ppp_pcb *pcb, int err_code, void *ctx) +{ + LWIP_UNUSED_ARG(pcb); + LWIP_UNUSED_ARG(err_code); + LWIP_UNUSED_ARG(ctx); +} + +static void pppos_setup(void) +{ + ppp = pppos_create(&pppos_netif, ppp_output_cb, ppp_link_status_cb, NULL); + fail_if(ppp == NULL); + ppp_connect(ppp, 0); +} + +static void pppos_teardown(void) +{ +} + +START_TEST(test_pppos_empty_packet_with_valid_fcs) +{ + u8_t two_breaks[] = { 0x7e, 0, 0, 0x7e }; + u8_t other_packet[] = { 0x7e, 0x7d, 0x20, 0x00, 0x7e }; + /* Set internal states of the underlying pcb */ + pppos_pcb *pppos = (pppos_pcb *)ppp->link_ctx_cb; + + LWIP_UNUSED_ARG(_i); + + pppos->open = 1; /* Pretend the connection is open already */ + pppos->in_accm[0] = 0xf0; /* Make sure 0x0's are not escaped chars */ + + pppos_input(ppp, two_breaks, sizeof(two_breaks)); + pppos_input(ppp, other_packet, sizeof(other_packet)); + +} +END_TEST + +/** Create the suite including all tests for this module */ +Suite * +pppos_suite(void) +{ + testfunc tests[] = { + TESTFUNC(test_pppos_empty_packet_with_valid_fcs) + }; + return create_suite("PPPOS", tests, sizeof(tests)/sizeof(testfunc), pppos_setup, pppos_teardown); +} + +#endif /* PPP_SUPPORT && PPPOS_SUPPORT */
diff --git a/test/unit/ppp/test_pppos.h b/test/unit/ppp/test_pppos.h new file mode 100644 index 0000000..56b3b0c --- /dev/null +++ b/test/unit/ppp/test_pppos.h
@@ -0,0 +1,13 @@ +#ifndef LWIP_HDR_TEST_PPPOS_H +#define LWIP_HDR_TEST_PPPOS_H + +#include "../lwip_check.h" +#include "netif/ppp/ppp.h" + +#if PPP_SUPPORT && PPPOS_SUPPORT + +Suite* pppos_suite(void); + +#endif /* PPP_SUPPORT && PPPOS_SUPPORT */ + +#endif /* LWIP_HDR_TEST_PPPOS_H */
diff --git a/test/unit/tcp/tcp_helper.c b/test/unit/tcp/tcp_helper.c index 8689b71..61d6e56 100644 --- a/test/unit/tcp/tcp_helper.c +++ b/test/unit/tcp/tcp_helper.c
@@ -3,6 +3,7 @@ #include "lwip/priv/tcp_priv.h" #include "lwip/stats.h" #include "lwip/pbuf.h" +#include "lwip/inet.h" #include "lwip/inet_chksum.h" #include "lwip/ip_addr.h" @@ -24,7 +25,11 @@ while(pcb != NULL) { pcb2 = pcb; pcb = pcb->next; - tcp_abort(pcb2); + if (pcb2->state == LISTEN) { + tcp_close(pcb2); + } else { + tcp_abort(pcb2); + } } } @@ -259,6 +264,7 @@ ip_addr_copy_from_ip4(*ip_current_src_addr(), iphdr->src); ip_current_netif() = inp; ip_data.current_ip4_header = iphdr; + ip_data.current_input_netif = inp; /* since adding IPv6, p->payload must point to tcp header, not ip header */ pbuf_header(p, -(s16_t)sizeof(struct ip_hdr));
diff --git a/test/unit/tcp/test_tcp.c b/test/unit/tcp/test_tcp.c index c7f85f6..42324d4 100644 --- a/test/unit/tcp/test_tcp.c +++ b/test/unit/tcp/test_tcp.c
@@ -2,6 +2,7 @@ #include "lwip/priv/tcp_priv.h" #include "lwip/stats.h" +#include "lwip/inet.h" #include "tcp_helper.h" #include "lwip/inet_chksum.h" @@ -664,6 +665,7 @@ check_seqnos(pcb->unsent, 6, seqnos); EXPECT(pcb->unacked == NULL); err = tcp_output(pcb); + EXPECT_RET(err == ERR_OK); EXPECT(txcounters.num_tx_calls == 2); EXPECT(txcounters.num_tx_bytes == 2 * (TCP_MSS + 40U)); memset(&txcounters, 0, sizeof(txcounters)); @@ -752,6 +754,7 @@ check_seqnos(pcb->unsent, 6, seqnos); EXPECT(pcb->unacked == NULL); err = tcp_output(pcb); + EXPECT_RET(err == ERR_OK); EXPECT(txcounters.num_tx_calls == 2); EXPECT(txcounters.num_tx_bytes == 2 * (TCP_MSS + 40U)); memset(&txcounters, 0, sizeof(txcounters)); @@ -1100,6 +1103,7 @@ check_seqnos(pcb->unsent, 5, seqnos); EXPECT(pcb->unacked == NULL); err = tcp_output(pcb); + EXPECT_RET(err == ERR_OK); EXPECT(txcounters.num_tx_calls == 5); EXPECT(txcounters.num_tx_bytes == 5 * (TCP_MSS + 40U)); memset(&txcounters, 0, sizeof(txcounters)); @@ -1215,6 +1219,7 @@ err = tcp_write(pcb, &tx_data[0], 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 == 1 * (TCP_MSS + 40U)); memset(&txcounters, 0, sizeof(txcounters)); @@ -1606,7 +1611,7 @@ #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) */ + /* Remainder segment should match pcb value (which is 0) */ EXPECT(pcb->unsent->oversize_left == pcb->unsent_oversize); #endif /* TCP_OVERSIZE_DBGCHECK */ #endif /* TCP_OVERSIZE */
diff --git a/test/unit/tcp/test_tcp_state.c b/test/unit/tcp/test_tcp_state.c new file mode 100644 index 0000000..afd21fc --- /dev/null +++ b/test/unit/tcp/test_tcp_state.c
@@ -0,0 +1,665 @@ +#include "test_tcp_state.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 + +static struct netif test_netif = {0}; +static struct test_tcp_txcounters test_txcounters = {0}; + +#define SEQNO1 (0xFFFFFF00 - TCP_MSS) +#define ISS 6510 +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(); + } +} + +/* Get TCP flags from packets */ +static u8_t +get_tcp_flags_from_packet(struct pbuf *p, u16_t tcp_hdr_offset) +{ + struct tcp_hdr tcphdr; + u16_t ret; + EXPECT_RETX(p != NULL, 0); + EXPECT_RETX(p->len >= tcp_hdr_offset + sizeof(struct tcp_hdr), 0); + ret = pbuf_copy_partial(p, &tcphdr, sizeof(struct tcp_hdr), tcp_hdr_offset); + EXPECT(ret == sizeof(struct tcp_hdr)); + return TCPH_FLAGS(&tcphdr); +} + +/* Create listening tcp_pcb */ +static struct tcp_pcb_listen * +create_listening_pcb(u16_t local_port, struct test_tcp_counters *counters) +{ + struct tcp_pcb *pcb; + struct tcp_pcb_listen *lpcb=NULL; + err_t err; + u16_t port = local_port?local_port:1234; + + if (counters) { + pcb = test_tcp_new_counters_pcb(counters); + } else { + pcb = tcp_new(); + } + EXPECT(pcb != NULL); + + if (pcb) { + err = tcp_bind(pcb, &test_netif.ip_addr, port); + EXPECT(err == ERR_OK); + lpcb = (struct tcp_pcb_listen *)tcp_listen(pcb); + } + + return lpcb; +} + +/* Setup/teardown functions */ +static struct netif* old_netif_list; +static struct netif* old_netif_default; + +static void +tcp_state_setup(void) +{ + struct tcp_pcb dummy_pcb; /* we need this for tcp_next_iss() only */ + + /* 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; + + old_netif_list = netif_list; + old_netif_default = netif_default; + netif_list = NULL; + netif_default = NULL; + tcp_remove_all(); + test_tcp_init_netif(&test_netif, &test_txcounters, &test_local_ip, &test_netmask); + lwip_check_ensure_no_alloc(SKIP_POOL(MEMP_SYS_TIMEOUT)); +} + +static void +tcp_state_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)); +} + +/* helper functions */ + +static void +test_rst_generation_with_incoming_packet(struct pbuf *p, + struct netif *netif, struct test_tcp_txcounters *tx_counters) +{ + u16_t tcp_flags; + EXPECT_RET(p != NULL); + memset(tx_counters, 0, sizeof(struct test_tcp_txcounters)); + /* pass the segment to tcp_input */ + tx_counters->copy_tx_packets = 1; + test_tcp_input(p, netif); + tx_counters->copy_tx_packets = 0; + /* check if packets are as expected */ + EXPECT(tx_counters->tx_packets != NULL); + if (tx_counters->tx_packets) { + tcp_flags = get_tcp_flags_from_packet(tx_counters->tx_packets, 20); + EXPECT(tcp_flags & TCP_RST); + pbuf_free(tx_counters->tx_packets); + tx_counters->tx_packets = NULL; + } +} + +/* Test functions */ + +/* Call tcp_new() and test memp stats (max number) */ +START_TEST(test_tcp_new_max_num) +{ + struct tcp_pcb* pcb[MEMP_NUM_TCP_PCB + 1]; + int i; + LWIP_UNUSED_ARG(_i); + + fail_unless(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0); + + for(i = 0;i < MEMP_NUM_TCP_PCB; i++) { + pcb[i] = tcp_new(); + fail_unless(MEMP_STATS_GET(used, MEMP_TCP_PCB) == (i + 1)); + } + fail_unless(MEMP_STATS_GET(used, MEMP_TCP_PCB) == MEMP_NUM_TCP_PCB); + /* Trying to remove the oldest pcb in TIME_WAIT,LAST_ACK,CLOSING state when pcb full */ + pcb[MEMP_NUM_TCP_PCB] = tcp_new(); + fail_unless(pcb[MEMP_NUM_TCP_PCB] == NULL); + tcp_set_state(pcb[0], TIME_WAIT, &test_local_ip, &test_remote_ip, TEST_LOCAL_PORT, TEST_REMOTE_PORT); + pcb[MEMP_NUM_TCP_PCB] = tcp_new(); + fail_unless(pcb[MEMP_NUM_TCP_PCB] != NULL); + fail_unless(MEMP_STATS_GET(used, MEMP_TCP_PCB) == MEMP_NUM_TCP_PCB); + + for (i = 1; i <= MEMP_NUM_TCP_PCB; i++) + { + tcp_abort(pcb[i]); + } + fail_unless(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0); +} +END_TEST + + +/* pcbs in TIME_WAIT state will be deleted when creating new pcb reach the max number */ +START_TEST(test_tcp_new_max_num_remove_TIME_WAIT) +{ + struct tcp_pcb* pcb; + struct tcp_pcb* pcb_list[MEMP_NUM_TCP_PCB + 1]; + int i; + LWIP_UNUSED_ARG(_i); + + /* create a pcb in TIME_WAIT state */ + pcb = tcp_new(); + EXPECT_RET(pcb != NULL); + tcp_set_state(pcb, TIME_WAIT, &test_local_ip, &test_remote_ip, TEST_LOCAL_PORT, TEST_REMOTE_PORT); + EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1); + EXPECT_RET(pcb->state == TIME_WAIT); + + /* Create max number pcbs */ + for(i = 0;i < MEMP_NUM_TCP_PCB-1; i++) { + pcb_list[i] = tcp_new(); + EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB) == (i + 2)); + } + EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB) == MEMP_NUM_TCP_PCB); + + /* Create one more pcb, and expect that the pcb in the TIME_WAIT state is deleted */ + pcb_list[MEMP_NUM_TCP_PCB-1] = tcp_new(); + EXPECT_RET(pcb_list[MEMP_NUM_TCP_PCB-1] != NULL); + EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == MEMP_NUM_TCP_PCB); + + for (i = 0; i <= MEMP_NUM_TCP_PCB-1; i++) + { + tcp_abort(pcb_list[i]); + } + EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0); + +} +END_TEST + + +/* Call tcp_connect to check active open */ +START_TEST(test_tcp_connect_active_open) +{ + struct test_tcp_counters counters; + struct tcp_pcb *pcb; + struct pbuf *p; + err_t err; + u16_t test_port = 1234; + u32_t seqno = 0; + LWIP_UNUSED_ARG(_i); + + /* create and initialize the pcb */ + tcp_ticks = SEQNO1 - ISS; + pcb = test_tcp_new_counters_pcb(&counters); + EXPECT_RET(pcb != NULL); + + /* Get seqno from SYN packet */ + test_txcounters.copy_tx_packets = 1; + err = tcp_connect(pcb, &test_remote_ip, test_port, NULL); + test_txcounters.copy_tx_packets = 0; + EXPECT(err == ERR_OK); + EXPECT(pcb->state == SYN_SENT); + EXPECT(test_txcounters.num_tx_calls == 1); + EXPECT_RET(test_txcounters.tx_packets != NULL); + if (test_txcounters.tx_packets != NULL) { + struct tcp_hdr tcphdr; + u16_t ret; + ret = pbuf_copy_partial(test_txcounters.tx_packets, &tcphdr, 20, 20); + EXPECT(ret == 20); + EXPECT(TCPH_FLAGS(&tcphdr) & TCP_SYN); + pbuf_free(test_txcounters.tx_packets); + test_txcounters.tx_packets = NULL; + seqno = lwip_htonl(tcphdr.seqno); + EXPECT(seqno == pcb->lastack); + } + + /* check correct syn packet */ + p = tcp_create_segment(&pcb->remote_ip, &pcb->local_ip, test_port, + pcb->local_port, NULL, 0, 12345, seqno + 1, TCP_SYN|TCP_ACK); + EXPECT_RET(p != NULL); + test_tcp_input(p, &test_netif); + EXPECT_RET(pcb->state == ESTABLISHED); + EXPECT_RET(test_txcounters.num_tx_calls == 2); + + /* 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 + +START_TEST(test_tcp_active_close) +{ + struct tcp_pcb *pcb, *pcbl; + struct test_tcp_counters counters; + struct pbuf *p; + err_t err; + u32_t i; + LWIP_UNUSED_ARG(_i); + + /* create TCP in LISTEN state */ + memset(&counters, 0, sizeof(counters)); + pcb = test_tcp_new_counters_pcb(&counters); + EXPECT_RET(pcb != NULL); + EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1); + err = tcp_bind(pcb, &test_netif.ip_addr, 1234); + EXPECT_RET(err == ERR_OK); + pcbl = tcp_listen(pcb); + EXPECT_RET(pcbl != NULL); + EXPECT_RET(pcbl->state == LISTEN); + EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0); + EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB_LISTEN) == 1); + + memset(&test_txcounters, 0, sizeof(test_txcounters)); + err = tcp_close(pcbl); + EXPECT_RET(err == ERR_OK); + EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0); + EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB_LISTEN) == 0); + EXPECT(test_txcounters.num_tx_calls == 0); + + /* close TCP in SYN_SENT state */ + memset(&counters, 0, sizeof(counters)); + pcb = test_tcp_new_counters_pcb(&counters); + EXPECT_RET(pcb != NULL); + err = tcp_connect(pcb, &test_netif.gw, 1234, NULL); + EXPECT_RET(err == ERR_OK); + EXPECT_RET(pcb->state == SYN_SENT); + EXPECT(test_txcounters.num_tx_calls == 1); + EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1); + + memset(&test_txcounters, 0, sizeof(test_txcounters)); + err = tcp_close(pcb); + EXPECT_RET(err == ERR_OK); + EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0); + EXPECT(test_txcounters.num_tx_calls == 0); + + /* close TCP in ESTABLISHED state */ + memset(&counters, 0, sizeof(counters)); + 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); + EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1); + + memset(&test_txcounters, 0, sizeof(test_txcounters)); + err = tcp_close(pcb); + EXPECT_RET(err == ERR_OK); + EXPECT_RET(pcb->state == FIN_WAIT_1); + EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1); + /* test_tcp_tmr(); */ + EXPECT(test_txcounters.num_tx_calls == 1); + /* create a segment ACK and pass it to tcp_input */ + p = tcp_create_rx_segment(pcb, NULL, 0, 0, 1, TCP_ACK); + EXPECT_RET(p != NULL); + test_tcp_input(p, &test_netif); + EXPECT_RET(pcb->state == FIN_WAIT_2); + /* create a segment FIN and pass it to tcp_input */ + p = tcp_create_rx_segment(pcb, NULL, 0, 0, 0, TCP_FIN); + EXPECT_RET(p != NULL); + test_tcp_input(p, &test_netif); + EXPECT_RET(pcb->state == TIME_WAIT); + EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1); + for (i = 0; i < 2 * TCP_MSL / TCP_TMR_INTERVAL + 1; i++) { + test_tcp_tmr(); + } + EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0); +} +END_TEST + +START_TEST(test_tcp_imultaneous_close) +{ + struct test_tcp_counters counters; + struct tcp_pcb* pcb; + struct pbuf* p; + char data = 0x0f; + err_t err; + u32_t i; + LWIP_UNUSED_ARG(_i); + + /* 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); + err = tcp_close(pcb); + EXPECT_RET(err == ERR_OK); + EXPECT_RET(pcb->state == FIN_WAIT_1); + /* create a FIN segment */ + p = tcp_create_rx_segment(pcb, &data, 0, 0, 0, TCP_FIN); + EXPECT(p != NULL); + if (p != NULL) { + test_tcp_input(p, &test_netif); + } + EXPECT_RET(pcb->state == CLOSING); + /* create an ACK segment */ + p = tcp_create_rx_segment(pcb, &data, 0, 0, 1, TCP_ACK); + EXPECT(p != NULL); + if (p != NULL) { + test_tcp_input(p, &test_netif); + } + EXPECT_RET(pcb->state == TIME_WAIT); + for (i = 0; i < 2 * TCP_MSL / TCP_TMR_INTERVAL + 1; i++) { + test_tcp_tmr(); + } + EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0); +} +END_TEST + + +/* RST was generated when receive any incoming segment in CLOSED state */ +START_TEST(test_tcp_gen_rst_in_CLOSED) +{ + struct pbuf *p; + ip_addr_t src_addr = test_remote_ip; + ip_addr_t dst_addr = test_local_ip; + LWIP_UNUSED_ARG(_i); + + /* Do not create any pcb */ + + /* create a segment */ + p = tcp_create_segment(&src_addr, &dst_addr, TEST_REMOTE_PORT, + TEST_LOCAL_PORT, NULL, 0, 12345, 54321, TCP_ACK); + EXPECT(p != NULL); + test_rst_generation_with_incoming_packet(p, &test_netif, &test_txcounters); + EXPECT(test_txcounters.num_tx_calls == 1); + +} +END_TEST + +/* RST was generated when receive ACK in LISTEN state */ +START_TEST(test_tcp_gen_rst_in_LISTEN) +{ + struct tcp_pcb_listen *lpcb; + struct pbuf *p; + ip_addr_t src_addr = test_remote_ip; + LWIP_UNUSED_ARG(_i); + + /* create a pcb in LISTEN state */ + lpcb = create_listening_pcb(TEST_LOCAL_PORT, NULL); + EXPECT_RET(lpcb != NULL); + + /* create a segment */ + p = tcp_create_segment(&src_addr,&lpcb->local_ip, TEST_REMOTE_PORT, + lpcb->local_port, NULL, 0, 12345, 54321, TCP_ACK); + EXPECT(p != NULL); + test_rst_generation_with_incoming_packet(p, &test_netif, &test_txcounters); + EXPECT(test_txcounters.num_tx_calls == 1); + + /* the PCB still in LISTEN state */ + EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB_LISTEN) == 1); + if (MEMP_STATS_GET(used, MEMP_TCP_PCB_LISTEN) != 0) { + /* can not use tcp_abort() */ + tcp_close((struct tcp_pcb *)lpcb); + } + +} +END_TEST + + +/* RST was generated when receive an SYN in TIME_WAIT state */ +START_TEST(test_tcp_gen_rst_in_TIME_WAIT) +{ + struct tcp_pcb *pcb; + struct pbuf *p; + LWIP_UNUSED_ARG(_i); + + /* create a pcb in LISTEN state */ + pcb = tcp_new(); + EXPECT_RET(pcb != NULL); + tcp_set_state(pcb, TIME_WAIT, &test_local_ip, &test_remote_ip, TEST_LOCAL_PORT, TEST_REMOTE_PORT); + + /* create a segment */ + p = tcp_create_rx_segment(pcb, NULL, 0, 0, 0, TCP_SYN); + EXPECT(p != NULL); + test_rst_generation_with_incoming_packet(p, &test_netif, &test_txcounters); + EXPECT(test_txcounters.num_tx_calls == 1); + + EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1); + EXPECT(pcb->state == TIME_WAIT); +} +END_TEST + +/* receive TCP_RST with different seqno */ +START_TEST(test_tcp_process_rst_seqno) +{ + struct test_tcp_counters counters; + struct tcp_pcb *pcb; + struct pbuf *p; + err_t err; + LWIP_UNUSED_ARG(_i); + + /* create and initialize a pcb in SYN_SENT state */ + memset(&counters, 0, sizeof(counters)); + pcb = test_tcp_new_counters_pcb(&counters); + EXPECT_RET(pcb != NULL); + err = tcp_connect(pcb, &test_remote_ip, TEST_REMOTE_PORT, NULL); + EXPECT_RET(err == ERR_OK); + + /* a RST segment with incorrect seqno will not be accepted */ + p = tcp_create_segment(&pcb->remote_ip, &pcb->local_ip, TEST_REMOTE_PORT, + pcb->local_port, NULL, 0, 12345, pcb->snd_nxt-10, TCP_RST); + EXPECT(p != NULL); + test_tcp_input(p, &test_netif); + EXPECT(counters.err_calls == 0); + + /* a RST segment with correct seqno will be accepted */ + p = tcp_create_segment(&pcb->remote_ip, &pcb->local_ip, TEST_REMOTE_PORT, + pcb->local_port, NULL, 0, 12345, pcb->snd_nxt, TCP_RST); + EXPECT(p != NULL); + test_tcp_input(p, &test_netif); + EXPECT(counters.err_calls == 1); + counters.err_calls = 0; + EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0); + + /* create another pcb in ESTABLISHED state */ + memset(&counters, 0, sizeof(counters)); + 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); + + /* a RST segment with incorrect seqno will not be accepted */ + p = tcp_create_segment(&pcb->remote_ip, &pcb->local_ip, pcb->remote_port, + pcb->local_port, NULL, 0, pcb->rcv_nxt-10, 54321, TCP_RST); + EXPECT(p != NULL); + test_tcp_input(p, &test_netif); + EXPECT(counters.err_calls == 0); + + /* a RST segment with correct seqno will be accepted */ + p = tcp_create_segment(&pcb->remote_ip, &pcb->local_ip, TEST_REMOTE_PORT, + pcb->local_port, NULL, 0, pcb->rcv_nxt, 54321, TCP_RST); + EXPECT(p != NULL); + test_tcp_input(p, &test_netif); + EXPECT(counters.err_calls == 1); + counters.err_calls = 0; + EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0); + +} +END_TEST + +/* RST was generated when receive an SYN+ACK with incorrect ACK number in SYN_SENT state */ +START_TEST(test_tcp_gen_rst_in_SYN_SENT_ackseq) +{ + struct tcp_pcb *pcb; + struct pbuf *p; + u16_t test_port = 1234; + err_t err; + LWIP_UNUSED_ARG(_i); + + /* create and initialize a pcb in listen state */ + pcb = tcp_new(); + EXPECT_RET(pcb != NULL); + err = tcp_connect(pcb, &test_remote_ip, test_port, NULL); + EXPECT_RET(err == ERR_OK); + + /* create a SYN+ACK segment with incorrect seqno */ + p = tcp_create_segment(&pcb->remote_ip, &pcb->local_ip, pcb->remote_port, + pcb->local_port, NULL, 0, 12345, pcb->lastack-10, TCP_SYN|TCP_ACK); + EXPECT(p != NULL); + test_rst_generation_with_incoming_packet(p, &test_netif, &test_txcounters); + + /* LWIP: send RST then re-send SYN immediately */ + EXPECT(test_txcounters.num_tx_calls == 2); + +} +END_TEST + +/* RST was generated when receive an ACK without SYN in SYN_SENT state */ +START_TEST(test_tcp_gen_rst_in_SYN_SENT_non_syn_ack) +{ + struct tcp_pcb *pcb; + struct pbuf *p; + u16_t test_port = 1234; + err_t err; + LWIP_UNUSED_ARG(_i); + + /* create and initialize a pcb in listen state */ + pcb = tcp_new(); + EXPECT_RET(pcb != NULL); + err = tcp_connect(pcb, &test_remote_ip, test_port, NULL); + EXPECT_RET(err == ERR_OK); + + /* create a SYN+ACK segment with incorrect seqno */ + p = tcp_create_segment(&pcb->remote_ip, &pcb->local_ip, pcb->remote_port, + pcb->local_port, NULL, 0, 12345, pcb->lastack, TCP_ACK); + EXPECT(p != NULL); + test_rst_generation_with_incoming_packet(p, &test_netif, &test_txcounters); + + /* LWIP: send RST then re-send SYN immediately */ + EXPECT(test_txcounters.num_tx_calls == 2); + +} +END_TEST + +/* RST was generated when receive an ACK with incorrect seqno in SYN_RCVD state */ +START_TEST(test_tcp_gen_rst_in_SYN_RCVD) +{ + struct tcp_pcb_listen *lpcb; + struct pbuf *p; + u32_t ack_seqno = 0; + ip_addr_t src_addr = test_remote_ip; + LWIP_UNUSED_ARG(_i); + + /* create and initialize a pcb in listen state */ + lpcb = create_listening_pcb(TEST_LOCAL_PORT, NULL); + EXPECT_RET(lpcb != NULL); + + /* LISTEN -> SYN_RCVD */ + p = tcp_create_segment(&src_addr, &lpcb->local_ip, TEST_REMOTE_PORT, + lpcb->local_port, NULL, 0, 1000, 54321, TCP_SYN); + EXPECT(p != NULL); + memset(&test_txcounters, 0, sizeof(struct test_tcp_txcounters)); + test_tcp_input(p, &test_netif); + EXPECT(test_txcounters.num_tx_calls == 1); + EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1); + if (MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1) { + ack_seqno = tcp_active_pcbs[0].lastack; + } + + /* create a ACK segment with incorrect seqno */ + p = tcp_create_segment(&src_addr, &lpcb->local_ip, TEST_REMOTE_PORT, + lpcb->local_port, NULL, 0, 1001, ack_seqno+1111, TCP_ACK); + EXPECT(p != NULL); + test_rst_generation_with_incoming_packet(p, &test_netif, &test_txcounters); + EXPECT(test_txcounters.num_tx_calls == 1); + + /* the active pcb still exists */ + EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1); + EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB_LISTEN) == 1); + if (MEMP_STATS_GET(used, MEMP_TCP_PCB_LISTEN) != 0) { + /* can not use tcp_abort() */ + tcp_close((struct tcp_pcb *)lpcb); + } +} +END_TEST + +/* a listen pcb returns to LISTEN from SYN_RCVD when RST received */ +START_TEST(test_tcp_receive_rst_SYN_RCVD_to_LISTEN) +{ + struct tcp_pcb_listen *lpcb; + struct pbuf *p; + u16_t tcp_flags; + ip_addr_t src_addr = test_remote_ip; + LWIP_UNUSED_ARG(_i); + + /* create and initialize a pcb in listen state */ + lpcb = create_listening_pcb(TEST_LOCAL_PORT, NULL); + EXPECT_RET(lpcb != NULL); + + /* create a SYN segment */ + p = tcp_create_segment(&src_addr, &lpcb->local_ip, TEST_REMOTE_PORT, + lpcb->local_port, NULL, 0, 1000, 54321, TCP_SYN); + EXPECT(p != NULL); + /* pass the segment to tcp_input */ + memset(&test_txcounters, 0, sizeof(struct test_tcp_txcounters)); + test_txcounters.copy_tx_packets = 1; + test_tcp_input(p, &test_netif); + test_txcounters.copy_tx_packets = 0; + /* check if packets are as expected */ + EXPECT(test_txcounters.num_tx_calls == 1); + tcp_flags = get_tcp_flags_from_packet(test_txcounters.tx_packets, 20); + pbuf_free(test_txcounters.tx_packets); + test_txcounters.tx_packets = NULL; + EXPECT((tcp_flags & TCP_SYN) && (tcp_flags & TCP_ACK)); + EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1); + + /* create a RST segment */ + p = tcp_create_segment(&src_addr, &lpcb->local_ip, TEST_REMOTE_PORT, + lpcb->local_port, NULL, 0, 1001, 54321, TCP_RST); + EXPECT(p != NULL); + test_tcp_input(p, &test_netif); + EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0); + EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB_LISTEN) == 1); + + if (MEMP_STATS_GET(used, MEMP_TCP_PCB_LISTEN) != 0) { + /* can not use tcp_abort() */ + tcp_close((struct tcp_pcb *)lpcb); + } +} +END_TEST + +/** Create the suite including all tests for this module */ +Suite * +tcp_state_suite(void) +{ + testfunc tests[] = { + TESTFUNC(test_tcp_new_max_num), + TESTFUNC(test_tcp_new_max_num_remove_TIME_WAIT), + TESTFUNC(test_tcp_connect_active_open), + TESTFUNC(test_tcp_active_close), + TESTFUNC(test_tcp_imultaneous_close), + TESTFUNC(test_tcp_gen_rst_in_CLOSED), + TESTFUNC(test_tcp_gen_rst_in_LISTEN), + TESTFUNC(test_tcp_gen_rst_in_TIME_WAIT), + TESTFUNC(test_tcp_process_rst_seqno), + TESTFUNC(test_tcp_gen_rst_in_SYN_SENT_ackseq), + TESTFUNC(test_tcp_gen_rst_in_SYN_SENT_non_syn_ack), + TESTFUNC(test_tcp_gen_rst_in_SYN_RCVD), + TESTFUNC(test_tcp_receive_rst_SYN_RCVD_to_LISTEN), + }; + return create_suite("TCP_STATE", tests, sizeof(tests) / sizeof(testfunc), tcp_state_setup, tcp_state_teardown); +}
diff --git a/test/unit/tcp/test_tcp_state.h b/test/unit/tcp/test_tcp_state.h new file mode 100644 index 0000000..00d5c8a --- /dev/null +++ b/test/unit/tcp/test_tcp_state.h
@@ -0,0 +1,8 @@ +#ifndef LWIP_HDR_TEST_TCP_STATE_H +#define LWIP_HDR_TEST_TCP_STATE_H + +#include "../lwip_check.h" + +Suite *tcp_state_suite(void); + +#endif