Release v2.1.3_20230818
diff --git a/License.md b/License.md
index d91bddb..c0c9afc 100644
--- a/License.md
+++ b/License.md
@@ -1,3 +1,27 @@
-# Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+# Portions COPYRIGHT 2016 STMicroelectronics
+# Copyright (c) 2001-2004 Swedish Institute of Computer Science, All rights reserved.
 
-This software component is licensed under the **BSD-3-Clause** license. You may not use this file except in compliance with this license. You may obtain a copy of the license [here](https://opensource.org/licenses/BSD-3-Clause).
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+this list of conditions and the following disclaimer in the documentation and/or
+other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its contributors
+may be used to endorse or promote products derived from this software without
+specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file
diff --git a/doc/doxygen/generate.sh b/doc/doxygen/generate.sh
deleted file mode 100644
index 89344b0..0000000
--- a/doc/doxygen/generate.sh
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/sh
-
-doxygen lwip.Doxyfile
diff --git a/doc/doxygen/lwip.Doxyfile b/doc/doxygen/lwip.Doxyfile
index c2039e5..3477722 100644
--- a/doc/doxygen/lwip.Doxyfile
+++ b/doc/doxygen/lwip.Doxyfile
@@ -38,7 +38,7 @@
 # could be handy for archiving the generated documentation or if some version
 # control system is used.
 
-PROJECT_NUMBER         = "2.1.2"
+PROJECT_NUMBER         = "2.1.3"
 
 # Using the PROJECT_BRIEF tag one can provide an optional one line description
 # for a project that appears at the top of each page and should give viewer a
diff --git a/doc/doxygen/main_page.h b/doc/doxygen/main_page.h
index 89d86f8..06b7937 100644
--- a/doc/doxygen/main_page.h
+++ b/doc/doxygen/main_page.h
@@ -205,6 +205,15 @@
  */
 
 /**
+ * @page mem_err Debugging memory pool sizes
+ * If you have issues with lwIP and you are using memory pools, check that your pools
+ * are correctly sized.\n
+ * To debug pool sizes, \#define LWIP_STATS and MEMP_STATS to 1. Check the global variable
+ * lwip_stats.memp[] using a debugger. If the "err" member of a pool is > 0, the pool
+ * may be too small for your application and you need to increase its size.
+ */
+
+/**
  * @page bugs Reporting bugs
  * Please report bugs in the lwIP bug tracker at savannah.\n
  * BEFORE submitting, please check if the bug has already been reported!\n
diff --git a/src/Filelists.cmake b/src/Filelists.cmake
index c765628..21d7b49 100644
--- a/src/Filelists.cmake
+++ b/src/Filelists.cmake
@@ -10,7 +10,7 @@
 
 set(LWIP_VERSION_MAJOR    "2")
 set(LWIP_VERSION_MINOR    "1")
-set(LWIP_VERSION_REVISION "2")
+set(LWIP_VERSION_REVISION "3")
 # LWIP_VERSION_RC is set to LWIP_RC_RELEASE for official releases
 # LWIP_VERSION_RC is set to LWIP_RC_DEVELOPMENT for Git versions
 # Numbers 1..31 are reserved for release candidates
diff --git a/src/api/api_lib.c b/src/api/api_lib.c
index e03b8b7..ffa14d6 100644
--- a/src/api/api_lib.c
+++ b/src/api/api_lib.c
@@ -201,16 +201,16 @@
 
   API_MSG_VAR_ALLOC(msg);
   API_MSG_VAR_REF(msg).conn = conn;
+#if LWIP_TCP
 #if LWIP_SO_SNDTIMEO || LWIP_SO_LINGER
   /* get the time we started, which is later compared to
      sys_now() + conn->send_timeout */
   API_MSG_VAR_REF(msg).msg.sd.time_started = sys_now();
 #else /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
-#if LWIP_TCP
   API_MSG_VAR_REF(msg).msg.sd.polls_left =
     ((LWIP_TCP_CLOSE_TIMEOUT_MS_DEFAULT + TCP_SLOW_INTERVAL - 1) / TCP_SLOW_INTERVAL) + 1;
-#endif /* LWIP_TCP */
 #endif /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
+#endif /* LWIP_TCP */
   err = netconn_apimsg(lwip_netconn_do_delconn, &API_MSG_VAR_REF(msg));
   API_MSG_VAR_FREE(msg);
 
@@ -500,7 +500,7 @@
 
   NETCONN_MBOX_WAITING_INC(conn);
   if (netconn_is_nonblocking(conn)) {
-    if (sys_arch_mbox_tryfetch(&conn->acceptmbox, &accept_ptr) == SYS_ARCH_TIMEOUT) {
+    if (sys_arch_mbox_tryfetch(&conn->acceptmbox, &accept_ptr) == SYS_MBOX_EMPTY) {
       API_MSG_VAR_FREE_ACCEPT(msg);
       NETCONN_MBOX_WAITING_DEC(conn);
       return ERR_WOULDBLOCK;
@@ -597,7 +597,7 @@
   NETCONN_MBOX_WAITING_INC(conn);
   if (netconn_is_nonblocking(conn) || (apiflags & NETCONN_DONTBLOCK) ||
       (conn->flags & NETCONN_FLAG_MBOXCLOSED) || (conn->pending_err != ERR_OK)) {
-    if (sys_arch_mbox_tryfetch(&conn->recvmbox, &buf) == SYS_ARCH_TIMEOUT) {
+    if (sys_arch_mbox_tryfetch(&conn->recvmbox, &buf) == SYS_MBOX_EMPTY) {
       err_t err;
       NETCONN_MBOX_WAITING_DEC(conn);
       err = netconn_err(conn);
@@ -1346,7 +1346,7 @@
 netconn_thread_init(void)
 {
   sys_sem_t *sem = LWIP_NETCONN_THREAD_SEM_GET();
-  if ((sem == NULL) || !sys_sem_valid(sem)) {
+  if (!sys_sem_valid(sem)) {
     /* call alloc only once */
     LWIP_NETCONN_THREAD_SEM_ALLOC();
     LWIP_ASSERT("LWIP_NETCONN_THREAD_SEM_ALLOC() failed", sys_sem_valid(LWIP_NETCONN_THREAD_SEM_GET()));
@@ -1357,7 +1357,7 @@
 netconn_thread_cleanup(void)
 {
   sys_sem_t *sem = LWIP_NETCONN_THREAD_SEM_GET();
-  if ((sem != NULL) && sys_sem_valid(sem)) {
+  if (sys_sem_valid(sem)) {
     /* call free only once */
     LWIP_NETCONN_THREAD_SEM_FREE();
   }
diff --git a/src/api/api_msg.c b/src/api/api_msg.c
index 3953102..3f08e03 100644
--- a/src/api/api_msg.c
+++ b/src/api/api_msg.c
@@ -716,6 +716,9 @@
   conn->pending_err = ERR_OK;
   conn->type = t;
   conn->pcb.tcp = NULL;
+#if LWIP_NETCONN_FULLDUPLEX
+  conn->mbox_threads_waiting = 0;
+#endif
 
   /* If all sizes are the same, every compiler should optimize this switch to nothing */
   switch (NETCONNTYPE_GROUP(t)) {
diff --git a/src/api/netdb.c b/src/api/netdb.c
index 8771425..ee78297 100644
--- a/src/api/netdb.c
+++ b/src/api/netdb.c
@@ -128,8 +128,7 @@
   if (s_hostent.h_addr_list != NULL) {
     u8_t idx;
     for (idx = 0; s_hostent.h_addr_list[idx]; idx++) {
-      LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addr_list[%i]   == %p\n", idx, s_hostent.h_addr_list[idx]));
-      LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addr_list[%i]-> == %s\n", idx, ipaddr_ntoa((ip_addr_t *)s_hostent.h_addr_list[idx])));
+      LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addr_list[%i]-> == %s\n", idx, ipaddr_ntoa(s_phostent_addr[idx])));
     }
   }
 #endif /* DNS_DEBUG */
@@ -306,7 +305,11 @@
     /* service name specified: convert to port number
      * @todo?: currently, only ASCII integers (port numbers) are supported (AI_NUMERICSERV)! */
     port_nr = atoi(servname);
-    if ((port_nr <= 0) || (port_nr > 0xffff)) {
+    if (port_nr == 0 && (servname[0] != '0')) {
+      /* atoi failed - service was not numeric */
+      return EAI_SERVICE;
+    }
+    if ((port_nr < 0) || (port_nr > 0xffff)) {
       return EAI_SERVICE;
     }
   }
diff --git a/src/api/sockets.c b/src/api/sockets.c
index cb7df91..7852635 100644
--- a/src/api/sockets.c
+++ b/src/api/sockets.c
@@ -688,7 +688,6 @@
     err = netconn_peer(newconn, &naddr, &port);
     if (err != ERR_OK) {
       LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d): netconn_peer failed, err=%d\n", s, err));
-      netconn_delete(newconn);
       free_socket(nsock, 1);
       sock_set_errno(sock, err_to_errno(err));
       done_socket(sock);
@@ -2073,7 +2072,9 @@
         /* Call lwip_selscan again: there could have been events between
            the last scan (without us on the list) and putting us on the list! */
         nready = lwip_selscan(maxfdp1, readset, writeset, exceptset, &lreadset, &lwriteset, &lexceptset);
-        if (!nready) {
+        if (nready < 0) {
+          set_errno(EBADF);
+        } else if (!nready) {
           /* Still none ready, just wait to be woken */
           if (timeout == 0) {
             /* Wait forever */
@@ -2102,7 +2103,8 @@
             (exceptset && FD_ISSET(i, exceptset))) {
           struct lwip_sock *sock;
           SYS_ARCH_PROTECT(lev);
-          sock = tryget_socket_unconn_locked(i);
+          sock = tryget_socket_unconn_nouse(i);
+          LWIP_ASSERT("socket gone at the end of select", sock != NULL);
           if (sock != NULL) {
             /* for now, handle select_waiting==0... */
             LWIP_ASSERT("sock->select_waiting > 0", sock->select_waiting > 0);
@@ -2110,7 +2112,6 @@
               sock->select_waiting--;
             }
             SYS_ARCH_UNPROTECT(lev);
-            done_socket(sock);
           } else {
             SYS_ARCH_UNPROTECT(lev);
             /* Not a valid socket */
@@ -2147,6 +2148,11 @@
         /* See what's set now after waiting */
         nready = lwip_selscan(maxfdp1, readset, writeset, exceptset, &lreadset, &lwriteset, &lexceptset);
         LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: nready=%d\n", nready));
+        if (nready < 0) {
+          set_errno(EBADF);
+          lwip_select_dec_sockets_used(maxfdp1, &used_sockets);
+          return -1;
+        }
       }
     }
   }
diff --git a/src/api/tcpip.c b/src/api/tcpip.c
index 743553a..a7e312a 100644
--- a/src/api/tcpip.c
+++ b/src/api/tcpip.c
@@ -217,7 +217,7 @@
   int ret = 0;
   struct tcpip_msg *msg;
 
-  if (sys_arch_mbox_tryfetch(&tcpip_mbox, (void **)&msg) != SYS_ARCH_TIMEOUT) {
+  if (sys_arch_mbox_tryfetch(&tcpip_mbox, (void **)&msg) != SYS_MBOX_EMPTY) {
     LOCK_TCPIP_CORE();
     if (msg != NULL) {
       tcpip_thread_handle_msg(msg);
diff --git a/src/apps/altcp_tls/altcp_tls_mbedtls.c b/src/apps/altcp_tls/altcp_tls_mbedtls.c
index d642dec..bb8e623 100644
--- a/src/apps/altcp_tls/altcp_tls_mbedtls.c
+++ b/src/apps/altcp_tls/altcp_tls_mbedtls.c
@@ -40,10 +40,8 @@
  *   track of the ratio of application data and TLS overhead would be too much.
  *
  * Mandatory security-related configuration:
- * - define ALTCP_MBEDTLS_RNG_FN to mbedtls_entropy_func to use the standard mbedTLS
- *   entropy and ensure to add at least one strong entropy source to your mbedtls port
- *   (implement mbedtls_platform_entropy_poll or mbedtls_hardware_poll providing strong
- *   entropy)
+ * - ensure to add at least one strong entropy source to your mbedtls port (implement
+ *   mbedtls_platform_entropy_poll or mbedtls_hardware_poll providing strong entropy)
  * - define ALTCP_MBEDTLS_ENTROPY_PTR and ALTCP_MBEDTLS_ENTROPY_LEN to something providing
  *   GOOD custom entropy
  *
@@ -78,6 +76,7 @@
 #include "mbedtls/platform.h"
 #include "mbedtls/memory_buffer_alloc.h"
 #include "mbedtls/ssl_cache.h"
+#include "mbedtls/ssl_ticket.h"
 
 #include "mbedtls/ssl_internal.h" /* to call mbedtls_flush_output after ERR_MEM */
 
@@ -97,17 +96,30 @@
 /** Our global mbedTLS configuration (server-specific, not connection-specific) */
 struct altcp_tls_config {
   mbedtls_ssl_config conf;
-  mbedtls_entropy_context entropy;
-  mbedtls_ctr_drbg_context ctr_drbg;
   mbedtls_x509_crt *cert;
   mbedtls_pk_context *pkey;
+  uint8_t cert_count;
+  uint8_t cert_max;
+  uint8_t pkey_count;
+  uint8_t pkey_max;
   mbedtls_x509_crt *ca;
-#if defined(MBEDTLS_SSL_CACHE_C) && ALTCP_MBEDTLS_SESSION_CACHE_TIMEOUT_SECONDS
+#if defined(MBEDTLS_SSL_CACHE_C) && ALTCP_MBEDTLS_USE_SESSION_CACHE
   /** Inter-connection cache for fast connection startup */
   struct mbedtls_ssl_cache_context cache;
 #endif
+#if defined(MBEDTLS_SSL_SESSION_TICKETS) && ALTCP_MBEDTLS_USE_SESSION_TICKETS
+  mbedtls_ssl_ticket_context ticket_ctx;
+#endif
 };
 
+/** Entropy and random generator are shared by all mbedTLS configuration */
+struct altcp_tls_entropy_rng {
+  mbedtls_entropy_context entropy;
+  mbedtls_ctr_drbg_context ctr_drbg;
+  int ref;
+};
+static struct altcp_tls_entropy_rng *altcp_tls_entropy_rng;
+
 static err_t altcp_mbedtls_lower_recv(void *arg, struct altcp_pcb *inner_conn, struct pbuf *p, err_t err);
 static err_t altcp_mbedtls_setup(void *conf, struct altcp_pcb *conn, struct altcp_pcb *inner_conn);
 static err_t altcp_mbedtls_lower_recv_process(struct altcp_pcb *conn, altcp_mbedtls_state_t *state);
@@ -449,6 +461,7 @@
     return MBEDTLS_ERR_NET_INVALID_CONTEXT;
   }
   state = (altcp_mbedtls_state_t *)conn->state;
+  LWIP_ASSERT("state != NULL", state != NULL);
   p = state->rx;
 
   /* @todo: return MBEDTLS_ERR_NET_CONN_RESET/MBEDTLS_ERR_NET_RECV_FAILED? */
@@ -636,41 +649,26 @@
   return NULL;
 }
 
-#if ALTCP_MBEDTLS_DEBUG != LWIP_DBG_OFF
+#if ALTCP_MBEDTLS_LIB_DEBUG != LWIP_DBG_OFF
 static void
 altcp_mbedtls_debug(void *ctx, int level, const char *file, int line, const char *str)
 {
   LWIP_UNUSED_ARG(ctx);
-  LWIP_UNUSED_ARG(level);
   LWIP_UNUSED_ARG(file);
   LWIP_UNUSED_ARG(line);
   LWIP_UNUSED_ARG(str);
 
-  LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("%s:%04d: %s", file, line, str));
+  if (level >= ALTCP_MBEDTLS_LIB_DEBUG_LEVEL_MIN) {
+    LWIP_DEBUGF(ALTCP_MBEDTLS_LIB_DEBUG, ("%s:%04d: %s", file, line, str));
+  }
 }
 #endif
 
-#ifndef ALTCP_MBEDTLS_RNG_FN
-/** ATTENTION: It is *really* important to *NOT* use this dummy RNG in production code!!!! */
-static int
-dummy_rng(void *ctx, unsigned char *buffer, size_t len)
-{
-  static size_t ctr;
-  size_t i;
-  LWIP_UNUSED_ARG(ctx);
-  for (i = 0; i < len; i++) {
-    buffer[i] = (unsigned char)++ctr;
-  }
-  return 0;
-}
-#define ALTCP_MBEDTLS_RNG_FN dummy_rng
-#endif /* ALTCP_MBEDTLS_RNG_FN */
-
 /** Create new TLS configuration
  * ATTENTION: Server certificate and private key have to be added outside this function!
  */
 static struct altcp_tls_config *
-altcp_tls_create_config(int is_server, int have_cert, int have_pkey, int have_ca)
+altcp_tls_create_config(int is_server, uint8_t cert_count, uint8_t pkey_count, int have_ca)
 {
   size_t sz;
   int ret;
@@ -679,49 +677,68 @@
 
   if (TCP_WND < MBEDTLS_SSL_MAX_CONTENT_LEN) {
     LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG|LWIP_DBG_LEVEL_SERIOUS,
-      ("altcp_tls: TCP_WND is smaller than the RX decryption buffer, connection RX might stall!\n"));
+      ("altcp_tls: TCP_WND is smaller than the RX decrypion buffer, connection RX might stall!\n"));
   }
 
   altcp_mbedtls_mem_init();
 
   sz = sizeof(struct altcp_tls_config);
-  if (have_cert) {
-    sz += sizeof(mbedtls_x509_crt);
+  if (cert_count > 0) {
+    sz += (cert_count * sizeof(mbedtls_x509_crt));
   }
   if (have_ca) {
     sz += sizeof(mbedtls_x509_crt);
   }
-  if (have_pkey) {
-    sz += sizeof(mbedtls_pk_context);
+  if (pkey_count > 0) {
+    sz += (pkey_count * sizeof(mbedtls_pk_context));
   }
 
   conf = (struct altcp_tls_config *)altcp_mbedtls_alloc_config(sz);
   if (conf == NULL) {
     return NULL;
   }
+  conf->cert_max = cert_count;
   mem = (mbedtls_x509_crt *)(conf + 1);
-  if (have_cert) {
+  if (cert_count > 0) {
     conf->cert = mem;
-    mem++;
+    mem += cert_count;
   }
   if (have_ca) {
     conf->ca = mem;
     mem++;
   }
-  if (have_pkey) {
+  conf->pkey_max = pkey_count;
+  if (pkey_count > 0) {
     conf->pkey = (mbedtls_pk_context *)mem;
   }
 
   mbedtls_ssl_config_init(&conf->conf);
-  mbedtls_entropy_init(&conf->entropy);
-  mbedtls_ctr_drbg_init(&conf->ctr_drbg);
 
-  /* Seed the RNG */
-  ret = mbedtls_ctr_drbg_seed(&conf->ctr_drbg, ALTCP_MBEDTLS_RNG_FN, &conf->entropy, ALTCP_MBEDTLS_ENTROPY_PTR, ALTCP_MBEDTLS_ENTROPY_LEN);
-  if (ret != 0) {
-    LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("mbedtls_ctr_drbg_seed failed: %d\n", ret));
-    altcp_mbedtls_free_config(conf);
-    return NULL;
+  if (!altcp_tls_entropy_rng) {
+    altcp_tls_entropy_rng = (struct altcp_tls_entropy_rng *)altcp_mbedtls_alloc_config(sizeof(struct altcp_tls_entropy_rng));
+    if (altcp_tls_entropy_rng) {
+      altcp_tls_entropy_rng->ref = 1;
+      mbedtls_entropy_init(&altcp_tls_entropy_rng->entropy);
+      mbedtls_ctr_drbg_init(&altcp_tls_entropy_rng->ctr_drbg);
+      /* Seed the RNG, only once */
+      ret = mbedtls_ctr_drbg_seed(&altcp_tls_entropy_rng->ctr_drbg,
+                                  mbedtls_entropy_func, &altcp_tls_entropy_rng->entropy,
+                                  ALTCP_MBEDTLS_ENTROPY_PTR, ALTCP_MBEDTLS_ENTROPY_LEN);
+      if (ret != 0) {
+        LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("mbedtls_ctr_drbg_seed failed: %d\n", ret));
+        mbedtls_ctr_drbg_free(&altcp_tls_entropy_rng->ctr_drbg);
+        mbedtls_entropy_free(&altcp_tls_entropy_rng->entropy);
+        altcp_mbedtls_free_config(altcp_tls_entropy_rng);
+        altcp_tls_entropy_rng = NULL;
+        altcp_mbedtls_free_config(conf);
+        return NULL;
+      }
+    } else {
+      altcp_mbedtls_free_config(conf);
+      return NULL;
+    }
+  } else {
+    altcp_tls_entropy_rng->ref++;
   }
 
   /* Setup ssl context (@todo: what's different for a client here? -> might better be done on listen/connect) */
@@ -729,24 +746,105 @@
                                     MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT);
   if (ret != 0) {
     LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("mbedtls_ssl_config_defaults failed: %d\n", ret));
+    if (altcp_tls_entropy_rng->ref == 1) {
+      mbedtls_ctr_drbg_free(&altcp_tls_entropy_rng->ctr_drbg);
+      mbedtls_entropy_free(&altcp_tls_entropy_rng->entropy);
+      altcp_mbedtls_free_config(altcp_tls_entropy_rng);
+      altcp_tls_entropy_rng = NULL;
+    }
     altcp_mbedtls_free_config(conf);
     return NULL;
   }
   mbedtls_ssl_conf_authmode(&conf->conf, MBEDTLS_SSL_VERIFY_OPTIONAL);
 
-  mbedtls_ssl_conf_rng(&conf->conf, mbedtls_ctr_drbg_random, &conf->ctr_drbg);
-#if ALTCP_MBEDTLS_DEBUG != LWIP_DBG_OFF
+  mbedtls_ssl_conf_rng(&conf->conf, mbedtls_ctr_drbg_random, &altcp_tls_entropy_rng->ctr_drbg);
+#if ALTCP_MBEDTLS_LIB_DEBUG != LWIP_DBG_OFF
   mbedtls_ssl_conf_dbg(&conf->conf, altcp_mbedtls_debug, stdout);
 #endif
-#if defined(MBEDTLS_SSL_CACHE_C) && ALTCP_MBEDTLS_SESSION_CACHE_TIMEOUT_SECONDS
+#if defined(MBEDTLS_SSL_CACHE_C) && ALTCP_MBEDTLS_USE_SESSION_CACHE
   mbedtls_ssl_conf_session_cache(&conf->conf, &conf->cache, mbedtls_ssl_cache_get, mbedtls_ssl_cache_set);
-  mbedtls_ssl_cache_set_timeout(&conf->cache, 30);
-  mbedtls_ssl_cache_set_max_entries(&conf->cache, 30);
+  mbedtls_ssl_cache_set_timeout(&conf->cache, ALTCP_MBEDTLS_SESSION_CACHE_TIMEOUT_SECONDS);
+  mbedtls_ssl_cache_set_max_entries(&conf->cache, ALTCP_MBEDTLS_SESSION_CACHE_SIZE);
+#endif
+
+#if defined(MBEDTLS_SSL_SESSION_TICKETS) && ALTCP_MBEDTLS_USE_SESSION_TICKETS
+  mbedtls_ssl_ticket_init(&conf->ticket_ctx);
+
+  ret = mbedtls_ssl_ticket_setup(&conf->ticket_ctx, mbedtls_ctr_drbg_random, &altcp_tls_entropy_rng->ctr_drbg,
+    ALTCP_MBEDTLS_SESSION_TICKET_CIPHER, ALTCP_MBEDTLS_SESSION_TICKET_TIMEOUT_SECONDS);
+  if (ret) {
+    LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("mbedtls_ssl_ticket_setup failed: %d\n", ret));
+    altcp_mbedtls_free_config(conf);
+    return NULL;
+  }
+
+  mbedtls_ssl_conf_session_tickets_cb(&conf->conf, mbedtls_ssl_ticket_write, mbedtls_ssl_ticket_parse,
+    &conf->ticket_ctx);
 #endif
 
   return conf;
 }
 
+struct altcp_tls_config *altcp_tls_create_config_server(uint8_t cert_count)
+{
+  struct altcp_tls_config *conf = altcp_tls_create_config(1, cert_count, cert_count, 0);
+  if (conf == NULL) {
+    return NULL;
+  }
+
+  mbedtls_ssl_conf_ca_chain(&conf->conf, NULL, NULL);
+  return conf;
+}
+
+err_t altcp_tls_config_server_add_privkey_cert(struct altcp_tls_config *config,
+      const u8_t *privkey, size_t privkey_len,
+      const u8_t *privkey_pass, size_t privkey_pass_len,
+      const u8_t *cert, size_t cert_len)
+{
+  int ret;
+  mbedtls_x509_crt *srvcert;
+  mbedtls_pk_context *pkey;
+
+  if (config->cert_count >= config->cert_max) {
+    return ERR_MEM;
+  }
+  if (config->pkey_count >= config->pkey_max) {
+    return ERR_MEM;
+  }
+
+  srvcert = config->cert + config->cert_count;
+  mbedtls_x509_crt_init(srvcert);
+
+  pkey = config->pkey + config->pkey_count;
+  mbedtls_pk_init(pkey);
+
+  /* Load the certificates and private key */
+  ret = mbedtls_x509_crt_parse(srvcert, cert, cert_len);
+  if (ret != 0) {
+    LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("mbedtls_x509_crt_parse failed: %d\n", ret));
+    return ERR_VAL;
+  }
+
+  ret = mbedtls_pk_parse_key(pkey, (const unsigned char *) privkey, privkey_len, privkey_pass, privkey_pass_len);
+  if (ret != 0) {
+    LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("mbedtls_pk_parse_public_key failed: %d\n", ret));
+    mbedtls_x509_crt_free(srvcert);
+    return ERR_VAL;
+  }
+
+  ret = mbedtls_ssl_conf_own_cert(&config->conf, srvcert, pkey);
+  if (ret != 0) {
+    LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("mbedtls_ssl_conf_own_cert failed: %d\n", ret));
+    mbedtls_x509_crt_free(srvcert);
+    mbedtls_pk_free(pkey);
+    return ERR_VAL;
+  }
+
+  config->cert_count++;
+  config->pkey_count++;
+  return ERR_OK;
+}
+
 /** Create new TLS configuration
  * This is a suboptimal version that gets the encrypted private key and its password,
  * as well as the server certificate.
@@ -756,45 +854,17 @@
     const u8_t *privkey_pass, size_t privkey_pass_len,
     const u8_t *cert, size_t cert_len)
 {
-  int ret;
-  mbedtls_x509_crt *srvcert;
-  mbedtls_pk_context *pkey;
-  struct altcp_tls_config *conf = altcp_tls_create_config(1, 1, 1, 0);
+  struct altcp_tls_config *conf = altcp_tls_create_config_server(1);
   if (conf == NULL) {
     return NULL;
   }
 
-  srvcert = conf->cert;
-  mbedtls_x509_crt_init(srvcert);
-
-  pkey = conf->pkey;
-  mbedtls_pk_init(pkey);
-
-  /* Load the certificates and private key */
-  ret = mbedtls_x509_crt_parse(srvcert, cert, cert_len);
-  if (ret != 0) {
-    LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("mbedtls_x509_crt_parse failed: %d\n", ret));
+  if (altcp_tls_config_server_add_privkey_cert(conf, privkey, privkey_len,
+    privkey_pass, privkey_pass_len, cert, cert_len) != ERR_OK) {
     altcp_mbedtls_free_config(conf);
     return NULL;
   }
 
-  ret = mbedtls_pk_parse_key(pkey, (const unsigned char *) privkey, privkey_len, privkey_pass, privkey_pass_len);
-  if (ret != 0) {
-    LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("mbedtls_pk_parse_public_key failed: %d\n", ret));
-    mbedtls_x509_crt_free(srvcert);
-    altcp_mbedtls_free_config(conf);
-    return NULL;
-  }
-
-  mbedtls_ssl_conf_ca_chain(&conf->conf, srvcert->next, NULL);
-  ret = mbedtls_ssl_conf_own_cert(&conf->conf, srvcert, pkey);
-  if (ret != 0) {
-    LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("mbedtls_ssl_conf_own_cert failed: %d\n", ret));
-    mbedtls_x509_crt_free(srvcert);
-    mbedtls_pk_free(pkey);
-    altcp_mbedtls_free_config(conf);
-    return NULL;
-  }
   return conf;
 }
 
@@ -802,7 +872,7 @@
 altcp_tls_create_config_client_common(const u8_t *ca, size_t ca_len, int is_2wayauth)
 {
   int ret;
-  struct altcp_tls_config *conf = altcp_tls_create_config(0, is_2wayauth, is_2wayauth, ca != NULL);
+  struct altcp_tls_config *conf = altcp_tls_create_config(0, (is_2wayauth) ? 1 : 0, (is_2wayauth) ? 1 : 0, ca != NULL);
   if (conf == NULL) {
     return NULL;
   }
@@ -886,8 +956,21 @@
   }
   if (conf->ca) {
     mbedtls_x509_crt_free(conf->ca);
-  }  
+  }
   altcp_mbedtls_free_config(conf);
+  if (altcp_tls_entropy_rng && altcp_tls_entropy_rng->ref)
+      altcp_tls_entropy_rng->ref--;
+}
+
+void
+altcp_tls_free_entropy(void)
+{
+  if (altcp_tls_entropy_rng && altcp_tls_entropy_rng->ref == 0) {
+    mbedtls_ctr_drbg_free(&altcp_tls_entropy_rng->ctr_drbg);
+    mbedtls_entropy_free(&altcp_tls_entropy_rng->entropy);
+    altcp_mbedtls_free_config(altcp_tls_entropy_rng);
+    altcp_tls_entropy_rng = NULL;
+  }
 }
 
 /* "virtual" functions */
@@ -944,6 +1027,11 @@
   }
   lpcb = altcp_listen_with_backlog_and_err(conn->inner_conn, backlog, err);
   if (lpcb != NULL) {
+    altcp_mbedtls_state_t *state = (altcp_mbedtls_state_t *)conn->state;
+    /* Free members of the ssl context (not used on listening pcb). This
+       includes freeing input/output buffers, so saves ~32KByte by default */
+    mbedtls_ssl_free(&state->ssl_context);
+
     conn->inner_conn = lpcb;
     altcp_accept(lpcb, altcp_mbedtls_lower_accept);
     return conn;
@@ -1169,6 +1257,10 @@
   altcp_default_get_tcp_addrinfo,
   altcp_default_get_ip,
   altcp_default_get_port
+#if LWIP_TCP_KEEPALIVE
+  , altcp_default_keepalive_disable
+  , altcp_default_keepalive_enable
+#endif
 #ifdef LWIP_DEBUG
   , altcp_default_dbg_get_tcp_state
 #endif
diff --git a/src/apps/http/httpd.c b/src/apps/http/httpd.c
index ccc9ba7..fc27e8e 100644
--- a/src/apps/http/httpd.c
+++ b/src/apps/http/httpd.c
@@ -1301,6 +1301,22 @@
             ssi->tag_state = TAG_NONE;
           }
 
+#if LWIP_HTTPD_DYNAMIC_FILE_READ && !LWIP_HTTPD_SSI_INCLUDE_TAG
+          if ((ssi->tag_state == TAG_NONE) &&
+              (ssi->parsed - hs->file < ssi->tag_index)) {
+            for(u16_t i = 0;i < ssi->tag_index;i++) {
+              ssi->tag_insert[i] = http_ssi_tag_desc[ssi->tag_type].lead_in[i];
+            }
+            ssi->tag_insert_len = ssi->tag_index;
+            hs->file += ssi->parsed - hs->file;
+            hs->left -= ssi->parsed - hs->file;
+            ssi->tag_end = hs->file;
+            ssi->tag_index = 0;
+            ssi->tag_state = TAG_SENDING;
+            break;
+          }
+#endif
+
           /* Move on to the next character in the buffer */
           ssi->parse_left--;
           ssi->parsed++;
diff --git a/src/apps/mqtt/mqtt.c b/src/apps/mqtt/mqtt.c
index 269f4a4..595da77 100644
--- a/src/apps/mqtt/mqtt.c
+++ b/src/apps/mqtt/mqtt.c
@@ -664,25 +664,24 @@
 /**
  * Complete MQTT message received or buffer full
  * @param client MQTT client
- * @param fixed_hdr_idx header index
+ * @param fixed_hdr_len length of fixed header
  * @param length length received part
  * @param remaining_length Remaining length of complete message
  */
 static mqtt_connection_status_t
-mqtt_message_received(mqtt_client_t *client, u8_t fixed_hdr_idx, u16_t length, u32_t remaining_length)
+mqtt_message_received(mqtt_client_t *client, u8_t fixed_hdr_len, u16_t length, u32_t remaining_length)
 {
   mqtt_connection_status_t res = MQTT_CONNECT_ACCEPTED;
 
-  u8_t *var_hdr_payload = client->rx_buffer + fixed_hdr_idx;
-  size_t var_hdr_payload_bufsize = sizeof(client->rx_buffer) - fixed_hdr_idx;
+  u8_t *var_hdr_payload = client->rx_buffer + fixed_hdr_len;
+  size_t var_hdr_payload_bufsize = sizeof(client->rx_buffer) - fixed_hdr_len;
 
   /* Control packet type */
   u8_t pkt_type = MQTT_CTL_PACKET_TYPE(client->rx_buffer[0]);
   u16_t pkt_id = 0;
 
-  LWIP_ASSERT("client->msg_idx < MQTT_VAR_HEADER_BUFFER_LEN", client->msg_idx < MQTT_VAR_HEADER_BUFFER_LEN);
-  LWIP_ASSERT("fixed_hdr_idx <= client->msg_idx", fixed_hdr_idx <= client->msg_idx);
-  LWIP_ERROR("buffer length mismatch", fixed_hdr_idx + length <= MQTT_VAR_HEADER_BUFFER_LEN,
+  LWIP_ASSERT("fixed_hdr_len <= client->msg_idx", fixed_hdr_len <= client->msg_idx);
+  LWIP_ERROR("buffer length mismatch", fixed_hdr_len + length <= MQTT_VAR_HEADER_BUFFER_LEN,
              return MQTT_CONNECT_DISCONNECTED);
 
   if (pkt_type == MQTT_MSG_TYPE_CONNACK) {
@@ -773,7 +772,9 @@
         LWIP_DEBUGF(MQTT_DEBUG_WARN,( "mqtt_message_received: Received short packet (payload)\n"));
         goto out_disconnect;
       }
-      client->data_cb(client->inpub_arg, var_hdr_payload + payload_offset, payload_length, remaining_length == 0 ? MQTT_DATA_FLAG_LAST : 0);
+      if (client->data_cb != NULL) {
+        client->data_cb(client->inpub_arg, var_hdr_payload + payload_offset, payload_length, remaining_length == 0 ? MQTT_DATA_FLAG_LAST : 0);
+      }
       /* Reply if QoS > 0 */
       if (remaining_length == 0 && qos > 0) {
         /* Send PUBACK for QoS 1 or PUBREC for QoS 2 */
@@ -840,61 +841,59 @@
 {
   u16_t in_offset = 0;
   u32_t msg_rem_len = 0;
-  u8_t fixed_hdr_idx = 0;
+  u8_t fixed_hdr_len = 0;
   u8_t b = 0;
 
   while (p->tot_len > in_offset) {
     /* We ALWAYS parse the header here first. Even if the header was not
        included in this segment, we re-parse it here by buffering it in
        client->rx_buffer. client->msg_idx keeps track of this. */
-    if ((fixed_hdr_idx < 2) || ((b & 0x80) != 0)) {
+    if ((fixed_hdr_len < 2) || ((b & 0x80) != 0)) {
 
-      if (fixed_hdr_idx < client->msg_idx) {
+      if (fixed_hdr_len < client->msg_idx) {
         /* parse header from old pbuf (buffered in client->rx_buffer) */
-        b = client->rx_buffer[fixed_hdr_idx];
+        b = client->rx_buffer[fixed_hdr_len];
       } else {
         /* parse header from this pbuf and save it in client->rx_buffer in case
            it comes in segmented */
         b = pbuf_get_at(p, in_offset++);
         client->rx_buffer[client->msg_idx++] = b;
       }
-      fixed_hdr_idx++;
+      fixed_hdr_len++;
 
-      if (fixed_hdr_idx >= 2) {
+      if (fixed_hdr_len >= 2) {
         /* fixed header contains at least 2 bytes but can contain more, depending on
            'remaining length'. All bytes but the last of this have 0x80 set to
            indicate more bytes are coming. */
-        msg_rem_len |= (u32_t)(b & 0x7f) << ((fixed_hdr_idx - 2) * 7);
+        msg_rem_len |= (u32_t)(b & 0x7f) << ((fixed_hdr_len - 2) * 7);
         if ((b & 0x80) == 0) {
           /* fixed header is done */
           LWIP_DEBUGF(MQTT_DEBUG_TRACE, ("mqtt_parse_incoming: Remaining length after fixed header: %"U32_F"\n", msg_rem_len));
           if (msg_rem_len == 0) {
             /* Complete message with no extra headers of payload received */
-            mqtt_message_received(client, fixed_hdr_idx, 0, 0);
+            mqtt_message_received(client, fixed_hdr_len, 0, 0);
             client->msg_idx = 0;
-            fixed_hdr_idx = 0;
+            fixed_hdr_len = 0;
           } else {
             /* Bytes remaining in message (changes remaining length if this is
                not the first segment of this message) */
-            msg_rem_len = (msg_rem_len + fixed_hdr_idx) - client->msg_idx;
+            msg_rem_len = (msg_rem_len + fixed_hdr_len) - client->msg_idx;
           }
         }
       }
     } else {
       /* Fixed header has been parsed, parse variable header */
-      u16_t cpy_len, cpy_start, buffer_space;
-
-      cpy_start = (client->msg_idx - fixed_hdr_idx) % (MQTT_VAR_HEADER_BUFFER_LEN - fixed_hdr_idx) + fixed_hdr_idx;
+      u16_t cpy_len, buffer_space;
 
       /* Allow to copy the lesser one of available length in input data or bytes remaining in message */
       cpy_len = (u16_t)LWIP_MIN((u16_t)(p->tot_len - in_offset), msg_rem_len);
 
       /* Limit to available space in buffer */
-      buffer_space = MQTT_VAR_HEADER_BUFFER_LEN - cpy_start;
+      buffer_space = MQTT_VAR_HEADER_BUFFER_LEN - fixed_hdr_len;
       if (cpy_len > buffer_space) {
         cpy_len = buffer_space;
       }
-      pbuf_copy_partial(p, client->rx_buffer + cpy_start, cpy_len, in_offset);
+      pbuf_copy_partial(p, client->rx_buffer + fixed_hdr_len, cpy_len, in_offset);
 
       /* Advance get and put indexes  */
       client->msg_idx += cpy_len;
@@ -904,7 +903,7 @@
       LWIP_DEBUGF(MQTT_DEBUG_TRACE, ("mqtt_parse_incoming: msg_idx: %"U32_F", cpy_len: %"U16_F", remaining %"U32_F"\n", client->msg_idx, cpy_len, msg_rem_len));
       if ((msg_rem_len == 0) || (cpy_len == buffer_space)) {
         /* Whole message received or buffer is full */
-        mqtt_connection_status_t res = mqtt_message_received(client, fixed_hdr_idx, (cpy_start + cpy_len) - fixed_hdr_idx, msg_rem_len);
+        mqtt_connection_status_t res = mqtt_message_received(client, fixed_hdr_len, cpy_len, msg_rem_len);
         if (res != MQTT_CONNECT_ACCEPTED) {
           return res;
         }
@@ -912,7 +911,7 @@
           /* Reset parser state */
           client->msg_idx = 0;
           /* msg_tot_len = 0; */
-          fixed_hdr_idx = 0;
+          fixed_hdr_len = 0;
         }
       }
     }
diff --git a/src/apps/netbiosns/netbiosns.c b/src/apps/netbiosns/netbiosns.c
index c94a779..479c375 100644
--- a/src/apps/netbiosns/netbiosns.c
+++ b/src/apps/netbiosns/netbiosns.c
@@ -240,12 +240,12 @@
 
 /** Decode a NetBIOS name (from packet to string) */
 static int
-netbiosns_name_decode(char *name_enc, char *name_dec, int name_dec_len)
+netbiosns_name_decode(const char *name_enc, char *name_dec, int name_dec_len)
 {
-  char *pname;
-  char  cname;
-  char  cnbname;
-  int   idx = 0;
+  const char *pname;
+  char       cname;
+  char       cnbname;
+  int        idx = 0;
 
   LWIP_UNUSED_ARG(name_dec_len);
 
diff --git a/src/apps/sntp/sntp.c b/src/apps/sntp/sntp.c
index b7ff56a..23731e5 100644
--- a/src/apps/sntp/sntp.c
+++ b/src/apps/sntp/sntp.c
@@ -238,9 +238,9 @@
 };
 static struct sntp_server sntp_servers[SNTP_MAX_SERVERS];
 
-#if SNTP_GET_SERVERS_FROM_DHCP
+#if SNTP_GET_SERVERS_FROM_DHCP || SNTP_GET_SERVERS_FROM_DHCPV6
 static u8_t sntp_set_servers_from_dhcp;
-#endif /* SNTP_GET_SERVERS_FROM_DHCP */
+#endif /* SNTP_GET_SERVERS_FROM_DHCP || SNTP_GET_SERVERS_FROM_DHCPV6 */
 #if SNTP_SUPPORT_MULTIPLE_SERVERS
 /** The currently used server (initialized to 0) */
 static u8_t sntp_current_server;
@@ -371,6 +371,7 @@
                                  sntp_retry_timeout));
 
   /* set up a timer to send a retry and increase the retry delay */
+  sys_untimeout(sntp_request, NULL);
   sys_timeout(sntp_retry_timeout, sntp_request, NULL);
 
 #if SNTP_RETRY_TIMEOUT_EXP
@@ -382,6 +383,8 @@
     if ((new_retry_timeout <= SNTP_RETRY_TIMEOUT_MAX) &&
         (new_retry_timeout > sntp_retry_timeout)) {
       sntp_retry_timeout = new_retry_timeout;
+    } else {
+      sntp_retry_timeout = SNTP_RETRY_TIMEOUT_MAX;
     }
   }
 #endif /* SNTP_RETRY_TIMEOUT_EXP */
@@ -558,6 +561,7 @@
     sntp_servers[sntp_current_server].reachability <<= 1;
 #endif /* SNTP_MONITOR_SERVER_REACHABILITY */
     /* set up receive timeout: try next server or retry on timeout */
+    sys_untimeout(sntp_try_next_server, NULL);
     sys_timeout((u32_t)SNTP_RECV_TIMEOUT, sntp_try_next_server, NULL);
 #if SNTP_CHECK_RESPONSE >= 1
     /* save server address to verify it in sntp_recv */
@@ -567,6 +571,7 @@
     LWIP_DEBUGF(SNTP_DEBUG_SERIOUS, ("sntp_send_request: Out of memory, trying again in %"U32_F" ms\n",
                                      (u32_t)SNTP_RETRY_TIMEOUT));
     /* out of memory: set up a timer to send a retry */
+    sys_untimeout(sntp_request, NULL);
     sys_timeout((u32_t)SNTP_RETRY_TIMEOUT, sntp_request, NULL);
   }
 }
@@ -635,6 +640,7 @@
   } else {
     /* address conversion failed, try another server */
     LWIP_DEBUGF(SNTP_DEBUG_WARN_STATE, ("sntp_request: Invalid server address, trying next server.\n"));
+    sys_untimeout(sntp_try_next_server, NULL);
     sys_timeout((u32_t)SNTP_RETRY_TIMEOUT, sntp_try_next_server, NULL);
   }
 }
@@ -648,6 +654,7 @@
 sntp_init(void)
 {
   /* LWIP_ASSERT_CORE_LOCKED(); is checked by udp_new() */
+  LWIP_DEBUGF(SNTP_DEBUG_TRACE, ("sntp_init: SNTP initialised\n"));
 
 #ifdef SNTP_SERVER_ADDRESS
 #if SNTP_SERVER_DNS
@@ -750,7 +757,7 @@
 }
 #endif /* SNTP_MONITOR_SERVER_REACHABILITY */
 
-#if SNTP_GET_SERVERS_FROM_DHCP
+#if SNTP_GET_SERVERS_FROM_DHCP || SNTP_GET_SERVERS_FROM_DHCPV6
 /**
  * Config SNTP server handling by IP address, name, or DHCP; clear table
  * @param set_servers_from_dhcp enable or disable getting server addresses from dhcp
@@ -764,7 +771,7 @@
     sntp_set_servers_from_dhcp = new_mode;
   }
 }
-#endif /* SNTP_GET_SERVERS_FROM_DHCP */
+#endif /* SNTP_GET_SERVERS_FROM_DHCP || SNTP_GET_SERVERS_FROM_DHCPV6 */
 
 /**
  * @ingroup sntp
@@ -816,6 +823,33 @@
 }
 #endif /* LWIP_DHCP && SNTP_GET_SERVERS_FROM_DHCP */
 
+#if LWIP_IPV6_DHCP6 && SNTP_GET_SERVERS_FROM_DHCPV6
+/**
+ * Initialize one of the NTP servers by IP address, required by DHCPV6
+ *
+ * @param num the number of NTP server addresses to set must be < SNTP_MAX_SERVERS
+ * @param server array of IP address of the NTP servers to set
+ */
+void
+dhcp6_set_ntp_servers(u8_t num_ntp_servers, ip_addr_t* ntp_server_addrs)
+{
+  LWIP_DEBUGF(SNTP_DEBUG_TRACE, ("sntp: %s %u NTP server(s) via DHCPv6\n",
+                                 (sntp_set_servers_from_dhcp ? "Got" : "Rejected"),
+                                 num_ntp_servers));
+  if (sntp_set_servers_from_dhcp && num_ntp_servers) {
+    u8_t i;
+    for (i = 0; (i < num_ntp_servers) && (i < SNTP_MAX_SERVERS); i++) {
+      LWIP_DEBUGF(SNTP_DEBUG_TRACE, ("sntp: NTP server %u: %s\n",
+                                     i, ipaddr_ntoa(&ntp_server_addrs[i])));
+      sntp_setserver(i, &ntp_server_addrs[i]);
+    }
+    for (i = num_ntp_servers; i < SNTP_MAX_SERVERS; i++) {
+      sntp_setserver(i, NULL);
+    }
+  }
+}
+#endif /* LWIP_DHCPv6 && SNTP_GET_SERVERS_FROM_DHCPV6 */
+
 /**
  * @ingroup sntp
  * Obtain one of the currently configured by IP address (or DHCP) NTP servers
diff --git a/src/core/altcp.c b/src/core/altcp.c
index d46d6cd..4abef7c 100644
--- a/src/core/altcp.c
+++ b/src/core/altcp.c
@@ -501,6 +501,24 @@
   return 0;
 }
 
+#if LWIP_TCP_KEEPALIVE
+void
+altcp_keepalive_disable(struct altcp_pcb *conn)
+{
+  if (conn && conn->fns && conn->fns->keepalive_disable) {
+    conn->fns->keepalive_disable(conn);
+  }
+}
+
+void
+altcp_keepalive_enable(struct altcp_pcb *conn, u32_t idle, u32_t intvl, u32_t count)
+{
+  if (conn && conn->fns && conn->fns->keepalive_enable) {
+      conn->fns->keepalive_enable(conn, idle, intvl, count);
+  }
+}
+#endif
+
 #ifdef LWIP_DEBUG
 enum tcp_state
 altcp_dbg_get_tcp_state(struct altcp_pcb *conn)
@@ -666,6 +684,24 @@
   return 0;
 }
 
+#if LWIP_TCP_KEEPALIVE
+void
+altcp_default_keepalive_disable(struct altcp_pcb *conn)
+{
+  if (conn && conn->inner_conn) {
+    altcp_keepalive_disable(conn->inner_conn);
+  }
+}
+
+void
+altcp_default_keepalive_enable(struct altcp_pcb *conn, u32_t idle, u32_t intvl, u32_t count)
+{
+  if (conn && conn->inner_conn) {
+      altcp_keepalive_enable(conn->inner_conn, idle, intvl, count);
+  }
+}
+#endif
+
 #ifdef LWIP_DEBUG
 enum tcp_state
 altcp_default_dbg_get_tcp_state(struct altcp_pcb *conn)
diff --git a/src/core/altcp_tcp.c b/src/core/altcp_tcp.c
index b715f04..1869e2a 100644
--- a/src/core/altcp_tcp.c
+++ b/src/core/altcp_tcp.c
@@ -49,6 +49,7 @@
 #include "lwip/altcp_tcp.h"
 #include "lwip/priv/altcp_priv.h"
 #include "lwip/tcp.h"
+#include "lwip/priv/tcp_priv.h"
 #include "lwip/mem.h"
 
 #include <string.h>
@@ -160,21 +161,25 @@
 altcp_tcp_remove_callbacks(struct tcp_pcb *tpcb)
 {
   tcp_arg(tpcb, NULL);
-  tcp_recv(tpcb, NULL);
-  tcp_sent(tpcb, NULL);
-  tcp_err(tpcb, NULL);
-  tcp_poll(tpcb, NULL, tpcb->pollinterval);
+  if (tpcb->state != LISTEN) {
+    tcp_recv(tpcb, NULL);
+    tcp_sent(tpcb, NULL);
+    tcp_err(tpcb, NULL);
+    tcp_poll(tpcb, NULL, tpcb->pollinterval);
+  }
 }
 
 static void
 altcp_tcp_setup_callbacks(struct altcp_pcb *conn, struct tcp_pcb *tpcb)
 {
   tcp_arg(tpcb, conn);
-  tcp_recv(tpcb, altcp_tcp_recv);
-  tcp_sent(tpcb, altcp_tcp_sent);
-  tcp_err(tpcb, altcp_tcp_err);
-  /* tcp_poll is set when interval is set by application */
-  /* listen is set totally different :-) */
+  /* this might be called for LISTN when close fails... */
+  if (tpcb->state != LISTEN) {
+    tcp_recv(tpcb, altcp_tcp_recv);
+    tcp_sent(tpcb, altcp_tcp_sent);
+    tcp_err(tpcb, altcp_tcp_err);
+    /* tcp_poll is set when interval is set by application */
+  }
 }
 
 static void
@@ -446,6 +451,31 @@
   }
 }
 
