diff --git a/core/app_main.c b/core/app_main.c
index cd59c79..9c3918c 100644
--- a/core/app_main.c
+++ b/core/app_main.c
@@ -373,16 +373,22 @@ void sdk_user_init_task(void *params) {
     sdk_wifi_mode_set(sdk_g_ic.s.wifi_mode);
     if (sdk_g_ic.s.wifi_mode == STATION_MODE) {
         sdk_wifi_station_start();
+        LOCK_TCPIP_CORE();
         netif_set_default(sdk_g_ic.v.station_netif_info->netif);
+        UNLOCK_TCPIP_CORE();
     }
     if (sdk_g_ic.s.wifi_mode == SOFTAP_MODE) {
         sdk_wifi_softap_start();
+        LOCK_TCPIP_CORE();
         netif_set_default(sdk_g_ic.v.softap_netif_info->netif);
+        UNLOCK_TCPIP_CORE();
     }
     if (sdk_g_ic.s.wifi_mode == STATIONAP_MODE) {
         sdk_wifi_station_start();
         sdk_wifi_softap_start();
+        LOCK_TCPIP_CORE();
         netif_set_default(sdk_g_ic.v.station_netif_info->netif);
+        UNLOCK_TCPIP_CORE();
     }
     if (sdk_wifi_station_get_auto_connect()) {
         sdk_wifi_station_connect();
diff --git a/extras/wificfg/wificfg.c b/extras/wificfg/wificfg.c
index 430740c..804adad 100644
--- a/extras/wificfg/wificfg.c
+++ b/extras/wificfg/wificfg.c
@@ -1689,7 +1689,9 @@ static void server_task(void *pvParameters)
         struct netif *softap_netif = sdk_system_get_netif(SOFTAP_IF);
         if ((wifi_sta_mdns && station_netif) || (wifi_ap_mdns && softap_netif)) {
 #if LWIP_MDNS_RESPONDER
+            LOCK_TCPIP_CORE();
             mdns_resp_init();
+            UNLOCK_TCPIP_CORE();
 #endif
 #if EXTRAS_MDNS_RESPONDER
             mdns_init();
@@ -1697,16 +1699,22 @@ static void server_task(void *pvParameters)
 #endif
         }
 #if LWIP_MDNS_RESPONDER
+        LOCK_TCPIP_CORE();
         if (wifi_sta_mdns && station_netif) {
+            LOCK_TCPIP_CORE();
             mdns_resp_add_netif(station_netif, hostname, 120);
             mdns_resp_add_service(station_netif, hostname, "_http",
                                   DNSSD_PROTO_TCP, 80, 3600, NULL, NULL);
+            UNLOCK_TCPIP_CORE();
         }
         if (wifi_ap_mdns && softap_netif) {
+            LOCK_TCPIP_CORE();
             mdns_resp_add_netif(softap_netif, hostname, 120);
             mdns_resp_add_service(softap_netif, hostname, "_http",
                                   DNSSD_PROTO_TCP, 80, 3600, NULL, NULL);
+            UNLOCK_TCPIP_CORE();
         }
+        UNLOCK_TCPIP_CORE();
 #endif
 
         free(hostname);
@@ -2233,10 +2241,6 @@ void wificfg_init(uint32_t port, const wificfg_dispatch *dispatch)
         params->dispatch = dispatch;
 
         size_t stack_size = 464;
-#if LWIP_MDNS_RESPONDER
-        /* Uses a lot of stack space, so allocate extra. */
-        stack_size += 128;
-#endif
         xTaskCreate(server_task, "WiFi Cfg HTTP", stack_size, params, 2, NULL);
     }
 }
diff --git a/lwip/include/arch/cc.h b/lwip/include/arch/cc.h
index 5787157..a88c413 100644
--- a/lwip/include/arch/cc.h
+++ b/lwip/include/arch/cc.h
@@ -87,14 +87,9 @@ typedef int sys_prot_t;
     } while(0)
 #define LWIP_PLATFORM_ASSERT(x) do { printf("Assertion \"%s\" failed at line %d in %s\n", \
                                             x, __LINE__, __FILE__); abort(); } while(0)
-
-#define LWIP_ERROR(message, expression, handler) do { if (!(expression)) { \
-  printf("Assertion \"%s\" failed at line %d in %s\n", message, __LINE__, __FILE__); \
-  handler;} } while(0)
 #else
 #define LWIP_PLATFORM_DIAG(x)
 #define LWIP_PLATFORM_ASSERT(x)
-#define LWIP_ERROR(m,e,h)
 #endif
 
 #define LWIP_PLATFORM_BYTESWAP 1
@@ -104,4 +99,7 @@ typedef int sys_prot_t;
 
 #define LWIP_RAND()                         hwrand()
 
+/* Newlib includes this definition so use it. */
+#define lwip_strnstr(buffer, token, n) strnstr(buffer, token, n)
+
 #endif /* __ARCH_CC_H__ */
