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