+#if LWIP_TCP_KEEPALIVE
+static void
+altcp_tcp_keepalive_disable(struct altcp_pcb *conn)
+{
+  if (conn && conn->state) {
+    struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
+    ALTCP_TCP_ASSERT_CONN(conn);
+    ip_reset_option(pcb, SOF_KEEPALIVE);
+  }
+}
+
+static void
+altcp_tcp_keepalive_enable(struct altcp_pcb *conn, u32_t idle, u32_t intvl, u32_t cnt)
+{
+  if (conn && conn->state) {
+    struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
+    ALTCP_TCP_ASSERT_CONN(conn);
+    ip_set_option(pcb, SOF_KEEPALIVE);
+    pcb->keep_idle = idle ? idle : TCP_KEEPIDLE_DEFAULT;
+    pcb->keep_intvl = intvl ? intvl : TCP_KEEPINTVL_DEFAULT;
+    pcb->keep_cnt = cnt ? cnt : TCP_KEEPCNT_DEFAULT;
+  }
+}
+#endif
+
 static void
 altcp_tcp_dealloc(struct altcp_pcb *conn)
 {
@@ -535,6 +565,10 @@
   altcp_tcp_get_tcp_addrinfo,
   altcp_tcp_get_ip,
   altcp_tcp_get_port
+#if LWIP_TCP_KEEPALIVE
+  , altcp_tcp_keepalive_disable
+  , altcp_tcp_keepalive_enable
+#endif
 #ifdef LWIP_DEBUG
   , altcp_tcp_dbg_get_tcp_state
 #endif
diff --git a/src/core/init.c b/src/core/init.c
index b3737a3..3620e1d 100644
--- a/src/core/init.c
+++ b/src/core/init.c
@@ -237,8 +237,9 @@
 #error "NETCONN_MORE != TCP_WRITE_FLAG_MORE"
 #endif
 #endif /* LWIP_NETCONN && LWIP_TCP */
-#if LWIP_SOCKET
-#endif /* LWIP_SOCKET */
+#if LWIP_NETCONN_FULLDUPLEX && !LWIP_NETCONN_SEM_PER_THREAD
+#error "For LWIP_NETCONN_FULLDUPLEX to work, LWIP_NETCONN_SEM_PER_THREAD is required"
+#endif
 
 
 /* Compile-time checks for deprecated options.
diff --git a/src/core/ipv4/dhcp.c b/src/core/ipv4/dhcp.c
index 534574f..76b9f68 100644
--- a/src/core/ipv4/dhcp.c
+++ b/src/core/ipv4/dhcp.c
@@ -127,6 +127,11 @@
 #define LWIP_DHCP_PROVIDE_DNS_SERVERS 0
 #endif
 
+#ifndef LWIP_DHCP_INPUT_ERROR
+#define LWIP_DHCP_INPUT_ERROR(message, expression, handler) do { if (!(expression)) { \
+  handler;} } while(0)
+#endif
+
 /** Option handling: options are parsed in dhcp_parse_reply
  * and saved in an array where other functions can load them from.
  * This might be moved into the struct dhcp (not necessarily since
@@ -1115,13 +1120,6 @@
   }
 
   ip4_addr_copy(gw_addr, dhcp->offered_gw_addr);
-  /* gateway address not given? */
-  if (ip4_addr_isany_val(gw_addr)) {
-    /* copy network address */
-    ip4_addr_get_network(&gw_addr, &dhcp->offered_ip_addr, &sn_mask);
-    /* use first host address on network as gateway */
-    ip4_addr_set_u32(&gw_addr, ip4_addr_get_u32(&gw_addr) | PP_HTONL(0x00000001UL));
-  }
 
 #if LWIP_DHCP_AUTOIP_COOP
   if (dhcp->autoip_coop_state == DHCP_AUTOIP_COOP_STATE_ON) {
@@ -1349,6 +1347,7 @@
     /* create and initialize the DHCP message header */
     struct pbuf *p_out;
     u16_t options_out_len;
+    dhcp_set_state(dhcp, DHCP_STATE_OFF);
     p_out = dhcp_create_msg(netif, dhcp, DHCP_RELEASE, &options_out_len);
     if (p_out != NULL) {
       struct dhcp_msg *msg_out = (struct dhcp_msg *)p_out->payload;
@@ -1365,10 +1364,12 @@
       /* sending release failed, but that's not a problem since the correct behaviour of dhcp does not rely on release */
       LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_release: could not allocate DHCP request\n"));
     }
-  }
 
-  /* remove IP address from interface (prevents routing from selecting this interface) */
-  netif_set_addr(netif, IP4_ADDR_ANY4, IP4_ADDR_ANY4, IP4_ADDR_ANY4);
+    /* remove IP address from interface (prevents routing from selecting this interface) */
+    netif_set_addr(netif, IP4_ADDR_ANY4, IP4_ADDR_ANY4, IP4_ADDR_ANY4);
+  } else {
+     dhcp_set_state(dhcp, DHCP_STATE_OFF);
+  }
 
 #if LWIP_DHCP_AUTOIP_COOP
   if (dhcp->autoip_coop_state == DHCP_AUTOIP_COOP_STATE_ON) {
@@ -1377,8 +1378,6 @@
   }
 #endif /* LWIP_DHCP_AUTOIP_COOP */
 