diff --git a/lwip/include/arch/sys_arch.h b/lwip/include/arch/sys_arch.h
index 0dfa482..00f7957 100644
--- a/lwip/include/arch/sys_arch.h
+++ b/lwip/include/arch/sys_arch.h
@@ -1,58 +1,62 @@
-/*
- * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
- * All rights reserved. 
- * 
- * Redistribution and use in source and binary forms, with or without modification, 
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission. 
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
- * OF SUCH DAMAGE.
- *
- * This file is part of the lwIP TCP/IP stack.
- * 
- * Author: Adam Dunkels <adam@sics.se>
- *
- */
-#ifndef __ARCH_SYS_ARCH_H__
-#define __ARCH_SYS_ARCH_H__
-
-#include "FreeRTOS.h"
-#include "task.h"
-#include "queue.h"
-#include "semphr.h"
-
-/* MBOX primitives */
-
-#define SYS_MBOX_NULL					( ( QueueHandle_t ) NULL )
-#define SYS_SEM_NULL					( ( SemaphoreHandle_t ) NULL )
-#define SYS_DEFAULT_THREAD_STACK_DEPTH	configMINIMAL_STACK_SIZE
-
-typedef SemaphoreHandle_t sys_sem_t;
-typedef SemaphoreHandle_t sys_mutex_t;
-typedef QueueHandle_t sys_mbox_t;
-typedef TaskHandle_t sys_thread_t;
-
-#define sys_mbox_valid( x ) ( ( ( *x ) == NULL) ? pdFALSE : pdTRUE )
-#define sys_mbox_set_invalid( x ) ( ( *x ) = NULL )
-#define sys_sem_valid( x ) ( ( ( *x ) == NULL) ? pdFALSE : pdTRUE )
-#define sys_sem_set_invalid( x ) ( ( *x ) = NULL )
-
-
-#endif /* __ARCH_SYS_ARCH_H__ */
-
+/*
+ * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without modification, 
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ * 
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef __ARCH_SYS_ARCH_H__
+#define __ARCH_SYS_ARCH_H__
+
+#include "FreeRTOS.h"
+#include "task.h"
+#include "queue.h"
+#include "semphr.h"
+
+/* MBOX primitives */
+
+#define SYS_MBOX_NULL					( ( QueueHandle_t ) NULL )
+#define SYS_SEM_NULL					( ( SemaphoreHandle_t ) NULL )
+#define SYS_DEFAULT_THREAD_STACK_DEPTH	configMINIMAL_STACK_SIZE
+
+typedef SemaphoreHandle_t sys_sem_t;
+typedef SemaphoreHandle_t sys_mutex_t;
+typedef QueueHandle_t sys_mbox_t;
+typedef TaskHandle_t sys_thread_t;
+
+#define sys_mbox_valid( x ) ( ( ( *x ) == NULL) ? pdFALSE : pdTRUE )
+#define sys_mbox_set_invalid( x ) ( ( *x ) = NULL )
+#define sys_sem_valid( x ) ( ( ( *x ) == NULL) ? pdFALSE : pdTRUE )
+#define sys_sem_set_invalid( x ) ( ( *x ) = NULL )
+
+#define sys_jiffies() xTaskGetTickCount()
+
+void sys_arch_msleep(uint32_t ms);
+#define sys_msleep(ms) sys_arch_msleep(ms)
+
+#endif /* __ARCH_SYS_ARCH_H__ */
+
diff --git a/lwip/include/lwipopts.h b/lwip/include/lwipopts.h
index d2c7323..57c6f05 100644
--- a/lwip/include/lwipopts.h
+++ b/lwip/include/lwipopts.h
@@ -81,7 +81,9 @@
  * UNLOCK_TCPIP_CORE().
  * Your system should provide mutexes supporting priority inversion to use this.
  */
+#ifndef LWIP_TCPIP_CORE_LOCKING
 #define LWIP_TCPIP_CORE_LOCKING         1
