diff --git a/mongoose.c b/mongoose.c
index 25c3393042df56fa63ce83170ebd4f503bd3750a..1ce98d01a385cbcb9c8f37fbee61220c80a01d55 100644
--- a/mongoose.c
+++ b/mongoose.c
@@ -14051,15 +14051,31 @@ void mg_lwip_mgr_schedule_poll(struct mg_mgr *mgr);
 
 /* Amalgamated: #include "common/mg_mem.h" */
 
+#include <lwip/init.h>
 #include <lwip/pbuf.h>
 #include <lwip/tcp.h>
-#if CS_PLATFORM != CS_P_STM32
+#include <lwip/tcpip.h>
+#if LWIP_VERSION >= 0x01050000
+#include <lwip/priv/tcpip_priv.h> /* For tcp_seg */
+#else
 #include <lwip/tcp_impl.h>
 #endif
 #include <lwip/udp.h>
 
 /* Amalgamated: #include "common/cs_dbg.h" */
 
+/*
+ * Newest versions of LWIP have ip_2_ip4, older have ipX_2_ip,
+ * even older have nothing.
+ */
+#ifndef ip_2_ip4
+#ifdef ipX_2_ip
+#define ip_2_ip4(addr) ipX_2_ip(addr)
+#else
+#define ip_2_ip4(addr) (addr)
+#endif
+#endif
+
 /*
  * Depending on whether Mongoose is compiled with ipv6 support, use right
  * lwip functions
@@ -14077,16 +14093,12 @@ void mg_lwip_mgr_schedule_poll(struct mg_mgr *mgr);
 #define TCP_BIND tcp_bind
 #define UDP_BIND udp_bind
 #define IPADDR_NTOA ipaddr_ntoa
-#define SET_ADDR(dst, src) (dst)->sin.sin_addr.s_addr = GET_IPV4(src)
+#define SET_ADDR(dst, src) (dst)->sin.sin_addr.s_addr = ip_2_ip4(src)->addr
 #endif
 
-/*
- * If lwip is compiled with ipv6 support, then API changes even for ipv4
- */
-#if !defined(LWIP_IPV6) || !LWIP_IPV6
-#define GET_IPV4(ipX_addr) ((ipX_addr)->addr)
-#else
-#define GET_IPV4(ipX_addr) ((ipX_addr)->ip4.addr)
+#if NO_SYS
+#define tcpip_callback(fn, arg) (fn)(arg)
+typedef void (*tcpip_callback_fn)(void *arg);
 #endif
 
 void mg_lwip_ssl_do_hs(struct mg_connection *nc);
@@ -14256,8 +14268,17 @@ static err_t mg_lwip_tcp_sent_cb(void *arg, struct tcp_pcb *tpcb,
   return ERR_OK;
 }
 
-void mg_lwip_if_connect_tcp(struct mg_connection *nc,
-                            const union socket_address *sa) {
+struct mg_lwip_if_connect_tcp_ctx {
+  struct mg_connection *nc;
+  const union socket_address *sa;
+};
+
+static void mg_lwip_if_connect_tcp_tcpip(void *arg) {
+  struct mg_lwip_if_connect_tcp_ctx *ctx =
+      (struct mg_lwip_if_connect_tcp_ctx *) arg;
+  struct mg_connection *nc = ctx->nc;
+  const union socket_address *sa = ctx->sa;
+
   struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
   struct tcp_pcb *tpcb = TCP_NEW();
   cs->pcb.tcp = tpcb;
@@ -14281,12 +14302,17 @@ void mg_lwip_if_connect_tcp(struct mg_connection *nc,
   }
 }
 
+void mg_lwip_if_connect_tcp(struct mg_connection *nc,
+                            const union socket_address *sa) {
+  struct mg_lwip_if_connect_tcp_ctx ctx = {.nc = nc, .sa = sa};
+  tcpip_callback(mg_lwip_if_connect_tcp_tcpip, &ctx);
+}
+
 /*
  * Lwip included in the SDKs for nRF5x chips has different type for the
  * callback of `udp_recv()`
  */
-#if CS_PLATFORM == CS_P_NRF51 || CS_PLATFORM == CS_P_NRF52 || \
-    CS_PLATFORM == CS_P_STM32
+#if LWIP_VERSION >= 0x01050000
 static void mg_lwip_udp_recv_cb(void *arg, struct udp_pcb *pcb, struct pbuf *p,
                                 const ip_addr_t *addr, u16_t port)
 #else
@@ -14304,7 +14330,11 @@ static void mg_lwip_udp_recv_cb(void *arg, struct udp_pcb *pcb, struct pbuf *p,
     return;
   }
   union socket_address *sa = (union socket_address *) sap->payload;
+#if LWIP_VERSION >= 0x01050000
+  sa->sin.sin_addr.s_addr = ip_2_ip4(addr)->addr;
+#else
   sa->sin.sin_addr.s_addr = addr->addr;
+#endif
   sa->sin.sin_port = htons(port);
   /* Logic in the recv handler requires that there be exactly one data pbuf. */
   p = pbuf_coalesce(p, PBUF_RAW);
@@ -14353,7 +14383,8 @@ static void mg_lwip_handle_recv_udp(struct mg_connection *nc) {
   }
 }
 