-  dhcp_set_state(dhcp, DHCP_STATE_OFF);
-
   if (dhcp->pcb_allocated != 0) {
     dhcp_dec_pcb_refcount(); /* free DHCP PCB if not needed any more */
     dhcp->pcb_allocated = 0;
@@ -1509,6 +1508,7 @@
   u8_t *options;
   u16_t offset;
   u16_t offset_max;
+  u16_t options_offset;
   u16_t options_idx;
   u16_t options_idx_max;
   struct pbuf *q;
@@ -1541,6 +1541,7 @@
   options_idx_max = p->tot_len;
 again:
   q = p;
+  options_offset = options_idx;
   while ((q != NULL) && (options_idx >= q->len)) {
     options_idx = (u16_t)(options_idx - q->len);
     options_idx_max = (u16_t)(options_idx_max - q->len);
@@ -1579,58 +1580,58 @@
         /* will be increased below */
         break;
       case (DHCP_OPTION_SUBNET_MASK):
-        LWIP_ERROR("len == 4", len == 4, return ERR_VAL;);
+        LWIP_DHCP_INPUT_ERROR("len == 4", len == 4, return ERR_VAL;);
         decode_idx = DHCP_OPTION_IDX_SUBNET_MASK;
         break;
       case (DHCP_OPTION_ROUTER):
         decode_len = 4; /* only copy the first given router */
-        LWIP_ERROR("len >= decode_len", len >= decode_len, return ERR_VAL;);
+        LWIP_DHCP_INPUT_ERROR("len >= decode_len", len >= decode_len, return ERR_VAL;);
         decode_idx = DHCP_OPTION_IDX_ROUTER;
         break;
 #if LWIP_DHCP_PROVIDE_DNS_SERVERS
       case (DHCP_OPTION_DNS_SERVER):
         /* special case: there might be more than one server */
-        LWIP_ERROR("len %% 4 == 0", len % 4 == 0, return ERR_VAL;);
+        LWIP_DHCP_INPUT_ERROR("len %% 4 == 0", len % 4 == 0, return ERR_VAL;);
         /* limit number of DNS servers */
         decode_len = LWIP_MIN(len, 4 * DNS_MAX_SERVERS);
-        LWIP_ERROR("len >= decode_len", len >= decode_len, return ERR_VAL;);
+        LWIP_DHCP_INPUT_ERROR("len >= decode_len", len >= decode_len, return ERR_VAL;);
         decode_idx = DHCP_OPTION_IDX_DNS_SERVER;
         break;
 #endif /* LWIP_DHCP_PROVIDE_DNS_SERVERS */
       case (DHCP_OPTION_LEASE_TIME):
-        LWIP_ERROR("len == 4", len == 4, return ERR_VAL;);
+        LWIP_DHCP_INPUT_ERROR("len == 4", len == 4, return ERR_VAL;);
         decode_idx = DHCP_OPTION_IDX_LEASE_TIME;
         break;
 #if LWIP_DHCP_GET_NTP_SRV
       case (DHCP_OPTION_NTP):
         /* special case: there might be more than one server */
-        LWIP_ERROR("len %% 4 == 0", len % 4 == 0, return ERR_VAL;);
+        LWIP_DHCP_INPUT_ERROR("len %% 4 == 0", len % 4 == 0, return ERR_VAL;);
         /* limit number of NTP servers */
         decode_len = LWIP_MIN(len, 4 * LWIP_DHCP_MAX_NTP_SERVERS);
-        LWIP_ERROR("len >= decode_len", len >= decode_len, return ERR_VAL;);
+        LWIP_DHCP_INPUT_ERROR("len >= decode_len", len >= decode_len, return ERR_VAL;);
         decode_idx = DHCP_OPTION_IDX_NTP_SERVER;
         break;
 #endif /* LWIP_DHCP_GET_NTP_SRV*/
       case (DHCP_OPTION_OVERLOAD):
-        LWIP_ERROR("len == 1", len == 1, return ERR_VAL;);
+        LWIP_DHCP_INPUT_ERROR("len == 1", len == 1, return ERR_VAL;);
         /* decode overload only in options, not in file/sname: invalid packet */
-        LWIP_ERROR("overload in file/sname", options_idx == DHCP_OPTIONS_OFS, return ERR_VAL;);
+        LWIP_DHCP_INPUT_ERROR("overload in file/sname", options_offset == DHCP_OPTIONS_OFS, return ERR_VAL;);
         decode_idx = DHCP_OPTION_IDX_OVERLOAD;
         break;
       case (DHCP_OPTION_MESSAGE_TYPE):
-        LWIP_ERROR("len == 1", len == 1, return ERR_VAL;);
+        LWIP_DHCP_INPUT_ERROR("len == 1", len == 1, return ERR_VAL;);
         decode_idx = DHCP_OPTION_IDX_MSG_TYPE;
         break;
       case (DHCP_OPTION_SERVER_ID):
-        LWIP_ERROR("len == 4", len == 4, return ERR_VAL;);
+        LWIP_DHCP_INPUT_ERROR("len == 4", len == 4, return ERR_VAL;);
         decode_idx = DHCP_OPTION_IDX_SERVER_ID;
         break;
       case (DHCP_OPTION_T1):
-        LWIP_ERROR("len == 4", len == 4, return ERR_VAL;);
+        LWIP_DHCP_INPUT_ERROR("len == 4", len == 4, return ERR_VAL;);
         decode_idx = DHCP_OPTION_IDX_T1;
         break;
       case (DHCP_OPTION_T2):
-        LWIP_ERROR("len == 4", len == 4, return ERR_VAL;);
+        LWIP_DHCP_INPUT_ERROR("len == 4", len == 4, return ERR_VAL;);
         decode_idx = DHCP_OPTION_IDX_T2;
         break;
       default:
@@ -1662,7 +1663,7 @@
           if (decode_len > 4) {
             /* decode more than one u32_t */
             u16_t next_val_offset;
-            LWIP_ERROR("decode_len %% 4 == 0", decode_len % 4 == 0, return ERR_VAL;);
+            LWIP_DHCP_INPUT_ERROR("decode_len %% 4 == 0", decode_len % 4 == 0, return ERR_VAL;);
             dhcp_got_option(dhcp, decode_idx);
             dhcp_set_option_value(dhcp, decode_idx, lwip_htonl(value));
             decode_len = (u8_t)(decode_len - 4);
@@ -1677,7 +1678,7 @@
           } else if (decode_len == 4) {
             value = lwip_ntohl(value);
           } else {
-            LWIP_ERROR("invalid decode_len", decode_len == 1, return ERR_VAL;);
+            LWIP_DHCP_INPUT_ERROR("invalid decode_len", decode_len == 1, return ERR_VAL;);
             value = ((u8_t *)&value)[0];
           }
           dhcp_got_option(dhcp, decode_idx);
@@ -1690,7 +1691,7 @@
       offset_max = (u16_t)(offset_max - q->len);
       if (offset < offset_max) {
         q = q->next;
-        LWIP_ERROR("next pbuf was null", q != NULL, return ERR_VAL;);
+        LWIP_DHCP_INPUT_ERROR("next pbuf was null", q != NULL, return ERR_VAL;);
         options = (u8_t *)q->payload;
       } else {
         /* We've run out of bytes, probably no end marker. Don't proceed. */
diff --git a/src/core/ipv4/etharp.c b/src/core/ipv4/etharp.c
index 442aac0..c3a5a10 100644
--- a/src/core/ipv4/etharp.c
+++ b/src/core/ipv4/etharp.c
@@ -983,6 +983,14 @@
       /* We don't re-send arp request in etharp_tmr, but we still queue packets,
          since this failure could be temporary, and the next packet calling
          etharp_query again could lead to sending the queued packets. */
+    } else {
+      /* ARP request successfully sent */
+      if ((arp_table[i].state == ETHARP_STATE_PENDING) && !is_new_entry) {
+        /* A new ARP request has been sent for a pending entry. Reset the ctime to
+           not let it expire too fast. */
+        LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: reset ctime for entry %"S16_F"\n", (s16_t)i));
+        arp_table[i].ctime = 0;
+      }
     }
     if (q == NULL) {
       return result;
diff --git a/src/core/ipv4/icmp.c b/src/core/ipv4/icmp.c
index a462ccd..59b493a 100644
--- a/src/core/ipv4/icmp.c
+++ b/src/core/ipv4/icmp.c
@@ -62,7 +62,7 @@
 #define LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN 1
 #endif /* LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN */
 
-/* The amount of data from the original packet to return in a dest-unreachable */
+/* The maximum amount of data from the original packet to return in a dest-unreachable */
 #define ICMP_DEST_UNREACH_DATASIZE 8
 
 static void icmp_send_response(struct pbuf *p, u8_t type, u8_t code);
@@ -345,20 +345,26 @@
   struct icmp_echo_hdr *icmphdr;
   ip4_addr_t iphdr_src;
   struct netif *netif;
+  u16_t response_pkt_len;
 
   /* increase number of messages attempted to send */
   MIB2_STATS_INC(mib2.icmpoutmsgs);
 
-  /* ICMP header + IP header + 8 bytes of data */
-  q = pbuf_alloc(PBUF_IP, sizeof(struct icmp_echo_hdr) + IP_HLEN + ICMP_DEST_UNREACH_DATASIZE,
-                 PBUF_RAM);
+  /* Keep IP header + up to 8 bytes */
+  response_pkt_len = IP_HLEN + ICMP_DEST_UNREACH_DATASIZE;
+  if (p->tot_len < response_pkt_len) {
+    response_pkt_len = p->tot_len;
+  }
+
+  /* ICMP header + part of original packet */
+  q = pbuf_alloc(PBUF_IP, sizeof(struct icmp_echo_hdr) + response_pkt_len, PBUF_RAM);
   if (q == NULL) {
     LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded: failed to allocate pbuf for ICMP packet.\n"));
     MIB2_STATS_INC(mib2.icmpouterrors);
     return;
   }
   LWIP_ASSERT("check that first pbuf can hold icmp message",
-              (q->len >= (sizeof(struct icmp_echo_hdr) + IP_HLEN + ICMP_DEST_UNREACH_DATASIZE)));
+              (q->len >= (sizeof(struct icmp_echo_hdr) + response_pkt_len)));
 
   iphdr = (struct ip_hdr *)p->payload;
   LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded from "));
@@ -375,7 +381,7 @@
 
   /* copy fields from original packet */
   SMEMCPY((u8_t *)q->payload + sizeof(struct icmp_echo_hdr), (u8_t *)p->payload,
-          IP_HLEN + ICMP_DEST_UNREACH_DATASIZE);
+          response_pkt_len);
 
   ip4_addr_copy(iphdr_src, iphdr->src);
 #ifdef LWIP_HOOK_IP4_ROUTE_SRC
diff --git a/src/core/ipv4/ip4_addr.c b/src/core/ipv4/ip4_addr.c
index 33204d1..f99d8dd 100644
--- a/src/core/ipv4/ip4_addr.c
+++ b/src/core/ipv4/ip4_addr.c
@@ -173,6 +173,8 @@
     }
     for (;;) {
       if (lwip_isdigit(c)) {
+        if((base == 8) && ((u32_t)(c - '0') >= 8))
+          break;
         val = (val * base) + (u32_t)(c - '0');
         c = *++cp;
       } else if (base == 16 && lwip_isxdigit(c)) {
diff --git a/src/core/ipv6/dhcp6.c b/src/core/ipv6/dhcp6.c
index 7cf98a5..41444a4 100644
--- a/src/core/ipv6/dhcp6.c
+++ b/src/core/ipv6/dhcp6.c
@@ -451,7 +451,16 @@
 static void
 dhcp6_information_request(struct netif *netif, struct dhcp6 *dhcp6)
 {
-  const u16_t requested_options[] = {DHCP6_OPTION_DNS_SERVERS, DHCP6_OPTION_DOMAIN_LIST, DHCP6_OPTION_SNTP_SERVERS};
+  const u16_t requested_options[] = {
+#if LWIP_DHCP6_PROVIDE_DNS_SERVERS
+    DHCP6_OPTION_DNS_SERVERS, 
+    DHCP6_OPTION_DOMAIN_LIST
+#endif
+#if LWIP_DHCP6_GET_NTP_SRV
+    , DHCP6_OPTION_SNTP_SERVERS
+#endif
+  };
+  
   u16_t msecs;
   struct pbuf *p_out;
   u16_t options_out_len;
@@ -527,7 +536,7 @@
     u16_t idx;
     u8_t n;
 
-    memset(&dns_addr, 0, sizeof(dns_addr));
+    ip_addr_set_zero_ip6(&dns_addr);
     dns_addr6 = ip_2_ip6(&dns_addr);
     for (n = 0, idx = op_start; (idx < op_start + op_len) && (n < LWIP_DHCP6_PROVIDE_DNS_SERVERS);
          n++, idx += sizeof(struct ip6_addr_packed)) {
diff --git a/src/core/ipv6/icmp6.c b/src/core/ipv6/icmp6.c
index 167738a..4fd1021 100644
--- a/src/core/ipv6/icmp6.c
+++ b/src/core/ipv6/icmp6.c
@@ -57,9 +57,9 @@
 
 #include <string.h>
 
-#if LWIP_ICMP6_DATASIZE == 0
+#if !LWIP_ICMP6_DATASIZE || (LWIP_ICMP6_DATASIZE > (IP6_MIN_MTU_LENGTH - IP6_HLEN - ICMP6_HLEN))
 #undef LWIP_ICMP6_DATASIZE
-#define LWIP_ICMP6_DATASIZE   8
+#define LWIP_ICMP6_DATASIZE   (IP6_MIN_MTU_LENGTH - IP6_HLEN - ICMP6_HLEN)
 #endif
 
 /* Forward declarations */
@@ -387,26 +387,35 @@
 {
   struct pbuf *q;
   struct icmp6_hdr *icmp6hdr;
+  u16_t datalen = LWIP_MIN(p->tot_len, LWIP_ICMP6_DATASIZE);
+  u16_t offset;
 
-  /* ICMPv6 header + IPv6 header + data */
-  q = pbuf_alloc(PBUF_IP, sizeof(struct icmp6_hdr) + IP6_HLEN + LWIP_ICMP6_DATASIZE,
+  /* ICMPv6 header + datalen (as much of the offending packet as possible) */
+  q = pbuf_alloc(PBUF_IP, sizeof(struct icmp6_hdr) + datalen,
                  PBUF_RAM);
   if (q == NULL) {
     LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded: failed to allocate pbuf for ICMPv6 packet.\n"));
     ICMP6_STATS_INC(icmp6.memerr);
     return;
   }
-  LWIP_ASSERT("check that first pbuf can hold icmp 6message",
-             (q->len >= (sizeof(struct icmp6_hdr) + IP6_HLEN + LWIP_ICMP6_DATASIZE)));
+  LWIP_ASSERT("check that first pbuf can hold icmp6 header",
+             (q->len >= (sizeof(struct icmp6_hdr))));
 
   icmp6hdr = (struct icmp6_hdr *)q->payload;
   icmp6hdr->type = type;
   icmp6hdr->code = code;
   icmp6hdr->data = lwip_htonl(data);
 
-  /* copy fields from original packet */
-  SMEMCPY((u8_t *)q->payload + sizeof(struct icmp6_hdr), (u8_t *)p->payload,
-          IP6_HLEN + LWIP_ICMP6_DATASIZE);
+  /* copy fields from original packet (which may be a chain of pbufs) */
+  offset = sizeof(struct icmp6_hdr);
+  while (p && datalen) {
+    u16_t len = LWIP_MIN(datalen, p->len);
+    err_t res = pbuf_take_at(q, p->payload, len, offset);
+    if (res != ERR_OK) break;
+    datalen -= len;
+    offset += len;
+    p = p->next;
+  }
 
   /* calculate checksum */
   icmp6hdr->chksum = 0;
diff --git a/src/core/ipv6/ip6.c b/src/core/ipv6/ip6.c
index eda11dc..060d5f3 100644
--- a/src/core/ipv6/ip6.c
+++ b/src/core/ipv6/ip6.c
@@ -1305,6 +1305,7 @@
     ip6_addr_copy_from_packed(src_addr, ip6hdr->src);
     ip6_addr_copy_from_packed(dest_addr, ip6hdr->dest);
     netif = ip6_route(&src_addr, &dest_addr);
+    dest = &dest_addr;
   }
 
   if (netif == NULL) {
@@ -1364,6 +1365,7 @@
     ip6_addr_copy_from_packed(src_addr, ip6hdr->src);
     ip6_addr_copy_from_packed(dest_addr, ip6hdr->dest);
     netif = ip6_route(&src_addr, &dest_addr);
+    dest = &dest_addr;
   }
 
   if (netif == NULL) {
diff --git a/src/core/ipv6/ip6_frag.c b/src/core/ipv6/ip6_frag.c
index d6c5d22..8b352f5 100644
--- a/src/core/ipv6/ip6_frag.c
+++ b/src/core/ipv6/ip6_frag.c
@@ -781,7 +781,7 @@
       return ERR_MEM;
     }
     LWIP_ASSERT("this needs a pbuf in one piece!",
-                (p->len >= (IP6_HLEN)));
+                (rambuf->len >= (IP6_HLEN)));
     SMEMCPY(rambuf->payload, original_ip6hdr, IP6_HLEN);
     ip6hdr = (struct ip6_hdr *)rambuf->payload;
     frag_hdr = (struct ip6_frag_hdr *)((u8_t*)rambuf->payload + IP6_HLEN);
diff --git a/src/core/ipv6/mld6.c b/src/core/ipv6/mld6.c
index 6387d46..a9c917d 100644
--- a/src/core/ipv6/mld6.c
+++ b/src/core/ipv6/mld6.c
@@ -253,7 +253,7 @@
       while (group != NULL) {
         if ((!(ip6_addr_ismulticast_iflocal(&(group->group_address)))) &&
             (!(ip6_addr_isallnodes_linklocal(&(group->group_address))))) {
-          mld6_delayed_report(group, mld_hdr->max_resp_delay);
+          mld6_delayed_report(group, lwip_ntohs(mld_hdr->max_resp_delay));
         }
         group = group->next;
       }
@@ -265,7 +265,7 @@
       group = mld6_lookfor_group(inp, ip6_current_dest_addr());
       if (group != NULL) {
         /* Schedule a report. */
-        mld6_delayed_report(group, mld_hdr->max_resp_delay);
+        mld6_delayed_report(group, lwip_ntohs(mld_hdr->max_resp_delay));
       }
     }
     break; /* ICMP6_TYPE_MLQ */
diff --git a/src/core/ipv6/nd6.c b/src/core/ipv6/nd6.c
index db0c132..81992fa 100644
--- a/src/core/ipv6/nd6.c
+++ b/src/core/ipv6/nd6.c
@@ -693,11 +693,11 @@
         }
         mtu_opt = (struct mtu_option *)buffer;
         mtu32 = lwip_htonl(mtu_opt->mtu);
-        if ((mtu32 >= 1280) && (mtu32 <= 0xffff)) {
+        if ((mtu32 >= IP6_MIN_MTU_LENGTH) && (mtu32 <= 0xffff)) {
 #if LWIP_ND6_ALLOW_RA_UPDATES
           if (inp->mtu) {
             /* don't set the mtu for IPv6 higher than the netif driver supports */
-            inp->mtu6 = LWIP_MIN(inp->mtu, (u16_t)mtu32);
+            inp->mtu6 = LWIP_MIN(LWIP_MIN(inp->mtu, inp->mtu6), (u16_t)mtu32);
           } else {
             inp->mtu6 = (u16_t)mtu32;
           }
@@ -766,7 +766,7 @@
 
         rdnss_opt = (struct rdnss_option *)buffer;
         num = (rdnss_opt->length - 1) / 2;
-        for (n = 0; (rdnss_server_idx < DNS_MAX_SERVERS) && (n < num); n++) {
+        for (n = 0; (rdnss_server_idx < DNS_MAX_SERVERS) && (n < num); n++, copy_offset += sizeof(ip6_addr_p_t)) {
           ip_addr_t rdnss_address;
 
           /* Copy directly from pbuf to get an aligned, zoned copy of the prefix. */
@@ -1182,15 +1182,27 @@
 {
   struct ns_header *ns_hdr;
   struct pbuf *p;
-  const ip6_addr_t *src_addr;
+  const ip6_addr_t *src_addr = NULL;
   u16_t lladdr_opt_len;
 
   LWIP_ASSERT("target address is required", target_addr != NULL);
 
-  if (!(flags & ND6_SEND_FLAG_ANY_SRC) &&
-      ip6_addr_isvalid(netif_ip6_addr_state(netif,0))) {
-    /* Use link-local address as source address. */
-    src_addr = netif_ip6_addr(netif, 0);
+  if (!(flags & ND6_SEND_FLAG_ANY_SRC)) {
+    int i;
+    for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+      if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) &&
+            ip6_addr_netcmp(target_addr, netif_ip6_addr(netif, i))) {
+        src_addr = netif_ip6_addr(netif, i);
+        break;
+      }
+    }
+
+    if (i == LWIP_IPV6_NUM_ADDRESSES) {
+      LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_WARNING, ("ICMPv6 NS: no available src address\n"));
+      ND6_STATS_INC(nd6.err);
+      return;
+    }
+
     /* calculate option length (in 8-byte-blocks) */
     lladdr_opt_len = ((netif->hwaddr_len + 2) + 7) >> 3;
   } else {
@@ -2300,7 +2312,7 @@
     return netif_mtu6(netif);
   }
 
-  return 1280; /* Minimum MTU */
+  return IP6_MIN_MTU_LENGTH; /* Minimum MTU */
 }
 
 
diff --git a/src/core/netif.c b/src/core/netif.c
index 15200a2..088b50e 100644
--- a/src/core/netif.c
+++ b/src/core/netif.c
@@ -348,10 +348,6 @@
 #if LWIP_IPV6 && LWIP_IPV6_MLD
   netif->mld_mac_filter = NULL;
 #endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
-#if ENABLE_LOOPBACK
-  netif->loop_first = NULL;
-  netif->loop_last = NULL;
-#endif /* ENABLE_LOOPBACK */
 
   /* remember netif specific state information data */
   netif->state = state;
@@ -359,9 +355,16 @@
   netif->input = input;
 
   NETIF_RESET_HINTS(netif);
-#if ENABLE_LOOPBACK && LWIP_LOOPBACK_MAX_PBUFS
+#if ENABLE_LOOPBACK
+  netif->loop_first = NULL;
+  netif->loop_last = NULL;
+#if LWIP_LOOPBACK_MAX_PBUFS
   netif->loop_cnt_current = 0;
-#endif /* ENABLE_LOOPBACK && LWIP_LOOPBACK_MAX_PBUFS */
+#endif /* LWIP_LOOPBACK_MAX_PBUFS */
+#if LWIP_NETIF_LOOPBACK_MULTITHREADING
+  netif->reschedule_poll = 0;
+#endif /* LWIP_NETIF_LOOPBACK_MULTITHREADING */
+#endif /* ENABLE_LOOPBACK */
 
 #if LWIP_IPV4
   netif_set_addr(netif, ipaddr, netmask, gw);
@@ -1031,6 +1034,10 @@
 
   if (netif->flags & NETIF_FLAG_LINK_UP) {
     netif_clear_flags(netif, NETIF_FLAG_LINK_UP);
+#if LWIP_IPV6 && LWIP_ND6_ALLOW_RA_UPDATES
+    netif->mtu6 = netif->mtu;
+#endif
+
     NETIF_LINK_CALLBACK(netif);
 #if LWIP_NETIF_EXT_STATUS_CALLBACK
     {
@@ -1062,11 +1069,12 @@
 /**
  * @ingroup netif
  * Send an IP packet to be received on the same netif (loopif-like).
- * The pbuf is simply copied and handed back to netif->input.
- * In multithreaded mode, this is done directly since netif->input must put
- * the packet on a queue.
- * In callback mode, the packet is put on an internal queue and is fed to
+ * The pbuf is copied and added to an internal queue which is fed to 
  * netif->input by netif_poll().
+ * In multithreaded mode, the call to netif_poll() is queued to be done on the
+ * TCP/IP thread.
+ * In callback mode, the user has the responsibility to call netif_poll() in 
+ * the main loop of their application.
  *
  * @param netif the lwip network interface structure
  * @param p the (IP) packet to 'send'
@@ -1143,6 +1151,12 @@
     LWIP_ASSERT("if first != NULL, last must also be != NULL", netif->loop_last != NULL);
     netif->loop_last->next = r;
     netif->loop_last = last;
+#if LWIP_NETIF_LOOPBACK_MULTITHREADING
+    if (netif->reschedule_poll) {
+      schedule_poll = 1;
+      netif->reschedule_poll = 0;
+    }
+#endif /* LWIP_NETIF_LOOPBACK_MULTITHREADING */
   } else {
     netif->loop_first = r;
     netif->loop_last = last;
@@ -1160,7 +1174,11 @@
 #if LWIP_NETIF_LOOPBACK_MULTITHREADING
   /* For multithreading environment, schedule a call to netif_poll */
   if (schedule_poll) {
-    tcpip_try_callback((tcpip_callback_fn)netif_poll, netif);
+    if (tcpip_try_callback((tcpip_callback_fn)netif_poll, netif) != ERR_OK) {
+      SYS_ARCH_PROTECT(lev);
+      netif->reschedule_poll = 1;
+      SYS_ARCH_UNPROTECT(lev);
+    }
   }
 #endif /* LWIP_NETIF_LOOPBACK_MULTITHREADING */
 
@@ -1710,6 +1728,10 @@
   }
 
   num = (u8_t)atoi(&name[2]);
+  if (!num && (name[2] != '0')) {
+    /* this means atoi has failed */
+    return NULL;
+  }
 
   NETIF_FOREACH(netif) {
     if (num == netif->num &&
diff --git a/src/core/pbuf.c b/src/core/pbuf.c
index a209e0c..7638dfd 100644
--- a/src/core/pbuf.c
+++ b/src/core/pbuf.c
@@ -271,7 +271,7 @@
       break;
     }
     case PBUF_RAM: {
-      u16_t payload_len = (u16_t)(LWIP_MEM_ALIGN_SIZE(offset) + LWIP_MEM_ALIGN_SIZE(length));
+      mem_size_t payload_len = (mem_size_t)(LWIP_MEM_ALIGN_SIZE(offset) + LWIP_MEM_ALIGN_SIZE(length));
       mem_size_t alloc_len = (mem_size_t)(LWIP_MEM_ALIGN_SIZE(SIZEOF_STRUCT_PBUF) + payload_len);
 
       /* bug #50040: Check for integer overflow when calculating alloc_len */
@@ -960,54 +960,88 @@
 err_t
 pbuf_copy(struct pbuf *p_to, const struct pbuf *p_from)
 {
-  size_t offset_to = 0, offset_from = 0, len;
-
   LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_copy(%p, %p)\n",
               (const void *)p_to, (const void *)p_from));
 
+  LWIP_ERROR("pbuf_copy: invalid source", p_from != NULL, return ERR_ARG;);
+  return pbuf_copy_partial_pbuf(p_to, p_from, p_from->tot_len, 0);
+}
+
+/**
+ * @ingroup pbuf
+ * Copy part or all of one packet buffer into another, to a specified offset.
+ *
+ * @note Only data in one packet is copied, no packet queue!
+ * @note Argument order is shared with pbuf_copy, but different than pbuf_copy_partial.
+ *
+ * @param p_to pbuf destination of the copy
+ * @param p_from pbuf source of the copy
+ * @param copy_len number of bytes to copy
+ * @param offset offset in destination pbuf where to copy to
+ *
+ * @return ERR_OK if copy_len bytes were copied
+ *         ERR_ARG if one of the pbufs is NULL or p_from is shorter than copy_len
+ *                 or p_to is not big enough to hold copy_len at offset
+ *         ERR_VAL if any of the pbufs are part of a queue
+ */
+err_t
+pbuf_copy_partial_pbuf(struct pbuf *p_to, const struct pbuf *p_from, u16_t copy_len, u16_t offset)
+{
+  size_t offset_to = offset, offset_from = 0, len_calc;
+  u16_t len;
+
+  LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_copy_partial_pbuf(%p, %p, %"U16_F", %"U16_F")\n",
+              (const void *)p_to, (const void *)p_from, copy_len, offset));
+
+  /* is the copy_len in range? */
+  LWIP_ERROR("pbuf_copy_partial_pbuf: copy_len bigger than source", ((p_from != NULL) &&
+             (p_from->tot_len >= copy_len)), return ERR_ARG;);
   /* is the target big enough to hold the source? */
-  LWIP_ERROR("pbuf_copy: target not big enough to hold source", ((p_to != NULL) &&
-             (p_from != NULL) && (p_to->tot_len >= p_from->tot_len)), return ERR_ARG;);
+  LWIP_ERROR("pbuf_copy_partial_pbuf: target not big enough", ((p_to != NULL) &&
+             (p_to->tot_len >= (offset + copy_len))), return ERR_ARG;);
 
   /* iterate through pbuf chain */
   do {
     /* copy one part of the original chain */
     if ((p_to->len - offset_to) >= (p_from->len - offset_from)) {
       /* complete current p_from fits into current p_to */
-      len = p_from->len - offset_from;
+      len_calc = p_from->len - offset_from;
     } else {
       /* current p_from does not fit into current p_to */
-      len = p_to->len - offset_to;
+      len_calc = p_to->len - offset_to;
     }
+    len = (u16_t)LWIP_MIN(copy_len, len_calc);
     MEMCPY((u8_t *)p_to->payload + offset_to, (u8_t *)p_from->payload + offset_from, len);
     offset_to += len;
     offset_from += len;
+    copy_len -= len;
     LWIP_ASSERT("offset_to <= p_to->len", offset_to <= p_to->len);
     LWIP_ASSERT("offset_from <= p_from->len", offset_from <= p_from->len);
     if (offset_from >= p_from->len) {
       /* on to next p_from (if any) */
       offset_from = 0;
       p_from = p_from->next;
+      LWIP_ERROR("p_from != NULL", (p_from != NULL) || (copy_len == 0), return ERR_ARG;);
     }
     if (offset_to == p_to->len) {
       /* on to next p_to (if any) */
       offset_to = 0;
       p_to = p_to->next;
-      LWIP_ERROR("p_to != NULL", (p_to != NULL) || (p_from == NULL), return ERR_ARG;);
+      LWIP_ERROR("p_to != NULL", (p_to != NULL) || (copy_len == 0), return ERR_ARG;);
     }
 
     if ((p_from != NULL) && (p_from->len == p_from->tot_len)) {
       /* don't copy more than one packet! */
-      LWIP_ERROR("pbuf_copy() does not allow packet queues!",
+      LWIP_ERROR("pbuf_copy_partial_pbuf() does not allow packet queues!",
                  (p_from->next == NULL), return ERR_VAL;);
     }
     if ((p_to != NULL) && (p_to->len == p_to->tot_len)) {
       /* don't copy more than one packet! */
-      LWIP_ERROR("pbuf_copy() does not allow packet queues!",
+      LWIP_ERROR("pbuf_copy_partial_pbuf() does not allow packet queues!",
                  (p_to->next == NULL), return ERR_VAL;);
     }
-  } while (p_from);
-  LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_copy: end of chain reached.\n"));
+  } while (copy_len);
+  LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_copy_partial_pbuf: copy complete.\n"));
   return ERR_OK;
 }
 
diff --git a/src/core/tcp.c b/src/core/tcp.c
index bd7d64e..371db2b 100644
--- a/src/core/tcp.c
+++ b/src/core/tcp.c
@@ -647,6 +647,7 @@
  * bound to all local IP addresses.
  * If another connection is bound to the same port, the function will
  * return ERR_USE, otherwise ERR_OK is returned.
+ * @see MEMP_NUM_TCP_PCB_LISTEN and MEMP_NUM_TCP_PCB
  *
  * @param pcb the tcp_pcb to bind (no check is done whether this pcb is
  *        already bound!)
@@ -889,7 +890,7 @@
   lpcb->state = LISTEN;
   lpcb->prio = pcb->prio;
   lpcb->so_options = pcb->so_options;
-  lpcb->netif_idx = NETIF_NO_INDEX;
+  lpcb->netif_idx = pcb->netif_idx;
   lpcb->ttl = pcb->ttl;
   lpcb->tos = pcb->tos;
 #if LWIP_IPV4 && LWIP_IPV6
@@ -1933,6 +1934,7 @@
  * any of the TCP PCB lists.
  * The pcb is not put on any list until binding using tcp_bind().
  * If memory is not available for creating the new pcb, NULL is returned.
+ * @see MEMP_NUM_TCP_PCB_LISTEN and MEMP_NUM_TCP_PCB
  *
  * @internal: Maybe there should be a idle TCP PCB list where these
  * PCBs are put on. Port reservation using tcp_bind() is implemented but
@@ -1952,6 +1954,7 @@
  * Creates a new TCP protocol control block but doesn't
  * place it on any of the TCP PCB lists.
  * The pcb is not put on any list until binding using tcp_bind().
+ * @see MEMP_NUM_TCP_PCB_LISTEN and MEMP_NUM_TCP_PCB
  *
  * @param type IP address type, see @ref lwip_ip_addr_type definitions.
  * If you want to listen to IPv4 and IPv6 (dual-stack) connections,
@@ -2067,6 +2070,7 @@
  * @ingroup tcp_raw
  * Used for specifying the function that should be called when a
  * LISTENing connection has been connected to another host.
+ * @see MEMP_NUM_TCP_PCB_LISTEN and MEMP_NUM_TCP_PCB
  *
  * @param pcb tcp_pcb to set the accept callback
  * @param accept callback function to call for this pcb when LISTENing
diff --git a/src/core/tcp_in.c b/src/core/tcp_in.c
index 428a6f4..2202e38 100644
--- a/src/core/tcp_in.c
+++ b/src/core/tcp_in.c
@@ -852,8 +852,9 @@
   /* Do different things depending on the TCP state. */
   switch (pcb->state) {
     case SYN_SENT:
-      LWIP_DEBUGF(TCP_INPUT_DEBUG, ("SYN-SENT: ackno %"U32_F" pcb->snd_nxt %"U32_F" unacked %"U32_F"\n", ackno,
-                                    pcb->snd_nxt, lwip_ntohl(pcb->unacked->tcphdr->seqno)));
+      LWIP_DEBUGF(TCP_INPUT_DEBUG, ("SYN-SENT: ackno %"U32_F" pcb->snd_nxt %"U32_F" unacked %s %"U32_F"\n",
+                                    ackno, pcb->snd_nxt, pcb->unacked ? "" : " empty:",
+                                    pcb->unacked ? lwip_ntohl(pcb->unacked->tcphdr->seqno) : 0));
       /* received SYN ACK with expected sequence number? */
       if ((flags & TCP_ACK) && (flags & TCP_SYN)
           && (ackno == pcb->lastack + 1)) {
diff --git a/src/core/tcp_out.c b/src/core/tcp_out.c
index 724df10..8149d39 100644
--- a/src/core/tcp_out.c
+++ b/src/core/tcp_out.c
@@ -913,6 +913,7 @@
 
   seg = tcp_create_segment(pcb, p, remainder_flags, lwip_ntohl(useg->tcphdr->seqno) + split, optflags);
   if (seg == NULL) {
+    p = NULL; /* Freed by tcp_create_segment */
     LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
                 ("tcp_split_unsent_seg: could not create new TCP segment\n"));
     goto memerr;
@@ -2002,7 +2003,7 @@
     LWIP_DEBUGF(TCP_DEBUG, ("tcp_rst: could not allocate memory for pbuf\n"));
     return;
   }
-  tcp_output_fill_options(pcb, p, 0, optlen);
+  tcp_output_fill_options(pcb, p, 0, 0);
 
   MIB2_STATS_INC(mib2.tcpoutrsts);
 
@@ -2096,7 +2097,7 @@
                 ("tcp_keepalive: could not allocate memory for pbuf\n"));
     return ERR_MEM;
   }
-  tcp_output_fill_options(pcb, p, 0, optlen);
+  tcp_output_fill_options(pcb, p, 0, 0);
   err = tcp_output_control_segment(pcb, p, &pcb->local_ip, &pcb->remote_ip);
 
   LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: seqno %"U32_F" ackno %"U32_F" err %d.\n",
@@ -2178,7 +2179,7 @@
   if (TCP_SEQ_LT(pcb->snd_nxt, snd_nxt)) {
     pcb->snd_nxt = snd_nxt;
   }
-  tcp_output_fill_options(pcb, p, 0, optlen);
+  tcp_output_fill_options(pcb, p, 0, 0);
 
   err = tcp_output_control_segment(pcb, p, &pcb->local_ip, &pcb->remote_ip);
 
diff --git a/src/core/udp.c b/src/core/udp.c
index 9d2cb4a..0b609d3 100644
--- a/src/core/udp.c
+++ b/src/core/udp.c
@@ -997,9 +997,13 @@
         {
           /* port matches that of PCB in list and REUSEADDR not set -> reject */
           if ((ipcb->local_port == port) &&
+              (((IP_GET_TYPE(&ipcb->local_ip) == IP_GET_TYPE(ipaddr)) &&
               /* IP address matches or any IP used? */
-              (ip_addr_cmp(&ipcb->local_ip, ipaddr) || ip_addr_isany(ipaddr) ||
-              ip_addr_isany(&ipcb->local_ip))) {
+              (ip_addr_cmp(&ipcb->local_ip, ipaddr) ||
+              ip_addr_isany(ipaddr) ||
+              ip_addr_isany(&ipcb->local_ip))) ||
+              (IP_GET_TYPE(&ipcb->local_ip) == IPADDR_TYPE_ANY) ||
+              (IP_GET_TYPE(ipaddr) == IPADDR_TYPE_ANY))) {
             /* other PCB already binds to this local IP and port */
             LWIP_DEBUGF(UDP_DEBUG,
                         ("udp_bind: local port %"U16_F" already bound by another pcb\n", port));
@@ -1208,6 +1212,7 @@
  * Creates a new UDP pcb which can be used for UDP communication. The
  * pcb is not active until it has either been bound to a local address
  * or connected to a remote address.
+ * @see MEMP_NUM_UDP_PCB
  *
  * @return The UDP PCB which was created. NULL if the PCB data structure
  * could not be allocated.
@@ -1242,7 +1247,8 @@
  * Create a UDP PCB for specific IP type.
  * The pcb is not active until it has either been bound to a local address
  * or connected to a remote address.
- * 
+ * @see MEMP_NUM_UDP_PCB
+ *
  * @param type IP address type, see @ref lwip_ip_addr_type definitions.
  * If you want to listen to IPv4 and IPv6 (dual-stack) packets,
  * supply @ref IPADDR_TYPE_ANY as argument and bind to @ref IP_ANY_TYPE.
diff --git a/src/include/lwip/altcp.h b/src/include/lwip/altcp.h
index 97abc54..1b24544 100644
--- a/src/include/lwip/altcp.h
+++ b/src/include/lwip/altcp.h
@@ -129,6 +129,11 @@
 ip_addr_t *altcp_get_ip(struct altcp_pcb *conn, int local);
 u16_t altcp_get_port(struct altcp_pcb *conn, int local);
 
+#if LWIP_TCP_KEEPALIVE
+void  altcp_keepalive_disable(struct altcp_pcb *conn);
+void  altcp_keepalive_enable(struct altcp_pcb *conn, u32_t idle, u32_t intvl, u32_t count);
+#endif
+
 #ifdef LWIP_DEBUG
 enum tcp_state altcp_dbg_get_tcp_state(struct altcp_pcb *conn);
 #endif
diff --git a/src/include/lwip/altcp_tls.h b/src/include/lwip/altcp_tls.h
index 7b17c60..ff797f2 100644
--- a/src/include/lwip/altcp_tls.h
+++ b/src/include/lwip/altcp_tls.h
@@ -61,7 +61,22 @@
 struct altcp_tls_config;
 
 /** @ingroup altcp_tls
- * Create an ALTCP_TLS server configuration handle
+ * Create an ALTCP_TLS server configuration handle prepared for multiple certificates
+ */
+struct altcp_tls_config *altcp_tls_create_config_server(uint8_t cert_count);
+
+/** @ingroup altcp_tls
+ * Add a certificate to an ALTCP_TLS server configuration handle
+ */
+err_t altcp_tls_config_server_add_privkey_cert(struct altcp_tls_config *config,
+      const u8_t *privkey, size_t privkey_len,
+      const u8_t *privkey_pass, size_t privkey_pass_len,
+      const u8_t *cert, size_t cert_len);
+
+/** @ingroup altcp_tls
+ * Create an ALTCP_TLS server configuration handle with one certificate
+ * (short version of calling @ref altcp_tls_create_config_server and
+ * @ref altcp_tls_config_server_add_privkey_cert)
  */
 struct altcp_tls_config *altcp_tls_create_config_server_privkey_cert(const u8_t *privkey, size_t privkey_len,
                             const u8_t *privkey_pass, size_t privkey_pass_len,
@@ -85,6 +100,17 @@
 void altcp_tls_free_config(struct altcp_tls_config *conf);
 
 /** @ingroup altcp_tls
+ * Free an ALTCP_TLS global entropy instance.
+ * All ALTCP_TLS configuration are linked to one altcp_tls_entropy_rng structure
+ * that handle an unique system entropy & ctr_drbg instance.
+ * This function allow application to free this altcp_tls_entropy_rng structure
+ * when all configuration referencing it were destroyed.
+ * This function does nothing if some ALTCP_TLS configuration handle are still
+ * active.
+ */
+void altcp_tls_free_entropy(void);
+
+/** @ingroup altcp_tls
  * Create new ALTCP_TLS layer wrapping an existing pcb as inner connection (e.g. TLS over TCP)
  */
 struct altcp_pcb *altcp_tls_wrap(struct altcp_tls_config *config, struct altcp_pcb *inner_pcb);
diff --git a/src/include/lwip/apps/altcp_tls_mbedtls_opts.h b/src/include/lwip/apps/altcp_tls_mbedtls_opts.h
index 36cddd9..71aa599 100644
--- a/src/include/lwip/apps/altcp_tls_mbedtls_opts.h
+++ b/src/include/lwip/apps/altcp_tls_mbedtls_opts.h
@@ -55,11 +55,49 @@
 #define ALTCP_MBEDTLS_DEBUG                           LWIP_DBG_OFF
 #endif
 
-/** Set a session timeout in seconds for the basic session cache
+/** Configure lwIP debug level of the mbedTLS library */
+#ifndef ALTCP_MBEDTLS_LIB_DEBUG
+#define ALTCP_MBEDTLS_LIB_DEBUG                       LWIP_DBG_OFF
+#endif
+
+/** Configure minimum internal debug level of the mbedTLS library */
+#ifndef ALTCP_MBEDTLS_LIB_DEBUG_LEVEL_MIN
+#define ALTCP_MBEDTLS_LIB_DEBUG_LEVEL_MIN             0
+#endif
+
+/** Enable the basic session cache
  * ATTENTION: Using a session cache can lower security by reusing keys!
  */
+#ifndef ALTCP_MBEDTLS_USE_SESSION_CACHE
+#define ALTCP_MBEDTLS_USE_SESSION_CACHE               0
+#endif
+
+/** Maximum cache size of the basic session cache */
+#ifndef ALTCP_MBEDTLS_SESSION_CACHE_SIZE
+#define ALTCP_MBEDTLS_SESSION_CACHE_SIZE              30
+#endif
+
+/** Set a session timeout in seconds for the basic session cache  */
 #ifndef ALTCP_MBEDTLS_SESSION_CACHE_TIMEOUT_SECONDS
-#define ALTCP_MBEDTLS_SESSION_CACHE_TIMEOUT_SECONDS   0
+#define ALTCP_MBEDTLS_SESSION_CACHE_TIMEOUT_SECONDS   (60 * 60)
+#endif
+
+/** Use session tickets to speed up connection setup (needs
+ * MBEDTLS_SSL_SESSION_TICKETS enabled in mbedTLS config).
+ * ATTENTION: Using session tickets can lower security by reusing keys!
+ */
+#ifndef ALTCP_MBEDTLS_USE_SESSION_TICKETS
+#define ALTCP_MBEDTLS_USE_SESSION_TICKETS             0
+#endif
+
+/** Session ticket cipher */
+#ifndef ALTCP_MBEDTLS_SESSION_TICKET_CIPHER
+#define ALTCP_MBEDTLS_SESSION_TICKET_CIPHER           MBEDTLS_CIPHER_AES_256_GCM
+#endif
+
+/** Maximum timeout for session tickets */
+#ifndef ALTCP_MBEDTLS_SESSION_TICKET_TIMEOUT_SECONDS
+#define ALTCP_MBEDTLS_SESSION_TICKET_TIMEOUT_SECONDS  (60 * 60 * 24)
 #endif
 
 #endif /* LWIP_ALTCP */
diff --git a/src/include/lwip/apps/sntp.h b/src/include/lwip/apps/sntp.h
index 3c0f95f..c415253 100644
--- a/src/include/lwip/apps/sntp.h
+++ b/src/include/lwip/apps/sntp.h
@@ -67,11 +67,11 @@
 const char *sntp_getservername(u8_t idx);
 #endif /* SNTP_SERVER_DNS */
 
-#if SNTP_GET_SERVERS_FROM_DHCP
+#if SNTP_GET_SERVERS_FROM_DHCP || SNTP_GET_SERVERS_FROM_DHCPV6
 void sntp_servermode_dhcp(int set_servers_from_dhcp);
-#else /* SNTP_GET_SERVERS_FROM_DHCP */
+#else /* SNTP_GET_SERVERS_FROM_DHCP || SNTP_GET_SERVERS_FROM_DHCPV6 */
 #define sntp_servermode_dhcp(x)
-#endif /* SNTP_GET_SERVERS_FROM_DHCP */
+#endif /* SNTP_GET_SERVERS_FROM_DHCP || SNTP_GET_SERVERS_FROM_DHCPV6 */
 
 #ifdef __cplusplus
 }
diff --git a/src/include/lwip/apps/sntp_opts.h b/src/include/lwip/apps/sntp_opts.h
index ed98040..cb62771 100644
--- a/src/include/lwip/apps/sntp_opts.h
+++ b/src/include/lwip/apps/sntp_opts.h
@@ -67,6 +67,12 @@
 #define SNTP_GET_SERVERS_FROM_DHCP LWIP_DHCP_GET_NTP_SRV
 #endif
 
+/** Set this to 1 to implement the callback function called by dhcpv6 when
+ * NTP servers are received. */
+#if !defined SNTP_GET_SERVERS_FROM_DHCPV6 || defined __DOXYGEN__
+#define SNTP_GET_SERVERS_FROM_DHCPV6 LWIP_DHCP6_GET_NTP_SRV
+#endif
+
 /** Set this to 1 to support DNS names (or IP address strings) to set sntp servers
  * One server address/name can be defined as default if SNTP_SERVER_DNS == 1:
  * \#define SNTP_SERVER_ADDRESS "pool.ntp.org"
diff --git a/src/include/lwip/debug.h b/src/include/lwip/debug.h
index baa6a40..579fd24 100644
--- a/src/include/lwip/debug.h
+++ b/src/include/lwip/debug.h
@@ -120,9 +120,7 @@
 #endif /* LWIP_NOASSERT */
 
 #ifndef LWIP_ERROR
-#ifndef LWIP_NOASSERT
-#define LWIP_PLATFORM_ERROR(message) LWIP_PLATFORM_ASSERT(message)
-#elif defined LWIP_DEBUG
+#ifdef LWIP_DEBUG
 #define LWIP_PLATFORM_ERROR(message) LWIP_PLATFORM_DIAG((message))
 #else
 #define LWIP_PLATFORM_ERROR(message)
diff --git a/src/include/lwip/if_api.h b/src/include/lwip/if_api.h
index 39017ab..b7269e2 100644
--- a/src/include/lwip/if_api.h
+++ b/src/include/lwip/if_api.h
@@ -49,7 +49,9 @@
 extern "C" {
 #endif
 
+#ifndef IF_NAMESIZE
 #define IF_NAMESIZE NETIF_NAMESIZE
+#endif
 
 char * lwip_if_indextoname(unsigned int ifindex, char *ifname);
 unsigned int lwip_if_nametoindex(const char *ifname);
diff --git a/src/include/lwip/init.h b/src/include/lwip/init.h
index a149be1..6cabfc8 100644
--- a/src/include/lwip/init.h
+++ b/src/include/lwip/init.h
@@ -54,7 +54,7 @@
 /** x.X.x: Minor version of the stack */
 #define LWIP_VERSION_MINOR      1
 /** x.x.X: Revision of the stack */
-#define LWIP_VERSION_REVISION   2
+#define LWIP_VERSION_REVISION   3
 /** For release candidates, this is set to 1..254
   * For official releases, this is set to 255 (LWIP_RC_RELEASE)
   * For development versions (Git), this is set to 0 (LWIP_RC_DEVELOPMENT) */
diff --git a/src/include/lwip/netif.h b/src/include/lwip/netif.h
index 911196a..9a16ded 100644
--- a/src/include/lwip/netif.h
+++ b/src/include/lwip/netif.h
@@ -386,6 +386,10 @@
 #if LWIP_LOOPBACK_MAX_PBUFS
   u16_t loop_cnt_current;
 #endif /* LWIP_LOOPBACK_MAX_PBUFS */
+#if LWIP_NETIF_LOOPBACK_MULTITHREADING
+  /* Used if the original scheduling failed. */
+  u8_t reschedule_poll;
+#endif /* LWIP_NETIF_LOOPBACK_MULTITHREADING */
 #endif /* ENABLE_LOOPBACK */
 };
 
@@ -451,7 +455,7 @@
 
 #define netif_set_flags(netif, set_flags)     do { (netif)->flags = (u8_t)((netif)->flags |  (set_flags)); } while(0)
 #define netif_clear_flags(netif, clr_flags)   do { (netif)->flags = (u8_t)((netif)->flags & (u8_t)(~(clr_flags) & 0xff)); } while(0)
-#define netif_is_flag_set(nefif, flag)        (((netif)->flags & (flag)) != 0)
+#define netif_is_flag_set(netif, flag)        (((netif)->flags & (flag)) != 0)
 
 void netif_set_up(struct netif *netif);
 void netif_set_down(struct netif *netif);
diff --git a/src/include/lwip/opt.h b/src/include/lwip/opt.h
index 82c420c..d8c82d1 100644
--- a/src/include/lwip/opt.h
+++ b/src/include/lwip/opt.h
@@ -1545,7 +1545,7 @@
  * TCP_MSS, IP header, and link header.
  */
 #if !defined PBUF_POOL_BUFSIZE || defined __DOXYGEN__
-#define PBUF_POOL_BUFSIZE               LWIP_MEM_ALIGN_SIZE(TCP_MSS+40+PBUF_LINK_ENCAPSULATION_HLEN+PBUF_LINK_HLEN)
+#define PBUF_POOL_BUFSIZE               LWIP_MEM_ALIGN_SIZE(TCP_MSS+PBUF_IP_HLEN+PBUF_TRANSPORT_HLEN+PBUF_LINK_ENCAPSULATION_HLEN+PBUF_LINK_HLEN)
 #endif
 
 /**
@@ -1555,6 +1555,14 @@
 #if !defined LWIP_PBUF_REF_T || defined __DOXYGEN__
 #define LWIP_PBUF_REF_T                 u8_t
 #endif
+
+/**
+ * LWIP_PBUF_CUSTOM_DATA: Store private data on pbufs (e.g. timestamps)
+ * This extends struct pbuf so user can store custom data on every pbuf.
+ */
+#if !defined LWIP_PBUF_CUSTOM_DATA || defined __DOXYGEN__
+#define LWIP_PBUF_CUSTOM_DATA
+#endif
 /**
  * @}
  */
@@ -1912,11 +1920,8 @@
 
 /** LWIP_NETCONN_FULLDUPLEX==1: Enable code that allows reading from one thread,
  * writing from a 2nd thread and closing from a 3rd thread at the same time.
- * ATTENTION: This is currently really alpha! Some requirements:
- * - LWIP_NETCONN_SEM_PER_THREAD==1 is required to use one socket/netconn from
- *   multiple threads at once
- * - sys_mbox_free() has to unblock receive tasks waiting on recvmbox/acceptmbox
- *   and prevent a task pending on this during/after deletion
+ * LWIP_NETCONN_SEM_PER_THREAD==1 is required to use one socket/netconn from
+ * multiple threads at once!
  */
 #if !defined LWIP_NETCONN_FULLDUPLEX || defined __DOXYGEN__
 #define LWIP_NETCONN_FULLDUPLEX         0
@@ -2446,7 +2451,7 @@
  * network startup.
  */
 #if !defined LWIP_IPV6_SEND_ROUTER_SOLICIT || defined __DOXYGEN__
-#define LWIP_IPV6_SEND_ROUTER_SOLICIT   1
+#define LWIP_IPV6_SEND_ROUTER_SOLICIT   LWIP_IPV6
 #endif
 
 /**
@@ -2491,10 +2496,12 @@
 
 /**
  * LWIP_ICMP6_DATASIZE: bytes from original packet to send back in
- * ICMPv6 error messages.
+ * ICMPv6 error messages (0 = default of IP6_MIN_MTU_LENGTH)
+ * ATTENTION: RFC4443 section 2.4 says IP6_MIN_MTU_LENGTH is a MUST,
+ * so override this only if you absolutely have to!
  */
 #if !defined LWIP_ICMP6_DATASIZE || defined __DOXYGEN__
-#define LWIP_ICMP6_DATASIZE             8
+#define LWIP_ICMP6_DATASIZE             0
 #endif
 
 /**
diff --git a/src/include/lwip/pbuf.h b/src/include/lwip/pbuf.h
index 82902a4..e5daf96 100644
--- a/src/include/lwip/pbuf.h
+++ b/src/include/lwip/pbuf.h
@@ -219,6 +219,9 @@
 
   /** For incoming packets, this contains the input netif's index */
   u8_t if_idx;
+
+  /** In case the user needs to store data custom data on a pbuf */
+  LWIP_PBUF_CUSTOM_DATA
 };
 
 
@@ -293,6 +296,7 @@
 void pbuf_chain(struct pbuf *head, struct pbuf *tail);
 struct pbuf *pbuf_dechain(struct pbuf *p);
 err_t pbuf_copy(struct pbuf *p_to, const struct pbuf *p_from);
+err_t pbuf_copy_partial_pbuf(struct pbuf *p_to, const struct pbuf *p_from, u16_t copy_len, u16_t offset);
 u16_t pbuf_copy_partial(const struct pbuf *p, void *dataptr, u16_t len, u16_t offset);
 void *pbuf_get_contiguous(const struct pbuf *p, void *buffer, size_t bufsize, u16_t len, u16_t offset);
 err_t pbuf_take(struct pbuf *buf, const void *dataptr, u16_t len);
diff --git a/src/include/lwip/priv/altcp_priv.h b/src/include/lwip/priv/altcp_priv.h
index 2d3b2fd..d1de9b1 100644
--- a/src/include/lwip/priv/altcp_priv.h
+++ b/src/include/lwip/priv/altcp_priv.h
@@ -85,6 +85,11 @@
 typedef ip_addr_t *(*altcp_get_ip_fn)(struct altcp_pcb *conn, int local);
 typedef u16_t (*altcp_get_port_fn)(struct altcp_pcb *conn, int local);
 
+#if LWIP_TCP_KEEPALIVE
+typedef void  (*altcp_keepalive_disable_fn)(struct altcp_pcb *conn);
+typedef void  (*altcp_keepalive_enable_fn)(struct altcp_pcb *conn, u32_t idle, u32_t intvl, u32_t count);
+#endif
+
 #ifdef LWIP_DEBUG
 typedef enum tcp_state (*altcp_dbg_get_tcp_state_fn)(struct altcp_pcb *conn);
 #endif
@@ -111,6 +116,10 @@
   altcp_get_tcp_addrinfo_fn   addrinfo;
   altcp_get_ip_fn             getip;
   altcp_get_port_fn           getport;
+#if LWIP_TCP_KEEPALIVE
+  altcp_keepalive_disable_fn  keepalive_disable;
+  altcp_keepalive_enable_fn   keepalive_enable;
+#endif
 #ifdef LWIP_DEBUG
   altcp_dbg_get_tcp_state_fn  dbg_get_tcp_state;
 #endif
@@ -133,6 +142,10 @@
 err_t altcp_default_get_tcp_addrinfo(struct altcp_pcb *conn, int local, ip_addr_t *addr, u16_t *port);
 ip_addr_t *altcp_default_get_ip(struct altcp_pcb *conn, int local);
 u16_t altcp_default_get_port(struct altcp_pcb *conn, int local);
+#if LWIP_TCP_KEEPALIVE
+void  altcp_default_keepalive_disable(struct altcp_pcb *conn);
+void  altcp_default_keepalive_enable(struct altcp_pcb *conn, u32_t idle, u32_t intvl, u32_t count);
+#endif
 #ifdef LWIP_DEBUG
 enum tcp_state altcp_default_dbg_get_tcp_state(struct altcp_pcb *conn);
 #endif
diff --git a/src/include/lwip/prot/icmp6.h b/src/include/lwip/prot/icmp6.h
index 3461120..36989f6 100644
--- a/src/include/lwip/prot/icmp6.h
+++ b/src/include/lwip/prot/icmp6.h
@@ -146,6 +146,8 @@
 #  include "arch/epstruct.h"
 #endif
 
+#define ICMP6_HLEN 8
+
 /** This is the ICMP6 header adapted for echo req/resp. */
 #ifdef PACK_STRUCT_USE_INCLUDES
 #  include "arch/bpstruct.h"
diff --git a/src/include/lwip/prot/ip6.h b/src/include/lwip/prot/ip6.h
index 0f6de45..7df81ed 100644
--- a/src/include/lwip/prot/ip6.h
+++ b/src/include/lwip/prot/ip6.h
@@ -44,6 +44,8 @@
 extern "C" {
 #endif
 
+#define IP6_MIN_MTU_LENGTH 1280
+
 /** This is the packed version of ip6_addr_t,
     used in network headers that are itself packed */
 #ifdef PACK_STRUCT_USE_INCLUDES
diff --git a/src/include/netif/ppp/ppp_opts.h b/src/include/netif/ppp/ppp_opts.h
index 6702bec..479a006 100644
--- a/src/include/netif/ppp/ppp_opts.h
+++ b/src/include/netif/ppp/ppp_opts.h
@@ -45,6 +45,13 @@
 #endif
 
 /**
+ * PPPOE_SCNAME_SUPPORT==1: Enable PPP Over Ethernet Service Name and Concentrator Name support
+ */
+#ifndef PPPOE_SCNAME_SUPPORT
+#define PPPOE_SCNAME_SUPPORT            0
+#endif
+
+/**
  * PPPOL2TP_SUPPORT==1: Enable PPP Over L2TP
  */
 #ifndef PPPOL2TP_SUPPORT
diff --git a/src/include/netif/ppp/pppoe.h b/src/include/netif/ppp/pppoe.h
index 08ab7ab..8994d38 100644
--- a/src/include/netif/ppp/pppoe.h
+++ b/src/include/netif/ppp/pppoe.h
@@ -149,10 +149,10 @@
   u16_t sc_session;            /* PPPoE session id */
   u8_t sc_state;               /* discovery phase or session connected */
 
-#ifdef PPPOE_TODO
-  u8_t *sc_service_name;       /* if != NULL: requested name of service */
-  u8_t *sc_concentrator_name;  /* if != NULL: requested concentrator id */
-#endif /* PPPOE_TODO */
+#if PPPOE_SCNAME_SUPPORT
+  const char *sc_service_name;      /* if != NULL: requested name of service */
+  const char *sc_concentrator_name; /* if != NULL: requested concentrator id */
+#endif /* PPPOE_SCNAME_SUPPORT */
   u8_t sc_ac_cookie[PPPOE_MAX_AC_COOKIE_LEN]; /* content of AC cookie we must echo back */
   u8_t sc_ac_cookie_len;       /* length of cookie data */
 #ifdef PPPOE_SERVER
diff --git a/src/netif/lowpan6.c b/src/netif/lowpan6.c
index 7f0d276..5e6f009 100644
--- a/src/netif/lowpan6.c
+++ b/src/netif/lowpan6.c
@@ -881,7 +881,7 @@
   MIB2_INIT_NETIF(netif, snmp_ifType_other, 0);
 
   /* maximum transfer unit */
-  netif->mtu = 1280;
+  netif->mtu = IP6_MIN_MTU_LENGTH;
 
   /* broadcast capability */
   netif->flags = NETIF_FLAG_BROADCAST /* | NETIF_FLAG_LOWPAN6 */;
diff --git a/src/netif/lowpan6_ble.c b/src/netif/lowpan6_ble.c
index d89816d..6de0ae3 100644
--- a/src/netif/lowpan6_ble.c
+++ b/src/netif/lowpan6_ble.c
@@ -417,7 +417,7 @@
   MIB2_INIT_NETIF(netif, snmp_ifType_other, 0);
 
   /* maximum transfer unit, set according to RFC7668 ch2.4 */
-  netif->mtu = 1280;
+  netif->mtu = IP6_MIN_MTU_LENGTH;
 
   /* no flags set (no broadcast, ethernet,...)*/
   netif->flags = 0;
diff --git a/src/netif/lowpan6_common.c b/src/netif/lowpan6_common.c
index baea206..4db1ebb 100644
--- a/src/netif/lowpan6_common.c
+++ b/src/netif/lowpan6_common.c
@@ -440,7 +440,7 @@
   if ((lowpan6_buffer[0] & 0x18) == 0x00) {
     header_temp = ((lowpan6_buffer[lowpan6_offset+1] & 0x0f) << 16) | \
       (lowpan6_buffer[lowpan6_offset + 2] << 8) | lowpan6_buffer[lowpan6_offset+3];
-    LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("TF: 00, ECN: 0x%2x, Flowlabel+DSCP: 0x%8X\n", \
+    LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("TF: 00, ECN: 0x%"X8_F", Flowlabel+DSCP: 0x%8"X32_F"\n", \
       lowpan6_buffer[lowpan6_offset],header_temp));
     IP6H_VTCFL_SET(ip6hdr, 6, lowpan6_buffer[lowpan6_offset], header_temp);
     /* increase offset, processed 4 bytes here:
@@ -448,14 +448,14 @@
     lowpan6_offset += 4;
   } else if ((lowpan6_buffer[0] & 0x18) == 0x08) {
     header_temp = ((lowpan6_buffer[lowpan6_offset] & 0x0f) << 16) | (lowpan6_buffer[lowpan6_offset + 1] << 8) | lowpan6_buffer[lowpan6_offset+2];
-    LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("TF: 01, ECN: 0x%2x, Flowlabel: 0x%2X, DSCP ignored\n", \
+    LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("TF: 01, ECN: 0x%"X8_F", Flowlabel: 0x%2"X32_F", DSCP ignored\n", \
       lowpan6_buffer[lowpan6_offset] & 0xc0,header_temp));
     IP6H_VTCFL_SET(ip6hdr, 6, lowpan6_buffer[lowpan6_offset] & 0xc0, header_temp);
     /* increase offset, processed 3 bytes here:
      * TF=01:  ECN + 2-bit Pad + Flow Label (3 bytes), DSCP is elided.*/
     lowpan6_offset += 3;
   } else if ((lowpan6_buffer[0] & 0x18) == 0x10) {
-    LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("TF: 10, DCSP+ECN: 0x%2x, Flowlabel ignored\n", lowpan6_buffer[lowpan6_offset]));
+    LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("TF: 10, DCSP+ECN: 0x%"X8_F", Flowlabel ignored\n", lowpan6_buffer[lowpan6_offset]));
     IP6H_VTCFL_SET(ip6hdr, 6, lowpan6_buffer[lowpan6_offset],0);
     /* increase offset, processed 1 byte here:
      * ECN + DSCP (1 byte), Flow Label is elided.*/
@@ -564,7 +564,7 @@
 #if LWIP_6LOWPAN_NUM_CONTEXTS > 0
       ip6hdr->src.addr[0] = lowpan6_contexts[i].addr[0];
       ip6hdr->src.addr[1] = lowpan6_contexts[i].addr[1];
-      LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("SAM == xx, context compression found @%d: %8X, %8X\n", (int)i, ip6hdr->src.addr[0], ip6hdr->src.addr[1]));
+      LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("SAM == xx, context compression found @%d: %8"X32_F", %8"X32_F"\n", (int)i, ip6hdr->src.addr[0], ip6hdr->src.addr[1]));
 #else
       LWIP_UNUSED_ARG(lowpan6_contexts);
 #endif
diff --git a/src/netif/ppp/ppp.c b/src/netif/ppp/ppp.c
index a9c18e3..be58553 100644
--- a/src/netif/ppp/ppp.c
+++ b/src/netif/ppp/ppp.c
@@ -216,7 +216,8 @@
 /***********************************/
 #if PPP_AUTH_SUPPORT
 void ppp_set_auth(ppp_pcb *pcb, u8_t authtype, const char *user, const char *passwd) {
-  LWIP_ASSERT_CORE_LOCKED();
+  LWIP_ASSERT("pcb->phase == PPP_PHASE_DEAD", pcb->phase == PPP_PHASE_DEAD);
+
 #if PAP_SUPPORT
   pcb->settings.refuse_pap = !(authtype & PPPAUTHTYPE_PAP);
 #endif /* PAP_SUPPORT */
@@ -238,6 +239,8 @@
 #if MPPE_SUPPORT
 /* Set MPPE configuration */
 void ppp_set_mppe(ppp_pcb *pcb, u8_t flags) {
+  LWIP_ASSERT("pcb->phase == PPP_PHASE_DEAD", pcb->phase == PPP_PHASE_DEAD);
+
   if (flags == PPP_MPPE_DISABLE) {
     pcb->settings.require_mppe = 0;
     return;
diff --git a/src/netif/ppp/pppoe.c b/src/netif/ppp/pppoe.c
index 8ed2d63..971b36b 100644
--- a/src/netif/ppp/pppoe.c
+++ b/src/netif/ppp/pppoe.c
@@ -175,8 +175,10 @@
 {
   ppp_pcb *ppp;
   struct pppoe_softc *sc;
+#if !PPPOE_SCNAME_SUPPORT
   LWIP_UNUSED_ARG(service_name);
   LWIP_UNUSED_ARG(concentrator_name);
+#endif /* !PPPOE_SCNAME_SUPPORT */
   LWIP_ASSERT_CORE_LOCKED();
 
   sc = (struct pppoe_softc *)LWIP_MEMPOOL_ALLOC(PPPOE_IF);
@@ -193,6 +195,10 @@
   memset(sc, 0, sizeof(struct pppoe_softc));
   sc->pcb = ppp;
   sc->sc_ethif = ethif;
+#if PPPOE_SCNAME_SUPPORT
+  sc->sc_service_name = service_name;
+  sc->sc_concentrator_name = concentrator_name;
+#endif /* PPPOE_SCNAME_SUPPORT */
   /* put the new interface at the head of the list */
   sc->next = pppoe_softc_list;
   pppoe_softc_list = sc;
@@ -300,15 +306,6 @@
        break;
     }
   }
-
-#ifdef PPPOE_TODO
-  if (sc->sc_concentrator_name) {
-    mem_free(sc->sc_concentrator_name);
-  }
-  if (sc->sc_service_name) {
-    mem_free(sc->sc_service_name);
-  }
-#endif /* PPPOE_TODO */
   LWIP_MEMPOOL_FREE(PPPOE_IF, sc);
 
   return ERR_OK;
@@ -757,13 +754,13 @@
   struct pbuf *pb;
   u8_t *p;
   int len;
-#ifdef PPPOE_TODO
+#if PPPOE_SCNAME_SUPPORT
   int l1 = 0, l2 = 0; /* XXX: gcc */
-#endif /* PPPOE_TODO */
+#endif /* PPPOE_SCNAME_SUPPORT */
 
   /* calculate length of frame (excluding ethernet header + pppoe header) */
   len = 2 + 2 + 2 + 2 + sizeof sc;  /* service name tag is required, host unique is send too */
-#ifdef PPPOE_TODO
+#if PPPOE_SCNAME_SUPPORT
   if (sc->sc_service_name != NULL) {
     l1 = (int)strlen(sc->sc_service_name);
     len += l1;
@@ -772,7 +769,7 @@
     l2 = (int)strlen(sc->sc_concentrator_name);
     len += 2 + 2 + l2;
   }
-#endif /* PPPOE_TODO */
+#endif /* PPPOE_SCNAME_SUPPORT */
   LWIP_ASSERT("sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len <= 0xffff",
     sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len <= 0xffff);
 
@@ -787,24 +784,24 @@
   /* fill in pkt */
   PPPOE_ADD_HEADER(p, PPPOE_CODE_PADI, 0, (u16_t)len);
   PPPOE_ADD_16(p, PPPOE_TAG_SNAME);
-#ifdef PPPOE_TODO
+#if PPPOE_SCNAME_SUPPORT
   if (sc->sc_service_name != NULL) {
     PPPOE_ADD_16(p, l1);
     MEMCPY(p, sc->sc_service_name, l1);
     p += l1;
   } else
-#endif /* PPPOE_TODO */
+#endif /* PPPOE_SCNAME_SUPPORT */
   {
     PPPOE_ADD_16(p, 0);
   }
-#ifdef PPPOE_TODO
+#if PPPOE_SCNAME_SUPPORT
   if (sc->sc_concentrator_name != NULL) {
     PPPOE_ADD_16(p, PPPOE_TAG_ACNAME);
     PPPOE_ADD_16(p, l2);
     MEMCPY(p, sc->sc_concentrator_name, l2);
     p += l2;
   }
-#endif /* PPPOE_TODO */
+#endif /* PPPOE_SCNAME_SUPPORT */
   PPPOE_ADD_16(p, PPPOE_TAG_HUNIQUE);
   PPPOE_ADD_16(p, sizeof(sc));
   MEMCPY(p, &sc, sizeof sc);
@@ -982,17 +979,17 @@
   struct pbuf *pb;
   u8_t *p;
   size_t len;
-#ifdef PPPOE_TODO
+#if PPPOE_SCNAME_SUPPORT
   size_t l1 = 0; /* XXX: gcc */
-#endif /* PPPOE_TODO */
+#endif /* PPPOE_SCNAME_SUPPORT */
 
   len = 2 + 2 + 2 + 2 + sizeof(sc);    /* service name, host unique */
-#ifdef PPPOE_TODO
+#if PPPOE_SCNAME_SUPPORT
   if (sc->sc_service_name != NULL) {    /* service name tag maybe empty */
     l1 = strlen(sc->sc_service_name);
     len += l1;
   }
-#endif /* PPPOE_TODO */
+#endif /* PPPOE_SCNAME_SUPPORT */
   if (sc->sc_ac_cookie_len > 0) {
     len += 2 + 2 + sc->sc_ac_cookie_len;  /* AC cookie */
   }
@@ -1006,13 +1003,13 @@
   p = (u8_t*)pb->payload;
   PPPOE_ADD_HEADER(p, PPPOE_CODE_PADR, 0, len);
   PPPOE_ADD_16(p, PPPOE_TAG_SNAME);
-#ifdef PPPOE_TODO
+#if PPPOE_SCNAME_SUPPORT
   if (sc->sc_service_name != NULL) {
     PPPOE_ADD_16(p, l1);
     MEMCPY(p, sc->sc_service_name, l1);
     p += l1;
   } else
-#endif /* PPPOE_TODO */
+#endif /* PPPOE_SCNAME_SUPPORT */
   {
     PPPOE_ADD_16(p, 0);
   }
diff --git a/src/netif/zepif.c b/src/netif/zepif.c
index b403303..de43b99 100644
--- a/src/netif/zepif.c
+++ b/src/netif/zepif.c
@@ -201,7 +201,7 @@
   state->seqno++;
   zep->len = (u8_t)p->tot_len;
 
-  err = pbuf_take_at(q, p->payload, p->tot_len, sizeof(struct zep_hdr));
+  err = pbuf_copy_partial_pbuf(q, p, p->tot_len, sizeof(struct zep_hdr));
   if (err == ERR_OK) {
 #if ZEPIF_LOOPBACK
     zepif_udp_recv(netif, state->pcb, pbuf_clone(PBUF_RAW, PBUF_RAM, q), NULL, 0);
diff --git a/st_readme.txt b/st_readme.txt
index 19f76bb..52611b2 100644
--- a/st_readme.txt
+++ b/st_readme.txt
@@ -46,6 +46,18 @@
   *
   ******************************************************************************
   @endverbatim
+
+### 18-August-2023 ###
+========================
+  + Add LICENSE.md file at the root directory.
+
+### 19-May-2023 ###
+========================
+  + Upgrade to use LwIP V2.1.3 version
+     - For more details about new features and bug fixes please refer to CHANGELOG.txt and UPGRADING files.
+  + sys_arch.c:
+     - Remove support of the cmsis_os1 API and keep only cmsis_os2.
+
 ### 15-March-2019 ###
 ========================
   + Upgrade to use LwIP V2.1.2 version
diff --git a/system/OS/sys_arch.c b/system/OS/sys_arch.c
index 65d9bf9..f7e3ff0 100644
--- a/system/OS/sys_arch.c
+++ b/system/OS/sys_arch.c
@@ -39,9 +39,9 @@
 
 #if !NO_SYS
 
-#include "cmsis_os.h"
+#include "cmsis_os2.h"
 
-#if defined(LWIP_PROVIDE_ERRNO)
+#if (defined (__CC_ARM) || defined (__ARMCC_VERSION) || defined (__ICCARM__))
 int errno;
 #endif
 
@@ -49,12 +49,8 @@
 //  Creates an empty mailbox.
 err_t sys_mbox_new(sys_mbox_t *mbox, int size)
 {
-#if (osCMSIS < 0x20000U)
-  osMessageQDef(QUEUE, size, void *);
-  *mbox = osMessageCreate(osMessageQ(QUEUE), NULL);
-#else
   *mbox = osMessageQueueNew(size, sizeof(void *), NULL);
-#endif
+
 #if SYS_STATS
   ++lwip_stats.sys.mbox.used;
   if(lwip_stats.sys.mbox.max < lwip_stats.sys.mbox.used)
@@ -76,24 +72,15 @@
 */
 void sys_mbox_free(sys_mbox_t *mbox)
 {
-#if (osCMSIS < 0x20000U)
-  if(osMessageWaiting(*mbox))
-#else
   if(osMessageQueueGetCount(*mbox))
-#endif
   {
     /* Line for breakpoint.  Should never break here! */
-    portNOP();
 #if SYS_STATS
     lwip_stats.sys.mbox.err++;
 #endif /* SYS_STATS */
 
   }
-#if (osCMSIS < 0x20000U)
-  osMessageDelete(*mbox);
-#else
   osMessageQueueDelete(*mbox);
-#endif
 #if SYS_STATS
   --lwip_stats.sys.mbox.used;
 #endif /* SYS_STATS */
@@ -103,11 +90,7 @@
 //   Posts the "msg" to the mailbox.
 void sys_mbox_post(sys_mbox_t *mbox, void *data)
 {
-#if (osCMSIS < 0x20000U)
-  while(osMessagePut(*mbox, (uint32_t)data, osWaitForever) != osOK);
-#else
   while(osMessageQueuePut(*mbox, &data, 0, osWaitForever) != osOK);
-#endif
 }
 
 
@@ -116,11 +99,7 @@
 err_t sys_mbox_trypost(sys_mbox_t *mbox, void *msg)
 {
   err_t result;
-#if (osCMSIS < 0x20000U)
-  if(osMessagePut(*mbox, (uint32_t)msg, 0) == osOK)
-#else
   if(osMessageQueuePut(*mbox, &msg, 0, 0) == osOK)
-#endif
   {
     result = ERR_OK;
   }
@@ -163,30 +142,16 @@
 */
 u32_t sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout)
 {
-#if (osCMSIS < 0x20000U)
-  osEvent event;
-  uint32_t starttime = osKernelSysTick();
-#else
   osStatus_t status;
   uint32_t starttime = osKernelGetTickCount();
-#endif
+
   if(timeout != 0)
   {
-#if (osCMSIS < 0x20000U)
-    event = osMessageGet (*mbox, timeout);
-
-    if(event.status == osEventMessage)
-    {
-      *msg = (void *)event.value.v;
-      return (osKernelSysTick() - starttime);
-    }
-#else
     status = osMessageQueueGet(*mbox, msg, 0, timeout);
     if (status == osOK)
     {
       return (osKernelGetTickCount() - starttime);
     }
-#endif
     else
     {
       return SYS_ARCH_TIMEOUT;
@@ -194,14 +159,8 @@
   }
   else
   {
-#if (osCMSIS < 0x20000U)
-    event = osMessageGet (*mbox, osWaitForever);
-    *msg = (void *)event.value.v;
-    return (osKernelSysTick() - starttime);
-#else
     osMessageQueueGet(*mbox, msg, 0, osWaitForever );
     return (osKernelGetTickCount() - starttime);
-#endif
   }
 }
 
@@ -212,18 +171,8 @@
 */
 u32_t sys_arch_mbox_tryfetch(sys_mbox_t *mbox, void **msg)
 {
-#if (osCMSIS < 0x20000U)
-  osEvent event;
-
-  event = osMessageGet (*mbox, 0);
-
-  if(event.status == osEventMessage)
-  {
-    *msg = (void *)event.value.v;
-#else
   if (osMessageQueueGet(*mbox, msg, 0, 0) == osOK)
   {
-#endif
     return ERR_OK;
   }
   else
@@ -250,12 +199,7 @@
 //  the initial state of the semaphore.
 err_t sys_sem_new(sys_sem_t *sem, u8_t count)
 {
-#if (osCMSIS < 0x20000U)
-  osSemaphoreDef(SEM);
-  *sem = osSemaphoreCreate (osSemaphore(SEM), 1);
-#else
   *sem = osSemaphoreNew(UINT16_MAX, count, NULL);
-#endif
 
   if(*sem == NULL)
   {
@@ -267,11 +211,7 @@
 
   if(count == 0)	// Means it can't be taken
   {
-#if (osCMSIS < 0x20000U)
-    osSemaphoreWait(*sem, 0);
-#else
     osSemaphoreAcquire(*sem, 0);
-#endif
   }
 
 #if SYS_STATS
@@ -302,22 +242,13 @@
 */
 u32_t sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout)
 {
-#if (osCMSIS < 0x20000U)
-  uint32_t starttime = osKernelSysTick();
-#else
   uint32_t starttime = osKernelGetTickCount();
-#endif
+
   if(timeout != 0)
   {
-#if (osCMSIS < 0x20000U)
-    if(osSemaphoreWait (*sem, timeout) == osOK)
-    {
-      return (osKernelSysTick() - starttime);
-#else
     if(osSemaphoreAcquire(*sem, timeout) == osOK)
     {
         return (osKernelGetTickCount() - starttime);
-#endif
     }
     else
     {
@@ -326,13 +257,8 @@
   }
   else
   {
-#if (osCMSIS < 0x20000U)
-    while(osSemaphoreWait (*sem, osWaitForever) != osOK);
-    return (osKernelSysTick() - starttime);
-#else
     while(osSemaphoreAcquire(*sem, osWaitForever) != osOK);
     return (osKernelGetTickCount() - starttime);
-#endif
   }
 }
 
@@ -369,20 +295,12 @@
 }
 
 /*-----------------------------------------------------------------------------------*/
-#if (osCMSIS < 0x20000U)
-osMutexId lwip_sys_mutex;
-osMutexDef(lwip_sys_mutex);
-#else
 osMutexId_t lwip_sys_mutex;
-#endif
+
 // Initialize sys arch
 void sys_init(void)
 {
-#if (osCMSIS < 0x20000U)
-  lwip_sys_mutex = osMutexCreate(osMutex(lwip_sys_mutex));
-#else
   lwip_sys_mutex = osMutexNew(NULL);
-#endif
 }
 /*-----------------------------------------------------------------------------------*/
                                       /* Mutexes*/
@@ -392,12 +310,7 @@
 /* Create a new mutex*/
 err_t sys_mutex_new(sys_mutex_t *mutex) {
 
-#if (osCMSIS < 0x20000U)
-  osMutexDef(MUTEX);
-  *mutex = osMutexCreate(osMutex(MUTEX));
-#else
   *mutex = osMutexNew(NULL);
-#endif
 
   if(*mutex == NULL)
   {
@@ -429,11 +342,7 @@
 /* Lock a mutex*/
 void sys_mutex_lock(sys_mutex_t *mutex)
 {
-#if (osCMSIS < 0x20000U)
-  osMutexWait(*mutex, osWaitForever);
-#else
   osMutexAcquire(*mutex, osWaitForever);
-#endif
 }
 
 /*-----------------------------------------------------------------------------------*/
@@ -454,17 +363,12 @@
 */
 sys_thread_t sys_thread_new(const char *name, lwip_thread_fn thread , void *arg, int stacksize, int prio)
 {
-#if (osCMSIS < 0x20000U)
-  const osThreadDef_t os_thread_def = { (char *)name, (os_pthread)thread, (osPriority)prio, 0, stacksize};
-  return osThreadCreate(&os_thread_def, arg);
-#else
   const osThreadAttr_t attributes = {
                         .name = name,
                         .stack_size = stacksize,
                         .priority = (osPriority_t)prio,
                       };
   return osThreadNew(thread, arg, &attributes);
-#endif
 }
 
 /*
@@ -485,11 +389,8 @@
 */
 sys_prot_t sys_arch_protect(void)
 {
-#if (osCMSIS < 0x20000U)
-  osMutexWait(lwip_sys_mutex, osWaitForever);
-#else
   osMutexAcquire(lwip_sys_mutex, osWaitForever);
-#endif
+
   return (sys_prot_t)1;
 }
 
diff --git a/system/arch/cc.h b/system/arch/cc.h
index 3065318..3a5e95f 100644
--- a/system/arch/cc.h
+++ b/system/arch/cc.h
@@ -40,7 +40,7 @@
 
 #define LWIP_PROVIDE_ERRNO
 
-#if defined (__GNUC__) & !defined (__CC_ARM)
+#if defined (__GNUC__) && !(defined (__CC_ARM) || defined (__ARMCC_VERSION))
 
 #define LWIP_TIMEVAL_PRIVATE 0
 #include <sys/time.h>
diff --git a/system/arch/sys_arch.h b/system/arch/sys_arch.h
index 49bfd4e..d02cf6f 100644
--- a/system/arch/sys_arch.h
+++ b/system/arch/sys_arch.h
@@ -38,23 +38,12 @@
 #error "NO_SYS need to be set to 0 to use threaded API"
 #endif
 
-#include "cmsis_os.h"
+#include "cmsis_os2.h"
 
 #ifdef  __cplusplus
 extern "C" {
 #endif
 
-#if (osCMSIS < 0x20000U)
-
-#define SYS_MBOX_NULL (osMessageQId)0
-#define SYS_SEM_NULL  (osSemaphoreId)0
-
-typedef osSemaphoreId sys_sem_t;
-typedef osSemaphoreId sys_mutex_t;
-typedef osMessageQId  sys_mbox_t;
-typedef osThreadId    sys_thread_t;
-#else
-
 #define SYS_MBOX_NULL (osMessageQueueId_t)0
 #define SYS_SEM_NULL  (osSemaphoreId_t)0
 
@@ -62,7 +51,6 @@
 typedef osSemaphoreId_t     sys_mutex_t;
 typedef osMessageQueueId_t  sys_mbox_t;
 typedef osThreadId_t        sys_thread_t;
-#endif
 
 #ifdef  __cplusplus
 }
diff --git a/test/fuzz/output_to_pcap.sh b/test/fuzz/output_to_pcap.sh
deleted file mode 100644
index c999ff0..0000000
--- a/test/fuzz/output_to_pcap.sh
+++ /dev/null
@@ -1,31 +0,0 @@
-#!/bin/bash
-
-if [ -z "$1" ]
-then
-	echo "This script will make pcap files from the afl-fuzz crash/hang files"
-	echo "It needs hexdump and text2pcap"
-	echo "Please give output directory as argument"
-	exit 2
-fi
-
-for i in `ls $1/crashes/id*`
-do
-	PCAPNAME=`echo $i | grep pcap`
-	if [ -z "$PCAPNAME" ]; then
-		hexdump -C $i > $1/$$.tmp
-		text2pcap $1/$$.tmp ${i}.pcap
-	fi
-done
-for i in `ls $1/hangs/id*`
-do
-	PCAPNAME=`echo $i | grep pcap`
-	if [ -z "$PCAPNAME" ]; then
-		hexdump -C $i > $1/$$.tmp
-		text2pcap $1/$$.tmp ${i}.pcap
-	fi
-done
-rm -f $1/$$.tmp
-
-echo
-echo "Created pcap files:"
-ls $1/*/*.pcap
diff --git a/test/unit/arch/sys_arch.c b/test/unit/arch/sys_arch.c
index cf826eb..ed0e34f 100644
--- a/test/unit/arch/sys_arch.c
+++ b/test/unit/arch/sys_arch.c
@@ -365,7 +365,23 @@
 }
 
 #if LWIP_NETCONN_SEM_PER_THREAD
-#error LWIP_NETCONN_SEM_PER_THREAD==1 not supported
+/* Simple implementation of this: unit tests only support one thread */
+static sys_sem_t global_netconn_sem;
+
+sys_sem_t* sys_arch_netconn_sem_get(void)
+{
+  return &global_netconn_sem;
+}
+
+void sys_arch_netconn_sem_alloc(void)
+{
+  sys_sem_new(&global_netconn_sem, 0);
+}
+
+void sys_arch_netconn_sem_free(void)
+{
+  sys_sem_free(&global_netconn_sem);
+}
 #endif /* LWIP_NETCONN_SEM_PER_THREAD */
 
 #endif /* !NO_SYS */
diff --git a/test/unit/arch/sys_arch.h b/test/unit/arch/sys_arch.h
index 331c2f2..9157b6a 100644
--- a/test/unit/arch/sys_arch.h
+++ b/test/unit/arch/sys_arch.h
@@ -68,5 +68,12 @@
 /* current time */
 extern u32_t lwip_sys_now;
 
+sys_sem_t* sys_arch_netconn_sem_get(void);
+void sys_arch_netconn_sem_alloc(void);
+void sys_arch_netconn_sem_free(void);
+#define LWIP_NETCONN_THREAD_SEM_GET()   sys_arch_netconn_sem_get()
+#define LWIP_NETCONN_THREAD_SEM_ALLOC() sys_arch_netconn_sem_alloc()
+#define LWIP_NETCONN_THREAD_SEM_FREE()  sys_arch_netconn_sem_free()
+
 #endif /* LWIP_HDR_TEST_SYS_ARCH_H */
 
diff --git a/test/unit/core/test_netif.c b/test/unit/core/test_netif.c
index 2f68725..c5fa75d 100644
--- a/test/unit/core/test_netif.c
+++ b/test/unit/core/test_netif.c
@@ -9,7 +9,7 @@
 #error "This tests needs LWIP_NETIF_EXT_STATUS_CALLBACK enabled"
 #endif
 
-struct netif net_test;
+static struct netif net_test;
 
 
 /* Setups/teardown functions */
@@ -215,13 +215,67 @@
 }
 END_TEST
 
+START_TEST(test_netif_flag_set)
+{
+  ip4_addr_t addr;
+  ip4_addr_t netmask;
+  ip4_addr_t gw;
+  LWIP_UNUSED_ARG(_i);
+
+  IP4_ADDR(&addr, 0, 0, 0, 0);
+  IP4_ADDR(&netmask, 0, 0, 0, 0);
+  IP4_ADDR(&gw, 0, 0, 0, 0);
+
+  netif_add(&net_test, &addr, &netmask, &gw, &net_test, testif_init, ethernet_input);
+
+  fail_if(netif_is_flag_set(&net_test, NETIF_FLAG_UP));
+  fail_unless(netif_is_flag_set(&net_test, NETIF_FLAG_BROADCAST));
+  fail_if(netif_is_flag_set(&net_test, NETIF_FLAG_LINK_UP));
+  fail_unless(netif_is_flag_set(&net_test, NETIF_FLAG_ETHARP));
+  fail_unless(netif_is_flag_set(&net_test, NETIF_FLAG_ETHERNET));
+  fail_unless(netif_is_flag_set(&net_test, NETIF_FLAG_IGMP));
+  fail_unless(netif_is_flag_set(&net_test, NETIF_FLAG_MLD6));
+
+  netif_remove(&net_test);
+}
+END_TEST
+
+START_TEST(test_netif_find)
+{
+  struct netif net0;
+  struct netif net1;
+  LWIP_UNUSED_ARG(_i);
+
+  /* No netifs available */
+  fail_unless(netif_find("ch0") == NULL);
+
+  /* Add netifs with known names */
+  fail_unless(netif_add_noaddr(&net0, NULL, testif_init, ethernet_input) == &net0);
+  net0.num = 0;
+  fail_unless(netif_add_noaddr(&net1, NULL, testif_init, ethernet_input) == &net1);
+  net1.num = 1;
+
+  fail_unless(netif_find("ch0") == &net0);
+  fail_unless(netif_find("CH0") == NULL);
+  fail_unless(netif_find("ch1") == &net1);
+  fail_unless(netif_find("ch3") == NULL);
+  /* atoi failure is not treated as zero */
+  fail_unless(netif_find("chX") == NULL);
+  fail_unless(netif_find("ab0") == NULL);
+
+  netif_remove(&net0);
+  netif_remove(&net1);
+}
+END_TEST
 
 /** Create the suite including all tests for this module */
 Suite *
 netif_suite(void)
 {
   testfunc tests[] = {
-    TESTFUNC(test_netif_extcallbacks)
+    TESTFUNC(test_netif_extcallbacks),
+    TESTFUNC(test_netif_flag_set),
+    TESTFUNC(test_netif_find)
   };
   return create_suite("NETIF", tests, sizeof(tests)/sizeof(testfunc), netif_setup, netif_teardown);
 }
diff --git a/test/unit/dhcp/test_dhcp.c b/test/unit/dhcp/test_dhcp.c
index 4cefa33..d84900d 100644
--- a/test/unit/dhcp/test_dhcp.c
+++ b/test/unit/dhcp/test_dhcp.c
@@ -6,7 +6,7 @@
 #include "lwip/etharp.h"
 #include "netif/ethernet.h"
 
-struct netif net_test;
+static struct netif net_test;
 
 static const u8_t broadcast[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
 
diff --git a/test/unit/ip6/test_ip6.c b/test/unit/ip6/test_ip6.c
index f337136..7303741 100644
--- a/test/unit/ip6/test_ip6.c
+++ b/test/unit/ip6/test_ip6.c
@@ -287,6 +287,43 @@
 }
 END_TEST
 
+/* Reproduces bug #57374 */
+START_TEST(test_ip6_frag_pbuf_len_assert)
+{
+  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 *payload, *hdr;
+  err_t err;
+  int 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);
+
+  /* Create packet with lots of small pbufs around mtu limit */
+  payload = pbuf_alloc(PBUF_RAW, 1400, PBUF_POOL);
+  fail_unless(payload != NULL);
+  for (i = 0; i < 16; i++) {
+    struct pbuf *p = pbuf_alloc(PBUF_RAW, 32, PBUF_RAM);
+    fail_unless(p != NULL);
+    pbuf_cat(payload, p);
+  }
+  /* Prefix with header like UDP would */
+  hdr = pbuf_alloc(PBUF_IP, 8, PBUF_RAM);
+  fail_unless(hdr != NULL);
+  pbuf_chain(hdr, payload);
+
+  /* Send it and don't crash while fragmenting */
+  err = ip6_output_if_src(hdr, ip_2_ip6(&my_addr), ip_2_ip6(&peer_addr), 15, 0, IP_PROTO_UDP, &test_netif6);
+  fail_unless(err == ERR_OK);
+
+  pbuf_free(hdr);
+  pbuf_free(payload);
+}
+END_TEST
+
 /** Create the suite including all tests for this module */
 Suite *
 ip6_suite(void)
@@ -296,7 +333,8 @@
     TESTFUNC(test_ip6_aton_ipv4mapped),
     TESTFUNC(test_ip6_ntoa_ipv4mapped),
     TESTFUNC(test_ip6_ntoa),
-    TESTFUNC(test_ip6_lladdr)
+    TESTFUNC(test_ip6_lladdr),
+    TESTFUNC(test_ip6_frag_pbuf_len_assert)
   };
   return create_suite("IPv6", tests, sizeof(tests)/sizeof(testfunc), ip6_setup, ip6_teardown);
 }
diff --git a/test/unit/lwip_check.h b/test/unit/lwip_check.h
index 522a9c3..0246c59 100644
--- a/test/unit/lwip_check.h
+++ b/test/unit/lwip_check.h
@@ -13,6 +13,7 @@
 #define EXPECT_RETX(x, y) do { fail_unless(x); if(!(x)) { return y; }} while(0)
 #define EXPECT_RETNULL(x) EXPECT_RETX(x, NULL)
 
+#if (CHECK_MAJOR_VERSION == 0 && CHECK_MINOR_VERSION < 13)
 typedef struct {
   TFun func;
   const char *name;
@@ -24,6 +25,15 @@
 #define tcase_add_named_test(tc,tf) \
    _tcase_add_test((tc),(tf).func,(tf).name,0, 0, 0, 1)
 
+#else
+/* From 0.13.0 check keeps track of the method name internally */
+typedef const TTest * testfunc;
+
+#define TESTFUNC(x) x
+
+#define tcase_add_named_test(tc,tf) tcase_add_test(tc,tf)
+#endif
+
 /** typedef for a function returning a test suite */
 typedef Suite* (suite_getter_fn)(void);
 
diff --git a/test/unit/lwipopts.h b/test/unit/lwipopts.h
index c00ace1..e4523fc 100644
--- a/test/unit/lwipopts.h
+++ b/test/unit/lwipopts.h
@@ -46,6 +46,7 @@
 #define LWIP_NETCONN                    !NO_SYS
 #define LWIP_SOCKET                     !NO_SYS
 #define LWIP_NETCONN_FULLDUPLEX         LWIP_SOCKET
+#define LWIP_NETCONN_SEM_PER_THREAD     1
 #define LWIP_NETBUF_RECVINFO            1
 #define LWIP_HAVE_LOOPIF                1
 #define TCPIP_THREAD_TEST
diff --git a/test/unit/udp/test_udp.c b/test/unit/udp/test_udp.c
index e26a83e..a9a6b25 100644
--- a/test/unit/udp/test_udp.c
+++ b/test/unit/udp/test_udp.c
@@ -335,13 +335,138 @@
 }
 END_TEST
 
+START_TEST(test_udp_bind)
+{
+  struct udp_pcb* pcb1;
+  struct udp_pcb* pcb2;
+  ip_addr_t ip1;
+  ip_addr_t ip2;
+  err_t err1;
+  err_t err2;
+  LWIP_UNUSED_ARG(_i);
+
+  /* bind on same port using different IP address types */
+  ip_addr_set_any_val(0, ip1);
+  ip_addr_set_any_val(1, ip2);
+
+  pcb1 = udp_new_ip_type(IPADDR_TYPE_V4);
+  pcb2 = udp_new_ip_type(IPADDR_TYPE_V6);
+
+  err1 = udp_bind(pcb1, &ip1, 2105);
+  err2 = udp_bind(pcb2, &ip2, 2105);
+
+  fail_unless(err1 == ERR_OK);
+  fail_unless(err2 == ERR_OK);
+
+  udp_remove(pcb1);
+  udp_remove(pcb2);
+
+  /* bind on same port using SAME IPv4 address type */
+  ip_addr_set_any_val(0, ip1);
+  ip_addr_set_any_val(0, ip2);
+
+  pcb1 = udp_new_ip_type(IPADDR_TYPE_V4);
+  pcb2 = udp_new_ip_type(IPADDR_TYPE_V4);
+
+  err1 = udp_bind(pcb1, &ip1, 2105);
+  err2 = udp_bind(pcb2, &ip2, 2105);
+
+  fail_unless(err1 == ERR_OK);
+  fail_unless(err2 == ERR_USE);
+
+  udp_remove(pcb1);
+  udp_remove(pcb2);
+
+  /* bind on same port using SAME IPv6 address type */
+  ip_addr_set_any_val(1, ip1);
+  ip_addr_set_any_val(1, ip2);
+
+  pcb1 = udp_new_ip_type(IPADDR_TYPE_V6);
+  pcb2 = udp_new_ip_type(IPADDR_TYPE_V6);
+
+  err1 = udp_bind(pcb1, &ip1, 2105);
+  err2 = udp_bind(pcb2, &ip2, 2105);
+
+  fail_unless(err1 == ERR_OK);
+  fail_unless(err2 == ERR_USE);
+
+  udp_remove(pcb1);
+  udp_remove(pcb2);
+
+  /* Bind with different IP address type */
+  ip_addr_set_any_val(0, ip1);
+  ip_addr_set_any_val(1, ip2);
+
+  pcb1 = udp_new_ip_type(IPADDR_TYPE_V6);
+  pcb2 = udp_new_ip_type(IPADDR_TYPE_V4);
+
+  err1 = udp_bind(pcb1, &ip1, 2105);
+  err2 = udp_bind(pcb2, &ip2, 2105);
+
+  fail_unless(err1 == ERR_OK);
+  fail_unless(err2 == ERR_OK);
+
+  udp_remove(pcb1);
+  udp_remove(pcb2);
+
+  /* Bind with different IP numbers */
+  IP_ADDR4(&ip1, 1, 2, 3, 4);
+  IP_ADDR4(&ip2, 4, 3, 2, 1);
+
+  pcb1 = udp_new_ip_type(IPADDR_TYPE_V6);
+  pcb2 = udp_new_ip_type(IPADDR_TYPE_V4);
+
+  err1 = udp_bind(pcb1, &ip1, 2105);
+  err2 = udp_bind(pcb2, &ip2, 2105);
+
+  fail_unless(err1 == ERR_OK);
+  fail_unless(err2 == ERR_OK);
+
+  udp_remove(pcb1);
+  udp_remove(pcb2);
+
+  /* Bind with same IP numbers */
+  IP_ADDR4(&ip1, 1, 2, 3, 4);
+  IP_ADDR4(&ip2, 1, 2, 3, 4);
+
+  pcb1 = udp_new_ip_type(IPADDR_TYPE_V6);
+  pcb2 = udp_new_ip_type(IPADDR_TYPE_V4);
+
+  err1 = udp_bind(pcb1, &ip1, 2105);
+  err2 = udp_bind(pcb2, &ip2, 2105);
+
+  fail_unless(err1 == ERR_OK);
+  fail_unless(err2 == ERR_USE);
+
+  udp_remove(pcb1);
+  udp_remove(pcb2);
+
+  /* bind on same port using ANY + IPv4 */
+  ip1 = *IP_ANY_TYPE;
+  IP_ADDR4(&ip2, 1, 2, 3, 4);
+
+  pcb1 = udp_new_ip_type(IPADDR_TYPE_ANY);
+  pcb2 = udp_new_ip_type(IPADDR_TYPE_V4);
+
+  err1 = udp_bind(pcb1, &ip1, 2105);
+  err2 = udp_bind(pcb2, &ip2, 2105);
+
+  fail_unless(err1 == ERR_OK);
+  fail_unless(err2 == ERR_USE);
+
+  udp_remove(pcb1);
+  udp_remove(pcb2);
+}
+END_TEST
+
 /** Create the suite including all tests for this module */
 Suite *
 udp_suite(void)
 {
   testfunc tests[] = {
     TESTFUNC(test_udp_new_remove),
-    TESTFUNC(test_udp_broadcast_rx_with_2_netifs)
+    TESTFUNC(test_udp_broadcast_rx_with_2_netifs),
+    TESTFUNC(test_udp_bind)
   };
   return create_suite("UDP", tests, sizeof(tests)/sizeof(testfunc), udp_setup, udp_teardown);
 }