+#endif
 
 /**
  * LWIP_TCPIP_CORE_LOCKING_INPUT: when LWIP_TCPIP_CORE_LOCKING is enabled,
@@ -95,6 +97,48 @@
 #define LWIP_TCPIP_CORE_LOCKING_INPUT   0
 #endif
 
+/**
+ * Macro/function to check whether lwIP's threading/locking
+ * requirements are satisfied during current function call.
+ * This macro usually calls a function that is implemented in the OS-dependent
+ * sys layer and performs the following checks:
+ * - Not in ISR
+ * - If @ref LWIP_TCPIP_CORE_LOCKING = 1: TCPIP core lock is held
+ * - If @ref LWIP_TCPIP_CORE_LOCKING = 0: function is called from TCPIP thread
+ * @see @ref multithreading
+ */
+#ifndef LWIP_ASSERT_CORE_LOCKED
+void sys_check_core_locking(void);
+#define LWIP_ASSERT_CORE_LOCKED()       sys_check_core_locking()
+#endif
+
+/**
+ * Called as first thing in the lwIP TCPIP thread. Can be used in conjunction
+ * with @ref LWIP_ASSERT_CORE_LOCKED to check core locking.
+ * @see @ref multithreading
+ */
+#ifndef LWIP_MARK_TCPIP_THREAD
+void sys_mark_tcpip_thread(void);
+#define LWIP_MARK_TCPIP_THREAD()        sys_mark_tcpip_thread()
+#endif
+
+#if LWIP_TCPIP_CORE_LOCKING
+
+#ifndef LOCK_TCPIP_CORE
+void sys_lock_tcpip_core(void);
+#define LOCK_TCPIP_CORE()          sys_lock_tcpip_core()
+#endif
+
+#ifndef UNLOCK_TCPIP_CORE
+void sys_unlock_tcpip_core(void);
+#define UNLOCK_TCPIP_CORE()        sys_unlock_tcpip_core()
+#endif
+
+#else
+#define LOCK_TCPIP_CORE()
+#define UNLOCK_TCPIP_CORE()
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+
 /*
    ------------------------------------
    ---------- Memory options ----------
@@ -483,6 +527,21 @@
 #define LWIP_NETIF_HOSTNAME             1
 #endif
 
+/**
+ * LWIP_NETIF_API==1: Support netif api (in netifapi.c)
+ */
+#ifndef LWIP_NETIF_API
+#define LWIP_NETIF_API                  0
+#endif
+
+/**
+ * LWIP_NETIF_STATUS_CALLBACK==1: Support a callback function whenever an interface
+ * changes its up/down status (i.e., due to DHCP IP acquisition)
+ */
+#ifndef LWIP_NETIF_STATUS_CALLBACK
+#define LWIP_NETIF_STATUS_CALLBACK      0
+#endif
+
 /**
  * LWIP_NETIF_TX_SINGLE_PBUF: if this is set to 1, lwIP *tries* to put all data
  * to be sent into one single pbuf. This is for compatibility with DMA-enabled
@@ -515,7 +574,7 @@
  * sys_thread_new() when the thread is created.
  */
 #ifndef TCPIP_THREAD_STACKSIZE
-#define TCPIP_THREAD_STACKSIZE          768
+#define TCPIP_THREAD_STACKSIZE          480
 #endif
 
 /**
@@ -672,6 +731,20 @@
    ---------------------------------------
 */
 
+/*
+   ---------------------------------------
+   ---------- mDNS options ---------------
+   ---------------------------------------
+*/
+
+/**
+ * LWIP_MDNS_RESPONDER_QUEUE_ANNOUNCEMENTS==1: Unsolicited announcements are
+ * queued and run from a timer callback.
+ */
+#ifndef LWIP_MDNS_RESPONDER_QUEUE_ANNOUNCEMENTS
+#define LWIP_MDNS_RESPONDER_QUEUE_ANNOUNCEMENTS     1
+#endif
+
 /*
    ---------------------------------------
    ---------- Debugging options ----------
@@ -855,6 +928,11 @@
  */
 #define IP6_DEBUG                       LWIP_DBG_OFF
 
+/**
+ * MDNS_DEBUG: Enable debugging for multicast DNS.
+ */
+#define MDNS_DEBUG                      LWIP_DBG_OFF
+
 /*
    --------------------------------------------------
    ---------- Performance tracking options ----------
diff --git a/lwip/lwip b/lwip/lwip
index 4e87c66..9f70dbe 160000
--- a/lwip/lwip
+++ b/lwip/lwip
@@ -1 +1 @@
-Subproject commit 4e87c66bff75fbd02be86522de0f0bcf40a11f34
+Subproject commit 9f70dbe91a68df81c6bbcb48306a590359a41d3f
diff --git a/lwip/sys_arch.c b/lwip/sys_arch.c
index 1a9c5f9..36a6eae 100644
--- a/lwip/sys_arch.c
+++ b/lwip/sys_arch.c
@@ -48,6 +48,11 @@
 #include "lwip/sys.h"
 #include "lwip/mem.h"
 #include "lwip/stats.h"
+#include "lwip/tcpip.h"
+
+#if configUSE_16_BIT_TICKS == 1
+#error This port requires 32 bit ticks or timer overflow will fail
+#endif
 
 /*---------------------------------------------------------------------------*
  * Routine:  sys_sem_new
@@ -62,24 +67,23 @@
  * Outputs:
  *      sys_sem_t               -- Created semaphore or 0 if could not create.
  *---------------------------------------------------------------------------*/