-void mg_lwip_if_connect_udp(struct mg_connection *nc) {
+static void mg_lwip_if_connect_udp_tcpip(void *arg) {
+  struct mg_connection *nc = (struct mg_connection *) arg;
   struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
   struct udp_pcb *upcb = udp_new();
   cs->err = UDP_BIND(upcb, IP_ADDR_ANY, 0 /* any port */);
@@ -14367,6 +14398,10 @@ void mg_lwip_if_connect_udp(struct mg_connection *nc) {
   mg_lwip_post_signal(MG_SIG_CONNECT_RESULT, nc);
 }
 
+void mg_lwip_if_connect_udp(struct mg_connection *nc) {
+  tcpip_callback(mg_lwip_if_connect_udp_tcpip, nc);
+}
+
 void mg_lwip_accept_conn(struct mg_connection *nc, struct tcp_pcb *tpcb) {
   union socket_address sa;
   SET_ADDR(&sa, &tpcb->remote_ip);
@@ -14374,13 +14409,17 @@ void mg_lwip_accept_conn(struct mg_connection *nc, struct tcp_pcb *tpcb) {
   mg_if_accept_tcp_cb(nc, &sa, sizeof(sa.sin));
 }
 
+static void tcp_close_tcpip(void *arg) {
+  tcp_close((struct tcp_pcb *) arg);
+}
+
 void mg_lwip_handle_accept(struct mg_connection *nc) {
   struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
 #if MG_ENABLE_SSL
   if (cs->lc->flags & MG_F_SSL) {
     if (mg_ssl_if_conn_accept(nc, cs->lc) != MG_SSL_OK) {
       LOG(LL_ERROR, ("SSL error"));
-      tcp_close(cs->pcb.tcp);
+      tcpip_callback(tcp_close_tcpip, cs->pcb.tcp);
     }
   } else
 #endif
@@ -14415,7 +14454,16 @@ static err_t mg_lwip_accept_cb(void *arg, struct tcp_pcb *newtpcb, err_t err) {
   return ERR_OK;
 }
 
-int mg_lwip_if_listen_tcp(struct mg_connection *nc, union socket_address *sa) {
+struct mg_lwip_if_listen_ctx {
+  struct mg_connection *nc;
+  union socket_address *sa;
+  int ret;
+};
+
+static void mg_lwip_if_listen_tcp_tcpip(void *arg) {
+  struct mg_lwip_if_listen_ctx *ctx = (struct mg_lwip_if_listen_ctx *) arg;
+  struct mg_connection *nc = ctx->nc;
+  union socket_address *sa = ctx->sa;
   struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
   struct tcp_pcb *tpcb = TCP_NEW();
   ip_addr_t *ip = (ip_addr_t *) &sa->sin.sin_addr.s_addr;
@@ -14424,16 +14472,26 @@ int mg_lwip_if_listen_tcp(struct mg_connection *nc, union socket_address *sa) {
   DBG(("%p tcp_bind(%s:%u) = %d", nc, IPADDR_NTOA(ip), port, cs->err));
   if (cs->err != ERR_OK) {
     tcp_close(tpcb);
-    return -1;
+    ctx->ret = -1;
+    return;
   }
   tcp_arg(tpcb, nc);
   tpcb = tcp_listen(tpcb);
   cs->pcb.tcp = tpcb;
   tcp_accept(tpcb, mg_lwip_accept_cb);
-  return 0;
+  ctx->ret = 0;
 }
 
-int mg_lwip_if_listen_udp(struct mg_connection *nc, union socket_address *sa) {
+int mg_lwip_if_listen_tcp(struct mg_connection *nc, union socket_address *sa) {
+  struct mg_lwip_if_listen_ctx ctx = {.nc = nc, .sa = sa};
+  tcpip_callback(mg_lwip_if_listen_tcp_tcpip, &ctx);
+  return ctx.ret;
+}
+
+static void mg_lwip_if_listen_udp_tcpip(void *arg) {
+  struct mg_lwip_if_listen_ctx *ctx = (struct mg_lwip_if_listen_ctx *) arg;
+  struct mg_connection *nc = ctx->nc;
+  union socket_address *sa = ctx->sa;
   struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
   struct udp_pcb *upcb = udp_new();
   ip_addr_t *ip = (ip_addr_t *) &sa->sin.sin_addr.s_addr;
@@ -14442,24 +14500,43 @@ int mg_lwip_if_listen_udp(struct mg_connection *nc, union socket_address *sa) {
   DBG(("%p udb_bind(%s:%u) = %d", nc, IPADDR_NTOA(ip), port, cs->err));
   if (cs->err != ERR_OK) {
     udp_remove(upcb);
-    return -1;
+    ctx->ret = -1;
+  } else {
+    udp_recv(upcb, mg_lwip_udp_recv_cb, nc);
+    cs->pcb.udp = upcb;
+    ctx->ret = 0;
   }
-  udp_recv(upcb, mg_lwip_udp_recv_cb, nc);
-  cs->pcb.udp = upcb;
-  return 0;
 }
 
-int mg_lwip_tcp_write(struct mg_connection *nc, const void *data,
-                      uint16_t len) {
+int mg_lwip_if_listen_udp(struct mg_connection *nc, union socket_address *sa) {
+  struct mg_lwip_if_listen_ctx ctx = {.nc = nc, .sa = sa};
+  tcpip_callback(mg_lwip_if_listen_udp_tcpip, &ctx);
+  return ctx.ret;
+}
+
+struct mg_lwip_tcp_write_ctx {
+  struct mg_connection *nc;
+  const void *data;
+  uint16_t len;
+  int ret;
+};
+
+static void tcp_output_tcpip(void *arg) {
+  tcp_output((struct tcp_pcb *) arg);
+}
+
+static void mg_lwip_tcp_write_tcpip(void *arg) {
+  struct mg_lwip_tcp_write_ctx *ctx = (struct mg_lwip_tcp_write_ctx *) arg;
+  struct mg_connection *nc = ctx->nc;
   struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
   struct tcp_pcb *tpcb = cs->pcb.tcp;
-  if (tpcb == NULL) return -1;
-  len = MIN(tpcb->mss, MIN(len, tpcb->snd_buf));
+  uint16_t len = MIN(tpcb->mss, MIN(ctx->len, tpcb->snd_buf));
   if (len == 0) {
     DBG(("%p no buf avail %u %u %u %p %p", tpcb, tpcb->acked, tpcb->snd_buf,
          tpcb->snd_queuelen, tpcb->unsent, tpcb->unacked));
-    tcp_output(tpcb);
-    return 0;
+    tcpip_callback(tcp_output_tcpip, tpcb);
+    ctx->ret = 0;
+    return;
   }
 /*
  * On ESP8266 we only allow one TCP segment in flight at any given time.
@@ -14469,22 +14546,49 @@ int mg_lwip_tcp_write(struct mg_connection *nc, const void *data,
  */
 #if CS_PLATFORM == CS_P_ESP8266
   if (tpcb->unacked != NULL) {
-    return 0;
+    ctx->ret = 0;
+    return;
   }
   if (tpcb->unsent != NULL) {
     len = MIN(len, (TCP_MSS - tpcb->unsent->len));
   }
 #endif
-  cs->err = tcp_write(tpcb, data, len, TCP_WRITE_FLAG_COPY);
+  cs->err = tcp_write(tpcb, ctx->data, len, TCP_WRITE_FLAG_COPY);
   DBG(("%p tcp_write %u = %d", tpcb, len, cs->err));
   if (cs->err != ERR_OK) {
     /*
      * We ignore ERR_MEM because memory will be freed up when the data is sent
      * and we'll retry.
      */
-    return (cs->err == ERR_MEM ? 0 : -1);
+    ctx->ret = (cs->err == ERR_MEM ? 0 : -1);
+    return;
   }
-  return len;
+  ctx->ret = len;
+}
+
+static int mg_lwip_tcp_write(struct mg_connection *nc, const void *data,
+                             uint16_t len) {
+  struct mg_lwip_tcp_write_ctx ctx = {.nc = nc, .data = data, .len = len};
+  struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
+  struct tcp_pcb *tpcb = cs->pcb.tcp;
+  if (tpcb == NULL) {
+    return -1;
+  }
+  tcpip_callback(mg_lwip_tcp_write_tcpip, &ctx);
+  return ctx.ret;
+}
+
+struct udp_sendto_ctx {
+  struct udp_pcb *upcb;
+  struct pbuf *p;
+  ip_addr_t *ip;
+  uint16_t port;
+  int ret;
+};
+
+static void udp_sendto_tcpip(void *arg) {
+  struct udp_sendto_ctx *ctx = (struct udp_sendto_ctx *) arg;
+  ctx->ret = udp_sendto(ctx->upcb, ctx->p, ctx->ip, ctx->port);
 }
 
 static int mg_lwip_udp_send(struct mg_connection *nc, const void *data,
@@ -14508,7 +14612,9 @@ static int mg_lwip_udp_send(struct mg_connection *nc, const void *data,
     return 0;
   }
   memcpy(p->payload, data, len);
-  cs->err = udp_sendto(upcb, p, (ip_addr_t *) ip, port);
+  struct udp_sendto_ctx ctx = {.upcb = upcb, .p = p, .ip = ip, .port = port};
+  tcpip_callback(udp_sendto_tcpip, &ctx);
+  cs->err = ctx.ret;
   DBG(("%p udp_sendto = %d", nc, cs->err));
   pbuf_free(p);
   return (cs->err == ERR_OK ? len : -1);
@@ -14544,6 +14650,16 @@ void mg_lwip_if_udp_send(struct mg_connection *nc, const void *buf,
   mg_lwip_mgr_schedule_poll(nc->mgr);
 }
 
+struct tcp_recved_ctx {
+  struct tcp_pcb *tpcb;
+  size_t len;
+};
+
+void tcp_recved_tcpip(void *arg) {
+  struct tcp_recved_ctx *ctx = (struct tcp_recved_ctx *) arg;
+  tcp_recved(ctx->tpcb, ctx->len);
+}
+
 void mg_lwip_if_recved(struct mg_connection *nc, size_t len) {
   if (nc->flags & MG_F_UDP) return;
   struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
@@ -14552,14 +14668,16 @@ void mg_lwip_if_recved(struct mg_connection *nc, size_t len) {
     return;
   }
   DBG(("%p %p %u", nc, cs->pcb.tcp, len));
-/* Currently SSL acknowledges data immediately.
- * TODO(rojer): Find a way to propagate mg_lwip_if_recved. */
+  struct tcp_recved_ctx ctx = {.tpcb = cs->pcb.tcp, .len = len};
 #if MG_ENABLE_SSL
   if (!(nc->flags & MG_F_SSL)) {
-    tcp_recved(cs->pcb.tcp, len);
+    tcpip_callback(tcp_recved_tcpip, &ctx);
+  } else {
+    /* Currently SSL acknowledges data immediately.
+     * TODO(rojer): Find a way to propagate mg_lwip_if_recved. */
   }
 #else
-  tcp_recved(cs->pcb.tcp, len);
+  tcpip_callback(tcp_recved_tcpip, &ctx);
 #endif
   mbuf_trim(&nc->recv_mbuf);
 }
@@ -14573,6 +14691,10 @@ int mg_lwip_if_create_conn(struct mg_connection *nc) {
   return 1;
 }
 
+static void udp_remove_tcpip(void *arg) {
+  udp_remove((struct udp_pcb *) arg);
+}
+
 void mg_lwip_if_destroy_conn(struct mg_connection *nc) {
   if (nc->sock == INVALID_SOCKET) return;
   struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
@@ -14582,7 +14704,7 @@ void mg_lwip_if_destroy_conn(struct mg_connection *nc) {
       tcp_arg(tpcb, NULL);
       DBG(("%p tcp_close %p", nc, tpcb));
       tcp_arg(tpcb, NULL);
-      tcp_close(tpcb);
+      tcpip_callback(tcp_close_tcpip, tpcb);
     }
     while (cs->rx_chain != NULL) {
       struct pbuf *seg = cs->rx_chain;
@@ -14596,7 +14718,7 @@ void mg_lwip_if_destroy_conn(struct mg_connection *nc) {
     struct udp_pcb *upcb = cs->pcb.udp;
     if (upcb != NULL) {
       DBG(("%p udp_remove %p", nc, upcb));
-      udp_remove(upcb);
+      tcpip_callback(udp_remove_tcpip, upcb);
     }
     memset(cs, 0, sizeof(*cs));
     MG_FREE(cs);
@@ -14830,7 +14952,7 @@ time_t mg_lwip_if_poll(struct mg_iface *iface, int timeout_ms) {
     if (nc->sock != INVALID_SOCKET &&
         !(nc->flags & (MG_F_UDP | MG_F_LISTENING)) && cs->pcb.tcp != NULL &&
         cs->pcb.tcp->unsent != NULL) {
-      tcp_output(cs->pcb.tcp);
+      tcpip_callback(tcp_output_tcpip, cs->pcb.tcp);
     }
     if (nc->ev_timer_time > 0) {
       if (num_timers == 0 || nc->ev_timer_time < min_timer) {