diff --git a/.gitmodules b/.gitmodules
index 19b3794..17f8003 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,6 +1,6 @@
 [submodule "lwip/lwip"]
 	path = lwip/lwip
-	url = https://github.com/SuperHouse/esp-lwip.git
+	url = https://github.com/ourairquality/lwip.git
 [submodule "extras/mbedtls/mbedtls"]
 	path = extras/mbedtls/mbedtls
 	url = https://github.com/ARMmbed/mbedtls.git
diff --git a/README.md b/README.md
index 6f1a381..5617771 100644
--- a/README.md
+++ b/README.md
@@ -78,7 +78,7 @@ Current status is alpha quality, actively developed. AP STATION mode (ie wifi cl
 ## Open Source Components
 
 * [FreeRTOS](http://www.freertos.org/) V9.0.0
-* [lwIP](http://lwip.wikia.com/wiki/LwIP_Wiki) v1.4.1, modified via the [esp-lwip project](https://github.com/kadamski/esp-lwip) by @kadamski.
+* [lwIP](http://lwip.wikia.com/wiki/LwIP_Wiki) v2.0.3, with [some modifications](https://github.com/ourairquality/lwip/).
 * [newlib](https://github.com/projectgus/newlib-xtensa) v2.2.0, with patches for xtensa support and locking stubs for thread-safe operation on FreeRTOS.
 
 For details of how third party libraries are integrated, [see the wiki page](https://github.com/SuperHouse/esp-open-rtos/wiki/Third-Party-Libraries).
diff --git a/core/app_main.c b/core/app_main.c
index e56b0ab..5ea0851 100644
--- a/core/app_main.c
+++ b/core/app_main.c
@@ -311,8 +311,8 @@ static void init_g_ic(void) {
     if (sdk_g_ic.s._unknown310 > 4) {
         sdk_g_ic.s._unknown310 = 4;
     }
-    if (sdk_g_ic.s._unknown1e4._unknown1e4 == 0xffffffff) {
-        bzero(&sdk_g_ic.s._unknown1e4, sizeof(sdk_g_ic.s._unknown1e4));
+    if (sdk_g_ic.s.sta_ssid.ssid_length == 0xffffffff) {
+        bzero(&sdk_g_ic.s.sta_ssid, sizeof(sdk_g_ic.s.sta_ssid));
         bzero(&sdk_g_ic.s.sta_password, sizeof(sdk_g_ic.s.sta_password));
     }
     sdk_g_ic.s.wifi_led_enable = 0;
diff --git a/core/include/sdk_internal.h b/core/include/sdk_internal.h
index 1847eef..3dfa8df 100644
--- a/core/include/sdk_internal.h
+++ b/core/include/sdk_internal.h
@@ -14,12 +14,12 @@
 // 'info' is declared in app_main.o at .bss+0x4
 
 struct sdk_info_st {
-    ip_addr_t softap_ipaddr;    // 0x00
-    ip_addr_t softap_netmask;   // 0x04
-    ip_addr_t softap_gw;        // 0x08
-    ip_addr_t sta_ipaddr;       // 0x0c
-    ip_addr_t sta_netmask;      // 0x10
-    ip_addr_t sta_gw;           // 0x14
+    ip4_addr_t softap_ipaddr;    // 0x00
+    ip4_addr_t softap_netmask;   // 0x04
+    ip4_addr_t softap_gw;        // 0x08
+    ip4_addr_t sta_ipaddr;       // 0x0c
+    ip4_addr_t sta_netmask;      // 0x10
+    ip4_addr_t sta_gw;           // 0x14
     uint8_t softap_mac_addr[6]; // 0x18
     uint8_t sta_mac_addr[6];    // 0x1e
 };
@@ -83,7 +83,7 @@ struct _unknown_wpa1 {
 };
 
 
-struct sdk_netif_conninfo {
+struct sdk_cnx_node {
     uint8_t mac_addr[6];
     uint8_t _unknown07[2];
 
@@ -105,10 +105,12 @@ struct sdk_netif_conninfo {
     uint16_t _unknown9c; // ieee80211_hostap. increases by one one each timer func called.
     uint16_t _unknown9e;
 
-    uint32_t _unknowna0[18];
+    uint32_t _unknowna0[17];
 
-    int8_t _unknowne8; //
-    int8_t _unknowne9; // ppInstallKey
+    void *_unknowne4;
+
+    uint8_t _unknowne8; //
+    uint8_t _unknowne9; // ppInstallKey
     int8_t _unknownea;
     int8_t _unknowneb;
 
@@ -128,9 +130,9 @@ struct sdk_g_ic_netif_info {
     uint32_t _unknown48;     // 0x48
     uint8_t _unknown4c;      // 0x4c
     uint8_t _unknown4d[59];  // 0x4d - 0x88
-    struct sdk_netif_conninfo *_unknown88;  // 0x88
+    struct sdk_cnx_node *_unknown88;  // 0x88
     uint32_t _unknown8c;     // 0x8c
-    struct sdk_netif_conninfo *conninfo[6]; // 0x90 - 0xa8
+    struct sdk_cnx_node *cnx_nodes[6]; // 0x90 - 0xa8
     uint8_t _unknowna8[12];  // 0xa8 - 0xb4
     struct _unknown_softap1 *_unknownb4;
     uint8_t statusb8;        // 0xb8 (arg of sta_status_set)
@@ -206,10 +208,9 @@ struct sdk_g_ic_volatile_st {
 };
 
 
-struct sdk_g_ic_unk0_st {
-    uint16_t _unknown1e4;  // sdk_wpa_config_profile
-    uint16_t _unknown1e6;  // sdk_wpa_config_profile
-    uint8_t sta_ssid[32];  // 0x1e8 Station ssid. Null terminated string.
+struct sdk_g_ic_ssid_with_length {
+    uint32_t ssid_length;  // 0x1e4 sdk_wpa_config_profile
+    uint8_t ssid[32];  // 0x1e8 Station ssid. Null terminated string.
 };
 
 // This is the portion of g_ic which is loaded/saved to the flash ROM, and thus
@@ -224,7 +225,8 @@ struct sdk_g_ic_saved_st {
     uint8_t wifi_led_gpio;
     uint8_t wifi_led_state;  // 0 or 1.
 
-    struct sdk_g_ic_unk0_st _unknown1e4;
+    // Current station ap config ssid and length.
+    struct sdk_g_ic_ssid_with_length sta_ssid; // 0x1e4
 
     uint8_t _unknown208;
     uint8_t _unknown209; // sdk_wpa_config_profile
@@ -260,7 +262,7 @@ struct sdk_g_ic_saved_st {
     uint8_t _unknown30d; // result of ieee80211_chan2ieee
     uint8_t _unknown30e;
     uint8_t _unknown30f;
-    uint8_t _unknown310; // count of entries in the softap conninfo array, less two.
+    uint8_t _unknown310; // count of entries in the softap cnx_node array, less two.
 
     uint8_t _unknown311[3];
 
@@ -331,8 +333,8 @@ _Static_assert(offsetof(struct _unknown_softap1, _unknown18) == 0x18, "bad struc
 _Static_assert(sizeof(struct _unknown_wpa1) == 0x4c, "_unknown_wpa1 is the wrong size!");
 _Static_assert(offsetof(struct _unknown_wpa1, _unknown48) == 0x48, "bad struct");
 
-_Static_assert(offsetof(struct sdk_netif_conninfo, _unknown78) == 0x78, "bad struct");
-_Static_assert(offsetof(struct sdk_netif_conninfo, _unknown108) == 0x108, "bad struct");
+_Static_assert(offsetof(struct sdk_cnx_node, _unknown78) == 0x78, "bad struct");
+_Static_assert(offsetof(struct sdk_cnx_node, _unknown108) == 0x108, "bad struct");
 
 _Static_assert(offsetof(struct sdk_g_ic_netif_info, started) == 0xbb, "bad struct");
 
@@ -340,7 +342,7 @@ _Static_assert(sizeof(struct sdk_g_ic_volatile_st) == 0x1d8, "sdk_g_ic_volatile_
 _Static_assert(offsetof(struct sdk_g_ic_volatile_st, _unknown1d5) == 0x1d5, "bad struct");
 
 _Static_assert(sizeof(struct sdk_g_ic_saved_st) == 0x370, "sdk_g_ic_saved_st is the wrong size!");
-_Static_assert(offsetof(struct sdk_g_ic_saved_st, _unknown1e4) == 0x1e4 - 0x1d8, "bad struct");
+_Static_assert(offsetof(struct sdk_g_ic_saved_st, sta_ssid) == 0x1e4 - 0x1d8, "bad struct");
 _Static_assert(offsetof(struct sdk_g_ic_saved_st, _unknown546) == 0x546 - 0x1d8, "bad struct");
 
 _Static_assert(sizeof(struct sdk_g_ic_st) == 0x548, "sdk_g_ic_st is the wrong size!");
@@ -357,21 +359,16 @@ _Static_assert(offsetof(struct esf_buf, length) == 0x16, "bad struct");
 // ieee80211_output_pbuf and perhaps elsewhere. The value is just passed through
 // lwip and and not used by lwip so just ensure this slot is at the expected
 // offset.
-_Static_assert(offsetof(struct netif, state) == 28, "netif->state offset wrong!");
+_Static_assert(offsetof(struct netif, state) == 4, "netif->state offset wrong!");
 
 // Some sdk uses of netif->hwaddr have been converted to source code, but many
 // remain, but the content of this slot should not change in future versions of
-// lwip, so just ensure it is at the expected offset.
-_Static_assert(offsetof(struct netif, hwaddr) == 41, "netif->hwaddr offset wrong!");
+// lwip, so just ensure it is at the expected offset. Note the sdk binary
+// libraries have been patched to move this offset from 41 to 42 to keep it
+// 16-bit aligned to keep lwip v2 happy.
+_Static_assert(offsetof(struct netif, hwaddr) == 8, "netif->hwaddr offset wrong!");
 
-// Most sdk uses of the netif->flags have been converted to source code. One
-// known sdk binary read of the flags remains in wl_cnx.o:sdk_cnx_sta_leave
-// which checks the NETIF_FLAG_DHCP flag. The NETIF_FLAG_DHCP has been removed
-// in lwip v2, so some lwip hacks are needed to handle this for now until
-// wl_cnx.o is converted so source code too.
-_Static_assert(offsetof(struct netif, flags) == 47, "netif->flags offset wrong!");
-
-_Static_assert(offsetof(struct pbuf, eb) == 16, "pbuf->eb offset wrong!");
+_Static_assert(offsetof(struct pbuf, esf_buf) == 16, "pbuf->esf_buf offset wrong!");
 
 
 /// Misc.
diff --git a/examples/access_point/access_point.c b/examples/access_point/access_point.c
index f8fcd50..52c622f 100644
--- a/examples/access_point/access_point.c
+++ b/examples/access_point/access_point.c
@@ -48,7 +48,7 @@ void user_init(void)
     };
     sdk_wifi_softap_set_config(&ap_config);
 
-    ip_addr_t first_client_ip;
+    ip4_addr_t first_client_ip;
     IP4_ADDR(&first_client_ip, 172, 16, 0, 2);
     dhcpserver_start(&first_client_ip, 4);
 
@@ -65,14 +65,14 @@ static void telnetTask(void *pvParameters)
     printf("Status monitor: Failed to allocate socket.\r\n");
     return;
   }
-  netconn_bind(nc, IP_ADDR_ANY, TELNET_PORT);
+  netconn_bind(nc, IP_ANY_TYPE, TELNET_PORT);
   netconn_listen(nc);
 
   while(1) {
     struct netconn *client = NULL;
     err_t err = netconn_accept(nc, &client);
 
-    if ( err != ERR_OK ) {
+    if (err != ERR_OK) {
       if(client)
 	netconn_delete(client);
       continue;
@@ -88,9 +88,8 @@ static void telnetTask(void *pvParameters)
     netconn_write(client, buf, strlen(buf), NETCONN_COPY);
     snprintf(buf, sizeof(buf), "Free heap %d bytes\r\n", (int)xPortGetFreeHeapSize());
     netconn_write(client, buf, strlen(buf), NETCONN_COPY);
-    snprintf(buf, sizeof(buf), "Your address is %d.%d.%d.%d\r\n\r\n",
-             ip4_addr1(&client_addr), ip4_addr2(&client_addr),
-             ip4_addr3(&client_addr), ip4_addr4(&client_addr));
+    char abuf[40];
+    snprintf(buf, sizeof(buf), "Your address is %s\r\n\r\n", ipaddr_ntoa_r(&client_addr, abuf, sizeof(abuf)));
     netconn_write(client, buf, strlen(buf), NETCONN_COPY);
     netconn_delete(client);
   }
diff --git a/examples/http_get/http_get.c b/examples/http_get/http_get.c
index 0fe0ae1..f59aa8e 100644
--- a/examples/http_get/http_get.c
+++ b/examples/http_get/http_get.c
@@ -20,9 +20,9 @@
 
 #include "ssid_config.h"
 
-#define WEB_SERVER "chainxor.org"
+#define WEB_SERVER "ipv6.google.com"
 #define WEB_PORT 80
-#define WEB_URL "http://chainxor.org/"
+#define WEB_PATH "/"
 
 void http_get_task(void *pvParameters)
 {
@@ -31,7 +31,7 @@ void http_get_task(void *pvParameters)
 
     while(1) {
         const struct addrinfo hints = {
-            .ai_family = AF_INET,
+            .ai_family = AF_UNSPEC,
             .ai_socktype = SOCK_STREAM,
         };
         struct addrinfo *res;
@@ -39,7 +39,7 @@ void http_get_task(void *pvParameters)
         printf("Running DNS lookup for %s...\r\n", WEB_SERVER);
         int err = getaddrinfo(WEB_SERVER, "80", &hints, &res);
 
-        if(err != 0 || res == NULL) {
+        if (err != 0 || res == NULL) {
             printf("DNS lookup failed err=%d res=%p\r\n", err, res);
             if(res)
                 freeaddrinfo(res);
@@ -47,9 +47,24 @@ void http_get_task(void *pvParameters)
             failures++;
             continue;
         }
-        /* Note: inet_ntoa is non-reentrant, look at ipaddr_ntoa_r for "real" code */
-        struct in_addr *addr = &((struct sockaddr_in *)res->ai_addr)->sin_addr;
-        printf("DNS lookup succeeded. IP=%s\r\n", inet_ntoa(*addr));
+
+        {
+            struct netif *netif = sdk_system_get_netif(0);
+            int i;
+            for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+                printf("  ip6 %d state %x\n", i, netif_ip6_addr_state(netif, i));
+                if (!ip6_addr_isinvalid(netif_ip6_addr_state(netif, i)))
+                    printf("  ip6 addr %d = %s\n", i, ip6addr_ntoa(netif_ip6_addr(netif, i)));
+            }
+        }
+
+        struct sockaddr *sa = res->ai_addr;
+        if (sa->sa_family == AF_INET) {
+            printf("DNS lookup succeeded. IP=%s\r\n", inet_ntoa(((struct sockaddr_in *)sa)->sin_addr));
+        }
+        if (sa->sa_family == AF_INET6) {
+            printf("DNS lookup succeeded. IP=%s\r\n", inet6_ntoa(((struct sockaddr_in6 *)sa)->sin6_addr));
+        }
 
         int s = socket(res->ai_family, res->ai_socktype, 0);
         if(s < 0) {
@@ -75,8 +90,10 @@ void http_get_task(void *pvParameters)
         freeaddrinfo(res);
 
         const char *req =
-            "GET "WEB_URL"\r\n"
+            "GET "WEB_PATH" HTTP/1.1\r\n"
+            "Host: "WEB_SERVER"\r\n"
             "User-Agent: esp-open-rtos/0.1 esp8266\r\n"
+            "Connection: close\r\n"
             "\r\n";
         if (write(s, req, strlen(req)) < 0) {
             printf("... socket send failed\r\n");
@@ -126,6 +143,6 @@ void user_init(void)
     sdk_wifi_set_opmode(STATION_MODE);
     sdk_wifi_station_set_config(&config);
 
-    xTaskCreate(&http_get_task, "get_task", 256, NULL, 2, NULL);
+    xTaskCreate(&http_get_task, "get_task", 384, NULL, 2, NULL);
 }
 
diff --git a/examples/tls_server/tls_server.c b/examples/tls_server/tls_server.c
index 1317785..8ecfa56 100644
--- a/examples/tls_server/tls_server.c
+++ b/examples/tls_server/tls_server.c
@@ -202,9 +202,8 @@ void tls_server_task(void *pvParameters)
         socklen_t peer_addr_len = sizeof(struct sockaddr_in);
         getpeername(client_ctx.fd, (struct sockaddr *)&peer_addr, &peer_addr_len);
         unsigned char buf[256];
-        int len = sprintf((char *) buf, "O hai, client %d.%d.%d.%d:%d\nFree heap size is %d bytes\n",
-                          ip4_addr1(&peer_addr.sin_addr), ip4_addr2(&peer_addr.sin_addr),
-                          ip4_addr3(&peer_addr.sin_addr), ip4_addr4(&peer_addr.sin_addr),
+        int len = sprintf((char *) buf, "O hai, client " IPSTR ":%d\nFree heap size is %d bytes\n",
+                          IP2STR((ip4_addr_t *)&peer_addr.sin_addr.s_addr),
                           peer_addr.sin_port, xPortGetFreeHeapSize());
         while((ret = mbedtls_ssl_write(&ssl, buf, len)) <= 0)
         {
diff --git a/examples/tls_server_bearssl/tls_server_bearssl.c b/examples/tls_server_bearssl/tls_server_bearssl.c
index 09632ae..b1e48dc 100644
--- a/examples/tls_server_bearssl/tls_server_bearssl.c
+++ b/examples/tls_server_bearssl/tls_server_bearssl.c
@@ -146,9 +146,8 @@ void tls_server_task(void *pvParameters)
 
         /* Prepare a message to the client */
         unsigned char buf[100];
-        int len = sprintf((char *) buf, "O hai, client %d.%d.%d.%d:%d\r\nFree heap size is %d bytes\r\n",
-                          ip4_addr1(&sa.sin_addr), ip4_addr2(&sa.sin_addr),
-                          ip4_addr3(&sa.sin_addr), ip4_addr4(&sa.sin_addr),
+        int len = sprintf((char *) buf, "O hai, client " IPSTR ":%d\r\nFree heap size is %d bytes\r\n",
+                          IP2STR((ip4_addr_t *)&sa.sin_addr.s_addr),
                           ntohs(sa.sin_port), xPortGetFreeHeapSize());
 
         /* Send the message and close the connection */
diff --git a/examples/upnp/lwipopts.h b/examples/upnp/lwipopts.h
index 80c105b..56f45f2 100644
--- a/examples/upnp/lwipopts.h
+++ b/examples/upnp/lwipopts.h
@@ -1,7 +1,5 @@
 
 #define LWIP_IGMP 1
 
-#define LWIP_POSIX_SOCKETS_IO_NAMES     0
-
 /* Use the defaults for everything else */
 #include_next <lwipopts.h>
diff --git a/examples/upnp/upnp.c b/examples/upnp/upnp.c
index a223d27..bb34cae 100644
--- a/examples/upnp/upnp.c
+++ b/examples/upnp/upnp.c
@@ -1,6 +1,7 @@
 #include <string.h>
 #include <lwip/udp.h>
 #include <lwip/igmp.h>
+#include <lwip/ip_addr.h>
 #include <espressif/esp_common.h>
 #include "upnp.h"
 
@@ -18,13 +19,13 @@ static const char* get_my_ip(void)
 }
 
 /**
-  * @brief This function joins a multicast group witht he specified ip/port
+  * @brief This function joins a multicast group with the specified ip/port
   * @param group_ip the specified multicast group ip
   * @param group_port the specified multicast port number
   * @param recv the lwip UDP callback
   * @retval udp_pcb* or NULL if joining failed
   */
-static struct udp_pcb* mcast_join_group(char *group_ip, uint16_t group_port, void (* recv)(void * arg, struct udp_pcb * upcb, struct pbuf * p, struct ip_addr * addr, u16_t port))
+static struct udp_pcb* mcast_join_group(char *group_ip, uint16_t group_port, void (* recv)(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr, u16_t port))
 {
     bool status = false;
     struct udp_pcb *upcb;
@@ -36,7 +37,7 @@ static struct udp_pcb* mcast_join_group(char *group_ip, uint16_t group_port, voi
             printf("Error, udp_new failed");
             break;
         }
-        udp_bind(upcb, IP_ADDR_ANY, group_port);
+        udp_bind(upcb, IP4_ADDR_ANY, group_port);
         struct netif* netif = sdk_system_get_netif(STATION_IF);
         if (!netif) {
             printf("Error, netif is null");
@@ -46,10 +47,10 @@ static struct udp_pcb* mcast_join_group(char *group_ip, uint16_t group_port, voi
             netif->flags |= NETIF_FLAG_IGMP;
             igmp_start(netif);
         }
-        ip_addr_t ipgroup;
-        ipaddr_aton(group_ip, &ipgroup);
-        err_t err = igmp_joingroup(&netif->ip_addr, &ipgroup);
-        if(ERR_OK != err) {
+        ip4_addr_t ipgroup;
+        ip4addr_aton(group_ip, &ipgroup);
+        err_t err = igmp_joingroup_netif(netif, &ipgroup);
+        if (ERR_OK != err) {
             printf("Failed to join multicast group: %d", err);
             break;
         }
@@ -68,7 +69,7 @@ static struct udp_pcb* mcast_join_group(char *group_ip, uint16_t group_port, voi
     return upcb;
 }
 
-static void send(struct udp_pcb *upcb, struct ip_addr *addr, u16_t port)
+static void send_udp(struct udp_pcb *upcb, const ip_addr_t *addr, u16_t port)
 {
     struct pbuf *p;
     char msg[500];
@@ -110,14 +111,14 @@ static void send(struct udp_pcb *upcb, struct ip_addr *addr, u16_t port)
   * @param port the remote port from which the packet was received
   * @retval None
   */
-static void receive_callback(void *arg, struct udp_pcb *upcb, struct pbuf *p, struct ip_addr *addr, u16_t port)
+static void receive_callback(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
 {
     if (p) {
         printf("Msg received port:%d len:%d\n", port, p->len);
         uint8_t *buf = (uint8_t*) p->payload;
         printf("Msg received port:%d len:%d\nbuf: %s\n", port, p->len, buf);
         
-        send(upcb, addr, port);
+        send_udp(upcb, addr, port);
 
         pbuf_free(p);
     }
diff --git a/extras/dhcpserver/dhcpserver.c b/extras/dhcpserver/dhcpserver.c
index d87daab..934beb2 100644
--- a/extras/dhcpserver/dhcpserver.c
+++ b/extras/dhcpserver/dhcpserver.c
@@ -14,18 +14,22 @@
  * BSD Licensed as described in the file LICENSE
  */
 #include <string.h>
+#include <strings.h>
 
 #include <FreeRTOS.h>
 #include <task.h>
 #include <lwip/netif.h>
 #include <lwip/api.h>
+#include "esplibs/libmain.h"
 
 /* Grow the size of the lwip dhcp_msg struct's options field, as LWIP
    defaults to a 68 octet options field for its DHCP client, and most
    full-sized clients send us more than this. */
 #define DHCP_OPTIONS_LEN 312
 
-#include <lwip/dhcp.h>
+#include <lwip/ip.h>
+#include <lwip/prot/dhcp.h>
+#include <lwip/prot/iana.h>
 
 _Static_assert(sizeof(struct dhcp_msg) == offsetof(struct dhcp_msg, options) + 312, "dhcp_msg_t should have extended options size");
 
@@ -35,15 +39,20 @@ _Static_assert(sizeof(struct dhcp_msg) == offsetof(struct dhcp_msg, options) + 3
 
 typedef struct {
     uint8_t hwaddr[NETIF_MAX_HWADDR_LEN];
+    uint8_t active;
     uint32_t expires;
 } dhcp_lease_t;
 
 typedef struct {
     struct netconn *nc;
     uint8_t max_leases;
-    ip_addr_t first_client_addr;
+    ip4_addr_t first_client_addr;
     struct netif *server_if;
     dhcp_lease_t *leases; /* length max_leases */
+    /* Optional router */
+    ip4_addr_t router;
+    /* Optional DNS server */
+    ip4_addr_t dns;
 } server_state_t;
 
 /* Only one DHCP server task can run at once, so we have global state
@@ -68,51 +77,67 @@ static uint8_t *add_dhcp_option_bytes(uint8_t *opt, uint8_t type, void *value, u
 static dhcp_lease_t *find_lease_slot(uint8_t *hwaddr);
 
 /* Copy IP address as dotted decimal to 'dest', must be at least 16 bytes long */
-inline static void sprintf_ipaddr(const ip_addr_t *addr, char *dest)
+inline static void sprintf_ipaddr(const ip4_addr_t *addr, char *dest)
 {
-    if(addr == NULL)
+    if (addr == NULL)
         sprintf(dest, "NULL");
     else
         sprintf(dest, "%d.%d.%d.%d", ip4_addr1(addr),
                 ip4_addr2(addr), ip4_addr3(addr), ip4_addr4(addr));
 }
 
-void dhcpserver_start(const ip_addr_t *first_client_addr, uint8_t max_leases)
+void dhcpserver_start(const ip4_addr_t *first_client_addr, uint8_t max_leases)
 {
     /* Stop any existing running dhcpserver */
-    if(dhcpserver_task_handle)
+    if (dhcpserver_task_handle)
         dhcpserver_stop();
 
     state = malloc(sizeof(server_state_t));
     state->max_leases = max_leases;
     state->leases = calloc(max_leases, sizeof(dhcp_lease_t));
+    bzero(state->leases, max_leases * sizeof(dhcp_lease_t));
     // state->server_if is assigned once the task is running - see comment in dhcpserver_task()
-    ip_addr_copy(state->first_client_addr, *first_client_addr);
+    ip4_addr_copy(state->first_client_addr, *first_client_addr);
 
-    xTaskCreate(dhcpserver_task, "DHCPServer", 768, NULL, 8, &dhcpserver_task_handle);
+    /* Clear options */
+    ip4_addr_set_zero(&state->router);
+    ip4_addr_set_zero(&state->dns);
+
+    xTaskCreate(dhcpserver_task, "DHCP Server", 448, NULL, 2, &dhcpserver_task_handle);
 }
 
 void dhcpserver_stop(void)
 {
-    if(dhcpserver_task_handle) {
+    if (dhcpserver_task_handle) {
         vTaskDelete(dhcpserver_task_handle);
         free(state);
         dhcpserver_task_handle = NULL;
     }
 }
 
+void dhcpserver_set_router(const ip4_addr_t *router)
+{
+    ip4_addr_copy(state->router, *router);
+}
+
+void dhcpserver_set_dns(const ip4_addr_t *dns)
+{
+    ip4_addr_copy(state->dns, *dns);
+}
+
 static void dhcpserver_task(void *pxParameter)
 {
     /* netif_list isn't assigned until after user_init completes, which is why we do it inside the task */
     state->server_if = netif_list; /* TODO: Make this configurable */
 
     state->nc = netconn_new (NETCONN_UDP);
-    if(!state->nc) {
+    if (!state->nc) {
         printf("DHCP Server Error: Failed to allocate socket.\r\n");
         return;
     }
 
-    netconn_bind(state->nc, IP_ADDR_ANY, DHCP_SERVER_PORT);
+    netconn_bind(state->nc, IP4_ADDR_ANY, LWIP_IANA_PORT_DHCP_SERVER);
+    netconn_bind_if (state->nc, netif_get_index(state->server_if));
 
     while(1)
     {
@@ -121,29 +146,32 @@ static void dhcpserver_task(void *pxParameter)
 
         /* Receive a DHCP packet */
         err_t err = netconn_recv(state->nc, &netbuf);
-        if(err != ERR_OK) {
+        if (err != ERR_OK) {
             printf("DHCP Server Error: Failed to receive DHCP packet. err=%d\r\n", err);
             continue;
         }
 
         /* expire any leases that have passed */
         uint32_t now = xTaskGetTickCount();
-        for(int i = 0; i < state->max_leases; i++) {
-            uint32_t expires = state->leases[i].expires;
-            if(expires && expires < now)
-                state->leases[i].expires = 0;
+        for (int i = 0; i < state->max_leases; i++) {
+            if (state->leases[i].active) {
+                uint32_t expires = state->leases[i].expires - now;
+                if (expires >= 0x80000000) {
+                    state->leases[i].active = 0;
+                }
+            }
         }
 
         ip_addr_t received_ip;
         u16_t port;
         netconn_addr(state->nc, &received_ip, &port);
 
-        if(netbuf_len(netbuf) < offsetof(struct dhcp_msg, options)) {
+        if (netbuf_len(netbuf) < offsetof(struct dhcp_msg, options)) {
             /* too short to be a valid DHCP client message */
             netbuf_delete(netbuf);
             continue;
         }
-        if(netbuf_len(netbuf) >= sizeof(struct dhcp_msg)) {
+        if (netbuf_len(netbuf) >= sizeof(struct dhcp_msg)) {
            printf("DHCP Server Warning: Client sent more options than we know how to parse. len=%d\r\n", netbuf_len(netbuf));
         }
 
@@ -152,18 +180,18 @@ static void dhcpserver_task(void *pxParameter)
 
         uint8_t *message_type = find_dhcp_option(&received, DHCP_OPTION_MESSAGE_TYPE,
                                                  DHCP_OPTION_MESSAGE_TYPE_LEN, NULL);
-        if(!message_type) {
+        if (!message_type) {
             printf("DHCP Server Error: No message type field found");
             continue;
         }
 
-
         printf("State dump. Message type %d\n", *message_type);
-        for(int i = 0; i < state->max_leases; i++) {
+        for (int i = 0; i < state->max_leases; i++) {
             dhcp_lease_t *lease = &state->leases[i];
-            printf("lease slot %d expiry %d hwaddr %02x:%02x:%02x:%02x:%02x:%02x\r\n", i, lease->expires, lease->hwaddr[0],
-                   lease->hwaddr[1], lease->hwaddr[2], lease->hwaddr[3], lease->hwaddr[4],
-                   lease->hwaddr[5]);
+            printf("lease slot %d active %d expiry %d hwaddr %02x:%02x:%02x:%02x:%02x:%02x\r\n", i,
+                   lease->active, lease->expires - now,
+                   lease->hwaddr[0], lease->hwaddr[1], lease->hwaddr[2],
+                   lease->hwaddr[3], lease->hwaddr[4], lease->hwaddr[5]);
         }
 
         switch(*message_type) {
@@ -184,13 +212,13 @@ static void dhcpserver_task(void *pxParameter)
 
 static void handle_dhcp_discover(struct dhcp_msg *dhcpmsg)
 {
-    if(dhcpmsg->htype != DHCP_HTYPE_ETH)
+    if (dhcpmsg->htype != LWIP_IANA_HWTYPE_ETHERNET)
         return;
-    if(dhcpmsg->hlen > NETIF_MAX_HWADDR_LEN)
+    if (dhcpmsg->hlen > NETIF_MAX_HWADDR_LEN)
         return;
 
     dhcp_lease_t *freelease = find_lease_slot(dhcpmsg->chaddr);
-    if(!freelease) {
+    if (!freelease) {
         printf("DHCP Server: All leases taken.\r\n");
         return; /* Nothing available, so do nothing */
     }
@@ -199,13 +227,19 @@ static void handle_dhcp_discover(struct dhcp_msg *dhcpmsg)
     dhcpmsg->op = DHCP_BOOTREPLY;
     bzero(dhcpmsg->options, DHCP_OPTIONS_LEN);
 
-    ip_addr_copy(dhcpmsg->yiaddr, state->first_client_addr);
-    ip4_addr4(&(dhcpmsg->yiaddr)) += (freelease - state->leases);
+    dhcpmsg->yiaddr.addr = lwip_htonl(lwip_ntohl(state->first_client_addr.addr) + (freelease - state->leases));
 
     uint8_t *opt = (uint8_t *)&dhcpmsg->options;
     opt = add_dhcp_option_byte(opt, DHCP_OPTION_MESSAGE_TYPE, DHCP_OFFER);
     opt = add_dhcp_option_bytes(opt, DHCP_OPTION_SERVER_ID, &state->server_if->ip_addr, 4);
     opt = add_dhcp_option_bytes(opt, DHCP_OPTION_SUBNET_MASK, &state->server_if->netmask, 4);
+    if (!ip4_addr_isany_val(state->router)) {
+        opt = add_dhcp_option_bytes(opt, DHCP_OPTION_ROUTER, &state->router, 4);
+    }
+    if (!ip4_addr_isany_val(state->dns)) {
+        opt = add_dhcp_option_bytes(opt, DHCP_OPTION_DNS_SERVER, &state->dns, 4);
+    }
+
     opt = add_dhcp_option_bytes(opt, DHCP_OPTION_END, NULL, 0);
 
     struct netbuf *netbuf = netbuf_new();
@@ -218,17 +252,17 @@ static void handle_dhcp_discover(struct dhcp_msg *dhcpmsg)
 static void handle_dhcp_request(struct dhcp_msg *dhcpmsg)
 {
     static char ipbuf[16];
-    if(dhcpmsg->htype != DHCP_HTYPE_ETH)
+    if (dhcpmsg->htype != LWIP_IANA_HWTYPE_ETHERNET)
         return;
-    if(dhcpmsg->hlen > NETIF_MAX_HWADDR_LEN)
+    if (dhcpmsg->hlen > NETIF_MAX_HWADDR_LEN)
         return;
 
-    ip_addr_t requested_ip;
+    ip4_addr_t requested_ip;
     uint8_t *requested_ip_opt = find_dhcp_option(dhcpmsg, DHCP_OPTION_REQUESTED_IP, 4, NULL);
-    if(requested_ip_opt) {
-            memcpy(&requested_ip.addr, requested_ip_opt, 4);
-    } else if(ip_addr_cmp(&requested_ip, IP_ADDR_ANY)) {
-        ip_addr_copy(requested_ip, dhcpmsg->ciaddr);
+    if (requested_ip_opt) {
+        memcpy(&requested_ip.addr, requested_ip_opt, 4);
+    } else if (ip4_addr_cmp(&requested_ip, IP4_ADDR_ANY4)) {
+        ip4_addr_copy(requested_ip, dhcpmsg->ciaddr);
     } else {
         printf("DHCP Server Error: No requested IP\r\n");
         send_dhcp_nak(dhcpmsg);
@@ -236,7 +270,7 @@ static void handle_dhcp_request(struct dhcp_msg *dhcpmsg)
     }
 
     /* Test the first 4 octets match */
-    if(ip4_addr1(&requested_ip) != ip4_addr1(&state->first_client_addr)
+    if (ip4_addr1(&requested_ip) != ip4_addr1(&state->first_client_addr)
        || ip4_addr2(&requested_ip) != ip4_addr2(&state->first_client_addr)
        || ip4_addr3(&requested_ip) != ip4_addr3(&state->first_client_addr)) {
         sprintf_ipaddr(&requested_ip, ipbuf);
@@ -246,14 +280,14 @@ static void handle_dhcp_request(struct dhcp_msg *dhcpmsg)
     }
     /* Test the last octet is in the MAXCLIENTS range */
     int16_t octet_offs = ip4_addr4(&requested_ip) - ip4_addr4(&state->first_client_addr);
-    if(octet_offs < 0 || octet_offs >= state->max_leases) {
+    if (octet_offs < 0 || octet_offs >= state->max_leases) {
         printf("DHCP Server Error: Address out of range\r\n");
         send_dhcp_nak(dhcpmsg);
         return;
     }
 
     dhcp_lease_t *requested_lease = state->leases + octet_offs;
-    if(requested_lease->expires != 0 && memcmp(requested_lease->hwaddr, dhcpmsg->chaddr,dhcpmsg->hlen))
+    if (requested_lease->active && memcmp(requested_lease->hwaddr, dhcpmsg->chaddr,dhcpmsg->hlen))
     {
         printf("DHCP Server Error: Lease for address already taken\r\n");
         send_dhcp_nak(dhcpmsg);
@@ -265,13 +299,17 @@ static void handle_dhcp_request(struct dhcp_msg *dhcpmsg)
     printf("DHCP lease addr %s assigned to MAC %02x:%02x:%02x:%02x:%02x:%02x\r\n", ipbuf, requested_lease->hwaddr[0],
            requested_lease->hwaddr[1], requested_lease->hwaddr[2], requested_lease->hwaddr[3], requested_lease->hwaddr[4],
            requested_lease->hwaddr[5]);
-    requested_lease->expires = DHCPSERVER_LEASE_TIME * configTICK_RATE_HZ;
+    uint32_t now = xTaskGetTickCount();
+    requested_lease->expires = now + DHCPSERVER_LEASE_TIME * configTICK_RATE_HZ;
+    requested_lease->active = 1;
+
+    sdk_wifi_softap_set_station_info(requested_lease->hwaddr, &requested_ip);
 
     /* Reuse the REQUEST message as the ACK message */
     dhcpmsg->op = DHCP_BOOTREPLY;
     bzero(dhcpmsg->options, DHCP_OPTIONS_LEN);
 
-    ip_addr_copy(dhcpmsg->yiaddr, requested_ip);
+    ip4_addr_copy(dhcpmsg->yiaddr, requested_ip);
 
     uint8_t *opt = (uint8_t *)&dhcpmsg->options;
     opt = add_dhcp_option_byte(opt, DHCP_OPTION_MESSAGE_TYPE, DHCP_ACK);
@@ -279,6 +317,13 @@ static void handle_dhcp_request(struct dhcp_msg *dhcpmsg)
     opt = add_dhcp_option_bytes(opt, DHCP_OPTION_LEASE_TIME, &expiry, 4);
     opt = add_dhcp_option_bytes(opt, DHCP_OPTION_SERVER_ID, &state->server_if->ip_addr, 4);
     opt = add_dhcp_option_bytes(opt, DHCP_OPTION_SUBNET_MASK, &state->server_if->netmask, 4);
+    if (!ip4_addr_isany_val(state->router)) {
+        opt = add_dhcp_option_bytes(opt, DHCP_OPTION_ROUTER, &state->router, 4);
+    }
+    if (!ip4_addr_isany_val(state->dns)) {
+        opt = add_dhcp_option_bytes(opt, DHCP_OPTION_DNS_SERVER, &state->dns, 4);
+    }
+
     opt = add_dhcp_option_bytes(opt, DHCP_OPTION_END, NULL, 0);
 
     struct netbuf *netbuf = netbuf_new();
@@ -291,7 +336,8 @@ static void handle_dhcp_request(struct dhcp_msg *dhcpmsg)
 static void handle_dhcp_release(struct dhcp_msg *dhcpmsg)
 {
     dhcp_lease_t *lease = find_lease_slot(dhcpmsg->chaddr);
-    if(lease) {
+    if (lease) {
+        lease->active = 0;
         lease->expires = 0;
     }
 }
@@ -319,17 +365,17 @@ static uint8_t *find_dhcp_option(struct dhcp_msg *msg, uint8_t option_num, uint8
     uint8_t *start = (uint8_t *)&msg->options;
     uint8_t *msg_end = (uint8_t *)msg + sizeof(struct dhcp_msg);
 
-    for(uint8_t *p = start; p < msg_end-2;) {
+    for (uint8_t *p = start; p < msg_end-2;) {
         uint8_t type = *p++;
         uint8_t len = *p++;
-        if(type == DHCP_OPTION_END)
+        if (type == DHCP_OPTION_END)
             return NULL;
-        if(p+len >= msg_end)
+        if (p+len >= msg_end)
             break; /* We've overrun our valid DHCP message size, or this isn't a valid option */
-        if(type == option_num) {
-            if(len < min_length)
+        if (type == option_num) {
+            if (len < min_length)
                 break;
-            if(length)
+            if (length)
                 *length = len;
             return p; /* start of actual option data */
         }
@@ -349,7 +395,7 @@ static uint8_t *add_dhcp_option_byte(uint8_t *opt, uint8_t type, uint8_t value)
 static uint8_t *add_dhcp_option_bytes(uint8_t *opt, uint8_t type, void *value, uint8_t len)
 {
     *opt++ = type;
-    if(len) {
+    if (len) {
         *opt++ = len;
         memcpy(opt, value, len);
     }
@@ -360,8 +406,8 @@ static uint8_t *add_dhcp_option_bytes(uint8_t *opt, uint8_t type, void *value, u
 static dhcp_lease_t *find_lease_slot(uint8_t *hwaddr)
 {
     dhcp_lease_t *empty_lease = NULL;
-    for(int i = 0; i < state->max_leases; i++) {
-        if(state->leases[i].expires == 0 && !empty_lease)
+    for (int i = 0; i < state->max_leases; i++) {
+        if (!state->leases[i].active && !empty_lease)
             empty_lease = &state->leases[i];
         else if (memcmp(hwaddr, state->leases[i].hwaddr, 6) == 0)
             return &state->leases[i];
diff --git a/extras/dhcpserver/include/dhcpserver.h b/extras/dhcpserver/include/dhcpserver.h
index 95a59b8..62fa0ac 100644
--- a/extras/dhcpserver/include/dhcpserver.h
+++ b/extras/dhcpserver/include/dhcpserver.h
@@ -26,14 +26,20 @@ extern "C" {
    to a client.  Subsequent lease addresses are calculated by
    incrementing the final octet of the IPv4 address, up to max_leases.
 */
-void dhcpserver_start(const ip_addr_t *first_client_addr, uint8_t max_leases);
+void dhcpserver_start(const ip4_addr_t *first_client_addr, uint8_t max_leases);
 
-void dhcpserver_get_lease(const ip_addr_t *first_client_addr, uint8_t max_leases);
+void dhcpserver_get_lease(const ip4_addr_t *first_client_addr, uint8_t max_leases);
 
 /* Stop DHCP server.
  */
 void dhcpserver_stop(void);
 
+/* Set a router address to send as an option. */
+void dhcpserver_set_router(const ip4_addr_t *router);
+
+/* Set a DNS address to send as an option. */
+void dhcpserver_set_dns(const ip4_addr_t *dns);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/extras/httpd/httpd.c b/extras/httpd/httpd.c
index f647ca6..253bc13 100644
--- a/extras/httpd/httpd.c
+++ b/extras/httpd/httpd.c
@@ -2675,7 +2675,7 @@ http_accept(void *arg, struct tcp_pcb *pcb, err_t err)
  * Initialize the httpd with the specified local address.
  */
 static void
-httpd_init_addr(ip_addr_t *local_addr)
+httpd_init_addr(const ip_addr_t *local_addr)
 {
   struct tcp_pcb *pcb;
   err_t err;
diff --git a/extras/sntp/sntp.c b/extras/sntp/sntp.c
index d69b60e..c034df2 100644
--- a/extras/sntp/sntp.c
+++ b/extras/sntp/sntp.c
@@ -38,7 +38,7 @@
 
 #include "sntp.h"
 
-#include "lwip/timers.h"
+#include "lwip/timeouts.h"
 #include "lwip/udp.h"
 #include "lwip/dns.h"
 #include "lwip/ip_addr.h"
@@ -136,12 +136,12 @@
 #define SNTP_STARTUP_DELAY          0
 #endif
 
-/** SNTP receive timeout - in milliseconds
+/** SNTP receive timeout - in seconds
  * Also used as retry timeout - this shouldn't be too low.
  * Default is 3 seconds.
  */
 #ifndef SNTP_RECV_TIMEOUT
-#define SNTP_RECV_TIMEOUT           3000
+#define SNTP_RECV_TIMEOUT           3
 #endif
 
 /** SNTP update delay - in milliseconds
@@ -384,8 +384,8 @@ sntp_request(void *arg)
       /* bind to local address */
       if (lwip_bind(sock, (struct sockaddr *)&local, sizeof(local)) == 0) {
         /* set recv timeout */
-        timeout = SNTP_RECV_TIMEOUT;
-        lwip_setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout));
+        const struct timeval timeout = { SNTP_RECV_TIMEOUT, 0 };
+        setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
 
         /* prepare SNTP request */
         sntp_initialize_request(&sntpmsg);
@@ -511,7 +511,7 @@ sntp_try_next_server(void* arg)
 
 /** UDP recv callback for the sntp pcb */
 static void
-sntp_recv(void *arg, struct udp_pcb* pcb, struct pbuf *p, ip_addr_t *addr, u16_t port)
+sntp_recv(void *arg, struct udp_pcb* pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
 {
   u8_t mode;
   u8_t stratum;
@@ -597,7 +597,7 @@ sntp_recv(void *arg, struct udp_pcb* pcb, struct pbuf *p, ip_addr_t *addr, u16_t
  * @param server_addr resolved IP address of the SNTP server
  */
 static void
-sntp_send_request(ip_addr_t *server_addr)
+sntp_send_request(const ip_addr_t *server_addr)
 {
   struct pbuf* p;
   p = pbuf_alloc(PBUF_TRANSPORT, SNTP_MSG_LEN, PBUF_RAM);
@@ -611,7 +611,7 @@ sntp_send_request(ip_addr_t *server_addr)
     pbuf_free(p);
 
     /* set up receive timeout: try next server or retry on timeout */
-    sys_timeout((u32_t)SNTP_RECV_TIMEOUT, sntp_try_next_server, NULL);
+    sys_timeout((u32_t)SNTP_RECV_TIMEOUT * 1000, sntp_try_next_server, NULL);
 #if SNTP_CHECK_RESPONSE >= 1
     /* save server address to verify it in sntp_recv */ 
     ip_addr_set(&sntp_last_server_address, server_addr);
@@ -629,7 +629,7 @@ sntp_send_request(ip_addr_t *server_addr)
  * DNS found callback when using DNS names as server address.
  */
 static void
-sntp_dns_found(const char* hostname, ip_addr_t *ipaddr, void *arg)
+sntp_dns_found(const char* hostname, const ip_addr_t *ipaddr, void *arg)
 {
   LWIP_UNUSED_ARG(hostname);
   LWIP_UNUSED_ARG(arg);
diff --git a/include/espressif/esp_wifi.h b/include/espressif/esp_wifi.h
index 5d9ba19..8932974 100644
--- a/include/espressif/esp_wifi.h
+++ b/include/espressif/esp_wifi.h
@@ -44,9 +44,9 @@ enum {
 };
 
 struct ip_info {
-    struct ip_addr ip;
-    struct ip_addr netmask;
-    struct ip_addr gw;
+    struct ip4_addr ip;
+    struct ip4_addr netmask;
+    struct ip4_addr gw;
 };
 
 bool sdk_wifi_get_ip_info(uint8_t if_index, struct ip_info *info);
diff --git a/include/espressif/sdk_private.h b/include/espressif/sdk_private.h
index fa643a7..7ae4336 100644
--- a/include/espressif/sdk_private.h
+++ b/include/espressif/sdk_private.h
@@ -20,7 +20,7 @@
 extern "C" {
 #endif
 
-struct ip_addr;
+struct ip4_addr;
 
 /*********************************************
 * Defined in libmain.a
@@ -43,17 +43,6 @@ int sdk_uart_rx_one_char(char *buf);
  */
 void sdk_os_putc(char c);
 
-/* Called when an IP gets set on the "station" (client) interface.
- */
-void sdk_system_station_got_ip_set(struct ip_addr *ip_addr, struct ip_addr *sn_mask, struct ip_addr *gw_addr);
-
-/* This is a no-op wrapper around ppRecycleRxPkt, which is defined in libpp.a
-
-   It's called when a pbuf is freed, and allows pp to reuse the 'eb' pointer to ESP-specific
-   pbuf data. (See esp-lwip pbuf.h)
- */
-void sdk_system_pp_recycle_rx_pkt(void *eb);
-
 #ifdef	__cplusplus
 }
 #endif
diff --git a/lib/allsymbols.rename b/lib/allsymbols.rename
index 5425845..cf30bc9 100644
--- a/lib/allsymbols.rename
+++ b/lib/allsymbols.rename
@@ -289,6 +289,7 @@ os_timer_disarm sdk_os_timer_disarm
 os_timer_setfn sdk_os_timer_setfn
 os_update_cpu_frequency sdk_os_update_cpu_frequency
 pbkdf2_sha1 sdk_pbkdf2_sha1
+pbuf_alloc sdk_pbuf_alloc
 pbus_set_rxbbgain sdk_pbus_set_rxbbgain
 pend_flag_noise_check sdk_pend_flag_noise_check
 pend_flag_periodic_cal sdk_pend_flag_periodic_cal
diff --git a/lib/libmain.a b/lib/libmain.a
index 60657ec..cd6f83a 100644
Binary files a/lib/libmain.a and b/lib/libmain.a differ
diff --git a/lib/libnet80211.a b/lib/libnet80211.a
index 4a36331..1146020 100644
Binary files a/lib/libnet80211.a and b/lib/libnet80211.a differ
diff --git a/lib/libpp.a b/lib/libpp.a
index 2747863..39631a2 100644
Binary files a/lib/libpp.a and b/lib/libpp.a differ
diff --git a/lib/libwpa.a b/lib/libwpa.a
index a8fa39a..1c99007 100644
Binary files a/lib/libwpa.a and b/lib/libwpa.a differ
diff --git a/lwip/component.mk b/lwip/component.mk
index 1bbe21c..ea35d05 100644
--- a/lwip/component.mk
+++ b/lwip/component.mk
@@ -1,14 +1,12 @@
 # Component makefile for LWIP
 
 LWIP_DIR = $(lwip_ROOT)lwip/src/
-INC_DIRS += $(LWIP_DIR)include $(ROOT)lwip/include $(lwip_ROOT)include $(LWIP_DIR)include/posix $(LWIP_DIR)include/ipv4 $(LWIP_DIR)include/ipv4/lwip $(LWIP_DIR)include/lwip
+INC_DIRS += $(LWIP_DIR)include $(ROOT)lwip/include $(lwip_ROOT)include $(LWIP_DIR)include/compat/posix $(LWIP_DIR)include/ipv4 $(LWIP_DIR)include/ipv4/lwip $(LWIP_DIR)include/lwip
 
 # args for passing into compile rule generation
 lwip_INC_DIR =  # all in INC_DIRS, needed for normal operation
-lwip_SRC_DIR = $(lwip_ROOT) $(LWIP_DIR)api $(LWIP_DIR)core $(LWIP_DIR)core/ipv4 $(LWIP_DIR)netif
-
-# LWIP 1.4.1 generates a single warning so we need to disable -Werror when building it
-lwip_CFLAGS = $(CFLAGS) -Wno-address
+lwip_SRC_DIR = $(lwip_ROOT) $(LWIP_DIR)api $(LWIP_DIR)core $(LWIP_DIR)core/ipv4 $(LWIP_DIR)core/ipv6 $(LWIP_DIR)netif
+lwip_SRC_DIR += $(LWIP_DIR)apps/*
 
 $(eval $(call component_compile_rules,lwip))
 
diff --git a/lwip/esp_interface.c b/lwip/esp_interface.c
index 6f37b5a..1edb508 100644
--- a/lwip/esp_interface.c
+++ b/lwip/esp_interface.c
@@ -37,6 +37,7 @@
  * Original Author: Simon Goldschmidt
  * Modified by Angus Gratton based on work by @kadamski/Espressif via esp-lwip project.
  */
+#include <string.h>
 #include "lwip/opt.h"
 
 #include "lwip/def.h"
@@ -44,71 +45,138 @@
 #include "lwip/pbuf.h"
 #include <lwip/stats.h>
 #include <lwip/snmp.h>
+#include "lwip/ip.h"
+#include "lwip/ethip6.h"
 #include "netif/etharp.h"
+#include "sysparam.h"
+#include "netif/ppp/pppoe.h"
 
 /* declared in libnet80211.a */
 int8_t sdk_ieee80211_output_pbuf(struct netif *ifp, struct pbuf* pb);
 
+/* Define those to better describe your network interface. */
+#define IFNAME0 'e'
+#define IFNAME1 'n'
+
+/**
+ * In this function, the hardware should be initialized.
+ * Called from ethernetif_init().
+ *
+ * @param netif the already initialized lwip network interface structure
+ *        for this ethernetif
+ */
+static void
+low_level_init(struct netif *netif)
+{
+    /* set MAC hardware address length */
+    netif->hwaddr_len = ETHARP_HWADDR_LEN;
+
+    /* maximum transfer unit */
+    netif->mtu = 1500;
+
+    /* device capabilities */
+    /* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */
+    netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP | NETIF_FLAG_IGMP | NETIF_FLAG_MLD6;
+
+    /* Do whatever else is needed to initialize interface. */
+}
+
+/**
+ * This function should do the actual transmission of the packet. The packet is
+ * contained in the pbuf that is passed to the function. This pbuf
+ * might be chained.
+ *
+ * @param netif the lwip network interface structure for this ethernetif
+ * @param p the MAC packet to send (e.g. IP packet including MAC addresses and type)
+ * @return ERR_OK if the packet could be sent
+ *         an err_t value if the packet couldn't be sent
+ *
+ * @note Returning ERR_MEM here if a DMA queue of your MAC is full can lead to
+ *       strange results. You might consider waiting for space in the DMA queue
+ *       to become available since the stack doesn't retry to send a packet
+ *       dropped because of memory failure (except for the TCP timers).
+ */
+#define SIZEOF_STRUCT_PBUF        LWIP_MEM_ALIGN_SIZE(sizeof(struct pbuf))
 static err_t
 low_level_output(struct netif *netif, struct pbuf *p)
 {
-  struct pbuf *q;
+    /*
+     * Note the pbuf reference count is generally one here, but not always. For
+     * example a buffer that had been queued by etharp_query() would have had
+     * its reference count increased to two, and the caller will free it on that
+     * return path.
+     */
 
-  for(q = p; q != NULL; q = q->next) {
-      sdk_ieee80211_output_pbuf(netif, q);
-  }
+    /* If the pbuf does not have contiguous data, or there is not enough room
+     * for the link layer header, or there are multiple pbufs in the chain then
+     * clone a pbuf to output. */
+    if ((p->type_internal & PBUF_TYPE_FLAG_STRUCT_DATA_CONTIGUOUS) == 0 ||
+        (u8_t *)p->payload < (u8_t *)p + SIZEOF_STRUCT_PBUF + PBUF_LINK_ENCAPSULATION_HLEN ||
+        p->next) {
+        struct pbuf *q = pbuf_clone(PBUF_RAW_TX, PBUF_RAM, p);
+        if (q == NULL) {
+            return ERR_MEM;
+        }
+        sdk_ieee80211_output_pbuf(netif, q);
+        /* The sdk will pbuf_ref the pbuf before returning and free it later
+         * when it has been sent so free the link to it here. */
+        pbuf_free(q);
+    } else {
+        /* The SDK modifies the eth_hdr, well the first 12 bytes of it at least,
+         * but otherwise leaves the payload unmodified so it can be reused by
+         * the caller. The only paths that appear to reuse the pbuf are in
+         * tcp_out for re-transmission of TCP segments, and these paths check
+         * that the number of references has returned to one before reusing the
+         * pbuf.
+         */
+        sdk_ieee80211_output_pbuf(netif, p);
+    }
 
-  LINK_STATS_INC(link.xmit);
-
-  return ERR_OK;
+    LINK_STATS_INC(link.xmit);
+    return ERR_OK;
 }
 
 
-err_t ethernetif_init(struct netif *netif)
-{
-  LWIP_ASSERT("netif != NULL", (netif != NULL));
-
-#if LWIP_NETIF_HOSTNAME
-  /* Initialize interface hostname */
-  netif->hostname = "lwip";
-#endif /* LWIP_NETIF_HOSTNAME */
-
-  /*
-   * Initialize the snmp variables and counters inside the struct netif.
-   * The last argument should be replaced with your link speed, in units
-   * of bits per second.
-   */
-  NETIF_INIT_SNMP(netif, snmp_ifType_ethernet_csmacd, LINK_SPEED_OF_YOUR_NETIF_IN_BPS);
-
-  // don't touch netif->state here, the field is used internally in the ESP SDK layers
-  netif->name[0] = 'e';
-  netif->name[1] = 'n';
-  netif->output = etharp_output;
-  netif->linkoutput = low_level_output;
-
-  /* low_level_init components */
-  netif->hwaddr_len = 6;
-  /* hwaddr seems to be set elsewhere, or (more likely) is set on tx by MAC layer */
-  netif->mtu = 1500;
-  netif->flags = NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP;
-
-  return ERR_OK;
-}
-
+/**
+ * This function should be called when a packet is ready to be read
+ * from the interface. It uses the function low_level_input() that
+ * should handle the actual reception of bytes from the network
+ * interface. Then the type of the received packet is determined and
+ * the appropriate input function is called.
+ *
+ * @param netif the lwip network interface structure for this ethernetif
+ */
 /* called from ieee80211_deliver_data with new IP frames */
 void ethernetif_input(struct netif *netif, struct pbuf *p)
 {
-    struct eth_hdr *ethhdr = p->payload;
-  /* examine packet payloads ethernet header */
+    struct eth_hdr *ethhdr;
 
+    if (p == NULL) {
+        return;
+    }
+
+    if (p->payload == NULL) {
+        return;
+    }
+
+    if (netif == NULL) {
+        return;
+    }
+
+    ethhdr = p->payload;
 
     switch(htons(ethhdr->type)) {
 	/* IP or ARP packet? */
     case ETHTYPE_IP:
+    case ETHTYPE_IPV6:
     case ETHTYPE_ARP:
-//  case ETHTYPE_IPV6:
+#if PPPOE_SUPPORT
+        /* PPPoE packet? */
+    case ETHTYPE_PPPOEDISC:
+    case ETHTYPE_PPPOE:
+#endif /* PPPOE_SUPPORT */
 	/* full packet send to tcpip_thread to process */
-	if (netif->input(p, netif)!=ERR_OK)
+	if (netif->input(p, netif) != ERR_OK)
 	{
 	    LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n"));
 	    pbuf_free(p);
@@ -122,3 +190,98 @@ void ethernetif_input(struct netif *netif, struct pbuf *p)
 	break;
     }
 }
+
+/* Since the pbuf_type definition has changed in lwip v2 and it is used by the
+ * sdk when calling pbuf_alloc, the SDK libraries have been modified to rename
+ * their references to pbuf_alloc to _pbufalloc allowing the pbuf_type to be
+ * rewritten here. Doing this here keeps this hack out of the lwip code, and
+ * ensures that this re-writing is only applied to the sdk calls to pbuf_alloc.
+ *
+ * The only pbuf types used by the SDK are type 0 for PBUF_RAM when writing
+ * data, and type 2 for the received data. The receive data path references
+ * internal buffer objects that need to be freed with custom code so a custom
+ * pbuf allocation type is used for these.
+ *
+ * The pbuf_layer is now also the header offset, but the sdk calls only call
+ * with a value of 3 which was PBUF_RAW and is now translated to a header
+ * offset of zero.
+ */
+struct pbuf *sdk_pbuf_alloc(pbuf_layer layer, u16_t length, pbuf_type type) {
+    if (type == 0) {
+        LWIP_ASSERT("Unexpected sdk_pbuf_alloc layer", layer == 3 || layer == 4);
+        return pbuf_alloc(PBUF_RAW_TX, length, PBUF_RAM);
+    } else if (type == 2) {
+        LWIP_ASSERT("Unexpected sdk_pbuf_alloc layer", layer == 3);
+        return pbuf_alloc_reference(NULL, length, PBUF_ALLOC_FLAG_RX | PBUF_TYPE_ALLOC_SRC_MASK_ESP_RX);
+    } else {
+        LWIP_ASSERT("Unexpected pbuf_alloc type", 0);
+        for (;;);
+    }
+}
+
+/**
+ * Should be called at the beginning of the program to set up the
+ * network interface. It calls the function low_level_init() to do the
+ * actual setup of the hardware.
+ *
+ * This function should be passed as a parameter to netif_add().
+ *
+ * @param netif the lwip network interface structure for this ethernetif
+ * @return ERR_OK if the loopif is initialized
+ *         ERR_MEM if private data couldn't be allocated
+ *         any other err_t on error
+ */
+err_t
+ethernetif_init(struct netif *netif)
+{
+    LWIP_ASSERT("netif != NULL", (netif != NULL));
+
+    /* The hwaddr is currently set by sdk_wifi_station_start or
+     * sdk_wifi_softap_start. */
+
+#if LWIP_IPV6
+    // Where to do this???
+    netif_create_ip6_linklocal_address(netif, 1);
+    netif->ip6_autoconfig_enabled = 1;
+    printf("ip6 link local address %s\n", ip6addr_ntoa(netif_ip6_addr(netif, 0)));
+#endif
+
+#if LWIP_NETIF_HOSTNAME
+    /* Initialize interface hostname */
+    char *hostname = NULL;
+    /* Disabled for now as there were reports of crashes here, sysparam issues */
+    /* sysparam_get_string("hostname", &hostname); */
+    if (hostname && strlen(hostname) == 0) {
+        free(hostname);
+        hostname = NULL;
+    }
+    netif->hostname = hostname;
+#endif /* LWIP_NETIF_HOSTNAME */
+
+    /*
+     * Initialize the snmp variables and counters inside the struct netif.
+     * The last argument should be replaced with your link speed, in units
+     * of bits per second.
+     */
+    NETIF_INIT_SNMP(netif, snmp_ifType_ethernet_csmacd, LINK_SPEED_OF_YOUR_NETIF_IN_BPS);
+
+    // don't touch netif->state here, the field is used internally in the ESP SDK layers
+    netif->name[0] = IFNAME0;
+    netif->name[1] = IFNAME1;
+    /* We directly use etharp_output() here to save a function call.
+     * You can instead declare your own function an call etharp_output()
+     * from it if you have to do some checks before sending (e.g. if link
+     * is available...) */
+#if LWIP_IPV4
+    netif->output = etharp_output;
+#endif /* LWIP_IPV4 */
+#if LWIP_IPV6
+    netif->output_ip6 = ethip6_output;
+#endif /* LWIP_IPV6 */
+    netif->linkoutput = low_level_output;
+
+    /* initialize the hardware */
+    low_level_init(netif);
+
+    return ERR_OK;
+}
diff --git a/lwip/include/arch/cc.h b/lwip/include/arch/cc.h
index 2922a54..5bf9d80 100644
--- a/lwip/include/arch/cc.h
+++ b/lwip/include/arch/cc.h
@@ -32,36 +32,18 @@
 #ifndef __ARCH_CC_H__
 #define __ARCH_CC_H__
 
-/* include ESP SDK prototypes as they're used in some LWIP routines */
-#include "espressif/sdk_private.h"
-
-/* ESP8266 SDK Interface
-
-   The lwip-esp stack is designed to be also compatible with other ESP8266 SDKs,
-   so we can't use our 'sdk_' prefixes there
-*/
-#define system_station_got_ip_set sdk_system_station_got_ip_set
-#define system_pp_recycle_rx_pkt sdk_system_pp_recycle_rx_pkt
-
 /* Include some files for defining library routines */
 #include <stdio.h> /* printf, fflush, FILE */
 #include <stdlib.h> /* abort */
 #include <stdint.h>
 #include <sys/time.h>
 #include <sys/errno.h>
+#include <esp/hwrand.h>
 
-#define ERRNO
-
-#define BYTE_ORDER LITTLE_ENDIAN
-
-/** @todo fix some warnings: don't use #pragma if compiling with cygwin gcc */
-#ifndef __GNUC__
-	#include <limits.h>
-	#pragma warning (disable: 4244) /* disable conversion warning (implicit integer promotion!) */
-	#pragma warning (disable: 4127) /* conditional expression is constant */
-	#pragma warning (disable: 4996) /* 'strncpy' was declared deprecated */
-	#pragma warning (disable: 4103) /* structure packing changed by including file */
-#endif
+struct ip4_addr;
+struct esf_buf;
+void sdk_system_station_got_ip_set(struct ip4_addr *, struct ip4_addr *, struct ip4_addr *);
+void sdk_system_pp_recycle_rx_pkt(struct esf_buf *);
 
 /* Define generic types used in lwIP */
 typedef uint8_t    u8_t;
@@ -112,4 +94,6 @@ typedef int sys_prot_t;
 #define LWIP_PLATFORM_HTONS(_n)  ((u16_t)((((_n) & 0xff) << 8) | (((_n) >> 8) & 0xff)))
 #define LWIP_PLATFORM_HTONL(_n)  ((u32_t)( (((_n) & 0xff) << 24) | (((_n) & 0xff00) << 8) | (((_n) >> 8)  & 0xff00) | (((_n) >> 24) & 0xff) ))
 
+#define LWIP_RAND()                         hwrand()
+
 #endif /* __ARCH_CC_H__ */
diff --git a/lwip/include/lwipopts.h b/lwip/include/lwipopts.h
index 8f46c7d..567c856 100644
--- a/lwip/include/lwipopts.h
+++ b/lwip/include/lwipopts.h
@@ -32,11 +32,15 @@
 #ifndef __LWIPOPTS_H__
 #define __LWIPOPTS_H__
 
-#define LWIP_ESP                            1
-#define ESP_RTOS                            1
-#define PBUF_RSV_FOR_WLAN                   1
-#define EBUF_LWIP                           1
+#define ESP_OPEN_RTOS                       1
+
+/* See tcp.c tcp_alloc(). */
+#ifndef ESP_TIMEWAIT_THRESHOLD
 #define ESP_TIMEWAIT_THRESHOLD              10000
+#endif
+
+/** LWIP_TIMEVAL_PRIVATE: if you want to use the struct timeval provided
+ * by your system, set this to 0 and include <sys/time.h> in cc.h */
 #define LWIP_TIMEVAL_PRIVATE                0
 
 /*
@@ -63,6 +67,32 @@
  */
 #define SMEMCPY(dst,src,len)            memcpy(dst,src,len)
 
+/*
+   ------------------------------------
+   ----------- Core locking -----------
+   ------------------------------------
+*/
+
+/**
+ * LWIP_TCPIP_CORE_LOCKING
+ * Creates a global mutex that is held during TCPIP thread operations.
+ * Can be locked by client code to perform lwIP operations without changing
+ * into TCPIP thread using callbacks. See LOCK_TCPIP_CORE() and
+ * UNLOCK_TCPIP_CORE().
+ * Your system should provide mutexes supporting priority inversion to use this.
+ */
+#define LWIP_TCPIP_CORE_LOCKING         1
+
+/**
+ * LWIP_TCPIP_CORE_LOCKING_INPUT: when LWIP_TCPIP_CORE_LOCKING is enabled,
+ * this lets tcpip_input() grab the mutex for input packets as well,
+ * instead of allocating a message and passing it to tcpip_thread.
+ *
+ * ATTENTION: this does not work when tcpip_input() is called from
+ * interrupt context!
+ */
+#define LWIP_TCPIP_CORE_LOCKING_INPUT   1
+
 /*
    ------------------------------------
    ---------- Memory options ----------
@@ -76,16 +106,20 @@
 #define MEM_LIBC_MALLOC        1
 
 /**
-* MEMP_MEM_MALLOC==1: Use mem_malloc/mem_free instead of the lwip pool allocator.
-* Especially useful with MEM_LIBC_MALLOC but handle with care regarding execution
-* speed and usage from interrupts!
-*/
+ * MEMP_MEM_MALLOC==1: Use mem_malloc/mem_free instead of the lwip pool allocator.
+ * Especially useful with MEM_LIBC_MALLOC but handle with care regarding execution
+ * speed (heap alloc can be much slower than pool alloc) and usage from interrupts
+ * (especially if your netif driver allocates PBUF_POOL pbufs for received frames
+ * from interrupt)!
+ * ATTENTION: Currently, this uses the heap for ALL pools (also for private pools,
+ * not only for internal pools defined in memp_std.h)!
+ */
 #define MEMP_MEM_MALLOC                 1
 
 /**
  * MEM_ALIGNMENT: should be set to the alignment of the CPU
- *    4 byte alignment -> #define MEM_ALIGNMENT 4
- *    2 byte alignment -> #define MEM_ALIGNMENT 2
+ *    4 byte alignment -> \#define MEM_ALIGNMENT 4
+ *    2 byte alignment -> \#define MEM_ALIGNMENT 2
  */
 #define MEM_ALIGNMENT           4
 
@@ -100,6 +134,14 @@
    ---------- ARP options -------
    --------------------------------
 */
+
+/**
+ * LWIP_ARP==1: Enable ARP functionality.
+ */
+#ifndef LWIP_ARP
+#define LWIP_ARP                        1
+#endif
+
 /**
  * ARP_QUEUEING==1: Multiple outgoing packets are queued during hardware address
  * resolution. By default, only the most recent packet is queued per IP address.
@@ -107,7 +149,9 @@
  * startup time. Set this to 1 if you know your application sends more than one
  * packet in a row to an IP address that is not in the ARP cache.
  */
+#ifndef ARP_QUEUEING
 #define ARP_QUEUEING                    1
+#endif
 
 /*
    --------------------------------
@@ -119,21 +163,27 @@
  * this option does not affect outgoing packet sizes, which can be controlled
  * via IP_FRAG.
  */
-#define IP_REASSEMBLY                   0
+#ifndef IP_REASSEMBLY
+#define IP_REASSEMBLY                   1
+#endif
 
 /**
  * IP_FRAG==1: Fragment outgoing IP packets if their size exceeds MTU. Note
  * that this option does not affect incoming packet sizes, which can be
  * controlled via IP_REASSEMBLY.
  */
+#ifndef IP_FRAG
 #define IP_FRAG                         1
+#endif
 
 /**
  * IP_REASS_MAXAGE: Maximum time (in multiples of IP_TMR_INTERVAL - so seconds, normally)
  * a fragmented IP packet waits for all fragments to arrive. If not all fragments arrived
  * in this time, the whole packet is discarded.
  */
+#ifndef IP_REASS_MAXAGE
 #define IP_REASS_MAXAGE                 3
+#endif
 
 /**
  * IP_REASS_MAX_PBUFS: Total maximum amount of pbufs waiting to be reassembled.
@@ -141,7 +191,9 @@
  * PBUF_POOL_SIZE > IP_REASS_MAX_PBUFS so that the stack is still able to receive
  * packets even if the maximum amount of fragments is enqueued for reassembly!
  */
-#define IP_REASS_MAX_PBUFS              10
+#ifndef IP_REASS_MAX_PBUFS
+#define IP_REASS_MAX_PBUFS              2
+#endif
 
 /*
    ----------------------------------
@@ -163,7 +215,16 @@
 /**
  * LWIP_DHCP==1: Enable DHCP module.
  */
+#ifndef LWIP_DHCP
 #define LWIP_DHCP                       1
+#endif
+
+/**
+ * DHCP_DOES_ARP_CHECK==1: Do an ARP check on the offered address.
+ */
+#ifndef DHCP_DOES_ARP_CHECK
+#define DHCP_DOES_ARP_CHECK             ((LWIP_DHCP) && (LWIP_ARP))
+#endif
 
 #define LWIP_DHCP_BOOTP_FILE            0
 
@@ -174,14 +235,21 @@
 */
 /*
    ----------------------------------
-   ---------- SNMP options ----------
+   ----- SNMP MIB2 support      -----
    ----------------------------------
 */
 /*
    ----------------------------------
-   ---------- IGMP options ----------
+   ----- Multicast/IGMP options -----
    ----------------------------------
 */
+/**
+ * LWIP_IGMP==1: Turn on IGMP module.
+ */
+#ifndef LWIP_IGMP
+#define LWIP_IGMP                       1
+#endif
+
 /*
    ----------------------------------
    ---------- DNS options -----------
@@ -191,10 +259,19 @@
  * LWIP_DNS==1: Turn on DNS module. UDP must be available for DNS
  * transport.
  */
+#ifndef LWIP_DNS
 #define LWIP_DNS                        1
+#endif
 
+/** DNS maximum number of entries to maintain locally. */
+#ifndef DNS_TABLE_SIZE
 #define DNS_TABLE_SIZE 1
+#endif
+
+/** DNS maximum host name length supported in the name table. */
+#ifndef DNS_MAX_NAME_LENGTH
 #define DNS_MAX_NAME_LENGTH 128
+#endif
 
 /*
    ---------------------------------
@@ -206,30 +283,74 @@
    ---------- TCP options ----------
    ---------------------------------
 */
-/**
- * TCP_QUEUE_OOSEQ==1: TCP will queue segments that arrive out of order.
- * Define to 0 if your device is low on memory.
- */
-#define TCP_QUEUE_OOSEQ                 0
-
-/*
- *     LWIP_EVENT_API==1: The user defines lwip_tcp_event() to receive all
- *         events (accept, sent, etc) that happen in the system.
- *     LWIP_CALLBACK_API==1: The PCB callback function is called directly
- *         for the event. This is the default.
-*/
-#define TCP_MSS                         1460
-
 /**
  * TCP_MAXRTX: Maximum number of retransmissions of data segments.
  */
+#ifndef TCP_MAXRTX
 #define TCP_MAXRTX                      6
-
+#endif
 
 /**
  * TCP_SYNMAXRTX: Maximum number of retransmissions of SYN segments.
  */
+#ifndef TCP_SYNMAXRTX
 #define TCP_SYNMAXRTX                   3
+#endif
+
+/**
+ * TCP_QUEUE_OOSEQ==1: TCP will queue segments that arrive out of order.
+ * Define to 0 if your device is low on memory.
+ */
+#ifndef TCP_QUEUE_OOSEQ
+#define TCP_QUEUE_OOSEQ                 1
+#endif
+
+/**
+ * TCP_MSS: TCP Maximum segment size. (default is 536, a conservative default,
+ * you might want to increase this.)
+ * For the receive side, this MSS is advertised to the remote side
+ * when opening a connection. For the transmit size, this MSS sets
+ * an upper limit on the MSS advertised by the remote host.
+ */
+#ifndef TCP_MSS
+#define TCP_MSS                         1460
+#endif
+
+/**
+ * TCP_OOSEQ_MAX_BYTES: The maximum number of bytes queued on ooseq per pcb.
+ * Default is 0 (no limit). Only valid for TCP_QUEUE_OOSEQ==1.
+ */
+#ifndef TCP_OOSEQ_MAX_BYTES
+#if TCP_OOSEQ_MAX_BYTES
+#define TCP_OOSEQ_MAX_BYTES             (2 * TCP_MSS)
+#endif
+#endif
+
+/**
+ * TCP_OOSEQ_MAX_PBUFS: The maximum number of pbufs queued on ooseq per pcb.
+ * Default is 0 (no limit). Only valid for TCP_QUEUE_OOSEQ==1.
+ */
+#ifndef TCP_OOSEQ_MAX_PBUFS
+#if TCP_OOSEQ_MAX_PBUFS
+#define TCP_OOSEQ_MAX_PBUFS             2
+#endif
+#endif
+
+/**
+ * TCP_LISTEN_BACKLOG: Enable the backlog option for tcp listen pcb.
+ */
+#ifndef TCP_LISTEN_BACKLOG
+#define TCP_LISTEN_BACKLOG              1
+#endif
+
+/**
+ * The maximum allowed backlog for TCP listen netconns.
+ * This backlog is used unless another is explicitly specified.
+ * 0xff is the maximum (u8_t).
+ */
+#ifndef TCP_DEFAULT_LISTEN_BACKLOG
+#define TCP_DEFAULT_LISTEN_BACKLOG      2
+#endif
 
 /*
    ----------------------------------
@@ -237,19 +358,50 @@
    ----------------------------------
 */
 
+/**
+ * PBUF_LINK_ENCAPSULATION_HLEN: the number of bytes that should be allocated
+ * for an additional encapsulation header before ethernet headers (e.g. 802.11)
+ *
+ * 1. LINK_HLEN 14Byte will be remove in WLAN layer
+ * 2. IEEE80211_HDR_MAX_LEN needs 40 bytes.
+ * 3. encryption needs exra 4 bytes ahead of actual data payload, and require
+ *     DAddr and SAddr to be 4-byte aligned.
+ * 4. TRANSPORT and IP are all 20, 4 bytes aligned, nice...
+ * 5. LCC add 6 bytes more, We don't consider WAPI yet...
+ * 6. define LWIP_MEM_ALIGN to be 4 Byte aligned, pbuf struct is 16B, Only thing may be
+ *     matter is ether_hdr is not 4B aligned.
+ *
+ * So, we need extra (40 + 4 - 14) = 30 and it's happen to be 4-Byte aligned
+ *
+ *    1. lwip
+ *         | empty 30B    | eth_hdr (14B)  | payload ...|
+ *              total: 44B ahead payload
+ *    2. net80211
+ *         | max 80211 hdr, 32B | ccmp/tkip iv (8B) | sec rsv(4B) | payload ...|
+ *              total: 40B ahead sec_rsv and 44B ahead payload
+ *
+ */
+#define PBUF_LINK_ENCAPSULATION_HLEN    36
+
 /*
    ------------------------------------------------
    ---------- Network Interfaces options ----------
    ------------------------------------------------
 */
 /**
- * LWIP_NETIF_TX_SINGLE_PBUF: if this is set to 1, lwIP tries to put all data
+ * LWIP_NETIF_HOSTNAME==1: use DHCP_OPTION_HOSTNAME with netif's hostname
+ * field.
+ */
+#ifndef LWIP_NETIF_HOSTNAME
+#define LWIP_NETIF_HOSTNAME             1
+#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
  * MACs that do not support scatter-gather.
  * Beware that this might involve CPU-memcpy before transmitting that would not
  * be needed without this flag! Use this only if you need to!
- *
- * @todo: TCP and IP-frag do not work with this, yet:
  */
 #define LWIP_NETIF_TX_SINGLE_PBUF             1
 
@@ -275,7 +427,9 @@
  * The stack size value itself is platform-dependent, but is passed to
  * sys_thread_new() when the thread is created.
  */
-#define TCPIP_THREAD_STACKSIZE          512			//not ok:384 
+#ifndef TCPIP_THREAD_STACKSIZE
+#define TCPIP_THREAD_STACKSIZE          768
+#endif
 
 /**
  * TCPIP_THREAD_PRIO: The priority assigned to the main tcpip thread.
@@ -327,30 +481,40 @@
  * LWIP_SO_SNDTIMEO==1: Enable send timeout for sockets/netconns and
  * SO_SNDTIMEO processing.
  */
+#ifndef LWIP_SO_SNDTIMEO
 #define LWIP_SO_SNDTIMEO                1
+#endif
 
 /**
  * LWIP_SO_RCVTIMEO==1: Enable receive timeout for sockets/netconns and
  * SO_RCVTIMEO processing.
  */
+#ifndef LWIP_SO_RCVTIMEO
 #define LWIP_SO_RCVTIMEO                1
+#endif
 
 /**
  * LWIP_TCP_KEEPALIVE==1: Enable TCP_KEEPIDLE, TCP_KEEPINTVL and TCP_KEEPCNT
  * options processing. Note that TCP_KEEPIDLE and TCP_KEEPINTVL have to be set
  * in seconds. (does not require sockets.c, and will affect tcp.c)
  */
+#ifndef LWIP_TCP_KEEPALIVE
 #define LWIP_TCP_KEEPALIVE              1
+#endif
 
 /**
  * LWIP_SO_RCVBUF==1: Enable SO_RCVBUF processing.
  */
+#ifndef LWIP_SO_RCVBUF
 #define LWIP_SO_RCVBUF                  0
+#endif
 
 /**
  * SO_REUSE==1: Enable SO_REUSEADDR option.
  */
+#ifndef SO_REUSE
 #define SO_REUSE                        1
+#endif
 
 /*
    ----------------------------------------
@@ -358,6 +522,20 @@
    ----------------------------------------
 */
 
+/**
+ * LWIP_STATS==1: Enable statistics collection in lwip_stats.
+ */
+#ifndef LWIP_STATS
+#define LWIP_STATS                      0
+#endif
+
+/**
+ * LWIP_STATS_DISPLAY==1: Compile in the statistics output functions.
+ */
+#ifndef LWIP_STATS_DISPLAY
+#define LWIP_STATS_DISPLAY              0
+#endif
+
 /*
    ---------------------------------
    ---------- PPP options ----------
@@ -375,6 +553,12 @@
    ---------- IPv6 options ---------------
    ---------------------------------------
 */
+/**
+ * LWIP_IPV6==1: Enable IPv6
+ */
+#ifndef LWIP_IPV6
+#define LWIP_IPV6                       0
+#endif
 
 /*
    ---------------------------------------
@@ -391,11 +575,29 @@
 // Uncomment this line, and set the individual debug options you want, for IP stack debug output
 //#define LWIP_DEBUG
 
+/**
+ * LWIP_DBG_MIN_LEVEL: After masking, the value of the debug is
+ * compared against this value. If it is smaller, then debugging
+ * messages are written.
+ */
+//#define LWIP_DBG_MIN_LEVEL              LWIP_DBG_LEVEL_WARNING
+
+/**
+ * LWIP_DBG_TYPES_ON: A mask that can be used to globally enable/disable
+ * debug messages of certain types.
+ */
+#define LWIP_DBG_TYPES_ON               LWIP_DBG_ON
+
 /**
  * ETHARP_DEBUG: Enable debugging in etharp.c.
  */
 #define ETHARP_DEBUG                    LWIP_DBG_OFF
 
+/**
+ * NETIF_DEBUG: Enable debugging in netif.c.
+ */
+#define NETIF_DEBUG                     LWIP_DBG_OFF
+
 /**
  * PBUF_DEBUG: Enable debugging in pbuf.c.
  */
@@ -406,50 +608,151 @@
  */
 #define API_LIB_DEBUG                   LWIP_DBG_OFF
 
+/**
+ * API_MSG_DEBUG: Enable debugging in api_msg.c.
+ */
+#define API_MSG_DEBUG                   LWIP_DBG_OFF
+
 /**
  * SOCKETS_DEBUG: Enable debugging in sockets.c.
  */
 #define SOCKETS_DEBUG                   LWIP_DBG_OFF
 
+/**
+ * ICMP_DEBUG: Enable debugging in icmp.c.
+ */
+#define ICMP_DEBUG                      LWIP_DBG_OFF
+
+/**
+ * IGMP_DEBUG: Enable debugging in igmp.c.
+ */
+#define IGMP_DEBUG                      LWIP_DBG_OFF
+
+/**
+ * INET_DEBUG: Enable debugging in inet.c.
+ */
+#define INET_DEBUG                      LWIP_DBG_OFF
+
 /**
  * IP_DEBUG: Enable debugging for IP.
  */
 #define IP_DEBUG                        LWIP_DBG_OFF
 
+/**
+ * IP_REASS_DEBUG: Enable debugging in ip_frag.c for both frag & reass.
+ */
+#define IP_REASS_DEBUG                  LWIP_DBG_OFF
+
+/**
+ * RAW_DEBUG: Enable debugging in raw.c.
+ */
+#define RAW_DEBUG                       LWIP_DBG_OFF
+
+/**
+ * MEM_DEBUG: Enable debugging in mem.c.
+ */
+#define MEM_DEBUG                       LWIP_DBG_OFF
+
 /**
  * MEMP_DEBUG: Enable debugging in memp.c.
  */
 #define MEMP_DEBUG                      LWIP_DBG_OFF
 
+/**
+ * SYS_DEBUG: Enable debugging in sys.c.
+ */
+#define SYS_DEBUG                       LWIP_DBG_OFF
+
+/**
+ * TIMERS_DEBUG: Enable debugging in timers.c.
+ */
+#define TIMERS_DEBUG                    LWIP_DBG_OFF
+
+/**
+ * TCP_DEBUG: Enable debugging for TCP.
+ */
+#define TCP_DEBUG                       LWIP_DBG_OFF
+
 /**
  * TCP_INPUT_DEBUG: Enable debugging in tcp_in.c for incoming debug.
  */
 #define TCP_INPUT_DEBUG                 LWIP_DBG_OFF
 
+/**
+ * TCP_FR_DEBUG: Enable debugging in tcp_in.c for fast retransmit.
+ */
+#define TCP_FR_DEBUG                    LWIP_DBG_OFF
+
+/**
+ * TCP_RTO_DEBUG: Enable debugging in TCP for retransmit
+ * timeout.
+ */
+#define TCP_RTO_DEBUG                   LWIP_DBG_OFF
+
+/**
+ * TCP_CWND_DEBUG: Enable debugging for TCP congestion window.
+ */
+#define TCP_CWND_DEBUG                  LWIP_DBG_OFF
+
+/**
+ * TCP_WND_DEBUG: Enable debugging in tcp_in.c for window updating.
+ */
+#define TCP_WND_DEBUG                   LWIP_DBG_OFF
+
 /**
  * TCP_OUTPUT_DEBUG: Enable debugging in tcp_out.c output functions.
  */
 #define TCP_OUTPUT_DEBUG                LWIP_DBG_OFF
 
 /**
- * UDP_DEBUG: Enable debugging in udp.c.
+ * TCP_RST_DEBUG: Enable debugging for TCP with the RST message.
  */
-#define UDP_DEBUG                     LWIP_DBG_OFF
+#define TCP_RST_DEBUG                   LWIP_DBG_OFF
 
 /**
- * ICMP_DEBUG: Enable debugging in udp.c.
+ * TCP_QLEN_DEBUG: Enable debugging for TCP queue lengths.
  */
-#define ICMP_DEBUG                     LWIP_DBG_OFF
+#define TCP_QLEN_DEBUG                  LWIP_DBG_OFF
+
+/**
+ * UDP_DEBUG: Enable debugging in UDP.
+ */
+#define UDP_DEBUG                       LWIP_DBG_OFF
 
 /**
  * TCPIP_DEBUG: Enable debugging in tcpip.c.
  */
 #define TCPIP_DEBUG                     LWIP_DBG_OFF
 
+/**
+ * SLIP_DEBUG: Enable debugging in slipif.c.
+ */
+#define SLIP_DEBUG                      LWIP_DBG_OFF
 
 /**
  * DHCP_DEBUG: Enable debugging in dhcp.c.
  */
 #define DHCP_DEBUG                      LWIP_DBG_OFF
 
+/**
+ * AUTOIP_DEBUG: Enable debugging in autoip.c.
+ */
+#define AUTOIP_DEBUG                    LWIP_DBG_OFF
+
+/**
+ * DNS_DEBUG: Enable debugging for DNS.
+ */
+#define DNS_DEBUG                       LWIP_DBG_OFF
+
+/**
+ * IP6_DEBUG: Enable debugging for IPv6.
+ */
+#define IP6_DEBUG                       LWIP_DBG_OFF
+
+/*
+   --------------------------------------------------
+   ---------- Performance tracking options ----------
+   --------------------------------------------------
+*/
+
 #endif /* __LWIPOPTS_H__ */
diff --git a/lwip/lwip b/lwip/lwip
index 3cf8d51..ae317fe 160000
--- a/lwip/lwip
+++ b/lwip/lwip
@@ -1 +1 @@
-Subproject commit 3cf8d514bd76e6ef77e6fa514d0ec6d96da7fd9a
+Subproject commit ae317fe74dd61ab5eacf5c83dfe8f2af4795421e
diff --git a/lwip/sys_arch.c b/lwip/sys_arch.c
index 87d8450..1a9c5f9 100644
--- a/lwip/sys_arch.c
+++ b/lwip/sys_arch.c
@@ -49,17 +49,147 @@
 #include "lwip/mem.h"
 #include "lwip/stats.h"
 
-extern bool esp_in_isr;
-
-/* Based on the default xInsideISR mechanism to determine
-   if an ISR is running.
-
-   Doesn't support the possibility that LWIP functions are called from the NMI
-   handler (none are called from NMI when using current/SDK implementation.)
-*/
-static inline bool is_inside_isr()
+/*---------------------------------------------------------------------------*
+ * Routine:  sys_sem_new
+ *---------------------------------------------------------------------------*
+ * Description:
+ *      Creates and returns a new semaphore. The "ucCount" argument specifies
+ *      the initial state of the semaphore.
+ *      NOTE: Currently this routine only creates counts of 1 or 0
+ * Inputs:
+ *      sys_mbox_t mbox         -- Handle of mailbox
+ *      u8_t ucCount            -- Initial ucCount of semaphore (1 or 0)
+ * Outputs:
+ *      sys_sem_t               -- Created semaphore or 0 if could not create.
+ *---------------------------------------------------------------------------*/
+err_t sys_sem_new(sys_sem_t *pxSemaphore, u8_t ucCount)
 {
-    return esp_in_isr;
+    err_t xReturn = ERR_MEM;
+
+    vSemaphoreCreateBinary(*pxSemaphore);
+
+    if (*pxSemaphore != NULL) {
+        if (ucCount == 0U) {
+            xSemaphoreTake(*pxSemaphore, 1UL);
+        }
+
+        xReturn = ERR_OK;
+        SYS_STATS_INC_USED(sem);
+    } else {
+        SYS_STATS_INC(sem.err);
+    }
+
+    return xReturn;
+}
+
+/*---------------------------------------------------------------------------*
+ * Routine:  sys_sem_free
+ *---------------------------------------------------------------------------*
+ * Description:
+ *      Deallocates a semaphore
+ * Inputs:
+ *      sys_sem_t sem           -- Semaphore to free
+ *---------------------------------------------------------------------------*/
+void sys_sem_free(sys_sem_t *pxSemaphore)
+{
+    SYS_STATS_DEC(sem.used);
+    vQueueDelete(*pxSemaphore);
+}
+
+/*---------------------------------------------------------------------------*
+ * Routine:  sys_sem_signal
+ *---------------------------------------------------------------------------*
+ * Description:
+ *      Signals (releases) a semaphore
+ * Inputs:
+ *      sys_sem_t sem           -- Semaphore to signal
+ *---------------------------------------------------------------------------*/
+void sys_sem_signal(sys_sem_t *pxSemaphore)
+{
+    xSemaphoreGive(*pxSemaphore);
+}
+
+/*---------------------------------------------------------------------------*
+ * Routine:  sys_arch_sem_wait
+ *---------------------------------------------------------------------------*
+ * Description:
+
+ *      Blocks the thread while waiting for the semaphore to be signaled. If the
+ *      "timeout" argument is non-zero, the thread should only be blocked for
+ *      the specified time (measured in milliseconds). If the "timeout" argument
+ *      is zero, the thread should be blocked until the semaphore is signalled.
+ *
+ *      The return value is SYS_ARCH_TIMEOUT if the semaphore wasn't signaled
+ *      within the specified time or any other value if it was signaled (with or
+ *      without waiting).
+ *
+ *      Notice that lwIP implements a function with a similar name,
+ *      sys_sem_wait(), that uses the sys_arch_sem_wait() function.
+ * Inputs:
+ *      sys_sem_t sem           -- Semaphore to wait on
+ *      u32_t timeout           -- Number of milliseconds until timeout
+ * 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 ulReturn;
+
+    if (ulTimeout != 0UL) {
+        if (xSemaphoreTake(*pxSemaphore, ulTimeout / portTICK_PERIOD_MS) == pdTRUE) {
+            ulReturn = 0;
+        } else {
+            ulReturn = SYS_ARCH_TIMEOUT;
+        }
+    } else {
+        while (xSemaphoreTake(*pxSemaphore, portMAX_DELAY) != pdTRUE);
+        ulReturn = 0;
+    }
+
+    return ulReturn;
+}
+
+/** Create a new mutex
+ * @param mutex pointer to the mutex to create
+ * @return a new mutex */
+err_t sys_mutex_new(sys_mutex_t *pxMutex)
+{
+    err_t xReturn;
+
+    *pxMutex = xSemaphoreCreateMutex();
+
+    if (*pxMutex != NULL) {
+        xReturn = ERR_OK;
+        SYS_STATS_INC_USED(mutex);
+    } else {
+        xReturn = ERR_MEM;
+        SYS_STATS_INC(mutex.err);
+    }
+
+    return xReturn;
+}
+
+/** Lock a mutex
+ * @param mutex the mutex to lock */
+void sys_mutex_lock(sys_mutex_t *pxMutex)
+{
+    while (xSemaphoreTake(*pxMutex, portMAX_DELAY) != pdPASS);
+}
+
+/** Unlock a mutex
+ * @param mutex the mutex to unlock */
+void sys_mutex_unlock(sys_mutex_t *pxMutex)
+{
+    xSemaphoreGive(*pxMutex);
+}
+
+
+/** Delete a semaphore
+ * @param mutex the mutex to delete */
+void sys_mutex_free(sys_mutex_t *pxMutex)
+{
+    SYS_STATS_DEC(mutex.used);
+    vQueueDelete(*pxMutex);
 }
 
 /*---------------------------------------------------------------------------*
@@ -72,16 +202,15 @@ static inline bool is_inside_isr()
  * 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 *pxMailBox, int iSize)
 {
     err_t xReturn = ERR_MEM;
 
-    *pxMailBox = xQueueCreate( iSize, sizeof( void * ) );
+    *pxMailBox = xQueueCreate(iSize, sizeof(void *));
 
-    if( *pxMailBox != NULL )
-    {
+    if (*pxMailBox != NULL) {
         xReturn = ERR_OK;
-        SYS_STATS_INC_USED( mbox );
+        SYS_STATS_INC_USED(mbox);
     }
 
     return xReturn;
@@ -100,25 +229,24 @@ 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 *pxMailBox)
 {
-unsigned long ulMessagesWaiting;
+    unsigned long ulMessagesWaiting;
 
-    ulMessagesWaiting = uxQueueMessagesWaiting( *pxMailBox );
-    configASSERT( ( ulMessagesWaiting == 0 ) );
+    ulMessagesWaiting = uxQueueMessagesWaiting(*pxMailBox);
+    configASSERT(ulMessagesWaiting == 0);
 
     #if SYS_STATS
     {
-        if( ulMessagesWaiting != 0UL )
-        {
-            SYS_STATS_INC( mbox.err );
+        if (ulMessagesWaiting != 0UL) {
+            SYS_STATS_INC(mbox.err);
         }
 
-        SYS_STATS_DEC( mbox.used );
+        SYS_STATS_DEC(mbox.used);
     }
     #endif /* SYS_STATS */
 
-    vQueueDelete( *pxMailBox );
+    vQueueDelete(*pxMailBox);
 }
 
 /*---------------------------------------------------------------------------*
@@ -130,9 +258,9 @@ unsigned long ulMessagesWaiting;
  *      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 *pxMailBox, void *pxMessageToPost)
 {
-    while( xQueueSendToBack( *pxMailBox, &pxMessageToPost, portMAX_DELAY ) != pdTRUE );
+    while (xQueueSendToBack(*pxMailBox, &pxMessageToPost, portMAX_DELAY) != pdTRUE);
 }
 
 /*---------------------------------------------------------------------------*
@@ -148,26 +276,13 @@ 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 *pxMailBox, void *pxMessageToPost)
 {
-err_t xReturn;
-portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
+    err_t xReturn;
 
-    if( is_inside_isr() != pdFALSE )
-    {
-        xReturn = xQueueSendFromISR( *pxMailBox, &pxMessageToPost, &xHigherPriorityTaskWoken );
-    }
-    else
-    {
-        xReturn = xQueueSend( *pxMailBox, &pxMessageToPost, ( TickType_t ) 0 );
-    }
-
-    if( xReturn == pdPASS )
-    {
+    if (xQueueSend(*pxMailBox, &pxMessageToPost, 0)) {
         xReturn = ERR_OK;
-    }
-    else
-    {
+    } else {
         /* The queue was already full. */
         xReturn = ERR_MEM;
         SYS_STATS_INC( mbox.err );
@@ -188,8 +303,8 @@ portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
  *      should be dropped.
  *
  *      The return values are the same as for the sys_arch_sem_wait() function:
- *      Number of milliseconds spent waiting or SYS_ARCH_TIMEOUT if there was a
- *      timeout.
+ *      SYS_ARCH_TIMEOUT if there was a timeout, any other value if a messages
+ *      is received.
  *
  *      Note that a function with a similar name, sys_mbox_fetch(), is
  *      implemented by lwIP.
@@ -198,52 +313,28 @@ portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
  *      void **msg              -- Pointer to pointer to msg received
  *      u32_t timeout           -- Number of milliseconds until timeout
  * Outputs:
- *      u32_t                   -- SYS_ARCH_TIMEOUT if timeout, else number
- *                                  of milliseconds until received.
+ *      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 *pxMailBox, void **ppvBuffer, u32_t ulTimeOut)
 {
-void *pvDummy;
-TickType_t xStartTime, xEndTime, xElapsed;
-unsigned long ulReturn;
+    void *pvDummy;
+    unsigned long ulReturn;
 
-    xStartTime = xTaskGetTickCount();
-
-    if( NULL == ppvBuffer )
-    {
+    if (ppvBuffer == NULL) {
         ppvBuffer = &pvDummy;
     }
 
-    if( ulTimeOut != 0UL )
-    {
-        configASSERT( is_inside_isr() == ( portBASE_TYPE ) 0 );
-
-        if( pdTRUE == xQueueReceive( *pxMailBox, &( *ppvBuffer ), ulTimeOut/ portTICK_PERIOD_MS ) )
-        {
-            xEndTime = xTaskGetTickCount();
-            xElapsed = ( xEndTime - xStartTime ) * portTICK_PERIOD_MS;
-
-            ulReturn = xElapsed;
-        }
-        else
-        {
+    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( pdTRUE != xQueueReceive( *pxMailBox, &( *ppvBuffer ), portMAX_DELAY ) );
-        xEndTime = xTaskGetTickCount();
-        xElapsed = ( xEndTime - xStartTime ) * portTICK_PERIOD_MS;
-
-        if( xElapsed == 0UL )
-        {
-            xElapsed = 1UL;
-        }
-
-        ulReturn = xElapsed;
+    } else {
+        while (xQueueReceive(*pxMailBox, &(*ppvBuffer), portMAX_DELAY) != pdTRUE);
+        ulReturn = 0;
     }
 
     return ulReturn;
@@ -263,218 +354,24 @@ unsigned long ulReturn;
  *      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 *pxMailBox, void **ppvBuffer)
 {
-void *pvDummy;
-unsigned long ulReturn;
-long lResult;
-portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
+    void *pvDummy;
+    unsigned long ulReturn;
 
-    if( ppvBuffer== NULL )
-    {
+    if (ppvBuffer== NULL) {
         ppvBuffer = &pvDummy;
     }
 
-    if( is_inside_isr() != pdFALSE )
-    {
-        lResult = xQueueReceiveFromISR( *pxMailBox, &( *ppvBuffer ), &xHigherPriorityTaskWoken );
-    }
-    else
-    {
-        lResult = xQueueReceive( *pxMailBox, &( *ppvBuffer ), 0UL );
-    }
-
-    if( lResult == pdPASS )
-    {
+    if (xQueueReceive(*pxMailBox, &(*ppvBuffer), 0UL) == pdPASS) {
         ulReturn = ERR_OK;
-    }
-    else
-    {
+    } else {
         ulReturn = SYS_MBOX_EMPTY;
     }
 
     return ulReturn;
 }
 
-/*---------------------------------------------------------------------------*
- * Routine:  sys_sem_new
- *---------------------------------------------------------------------------*
- * Description:
- *      Creates and returns a new semaphore. The "ucCount" argument specifies
- *      the initial state of the semaphore.
- *      NOTE: Currently this routine only creates counts of 1 or 0
- * Inputs:
- *      sys_mbox_t mbox         -- Handle of mailbox
- *      u8_t ucCount              -- Initial ucCount of semaphore (1 or 0)
- * 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 xReturn = ERR_MEM;
-
-    vSemaphoreCreateBinary( ( *pxSemaphore ) );
-
-    if( *pxSemaphore != NULL )
-    {
-        if( ucCount == 0U )
-        {
-            xSemaphoreTake( *pxSemaphore, 1UL );
-        }
-
-        xReturn = ERR_OK;
-        SYS_STATS_INC_USED( sem );
-    }
-    else
-    {
-        SYS_STATS_INC( sem.err );
-    }
-
-    return xReturn;
-}
-
-/*---------------------------------------------------------------------------*
- * Routine:  sys_arch_sem_wait
- *---------------------------------------------------------------------------*
- * Description:
- *      Blocks the thread while waiting for the semaphore to be
- *      signaled. If the "timeout" argument is non-zero, the thread should
- *      only be blocked for the specified time (measured in
- *      milliseconds).
- *
- *      If the timeout argument is non-zero, the return value is the number of
- *      milliseconds spent waiting for the semaphore to be signaled. If the
- *      semaphore wasn't signaled within the specified time, the return value is
- *      SYS_ARCH_TIMEOUT. If the thread didn't have to wait for the semaphore
- *      (i.e., it was already signaled), the function may return zero.
- *
- *      Notice that lwIP implements a function with a similar name,
- *      sys_sem_wait(), that uses the sys_arch_sem_wait() function.
- * Inputs:
- *      sys_sem_t sem           -- Semaphore to wait on
- *      u32_t timeout           -- Number of milliseconds until timeout
- * Outputs:
- *      u32_t                   -- Time elapsed or SYS_ARCH_TIMEOUT.
- *---------------------------------------------------------------------------*/
-u32_t sys_arch_sem_wait( sys_sem_t *pxSemaphore, u32_t ulTimeout )
-{
-TickType_t xStartTime, xEndTime, xElapsed;
-unsigned long ulReturn;
-
-    xStartTime = xTaskGetTickCount();
-
-    if( ulTimeout != 0UL )
-    {
-        if( xSemaphoreTake( *pxSemaphore, ulTimeout / portTICK_PERIOD_MS ) == pdTRUE )
-        {
-            xEndTime = xTaskGetTickCount();
-            xElapsed = (xEndTime - xStartTime) * portTICK_PERIOD_MS;
-            ulReturn = xElapsed;
-        }
-        else
-        {
-            ulReturn = SYS_ARCH_TIMEOUT;
-        }
-    }
-    else
-    {
-        while( xSemaphoreTake( *pxSemaphore, portMAX_DELAY ) != pdTRUE );
-        xEndTime = xTaskGetTickCount();
-        xElapsed = ( xEndTime - xStartTime ) * portTICK_PERIOD_MS;
-
-        if( xElapsed == 0UL )
-        {
-            xElapsed = 1UL;
-        }
-
-        ulReturn = xElapsed;
-    }
-
-    return ulReturn;
-}
-
-/** Create a new mutex
- * @param mutex pointer to the mutex to create
- * @return a new mutex */
-err_t sys_mutex_new( sys_mutex_t *pxMutex )
-{
-err_t xReturn = ERR_MEM;
-
-    *pxMutex = xSemaphoreCreateMutex();
-
-    if( *pxMutex != NULL )
-    {
-        xReturn = ERR_OK;
-        SYS_STATS_INC_USED( mutex );
-    }
-    else
-    {
-        SYS_STATS_INC( mutex.err );
-    }
-
-    return xReturn;
-}
-
-/** Lock a mutex
- * @param mutex the mutex to lock */
-void sys_mutex_lock( sys_mutex_t *pxMutex )
-{
-    while( xSemaphoreTake( *pxMutex, portMAX_DELAY ) != pdPASS );
-}
-
-/** Unlock a mutex
- * @param mutex the mutex to unlock */
-void sys_mutex_unlock(sys_mutex_t *pxMutex )
-{
-    xSemaphoreGive( *pxMutex );
-}
-
-
-/** Delete a semaphore
- * @param mutex the mutex to delete */
-void sys_mutex_free( sys_mutex_t *pxMutex )
-{
-    SYS_STATS_DEC( mutex.used );
-    vQueueDelete( *pxMutex );
-}
-
-
-/*---------------------------------------------------------------------------*
- * Routine:  sys_sem_signal
- *---------------------------------------------------------------------------*
- * Description:
- *      Signals (releases) a semaphore
- * Inputs:
- *      sys_sem_t sem           -- Semaphore to signal
- *---------------------------------------------------------------------------*/
-void sys_sem_signal( sys_sem_t *pxSemaphore )
-{
-portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
-
-    if( is_inside_isr() != pdFALSE )
-    {
-        xSemaphoreGiveFromISR( *pxSemaphore, &xHigherPriorityTaskWoken );
-    }
-    else
-    {
-        xSemaphoreGive( *pxSemaphore );
-    }
-}
-
-/*---------------------------------------------------------------------------*
- * Routine:  sys_sem_free
- *---------------------------------------------------------------------------*
- * Description:
- *      Deallocates a semaphore
- * Inputs:
- *      sys_sem_t sem           -- Semaphore to free
- *---------------------------------------------------------------------------*/
-void sys_sem_free( sys_sem_t *pxSemaphore )
-{
-    SYS_STATS_DEC(sem.used);
-    vQueueDelete( *pxSemaphore );
-}
-
 /*---------------------------------------------------------------------------*
  * Routine:  sys_init
  *---------------------------------------------------------------------------*
@@ -508,20 +405,17 @@ 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 iStackSize, int iPriority)
 {
-TaskHandle_t xCreatedTask;
-portBASE_TYPE xResult;
-sys_thread_t xReturn;
+    TaskHandle_t xCreatedTask;
+    portBASE_TYPE xResult;
+    sys_thread_t xReturn;
 
-    xResult = xTaskCreate( pxThread, pcName, iStackSize, pvArg, iPriority, &xCreatedTask );
+    xResult = xTaskCreate(pxThread, pcName, iStackSize, pvArg, iPriority, &xCreatedTask);
 
-    if( xResult == pdPASS )
-    {
+    if (xResult == pdPASS) {
         xReturn = xCreatedTask;
-    }
-    else
-    {
+    } else {
         xReturn = NULL;
     }
 
@@ -547,13 +441,14 @@ sys_thread_t xReturn;
  * Outputs:
  *      sys_prot_t              -- Previous protection level (not used here)
  *---------------------------------------------------------------------------*/
-sys_prot_t sys_arch_protect( void )
+static uint32_t my_nesting = 0;
+sys_prot_t sys_arch_protect(void)
 {
-    if( is_inside_isr() == pdFALSE )
-    {
-        taskENTER_CRITICAL();
-    }
-    return ( sys_prot_t ) 1;
+    taskENTER_CRITICAL();
+    uint32_t prev = my_nesting;
+    my_nesting++;
+    return prev;
+    //return (sys_prot_t)1;
 }
 
 /*---------------------------------------------------------------------------*
@@ -567,26 +462,16 @@ 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 xValue)
 {
-    (void) xValue;
-    if( is_inside_isr() == pdFALSE )
-    {
-        taskEXIT_CRITICAL();
+    //(void) xValue;
+    my_nesting--;
+    if (xValue != my_nesting) {
+        printf("lwip nesting %d\n", my_nesting);
     }
+    taskEXIT_CRITICAL();
 }
 
-/*
- * Prints an assertion messages and aborts execution.
- */
-void sys_assert( const char *pcMessage )
-{
-    (void) pcMessage;
-
-    for (;;)
-    {
-    }
-}
 /*-------------------------------------------------------------------------*
  * End of File:  sys_arch.c
  *-------------------------------------------------------------------------*/
diff --git a/open_esplibs/include/esplibs/libmain.h b/open_esplibs/include/esplibs/libmain.h
index 9f4ad1a..593092c 100644
--- a/open_esplibs/include/esplibs/libmain.h
+++ b/open_esplibs/include/esplibs/libmain.h
@@ -56,6 +56,7 @@ uint32_t sdk_system_relative_time(uint32_t reltime);
 uint32_t sdk_system_get_checksum(uint8_t *, uint32_t);
 void sdk_wifi_softap_cacl_mac(uint8_t *, uint8_t *);
 void sdk_wifi_softap_set_default_ssid(void);
+bool sdk_wifi_softap_set_station_info(const uint8_t *hwaddr, ip4_addr_t *);
 
 // xtensa_context.o
 
diff --git a/open_esplibs/include/esplibs/libnet80211.h b/open_esplibs/include/esplibs/libnet80211.h
index b7a2db6..d2c117b 100644
--- a/open_esplibs/include/esplibs/libnet80211.h
+++ b/open_esplibs/include/esplibs/libnet80211.h
@@ -32,7 +32,7 @@ struct esf_buf *sdk_ieee80211_getmgtframe(void **arg0, uint32_t arg1, uint32_t a
 extern uint8_t sdk_TmpSTAAPCloseAP;
 extern uint8_t sdk_PendFreeBcnEb;
 void sdk_ieee80211_hostap_attach(struct sdk_g_ic_st *);
-void sdk_hostap_handle_timer(struct sdk_netif_conninfo *cnx_node);
+void sdk_hostap_handle_timer(struct sdk_cnx_node *cnx_node);
 bool sdk_wifi_softap_start();
 bool sdk_wifi_softap_stop();
 
@@ -85,10 +85,10 @@ int sdk_chm_check_same_channel();
 extern ETSTimer sdk_sta_con_timer;
 extern void *sdk_g_cnx_probe_rc_list_cb;
 void sdk_cnx_sta_leave(struct sdk_g_ic_netif_info *netif_info, void *);
-void *sdk_cnx_node_search(uint8_t mac[6]);
-void sdk_cnx_node_leave(struct sdk_g_ic_netif_info *netif, struct sdk_netif_conninfo *conn);
+struct sdk_cnx_node *sdk_cnx_node_search(uint8_t mac[6]);
+void sdk_cnx_node_leave(struct sdk_g_ic_netif_info *netif, struct sdk_cnx_node *conn);
 void sdk_cnx_rc_update_state_metric(void *, int, int);
-void sdk_cnx_remove_rc(void *);
+void sdk_cnx_node_remove(struct sdk_cnx_node *cnx_node);
 void sdk_cnx_attach(struct sdk_g_ic_st *);
 
 #endif /* _ESPLIBS_LIBNET80211_H */
diff --git a/open_esplibs/include/esplibs/libpp.h b/open_esplibs/include/esplibs/libpp.h
index c6185b6..836a689 100644
--- a/open_esplibs/include/esplibs/libpp.h
+++ b/open_esplibs/include/esplibs/libpp.h
@@ -23,6 +23,7 @@ extern uint8_t sdk_interface_mask;
 void sdk_ic_set_vif(int, int, uint8_t (*)[6], int, int);
 void sdk_ic_bss_info_update(int, uint8_t (*hwaddr)[], int, int);
 void sdk_ic_set_sta(int, int, void *, int, int, int, int, int);
+void sdk_ic_remove_key(uint32_t);
 
 // lmac.o
 extern uint32_t sdk_lmacConfMib;
diff --git a/open_esplibs/include/esplibs/libwpa.h b/open_esplibs/include/esplibs/libwpa.h
index 36a2fc2..2839e56 100644
--- a/open_esplibs/include/esplibs/libwpa.h
+++ b/open_esplibs/include/esplibs/libwpa.h
@@ -53,6 +53,7 @@ int sdk_os_get_random(uint8_t *dst, uint32_t size);
 
 // wpa_auth.o
 uint32_t *sdk_wpa_init(uint8_t (*hwaddr)[], struct _unknown_wpa1 *, int);
+void sdk_wpa_auth_sta_deinit(void *);
 
 // wpabuf.o
 
diff --git a/open_esplibs/include/open_esplibs.h b/open_esplibs/include/open_esplibs.h
index da59d3c..8db78c5 100644
--- a/open_esplibs/include/open_esplibs.h
+++ b/open_esplibs/include/open_esplibs.h
@@ -6,6 +6,10 @@
 // useful for quickly troubleshooting whether a bug is due to the
 // reimplementation of Espressif libraries, or something else.
 
+// Some source code is mandatory and these are not included as options here.
+// For example code referencing lwip structures and flags etc that has changed
+// since the initial binary code.
+
 #ifndef OPEN_ESPLIBS
 #define OPEN_ESPLIBS 1
 #endif
@@ -32,8 +36,8 @@
 #ifndef OPEN_LIBMAIN_XTENSA_CONTEXT
 #define OPEN_LIBMAIN_XTENSA_CONTEXT (OPEN_LIBMAIN)
 #endif
-#ifndef OPEN_LIBMAIN_USER_INTERFACE
-#define OPEN_LIBMAIN_USER_INTERFACE (OPEN_LIBMAIN)
+#ifndef OPEN_LIBMAIN_ETS_TIMER
+#define OPEN_LIBMAIN_ETS_TIMER (OPEN_LIBMAIN)
 #endif
 
 #ifndef OPEN_LIBNET80211
@@ -42,18 +46,6 @@
 #ifndef OPEN_LIBNET80211_ETS
 #define OPEN_LIBNET80211_ETS (OPEN_LIBNET80211)
 #endif
-#ifndef OPEN_LIBNET80211_HOSTAP
-#define OPEN_LIBNET80211_HOSTAP (OPEN_LIBNET80211)
-#endif
-#ifndef OPEN_LIBNET80211_INPUT
-#define OPEN_LIBNET80211_INPUT (OPEN_LIBNET80211)
-#endif
-#ifndef OPEN_LIBNET80211_STA
-#define OPEN_LIBNET80211_STA (OPEN_LIBNET80211)
-#endif
-#ifndef OPEN_LIBNET80211_WL_CNX
-#define OPEN_LIBNET80211_WL_CNX (OPEN_LIBNET80211)
-#endif
 
 #ifndef OPEN_LIBPHY
 #define OPEN_LIBPHY (OPEN_ESPLIBS)
@@ -115,8 +107,4 @@
 #define OPEN_LIBWPA_OS_XTENSA (OPEN_LIBWPA)
 #endif
 
-#ifndef OPEN_LIBWPA_WPA_MAIN
-#define OPEN_LIBWPA_WPA_MAIN (OPEN_LIBWPA)
-#endif
-
 #endif /* _OPEN_ESPLIBS_H */
diff --git a/open_esplibs/libmain/user_interface.c b/open_esplibs/libmain/user_interface.c
index eed34a1..a21530d 100644
--- a/open_esplibs/libmain/user_interface.c
+++ b/open_esplibs/libmain/user_interface.c
@@ -3,9 +3,6 @@
    Copyright (C) 2015 Espressif Systems. Derived from MIT Licensed SDK libraries.
    BSD Licensed as described in the file LICENSE
 */
-#include "open_esplibs.h"
-#if OPEN_LIBMAIN_USER_INTERFACE
-// The contents of this file are only built if OPEN_LIBMAIN_USER_INTERFACE is set to true
 
 #include "FreeRTOS.h"
 #include "task.h"
@@ -147,8 +144,8 @@ bool IRAM sdk_system_rtc_mem_read(uint32_t src_addr, void *des_addr, uint16_t sa
     return true;
 }
 
-void sdk_system_pp_recycle_rx_pkt(void *eb) {
-        sdk_ppRecycleRxPkt(eb);
+void sdk_system_pp_recycle_rx_pkt(struct esf_buf *esf_buf) {
+        sdk_ppRecycleRxPkt(esf_buf);
 }
 
 uint16_t sdk_system_adc_read(void) {
@@ -471,8 +468,7 @@ uint32_t sdk_system_relative_time(uint32_t reltime) {
     return WDEV.SYS_TIME - reltime;
 }
 
-// Change arg types to ip4_addr for lwip v2.
-void sdk_system_station_got_ip_set(struct ip_addr *ip, struct ip_addr *mask, struct ip_addr *gw) {
+void sdk_system_station_got_ip_set(struct ip4_addr *ip, struct ip4_addr *mask, struct ip4_addr *gw) {
     uint8_t *ip_bytes = (uint8_t *)&ip->addr;
     uint8_t *mask_bytes = (uint8_t *)&mask->addr;
     uint8_t *gw_bytes = (uint8_t *)&gw->addr;
@@ -588,9 +584,9 @@ bool sdk_wifi_get_ip_info(uint8_t if_index, struct ip_info *info) {
     if (!info) return false;
     struct netif *netif = _get_netif(if_index);
     if (netif) {
-        info->ip = netif->ip_addr;
-        info->netmask = netif->netmask;
-        info->gw = netif->gw;
+        ip4_addr_set(&info->ip, ip_2_ip4(&netif->ip_addr));
+        ip4_addr_set(&info->netmask, ip_2_ip4(&netif->netmask));
+        ip4_addr_set(&info->gw, ip_2_ip4(&netif->gw));
         return true;
     }
 
@@ -719,5 +715,3 @@ bool sdk_wifi_set_sleep_type(enum sdk_sleep_type type)
     sdk_pm_set_sleep_type_from_upper(type);
     return true;
 }
-
-#endif /* OPEN_LIBMAIN_USER_INTERFACE */
diff --git a/open_esplibs/libnet80211/ieee80211_hostap.c b/open_esplibs/libnet80211/ieee80211_hostap.c
index fbe7f49..74f9675 100644
--- a/open_esplibs/libnet80211/ieee80211_hostap.c
+++ b/open_esplibs/libnet80211/ieee80211_hostap.c
@@ -3,8 +3,306 @@
    Copyright (C) 2015 Espressif Systems. Derived from MIT Licensed SDK libraries.
    BSD Licensed as described in the file LICENSE
 */
-#include "open_esplibs.h"
-#if OPEN_LIBNET80211_HOSTAP
-// The contents of this file are only built if OPEN_LIBNET80211_HOSTAP is set to true
 
-#endif /* OPEN_LIBNET80211_HOSTAP */
+#include <string.h>
+#include "tcpip.h"
+#include "espressif/esp_wifi.h"
+#include "espressif/esp_misc.h"
+#include "etstimer.h"
+#include "esplibs/libmain.h"
+#include "esplibs/libnet80211.h"
+#include "esplibs/libpp.h"
+#include "esplibs/libwpa.h"
+
+static uint8_t hostap_flags = 0;
+static ETSTimer hostap_timer;
+static struct esf_buf *hostap_timer_parg = NULL;
+
+void IRAM *zalloc(size_t nbytes);
+
+static void IRAM hostap_timer_func(struct esf_buf *esf_buf) {
+    struct sdk_cnx_node *cnx_node = sdk_g_ic.v.softap_netif_info->cnx_nodes[0];
+    int32_t mode = sdk_wifi_get_phy_mode();
+    uint8_t *frame = esf_buf->frame;
+    *(uint16_t *)(frame + 22) = (cnx_node->_unknown9c - 1) << 4;
+
+    cnx_node->_unknown9c += 1;
+
+    if (sdk_g_ic.s.wifi_led_enable) {
+        uint32_t gpio = sdk_g_ic.s.wifi_led_gpio;
+        uint32_t state = sdk_g_ic.s.wifi_led_state;
+        sdk_gpio_output_set(state << gpio, (((state & 1) == 0) ? 1 : 0) << gpio,
+                            1 << gpio, 0);
+        sdk_g_ic.s.wifi_led_state = (state & 1) ? 0 : 1;
+    }
+
+    uint8_t *frame2 = frame + sdk_g_ic.s._unknown288 + (mode == 1 ? 23 : 27) ;
+    memcpy(frame2 + 29, &sdk_g_ic.v._unknown1d2, 1);
+
+    uint32_t v1 = frame2[26];
+    if (v1 == 0) {
+        v1 = frame2[27];
+    }
+    frame2[26] = v1 - 1;
+
+    uint32_t v2 = sdk_ieee80211_chan2ieee(sdk_g_ic.v._unknown14c);
+    frame2[23] = v2;
+    sdk_g_ic.s._unknown30d = v2;
+
+    int32_t v3 = *((volatile int32_t *)0x3ff20c00); // mactime
+    *(uint32_t *)(esf_buf->extra + 16) = v3;
+    *(uint32_t *)(frame + 24) = v3;
+    *(uint32_t *)(frame + 28) = 0;
+
+    if (sdk_chm_check_same_channel()) {
+        hostap_flags |= 1;
+        sdk_ppTxPkt(esf_buf);
+        return;
+    }
+    sdk_ets_timer_disarm(&hostap_timer);
+    sdk_ets_timer_arm(&hostap_timer, sdk_wDev_Get_Next_TBTT(), 0);
+}
+
+static void IRAM hostap_tx_callback() {
+    uint32_t flags = hostap_flags & 0xfe;
+
+    if (sdk_TmpSTAAPCloseAP == 0) {
+        hostap_flags = flags;
+        sdk_ets_timer_disarm(&hostap_timer);
+        sdk_ets_timer_arm(&hostap_timer, sdk_wDev_Get_Next_TBTT(), 0);
+        return;
+    }
+
+    sdk_PendFreeBcnEb = 1;
+
+    if (flags & 2) {
+        hostap_flags = flags & 0xfd;
+        sdk_wifi_softap_start();
+        return;
+    }
+
+    hostap_flags = flags;
+}
+
+static void hostap_attach_misc() {
+    struct sdk_g_ic_netif_info *netif_info = sdk_g_ic.v.softap_netif_info;
+
+    struct _unknown_softap1 *ptr1 = zalloc(28); // 0x1c
+    netif_info->_unknownb4 = ptr1;
+
+    struct _unknown_softap2 *ptr2 = zalloc(204); // 0xcc
+    ptr1->_unknown04 = ptr2;
+
+    struct _unknown_wpa1 *ptr3 = zalloc(76); // 0x4c
+
+    uint32_t v1 = sdk_g_ic.s._unknown30e;
+    if (v1 == 2) {
+        ptr3->_unknown00 = 1;
+    } else if (v1 == 3) {
+        ptr3->_unknown00 = 2;
+    } else if (v1 == 4) {
+        ptr3->_unknown00 = 3;
+    }
+
+    ptr3->_unknown28 = 2;
+    ptr3->_unknown04 = 2;
+    ptr3->_unknown0c = 2;
+    ptr3->_unknown08 = 10;
+    ptr3->_unknown20 = 10;
+
+    int s1 = (sdk_g_ic.s._unknown28a << 16) | sdk_g_ic.s._unknown288;
+    memcpy(&ptr2->_unknown10, &sdk_g_ic.s._unknown28c, s1);
+    ptr2->_unknown30 = s1;
+
+    uint8_t *ptr4 = zalloc(64); // 0x40
+    ptr2->_unknown3c = ptr4;
+
+    char *str1 = sdk_g_ic.s._unknown2ac;
+    memcpy(ptr4, str1, strlen(str1));
+
+    struct sdk_cnx_node *cnx_node = netif_info->cnx_nodes[0];
+
+    ptr2->_unknownb4 = 300;
+
+    netif_info->_unknown4c = 3;
+    netif_info->_unknown48 |= 16;
+
+    cnx_node->_unknown08 |= 1;
+
+    sdk_hostapd_setup_wpa_psk(ptr2);
+
+    struct netif *netif = netif_info->netif;
+
+    ptr1->_unknown18 = sdk_wpa_init(&netif->hwaddr, ptr3, 0);
+
+    free(ptr3);
+}
+
+static void softap_stop_free() {
+    struct sdk_g_ic_netif_info *netif_info = sdk_g_ic.v.softap_netif_info;
+    netif_info->_unknown4c = 0;
+    netif_info->_unknown48 &= 0xffffffef;
+    netif_info->cnx_nodes[0]->_unknown08 = 0;
+
+    struct _unknown_softap1 *unkb4 = netif_info->_unknownb4;
+    if (!unkb4) return;
+
+    uint32_t *unk18 = unkb4->_unknown18;
+    if (unk18) {
+        uint32_t *ptr1 = ((uint32_t **)unk18)[20];
+        if (ptr1)
+            free(ptr1);
+
+        uint32_t *ptr2 = *(uint32_t **)unk18;
+        if (ptr2)
+            free(ptr2);
+
+        free(unk18);
+    }
+
+    struct _unknown_softap2 *unk04 = unkb4->_unknown04;
+    if (unk04) {
+        uint32_t *unk38 = unk04->_unknown38;
+        if (unk38)
+            free(unk38);
+
+        uint8_t *unk3c = unk04->_unknown3c;
+        if (unk3c)
+            free(unk3c);
+
+        free(unk04);
+    }
+
+    free(unkb4);
+    netif_info->_unknownb4 = NULL;
+}
+
+void sdk_ieee80211_hostap_attach(struct sdk_g_ic_st *ic) {
+    uint32_t scratch[12]; // ??
+    struct sdk_g_ic_netif_info *netif_info = ic->v.softap_netif_info;
+
+    uint32_t v1 = ic->s._unknown30e;
+    if (v1 >= 2 && v1 < 5)
+        hostap_attach_misc();
+
+    struct netif *netif = netif_info->netif;
+    sdk_ic_bss_info_update(1, &netif->hwaddr, 2, 100);
+
+    ic->v._unknown1d0 = 0;
+    netif_info->_unknown3c = 5;
+
+    sdk_ppRegisterTxCallback(hostap_tx_callback, 4);
+
+    hostap_timer_parg = sdk_ieee80211_beacon_alloc(netif_info, scratch);
+    sdk_ets_timer_disarm(&hostap_timer);
+    sdk_ets_timer_setfn(&hostap_timer, (ETSTimerFunc *)hostap_timer_func, hostap_timer_parg);
+    sdk_wDev_Reset_TBTT();
+    sdk_ets_timer_arm(&hostap_timer, sdk_wDev_Get_Next_TBTT(), 0);
+}
+
+bool sdk_wifi_softap_start() {
+    struct sdk_g_ic_netif_info *netif_info = sdk_g_ic.v.softap_netif_info;
+    if (!netif_info) return 0;
+    if (netif_info->started) return 1;
+
+    uint8_t flags = hostap_flags;
+    if (flags & 1) {
+        hostap_flags = flags | 2;
+        return 1;
+    }
+
+    uint8_t (*mac_addr)[6]  = &sdk_info.softap_mac_addr;
+    if (!netif_info->netif) {
+         struct netif *netif = (struct netif *)malloc(sizeof(struct netif));
+         netif_info->netif = netif;
+         memcpy(&netif->hwaddr, mac_addr, 6);
+         netif_add(netif, &sdk_info.softap_ipaddr, &sdk_info.softap_netmask,
+                   &sdk_info.softap_gw, netif_info, ethernetif_init, tcpip_input);
+     }
+
+    sdk_ic_set_vif(1, 1, mac_addr, 1, 0);
+
+    netif_set_up(netif_info->netif);
+
+    if (sdk_wifi_get_opmode() != 3 ||
+        !sdk_g_ic.v.station_netif_info ||
+        sdk_g_ic.v.station_netif_info->_unknown3c < 2) {
+
+        uint32_t i1 = (sdk_g_ic.s._unknown30d - 1) & 0xff;
+
+        int nmi_on = sdk_NMIIrqIsOn;
+        if (!nmi_on) {
+            vPortEnterCritical();
+
+            do {
+                DPORT.DPORT0 = DPORT.DPORT0 & 0xffffffe0;
+            }  while (DPORT.DPORT0 & 1);
+        }
+
+        // current channel?
+        uint32_t *chan = &sdk_g_ic.v._unknown84[i1 * 3];
+        sdk_g_ic.v._unknown14c = chan;
+
+        if (!nmi_on) {
+            DPORT.DPORT0 = (DPORT.DPORT0 & 0xffffffe0) | 1;
+            vPortExitCritical();
+        }
+
+        sdk_chm_set_current_channel(chan);
+    }
+
+    sdk_ieee80211_hostap_attach(&sdk_g_ic);
+    sdk_TmpSTAAPCloseAP = 0;
+    netif_info->started = 1;
+    return 1;
+}
+
+bool sdk_wifi_softap_stop() {
+    struct sdk_g_ic_netif_info *netif_info = sdk_g_ic.v.softap_netif_info;
+
+    if (!netif_info)
+        return 0;
+
+    if (!netif_info->started)
+        return 1;
+
+    uint32_t end = sdk_g_ic.s._unknown310 + 2;
+    uint32_t count = 1;
+
+    // Note this defensive test seems dead code, the value is loaded
+    // as a uint8_t value so adding 2 ensures this test always passes.
+    if (end >= 2) {
+        do {
+            struct sdk_cnx_node *cnx_node = netif_info->cnx_nodes[count];
+            if (cnx_node) {
+                struct sdk_cnx_node *cnx_node2 = netif_info->_unknown88;
+                netif_info->_unknown88 = cnx_node;
+
+                sdk_ieee80211_send_mgmt(netif_info, 160, 4);
+                sdk_ieee80211_send_mgmt(netif_info, 192, 2);
+
+                netif_info->_unknown88 = cnx_node2;
+
+                sdk_cnx_node_leave(netif_info, netif_info->cnx_nodes[count]);
+
+                // Number of entries might have changed, perhaps
+                // should have if one was removed above?
+                end = sdk_g_ic.s._unknown310 + 2;
+            }
+            count++;
+        } while (count < end);
+    }
+
+    netif_set_down(netif_info->netif);
+    sdk_TmpSTAAPCloseAP = 1;
+    sdk_ets_timer_disarm(&hostap_timer);
+    sdk_ic_bss_info_update(1, &sdk_info.softap_mac_addr, 2, 0);
+    sdk_ic_set_vif(1, 0, NULL, 1, 0);
+    softap_stop_free();
+
+    if ((hostap_flags & 1) == 0)
+        sdk_esf_buf_recycle(hostap_timer_parg, 4);
+
+    netif_info->started = 0;
+    return 1;
+}
diff --git a/open_esplibs/libnet80211/ieee80211_input.c b/open_esplibs/libnet80211/ieee80211_input.c
index be07a51..583dba9 100644
--- a/open_esplibs/libnet80211/ieee80211_input.c
+++ b/open_esplibs/libnet80211/ieee80211_input.c
@@ -3,9 +3,6 @@
    Copyright (C) 2015 Espressif Systems. Derived from MIT Licensed SDK libraries.
    BSD Licensed as described in the file LICENSE
 */
-#include "open_esplibs.h"
-#if OPEN_LIBNET80211_INPUT
-// The contents of this file are only built if OPEN_LIBNET80211_INPUT is set to true
 
 #include "esplibs/libpp.h"
 
@@ -14,10 +11,9 @@ void IRAM sdk_ieee80211_deliver_data(struct sdk_g_ic_netif_info *netif_info, str
 
     if (netif->flags & NETIF_FLAG_LINK_UP) {
         uint16_t length = esf_buf->length;
-        struct pbuf *pbuf = pbuf_alloc(PBUF_RAW, length, PBUF_REF);
-        pbuf->payload = esf_buf->pbuf2->payload;
+        struct pbuf *pbuf = pbuf_alloc_reference(esf_buf->pbuf2->payload, length, PBUF_ALLOC_FLAG_RX | PBUF_TYPE_ALLOC_SRC_MASK_ESP_RX);
         esf_buf->pbuf1 = pbuf;
-        pbuf->eb = (void *)esf_buf;
+        pbuf->esf_buf = (void *)esf_buf;
         ethernetif_input(netif, pbuf);
         return;
     }
@@ -27,5 +23,3 @@ void IRAM sdk_ieee80211_deliver_data(struct sdk_g_ic_netif_info *netif_info, str
 
     return;
 }
-
-#endif /* OPEN_LIBNET80211_INPUT */
diff --git a/open_esplibs/libnet80211/ieee80211_sta.c b/open_esplibs/libnet80211/ieee80211_sta.c
index 5362dc3..3e7f695 100644
--- a/open_esplibs/libnet80211/ieee80211_sta.c
+++ b/open_esplibs/libnet80211/ieee80211_sta.c
@@ -3,9 +3,6 @@
    Copyright (C) 2015 Espressif Systems. Derived from MIT Licensed SDK libraries.
    BSD Licensed as described in the file LICENSE
 */
-#include "open_esplibs.h"
-#if OPEN_LIBNET80211_STA
-// The contents of this file are only built if OPEN_LIBNET80211_STA is set to true
 
 #include <string.h>
 #include "esplibs/libmain.h"
@@ -72,5 +69,3 @@ bool sdk_wifi_station_stop() {
 
     return 1;
 }
-
-#endif /* OPEN_LIBNET80211_STA */
diff --git a/open_esplibs/libnet80211/wl_cnx.c b/open_esplibs/libnet80211/wl_cnx.c
index 3b7d709..0d2459e 100644
--- a/open_esplibs/libnet80211/wl_cnx.c
+++ b/open_esplibs/libnet80211/wl_cnx.c
@@ -3,18 +3,29 @@
    Copyright (C) 2015 Espressif Systems. Derived from MIT Licensed SDK libraries.
    BSD Licensed as described in the file LICENSE
 */
-#include "open_esplibs.h"
-#if OPEN_LIBNET80211_WL_CNX
-// The contents of this file are only built if OPEN_LIBNET80211_WL_CNX is set to true
 
 #include "espressif/esp_misc.h"
 #include "esplibs/libnet80211.h"
+#include "esplibs/libpp.h"
+#include "esplibs/libwpa.h"
 #include <string.h>
 #include "lwip/dhcp.h"
 
 ETSTimer sdk_sta_con_timer;
 void *sdk_g_cnx_probe_rc_list_cb;
 
+/*
+ * Called from the ESP sdk_cnx_sta_leave function. Split out via a hack to the
+ * binary library to allow modification to track changes to lwip, for example
+ * changes to the offset of the netif->flags removal of the NETIF_FLAG_DHCP flag
+ * lwip v2 etc.
+ */
+void dhcp_if_down(struct netif *netif)
+{
+    dhcp_release_and_stop(netif);
+    netif_set_down(netif);
+}
+
 #if 0
 
 // Most of the code in this file assesses static data so it will be all or none.
@@ -24,7 +35,6 @@ static uint8_t Ldata004;
 static uint32_t Ldata006;
 static void *Ldate007;
 
-// Use of the netif->flags and the NETIF_FLAG_DHCP flag removed in lwip v2.
 void sdk_cnx_sta_leave(struct sdk_g_ic_netif_info *netif_info, void *arg1) {
     struct netif *netif = netif_info->netif;
 
@@ -32,13 +42,9 @@ void sdk_cnx_sta_leave(struct sdk_g_ic_netif_info *netif_info, void *arg1) {
     uint16_t v1 = *(uint16_t *)(arg1 + 0x1a) & 0xfff;
     sdk_ic_set_sta(0, 0, arg1, 0, v1, phy_type, 0, 0);
 
-    netif_set_down(netif);
-
-    // The NETIF_FLAG_DHCP flags is removed in lwip v2?
-    if (netif->flags & 0x8) {
-        dhcp_release(netif);
-        dhcp_stop(netif);
-    }
+    // Note the SDK binary was modified here as it made use of the
+    // netif flags which changed in lwip v2.
+    dhcp_if_down(netif);
 
     uint32_t v2 = *(uint8_t *)(arg1 + 0xe8);
     free(sdk_g_ic.v._unknown190[v2]);
@@ -77,7 +83,30 @@ void sdk_cnx_sta_leave(struct sdk_g_ic_netif_info *netif_info, void *arg1) {
 }
 #endif
 
-void IRAM *sdk_cnx_node_search(uint8_t mac[6])
+void sdk_cnx_node_remove(struct sdk_cnx_node *cnx_node) {
+    const uint32_t num = sdk_g_ic.s._unknown310 + 2;
+    if ((int32_t)num < (int32_t)2) {
+        return;
+    }
+
+    struct sdk_g_ic_netif_info *netif_info = sdk_g_ic.v.softap_netif_info;
+    uint32_t i = 1;
+    do {
+        if (netif_info->cnx_nodes[i] == cnx_node) {
+            uint32_t v2 = cnx_node->_unknowne8;
+            sdk_ic_remove_key(v2 + 2);
+            sdk_wpa_auth_sta_deinit(cnx_node->_unknowne4);
+            free(sdk_g_ic.v._unknown190[v2]);
+            sdk_g_ic.v._unknown190[v2] = NULL;
+            free(cnx_node);
+            netif_info->cnx_nodes[i] = NULL;
+            return;
+        }
+        i += 1;
+    } while (i < num);
+}
+
+struct sdk_cnx_node *sdk_cnx_node_search(uint8_t mac[6])
 {
     int end = sdk_g_ic.s._unknown310 + 2;
 
@@ -86,15 +115,15 @@ void IRAM *sdk_cnx_node_search(uint8_t mac[6])
     if (end < 1)
         return NULL;
 
-    struct sdk_netif_conninfo **conninfo = sdk_g_ic.v.softap_netif_info->conninfo;
+    struct sdk_cnx_node **cnx_nodes = sdk_g_ic.v.softap_netif_info->cnx_nodes;
 
     int i = 0;
     do {
-        struct sdk_netif_conninfo *info = conninfo[i];
+        struct sdk_cnx_node *cnx_node = cnx_nodes[i];
 
-        if (info) {
-            if (memcmp(mac, info->mac_addr, 6) == 0) {
-                return info;
+        if (cnx_node) {
+            if (memcmp(mac, cnx_node->mac_addr, 6) == 0) {
+                return cnx_node;
             }
         }
         i++;
@@ -102,5 +131,3 @@ void IRAM *sdk_cnx_node_search(uint8_t mac[6])
 
     return NULL;
 }
-
-#endif /* OPEN_LIBNET80211_WL_CNX */
diff --git a/open_esplibs/libpp/wdev.c b/open_esplibs/libpp/wdev.c
index 1564a59..902b3dc 100644
--- a/open_esplibs/libpp/wdev.c
+++ b/open_esplibs/libpp/wdev.c
@@ -5,7 +5,13 @@
 */
 #include "open_esplibs.h"
 #if OPEN_LIBPP_WDEV
-// The contents of this file are only built if OPEN_LIBPHY_PHY_CHIP_SLEEP is set to true
+// The contents of this file are only built if OPEN_LIBPP_WDEV is set to true
 
+// Note the SDK allocated 8000 bytes for TX buffers that appears to be
+// unused. Rather TX buffers appear to be allocated by upper layers. The
+// location of this areas is at wDevCtrl + 0x2190. The SDK has been modified
+// to allocate only one word (4 bytes) per buffer on initialization and even
+// these seem unused but to be sure avoid the first 20 bytes. So there are
+// 7980 bytes free starting at wDevCtrl + 0x21a4.
 
 #endif /* OPEN_LIBPP_WDEV */
diff --git a/open_esplibs/libwpa/wpa_main.c b/open_esplibs/libwpa/wpa_main.c
index 1c70516..43f332c 100644
--- a/open_esplibs/libwpa/wpa_main.c
+++ b/open_esplibs/libwpa/wpa_main.c
@@ -3,13 +3,13 @@
    Copyright (C) 2015 Espressif Systems. Derived from MIT Licensed SDK libraries.
    BSD Licensed as described in the file LICENSE
 */
-#include "open_esplibs.h"
-#if OPEN_LIBWPA_WPA_MAIN
-// The contents of this file are only built if OPEN_LIBWPA_WPA_MAIN is set to true
 
+#include <strings.h>
+#include <string.h>
 #include "espressif/user_interface.h"
 #include "etstimer.h"
 #include "espressif/osapi.h"
+#include "espressif/esp_sta.h"
 #include "esplibs/libnet80211.h"
 #include "esplibs/libmain.h"
 #include "esplibs/libwpa.h"
@@ -44,8 +44,8 @@ void sdk_wpa_config_bss(struct sdk_g_ic_st *g_ic, uint8_t (* hwaddr2)[6]) {
     struct sdk_g_ic_netif_info *netif_info = g_ic->v.station_netif_info;
     struct netif *netif = netif_info->netif;
     sdk_wpa_set_bss(netif->hwaddr, hwaddr2, g_ic->s._unknown20a, g_ic->s._unknown20c,
-                    g_ic->s.sta_password, g_ic->s._unknown1e4.sta_ssid,
-                    (g_ic->s._unknown1e4._unknown1e6 << 16) | g_ic->s._unknown1e4._unknown1e4);
+                    g_ic->s.sta_password, g_ic->s.sta_ssid.ssid,
+                    g_ic->s.sta_ssid.ssid_length);
 }
 
 void sdk_wpa_config_assoc_ie(int arg0, int16_t *arg1, int32_t arg2) {
@@ -57,14 +57,14 @@ void sdk_wpa_config_assoc_ie(int arg0, int16_t *arg1, int32_t arg2) {
     *arg1 = arg2;
 }
 
-void sdk_dhcp_bind_check() {
+void sdk_dhcp_bind_check(void *parg) {
     struct sdk_g_ic_netif_info *netif_info = sdk_g_ic.v.station_netif_info;
     uint8_t connect_status = netif_info->connect_status;
     uint8_t unknown20a = sdk_g_ic.s._unknown20a;
 
-    if (connect_status != 5) {
+    if (connect_status != STATION_GOT_IP) {
         if (unknown20a == 7 || unknown20a == 8) {
-            netif_info->connect_status = 2;
+            netif_info->connect_status = STATION_CONNECTING;
         }
     }
 }
@@ -72,13 +72,13 @@ void sdk_dhcp_bind_check() {
 void sdk_eagle_auth_done() {
     struct sdk_g_ic_netif_info *netif_info = sdk_g_ic.v.station_netif_info;
     struct netif *netif = netif_info->netif;
-    struct sdk_netif_conninfo *conninfo = netif_info->_unknown88;
+    struct sdk_cnx_node *cnx_node = netif_info->_unknown88;
 
-    if (conninfo->_unknown08 & 1)
+    if (cnx_node->_unknown08 & 1)
         return;
 
-    uint32_t channel = conninfo->_unknown78->channel;
-    char *ssid = (char *)sdk_g_ic.s._unknown1e4.sta_ssid;
+    uint32_t channel = cnx_node->_unknown78->channel;
+    char *ssid = (char *)sdk_g_ic.s.sta_ssid.ssid;
     printf("\nconnected with %s, channel %d\n", ssid, channel);
 
     RTCMEM_SYSTEM[61] = 0x00010000 | channel;
@@ -89,24 +89,29 @@ void sdk_eagle_auth_done() {
     sdk_os_timer_arm(timer, 15000, 0);
 
     netif_info->statusb9 = 0;
-    conninfo->_unknown18 = 0;
-    conninfo->_unknown08 |= 1;
+    cnx_node->_unknown18 = 0;
+    cnx_node->_unknown08 |= 1;
 
-    // TODO lwip v2 removed the NETIF_FLAG_DHCP flag.
-    if (netif->flags & 0x08) // NETIF_FLAG_DHCP
+    if (dhcp_supplied_address(netif))
         return;
 
-    // lwip v2: if (ip4_addr_isany_val(netif->ip_addr)) {
-    if (netif->ip_addr.addr == 0) {
-        if (sdk_dhcpc_flag != DHCP_STOPPED) {
-            printf("dhcp client start...\n");
-            dhcp_start(netif);
-        }
+    if (sdk_dhcpc_flag != DHCP_STOPPED) {
+        printf("dhcp client start...\n");
+        netif_set_up(netif);
+        dhcp_start(netif);
         return;
     }
 
-    system_station_got_ip_set(&netif->ip_addr, &netif->netmask, &netif->gw);
+    if (ip4_addr_isany_val(sdk_info.sta_ipaddr)) {
+        printf("expected a static ip address?\n");
+        return;
+    }
+
+    netif_set_addr(netif, &sdk_info.sta_ipaddr, &sdk_info.sta_netmask, &sdk_info.sta_gw);
     netif_set_up(netif);
+    sdk_system_station_got_ip_set(ip_2_ip4(&netif->ip_addr),
+                                  ip_2_ip4(&netif->netmask),
+                                  ip_2_ip4(&netif->gw));
 }
 
 void sdk_wpa_neg_complete() {
@@ -120,5 +125,3 @@ void sdk_wpa_attach(struct sdk_g_ic_st *g_ic) {
                      wpa_callback2, sdk_wpa_neg_complete);
     sdk_ppRegisterTxCallback(sdk_eapol_txcb, 3);
 }
-
-#endif /* OPEN_LIBWPA_WPA_MAIN */