-err_t sys_sem_new(sys_sem_t *pxSemaphore, u8_t ucCount)
+err_t sys_sem_new(sys_sem_t *pxSemaphore, u8_t initial_count)
 {
-    err_t xReturn = ERR_MEM;
+    LWIP_ASSERT("initial_count invalid (not 0 or 1)",
+                (initial_count == 0) || (initial_count == 1));
 
-    vSemaphoreCreateBinary(*pxSemaphore);
-
-    if (*pxSemaphore != NULL) {
-        if (ucCount == 0U) {
-            xSemaphoreTake(*pxSemaphore, 1UL);
-        }
-
-        xReturn = ERR_OK;
-        SYS_STATS_INC_USED(sem);
-    } else {
+    *pxSemaphore = xSemaphoreCreateBinary();
+    if (*pxSemaphore == NULL) {
         SYS_STATS_INC(sem.err);
+        return ERR_MEM;
     }
+    SYS_STATS_INC_USED(sem);
 
-    return xReturn;
+    if (initial_count == 1) {
+        BaseType_t ret = xSemaphoreGive(*pxSemaphore);
+        LWIP_ASSERT("sys_sem_new: initial give failed", ret == pdTRUE);
+    }
+    return ERR_OK;
 }
 
 /*---------------------------------------------------------------------------*
@@ -93,7 +97,8 @@ err_t sys_sem_new(sys_sem_t *pxSemaphore, u8_t ucCount)
 void sys_sem_free(sys_sem_t *pxSemaphore)
 {
     SYS_STATS_DEC(sem.used);
-    vQueueDelete(*pxSemaphore);
+    vSemaphoreDelete(*pxSemaphore);
+    *pxSemaphore = NULL;
 }
 
 /*---------------------------------------------------------------------------*
@@ -131,22 +136,19 @@ void sys_sem_signal(sys_sem_t *pxSemaphore)
  * Outputs:
  *      u32_t                   -- SYS_ARCH_TIMEOUT on timeout, any other value on success
  *---------------------------------------------------------------------------*/
-u32_t sys_arch_sem_wait(sys_sem_t *pxSemaphore, u32_t ulTimeout)
+u32_t sys_arch_sem_wait(sys_sem_t *pxSemaphore, u32_t timeout_ms)
 {
-    u32_t ulReturn;
-
-    if (ulTimeout != 0UL) {
-        if (xSemaphoreTake(*pxSemaphore, ulTimeout / portTICK_PERIOD_MS) == pdTRUE) {
-            ulReturn = 0;
-        } else {
-            ulReturn = SYS_ARCH_TIMEOUT;
-        }
-    } else {
+    if (timeout_ms == 0) {
+        /* Wait infinite */
         while (xSemaphoreTake(*pxSemaphore, portMAX_DELAY) != pdTRUE);
-        ulReturn = 0;
+        return 0;
     }
 
-    return ulReturn;
+    if (xSemaphoreTake(*pxSemaphore, timeout_ms / portTICK_PERIOD_MS) == pdTRUE) {
+        return 0;
+    } else {
+        return SYS_ARCH_TIMEOUT;
+    }
 }
 
 /** Create a new mutex
@@ -154,33 +156,30 @@ u32_t sys_arch_sem_wait(sys_sem_t *pxSemaphore, u32_t ulTimeout)
  * @return a new mutex */
 err_t sys_mutex_new(sys_mutex_t *pxMutex)
 {
-    err_t xReturn;
+    *pxMutex = xSemaphoreCreateRecursiveMutex();
 
-    *pxMutex = xSemaphoreCreateMutex();
-
-    if (*pxMutex != NULL) {
-        xReturn = ERR_OK;
-        SYS_STATS_INC_USED(mutex);
-    } else {
-        xReturn = ERR_MEM;
+    if (*pxMutex == NULL) {
         SYS_STATS_INC(mutex.err);
+        return ERR_MEM;
     }
 
-    return xReturn;
+    SYS_STATS_INC_USED(mutex);
+    return ERR_OK;
 }
 
 /** Lock a mutex
  * @param mutex the mutex to lock */
 void sys_mutex_lock(sys_mutex_t *pxMutex)
 {
-    while (xSemaphoreTake(*pxMutex, portMAX_DELAY) != pdPASS);
+    while (xSemaphoreTakeRecursive(*pxMutex, portMAX_DELAY) != pdTRUE);
 }
 
 /** Unlock a mutex
  * @param mutex the mutex to unlock */
 void sys_mutex_unlock(sys_mutex_t *pxMutex)
 {
-    xSemaphoreGive(*pxMutex);
+    BaseType_t ret = xSemaphoreGiveRecursive(*pxMutex);
+    LWIP_ASSERT("failed to give the mutex", ret == pdTRUE);
 }
 
 
@@ -189,7 +188,8 @@ void sys_mutex_unlock(sys_mutex_t *pxMutex)
 void sys_mutex_free(sys_mutex_t *pxMutex)
 {
     SYS_STATS_DEC(mutex.used);
-    vQueueDelete(*pxMutex);
+    vSemaphoreDelete(*pxMutex);
+    *pxMutex = NULL;
 }
 
 /*---------------------------------------------------------------------------*
@@ -202,21 +202,21 @@ void sys_mutex_free(sys_mutex_t *pxMutex)
  * Outputs:
  *      sys_mbox_t              -- Handle to new mailbox
  *---------------------------------------------------------------------------*/
-err_t sys_mbox_new(sys_mbox_t *pxMailBox, int iSize)
+err_t sys_mbox_new(sys_mbox_t *mbox, int size)
 {
-    err_t xReturn = ERR_MEM;
+    LWIP_ASSERT("size > 0", size > 0);
 
-    *pxMailBox = xQueueCreate(iSize, sizeof(void *));
+    *mbox = xQueueCreate(size, sizeof(void *));
 
-    if (*pxMailBox != NULL) {
-        xReturn = ERR_OK;
-        SYS_STATS_INC_USED(mbox);
+    if (*mbox == NULL) {
+        SYS_STATS_INC(mbox.err);
+        return ERR_MEM;
     }
 
-    return xReturn;
+    SYS_STATS_INC_USED(mbox);
+    return ERR_OK;
 }
 
-
 /*---------------------------------------------------------------------------*
  * Routine:  sys_mbox_free
  *---------------------------------------------------------------------------*
@@ -229,24 +229,21 @@ err_t sys_mbox_new(sys_mbox_t *pxMailBox, int iSize)
  * Outputs:
  *      sys_mbox_t              -- Handle to new mailbox
  *---------------------------------------------------------------------------*/
-void sys_mbox_free(sys_mbox_t *pxMailBox)
+void sys_mbox_free(sys_mbox_t *mbox)
 {
-    unsigned long ulMessagesWaiting;
+    UBaseType_t msgs_waiting;
 
-    ulMessagesWaiting = uxQueueMessagesWaiting(*pxMailBox);
-    configASSERT(ulMessagesWaiting == 0);
+    msgs_waiting = uxQueueMessagesWaiting(*mbox);
+    configASSERT(msgs_waiting == 0);
 
-    #if SYS_STATS
-    {
-        if (ulMessagesWaiting != 0UL) {
-            SYS_STATS_INC(mbox.err);
-        }
-
-        SYS_STATS_DEC(mbox.used);
+#if SYS_STATS
+    if (msgs_waiting != 0) {
+        SYS_STATS_INC(mbox.err);
     }
-    #endif /* SYS_STATS */
+    SYS_STATS_DEC(mbox.used);
+#endif /* SYS_STATS */
 
-    vQueueDelete(*pxMailBox);
+    vQueueDelete(*mbox);
 }
 
 /*---------------------------------------------------------------------------*
@@ -258,9 +255,9 @@ void sys_mbox_free(sys_mbox_t *pxMailBox)
  *      sys_mbox_t mbox         -- Handle of mailbox
  *      void *data              -- Pointer to data to post
  *---------------------------------------------------------------------------*/
-void sys_mbox_post(sys_mbox_t *pxMailBox, void *pxMessageToPost)
+void sys_mbox_post(sys_mbox_t *mbox, void *msg)
 {
-    while (xQueueSendToBack(*pxMailBox, &pxMessageToPost, portMAX_DELAY) != pdTRUE);
+    while (xQueueSendToBack(*mbox, &msg, portMAX_DELAY) != pdTRUE);
 }
 
 /*---------------------------------------------------------------------------*
@@ -276,19 +273,15 @@ void sys_mbox_post(sys_mbox_t *pxMailBox, void *pxMessageToPost)
  *      err_t                   -- ERR_OK if message posted, else ERR_MEM
  *                                  if not.
  *---------------------------------------------------------------------------*/
-err_t sys_mbox_trypost(sys_mbox_t *pxMailBox, void *pxMessageToPost)
+err_t sys_mbox_trypost(sys_mbox_t *mbox, void *msg)
 {
-    err_t xReturn;
-
-    if (xQueueSend(*pxMailBox, &pxMessageToPost, 0)) {
-        xReturn = ERR_OK;
-    } else {
-        /* The queue was already full. */
-        xReturn = ERR_MEM;
-        SYS_STATS_INC( mbox.err );
+    if (xQueueSendToBack(*mbox, &msg, 0) == pdTRUE) {
+        return ERR_OK;
     }
 
-    return xReturn;
+    /* The queue was already full. */
+    SYS_STATS_INC(mbox.err);
+    return ERR_MEM;
 }
 
 /*---------------------------------------------------------------------------*
@@ -315,29 +308,26 @@ err_t sys_mbox_trypost(sys_mbox_t *pxMailBox, void *pxMessageToPost)
  * Outputs:
  *      u32_t                   -- SYS_ARCH_TIMEOUT on timeout, any other value if a message has been received
  *---------------------------------------------------------------------------*/
-u32_t sys_arch_mbox_fetch(sys_mbox_t *pxMailBox, void **ppvBuffer, u32_t ulTimeOut)
+u32_t sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout)
 {
-    void *pvDummy;
-    unsigned long ulReturn;
+    void *msg_dummy;
 
-    if (ppvBuffer == NULL) {
-        ppvBuffer = &pvDummy;
+    if (msg == NULL) {
+        msg = &msg_dummy;
     }
 
-    if (ulTimeOut != 0UL) {
-        if (xQueueReceive(*pxMailBox, &(*ppvBuffer), ulTimeOut / portTICK_PERIOD_MS) == pdTRUE) {
-            ulReturn = 0;
-        } else {
-            /* Timed out. */
-            *ppvBuffer = NULL;
-            ulReturn = SYS_ARCH_TIMEOUT;
-        }
-    } else {
-        while (xQueueReceive(*pxMailBox, &(*ppvBuffer), portMAX_DELAY) != pdTRUE);
-        ulReturn = 0;
+    if (timeout == 0) {
+        while (xQueueReceive(*mbox, &(*msg), portMAX_DELAY) != pdTRUE);
+        return 0;
     }
 
-    return ulReturn;
+    if (xQueueReceive(*mbox, &(*msg), timeout / portTICK_PERIOD_MS) == pdTRUE) {
+        return 0;
+    }
+
+    /* Timed out. */
+    *msg = NULL;
+    return SYS_ARCH_TIMEOUT;
 }
 
 /*---------------------------------------------------------------------------*
@@ -354,22 +344,20 @@ u32_t sys_arch_mbox_fetch(sys_mbox_t *pxMailBox, void **ppvBuffer, u32_t ulTimeO
  *      u32_t                   -- SYS_MBOX_EMPTY if no messages.  Otherwise,
  *                                  return ERR_OK.
  *---------------------------------------------------------------------------*/
-u32_t sys_arch_mbox_tryfetch(sys_mbox_t *pxMailBox, void **ppvBuffer)
+u32_t sys_arch_mbox_tryfetch(sys_mbox_t *mbox, void **msg)
 {
-    void *pvDummy;
-    unsigned long ulReturn;
+    void *msg_dummy;
 
-    if (ppvBuffer== NULL) {
-        ppvBuffer = &pvDummy;
+    if (msg == NULL) {
+        msg = &msg_dummy;
     }
 
-    if (xQueueReceive(*pxMailBox, &(*ppvBuffer), 0UL) == pdPASS) {
-        ulReturn = ERR_OK;
-    } else {
-        ulReturn = SYS_MBOX_EMPTY;
+    if (xQueueReceive(*mbox, &(*msg), 0) == pdTRUE) {
+        return ERR_OK;
     }
 
-    return ulReturn;
+    *msg = NULL;
+    return SYS_MBOX_EMPTY;
 }
 
 /*---------------------------------------------------------------------------*
@@ -387,6 +375,12 @@ u32_t sys_now(void)
     return xTaskGetTickCount() * portTICK_PERIOD_MS;
 }
 
+void sys_arch_msleep(u32_t delay_ms)
+{
+    TickType_t delay_ticks = delay_ms / portTICK_PERIOD_MS;
+    vTaskDelay(delay_ticks);
+}
+
 /*---------------------------------------------------------------------------*
  * Routine:  sys_thread_new
  *---------------------------------------------------------------------------*
@@ -405,21 +399,19 @@ u32_t sys_now(void)
  * Outputs:
  *      sys_thread_t            -- Pointer to per-thread timeouts.
  *---------------------------------------------------------------------------*/
-sys_thread_t sys_thread_new(const char *pcName, void(*pxThread)(void *pvParameters), void *pvArg, int iStackSize, int iPriority)
+sys_thread_t sys_thread_new(const char *pcName, void(*pxThread)(void *pvParameters), void *pvArg, int stacksize, int iPriority)
 {
     TaskHandle_t xCreatedTask;
-    portBASE_TYPE xResult;
-    sys_thread_t xReturn;
+    BaseType_t xResult;
 
-    xResult = xTaskCreate(pxThread, pcName, iStackSize, pvArg, iPriority, &xCreatedTask);
+    xResult = xTaskCreate(pxThread, pcName, stacksize, pvArg, iPriority, &xCreatedTask);
+    LWIP_ASSERT("task creation failed", xResult == pdPASS);
 
     if (xResult == pdPASS) {
-        xReturn = xCreatedTask;
-    } else {
-        xReturn = NULL;
+        return xCreatedTask;
     }
 
-    return xReturn;
+    return NULL;
 }
 
 /*---------------------------------------------------------------------------*
@@ -441,12 +433,13 @@ sys_thread_t sys_thread_new(const char *pcName, void(*pxThread)(void *pvParamete
  * Outputs:
  *      sys_prot_t              -- Previous protection level (not used here)
  *---------------------------------------------------------------------------*/
-static uint32_t my_nesting = 0;
+static sys_prot_t critical_nesting;
 sys_prot_t sys_arch_protect(void)
 {
+    sys_prot_t prev;
     taskENTER_CRITICAL();
-    uint32_t prev = my_nesting;
-    my_nesting++;
+    prev = critical_nesting;
+    critical_nesting++;
     return prev;
     //return (sys_prot_t)1;
 }
@@ -462,16 +455,70 @@ sys_prot_t sys_arch_protect(void)
  * Inputs:
  *      sys_prot_t              -- Previous protection level (not used here)
  *---------------------------------------------------------------------------*/
-void sys_arch_unprotect(sys_prot_t xValue)
+void sys_arch_unprotect(sys_prot_t pval)
 {
     //(void) xValue;
-    my_nesting--;
-    if (xValue != my_nesting) {
-        printf("lwip nesting %d\n", my_nesting);
+    critical_nesting--;
+    //LWIP_ASSERT("unexpected critical_nestion", pval == critical_nesting);
+    if (pval != critical_nesting) {
+        printf("lwip nesting %d\n", critical_nesting);
     }
     taskEXIT_CRITICAL();
 }
 
+#if LWIP_TCPIP_CORE_LOCKING
+
+/** Flag the core lock held. A counter for recusive locks. */
+u8_t lwip_core_lock_count;
+TaskHandle_t lwip_core_lock_holder_thread;
+void sys_lock_tcpip_core(void)
+{
+    sys_mutex_lock(&lock_tcpip_core);
+    if (lwip_core_lock_count == 0) {
+        lwip_core_lock_holder_thread = xTaskGetCurrentTaskHandle();
+    }
+    lwip_core_lock_count++;
+}
+
+void sys_unlock_tcpip_core(void)
+{
+    lwip_core_lock_count--;
+    if (lwip_core_lock_count == 0) {
+        lwip_core_lock_holder_thread = 0;
+    }
+    sys_mutex_unlock(&lock_tcpip_core);
+}
+
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+
+TaskHandle_t lwip_tcpip_thread;
+void sys_mark_tcpip_thread(void)
+{
+    lwip_tcpip_thread = xTaskGetCurrentTaskHandle();
+}
+
+void sys_check_core_locking(void)
+{
+    /* Embedded systems should check we are NOT in an interrupt context here */
+
+    if (lwip_tcpip_thread != 0) {
+        TaskHandle_t current_thread = xTaskGetCurrentTaskHandle();
+
+#if LWIP_TCPIP_CORE_LOCKING
+        if (current_thread != lwip_core_lock_holder_thread ||
+            lwip_core_lock_count == 0) {
+            printf("Function called without core lock\n");
+        }
+        //LWIP_ASSERT("Function called without core lock", current_thread == lwip_core_lock_holder_thread && lwip_core_lock_count > 0);
+#else
+        if (current_thread != lwip_tcpip_thread) {
+            printf("Function called from wrong thread\n");
+        }
+        //LWIP_ASSERT("Function called from wrong thread", current_thread == lwip_tcpip_thread);
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+    }
+}
+
 /*-------------------------------------------------------------------------*
  * End of File:  sys_arch.c
  *-------------------------------------------------------------------------*/
diff --git a/open_esplibs/libmain/user_interface.c b/open_esplibs/libmain/user_interface.c
index 1fd73b5..4483e5d 100644
--- a/open_esplibs/libmain/user_interface.c
+++ b/open_esplibs/libmain/user_interface.c
@@ -546,10 +546,13 @@ bool sdk_wifi_station_dhcpc_start(void) {
         sdk_info.sta_ipaddr.addr = 0;
         sdk_info.sta_netmask.addr = 0;
         sdk_info.sta_gw.addr = 0;
+        LOCK_TCPIP_CORE();
         netif_set_addr(netif, &sdk_info.sta_ipaddr, &sdk_info.sta_netmask, &sdk_info.sta_gw);
         if (dhcp_start(netif)) {
+            UNLOCK_TCPIP_CORE();
             return false;
         }
+        UNLOCK_TCPIP_CORE();
     }
     sdk_dhcpc_flag = DHCP_STARTED;
     return true;
@@ -561,9 +564,11 @@ bool sdk_wifi_station_dhcpc_stop(void) {
         return false;
     }
     if (netif && sdk_dhcpc_flag == DHCP_STARTED) {
+        LOCK_TCPIP_CORE();
         dhcp_stop(netif);
+        sdk_dhcpc_flag = DHCP_STOPPED;
+        UNLOCK_TCPIP_CORE();
     }
-    sdk_dhcpc_flag = DHCP_STOPPED;
     return true;
 }
 
@@ -616,8 +621,11 @@ bool sdk_wifi_set_ip_info(uint8_t if_index, struct ip_info *info) {
     }
 
     struct netif *netif = _get_netif(if_index);
-    if (netif)
+    if (netif) {
+        LOCK_TCPIP_CORE();
         netif_set_addr(netif, &info->ip, &info->netmask, &info->gw);
+        UNLOCK_TCPIP_CORE();
+    }
 
     return true;
 }
diff --git a/open_esplibs/libnet80211/ieee80211_hostap.c b/open_esplibs/libnet80211/ieee80211_hostap.c
index 74f9675..fec4801 100644
--- a/open_esplibs/libnet80211/ieee80211_hostap.c
+++ b/open_esplibs/libnet80211/ieee80211_hostap.c
@@ -216,13 +216,17 @@ bool sdk_wifi_softap_start() {
          struct netif *netif = (struct netif *)malloc(sizeof(struct netif));
          netif_info->netif = netif;
          memcpy(&netif->hwaddr, mac_addr, 6);
+         LOCK_TCPIP_CORE();
          netif_add(netif, &sdk_info.softap_ipaddr, &sdk_info.softap_netmask,
                    &sdk_info.softap_gw, netif_info, ethernetif_init, tcpip_input);
+         UNLOCK_TCPIP_CORE();
      }
 
     sdk_ic_set_vif(1, 1, mac_addr, 1, 0);
 
+    LOCK_TCPIP_CORE();
     netif_set_up(netif_info->netif);
+    UNLOCK_TCPIP_CORE();
 
     if (sdk_wifi_get_opmode() != 3 ||
         !sdk_g_ic.v.station_netif_info ||
@@ -293,7 +297,9 @@ bool sdk_wifi_softap_stop() {
         } while (count < end);
     }
 
+    LOCK_TCPIP_CORE();
     netif_set_down(netif_info->netif);
+    UNLOCK_TCPIP_CORE();
     sdk_TmpSTAAPCloseAP = 1;
     sdk_ets_timer_disarm(&hostap_timer);
     sdk_ic_bss_info_update(1, &sdk_info.softap_mac_addr, 2, 0);
diff --git a/open_esplibs/libnet80211/ieee80211_sta.c b/open_esplibs/libnet80211/ieee80211_sta.c
index 3e7f695..7c405cb 100644
--- a/open_esplibs/libnet80211/ieee80211_sta.c
+++ b/open_esplibs/libnet80211/ieee80211_sta.c
@@ -42,7 +42,10 @@ bool sdk_wifi_station_start() {
             struct netif *netif = (struct netif *)malloc(sizeof(struct netif));
             netif_info->netif = netif;
             memcpy(&netif->hwaddr, &sdk_info.sta_mac_addr, 6);
-            netif_add(netif, &sdk_info.sta_ipaddr, &sdk_info.sta_netmask, &sdk_info.sta_gw, netif_info, ethernetif_init, tcpip_input);
+            LOCK_TCPIP_CORE();
+            netif_add(netif, &sdk_info.sta_ipaddr, &sdk_info.sta_netmask,
+                      &sdk_info.sta_gw, netif_info, ethernetif_init, tcpip_input);
+            UNLOCK_TCPIP_CORE();
             sdk_wpa_attach(&sdk_g_ic);
         }
         sdk_ic_set_vif(0, 1, &sdk_info.sta_mac_addr, 0, 0);
diff --git a/open_esplibs/libnet80211/wl_cnx.c b/open_esplibs/libnet80211/wl_cnx.c
index 2d29960..bb5ec12 100644
--- a/open_esplibs/libnet80211/wl_cnx.c
+++ b/open_esplibs/libnet80211/wl_cnx.c
@@ -24,8 +24,10 @@ extern void *sdk_g_cnx_probe_rc_list_cb;
  */
 void dhcp_if_down(struct netif *netif)
 {
+    LOCK_TCPIP_CORE();
     dhcp_release_and_stop(netif);
     netif_set_down(netif);
+    UNLOCK_TCPIP_CORE();
 }
 
 struct sdk_cnx_node *sdk_cnx_rc_search(uint8_t *hwaddr) {
diff --git a/open_esplibs/libwpa/wpa_main.c b/open_esplibs/libwpa/wpa_main.c
index e13e3b6..1c5646e 100644
--- a/open_esplibs/libwpa/wpa_main.c
+++ b/open_esplibs/libwpa/wpa_main.c
@@ -97,8 +97,10 @@ void sdk_eagle_auth_done() {
 
     if (sdk_dhcpc_flag != DHCP_STOPPED) {
         printf("dhcp client start...\n");
+        LOCK_TCPIP_CORE();
         netif_set_up(netif);
         dhcp_start(netif);
+        UNLOCK_TCPIP_CORE();
         return;
     }
 
@@ -107,8 +109,10 @@ void sdk_eagle_auth_done() {
         return;
     }
 
+    LOCK_TCPIP_CORE();
     netif_set_addr(netif, &sdk_info.sta_ipaddr, &sdk_info.sta_netmask, &sdk_info.sta_gw);
     netif_set_up(netif);
+    UNLOCK_TCPIP_CORE();
     sdk_system_station_got_ip_set(ip_2_ip4(&netif->ip_addr),
                                   ip_2_ip4(&netif->netmask),
                                   ip_2_ip4(&netif->gw));