diff --git a/lib/Makefile b/lib/Makefile
index 2a76acf100..c35786a057 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -94,6 +94,8 @@ obj-$(CONFIG_LIBAVB) += libavb/
 obj-$(CONFIG_$(SPL_TPL_)OF_LIBFDT) += libfdt/
 obj-$(CONFIG_$(SPL_TPL_)OF_REAL) += fdtdec_common.o fdtdec.o
 
+obj-$(CONFIG_NET_LWIP) += lwip/
+
 ifdef CONFIG_SPL_BUILD
 obj-$(CONFIG_SPL_YMODEM_SUPPORT) += crc16-ccitt.o
 obj-$(CONFIG_$(SPL_TPL_)HASH) += crc16-ccitt.o
diff --git a/lib/lwip/Makefile b/lib/lwip/Makefile
new file mode 100644
index 0000000000..6022973f74
--- /dev/null
+++ b/lib/lwip/Makefile
@@ -0,0 +1,57 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Copyright (C) 2024 Linaro Ltd.
+
+ccflags-y += -I$(srctree)/lib/lwip/include -I$(srctree)/lib/lwip/u-boot
+
+obj-y += \
+	api/api_lib.o \
+	api/api_msg.o \
+	api/err.o \
+	api/if_api.o \
+	api/netbuf.o \
+	api/netdb.o \
+	api/netifapi.o \
+	api/sockets.o \
+	api/tcpip.o \
+	apps/http/http_client.o \
+	apps/tftp/tftp.o \
+	core/altcp_alloc.o \
+	core/altcp.o \
+	core/altcp_tcp.o \
+	core/def.o \
+	core/dns.o \
+	core/inet_chksum.o \
+	core/init.o \
+	core/ip.o \
+	core/ipv4/acd.o \
+	core/ipv4/autoip.o \
+	core/ipv4/dhcp.o \
+	core/ipv4/etharp.o \
+	core/ipv4/icmp.o \
+	core/ipv4/igmp.o \
+	core/ipv4/ip4_addr.o \
+	core/ipv4/ip4.o \
+	core/ipv4/ip4_frag.o \
+	core/ipv6/dhcp6.o \
+	core/ipv6/ethip6.o \
+	core/ipv6/icmp6.o \
+	core/ipv6/inet6.o \
+	core/ipv6/ip6_addr.o \
+	core/ipv6/ip6.o \
+	core/ipv6/ip6_frag.o \
+	core/ipv6/mld6.o \
+	core/ipv6/nd6.o \
+	core/mem.o \
+	core/memp.o \
+	core/netif.o \
+	core/pbuf.o \
+	core/raw.o \
+	core/stats.o \
+	core/sys.o \
+	core/tcp.o \
+	core/tcp_in.o \
+	core/tcp_out.o \
+	core/timeouts.o \
+	core/udp.o \
+	netif/ethernet.o
diff --git a/lib/lwip/README b/lib/lwip/README
new file mode 100644
index 0000000000..fa1795c9a8
--- /dev/null
+++ b/lib/lwip/README
@@ -0,0 +1,6 @@
+Lightweight IP (lwIP) library
+
+The source code in this directory was imported from:
+http://git.savannah.gnu.org/cgit/lwip.git
+
+Please see the commit description for details.
diff --git a/lib/lwip/api/api_lib.c b/lib/lwip/api/api_lib.c
new file mode 100644
index 0000000000..60678f88e2
--- /dev/null
+++ b/lib/lwip/api/api_lib.c
@@ -0,0 +1,1367 @@
+/**
+ * @file
+ * Sequential API External module
+ *
+ * @defgroup netconn Netconn API
+ * @ingroup sequential_api
+ * Thread-safe, to be called from non-TCPIP threads only.
+ * TX/RX handling based on @ref netbuf (containing @ref pbuf)
+ * to avoid copying data around.
+ *
+ * @defgroup netconn_common Common functions
+ * @ingroup netconn
+ * For use with TCP and UDP
+ *
+ * @defgroup netconn_tcp TCP only
+ * @ingroup netconn
+ * TCP only functions
+ *
+ * @defgroup netconn_udp UDP only
+ * @ingroup netconn
+ * UDP only functions
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ */
+
+/* This is the part of the API that is linked with
+   the application */
+
+#include "lwip/opt.h"
+
+#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/api.h"
+#include "lwip/memp.h"
+
+#include "lwip/ip.h"
+#include "lwip/raw.h"
+#include "lwip/udp.h"
+#include "lwip/priv/api_msg.h"
+#include "lwip/priv/tcp_priv.h"
+#include "lwip/priv/tcpip_priv.h"
+
+#ifdef LWIP_HOOK_FILENAME
+#include LWIP_HOOK_FILENAME
+#endif
+
+#include <string.h>
+
+#define API_MSG_VAR_REF(name)               API_VAR_REF(name)
+#define API_MSG_VAR_DECLARE(name)           API_VAR_DECLARE(struct api_msg, name)
+#define API_MSG_VAR_ALLOC(name)             API_VAR_ALLOC(struct api_msg, MEMP_API_MSG, name, ERR_MEM)
+#define API_MSG_VAR_ALLOC_RETURN_NULL(name) API_VAR_ALLOC(struct api_msg, MEMP_API_MSG, name, NULL)
+#define API_MSG_VAR_FREE(name)              API_VAR_FREE(MEMP_API_MSG, name)
+
+#if TCP_LISTEN_BACKLOG
+/* need to allocate API message for accept so empty message pool does not result in event loss
+ * see bug #47512: MPU_COMPATIBLE may fail on empty pool */
+#define API_MSG_VAR_ALLOC_ACCEPT(msg) API_MSG_VAR_ALLOC(msg)
+#define API_MSG_VAR_FREE_ACCEPT(msg) API_MSG_VAR_FREE(msg)
+#else /* TCP_LISTEN_BACKLOG */
+#define API_MSG_VAR_ALLOC_ACCEPT(msg)
+#define API_MSG_VAR_FREE_ACCEPT(msg)
+#endif /* TCP_LISTEN_BACKLOG */
+
+#if LWIP_NETCONN_FULLDUPLEX
+#define NETCONN_RECVMBOX_WAITABLE(conn) (sys_mbox_valid(&(conn)->recvmbox) && (((conn)->flags & NETCONN_FLAG_MBOXINVALID) == 0))
+#define NETCONN_ACCEPTMBOX_WAITABLE(conn) (sys_mbox_valid(&(conn)->acceptmbox) && (((conn)->flags & (NETCONN_FLAG_MBOXCLOSED|NETCONN_FLAG_MBOXINVALID)) == 0))
+#define NETCONN_MBOX_WAITING_INC(conn) SYS_ARCH_INC(conn->mbox_threads_waiting, 1)
+#define NETCONN_MBOX_WAITING_DEC(conn) SYS_ARCH_DEC(conn->mbox_threads_waiting, 1)
+#else /* LWIP_NETCONN_FULLDUPLEX */
+#define NETCONN_RECVMBOX_WAITABLE(conn)   sys_mbox_valid(&(conn)->recvmbox)
+#define NETCONN_ACCEPTMBOX_WAITABLE(conn) (sys_mbox_valid(&(conn)->acceptmbox) && (((conn)->flags & NETCONN_FLAG_MBOXCLOSED) == 0))
+#define NETCONN_MBOX_WAITING_INC(conn)
+#define NETCONN_MBOX_WAITING_DEC(conn)
+#endif /* LWIP_NETCONN_FULLDUPLEX */
+
+static err_t netconn_close_shutdown(struct netconn *conn, u8_t how);
+
+/**
+ * Call the lower part of a netconn_* function
+ * This function is then running in the thread context
+ * of tcpip_thread and has exclusive access to lwIP core code.
+ *
+ * @param fn function to call
+ * @param apimsg a struct containing the function to call and its parameters
+ * @return ERR_OK if the function was called, another err_t if not
+ */
+static err_t
+netconn_apimsg(tcpip_callback_fn fn, struct api_msg *apimsg)
+{
+  err_t err;
+
+#ifdef LWIP_DEBUG
+  /* catch functions that don't set err */
+  apimsg->err = ERR_VAL;
+#endif /* LWIP_DEBUG */
+
+#if LWIP_NETCONN_SEM_PER_THREAD
+  apimsg->op_completed_sem = LWIP_NETCONN_THREAD_SEM_GET();
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
+
+  err = tcpip_send_msg_wait_sem(fn, apimsg, LWIP_API_MSG_SEM(apimsg));
+  if (err == ERR_OK) {
+    return apimsg->err;
+  }
+  return err;
+}
+
+/**
+ * Create a new netconn (of a specific type) that has a callback function.
+ * The corresponding pcb is also created.
+ *
+ * @param t the type of 'connection' to create (@see enum netconn_type)
+ * @param proto the IP protocol for RAW IP pcbs
+ * @param callback a function to call on status changes (RX available, TX'ed)
+ * @return a newly allocated struct netconn or
+ *         NULL on memory error
+ */
+struct netconn *
+netconn_new_with_proto_and_callback(enum netconn_type t, u8_t proto, netconn_callback callback)
+{
+  struct netconn *conn;
+  API_MSG_VAR_DECLARE(msg);
+  API_MSG_VAR_ALLOC_RETURN_NULL(msg);
+
+  conn = netconn_alloc(t, callback);
+  if (conn != NULL) {
+    err_t err;
+
+    API_MSG_VAR_REF(msg).msg.n.proto = proto;
+    API_MSG_VAR_REF(msg).conn = conn;
+    err = netconn_apimsg(lwip_netconn_do_newconn, &API_MSG_VAR_REF(msg));
+    if (err != ERR_OK) {
+      LWIP_ASSERT("freeing conn without freeing pcb", conn->pcb.tcp == NULL);
+      LWIP_ASSERT("conn has no recvmbox", sys_mbox_valid(&conn->recvmbox));
+#if LWIP_TCP
+      LWIP_ASSERT("conn->acceptmbox shouldn't exist", !sys_mbox_valid(&conn->acceptmbox));
+#endif /* LWIP_TCP */
+#if !LWIP_NETCONN_SEM_PER_THREAD
+      LWIP_ASSERT("conn has no op_completed", sys_sem_valid(&conn->op_completed));
+      sys_sem_free(&conn->op_completed);
+#endif /* !LWIP_NETCONN_SEM_PER_THREAD */
+      sys_mbox_free(&conn->recvmbox);
+      memp_free(MEMP_NETCONN, conn);
+      API_MSG_VAR_FREE(msg);
+      return NULL;
+    }
+  }
+  API_MSG_VAR_FREE(msg);
+  return conn;
+}
+
+/**
+ * @ingroup netconn_common
+ * Close a netconn 'connection' and free all its resources but not the netconn itself.
+ * UDP and RAW connection are completely closed, TCP pcbs might still be in a waitstate
+ * after this returns.
+ *
+ * @param conn the netconn to delete
+ * @return ERR_OK if the connection was deleted
+ */
+err_t
+netconn_prepare_delete(struct netconn *conn)
+{
+  err_t err;
+  API_MSG_VAR_DECLARE(msg);
+
+  /* No ASSERT here because possible to get a (conn == NULL) if we got an accept error */
+  if (conn == NULL) {
+    return ERR_OK;
+  }
+
+  API_MSG_VAR_ALLOC(msg);
+  API_MSG_VAR_REF(msg).conn = conn;
+#if LWIP_TCP
+#if LWIP_SO_SNDTIMEO || LWIP_SO_LINGER
+  /* get the time we started, which is later compared to
+     sys_now() + conn->send_timeout */
+  API_MSG_VAR_REF(msg).msg.sd.time_started = sys_now();
+#else /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
+  API_MSG_VAR_REF(msg).msg.sd.polls_left =
+    ((LWIP_TCP_CLOSE_TIMEOUT_MS_DEFAULT + TCP_SLOW_INTERVAL - 1) / TCP_SLOW_INTERVAL) + 1;
+#endif /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
+#endif /* LWIP_TCP */
+  err = netconn_apimsg(lwip_netconn_do_delconn, &API_MSG_VAR_REF(msg));
+  API_MSG_VAR_FREE(msg);
+
+  if (err != ERR_OK) {
+    return err;
+  }
+  return ERR_OK;
+}
+
+/**
+ * @ingroup netconn_common
+ * Close a netconn 'connection' and free its resources.
+ * UDP and RAW connection are completely closed, TCP pcbs might still be in a waitstate
+ * after this returns.
+ *
+ * @param conn the netconn to delete
+ * @return ERR_OK if the connection was deleted
+ */
+err_t
+netconn_delete(struct netconn *conn)
+{
+  err_t err;
+
+  /* No ASSERT here because possible to get a (conn == NULL) if we got an accept error */
+  if (conn == NULL) {
+    return ERR_OK;
+  }
+
+#if LWIP_NETCONN_FULLDUPLEX
+  if (conn->flags & NETCONN_FLAG_MBOXINVALID) {
+    /* Already called netconn_prepare_delete() before */
+    err = ERR_OK;
+  } else
+#endif /* LWIP_NETCONN_FULLDUPLEX */
+  {
+    err = netconn_prepare_delete(conn);
+  }
+  if (err == ERR_OK) {
+    netconn_free(conn);
+  }
+  return err;
+}
+
+/**
+ * Get the local or remote IP address and port of a netconn.
+ * For RAW netconns, this returns the protocol instead of a port!
+ *
+ * @param conn the netconn to query
+ * @param addr a pointer to which to save the IP address
+ * @param port a pointer to which to save the port (or protocol for RAW)
+ * @param local 1 to get the local IP address, 0 to get the remote one
+ * @return ERR_CONN for invalid connections
+ *         ERR_OK if the information was retrieved
+ */
+err_t
+netconn_getaddr(struct netconn *conn, ip_addr_t *addr, u16_t *port, u8_t local)
+{
+  API_MSG_VAR_DECLARE(msg);
+  err_t err;
+
+  LWIP_ERROR("netconn_getaddr: invalid conn", (conn != NULL), return ERR_ARG;);
+  LWIP_ERROR("netconn_getaddr: invalid addr", (addr != NULL), return ERR_ARG;);
+  LWIP_ERROR("netconn_getaddr: invalid port", (port != NULL), return ERR_ARG;);
+
+  API_MSG_VAR_ALLOC(msg);
+  API_MSG_VAR_REF(msg).conn = conn;
+  API_MSG_VAR_REF(msg).msg.ad.local = local;
+#if LWIP_MPU_COMPATIBLE
+  err = netconn_apimsg(lwip_netconn_do_getaddr, &API_MSG_VAR_REF(msg));
+  *addr = msg->msg.ad.ipaddr;
+  *port = msg->msg.ad.port;
+#else /* LWIP_MPU_COMPATIBLE */
+  msg.msg.ad.ipaddr = addr;
+  msg.msg.ad.port = port;
+  err = netconn_apimsg(lwip_netconn_do_getaddr, &msg);
+#endif /* LWIP_MPU_COMPATIBLE */
+  API_MSG_VAR_FREE(msg);
+
+  return err;
+}
+
+/**
+ * @ingroup netconn_common
+ * Bind a netconn to a specific local IP address and port.
+ * Binding one netconn twice might not always be checked correctly!
+ *
+ * @param conn the netconn to bind
+ * @param addr the local IP address to bind the netconn to
+ *             (use IP4_ADDR_ANY/IP6_ADDR_ANY to bind to all addresses)
+ * @param port the local port to bind the netconn to (not used for RAW)
+ * @return ERR_OK if bound, any other err_t on failure
+ */
+err_t
+netconn_bind(struct netconn *conn, const ip_addr_t *addr, u16_t port)
+{
+  API_MSG_VAR_DECLARE(msg);
+  err_t err;
+
+  LWIP_ERROR("netconn_bind: invalid conn", (conn != NULL), return ERR_ARG;);
+
+#if LWIP_IPV4
+  /* Don't propagate NULL pointer (IP_ADDR_ANY alias) to subsequent functions */
+  if (addr == NULL) {
+    addr = IP4_ADDR_ANY;
+  }
+#endif /* LWIP_IPV4 */
+
+#if LWIP_IPV4 && LWIP_IPV6
+  /* "Socket API like" dual-stack support: If IP to bind to is IP6_ADDR_ANY,
+   * and NETCONN_FLAG_IPV6_V6ONLY is 0, use IP_ANY_TYPE to bind
+   */
+  if ((netconn_get_ipv6only(conn) == 0) &&
+      ip_addr_eq(addr, IP6_ADDR_ANY)) {
+    addr = IP_ANY_TYPE;
+  }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
+  API_MSG_VAR_ALLOC(msg);
+  API_MSG_VAR_REF(msg).conn = conn;
+  API_MSG_VAR_REF(msg).msg.bc.ipaddr = API_MSG_VAR_REF(addr);
+  API_MSG_VAR_REF(msg).msg.bc.port = port;
+  err = netconn_apimsg(lwip_netconn_do_bind, &API_MSG_VAR_REF(msg));
+  API_MSG_VAR_FREE(msg);
+
+  return err;
+}
+
+/**
+ * @ingroup netconn_common
+ * Bind a netconn to a specific interface and port.
+ * Binding one netconn twice might not always be checked correctly!
+ *
+ * @param conn the netconn to bind
+ * @param if_idx the local interface index to bind the netconn to
+ * @return ERR_OK if bound, any other err_t on failure
+ */
+err_t
+netconn_bind_if(struct netconn *conn, u8_t if_idx)
+{
+  API_MSG_VAR_DECLARE(msg);
+  err_t err;
+
+  LWIP_ERROR("netconn_bind_if: invalid conn", (conn != NULL), return ERR_ARG;);
+
+  API_MSG_VAR_ALLOC(msg);
+  API_MSG_VAR_REF(msg).conn = conn;
+  API_MSG_VAR_REF(msg).msg.bc.if_idx = if_idx;
+  err = netconn_apimsg(lwip_netconn_do_bind_if, &API_MSG_VAR_REF(msg));
+  API_MSG_VAR_FREE(msg);
+
+  return err;
+}
+
+/**
+ * @ingroup netconn_common
+ * Connect a netconn to a specific remote IP address and port.
+ *
+ * @param conn the netconn to connect
+ * @param addr the remote IP address to connect to
+ * @param port the remote port to connect to (no used for RAW)
+ * @return ERR_OK if connected, return value of tcp_/udp_/raw_connect otherwise
+ */
+err_t
+netconn_connect(struct netconn *conn, const ip_addr_t *addr, u16_t port)
+{
+  API_MSG_VAR_DECLARE(msg);
+  err_t err;
+
+  LWIP_ERROR("netconn_connect: invalid conn", (conn != NULL), return ERR_ARG;);
+
+#if LWIP_IPV4
+  /* Don't propagate NULL pointer (IP_ADDR_ANY alias) to subsequent functions */
+  if (addr == NULL) {
+    addr = IP4_ADDR_ANY;
+  }
+#endif /* LWIP_IPV4 */
+
+  API_MSG_VAR_ALLOC(msg);
+  API_MSG_VAR_REF(msg).conn = conn;
+  API_MSG_VAR_REF(msg).msg.bc.ipaddr = API_MSG_VAR_REF(addr);
+  API_MSG_VAR_REF(msg).msg.bc.port = port;
+  err = netconn_apimsg(lwip_netconn_do_connect, &API_MSG_VAR_REF(msg));
+  API_MSG_VAR_FREE(msg);
+
+  return err;
+}
+
+/**
+ * @ingroup netconn_udp
+ * Disconnect a netconn from its current peer (only valid for UDP netconns).
+ *
+ * @param conn the netconn to disconnect
+ * @return See @ref err_t
+ */
+err_t
+netconn_disconnect(struct netconn *conn)
+{
+  API_MSG_VAR_DECLARE(msg);
+  err_t err;
+
+  LWIP_ERROR("netconn_disconnect: invalid conn", (conn != NULL), return ERR_ARG;);
+
+  API_MSG_VAR_ALLOC(msg);
+  API_MSG_VAR_REF(msg).conn = conn;
+  err = netconn_apimsg(lwip_netconn_do_disconnect, &API_MSG_VAR_REF(msg));
+  API_MSG_VAR_FREE(msg);
+
+  return err;
+}
+
+/**
+ * @ingroup netconn_tcp
+ * Set a TCP netconn into listen mode
+ *
+ * @param conn the tcp netconn to set to listen mode
+ * @param backlog the listen backlog, only used if TCP_LISTEN_BACKLOG==1
+ * @return ERR_OK if the netconn was set to listen (UDP and RAW netconns
+ *         don't return any error (yet?))
+ */
+err_t
+netconn_listen_with_backlog(struct netconn *conn, u8_t backlog)
+{
+#if LWIP_TCP
+  API_MSG_VAR_DECLARE(msg);
+  err_t err;
+
+  /* This does no harm. If TCP_LISTEN_BACKLOG is off, backlog is unused. */
+  LWIP_UNUSED_ARG(backlog);
+
+  LWIP_ERROR("netconn_listen: invalid conn", (conn != NULL), return ERR_ARG;);
+
+  API_MSG_VAR_ALLOC(msg);
+  API_MSG_VAR_REF(msg).conn = conn;
+#if TCP_LISTEN_BACKLOG
+  API_MSG_VAR_REF(msg).msg.lb.backlog = backlog;
+#endif /* TCP_LISTEN_BACKLOG */
+  err = netconn_apimsg(lwip_netconn_do_listen, &API_MSG_VAR_REF(msg));
+  API_MSG_VAR_FREE(msg);
+
+  return err;
+#else /* LWIP_TCP */
+  LWIP_UNUSED_ARG(conn);
+  LWIP_UNUSED_ARG(backlog);
+  return ERR_ARG;
+#endif /* LWIP_TCP */
+}
+
+/**
+ * @ingroup netconn_tcp
+ * Accept a new connection on a TCP listening netconn.
+ *
+ * @param conn the TCP listen netconn
+ * @param new_conn pointer where the new connection is stored
+ * @return ERR_OK if a new connection has been received or an error
+ *                code otherwise
+ */
+err_t
+netconn_accept(struct netconn *conn, struct netconn **new_conn)
+{
+#if LWIP_TCP
+  err_t err;
+  void *accept_ptr;
+  struct netconn *newconn;
+#if TCP_LISTEN_BACKLOG
+  API_MSG_VAR_DECLARE(msg);
+#endif /* TCP_LISTEN_BACKLOG */
+
+  LWIP_ERROR("netconn_accept: invalid pointer",    (new_conn != NULL),                  return ERR_ARG;);
+  *new_conn = NULL;
+  LWIP_ERROR("netconn_accept: invalid conn",       (conn != NULL),                      return ERR_ARG;);
+
+  /* NOTE: Although the opengroup spec says a pending error shall be returned to
+           send/recv/getsockopt(SO_ERROR) only, we return it for listening
+           connections also, to handle embedded-system errors */
+  err = netconn_err(conn);
+  if (err != ERR_OK) {
+    /* return pending error */
+    return err;
+  }
+  if (!NETCONN_ACCEPTMBOX_WAITABLE(conn)) {
+    /* don't accept if closed: this might block the application task
+       waiting on acceptmbox forever! */
+    return ERR_CLSD;
+  }
+
+  API_MSG_VAR_ALLOC_ACCEPT(msg);
+
+  NETCONN_MBOX_WAITING_INC(conn);
+  if (netconn_is_nonblocking(conn)) {
+    if (sys_arch_mbox_tryfetch(&conn->acceptmbox, &accept_ptr) == SYS_MBOX_EMPTY) {
+      API_MSG_VAR_FREE_ACCEPT(msg);
+      NETCONN_MBOX_WAITING_DEC(conn);
+      return ERR_WOULDBLOCK;
+    }
+  } else {
+#if LWIP_SO_RCVTIMEO
+    if (sys_arch_mbox_fetch(&conn->acceptmbox, &accept_ptr, conn->recv_timeout) == SYS_ARCH_TIMEOUT) {
+      API_MSG_VAR_FREE_ACCEPT(msg);
+      NETCONN_MBOX_WAITING_DEC(conn);
+      return ERR_TIMEOUT;
+    }
+#else
+    sys_arch_mbox_fetch(&conn->acceptmbox, &accept_ptr, 0);
+#endif /* LWIP_SO_RCVTIMEO*/
+  }
+  NETCONN_MBOX_WAITING_DEC(conn);
+#if LWIP_NETCONN_FULLDUPLEX
+  if (conn->flags & NETCONN_FLAG_MBOXINVALID) {
+    if (lwip_netconn_is_deallocated_msg(accept_ptr)) {
+      /* the netconn has been closed from another thread */
+      API_MSG_VAR_FREE_ACCEPT(msg);
+      return ERR_CONN;
+    }
+  }
+#endif
+
+  /* Register event with callback */
+  API_EVENT(conn, NETCONN_EVT_RCVMINUS, 0);
+
+  if (lwip_netconn_is_err_msg(accept_ptr, &err)) {
+    /* a connection has been aborted: e.g. out of pcbs or out of netconns during accept */
+    API_MSG_VAR_FREE_ACCEPT(msg);
+    return err;
+  }
+  if (accept_ptr == NULL) {
+    /* connection has been aborted */
+    API_MSG_VAR_FREE_ACCEPT(msg);
+    return ERR_CLSD;
+  }
+  newconn = (struct netconn *)accept_ptr;
+#if TCP_LISTEN_BACKLOG
+  /* Let the stack know that we have accepted the connection. */
+  API_MSG_VAR_REF(msg).conn = newconn;
+  /* don't care for the return value of lwip_netconn_do_recv */
+  netconn_apimsg(lwip_netconn_do_accepted, &API_MSG_VAR_REF(msg));
+  API_MSG_VAR_FREE(msg);
+#endif /* TCP_LISTEN_BACKLOG */
+
+  *new_conn = newconn;
+  /* don't set conn->last_err: it's only ERR_OK, anyway */
+  return ERR_OK;
+#else /* LWIP_TCP */
+  LWIP_UNUSED_ARG(conn);
+  LWIP_UNUSED_ARG(new_conn);
+  return ERR_ARG;
+#endif /* LWIP_TCP */
+}
+
+/**
+ * @ingroup netconn_common
+ * Receive data: actual implementation that doesn't care whether pbuf or netbuf
+ * is received (this is internal, it's just here for describing common errors)
+ *
+ * @param conn the netconn from which to receive data
+ * @param new_buf pointer where a new pbuf/netbuf is stored when received data
+ * @param apiflags flags that control function behaviour. For now only:
+ * - NETCONN_DONTBLOCK: only read data that is available now, don't wait for more data
+ * @return ERR_OK if data has been received, an error code otherwise (timeout,
+ *                memory error or another error)
+ *         ERR_CONN if not connected
+ *         ERR_CLSD if TCP connection has been closed
+ *         ERR_WOULDBLOCK if the netconn is nonblocking but would block to wait for data
+ *         ERR_TIMEOUT if the netconn has a receive timeout and no data was received
+ */
+static err_t
+netconn_recv_data(struct netconn *conn, void **new_buf, u8_t apiflags)
+{
+  void *buf = NULL;
+  u16_t len;
+
+  LWIP_ERROR("netconn_recv: invalid pointer", (new_buf != NULL), return ERR_ARG;);
+  *new_buf = NULL;
+  LWIP_ERROR("netconn_recv: invalid conn",    (conn != NULL),    return ERR_ARG;);
+
+  if (!NETCONN_RECVMBOX_WAITABLE(conn)) {
+    err_t err = netconn_err(conn);
+    if (err != ERR_OK) {
+      /* return pending error */
+      return err;
+    }
+    return ERR_CONN;
+  }
+
+  NETCONN_MBOX_WAITING_INC(conn);
+  if (netconn_is_nonblocking(conn) || (apiflags & NETCONN_DONTBLOCK) ||
+      (conn->flags & NETCONN_FLAG_MBOXCLOSED) || (conn->pending_err != ERR_OK)) {
+    if (sys_arch_mbox_tryfetch(&conn->recvmbox, &buf) == SYS_MBOX_EMPTY) {
+      err_t err;
+      NETCONN_MBOX_WAITING_DEC(conn);
+      err = netconn_err(conn);
+      if (err != ERR_OK) {
+        /* return pending error */
+        return err;
+      }
+      if (conn->flags & NETCONN_FLAG_MBOXCLOSED) {
+        return ERR_CONN;
+      }
+      return ERR_WOULDBLOCK;
+    }
+  } else {
+#if LWIP_SO_RCVTIMEO
+    if (sys_arch_mbox_fetch(&conn->recvmbox, &buf, conn->recv_timeout) == SYS_ARCH_TIMEOUT) {
+      NETCONN_MBOX_WAITING_DEC(conn);
+      return ERR_TIMEOUT;
+    }
+#else
+    sys_arch_mbox_fetch(&conn->recvmbox, &buf, 0);
+#endif /* LWIP_SO_RCVTIMEO*/
+  }
+  NETCONN_MBOX_WAITING_DEC(conn);
+#if LWIP_NETCONN_FULLDUPLEX
+  if (conn->flags & NETCONN_FLAG_MBOXINVALID) {
+    if (lwip_netconn_is_deallocated_msg(buf)) {
+      /* the netconn has been closed from another thread */
+      API_MSG_VAR_FREE_ACCEPT(msg);
+      return ERR_CONN;
+    }
+  }
+#endif
+
+#if LWIP_TCP
+#if (LWIP_UDP || LWIP_RAW)
+  if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP)
+#endif /* (LWIP_UDP || LWIP_RAW) */
+  {
+    err_t err;
+    /* Check if this is an error message or a pbuf */
+    if (lwip_netconn_is_err_msg(buf, &err)) {
+      /* new_buf has been zeroed above already */
+      if (err == ERR_CLSD) {
+        /* connection closed translates to ERR_OK with *new_buf == NULL */
+        return ERR_OK;
+      }
+      return err;
+    }
+    len = ((struct pbuf *)buf)->tot_len;
+  }
+#endif /* LWIP_TCP */
+#if LWIP_TCP && (LWIP_UDP || LWIP_RAW)
+  else
+#endif /* LWIP_TCP && (LWIP_UDP || LWIP_RAW) */
+#if (LWIP_UDP || LWIP_RAW)
+  {
+    LWIP_ASSERT("buf != NULL", buf != NULL);
+    len = netbuf_len((struct netbuf *)buf);
+  }
+#endif /* (LWIP_UDP || LWIP_RAW) */
+
+#if LWIP_SO_RCVBUF
+  SYS_ARCH_DEC(conn->recv_avail, len);
+#endif /* LWIP_SO_RCVBUF */
+  /* Register event with callback */
+  API_EVENT(conn, NETCONN_EVT_RCVMINUS, len);
+
+  LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_recv_data: received %p, len=%"U16_F"\n", buf, len));
+
+  *new_buf = buf;
+  /* don't set conn->last_err: it's only ERR_OK, anyway */
+  return ERR_OK;
+}
+
+#if LWIP_TCP
+static err_t
+netconn_tcp_recvd_msg(struct netconn *conn, size_t len, struct api_msg *msg)
+{
+  LWIP_ERROR("netconn_recv_tcp_pbuf: invalid conn", (conn != NULL) &&
+             NETCONNTYPE_GROUP(netconn_type(conn)) == NETCONN_TCP, return ERR_ARG;);
+
+  msg->conn = conn;
+  msg->msg.r.len = len;
+
+  return netconn_apimsg(lwip_netconn_do_recv, msg);
+}
+
+err_t
+netconn_tcp_recvd(struct netconn *conn, size_t len)
+{
+  err_t err;
+  API_MSG_VAR_DECLARE(msg);
+  LWIP_ERROR("netconn_recv_tcp_pbuf: invalid conn", (conn != NULL) &&
+             NETCONNTYPE_GROUP(netconn_type(conn)) == NETCONN_TCP, return ERR_ARG;);
+
+  API_MSG_VAR_ALLOC(msg);
+  err = netconn_tcp_recvd_msg(conn, len, &API_VAR_REF(msg));
+  API_MSG_VAR_FREE(msg);
+  return err;
+}
+
+static err_t
+netconn_recv_data_tcp(struct netconn *conn, struct pbuf **new_buf, u8_t apiflags)
+{
+  err_t err;
+  struct pbuf *buf;
+  API_MSG_VAR_DECLARE(msg);
+#if LWIP_MPU_COMPATIBLE
+  msg = NULL;
+#endif
+
+  if (!NETCONN_RECVMBOX_WAITABLE(conn)) {
+    /* This only happens when calling this function more than once *after* receiving FIN */
+    return ERR_CONN;
+  }
+  if (netconn_is_flag_set(conn, NETCONN_FIN_RX_PENDING)) {
+    netconn_clear_flags(conn, NETCONN_FIN_RX_PENDING);
+    goto handle_fin;
+  }
+
+  if (!(apiflags & NETCONN_NOAUTORCVD)) {
+    /* need to allocate API message here so empty message pool does not result in event loss
+      * see bug #47512: MPU_COMPATIBLE may fail on empty pool */
+    API_MSG_VAR_ALLOC(msg);
+  }
+
+  err = netconn_recv_data(conn, (void **)new_buf, apiflags);
+  if (err != ERR_OK) {
+    if (!(apiflags & NETCONN_NOAUTORCVD)) {
+      API_MSG_VAR_FREE(msg);
+    }
+    return err;
+  }
+  buf = *new_buf;
+  if (!(apiflags & NETCONN_NOAUTORCVD)) {
+    /* Let the stack know that we have taken the data. */
+    u16_t len = buf ? buf->tot_len : 1;
+    /* don't care for the return value of lwip_netconn_do_recv */
+    /* @todo: this should really be fixed, e.g. by retrying in poll on error */
+    netconn_tcp_recvd_msg(conn, len,  &API_VAR_REF(msg));
+    API_MSG_VAR_FREE(msg);
+  }
+
+  /* If we are closed, we indicate that we no longer wish to use the socket */
+  if (buf == NULL) {
+    if (apiflags & NETCONN_NOFIN) {
+      /* received a FIN but the caller cannot handle it right now:
+         re-enqueue it and return "no data" */
+      netconn_set_flags(conn, NETCONN_FIN_RX_PENDING);
+      return ERR_WOULDBLOCK;
+    } else {
+handle_fin:
+      API_EVENT(conn, NETCONN_EVT_RCVMINUS, 0);
+      if (conn->pcb.ip == NULL) {
+        /* race condition: RST during recv */
+        err = netconn_err(conn);
+        if (err != ERR_OK) {
+          return err;
+        }
+        return ERR_RST;
+      }
+      /* RX side is closed, so deallocate the recvmbox */
+      netconn_close_shutdown(conn, NETCONN_SHUT_RD);
+      /* Don' store ERR_CLSD as conn->err since we are only half-closed */
+      return ERR_CLSD;
+    }
+  }
+  return err;
+}
+
+/**
+ * @ingroup netconn_tcp
+ * Receive data (in form of a pbuf) from a TCP netconn
+ *
+ * @param conn the netconn from which to receive data
+ * @param new_buf pointer where a new pbuf is stored when received data
+ * @return ERR_OK if data has been received, an error code otherwise (timeout,
+ *                memory error or another error, @see netconn_recv_data)
+ *         ERR_ARG if conn is not a TCP netconn
+ */
+err_t
+netconn_recv_tcp_pbuf(struct netconn *conn, struct pbuf **new_buf)
+{
+  LWIP_ERROR("netconn_recv_tcp_pbuf: invalid conn", (conn != NULL) &&
+             NETCONNTYPE_GROUP(netconn_type(conn)) == NETCONN_TCP, return ERR_ARG;);
+
+  return netconn_recv_data_tcp(conn, new_buf, 0);
+}
+
+/**
+ * @ingroup netconn_tcp
+ * Receive data (in form of a pbuf) from a TCP netconn
+ *
+ * @param conn the netconn from which to receive data
+ * @param new_buf pointer where a new pbuf is stored when received data
+ * @param apiflags flags that control function behaviour. For now only:
+ * - NETCONN_DONTBLOCK: only read data that is available now, don't wait for more data
+ * @return ERR_OK if data has been received, an error code otherwise (timeout,
+ *                memory error or another error, @see netconn_recv_data)
+ *         ERR_ARG if conn is not a TCP netconn
+ */
+err_t
+netconn_recv_tcp_pbuf_flags(struct netconn *conn, struct pbuf **new_buf, u8_t apiflags)
+{
+  LWIP_ERROR("netconn_recv_tcp_pbuf: invalid conn", (conn != NULL) &&
+             NETCONNTYPE_GROUP(netconn_type(conn)) == NETCONN_TCP, return ERR_ARG;);
+
+  return netconn_recv_data_tcp(conn, new_buf, apiflags);
+}
+#endif /* LWIP_TCP */
+
+/**
+ * Receive data (in form of a netbuf) from a UDP or RAW netconn
+ *
+ * @param conn the netconn from which to receive data
+ * @param new_buf pointer where a new netbuf is stored when received data
+ * @return ERR_OK if data has been received, an error code otherwise (timeout,
+ *                memory error or another error)
+ *         ERR_ARG if conn is not a UDP/RAW netconn
+ */
+err_t
+netconn_recv_udp_raw_netbuf(struct netconn *conn, struct netbuf **new_buf)
+{
+  LWIP_ERROR("netconn_recv_udp_raw_netbuf: invalid conn", (conn != NULL) &&
+             NETCONNTYPE_GROUP(netconn_type(conn)) != NETCONN_TCP, return ERR_ARG;);
+
+  return netconn_recv_data(conn, (void **)new_buf, 0);
+}
+
+/**
+ * Receive data (in form of a netbuf) from a UDP or RAW netconn
+ *
+ * @param conn the netconn from which to receive data
+ * @param new_buf pointer where a new netbuf is stored when received data
+ * @param apiflags flags that control function behaviour. For now only:
+ * - NETCONN_DONTBLOCK: only read data that is available now, don't wait for more data
+ * @return ERR_OK if data has been received, an error code otherwise (timeout,
+ *                memory error or another error)
+ *         ERR_ARG if conn is not a UDP/RAW netconn
+ */
+err_t
+netconn_recv_udp_raw_netbuf_flags(struct netconn *conn, struct netbuf **new_buf, u8_t apiflags)
+{
+  LWIP_ERROR("netconn_recv_udp_raw_netbuf: invalid conn", (conn != NULL) &&
+             NETCONNTYPE_GROUP(netconn_type(conn)) != NETCONN_TCP, return ERR_ARG;);
+
+  return netconn_recv_data(conn, (void **)new_buf, apiflags);
+}
+
+/**
+ * @ingroup netconn_common
+ * Receive data (in form of a netbuf containing a packet buffer) from a netconn
+ *
+ * @param conn the netconn from which to receive data
+ * @param new_buf pointer where a new netbuf is stored when received data
+ * @return ERR_OK if data has been received, an error code otherwise (timeout,
+ *                memory error or another error)
+ */
+err_t
+netconn_recv(struct netconn *conn, struct netbuf **new_buf)
+{
+#if LWIP_TCP
+  struct netbuf *buf = NULL;
+  err_t err;
+#endif /* LWIP_TCP */
+
+  LWIP_ERROR("netconn_recv: invalid pointer", (new_buf != NULL), return ERR_ARG;);
+  *new_buf = NULL;
+  LWIP_ERROR("netconn_recv: invalid conn",    (conn != NULL),    return ERR_ARG;);
+
+#if LWIP_TCP
+#if (LWIP_UDP || LWIP_RAW)
+  if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP)
+#endif /* (LWIP_UDP || LWIP_RAW) */
+  {
+    struct pbuf *p = NULL;
+    /* This is not a listening netconn, since recvmbox is set */
+
+    buf = (struct netbuf *)memp_malloc(MEMP_NETBUF);
+    if (buf == NULL) {
+      return ERR_MEM;
+    }
+
+    err = netconn_recv_data_tcp(conn, &p, 0);
+    if (err != ERR_OK) {
+      memp_free(MEMP_NETBUF, buf);
+      return err;
+    }
+    LWIP_ASSERT("p != NULL", p != NULL);
+
+    buf->p = p;
+    buf->ptr = p;
+    buf->port = 0;
+    ip_addr_set_zero(&buf->addr);
+    *new_buf = buf;
+    /* don't set conn->last_err: it's only ERR_OK, anyway */
+    return ERR_OK;
+  }
+#endif /* LWIP_TCP */
+#if LWIP_TCP && (LWIP_UDP || LWIP_RAW)
+  else
+#endif /* LWIP_TCP && (LWIP_UDP || LWIP_RAW) */
+  {
+#if (LWIP_UDP || LWIP_RAW)
+    return netconn_recv_data(conn, (void **)new_buf, 0);
+#endif /* (LWIP_UDP || LWIP_RAW) */
+  }
+}
+
+/**
+ * @ingroup netconn_udp
+ * Send data (in form of a netbuf) to a specific remote IP address and port.
+ * Only to be used for UDP and RAW netconns (not TCP).
+ *
+ * @param conn the netconn over which to send data
+ * @param buf a netbuf containing the data to send
+ * @param addr the remote IP address to which to send the data
+ * @param port the remote port to which to send the data
+ * @return ERR_OK if data was sent, any other err_t on error
+ */
+err_t
+netconn_sendto(struct netconn *conn, struct netbuf *buf, const ip_addr_t *addr, u16_t port)
+{
+  if (buf != NULL) {
+    ip_addr_set(&buf->addr, addr);
+    buf->port = port;
+    return netconn_send(conn, buf);
+  }
+  return ERR_VAL;
+}
+
+/**
+ * @ingroup netconn_udp
+ * Send data over a UDP or RAW netconn (that is already connected).
+ *
+ * @param conn the UDP or RAW netconn over which to send data
+ * @param buf a netbuf containing the data to send
+ * @return ERR_OK if data was sent, any other err_t on error
+ */
+err_t
+netconn_send(struct netconn *conn, struct netbuf *buf)
+{
+  API_MSG_VAR_DECLARE(msg);
+  err_t err;
+
+  LWIP_ERROR("netconn_send: invalid conn",  (conn != NULL), return ERR_ARG;);
+
+  LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_send: sending %"U16_F" bytes\n", buf->p->tot_len));
+
+  API_MSG_VAR_ALLOC(msg);
+  API_MSG_VAR_REF(msg).conn = conn;
+  API_MSG_VAR_REF(msg).msg.b = buf;
+  err = netconn_apimsg(lwip_netconn_do_send, &API_MSG_VAR_REF(msg));
+  API_MSG_VAR_FREE(msg);
+
+  return err;
+}
+
+/**
+ * @ingroup netconn_tcp
+ * Send data over a TCP netconn.
+ *
+ * @param conn the TCP netconn over which to send data
+ * @param dataptr pointer to the application buffer that contains the data to send
+ * @param size size of the application data to send
+ * @param apiflags combination of following flags :
+ * - NETCONN_COPY: data will be copied into memory belonging to the stack
+ * - NETCONN_MORE: for TCP connection, PSH flag will be set on last segment sent
+ * - NETCONN_DONTBLOCK: only write the data if all data can be written at once
+ * @param bytes_written pointer to a location that receives the number of written bytes
+ * @return ERR_OK if data was sent, any other err_t on error
+ */
+err_t
+netconn_write_partly(struct netconn *conn, const void *dataptr, size_t size,
+                     u8_t apiflags, size_t *bytes_written)
+{
+  struct netvector vector;
+  vector.ptr = dataptr;
+  vector.len = size;
+  return netconn_write_vectors_partly(conn, &vector, 1, apiflags, bytes_written);
+}
+
+/**
+ * Send vectorized data atomically over a TCP netconn.
+ *
+ * @param conn the TCP netconn over which to send data
+ * @param vectors array of vectors containing data to send
+ * @param vectorcnt number of vectors in the array
+ * @param apiflags combination of following flags :
+ * - NETCONN_COPY: data will be copied into memory belonging to the stack
+ * - NETCONN_MORE: for TCP connection, PSH flag will be set on last segment sent
+ * - NETCONN_DONTBLOCK: only write the data if all data can be written at once
+ * @param bytes_written pointer to a location that receives the number of written bytes
+ * @return ERR_OK if data was sent, any other err_t on error
+ */
+err_t
+netconn_write_vectors_partly(struct netconn *conn, struct netvector *vectors, u16_t vectorcnt,
+                             u8_t apiflags, size_t *bytes_written)
+{
+  API_MSG_VAR_DECLARE(msg);
+  err_t err;
+  u8_t dontblock;
+  size_t size;
+  int i;
+
+  LWIP_ERROR("netconn_write: invalid conn",  (conn != NULL), return ERR_ARG;);
+  LWIP_ERROR("netconn_write: invalid conn->type",  (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP), return ERR_VAL;);
+  dontblock = netconn_is_nonblocking(conn) || (apiflags & NETCONN_DONTBLOCK);
+#if LWIP_SO_SNDTIMEO
+  if (conn->send_timeout != 0) {
+    dontblock = 1;
+  }
+#endif /* LWIP_SO_SNDTIMEO */
+  if (dontblock && !bytes_written) {
+    /* This implies netconn_write() cannot be used for non-blocking send, since
+       it has no way to return the number of bytes written. */
+    return ERR_VAL;
+  }
+
+  /* sum up the total size */
+  size = 0;
+  for (i = 0; i < vectorcnt; i++) {
+    size += vectors[i].len;
+    if (size < vectors[i].len) {
+      /* overflow */
+      return ERR_VAL;
+    }
+  }
+  if (size == 0) {
+    return ERR_OK;
+  } else if (size > SSIZE_MAX) {
+    ssize_t limited;
+    /* this is required by the socket layer (cannot send full size_t range) */
+    if (!bytes_written) {
+      return ERR_VAL;
+    }
+    /* limit the amount of data to send */
+    limited = SSIZE_MAX;
+    size = (size_t)limited;
+  }
+
+  API_MSG_VAR_ALLOC(msg);
+  /* non-blocking write sends as much  */
+  API_MSG_VAR_REF(msg).conn = conn;
+  API_MSG_VAR_REF(msg).msg.w.vector = vectors;
+  API_MSG_VAR_REF(msg).msg.w.vector_cnt = vectorcnt;
+  API_MSG_VAR_REF(msg).msg.w.vector_off = 0;
+  API_MSG_VAR_REF(msg).msg.w.apiflags = apiflags;
+  API_MSG_VAR_REF(msg).msg.w.len = size;
+  API_MSG_VAR_REF(msg).msg.w.offset = 0;
+#if LWIP_SO_SNDTIMEO
+  if (conn->send_timeout != 0) {
+    /* get the time we started, which is later compared to
+        sys_now() + conn->send_timeout */
+    API_MSG_VAR_REF(msg).msg.w.time_started = sys_now();
+  } else {
+    API_MSG_VAR_REF(msg).msg.w.time_started = 0;
+  }
+#endif /* LWIP_SO_SNDTIMEO */
+
+  /* For locking the core: this _can_ be delayed on low memory/low send buffer,
+     but if it is, this is done inside api_msg.c:do_write(), so we can use the
+     non-blocking version here. */
+  err = netconn_apimsg(lwip_netconn_do_write, &API_MSG_VAR_REF(msg));
+  if (err == ERR_OK) {
+    if (bytes_written != NULL) {
+      *bytes_written = API_MSG_VAR_REF(msg).msg.w.offset;
+    }
+    /* for blocking, check all requested bytes were written, NOTE: send_timeout is
+       treated as dontblock (see dontblock assignment above) */
+    if (!dontblock) {
+      LWIP_ASSERT("do_write failed to write all bytes", API_MSG_VAR_REF(msg).msg.w.offset == size);
+    }
+  }
+  API_MSG_VAR_FREE(msg);
+
+  return err;
+}
+
+/**
+ * @ingroup netconn_tcp
+ * Close or shutdown a TCP netconn (doesn't delete it).
+ *
+ * @param conn the TCP netconn to close or shutdown
+ * @param how fully close or only shutdown one side?
+ * @return ERR_OK if the netconn was closed, any other err_t on error
+ */
+static err_t
+netconn_close_shutdown(struct netconn *conn, u8_t how)
+{
+  API_MSG_VAR_DECLARE(msg);
+  err_t err;
+  LWIP_UNUSED_ARG(how);
+
+  LWIP_ERROR("netconn_close: invalid conn",  (conn != NULL), return ERR_ARG;);
+
+  API_MSG_VAR_ALLOC(msg);
+  API_MSG_VAR_REF(msg).conn = conn;
+#if LWIP_TCP
+  /* shutting down both ends is the same as closing */
+  API_MSG_VAR_REF(msg).msg.sd.shut = how;
+#if LWIP_SO_SNDTIMEO || LWIP_SO_LINGER
+  /* get the time we started, which is later compared to
+     sys_now() + conn->send_timeout */
+  API_MSG_VAR_REF(msg).msg.sd.time_started = sys_now();
+#else /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
+  API_MSG_VAR_REF(msg).msg.sd.polls_left =
+    ((LWIP_TCP_CLOSE_TIMEOUT_MS_DEFAULT + TCP_SLOW_INTERVAL - 1) / TCP_SLOW_INTERVAL) + 1;
+#endif /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
+#endif /* LWIP_TCP */
+  err = netconn_apimsg(lwip_netconn_do_close, &API_MSG_VAR_REF(msg));
+  API_MSG_VAR_FREE(msg);
+
+  return err;
+}
+
+/**
+ * @ingroup netconn_tcp
+ * Close a TCP netconn (doesn't delete it).
+ *
+ * @param conn the TCP netconn to close
+ * @return ERR_OK if the netconn was closed, any other err_t on error
+ */
+err_t
+netconn_close(struct netconn *conn)
+{
+  /* shutting down both ends is the same as closing */
+  return netconn_close_shutdown(conn, NETCONN_SHUT_RDWR);
+}
+
+/**
+ * @ingroup netconn_common
+ * Get and reset pending error on a netconn
+ *
+ * @param conn the netconn to get the error from
+ * @return and pending error or ERR_OK if no error was pending
+ */
+err_t
+netconn_err(struct netconn *conn)
+{
+  err_t err;
+  SYS_ARCH_DECL_PROTECT(lev);
+  if (conn == NULL) {
+    return ERR_OK;
+  }
+  SYS_ARCH_PROTECT(lev);
+  err = conn->pending_err;
+  conn->pending_err = ERR_OK;
+  SYS_ARCH_UNPROTECT(lev);
+  return err;
+}
+
+/**
+ * @ingroup netconn_tcp
+ * Shut down one or both sides of a TCP netconn (doesn't delete it).
+ *
+ * @param conn the TCP netconn to shut down
+ * @param shut_rx shut down the RX side (no more read possible after this)
+ * @param shut_tx shut down the TX side (no more write possible after this)
+ * @return ERR_OK if the netconn was closed, any other err_t on error
+ */
+err_t
+netconn_shutdown(struct netconn *conn, u8_t shut_rx, u8_t shut_tx)
+{
+  return netconn_close_shutdown(conn, (u8_t)((shut_rx ? NETCONN_SHUT_RD : 0) | (shut_tx ? NETCONN_SHUT_WR : 0)));
+}
+
+#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD)
+/**
+ * @ingroup netconn_udp
+ * Join multicast groups for UDP netconns.
+ *
+ * @param conn the UDP netconn for which to change multicast addresses
+ * @param multiaddr IP address of the multicast group to join or leave
+ * @param netif_addr the IP address of the network interface on which to send
+ *                  the igmp message
+ * @param join_or_leave flag whether to send a join- or leave-message
+ * @return ERR_OK if the action was taken, any err_t on error
+ */
+err_t
+netconn_join_leave_group(struct netconn *conn,
+                         const ip_addr_t *multiaddr,
+                         const ip_addr_t *netif_addr,
+                         enum netconn_igmp join_or_leave)
+{
+  API_MSG_VAR_DECLARE(msg);
+  err_t err;
+
+  LWIP_ERROR("netconn_join_leave_group: invalid conn",  (conn != NULL), return ERR_ARG;);
+
+  API_MSG_VAR_ALLOC(msg);
+
+#if LWIP_IPV4
+  /* Don't propagate NULL pointer (IP_ADDR_ANY alias) to subsequent functions */
+  if (multiaddr == NULL) {
+    multiaddr = IP4_ADDR_ANY;
+  }
+  if (netif_addr == NULL) {
+    netif_addr = IP4_ADDR_ANY;
+  }
+#endif /* LWIP_IPV4 */
+
+  API_MSG_VAR_REF(msg).conn = conn;
+  API_MSG_VAR_REF(msg).msg.jl.multiaddr = API_MSG_VAR_REF(multiaddr);
+  API_MSG_VAR_REF(msg).msg.jl.netif_addr = API_MSG_VAR_REF(netif_addr);
+  API_MSG_VAR_REF(msg).msg.jl.join_or_leave = join_or_leave;
+  err = netconn_apimsg(lwip_netconn_do_join_leave_group, &API_MSG_VAR_REF(msg));
+  API_MSG_VAR_FREE(msg);
+
+  return err;
+}
+/**
+ * @ingroup netconn_udp
+ * Join multicast groups for UDP netconns.
+ *
+ * @param conn the UDP netconn for which to change multicast addresses
+ * @param multiaddr IP address of the multicast group to join or leave
+ * @param if_idx the index of the netif
+ * @param join_or_leave flag whether to send a join- or leave-message
+ * @return ERR_OK if the action was taken, any err_t on error
+ */
+err_t
+netconn_join_leave_group_netif(struct netconn *conn,
+                               const ip_addr_t *multiaddr,
+                               u8_t if_idx,
+                               enum netconn_igmp join_or_leave)
+{
+  API_MSG_VAR_DECLARE(msg);
+  err_t err;
+
+  LWIP_ERROR("netconn_join_leave_group: invalid conn",  (conn != NULL), return ERR_ARG;);
+
+  API_MSG_VAR_ALLOC(msg);
+
+#if LWIP_IPV4
+  /* Don't propagate NULL pointer (IP_ADDR_ANY alias) to subsequent functions */
+  if (multiaddr == NULL) {
+    multiaddr = IP4_ADDR_ANY;
+  }
+  if (if_idx == NETIF_NO_INDEX) {
+    return ERR_IF;
+  }
+#endif /* LWIP_IPV4 */
+
+  API_MSG_VAR_REF(msg).conn = conn;
+  API_MSG_VAR_REF(msg).msg.jl.multiaddr = API_MSG_VAR_REF(multiaddr);
+  API_MSG_VAR_REF(msg).msg.jl.if_idx = if_idx;
+  API_MSG_VAR_REF(msg).msg.jl.join_or_leave = join_or_leave;
+  err = netconn_apimsg(lwip_netconn_do_join_leave_group_netif, &API_MSG_VAR_REF(msg));
+  API_MSG_VAR_FREE(msg);
+
+  return err;
+}
+#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */
+
+#if LWIP_DNS
+/**
+ * @ingroup netconn_common
+ * Execute a DNS query, only one IP address is returned
+ *
+ * @param name a string representation of the DNS host name to query
+ * @param addr a preallocated ip_addr_t where to store the resolved IP address
+ * @return ERR_OK: resolving succeeded
+ *         ERR_MEM: memory error, try again later
+ *         ERR_ARG: dns client not initialized or invalid hostname
+ *         ERR_VAL: dns server response was invalid
+ */
+#if LWIP_IPV4 && LWIP_IPV6
+/** @param dns_addrtype IP address type (IPv4 / IPv6) */
+err_t
+netconn_gethostbyname_addrtype(const char *name, ip_addr_t *addr, u8_t dns_addrtype)
+#else
+err_t
+netconn_gethostbyname(const char *name, ip_addr_t *addr)
+#endif
+{
+  API_VAR_DECLARE(struct dns_api_msg, msg);
+#if !LWIP_MPU_COMPATIBLE
+  sys_sem_t sem;
+#endif /* LWIP_MPU_COMPATIBLE */
+  err_t err;
+  err_t cberr;
+
+  LWIP_ERROR("netconn_gethostbyname: invalid name", (name != NULL), return ERR_ARG;);
+  LWIP_ERROR("netconn_gethostbyname: invalid addr", (addr != NULL), return ERR_ARG;);
+#if LWIP_MPU_COMPATIBLE
+  if (strlen(name) >= DNS_MAX_NAME_LENGTH) {
+    return ERR_ARG;
+  }
+#endif
+
+#ifdef LWIP_HOOK_NETCONN_EXTERNAL_RESOLVE
+#if LWIP_IPV4 && LWIP_IPV6
+  if (LWIP_HOOK_NETCONN_EXTERNAL_RESOLVE(name, addr, dns_addrtype, &err)) {
+#else
+  if (LWIP_HOOK_NETCONN_EXTERNAL_RESOLVE(name, addr, NETCONN_DNS_DEFAULT, &err)) {
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+    return err;
+  }
+#endif /* LWIP_HOOK_NETCONN_EXTERNAL_RESOLVE */
+
+  API_VAR_ALLOC(struct dns_api_msg, MEMP_DNS_API_MSG, msg, ERR_MEM);
+#if LWIP_MPU_COMPATIBLE
+  strncpy(API_VAR_REF(msg).name, name, DNS_MAX_NAME_LENGTH - 1);
+  API_VAR_REF(msg).name[DNS_MAX_NAME_LENGTH - 1] = 0;
+#else /* LWIP_MPU_COMPATIBLE */
+  msg.err = &err;
+  msg.sem = &sem;
+  API_VAR_REF(msg).addr = API_VAR_REF(addr);
+  API_VAR_REF(msg).name = name;
+#endif /* LWIP_MPU_COMPATIBLE */
+#if LWIP_IPV4 && LWIP_IPV6
+  API_VAR_REF(msg).dns_addrtype = dns_addrtype;
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+#if LWIP_NETCONN_SEM_PER_THREAD
+  API_VAR_REF(msg).sem = LWIP_NETCONN_THREAD_SEM_GET();
+#else /* LWIP_NETCONN_SEM_PER_THREAD*/
+  err = sys_sem_new(API_EXPR_REF(API_VAR_REF(msg).sem), 0);
+  if (err != ERR_OK) {
+    API_VAR_FREE(MEMP_DNS_API_MSG, msg);
+    return err;
+  }
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
+
+  cberr = tcpip_send_msg_wait_sem(lwip_netconn_do_gethostbyname, &API_VAR_REF(msg), API_EXPR_REF(API_VAR_REF(msg).sem));
+#if !LWIP_NETCONN_SEM_PER_THREAD
+  sys_sem_free(API_EXPR_REF(API_VAR_REF(msg).sem));
+#endif /* !LWIP_NETCONN_SEM_PER_THREAD */
+  if (cberr != ERR_OK) {
+    API_VAR_FREE(MEMP_DNS_API_MSG, msg);
+    return cberr;
+  }
+
+#if LWIP_MPU_COMPATIBLE
+  *addr = msg->addr;
+  err = msg->err;
+#endif /* LWIP_MPU_COMPATIBLE */
+
+  API_VAR_FREE(MEMP_DNS_API_MSG, msg);
+  return err;
+}
+#endif /* LWIP_DNS*/
+
+#if LWIP_NETCONN_SEM_PER_THREAD
+void
+netconn_thread_init(void)
+{
+  sys_sem_t *sem = LWIP_NETCONN_THREAD_SEM_GET();
+  if (!sys_sem_valid(sem)) {
+    /* call alloc only once */
+    LWIP_NETCONN_THREAD_SEM_ALLOC();
+    LWIP_ASSERT("LWIP_NETCONN_THREAD_SEM_ALLOC() failed", sys_sem_valid(LWIP_NETCONN_THREAD_SEM_GET()));
+  }
+}
+
+void
+netconn_thread_cleanup(void)
+{
+  sys_sem_t *sem = LWIP_NETCONN_THREAD_SEM_GET();
+  if (sys_sem_valid(sem)) {
+    /* call free only once */
+    LWIP_NETCONN_THREAD_SEM_FREE();
+  }
+}
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
+
+#endif /* LWIP_NETCONN */
diff --git a/lib/lwip/api/api_msg.c b/lib/lwip/api/api_msg.c
new file mode 100644
index 0000000000..8092be96c2
--- /dev/null
+++ b/lib/lwip/api/api_msg.c
@@ -0,0 +1,2177 @@
+/**
+ * @file
+ * Sequential API Internal module
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/priv/api_msg.h"
+
+#include "lwip/ip.h"
+#include "lwip/ip_addr.h"
+#include "lwip/udp.h"
+#include "lwip/tcp.h"
+#include "lwip/raw.h"
+
+#include "lwip/memp.h"
+#include "lwip/igmp.h"
+#include "lwip/dns.h"
+#include "lwip/mld6.h"
+#include "lwip/priv/tcpip_priv.h"
+
+#include <string.h>
+
+/* netconns are polled once per second (e.g. continue write on memory error) */
+#define NETCONN_TCP_POLL_INTERVAL 2
+
+#define SET_NONBLOCKING_CONNECT(conn, val)  do { if (val) { \
+  netconn_set_flags(conn, NETCONN_FLAG_IN_NONBLOCKING_CONNECT); \
+} else { \
+  netconn_clear_flags(conn, NETCONN_FLAG_IN_NONBLOCKING_CONNECT); }} while(0)
+#define IN_NONBLOCKING_CONNECT(conn) netconn_is_flag_set(conn, NETCONN_FLAG_IN_NONBLOCKING_CONNECT)
+
+#if LWIP_NETCONN_FULLDUPLEX
+#define NETCONN_MBOX_VALID(conn, mbox) (sys_mbox_valid(mbox) && ((conn->flags & NETCONN_FLAG_MBOXINVALID) == 0))
+#else
+#define NETCONN_MBOX_VALID(conn, mbox) sys_mbox_valid(mbox)
+#endif
+
+/* forward declarations */
+#if LWIP_TCP
+#if LWIP_TCPIP_CORE_LOCKING
+#define WRITE_DELAYED         , 1
+#define WRITE_DELAYED_PARAM   , u8_t delayed
+#else /* LWIP_TCPIP_CORE_LOCKING */
+#define WRITE_DELAYED
+#define WRITE_DELAYED_PARAM
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+static err_t lwip_netconn_do_writemore(struct netconn *conn  WRITE_DELAYED_PARAM);
+static err_t lwip_netconn_do_close_internal(struct netconn *conn  WRITE_DELAYED_PARAM);
+#endif
+
+static void netconn_drain(struct netconn *conn);
+
+#if LWIP_TCPIP_CORE_LOCKING
+#define TCPIP_APIMSG_ACK(m)
+#else /* LWIP_TCPIP_CORE_LOCKING */
+#define TCPIP_APIMSG_ACK(m)   do { sys_sem_signal(LWIP_API_MSG_SEM(m)); } while(0)
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+
+#if LWIP_NETCONN_FULLDUPLEX
+static const u8_t netconn_deleted = 0;
+
+int
+lwip_netconn_is_deallocated_msg(void *msg)
+{
+  if (msg == &netconn_deleted) {
+    return 1;
+  }
+  return 0;
+}
+#endif /* LWIP_NETCONN_FULLDUPLEX */
+
+#if LWIP_TCP
+static const u8_t netconn_aborted = 0;
+static const u8_t netconn_reset = 0;
+static const u8_t netconn_closed = 0;
+
+/** Translate an error to a unique void* passed via an mbox */
+static void *
+lwip_netconn_err_to_msg(err_t err)
+{
+  switch (err) {
+    case ERR_ABRT:
+      return LWIP_CONST_CAST(void *, &netconn_aborted);
+    case ERR_RST:
+      return LWIP_CONST_CAST(void *, &netconn_reset);
+    case ERR_CLSD:
+      return LWIP_CONST_CAST(void *, &netconn_closed);
+    default:
+      LWIP_ASSERT("unhandled error", err == ERR_OK);
+      return NULL;
+  }
+}
+
+int
+lwip_netconn_is_err_msg(void *msg, err_t *err)
+{
+  LWIP_ASSERT("err != NULL", err != NULL);
+
+  if (msg == &netconn_aborted) {
+    *err = ERR_ABRT;
+    return 1;
+  } else if (msg == &netconn_reset) {
+    *err = ERR_RST;
+    return 1;
+  } else if (msg == &netconn_closed) {
+    *err = ERR_CLSD;
+    return 1;
+  }
+  return 0;
+}
+#endif /* LWIP_TCP */
+
+
+#if LWIP_RAW
+/**
+ * Receive callback function for RAW netconns.
+ * Doesn't 'eat' the packet, only copies it and sends it to
+ * conn->recvmbox
+ *
+ * @see raw.h (struct raw_pcb.recv) for parameters and return value
+ */
+static u8_t
+recv_raw(void *arg, struct raw_pcb *pcb, struct pbuf *p,
+         const ip_addr_t *addr)
+{
+  struct pbuf *q;
+  struct netbuf *buf;
+  struct netconn *conn;
+
+  LWIP_UNUSED_ARG(addr);
+  conn = (struct netconn *)arg;
+
+  if ((conn != NULL) && NETCONN_MBOX_VALID(conn, &conn->recvmbox)) {
+#if LWIP_SO_RCVBUF
+    int recv_avail;
+    SYS_ARCH_GET(conn->recv_avail, recv_avail);
+    if ((recv_avail + (int)(p->tot_len)) > conn->recv_bufsize) {
+      return 0;
+    }
+#endif /* LWIP_SO_RCVBUF */
+    /* copy the whole packet into new pbufs */
+    q = pbuf_clone(PBUF_RAW, PBUF_RAM, p);
+    if (q != NULL) {
+      u16_t len;
+      buf = (struct netbuf *)memp_malloc(MEMP_NETBUF);
+      if (buf == NULL) {
+        pbuf_free(q);
+        return 0;
+      }
+
+      buf->p = q;
+      buf->ptr = q;
+      ip_addr_copy(buf->addr, *ip_current_src_addr());
+      buf->port = pcb->protocol;
+
+      len = q->tot_len;
+      if (sys_mbox_trypost(&conn->recvmbox, buf) != ERR_OK) {
+        netbuf_delete(buf);
+        return 0;
+      } else {
+#if LWIP_SO_RCVBUF
+        SYS_ARCH_INC(conn->recv_avail, len);
+#endif /* LWIP_SO_RCVBUF */
+        /* Register event with callback */
+        API_EVENT(conn, NETCONN_EVT_RCVPLUS, len);
+      }
+    }
+  }
+
+  return 0; /* do not eat the packet */
+}
+#endif /* LWIP_RAW*/
+
+#if LWIP_UDP
+/**
+ * Receive callback function for UDP netconns.
+ * Posts the packet to conn->recvmbox or deletes it on memory error.
+ *
+ * @see udp.h (struct udp_pcb.recv) for parameters
+ */
+static void
+recv_udp(void *arg, struct udp_pcb *pcb, struct pbuf *p,
+         const ip_addr_t *addr, u16_t port)
+{
+  struct netbuf *buf;
+  struct netconn *conn;
+  u16_t len;
+  err_t err;
+#if LWIP_SO_RCVBUF
+  int recv_avail;
+#endif /* LWIP_SO_RCVBUF */
+
+  LWIP_UNUSED_ARG(pcb); /* only used for asserts... */
+  LWIP_ASSERT("recv_udp must have a pcb argument", pcb != NULL);
+  LWIP_ASSERT("recv_udp must have an argument", arg != NULL);
+  conn = (struct netconn *)arg;
+
+  if (conn == NULL) {
+    pbuf_free(p);
+    return;
+  }
+
+  LWIP_ASSERT("recv_udp: recv for wrong pcb!", conn->pcb.udp == pcb);
+
+#if LWIP_SO_RCVBUF
+  SYS_ARCH_GET(conn->recv_avail, recv_avail);
+  if (!NETCONN_MBOX_VALID(conn, &conn->recvmbox) ||
+      ((recv_avail + (int)(p->tot_len)) > conn->recv_bufsize)) {
+#else  /* LWIP_SO_RCVBUF */
+  if (!NETCONN_MBOX_VALID(conn, &conn->recvmbox)) {
+#endif /* LWIP_SO_RCVBUF */
+    pbuf_free(p);
+    return;
+  }
+
+  buf = (struct netbuf *)memp_malloc(MEMP_NETBUF);
+  if (buf == NULL) {
+    pbuf_free(p);
+    return;
+  } else {
+    buf->p = p;
+    buf->ptr = p;
+    ip_addr_set(&buf->addr, addr);
+    buf->port = port;
+#if LWIP_NETBUF_RECVINFO
+    if (conn->flags & NETCONN_FLAG_PKTINFO) {
+      /* get the UDP header - always in the first pbuf, ensured by udp_input */
+      const struct udp_hdr *udphdr = (const struct udp_hdr *)ip_next_header_ptr();
+      buf->flags = NETBUF_FLAG_DESTADDR;
+      ip_addr_set(&buf->toaddr, ip_current_dest_addr());
+      buf->toport_chksum = udphdr->dest;
+    }
+#endif /* LWIP_NETBUF_RECVINFO */
+  }
+
+  len = p->tot_len;
+  err = sys_mbox_trypost(&conn->recvmbox, buf);
+  if (err != ERR_OK) {
+    netbuf_delete(buf);
+    LWIP_DEBUGF(API_MSG_DEBUG, ("recv_udp: sys_mbox_trypost failed, err=%d\n", err));
+    return;
+  } else {
+#if LWIP_SO_RCVBUF
+    SYS_ARCH_INC(conn->recv_avail, len);
+#endif /* LWIP_SO_RCVBUF */
+    /* Register event with callback */
+    API_EVENT(conn, NETCONN_EVT_RCVPLUS, len);
+  }
+}
+#endif /* LWIP_UDP */
+
+#if LWIP_TCP
+/**
+ * Receive callback function for TCP netconns.
+ * Posts the packet to conn->recvmbox, but doesn't delete it on errors.
+ *
+ * @see tcp.h (struct tcp_pcb.recv) for parameters and return value
+ */
+static err_t
+recv_tcp(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
+{
+  struct netconn *conn;
+  u16_t len;
+  void *msg;
+
+  LWIP_UNUSED_ARG(pcb);
+  LWIP_ASSERT("recv_tcp must have a pcb argument", pcb != NULL);
+  LWIP_ASSERT("recv_tcp must have an argument", arg != NULL);
+  LWIP_ASSERT("err != ERR_OK unhandled", err == ERR_OK);
+  LWIP_UNUSED_ARG(err); /* for LWIP_NOASSERT */
+  conn = (struct netconn *)arg;
+
+  if (conn == NULL) {
+    return ERR_VAL;
+  }
+  LWIP_ASSERT("recv_tcp: recv for wrong pcb!", conn->pcb.tcp == pcb);
+
+  if (!NETCONN_MBOX_VALID(conn, &conn->recvmbox)) {
+    /* recvmbox already deleted */
+    if (p != NULL) {
+      tcp_recved(pcb, p->tot_len);
+      pbuf_free(p);
+    }
+    return ERR_OK;
+  }
+  /* Unlike for UDP or RAW pcbs, don't check for available space
+     using recv_avail since that could break the connection
+     (data is already ACKed) */
+
+  if (p != NULL) {
+    msg = p;
+    len = p->tot_len;
+  } else {
+    msg = LWIP_CONST_CAST(void *, &netconn_closed);
+    len = 0;
+  }
+
+  if (sys_mbox_trypost(&conn->recvmbox, msg) != ERR_OK) {
+    /* don't deallocate p: it is presented to us later again from tcp_fasttmr! */
+    return ERR_MEM;
+  } else {
+#if LWIP_SO_RCVBUF
+    SYS_ARCH_INC(conn->recv_avail, len);
+#endif /* LWIP_SO_RCVBUF */
+    /* Register event with callback */
+    API_EVENT(conn, NETCONN_EVT_RCVPLUS, len);
+  }
+
+  return ERR_OK;
+}
+
+/**
+ * Poll callback function for TCP netconns.
+ * Wakes up an application thread that waits for a connection to close
+ * or data to be sent. The application thread then takes the
+ * appropriate action to go on.
+ *
+ * Signals the conn->sem.
+ * netconn_close waits for conn->sem if closing failed.
+ *
+ * @see tcp.h (struct tcp_pcb.poll) for parameters and return value
+ */
+static err_t
+poll_tcp(void *arg, struct tcp_pcb *pcb)
+{
+  struct netconn *conn = (struct netconn *)arg;
+
+  LWIP_UNUSED_ARG(pcb);
+  LWIP_ASSERT("conn != NULL", (conn != NULL));
+
+  if (conn->state == NETCONN_WRITE) {
+    lwip_netconn_do_writemore(conn  WRITE_DELAYED);
+  } else if (conn->state == NETCONN_CLOSE) {
+#if !LWIP_SO_SNDTIMEO && !LWIP_SO_LINGER
+    if (conn->current_msg && conn->current_msg->msg.sd.polls_left) {
+      conn->current_msg->msg.sd.polls_left--;
+    }
+#endif /* !LWIP_SO_SNDTIMEO && !LWIP_SO_LINGER */
+    lwip_netconn_do_close_internal(conn  WRITE_DELAYED);
+  }
+  /* @todo: implement connect timeout here? */
+
+  /* Did a nonblocking write fail before? Then check available write-space. */
+  if (conn->flags & NETCONN_FLAG_CHECK_WRITESPACE) {
+    /* If the queued byte- or pbuf-count drops below the configured low-water limit,
+       let select mark this pcb as writable again. */
+    if ((conn->pcb.tcp != NULL) && (tcp_sndbuf(conn->pcb.tcp) > TCP_SNDLOWAT) &&
+        (tcp_sndqueuelen(conn->pcb.tcp) < TCP_SNDQUEUELOWAT)) {
+      netconn_clear_flags(conn, NETCONN_FLAG_CHECK_WRITESPACE);
+      API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0);
+    }
+  }
+
+  return ERR_OK;
+}
+
+/**
+ * Sent callback function for TCP netconns.
+ * Signals the conn->sem and calls API_EVENT.
+ * netconn_write waits for conn->sem if send buffer is low.
+ *
+ * @see tcp.h (struct tcp_pcb.sent) for parameters and return value
+ */
+static err_t
+sent_tcp(void *arg, struct tcp_pcb *pcb, u16_t len)
+{
+  struct netconn *conn = (struct netconn *)arg;
+
+  LWIP_UNUSED_ARG(pcb);
+  LWIP_ASSERT("conn != NULL", (conn != NULL));
+
+  if (conn) {
+    if (conn->state == NETCONN_WRITE) {
+      lwip_netconn_do_writemore(conn  WRITE_DELAYED);
+    } else if (conn->state == NETCONN_CLOSE) {
+      lwip_netconn_do_close_internal(conn  WRITE_DELAYED);
+    }
+
+    /* If the queued byte- or pbuf-count drops below the configured low-water limit,
+       let select mark this pcb as writable again. */
+    if ((conn->pcb.tcp != NULL) && (tcp_sndbuf(conn->pcb.tcp) > TCP_SNDLOWAT) &&
+        (tcp_sndqueuelen(conn->pcb.tcp) < TCP_SNDQUEUELOWAT)) {
+      netconn_clear_flags(conn, NETCONN_FLAG_CHECK_WRITESPACE);
+      API_EVENT(conn, NETCONN_EVT_SENDPLUS, len);
+    }
+  }
+
+  return ERR_OK;
+}
+
+/**
+ * Error callback function for TCP netconns.
+ * Signals conn->sem, posts to all conn mboxes and calls API_EVENT.
+ * The application thread has then to decide what to do.
+ *
+ * @see tcp.h (struct tcp_pcb.err) for parameters
+ */
+static void
+err_tcp(void *arg, err_t err)
+{
+  struct netconn *conn;
+  enum netconn_state old_state;
+  void *mbox_msg;
+  SYS_ARCH_DECL_PROTECT(lev);
+
+  conn = (struct netconn *)arg;
+  LWIP_ASSERT("conn != NULL", (conn != NULL));
+
+  SYS_ARCH_PROTECT(lev);
+
+  /* when err is called, the pcb is deallocated, so delete the reference */
+  conn->pcb.tcp = NULL;
+  /* store pending error */
+  conn->pending_err = err;
+  /* prevent application threads from blocking on 'recvmbox'/'acceptmbox' */
+  conn->flags |= NETCONN_FLAG_MBOXCLOSED;
+
+  /* reset conn->state now before waking up other threads */
+  old_state = conn->state;
+  conn->state = NETCONN_NONE;
+
+  SYS_ARCH_UNPROTECT(lev);
+
+  /* Notify the user layer about a connection error. Used to signal select. */
+  API_EVENT(conn, NETCONN_EVT_ERROR, 0);
+  /* Try to release selects pending on 'read' or 'write', too.
+     They will get an error if they actually try to read or write. */
+  API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
+  API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0);
+
+  mbox_msg = lwip_netconn_err_to_msg(err);
+  /* pass error message to recvmbox to wake up pending recv */
+  if (NETCONN_MBOX_VALID(conn, &conn->recvmbox)) {
+    /* use trypost to prevent deadlock */
+    sys_mbox_trypost(&conn->recvmbox, mbox_msg);
+  }
+  /* pass error message to acceptmbox to wake up pending accept */
+  if (NETCONN_MBOX_VALID(conn, &conn->acceptmbox)) {
+    /* use trypost to prevent deadlock */
+    sys_mbox_trypost(&conn->acceptmbox, mbox_msg);
+  }
+
+  if ((old_state == NETCONN_WRITE) || (old_state == NETCONN_CLOSE) ||
+      (old_state == NETCONN_CONNECT)) {
+    /* calling lwip_netconn_do_writemore/lwip_netconn_do_close_internal is not necessary
+       since the pcb has already been deleted! */
+    int was_nonblocking_connect = IN_NONBLOCKING_CONNECT(conn);
+    SET_NONBLOCKING_CONNECT(conn, 0);
+
+    if (!was_nonblocking_connect) {
+      sys_sem_t *op_completed_sem;
+      /* set error return code */
+      LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL);
+      if (old_state == NETCONN_CLOSE) {
+        /* let close succeed: the connection is closed after all... */
+        conn->current_msg->err = ERR_OK;
+      } else {
+        /* Write and connect fail */
+        conn->current_msg->err = err;
+      }
+      op_completed_sem = LWIP_API_MSG_SEM(conn->current_msg);
+      LWIP_ASSERT("invalid op_completed_sem", sys_sem_valid(op_completed_sem));
+      conn->current_msg = NULL;
+      /* wake up the waiting task */
+      sys_sem_signal(op_completed_sem);
+    } else {
+      /* @todo: test what happens for error on nonblocking connect */
+    }
+  } else {
+    LWIP_ASSERT("conn->current_msg == NULL", conn->current_msg == NULL);
+  }
+}
+
+/**
+ * Setup a tcp_pcb with the correct callback function pointers
+ * and their arguments.
+ *
+ * @param conn the TCP netconn to setup
+ */
+static void
+setup_tcp(struct netconn *conn)
+{
+  struct tcp_pcb *pcb;
+
+  pcb = conn->pcb.tcp;
+  tcp_arg(pcb, conn);
+  tcp_recv(pcb, recv_tcp);
+  tcp_sent(pcb, sent_tcp);
+  tcp_poll(pcb, poll_tcp, NETCONN_TCP_POLL_INTERVAL);
+  tcp_err(pcb, err_tcp);
+}
+
+/**
+ * Accept callback function for TCP netconns.
+ * Allocates a new netconn and posts that to conn->acceptmbox.
+ *
+ * @see tcp.h (struct tcp_pcb_listen.accept) for parameters and return value
+ */
+static err_t
+accept_function(void *arg, struct tcp_pcb *newpcb, err_t err)
+{
+  struct netconn *newconn;
+  struct netconn *conn = (struct netconn *)arg;
+
+  if (conn == NULL) {
+    return ERR_VAL;
+  }
+  if (!NETCONN_MBOX_VALID(conn, &conn->acceptmbox)) {
+    LWIP_DEBUGF(API_MSG_DEBUG, ("accept_function: acceptmbox already deleted\n"));
+    return ERR_VAL;
+  }
+
+  if (newpcb == NULL) {
+    /* out-of-pcbs during connect: pass on this error to the application */
+    if (sys_mbox_trypost(&conn->acceptmbox, lwip_netconn_err_to_msg(ERR_ABRT)) == ERR_OK) {
+      /* Register event with callback */
+      API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
+    }
+    return ERR_VAL;
+  }
+  LWIP_ASSERT("expect newpcb == NULL or err == ERR_OK", err == ERR_OK);
+  LWIP_UNUSED_ARG(err); /* for LWIP_NOASSERT */
+
+  LWIP_DEBUGF(API_MSG_DEBUG, ("accept_function: newpcb->state: %s\n", tcp_debug_state_str(newpcb->state)));
+
+  /* We have to set the callback here even though
+   * the new socket is unknown. newconn->socket is marked as -1. */
+  newconn = netconn_alloc(conn->type, conn->callback);
+  if (newconn == NULL) {
+    /* outof netconns: pass on this error to the application */
+    if (sys_mbox_trypost(&conn->acceptmbox, lwip_netconn_err_to_msg(ERR_ABRT)) == ERR_OK) {
+      /* Register event with callback */
+      API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
+    }
+    return ERR_MEM;
+  }
+  newconn->pcb.tcp = newpcb;
+  setup_tcp(newconn);
+
+  /* handle backlog counter */
+  tcp_backlog_delayed(newpcb);
+
+  if (sys_mbox_trypost(&conn->acceptmbox, newconn) != ERR_OK) {
+    /* When returning != ERR_OK, the pcb is aborted in tcp_process(),
+       so do nothing here! */
+    /* remove all references to this netconn from the pcb */
+    struct tcp_pcb *pcb = newconn->pcb.tcp;
+    tcp_arg(pcb, NULL);
+    tcp_recv(pcb, NULL);
+    tcp_sent(pcb, NULL);
+    tcp_poll(pcb, NULL, 0);
+    tcp_err(pcb, NULL);
+    /* remove reference from to the pcb from this netconn */
+    newconn->pcb.tcp = NULL;
+    /* no need to drain since we know the recvmbox is empty. */
+    sys_mbox_free(&newconn->recvmbox);
+    sys_mbox_set_invalid(&newconn->recvmbox);
+    netconn_free(newconn);
+    return ERR_MEM;
+  } else {
+    /* Register event with callback */
+    API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
+  }
+
+  return ERR_OK;
+}
+#endif /* LWIP_TCP */
+
+/**
+ * Create a new pcb of a specific type.
+ * Called from lwip_netconn_do_newconn().
+ *
+ * @param msg the api_msg describing the connection type
+ */
+static void
+pcb_new(struct api_msg *msg)
+{
+  enum lwip_ip_addr_type iptype = IPADDR_TYPE_V4;
+
+  LWIP_ASSERT("pcb_new: pcb already allocated", msg->conn->pcb.tcp == NULL);
+
+#if LWIP_IPV6 && LWIP_IPV4
+  /* IPv6: Dual-stack by default, unless netconn_set_ipv6only() is called */
+  if (NETCONNTYPE_ISIPV6(netconn_type(msg->conn))) {
+    iptype = IPADDR_TYPE_ANY;
+  }
+#endif
+
+  /* Allocate a PCB for this connection */
+  switch (NETCONNTYPE_GROUP(msg->conn->type)) {
+#if LWIP_RAW
+    case NETCONN_RAW:
+      msg->conn->pcb.raw = raw_new_ip_type(iptype, msg->msg.n.proto);
+      if (msg->conn->pcb.raw != NULL) {
+#if LWIP_IPV6
+        /* ICMPv6 packets should always have checksum calculated by the stack as per RFC 3542 chapter 3.1 */
+        if (NETCONNTYPE_ISIPV6(msg->conn->type) && msg->conn->pcb.raw->protocol == IP6_NEXTH_ICMP6) {
+          msg->conn->pcb.raw->chksum_reqd = 1;
+          msg->conn->pcb.raw->chksum_offset = 2;
+        }
+#endif /* LWIP_IPV6 */
+        raw_recv(msg->conn->pcb.raw, recv_raw, msg->conn);
+      }
+      break;
+#endif /* LWIP_RAW */
+#if LWIP_UDP
+    case NETCONN_UDP:
+      msg->conn->pcb.udp = udp_new_ip_type(iptype);
+      if (msg->conn->pcb.udp != NULL) {
+#if LWIP_UDPLITE
+        if (NETCONNTYPE_ISUDPLITE(msg->conn->type)) {
+          udp_setflags(msg->conn->pcb.udp, UDP_FLAGS_UDPLITE);
+        }
+#endif /* LWIP_UDPLITE */
+        if (NETCONNTYPE_ISUDPNOCHKSUM(msg->conn->type)) {
+          udp_setflags(msg->conn->pcb.udp, UDP_FLAGS_NOCHKSUM);
+        }
+        udp_recv(msg->conn->pcb.udp, recv_udp, msg->conn);
+      }
+      break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+    case NETCONN_TCP:
+      msg->conn->pcb.tcp = tcp_new_ip_type(iptype);
+      if (msg->conn->pcb.tcp != NULL) {
+        setup_tcp(msg->conn);
+      }
+      break;
+#endif /* LWIP_TCP */
+    default:
+      /* Unsupported netconn type, e.g. protocol disabled */
+      msg->err = ERR_VAL;
+      return;
+  }
+  if (msg->conn->pcb.ip == NULL) {
+    msg->err = ERR_MEM;
+  }
+}
+
+/**
+ * Create a new pcb of a specific type inside a netconn.
+ * Called from netconn_new_with_proto_and_callback.
+ *
+ * @param m the api_msg describing the connection type
+ */
+void
+lwip_netconn_do_newconn(void *m)
+{
+  struct api_msg *msg = (struct api_msg *)m;
+
+  msg->err = ERR_OK;
+  if (msg->conn->pcb.tcp == NULL) {
+    pcb_new(msg);
+  }
+  /* Else? This "new" connection already has a PCB allocated. */
+  /* Is this an error condition? Should it be deleted? */
+  /* We currently just are happy and return. */
+
+  TCPIP_APIMSG_ACK(msg);
+}
+
+/**
+ * Create a new netconn (of a specific type) that has a callback function.
+ * The corresponding pcb is NOT created!
+ *
+ * @param t the type of 'connection' to create (@see enum netconn_type)
+ * @param callback a function to call on status changes (RX available, TX'ed)
+ * @return a newly allocated struct netconn or
+ *         NULL on memory error
+ */
+struct netconn *
+netconn_alloc(enum netconn_type t, netconn_callback callback)
+{
+  struct netconn *conn;
+  int size;
+  u8_t init_flags = 0;
+
+  conn = (struct netconn *)memp_malloc(MEMP_NETCONN);
+  if (conn == NULL) {
+    return NULL;
+  }
+
+  conn->pending_err = ERR_OK;
+  conn->type = t;
+  conn->pcb.tcp = NULL;
+#if LWIP_NETCONN_FULLDUPLEX
+  conn->mbox_threads_waiting = 0;
+#endif
+
+  /* If all sizes are the same, every compiler should optimize this switch to nothing */
+  switch (NETCONNTYPE_GROUP(t)) {
+#if LWIP_RAW
+    case NETCONN_RAW:
+      size = DEFAULT_RAW_RECVMBOX_SIZE;
+      break;
+#endif /* LWIP_RAW */
+#if LWIP_UDP
+    case NETCONN_UDP:
+      size = DEFAULT_UDP_RECVMBOX_SIZE;
+#if LWIP_NETBUF_RECVINFO
+      init_flags |= NETCONN_FLAG_PKTINFO;
+#endif /* LWIP_NETBUF_RECVINFO */
+      break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+    case NETCONN_TCP:
+      size = DEFAULT_TCP_RECVMBOX_SIZE;
+      break;
+#endif /* LWIP_TCP */
+    default:
+      LWIP_ASSERT("netconn_alloc: undefined netconn_type", 0);
+      goto free_and_return;
+  }
+
+  if (sys_mbox_new(&conn->recvmbox, size) != ERR_OK) {
+    goto free_and_return;
+  }
+#if !LWIP_NETCONN_SEM_PER_THREAD
+  if (sys_sem_new(&conn->op_completed, 0) != ERR_OK) {
+    sys_mbox_free(&conn->recvmbox);
+    goto free_and_return;
+  }
+#endif
+
+#if LWIP_TCP
+  sys_mbox_set_invalid(&conn->acceptmbox);
+#endif
+  conn->state        = NETCONN_NONE;
+  /* initialize socket to -1 since 0 is a valid socket */
+  conn->callback_arg.socket = -1;
+  conn->callback     = callback;
+#if LWIP_TCP
+  conn->current_msg  = NULL;
+#endif /* LWIP_TCP */
+#if LWIP_SO_SNDTIMEO
+  conn->send_timeout = 0;
+#endif /* LWIP_SO_SNDTIMEO */
+#if LWIP_SO_RCVTIMEO
+  conn->recv_timeout = 0;
+#endif /* LWIP_SO_RCVTIMEO */
+#if LWIP_SO_RCVBUF
+  conn->recv_bufsize = RECV_BUFSIZE_DEFAULT;
+  conn->recv_avail   = 0;
+#endif /* LWIP_SO_RCVBUF */
+#if LWIP_SO_LINGER
+  conn->linger = -1;
+#endif /* LWIP_SO_LINGER */
+  conn->flags = init_flags;
+  return conn;
+free_and_return:
+  memp_free(MEMP_NETCONN, conn);
+  return NULL;
+}
+
+/**
+ * Delete a netconn and all its resources.
+ * The pcb is NOT freed (since we might not be in the right thread context do this).
+ *
+ * @param conn the netconn to free
+ */
+void
+netconn_free(struct netconn *conn)
+{
+  LWIP_ASSERT("PCB must be deallocated outside this function", conn->pcb.tcp == NULL);
+
+#if LWIP_NETCONN_FULLDUPLEX
+  /* in fullduplex, netconn is drained here */
+  netconn_drain(conn);
+#endif /* LWIP_NETCONN_FULLDUPLEX */
+
+  LWIP_ASSERT("recvmbox must be deallocated before calling this function",
+              !sys_mbox_valid(&conn->recvmbox));
+#if LWIP_TCP
+  LWIP_ASSERT("acceptmbox must be deallocated before calling this function",
+              !sys_mbox_valid(&conn->acceptmbox));
+#endif /* LWIP_TCP */
+
+#if !LWIP_NETCONN_SEM_PER_THREAD
+  sys_sem_free(&conn->op_completed);
+  sys_sem_set_invalid(&conn->op_completed);
+#endif
+
+  memp_free(MEMP_NETCONN, conn);
+}
+
+/**
+ * Delete rcvmbox and acceptmbox of a netconn and free the left-over data in
+ * these mboxes
+ *
+ * @param conn the netconn to free
+ * @bytes_drained bytes drained from recvmbox
+ * @accepts_drained pending connections drained from acceptmbox
+ */
+static void
+netconn_drain(struct netconn *conn)
+{
+  void *mem;
+
+  /* This runs when mbox and netconn are marked as closed,
+     so we don't need to lock against rx packets */
+#if LWIP_NETCONN_FULLDUPLEX
+  LWIP_ASSERT("netconn marked closed", conn->flags & NETCONN_FLAG_MBOXINVALID);
+#endif /* LWIP_NETCONN_FULLDUPLEX */
+
+  /* Delete and drain the recvmbox. */
+  if (sys_mbox_valid(&conn->recvmbox)) {
+    while (sys_mbox_tryfetch(&conn->recvmbox, &mem) != SYS_MBOX_EMPTY) {
+#if LWIP_NETCONN_FULLDUPLEX
+      if (!lwip_netconn_is_deallocated_msg(mem))
+#endif /* LWIP_NETCONN_FULLDUPLEX */
+      {
+#if LWIP_TCP
+        if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP) {
+          err_t err;
+          if (!lwip_netconn_is_err_msg(mem, &err)) {
+            pbuf_free((struct pbuf *)mem);
+          }
+        } else
+#endif /* LWIP_TCP */
+        {
+          netbuf_delete((struct netbuf *)mem);
+        }
+      }
+    }
+    sys_mbox_free(&conn->recvmbox);
+    sys_mbox_set_invalid(&conn->recvmbox);
+  }
+
+  /* Delete and drain the acceptmbox. */
+#if LWIP_TCP
+  if (sys_mbox_valid(&conn->acceptmbox)) {
+    while (sys_mbox_tryfetch(&conn->acceptmbox, &mem) != SYS_MBOX_EMPTY) {
+#if LWIP_NETCONN_FULLDUPLEX
+      if (!lwip_netconn_is_deallocated_msg(mem))
+#endif /* LWIP_NETCONN_FULLDUPLEX */
+      {
+        err_t err;
+        if (!lwip_netconn_is_err_msg(mem, &err)) {
+          struct netconn *newconn = (struct netconn *)mem;
+          /* Only tcp pcbs have an acceptmbox, so no need to check conn->type */
+          /* pcb might be set to NULL already by err_tcp() */
+          /* drain recvmbox */
+          netconn_drain(newconn);
+          if (newconn->pcb.tcp != NULL) {
+            tcp_abort(newconn->pcb.tcp);
+            newconn->pcb.tcp = NULL;
+          }
+          netconn_free(newconn);
+        }
+      }
+    }
+    sys_mbox_free(&conn->acceptmbox);
+    sys_mbox_set_invalid(&conn->acceptmbox);
+  }
+#endif /* LWIP_TCP */
+}
+
+#if LWIP_NETCONN_FULLDUPLEX
+static void
+netconn_mark_mbox_invalid(struct netconn *conn)
+{
+  int i, num_waiting;
+  void *msg = LWIP_CONST_CAST(void *, &netconn_deleted);
+
+  /* Prevent new calls/threads from reading from the mbox */
+  conn->flags |= NETCONN_FLAG_MBOXINVALID;
+
+  SYS_ARCH_LOCKED(num_waiting = conn->mbox_threads_waiting);
+  for (i = 0; i < num_waiting; i++) {
+    if (sys_mbox_valid_val(conn->recvmbox)) {
+      sys_mbox_trypost(&conn->recvmbox, msg);
+    } else {
+      sys_mbox_trypost(&conn->acceptmbox, msg);
+    }
+  }
+}
+#endif /* LWIP_NETCONN_FULLDUPLEX */
+
+#if LWIP_TCP
+/**
+ * Internal helper function to close a TCP netconn: since this sometimes
+ * doesn't work at the first attempt, this function is called from multiple
+ * places.
+ *
+ * @param conn the TCP netconn to close
+ */
+static err_t
+lwip_netconn_do_close_internal(struct netconn *conn  WRITE_DELAYED_PARAM)
+{
+  err_t err;
+  u8_t shut, shut_rx, shut_tx, shut_close;
+  u8_t close_finished = 0;
+  struct tcp_pcb *tpcb;
+#if LWIP_SO_LINGER
+  u8_t linger_wait_required = 0;
+#endif /* LWIP_SO_LINGER */
+
+  LWIP_ASSERT("invalid conn", (conn != NULL));
+  LWIP_ASSERT("this is for tcp netconns only", (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP));
+  LWIP_ASSERT("conn must be in state NETCONN_CLOSE", (conn->state == NETCONN_CLOSE));
+  LWIP_ASSERT("pcb already closed", (conn->pcb.tcp != NULL));
+  LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL);
+
+  tpcb = conn->pcb.tcp;
+  shut = conn->current_msg->msg.sd.shut;
+  shut_rx = shut & NETCONN_SHUT_RD;
+  shut_tx = shut & NETCONN_SHUT_WR;
+  /* shutting down both ends is the same as closing
+     (also if RD or WR side was shut down before already) */
+  if (shut == NETCONN_SHUT_RDWR) {
+    shut_close = 1;
+  } else if (shut_rx &&
+             ((tpcb->state == FIN_WAIT_1) ||
+              (tpcb->state == FIN_WAIT_2) ||
+              (tpcb->state == CLOSING))) {
+    shut_close = 1;
+  } else if (shut_tx && ((tpcb->flags & TF_RXCLOSED) != 0)) {
+    shut_close = 1;
+  } else {
+    shut_close = 0;
+  }
+
+  /* Set back some callback pointers */
+  if (shut_close) {
+    tcp_arg(tpcb, NULL);
+  }
+  if (tpcb->state == LISTEN) {
+    tcp_accept(tpcb, NULL);
+  } else {
+    /* some callbacks have to be reset if tcp_close is not successful */
+    if (shut_rx) {
+      tcp_recv(tpcb, NULL);
+      tcp_accept(tpcb, NULL);
+    }
+    if (shut_tx) {
+      tcp_sent(tpcb, NULL);
+    }
+    if (shut_close) {
+      tcp_poll(tpcb, NULL, 0);
+      tcp_err(tpcb, NULL);
+    }
+  }
+  /* Try to close the connection */
+  if (shut_close) {
+#if LWIP_SO_LINGER
+    /* check linger possibilities before calling tcp_close */
+    err = ERR_OK;
+    /* linger enabled/required at all? (i.e. is there untransmitted data left?) */
+    if ((conn->linger >= 0) && (conn->pcb.tcp->unsent || conn->pcb.tcp->unacked)) {
+      if ((conn->linger == 0)) {
+        /* data left but linger prevents waiting */
+        tcp_abort(tpcb);
+        tpcb = NULL;
+      } else if (conn->linger > 0) {
+        /* data left and linger says we should wait */
+        if (netconn_is_nonblocking(conn)) {
+          /* data left on a nonblocking netconn -> cannot linger */
+          err = ERR_WOULDBLOCK;
+        } else if ((s32_t)(sys_now() - conn->current_msg->msg.sd.time_started) >=
+                   (conn->linger * 1000)) {
+          /* data left but linger timeout has expired (this happens on further
+             calls to this function through poll_tcp */
+          tcp_abort(tpcb);
+          tpcb = NULL;
+        } else {
+          /* data left -> need to wait for ACK after successful close */
+          linger_wait_required = 1;
+        }
+      }
+    }
+    if ((err == ERR_OK) && (tpcb != NULL))
+#endif /* LWIP_SO_LINGER */
+    {
+      err = tcp_close(tpcb);
+    }
+  } else {
+    err = tcp_shutdown(tpcb, shut_rx, shut_tx);
+  }
+  if (err == ERR_OK) {
+    close_finished = 1;
+#if LWIP_SO_LINGER
+    if (linger_wait_required) {
+      /* wait for ACK of all unsent/unacked data by just getting called again */
+      close_finished = 0;
+      err = ERR_INPROGRESS;
+    }
+#endif /* LWIP_SO_LINGER */
+  } else {
+    if (err == ERR_MEM) {
+      /* Closing failed because of memory shortage, try again later. Even for
+         nonblocking netconns, we have to wait since no standard socket application
+         is prepared for close failing because of resource shortage.
+         Check the timeout: this is kind of an lwip addition to the standard sockets:
+         we wait for some time when failing to allocate a segment for the FIN */
+#if LWIP_SO_SNDTIMEO || LWIP_SO_LINGER
+      s32_t close_timeout = LWIP_TCP_CLOSE_TIMEOUT_MS_DEFAULT;
+#if LWIP_SO_SNDTIMEO
+      if (conn->send_timeout > 0) {
+        close_timeout = conn->send_timeout;
+      }
+#endif /* LWIP_SO_SNDTIMEO */
+#if LWIP_SO_LINGER
+      if (conn->linger >= 0) {
+        /* use linger timeout (seconds) */
+        close_timeout = conn->linger * 1000U;
+      }
+#endif
+      if ((s32_t)(sys_now() - conn->current_msg->msg.sd.time_started) >= close_timeout) {
+#else /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
+      if (conn->current_msg->msg.sd.polls_left == 0) {
+#endif /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
+        close_finished = 1;
+        if (shut_close) {
+          /* in this case, we want to RST the connection */
+          tcp_abort(tpcb);
+          err = ERR_OK;
+        }
+      }
+    } else {
+      /* Closing failed for a non-memory error: give up */
+      close_finished = 1;
+    }
+  }
+  if (close_finished) {
+    /* Closing done (succeeded, non-memory error, nonblocking error or timeout) */
+    sys_sem_t *op_completed_sem = LWIP_API_MSG_SEM(conn->current_msg);
+    conn->current_msg->err = err;
+    conn->current_msg = NULL;
+    conn->state = NETCONN_NONE;
+    if (err == ERR_OK) {
+      if (shut_close) {
+        /* Set back some callback pointers as conn is going away */
+        conn->pcb.tcp = NULL;
+        /* Trigger select() in socket layer. Make sure everybody notices activity
+         on the connection, error first! */
+        API_EVENT(conn, NETCONN_EVT_ERROR, 0);
+      }
+      if (shut_rx) {
+        API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
+      }
+      if (shut_tx) {
+        API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0);
+      }
+    }
+#if LWIP_TCPIP_CORE_LOCKING
+    if (delayed)
+#endif
+    {
+      /* wake up the application task */
+      sys_sem_signal(op_completed_sem);
+    }
+    return ERR_OK;
+  }
+  if (!close_finished) {
+    /* Closing failed and we want to wait: restore some of the callbacks */
+    /* Closing of listen pcb will never fail! */
+    LWIP_ASSERT("Closing a listen pcb may not fail!", (tpcb->state != LISTEN));
+    if (shut_tx) {
+      tcp_sent(tpcb, sent_tcp);
+    }
+    /* when waiting for close, set up poll interval to 500ms */
+    tcp_poll(tpcb, poll_tcp, 1);
+    tcp_err(tpcb, err_tcp);
+    tcp_arg(tpcb, conn);
+    /* don't restore recv callback: we don't want to receive any more data */
+  }
+  /* If closing didn't succeed, we get called again either
+     from poll_tcp or from sent_tcp */
+  LWIP_ASSERT("err != ERR_OK", err != ERR_OK);
+  return err;
+}
+#endif /* LWIP_TCP */
+
+/**
+ * Delete the pcb inside a netconn.
+ * Called from netconn_delete.
+ *
+ * @param m the api_msg pointing to the connection
+ */
+void
+lwip_netconn_do_delconn(void *m)
+{
+  struct api_msg *msg = (struct api_msg *)m;
+
+  enum netconn_state state = msg->conn->state;
+  LWIP_ASSERT("netconn state error", /* this only happens for TCP netconns */
+              (state == NETCONN_NONE) || (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP));
+#if LWIP_NETCONN_FULLDUPLEX
+  /* In full duplex mode, blocking write/connect is aborted with ERR_CLSD */
+  if (state != NETCONN_NONE) {
+    if ((state == NETCONN_WRITE) ||
+        ((state == NETCONN_CONNECT) && !IN_NONBLOCKING_CONNECT(msg->conn))) {
+      /* close requested, abort running write/connect */
+      sys_sem_t *op_completed_sem;
+      LWIP_ASSERT("msg->conn->current_msg != NULL", msg->conn->current_msg != NULL);
+      op_completed_sem = LWIP_API_MSG_SEM(msg->conn->current_msg);
+      msg->conn->current_msg->err = ERR_CLSD;
+      msg->conn->current_msg = NULL;
+      msg->conn->state = NETCONN_NONE;
+      sys_sem_signal(op_completed_sem);
+    }
+  }
+#else /* LWIP_NETCONN_FULLDUPLEX */
+  if (((state != NETCONN_NONE) &&
+       (state != NETCONN_LISTEN) &&
+       (state != NETCONN_CONNECT)) ||
+      ((state == NETCONN_CONNECT) && !IN_NONBLOCKING_CONNECT(msg->conn))) {
+    /* This means either a blocking write or blocking connect is running
+       (nonblocking write returns and sets state to NONE) */
+    msg->err = ERR_INPROGRESS;
+  } else
+#endif /* LWIP_NETCONN_FULLDUPLEX */
+  {
+    LWIP_ASSERT("blocking connect in progress",
+                (state != NETCONN_CONNECT) || IN_NONBLOCKING_CONNECT(msg->conn));
+    msg->err = ERR_OK;
+#if LWIP_NETCONN_FULLDUPLEX
+    /* Mark mboxes invalid */
+    netconn_mark_mbox_invalid(msg->conn);
+#else /* LWIP_NETCONN_FULLDUPLEX */
+    netconn_drain(msg->conn);
+#endif /* LWIP_NETCONN_FULLDUPLEX */
+
+    if (msg->conn->pcb.tcp != NULL) {
+
+      switch (NETCONNTYPE_GROUP(msg->conn->type)) {
+#if LWIP_RAW
+        case NETCONN_RAW:
+          raw_remove(msg->conn->pcb.raw);
+          break;
+#endif /* LWIP_RAW */
+#if LWIP_UDP
+        case NETCONN_UDP:
+          msg->conn->pcb.udp->recv_arg = NULL;
+          udp_remove(msg->conn->pcb.udp);
+          break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+        case NETCONN_TCP:
+          LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL);
+          msg->conn->state = NETCONN_CLOSE;
+          msg->msg.sd.shut = NETCONN_SHUT_RDWR;
+          msg->conn->current_msg = msg;
+#if LWIP_TCPIP_CORE_LOCKING
+          if (lwip_netconn_do_close_internal(msg->conn, 0) != ERR_OK) {
+            LWIP_ASSERT("state!", msg->conn->state == NETCONN_CLOSE);
+            UNLOCK_TCPIP_CORE();
+            sys_arch_sem_wait(LWIP_API_MSG_SEM(msg), 0);
+            LOCK_TCPIP_CORE();
+            LWIP_ASSERT("state!", msg->conn->state == NETCONN_NONE);
+          }
+#else /* LWIP_TCPIP_CORE_LOCKING */
+          lwip_netconn_do_close_internal(msg->conn);
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+          /* API_EVENT is called inside lwip_netconn_do_close_internal, before releasing
+             the application thread, so we can return at this point! */
+          return;
+#endif /* LWIP_TCP */
+        default:
+          break;
+      }
+      msg->conn->pcb.tcp = NULL;
+    }
+    /* tcp netconns don't come here! */
+
+    /* @todo: this lets select make the socket readable and writable,
+       which is wrong! errfd instead? */
+    API_EVENT(msg->conn, NETCONN_EVT_RCVPLUS, 0);
+    API_EVENT(msg->conn, NETCONN_EVT_SENDPLUS, 0);
+  }
+  if (sys_sem_valid(LWIP_API_MSG_SEM(msg))) {
+    TCPIP_APIMSG_ACK(msg);
+  }
+}
+
+/**
+ * Bind a pcb contained in a netconn
+ * Called from netconn_bind.
+ *
+ * @param m the api_msg pointing to the connection and containing
+ *          the IP address and port to bind to
+ */
+void
+lwip_netconn_do_bind(void *m)
+{
+  struct api_msg *msg = (struct api_msg *)m;
+  err_t err;
+
+  if (msg->conn->pcb.tcp != NULL) {
+    switch (NETCONNTYPE_GROUP(msg->conn->type)) {
+#if LWIP_RAW
+      case NETCONN_RAW:
+        err = raw_bind(msg->conn->pcb.raw, API_EXPR_REF(msg->msg.bc.ipaddr));
+        break;
+#endif /* LWIP_RAW */
+#if LWIP_UDP
+      case NETCONN_UDP:
+        err = udp_bind(msg->conn->pcb.udp, API_EXPR_REF(msg->msg.bc.ipaddr), msg->msg.bc.port);
+        break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+      case NETCONN_TCP:
+        err = tcp_bind(msg->conn->pcb.tcp, API_EXPR_REF(msg->msg.bc.ipaddr), msg->msg.bc.port);
+        break;
+#endif /* LWIP_TCP */
+      default:
+        err = ERR_VAL;
+        break;
+    }
+  } else {
+    err = ERR_VAL;
+  }
+  msg->err = err;
+  TCPIP_APIMSG_ACK(msg);
+}
+/**
+ * Bind a pcb contained in a netconn to an interface
+ * Called from netconn_bind_if.
+ *
+ * @param m the api_msg pointing to the connection and containing
+ *          the IP address and port to bind to
+ */
+void
+lwip_netconn_do_bind_if(void *m)
+{
+  struct netif *netif;
+  struct api_msg *msg = (struct api_msg *)m;
+  err_t err;
+
+  netif = netif_get_by_index(msg->msg.bc.if_idx);
+
+  if ((netif != NULL) && (msg->conn->pcb.tcp != NULL)) {
+    err = ERR_OK;
+    switch (NETCONNTYPE_GROUP(msg->conn->type)) {
+#if LWIP_RAW
+      case NETCONN_RAW:
+        raw_bind_netif(msg->conn->pcb.raw, netif);
+        break;
+#endif /* LWIP_RAW */
+#if LWIP_UDP
+      case NETCONN_UDP:
+        udp_bind_netif(msg->conn->pcb.udp, netif);
+        break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+      case NETCONN_TCP:
+        tcp_bind_netif(msg->conn->pcb.tcp, netif);
+        break;
+#endif /* LWIP_TCP */
+      default:
+        err = ERR_VAL;
+        break;
+    }
+  } else {
+    err = ERR_VAL;
+  }
+  msg->err = err;
+  TCPIP_APIMSG_ACK(msg);
+}
+
+#if LWIP_TCP
+/**
+ * TCP callback function if a connection (opened by tcp_connect/lwip_netconn_do_connect) has
+ * been established (or reset by the remote host).
+ *
+ * @see tcp.h (struct tcp_pcb.connected) for parameters and return values
+ */
+static err_t
+lwip_netconn_do_connected(void *arg, struct tcp_pcb *pcb, err_t err)
+{
+  struct netconn *conn;
+  int was_blocking;
+  sys_sem_t *op_completed_sem = NULL;
+
+  LWIP_UNUSED_ARG(pcb);
+
+  conn = (struct netconn *)arg;
+
+  if (conn == NULL) {
+    return ERR_VAL;
+  }
+
+  LWIP_ASSERT("conn->state == NETCONN_CONNECT", conn->state == NETCONN_CONNECT);
+  LWIP_ASSERT("(conn->current_msg != NULL) || conn->in_non_blocking_connect",
+              (conn->current_msg != NULL) || IN_NONBLOCKING_CONNECT(conn));
+
+  if (conn->current_msg != NULL) {
+    conn->current_msg->err = err;
+    op_completed_sem = LWIP_API_MSG_SEM(conn->current_msg);
+  }
+  if ((NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP) && (err == ERR_OK)) {
+    setup_tcp(conn);
+  }
+  was_blocking = !IN_NONBLOCKING_CONNECT(conn);
+  SET_NONBLOCKING_CONNECT(conn, 0);
+  LWIP_ASSERT("blocking connect state error",
+              (was_blocking && op_completed_sem != NULL) ||
+              (!was_blocking && op_completed_sem == NULL));
+  conn->current_msg = NULL;
+  conn->state = NETCONN_NONE;
+  API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0);
+
+  if (was_blocking) {
+    sys_sem_signal(op_completed_sem);
+  }
+  return ERR_OK;
+}
+#endif /* LWIP_TCP */
+
+/**
+ * Connect a pcb contained inside a netconn
+ * Called from netconn_connect.
+ *
+ * @param m the api_msg pointing to the connection and containing
+ *          the IP address and port to connect to
+ */
+void
+lwip_netconn_do_connect(void *m)
+{
+  struct api_msg *msg = (struct api_msg *)m;
+  err_t err;
+
+  if (msg->conn->pcb.tcp == NULL) {
+    /* This may happen when calling netconn_connect() a second time */
+    err = ERR_CLSD;
+  } else {
+    switch (NETCONNTYPE_GROUP(msg->conn->type)) {
+#if LWIP_RAW
+      case NETCONN_RAW:
+        err = raw_connect(msg->conn->pcb.raw, API_EXPR_REF(msg->msg.bc.ipaddr));
+        break;
+#endif /* LWIP_RAW */
+#if LWIP_UDP
+      case NETCONN_UDP:
+        err = udp_connect(msg->conn->pcb.udp, API_EXPR_REF(msg->msg.bc.ipaddr), msg->msg.bc.port);
+        break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+      case NETCONN_TCP:
+        /* Prevent connect while doing any other action. */
+        if (msg->conn->state == NETCONN_CONNECT) {
+          err = ERR_ALREADY;
+        } else if (msg->conn->state != NETCONN_NONE) {
+          err = ERR_ISCONN;
+        } else {
+          setup_tcp(msg->conn);
+          err = tcp_connect(msg->conn->pcb.tcp, API_EXPR_REF(msg->msg.bc.ipaddr),
+                            msg->msg.bc.port, lwip_netconn_do_connected);
+          if (err == ERR_OK) {
+            u8_t non_blocking = netconn_is_nonblocking(msg->conn);
+            msg->conn->state = NETCONN_CONNECT;
+            SET_NONBLOCKING_CONNECT(msg->conn, non_blocking);
+            if (non_blocking) {
+              err = ERR_INPROGRESS;
+            } else {
+              msg->conn->current_msg = msg;
+              /* sys_sem_signal() is called from lwip_netconn_do_connected (or err_tcp()),
+                 when the connection is established! */
+#if LWIP_TCPIP_CORE_LOCKING
+              LWIP_ASSERT("state!", msg->conn->state == NETCONN_CONNECT);
+              UNLOCK_TCPIP_CORE();
+              sys_arch_sem_wait(LWIP_API_MSG_SEM(msg), 0);
+              LOCK_TCPIP_CORE();
+              LWIP_ASSERT("state!", msg->conn->state != NETCONN_CONNECT);
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+              return;
+            }
+          }
+        }
+        break;
+#endif /* LWIP_TCP */
+      default:
+        LWIP_ERROR("Invalid netconn type", 0, do {
+          err = ERR_VAL;
+        } while (0));
+        break;
+    }
+  }
+  msg->err = err;
+  /* For all other protocols, netconn_connect() calls netconn_apimsg(),
+     so use TCPIP_APIMSG_ACK() here. */
+  TCPIP_APIMSG_ACK(msg);
+}
+
+/**
+ * Disconnect a pcb contained inside a netconn
+ * Only used for UDP netconns.
+ * Called from netconn_disconnect.
+ *
+ * @param m the api_msg pointing to the connection to disconnect
+ */
+void
+lwip_netconn_do_disconnect(void *m)
+{
+  struct api_msg *msg = (struct api_msg *)m;
+
+#if LWIP_UDP
+  if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_UDP) {
+    udp_disconnect(msg->conn->pcb.udp);
+    msg->err = ERR_OK;
+  } else
+#endif /* LWIP_UDP */
+  {
+    msg->err = ERR_VAL;
+  }
+  TCPIP_APIMSG_ACK(msg);
+}
+
+#if LWIP_TCP
+/**
+ * Set a TCP pcb contained in a netconn into listen mode
+ * Called from netconn_listen.
+ *
+ * @param m the api_msg pointing to the connection
+ */
+void
+lwip_netconn_do_listen(void *m)
+{
+  struct api_msg *msg = (struct api_msg *)m;
+  err_t err;
+
+  if (msg->conn->pcb.tcp != NULL) {
+    if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) {
+      if (msg->conn->state == NETCONN_NONE) {
+        struct tcp_pcb *lpcb;
+        if (msg->conn->pcb.tcp->state != CLOSED) {
+          /* connection is not closed, cannot listen */
+          err = ERR_VAL;
+        } else {
+          u8_t backlog;
+#if TCP_LISTEN_BACKLOG
+          backlog = msg->msg.lb.backlog;
+#else  /* TCP_LISTEN_BACKLOG */
+          backlog = TCP_DEFAULT_LISTEN_BACKLOG;
+#endif /* TCP_LISTEN_BACKLOG */
+#if LWIP_IPV4 && LWIP_IPV6
+          /* "Socket API like" dual-stack support: If IP to listen to is IP6_ADDR_ANY,
+            * and NETCONN_FLAG_IPV6_V6ONLY is NOT set, use IP_ANY_TYPE to listen
+            */
+          if (ip_addr_eq(&msg->conn->pcb.ip->local_ip, IP6_ADDR_ANY) &&
+              (netconn_get_ipv6only(msg->conn) == 0)) {
+            /* change PCB type to IPADDR_TYPE_ANY */
+            IP_SET_TYPE_VAL(msg->conn->pcb.tcp->local_ip,  IPADDR_TYPE_ANY);
+            IP_SET_TYPE_VAL(msg->conn->pcb.tcp->remote_ip, IPADDR_TYPE_ANY);
+          }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
+          lpcb = tcp_listen_with_backlog_and_err(msg->conn->pcb.tcp, backlog, &err);
+
+          if (lpcb == NULL) {
+            /* in this case, the old pcb is still allocated */
+          } else {
+            /* delete the recvmbox and allocate the acceptmbox */
+            if (sys_mbox_valid(&msg->conn->recvmbox)) {
+              /** @todo: should we drain the recvmbox here? */
+              sys_mbox_free(&msg->conn->recvmbox);
+              sys_mbox_set_invalid(&msg->conn->recvmbox);
+            }
+            err = ERR_OK;
+            if (!sys_mbox_valid(&msg->conn->acceptmbox)) {
+              err = sys_mbox_new(&msg->conn->acceptmbox, DEFAULT_ACCEPTMBOX_SIZE);
+            }
+            if (err == ERR_OK) {
+              msg->conn->state = NETCONN_LISTEN;
+              msg->conn->pcb.tcp = lpcb;
+              tcp_arg(msg->conn->pcb.tcp, msg->conn);
+              tcp_accept(msg->conn->pcb.tcp, accept_function);
+            } else {
+              /* since the old pcb is already deallocated, free lpcb now */
+              tcp_close(lpcb);
+              msg->conn->pcb.tcp = NULL;
+            }
+          }
+        }
+      } else if (msg->conn->state == NETCONN_LISTEN) {
+        /* already listening, allow updating of the backlog */
+        err = ERR_OK;
+        tcp_backlog_set(msg->conn->pcb.tcp, msg->msg.lb.backlog);
+      } else {
+        err = ERR_CONN;
+      }
+    } else {
+      err = ERR_ARG;
+    }
+  } else {
+    err = ERR_CONN;
+  }
+  msg->err = err;
+  TCPIP_APIMSG_ACK(msg);
+}
+#endif /* LWIP_TCP */
+
+/**
+ * Send some data on a RAW or UDP pcb contained in a netconn
+ * Called from netconn_send
+ *
+ * @param m the api_msg pointing to the connection
+ */
+void
+lwip_netconn_do_send(void *m)
+{
+  struct api_msg *msg = (struct api_msg *)m;
+
+  err_t err = netconn_err(msg->conn);
+  if (err == ERR_OK) {
+    if (msg->conn->pcb.tcp != NULL) {
+      switch (NETCONNTYPE_GROUP(msg->conn->type)) {
+#if LWIP_RAW
+        case NETCONN_RAW:
+          if (ip_addr_isany(&msg->msg.b->addr) || IP_IS_ANY_TYPE_VAL(msg->msg.b->addr)) {
+            err = raw_send(msg->conn->pcb.raw, msg->msg.b->p);
+          } else {
+            err = raw_sendto(msg->conn->pcb.raw, msg->msg.b->p, &msg->msg.b->addr);
+          }
+          break;
+#endif
+#if LWIP_UDP
+        case NETCONN_UDP:
+#if LWIP_CHECKSUM_ON_COPY
+          if (ip_addr_isany(&msg->msg.b->addr) || IP_IS_ANY_TYPE_VAL(msg->msg.b->addr)) {
+            err = udp_send_chksum(msg->conn->pcb.udp, msg->msg.b->p,
+                                  msg->msg.b->flags & NETBUF_FLAG_CHKSUM, msg->msg.b->toport_chksum);
+          } else {
+            err = udp_sendto_chksum(msg->conn->pcb.udp, msg->msg.b->p,
+                                    &msg->msg.b->addr, msg->msg.b->port,
+                                    msg->msg.b->flags & NETBUF_FLAG_CHKSUM, msg->msg.b->toport_chksum);
+          }
+#else /* LWIP_CHECKSUM_ON_COPY */
+          if (ip_addr_isany_val(msg->msg.b->addr) || IP_IS_ANY_TYPE_VAL(msg->msg.b->addr)) {
+            err = udp_send(msg->conn->pcb.udp, msg->msg.b->p);
+          } else {
+            err = udp_sendto(msg->conn->pcb.udp, msg->msg.b->p, &msg->msg.b->addr, msg->msg.b->port);
+          }
+#endif /* LWIP_CHECKSUM_ON_COPY */
+          break;
+#endif /* LWIP_UDP */
+        default:
+          err = ERR_CONN;
+          break;
+      }
+    } else {
+      err = ERR_CONN;
+    }
+  }
+  msg->err = err;
+  TCPIP_APIMSG_ACK(msg);
+}
+
+#if LWIP_TCP
+/**
+ * Indicate data has been received from a TCP pcb contained in a netconn
+ * Called from netconn_recv
+ *
+ * @param m the api_msg pointing to the connection
+ */
+void
+lwip_netconn_do_recv(void *m)
+{
+  struct api_msg *msg = (struct api_msg *)m;
+
+  msg->err = ERR_OK;
+  if (msg->conn->pcb.tcp != NULL) {
+    if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) {
+      size_t remaining = msg->msg.r.len;
+      do {
+        u16_t recved = (u16_t)((remaining > 0xffff) ? 0xffff : remaining);
+        tcp_recved(msg->conn->pcb.tcp, recved);
+        remaining -= recved;
+      } while (remaining != 0);
+    }
+  }
+  TCPIP_APIMSG_ACK(msg);
+}
+
+#if TCP_LISTEN_BACKLOG
+/** Indicate that a TCP pcb has been accepted
+ * Called from netconn_accept
+ *
+ * @param m the api_msg pointing to the connection
+ */
+void
+lwip_netconn_do_accepted(void *m)
+{
+  struct api_msg *msg = (struct api_msg *)m;
+
+  msg->err = ERR_OK;
+  if (msg->conn->pcb.tcp != NULL) {
+    if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) {
+      tcp_backlog_accepted(msg->conn->pcb.tcp);
+    }
+  }
+  TCPIP_APIMSG_ACK(msg);
+}
+#endif /* TCP_LISTEN_BACKLOG */
+
+/**
+ * See if more data needs to be written from a previous call to netconn_write.
+ * Called initially from lwip_netconn_do_write. If the first call can't send all data
+ * (because of low memory or empty send-buffer), this function is called again
+ * from sent_tcp() or poll_tcp() to send more data. If all data is sent, the
+ * blocking application thread (waiting in netconn_write) is released.
+ *
+ * @param conn netconn (that is currently in state NETCONN_WRITE) to process
+ * @return ERR_OK
+ *         ERR_MEM if LWIP_TCPIP_CORE_LOCKING=1 and sending hasn't yet finished
+ */
+static err_t
+lwip_netconn_do_writemore(struct netconn *conn  WRITE_DELAYED_PARAM)
+{
+  err_t err;
+  const void *dataptr;
+  u16_t len, available;
+  u8_t write_finished = 0;
+  size_t diff;
+  u8_t dontblock;
+  u8_t apiflags;
+  u8_t write_more;
+
+  LWIP_ASSERT("conn != NULL", conn != NULL);
+  LWIP_ASSERT("conn->state == NETCONN_WRITE", (conn->state == NETCONN_WRITE));
+  LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL);
+  LWIP_ASSERT("conn->pcb.tcp != NULL", conn->pcb.tcp != NULL);
+  LWIP_ASSERT("conn->current_msg->msg.w.offset < conn->current_msg->msg.w.len",
+              conn->current_msg->msg.w.offset < conn->current_msg->msg.w.len);
+  LWIP_ASSERT("conn->current_msg->msg.w.vector_cnt > 0", conn->current_msg->msg.w.vector_cnt > 0);
+
+  apiflags = conn->current_msg->msg.w.apiflags;
+  dontblock = netconn_is_nonblocking(conn) || (apiflags & NETCONN_DONTBLOCK);
+
+#if LWIP_SO_SNDTIMEO
+  if ((conn->send_timeout != 0) &&
+      ((s32_t)(sys_now() - conn->current_msg->msg.w.time_started) >= conn->send_timeout)) {
+    write_finished = 1;
+    if (conn->current_msg->msg.w.offset == 0) {
+      /* nothing has been written */
+      err = ERR_WOULDBLOCK;
+    } else {
+      /* partial write */
+      err = ERR_OK;
+    }
+  } else
+#endif /* LWIP_SO_SNDTIMEO */
+  {
+    do {
+      dataptr = (const u8_t *)conn->current_msg->msg.w.vector->ptr + conn->current_msg->msg.w.vector_off;
+      diff = conn->current_msg->msg.w.vector->len - conn->current_msg->msg.w.vector_off;
+      if (diff > 0xffffUL) { /* max_u16_t */
+        len = 0xffff;
+        apiflags |= TCP_WRITE_FLAG_MORE;
+      } else {
+        len = (u16_t)diff;
+      }
+      available = tcp_sndbuf(conn->pcb.tcp);
+      if (available < len) {
+        /* don't try to write more than sendbuf */
+        len = available;
+        if (dontblock) {
+          if (!len) {
+            /* set error according to partial write or not */
+            err = (conn->current_msg->msg.w.offset == 0) ? ERR_WOULDBLOCK : ERR_OK;
+            goto err_mem;
+          }
+        } else {
+          apiflags |= TCP_WRITE_FLAG_MORE;
+        }
+      }
+      LWIP_ASSERT("lwip_netconn_do_writemore: invalid length!",
+                  ((conn->current_msg->msg.w.vector_off + len) <= conn->current_msg->msg.w.vector->len));
+      /* we should loop around for more sending in the following cases:
+           1) We couldn't finish the current vector because of 16-bit size limitations.
+              tcp_write() and tcp_sndbuf() both are limited to 16-bit sizes
+           2) We are sending the remainder of the current vector and have more */
+      if ((len == 0xffff && diff > 0xffffUL) ||
+          (len == (u16_t)diff && conn->current_msg->msg.w.vector_cnt > 1)) {
+        write_more = 1;
+        apiflags |= TCP_WRITE_FLAG_MORE;
+      } else {
+        write_more = 0;
+      }
+      err = tcp_write(conn->pcb.tcp, dataptr, len, apiflags);
+      if (err == ERR_OK) {
+        conn->current_msg->msg.w.offset += len;
+        conn->current_msg->msg.w.vector_off += len;
+        /* check if current vector is finished */
+        if (conn->current_msg->msg.w.vector_off == conn->current_msg->msg.w.vector->len) {
+          conn->current_msg->msg.w.vector_cnt--;
+          /* if we have additional vectors, move on to them */
+          if (conn->current_msg->msg.w.vector_cnt > 0) {
+            conn->current_msg->msg.w.vector++;
+            conn->current_msg->msg.w.vector_off = 0;
+          }
+        }
+      }
+    } while (write_more && err == ERR_OK);
+    /* if OK or memory error, check available space */
+    if ((err == ERR_OK) || (err == ERR_MEM)) {
+err_mem:
+      if (dontblock && (conn->current_msg->msg.w.offset < conn->current_msg->msg.w.len)) {
+        /* non-blocking write did not write everything: mark the pcb non-writable
+           and let poll_tcp check writable space to mark the pcb writable again */
+        API_EVENT(conn, NETCONN_EVT_SENDMINUS, 0);
+        conn->flags |= NETCONN_FLAG_CHECK_WRITESPACE;
+      } else if ((tcp_sndbuf(conn->pcb.tcp) <= TCP_SNDLOWAT) ||
+                 (tcp_sndqueuelen(conn->pcb.tcp) >= TCP_SNDQUEUELOWAT)) {
+        /* The queued byte- or pbuf-count exceeds the configured low-water limit,
+           let select mark this pcb as non-writable. */
+        API_EVENT(conn, NETCONN_EVT_SENDMINUS, 0);
+      }
+    }
+
+    if (err == ERR_OK) {
+      err_t out_err;
+      if ((conn->current_msg->msg.w.offset == conn->current_msg->msg.w.len) || dontblock) {
+        /* return sent length (caller reads length from msg.w.offset) */
+        write_finished = 1;
+      }
+      out_err = tcp_output(conn->pcb.tcp);
+      if (out_err == ERR_RTE) {
+        /* If tcp_output fails because no route is found,
+           don't try writing any more but return the error
+           to the application thread. */
+        err = out_err;
+        write_finished = 1;
+      }
+    } else if (err == ERR_MEM) {
+      /* If ERR_MEM, we wait for sent_tcp or poll_tcp to be called.
+         For blocking sockets, we do NOT return to the application
+         thread, since ERR_MEM is only a temporary error! Non-blocking
+         will remain non-writable until sent_tcp/poll_tcp is called */
+
+      /* tcp_write returned ERR_MEM, try tcp_output anyway */
+      err_t out_err = tcp_output(conn->pcb.tcp);
+      if (out_err == ERR_RTE) {
+        /* If tcp_output fails because no route is found,
+           don't try writing any more but return the error
+           to the application thread. */
+        err = out_err;
+        write_finished = 1;
+      } else if (dontblock) {
+        /* non-blocking write is done on ERR_MEM, set error according
+           to partial write or not */
+        err = (conn->current_msg->msg.w.offset == 0) ? ERR_WOULDBLOCK : ERR_OK;
+        write_finished = 1;
+      }
+    } else {
+      /* On errors != ERR_MEM, we don't try writing any more but return
+         the error to the application thread. */
+      write_finished = 1;
+    }
+  }
+  if (write_finished) {
+    /* everything was written: set back connection state
+       and back to application task */
+    sys_sem_t *op_completed_sem = LWIP_API_MSG_SEM(conn->current_msg);
+    conn->current_msg->err = err;
+    conn->current_msg = NULL;
+    conn->state = NETCONN_NONE;
+#if LWIP_TCPIP_CORE_LOCKING
+    if (delayed)
+#endif
+    {
+      sys_sem_signal(op_completed_sem);
+    }
+  }
+#if LWIP_TCPIP_CORE_LOCKING
+  else {
+    return ERR_MEM;
+  }
+#endif
+  return ERR_OK;
+}
+#endif /* LWIP_TCP */
+
+/**
+ * Send some data on a TCP pcb contained in a netconn
+ * Called from netconn_write
+ *
+ * @param m the api_msg pointing to the connection
+ */
+void
+lwip_netconn_do_write(void *m)
+{
+  struct api_msg *msg = (struct api_msg *)m;
+
+  err_t err = netconn_err(msg->conn);
+  if (err == ERR_OK) {
+    if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) {
+#if LWIP_TCP
+      if (msg->conn->state != NETCONN_NONE) {
+        /* netconn is connecting, closing or in blocking write */
+        err = ERR_INPROGRESS;
+      } else if (msg->conn->pcb.tcp != NULL) {
+        msg->conn->state = NETCONN_WRITE;
+        /* set all the variables used by lwip_netconn_do_writemore */
+        LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL);
+        LWIP_ASSERT("msg->msg.w.len != 0", msg->msg.w.len != 0);
+        msg->conn->current_msg = msg;
+#if LWIP_TCPIP_CORE_LOCKING
+        if (lwip_netconn_do_writemore(msg->conn, 0) != ERR_OK) {
+          LWIP_ASSERT("state!", msg->conn->state == NETCONN_WRITE);
+          UNLOCK_TCPIP_CORE();
+          sys_arch_sem_wait(LWIP_API_MSG_SEM(msg), 0);
+          LOCK_TCPIP_CORE();
+          LWIP_ASSERT("state!", msg->conn->state != NETCONN_WRITE);
+        }
+#else /* LWIP_TCPIP_CORE_LOCKING */
+        lwip_netconn_do_writemore(msg->conn);
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+        /* for both cases: if lwip_netconn_do_writemore was called, don't ACK the APIMSG
+           since lwip_netconn_do_writemore ACKs it! */
+        return;
+      } else {
+        err = ERR_CONN;
+      }
+#else /* LWIP_TCP */
+      err = ERR_VAL;
+#endif /* LWIP_TCP */
+#if (LWIP_UDP || LWIP_RAW)
+    } else {
+      err = ERR_VAL;
+#endif /* (LWIP_UDP || LWIP_RAW) */
+    }
+  }
+  msg->err = err;
+  TCPIP_APIMSG_ACK(msg);
+}
+
+/**
+ * Return a connection's local or remote address
+ * Called from netconn_getaddr
+ *
+ * @param m the api_msg pointing to the connection
+ */
+void
+lwip_netconn_do_getaddr(void *m)
+{
+  struct api_msg *msg = (struct api_msg *)m;
+
+  if (msg->conn->pcb.ip != NULL) {
+    if (msg->msg.ad.local) {
+      ip_addr_copy(API_EXPR_DEREF(msg->msg.ad.ipaddr),
+                   msg->conn->pcb.ip->local_ip);
+    } else {
+      ip_addr_copy(API_EXPR_DEREF(msg->msg.ad.ipaddr),
+                   msg->conn->pcb.ip->remote_ip);
+    }
+
+    msg->err = ERR_OK;
+    switch (NETCONNTYPE_GROUP(msg->conn->type)) {
+#if LWIP_RAW
+      case NETCONN_RAW:
+        if (msg->msg.ad.local) {
+          API_EXPR_DEREF(msg->msg.ad.port) = msg->conn->pcb.raw->protocol;
+        } else {
+          /* return an error as connecting is only a helper for upper layers */
+          msg->err = ERR_CONN;
+        }
+        break;
+#endif /* LWIP_RAW */
+#if LWIP_UDP
+      case NETCONN_UDP:
+        if (msg->msg.ad.local) {
+          API_EXPR_DEREF(msg->msg.ad.port) = msg->conn->pcb.udp->local_port;
+        } else {
+          if ((msg->conn->pcb.udp->flags & UDP_FLAGS_CONNECTED) == 0) {
+            msg->err = ERR_CONN;
+          } else {
+            API_EXPR_DEREF(msg->msg.ad.port) = msg->conn->pcb.udp->remote_port;
+          }
+        }
+        break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+      case NETCONN_TCP:
+        if ((msg->msg.ad.local == 0) &&
+            ((msg->conn->pcb.tcp->state == CLOSED) || (msg->conn->pcb.tcp->state == LISTEN))) {
+          /* pcb is not connected and remote name is requested */
+          msg->err = ERR_CONN;
+        } else {
+          API_EXPR_DEREF(msg->msg.ad.port) = (msg->msg.ad.local ? msg->conn->pcb.tcp->local_port : msg->conn->pcb.tcp->remote_port);
+        }
+        break;
+#endif /* LWIP_TCP */
+      default:
+        LWIP_ASSERT("invalid netconn_type", 0);
+        break;
+    }
+  } else {
+    msg->err = ERR_CONN;
+  }
+  TCPIP_APIMSG_ACK(msg);
+}
+
+/**
+ * Close or half-shutdown a TCP pcb contained in a netconn
+ * Called from netconn_close
+ * In contrast to closing sockets, the netconn is not deallocated.
+ *
+ * @param m the api_msg pointing to the connection
+ */
+void
+lwip_netconn_do_close(void *m)
+{
+  struct api_msg *msg = (struct api_msg *)m;
+
+#if LWIP_TCP
+  enum netconn_state state = msg->conn->state;
+  /* First check if this is a TCP netconn and if it is in a correct state
+      (LISTEN doesn't support half shutdown) */
+  if ((msg->conn->pcb.tcp != NULL) &&
+      (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) &&
+      ((msg->msg.sd.shut == NETCONN_SHUT_RDWR) || (state != NETCONN_LISTEN))) {
+    /* Check if we are in a connected state */
+    if (state == NETCONN_CONNECT) {
+      /* TCP connect in progress: cannot shutdown */
+      msg->err = ERR_CONN;
+    } else if (state == NETCONN_WRITE) {
+#if LWIP_NETCONN_FULLDUPLEX
+      if (msg->msg.sd.shut & NETCONN_SHUT_WR) {
+        /* close requested, abort running write */
+        sys_sem_t *write_completed_sem;
+        LWIP_ASSERT("msg->conn->current_msg != NULL", msg->conn->current_msg != NULL);
+        write_completed_sem = LWIP_API_MSG_SEM(msg->conn->current_msg);
+        msg->conn->current_msg->err = ERR_CLSD;
+        msg->conn->current_msg = NULL;
+        msg->conn->state = NETCONN_NONE;
+        state = NETCONN_NONE;
+        sys_sem_signal(write_completed_sem);
+      } else {
+        LWIP_ASSERT("msg->msg.sd.shut == NETCONN_SHUT_RD", msg->msg.sd.shut == NETCONN_SHUT_RD);
+        /* In this case, let the write continue and do not interfere with
+           conn->current_msg or conn->state! */
+        msg->err = tcp_shutdown(msg->conn->pcb.tcp, 1, 0);
+      }
+    }
+    if (state == NETCONN_NONE) {
+#else /* LWIP_NETCONN_FULLDUPLEX */
+      msg->err = ERR_INPROGRESS;
+    } else {
+#endif /* LWIP_NETCONN_FULLDUPLEX */
+      if (msg->msg.sd.shut & NETCONN_SHUT_RD) {
+#if LWIP_NETCONN_FULLDUPLEX
+        /* Mark mboxes invalid */
+        netconn_mark_mbox_invalid(msg->conn);
+#else /* LWIP_NETCONN_FULLDUPLEX */
+        netconn_drain(msg->conn);
+#endif /* LWIP_NETCONN_FULLDUPLEX */
+      }
+      LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL);
+      msg->conn->state = NETCONN_CLOSE;
+      msg->conn->current_msg = msg;
+#if LWIP_TCPIP_CORE_LOCKING
+      if (lwip_netconn_do_close_internal(msg->conn, 0) != ERR_OK) {
+        LWIP_ASSERT("state!", msg->conn->state == NETCONN_CLOSE);
+        UNLOCK_TCPIP_CORE();
+        sys_arch_sem_wait(LWIP_API_MSG_SEM(msg), 0);
+        LOCK_TCPIP_CORE();
+        LWIP_ASSERT("state!", msg->conn->state == NETCONN_NONE);
+      }
+#else /* LWIP_TCPIP_CORE_LOCKING */
+      lwip_netconn_do_close_internal(msg->conn);
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+      /* for tcp netconns, lwip_netconn_do_close_internal ACKs the message */
+      return;
+    }
+  } else
+#endif /* LWIP_TCP */
+  {
+    msg->err = ERR_CONN;
+  }
+  TCPIP_APIMSG_ACK(msg);
+}
+
+#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD)
+/**
+ * Join multicast groups for UDP netconns.
+ * Called from netconn_join_leave_group
+ *
+ * @param m the api_msg pointing to the connection
+ */
+void
+lwip_netconn_do_join_leave_group(void *m)
+{
+  struct api_msg *msg = (struct api_msg *)m;
+
+  msg->err = ERR_CONN;
+  if (msg->conn->pcb.tcp != NULL) {
+    if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_UDP) {
+#if LWIP_UDP
+#if LWIP_IPV6 && LWIP_IPV6_MLD
+      if (NETCONNTYPE_ISIPV6(msg->conn->type)) {
+        if (msg->msg.jl.join_or_leave == NETCONN_JOIN) {
+          msg->err = mld6_joingroup(ip_2_ip6(API_EXPR_REF(msg->msg.jl.netif_addr)),
+                                    ip_2_ip6(API_EXPR_REF(msg->msg.jl.multiaddr)));
+        } else {
+          msg->err = mld6_leavegroup(ip_2_ip6(API_EXPR_REF(msg->msg.jl.netif_addr)),
+                                     ip_2_ip6(API_EXPR_REF(msg->msg.jl.multiaddr)));
+        }
+      } else
+#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
+      {
+#if LWIP_IGMP
+        if (msg->msg.jl.join_or_leave == NETCONN_JOIN) {
+          msg->err = igmp_joingroup(ip_2_ip4(API_EXPR_REF(msg->msg.jl.netif_addr)),
+                                    ip_2_ip4(API_EXPR_REF(msg->msg.jl.multiaddr)));
+        } else {
+          msg->err = igmp_leavegroup(ip_2_ip4(API_EXPR_REF(msg->msg.jl.netif_addr)),
+                                     ip_2_ip4(API_EXPR_REF(msg->msg.jl.multiaddr)));
+        }
+#endif /* LWIP_IGMP */
+      }
+#endif /* LWIP_UDP */
+#if (LWIP_TCP || LWIP_RAW)
+    } else {
+      msg->err = ERR_VAL;
+#endif /* (LWIP_TCP || LWIP_RAW) */
+    }
+  }
+  TCPIP_APIMSG_ACK(msg);
+}
+/**
+ * Join multicast groups for UDP netconns.
+ * Called from netconn_join_leave_group_netif
+ *
+ * @param m the api_msg pointing to the connection
+ */
+void
+lwip_netconn_do_join_leave_group_netif(void *m)
+{
+  struct api_msg *msg = (struct api_msg *)m;
+  struct netif *netif;
+
+  netif = netif_get_by_index(msg->msg.jl.if_idx);
+  if (netif == NULL) {
+    msg->err = ERR_IF;
+    goto done;
+  }
+
+  msg->err = ERR_CONN;
+  if (msg->conn->pcb.tcp != NULL) {
+    if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_UDP) {
+#if LWIP_UDP
+#if LWIP_IPV6 && LWIP_IPV6_MLD
+      if (NETCONNTYPE_ISIPV6(msg->conn->type)) {
+        if (msg->msg.jl.join_or_leave == NETCONN_JOIN) {
+          msg->err = mld6_joingroup_netif(netif,
+                                          ip_2_ip6(API_EXPR_REF(msg->msg.jl.multiaddr)));
+        } else {
+          msg->err = mld6_leavegroup_netif(netif,
+                                           ip_2_ip6(API_EXPR_REF(msg->msg.jl.multiaddr)));
+        }
+      } else
+#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
+      {
+#if LWIP_IGMP
+        if (msg->msg.jl.join_or_leave == NETCONN_JOIN) {
+          msg->err = igmp_joingroup_netif(netif,
+                                          ip_2_ip4(API_EXPR_REF(msg->msg.jl.multiaddr)));
+        } else {
+          msg->err = igmp_leavegroup_netif(netif,
+                                           ip_2_ip4(API_EXPR_REF(msg->msg.jl.multiaddr)));
+        }
+#endif /* LWIP_IGMP */
+      }
+#endif /* LWIP_UDP */
+#if (LWIP_TCP || LWIP_RAW)
+    } else {
+      msg->err = ERR_VAL;
+#endif /* (LWIP_TCP || LWIP_RAW) */
+    }
+  }
+
+done:
+  TCPIP_APIMSG_ACK(msg);
+}
+#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */
+
+#if LWIP_DNS
+/**
+ * Callback function that is called when DNS name is resolved
+ * (or on timeout). A waiting application thread is waked up by
+ * signaling the semaphore.
+ */
+static void
+lwip_netconn_do_dns_found(const char *name, const ip_addr_t *ipaddr, void *arg)
+{
+  struct dns_api_msg *msg = (struct dns_api_msg *)arg;
+
+  /* we trust the internal implementation to be correct :-) */
+  LWIP_UNUSED_ARG(name);
+
+  if (ipaddr == NULL) {
+    /* timeout or memory error */
+    API_EXPR_DEREF(msg->err) = ERR_VAL;
+  } else {
+    /* address was resolved */
+    API_EXPR_DEREF(msg->err) = ERR_OK;
+    API_EXPR_DEREF(msg->addr) = *ipaddr;
+  }
+  /* wake up the application task waiting in netconn_gethostbyname */
+  sys_sem_signal(API_EXPR_REF_SEM(msg->sem));
+}
+
+/**
+ * Execute a DNS query
+ * Called from netconn_gethostbyname
+ *
+ * @param arg the dns_api_msg pointing to the query
+ */
+void
+lwip_netconn_do_gethostbyname(void *arg)
+{
+  struct dns_api_msg *msg = (struct dns_api_msg *)arg;
+  u8_t addrtype =
+#if LWIP_IPV4 && LWIP_IPV6
+    msg->dns_addrtype;
+#else
+    LWIP_DNS_ADDRTYPE_DEFAULT;
+#endif
+
+  API_EXPR_DEREF(msg->err) = dns_gethostbyname_addrtype(msg->name,
+                             API_EXPR_REF(msg->addr), lwip_netconn_do_dns_found, msg, addrtype);
+#if LWIP_TCPIP_CORE_LOCKING
+  /* For core locking, only block if we need to wait for answer/timeout */
+  if (API_EXPR_DEREF(msg->err) == ERR_INPROGRESS) {
+    UNLOCK_TCPIP_CORE();
+    sys_sem_wait(API_EXPR_REF_SEM(msg->sem));
+    LOCK_TCPIP_CORE();
+    LWIP_ASSERT("do_gethostbyname still in progress!!", API_EXPR_DEREF(msg->err) != ERR_INPROGRESS);
+  }
+#else /* LWIP_TCPIP_CORE_LOCKING */
+  if (API_EXPR_DEREF(msg->err) != ERR_INPROGRESS) {
+    /* on error or immediate success, wake up the application
+     * task waiting in netconn_gethostbyname */
+    sys_sem_signal(API_EXPR_REF_SEM(msg->sem));
+  }
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+}
+#endif /* LWIP_DNS */
+
+#endif /* LWIP_NETCONN */
diff --git a/lib/lwip/api/err.c b/lib/lwip/api/err.c
new file mode 100644
index 0000000000..dd2b62dab1
--- /dev/null
+++ b/lib/lwip/api/err.c
@@ -0,0 +1,115 @@
+/**
+ * @file
+ * Error Management module
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/err.h"
+#include "lwip/def.h"
+#include "lwip/sys.h"
+
+#include "lwip/errno.h"
+
+#if !NO_SYS
+/** Table to quickly map an lwIP error (err_t) to a socket error
+  * by using -err as an index */
+static const int err_to_errno_table[] = {
+  0,             /* ERR_OK          0      No error, everything OK. */
+  ENOMEM,        /* ERR_MEM        -1      Out of memory error.     */
+  ENOBUFS,       /* ERR_BUF        -2      Buffer error.            */
+  EWOULDBLOCK,   /* ERR_TIMEOUT    -3      Timeout                  */
+  EHOSTUNREACH,  /* ERR_RTE        -4      Routing problem.         */
+  EINPROGRESS,   /* ERR_INPROGRESS -5      Operation in progress    */
+  EINVAL,        /* ERR_VAL        -6      Illegal value.           */
+  EWOULDBLOCK,   /* ERR_WOULDBLOCK -7      Operation would block.   */
+  EADDRINUSE,    /* ERR_USE        -8      Address in use.          */
+  EALREADY,      /* ERR_ALREADY    -9      Already connecting.      */
+  EISCONN,       /* ERR_ISCONN     -10     Conn already established.*/
+  ENOTCONN,      /* ERR_CONN       -11     Not connected.           */
+  -1,            /* ERR_IF         -12     Low-level netif error    */
+  ECONNABORTED,  /* ERR_ABRT       -13     Connection aborted.      */
+  ECONNRESET,    /* ERR_RST        -14     Connection reset.        */
+  ENOTCONN,      /* ERR_CLSD       -15     Connection closed.       */
+  EIO            /* ERR_ARG        -16     Illegal argument.        */
+};
+
+int
+err_to_errno(err_t err)
+{
+  if ((err > 0) || (-err >= (err_t)LWIP_ARRAYSIZE(err_to_errno_table))) {
+    return EIO;
+  }
+  return err_to_errno_table[-err];
+}
+#endif /* !NO_SYS */
+
+#ifdef LWIP_DEBUG
+
+static const char *err_strerr[] = {
+  "Ok.",                    /* ERR_OK          0  */
+  "Out of memory error.",   /* ERR_MEM        -1  */
+  "Buffer error.",          /* ERR_BUF        -2  */
+  "Timeout.",               /* ERR_TIMEOUT    -3  */
+  "Routing problem.",       /* ERR_RTE        -4  */
+  "Operation in progress.", /* ERR_INPROGRESS -5  */
+  "Illegal value.",         /* ERR_VAL        -6  */
+  "Operation would block.", /* ERR_WOULDBLOCK -7  */
+  "Address in use.",        /* ERR_USE        -8  */
+  "Already connecting.",    /* ERR_ALREADY    -9  */
+  "Already connected.",     /* ERR_ISCONN     -10 */
+  "Not connected.",         /* ERR_CONN       -11 */
+  "Low-level netif error.", /* ERR_IF         -12 */
+  "Connection aborted.",    /* ERR_ABRT       -13 */
+  "Connection reset.",      /* ERR_RST        -14 */
+  "Connection closed.",     /* ERR_CLSD       -15 */
+  "Illegal argument."       /* ERR_ARG        -16 */
+};
+
+/**
+ * Convert an lwip internal error to a string representation.
+ *
+ * @param err an lwip internal err_t
+ * @return a string representation for err
+ */
+const char *
+lwip_strerr(err_t err)
+{
+  if ((err > 0) || (-err >= (err_t)LWIP_ARRAYSIZE(err_strerr))) {
+    return "Unknown error.";
+  }
+  return err_strerr[-err];
+}
+
+#endif /* LWIP_DEBUG */
diff --git a/lib/lwip/api/if_api.c b/lib/lwip/api/if_api.c
new file mode 100644
index 0000000000..d274c323af
--- /dev/null
+++ b/lib/lwip/api/if_api.c
@@ -0,0 +1,102 @@
+/**
+ * @file
+ * Interface Identification APIs from:
+ *              RFC 3493: Basic Socket Interface Extensions for IPv6
+ *                  Section 4: Interface Identification
+ *
+ * @defgroup if_api Interface Identification API
+ * @ingroup socket
+ */
+
+/*
+ * Copyright (c) 2017 Joel Cunningham, Garmin International, Inc. <joel.cunningham@garmin.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Joel Cunningham <joel.cunningham@me.com>
+ *
+ */
+#include "lwip/opt.h"
+
+#if LWIP_SOCKET
+
+#include "lwip/errno.h"
+#include "lwip/if_api.h"
+#include "lwip/netifapi.h"
+#include "lwip/priv/sockets_priv.h"
+
+/**
+ * @ingroup if_api
+ * Maps an interface index to its corresponding name.
+ * @param ifindex interface index
+ * @param ifname shall point to a buffer of at least {IF_NAMESIZE} bytes
+ * @return If ifindex is an interface index, then the function shall return the
+ * value supplied in ifname, which points to a buffer now containing the interface name.
+ * Otherwise, the function shall return a NULL pointer.
+ */
+char *
+lwip_if_indextoname(unsigned int ifindex, char *ifname)
+{
+#if LWIP_NETIF_API
+  if (ifindex <= 0xff) {
+    err_t err = netifapi_netif_index_to_name((u8_t)ifindex, ifname);
+    if (!err && ifname[0] != '\0') {
+      return ifname;
+    }
+  }
+#else /* LWIP_NETIF_API */
+  LWIP_UNUSED_ARG(ifindex);
+  LWIP_UNUSED_ARG(ifname);
+#endif /* LWIP_NETIF_API */
+  set_errno(ENXIO);
+  return NULL;
+}
+
+/**
+ * @ingroup if_api
+ * Returns the interface index corresponding to name ifname.
+ * @param ifname Interface name
+ * @return The corresponding index if ifname is the name of an interface;
+ * otherwise, zero.
+ */
+unsigned int
+lwip_if_nametoindex(const char *ifname)
+{
+#if LWIP_NETIF_API
+  err_t err;
+  u8_t idx;
+
+  err = netifapi_netif_name_to_index(ifname, &idx);
+  if (!err) {
+    return idx;
+  }
+#else /* LWIP_NETIF_API */
+  LWIP_UNUSED_ARG(ifname);
+#endif /* LWIP_NETIF_API */
+  return 0; /* invalid index */
+}
+
+#endif /* LWIP_SOCKET */
diff --git a/lib/lwip/api/netbuf.c b/lib/lwip/api/netbuf.c
new file mode 100644
index 0000000000..8f5be9e2b2
--- /dev/null
+++ b/lib/lwip/api/netbuf.c
@@ -0,0 +1,250 @@
+/**
+ * @file
+ * Network buffer management
+ *
+ * @defgroup netbuf Network buffers
+ * @ingroup netconn
+ * Network buffer descriptor for @ref netconn. Based on @ref pbuf internally
+ * to avoid copying data around.<br>
+ * Buffers must not be shared across multiple threads, all functions except
+ * netbuf_new() and netbuf_delete() are not thread-safe.
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/netbuf.h"
+#include "lwip/memp.h"
+
+#include <string.h>
+
+/**
+ * @ingroup netbuf
+ * Create (allocate) and initialize a new netbuf.
+ * The netbuf doesn't yet contain a packet buffer!
+ *
+ * @return a pointer to a new netbuf
+ *         NULL on lack of memory
+ */
+struct
+netbuf *netbuf_new(void)
+{
+  struct netbuf *buf;
+
+  buf = (struct netbuf *)memp_malloc(MEMP_NETBUF);
+  if (buf != NULL) {
+    memset(buf, 0, sizeof(struct netbuf));
+  }
+  return buf;
+}
+
+/**
+ * @ingroup netbuf
+ * Deallocate a netbuf allocated by netbuf_new().
+ *
+ * @param buf pointer to a netbuf allocated by netbuf_new()
+ */
+void
+netbuf_delete(struct netbuf *buf)
+{
+  if (buf != NULL) {
+    if (buf->p != NULL) {
+      pbuf_free(buf->p);
+      buf->p = buf->ptr = NULL;
+    }
+    memp_free(MEMP_NETBUF, buf);
+  }
+}
+
+/**
+ * @ingroup netbuf
+ * Allocate memory for a packet buffer for a given netbuf.
+ *
+ * @param buf the netbuf for which to allocate a packet buffer
+ * @param size the size of the packet buffer to allocate
+ * @return pointer to the allocated memory
+ *         NULL if no memory could be allocated
+ */
+void *
+netbuf_alloc(struct netbuf *buf, u16_t size)
+{
+  LWIP_ERROR("netbuf_alloc: invalid buf", (buf != NULL), return NULL;);
+
+  /* Deallocate any previously allocated memory. */
+  if (buf->p != NULL) {
+    pbuf_free(buf->p);
+  }
+  buf->p = pbuf_alloc(PBUF_TRANSPORT, size, PBUF_RAM);
+  if (buf->p == NULL) {
+    return NULL;
+  }
+  LWIP_ASSERT("check that first pbuf can hold size",
+              (buf->p->len >= size));
+  buf->ptr = buf->p;
+  return buf->p->payload;
+}
+
+/**
+ * @ingroup netbuf
+ * Free the packet buffer included in a netbuf
+ *
+ * @param buf pointer to the netbuf which contains the packet buffer to free
+ */
+void
+netbuf_free(struct netbuf *buf)
+{
+  LWIP_ERROR("netbuf_free: invalid buf", (buf != NULL), return;);
+  if (buf->p != NULL) {
+    pbuf_free(buf->p);
+  }
+  buf->p = buf->ptr = NULL;
+#if LWIP_CHECKSUM_ON_COPY
+  buf->flags = 0;
+  buf->toport_chksum = 0;
+#endif /* LWIP_CHECKSUM_ON_COPY */
+}
+
+/**
+ * @ingroup netbuf
+ * Let a netbuf reference existing (non-volatile) data.
+ *
+ * @param buf netbuf which should reference the data
+ * @param dataptr pointer to the data to reference
+ * @param size size of the data
+ * @return ERR_OK if data is referenced
+ *         ERR_MEM if data couldn't be referenced due to lack of memory
+ */
+err_t
+netbuf_ref(struct netbuf *buf, const void *dataptr, u16_t size)
+{
+  LWIP_ERROR("netbuf_ref: invalid buf", (buf != NULL), return ERR_ARG;);
+  if (buf->p != NULL) {
+    pbuf_free(buf->p);
+  }
+  buf->p = pbuf_alloc(PBUF_TRANSPORT, 0, PBUF_REF);
+  if (buf->p == NULL) {
+    buf->ptr = NULL;
+    return ERR_MEM;
+  }
+  ((struct pbuf_rom *)buf->p)->payload = dataptr;
+  buf->p->len = buf->p->tot_len = size;
+  buf->ptr = buf->p;
+  return ERR_OK;
+}
+
+/**
+ * @ingroup netbuf
+ * Chain one netbuf to another (@see pbuf_chain)
+ *
+ * @param head the first netbuf
+ * @param tail netbuf to chain after head, freed by this function, may not be reference after returning
+ */
+void
+netbuf_chain(struct netbuf *head, struct netbuf *tail)
+{
+  LWIP_ERROR("netbuf_chain: invalid head", (head != NULL), return;);
+  LWIP_ERROR("netbuf_chain: invalid tail", (tail != NULL), return;);
+  pbuf_cat(head->p, tail->p);
+  head->ptr = head->p;
+  memp_free(MEMP_NETBUF, tail);
+}
+
+/**
+ * @ingroup netbuf
+ * Get the data pointer and length of the data inside a netbuf.
+ *
+ * @param buf netbuf to get the data from
+ * @param dataptr pointer to a void pointer where to store the data pointer
+ * @param len pointer to an u16_t where the length of the data is stored
+ * @return ERR_OK if the information was retrieved,
+ *         ERR_BUF on error.
+ */
+err_t
+netbuf_data(struct netbuf *buf, void **dataptr, u16_t *len)
+{
+  LWIP_ERROR("netbuf_data: invalid buf", (buf != NULL), return ERR_ARG;);
+  LWIP_ERROR("netbuf_data: invalid dataptr", (dataptr != NULL), return ERR_ARG;);
+  LWIP_ERROR("netbuf_data: invalid len", (len != NULL), return ERR_ARG;);
+
+  if (buf->ptr == NULL) {
+    return ERR_BUF;
+  }
+  *dataptr = buf->ptr->payload;
+  *len = buf->ptr->len;
+  return ERR_OK;
+}
+
+/**
+ * @ingroup netbuf
+ * Move the current data pointer of a packet buffer contained in a netbuf
+ * to the next part.
+ * The packet buffer itself is not modified.
+ *
+ * @param buf the netbuf to modify
+ * @return -1 if there is no next part
+ *         1  if moved to the next part but now there is no next part
+ *         0  if moved to the next part and there are still more parts
+ */
+s8_t
+netbuf_next(struct netbuf *buf)
+{
+  LWIP_ERROR("netbuf_next: invalid buf", (buf != NULL), return -1;);
+  if (buf->ptr->next == NULL) {
+    return -1;
+  }
+  buf->ptr = buf->ptr->next;
+  if (buf->ptr->next == NULL) {
+    return 1;
+  }
+  return 0;
+}
+
+/**
+ * @ingroup netbuf
+ * Move the current data pointer of a packet buffer contained in a netbuf
+ * to the beginning of the packet.
+ * The packet buffer itself is not modified.
+ *
+ * @param buf the netbuf to modify
+ */
+void
+netbuf_first(struct netbuf *buf)
+{
+  LWIP_ERROR("netbuf_first: invalid buf", (buf != NULL), return;);
+  buf->ptr = buf->p;
+}
+
+#endif /* LWIP_NETCONN */
diff --git a/lib/lwip/api/netdb.c b/lib/lwip/api/netdb.c
new file mode 100644
index 0000000000..e07ab04998
--- /dev/null
+++ b/lib/lwip/api/netdb.c
@@ -0,0 +1,422 @@
+/**
+ * @file
+ * API functions for name resolving
+ *
+ * @defgroup netdbapi NETDB API
+ * @ingroup socket
+ */
+
+/*
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt
+ *
+ */
+
+#include "lwip/netdb.h"
+
+#if LWIP_DNS && LWIP_SOCKET
+
+#include "lwip/err.h"
+#include "lwip/mem.h"
+#include "lwip/memp.h"
+#include "lwip/ip_addr.h"
+#include "lwip/api.h"
+#include "lwip/dns.h"
+
+#include <string.h> /* memset */
+#include <stdlib.h> /* atoi */
+
+/** helper struct for gethostbyname_r to access the char* buffer */
+struct gethostbyname_r_helper {
+  ip_addr_t *addr_list[2];
+  ip_addr_t addr;
+  char *aliases;
+};
+
+/** h_errno is exported in netdb.h for access by applications. */
+#if LWIP_DNS_API_DECLARE_H_ERRNO
+int h_errno;
+#endif /* LWIP_DNS_API_DECLARE_H_ERRNO */
+
+/** LWIP_DNS_API_HOSTENT_STORAGE: if set to 0 (default), lwip_gethostbyname()
+ * returns the same global variabe for all calls (in all threads).
+ * When set to 1, your port should provide a function
+ *      struct hostent* sys_thread_hostent( struct hostent* h);
+ * which have to do a copy of "h" and return a pointer ont the "per-thread"
+ * copy.
+ */
+#ifndef LWIP_DNS_API_HOSTENT_STORAGE
+#define LWIP_DNS_API_HOSTENT_STORAGE 0
+#endif
+
+/* define "hostent" variables storage */
+#if LWIP_DNS_API_HOSTENT_STORAGE
+#define HOSTENT_STORAGE
+#else
+#define HOSTENT_STORAGE static
+#endif /* LWIP_DNS_API_STATIC_HOSTENT */
+
+/**
+ * Returns an entry containing addresses of address family AF_INET
+ * for the host with name name.
+ * Due to dns_gethostbyname limitations, only one address is returned.
+ *
+ * @param name the hostname to resolve
+ * @return an entry containing addresses of address family AF_INET
+ *         for the host with name name
+ */
+struct hostent *
+lwip_gethostbyname(const char *name)
+{
+  err_t err;
+  ip_addr_t addr;
+
+  /* buffer variables for lwip_gethostbyname() */
+  HOSTENT_STORAGE struct hostent s_hostent;
+  HOSTENT_STORAGE char *s_aliases;
+  HOSTENT_STORAGE ip_addr_t s_hostent_addr;
+  HOSTENT_STORAGE ip_addr_t *s_phostent_addr[2];
+  HOSTENT_STORAGE char s_hostname[DNS_MAX_NAME_LENGTH + 1];
+
+  /* query host IP address */
+  err = netconn_gethostbyname(name, &addr);
+  if (err != ERR_OK) {
+    LWIP_DEBUGF(DNS_DEBUG, ("lwip_gethostbyname(%s) failed, err=%d\n", name, err));
+    h_errno = HOST_NOT_FOUND;
+    return NULL;
+  }
+
+  /* fill hostent */
+  s_hostent_addr = addr;
+  s_phostent_addr[0] = &s_hostent_addr;
+  s_phostent_addr[1] = NULL;
+  strncpy(s_hostname, name, DNS_MAX_NAME_LENGTH);
+  s_hostname[DNS_MAX_NAME_LENGTH] = 0;
+  s_hostent.h_name = s_hostname;
+  s_aliases = NULL;
+  s_hostent.h_aliases = &s_aliases;
+  s_hostent.h_addrtype = AF_INET;
+  s_hostent.h_length = sizeof(ip_addr_t);
+  s_hostent.h_addr_list = (char **)&s_phostent_addr;
+
+#if DNS_DEBUG
+  /* dump hostent */
+  LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_name           == %s\n", s_hostent.h_name));
+  LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_aliases        == %p\n", (void *)s_hostent.h_aliases));
+  /* h_aliases are always empty */
+  LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addrtype       == %d\n", s_hostent.h_addrtype));
+  LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_length         == %d\n", s_hostent.h_length));
+  LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addr_list      == %p\n", (void *)s_hostent.h_addr_list));
+  if (s_hostent.h_addr_list != NULL) {
+    u8_t idx;
+    for (idx = 0; s_hostent.h_addr_list[idx]; idx++) {
+      LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addr_list[%i]-> == %s\n", idx, ipaddr_ntoa(s_phostent_addr[idx])));
+    }
+  }
+#endif /* DNS_DEBUG */
+
+#if LWIP_DNS_API_HOSTENT_STORAGE
+  /* this function should return the "per-thread" hostent after copy from s_hostent */
+  return sys_thread_hostent(&s_hostent);
+#else
+  return &s_hostent;
+#endif /* LWIP_DNS_API_HOSTENT_STORAGE */
+}
+
+/**
+ * Thread-safe variant of lwip_gethostbyname: instead of using a static
+ * buffer, this function takes buffer and errno pointers as arguments
+ * and uses these for the result.
+ *
+ * @param name the hostname to resolve
+ * @param ret pre-allocated struct where to store the result
+ * @param buf pre-allocated buffer where to store additional data
+ * @param buflen the size of buf
+ * @param result pointer to a hostent pointer that is set to ret on success
+ *               and set to zero on error
+ * @param h_errnop pointer to an int where to store errors (instead of modifying
+ *                 the global h_errno)
+ * @return 0 on success, non-zero on error, additional error information
+ *         is stored in *h_errnop instead of h_errno to be thread-safe
+ */
+int
+lwip_gethostbyname_r(const char *name, struct hostent *ret, char *buf,
+                     size_t buflen, struct hostent **result, int *h_errnop)
+{
+  err_t err;
+  struct gethostbyname_r_helper *h;
+  char *hostname;
+  size_t namelen;
+  int lh_errno;
+
+  if (h_errnop == NULL) {
+    /* ensure h_errnop is never NULL */
+    h_errnop = &lh_errno;
+  }
+
+  if (result == NULL) {
+    /* not all arguments given */
+    *h_errnop = EINVAL;
+    return -1;
+  }
+  /* first thing to do: set *result to nothing */
+  *result = NULL;
+  if ((name == NULL) || (ret == NULL) || (buf == NULL)) {
+    /* not all arguments given */
+    *h_errnop = EINVAL;
+    return -1;
+  }
+
+  namelen = strlen(name);
+  if (buflen < (sizeof(struct gethostbyname_r_helper) + LWIP_MEM_ALIGN_BUFFER(namelen + 1))) {
+    /* buf can't hold the data needed + a copy of name */
+    *h_errnop = ERANGE;
+    return -1;
+  }
+
+  h = (struct gethostbyname_r_helper *)LWIP_MEM_ALIGN(buf);
+  hostname = ((char *)h) + sizeof(struct gethostbyname_r_helper);
+
+  /* query host IP address */
+  err = netconn_gethostbyname(name, &h->addr);
+  if (err != ERR_OK) {
+    LWIP_DEBUGF(DNS_DEBUG, ("lwip_gethostbyname(%s) failed, err=%d\n", name, err));
+    *h_errnop = HOST_NOT_FOUND;
+    return -1;
+  }
+
+  /* copy the hostname into buf */
+  MEMCPY(hostname, name, namelen);
+  hostname[namelen] = 0;
+
+  /* fill hostent */
+  h->addr_list[0] = &h->addr;
+  h->addr_list[1] = NULL;
+  h->aliases = NULL;
+  ret->h_name = hostname;
+  ret->h_aliases = &h->aliases;
+  ret->h_addrtype = AF_INET;
+  ret->h_length = sizeof(ip_addr_t);
+  ret->h_addr_list = (char **)&h->addr_list;
+
+  /* set result != NULL */
+  *result = ret;
+
+  /* return success */
+  return 0;
+}
+
+/**
+ * Frees one or more addrinfo structures returned by getaddrinfo(), along with
+ * any additional storage associated with those structures. If the ai_next field
+ * of the structure is not null, the entire list of structures is freed.
+ *
+ * @param ai struct addrinfo to free
+ */
+void
+lwip_freeaddrinfo(struct addrinfo *ai)
+{
+  struct addrinfo *next;
+
+  while (ai != NULL) {
+    next = ai->ai_next;
+    memp_free(MEMP_NETDB, ai);
+    ai = next;
+  }
+}
+
+/**
+ * Translates the name of a service location (for example, a host name) and/or
+ * a service name and returns a set of socket addresses and associated
+ * information to be used in creating a socket with which to address the
+ * specified service.
+ * Memory for the result is allocated internally and must be freed by calling
+ * lwip_freeaddrinfo()!
+ *
+ * Due to a limitation in dns_gethostbyname, only the first address of a
+ * host is returned.
+ * Also, service names are not supported (only port numbers)!
+ *
+ * @param nodename descriptive name or address string of the host
+ *                 (may be NULL -> local address)
+ * @param servname port number as string of NULL
+ * @param hints structure containing input values that set socktype and protocol
+ * @param res pointer to a pointer where to store the result (set to NULL on failure)
+ * @return 0 on success, non-zero on failure
+ *
+ * @todo: implement AI_V4MAPPED, AI_ADDRCONFIG
+ */
+int
+lwip_getaddrinfo(const char *nodename, const char *servname,
+                 const struct addrinfo *hints, struct addrinfo **res)
+{
+  err_t err;
+  ip_addr_t addr;
+  struct addrinfo *ai;
+  struct sockaddr_storage *sa = NULL;
+  int port_nr = 0;
+  size_t total_size;
+  size_t namelen = 0;
+  int ai_family;
+
+  if (res == NULL) {
+    return EAI_FAIL;
+  }
+  *res = NULL;
+  if ((nodename == NULL) && (servname == NULL)) {
+    return EAI_NONAME;
+  }
+
+  if (hints != NULL) {
+    ai_family = hints->ai_family;
+    if ((ai_family != AF_UNSPEC)
+#if LWIP_IPV4
+        && (ai_family != AF_INET)
+#endif /* LWIP_IPV4 */
+#if LWIP_IPV6
+        && (ai_family != AF_INET6)
+#endif /* LWIP_IPV6 */
+       ) {
+      return EAI_FAMILY;
+    }
+  } else {
+    ai_family = AF_UNSPEC;
+  }
+
+  if (servname != NULL) {
+    /* service name specified: convert to port number
+     * @todo?: currently, only ASCII integers (port numbers) are supported (AI_NUMERICSERV)! */
+    port_nr = atoi(servname);
+    if (port_nr == 0 && (servname[0] != '0')) {
+      /* atoi failed - service was not numeric */
+      return EAI_SERVICE;
+    }
+    if ((port_nr < 0) || (port_nr > 0xffff)) {
+      return EAI_SERVICE;
+    }
+  }
+
+  if (nodename != NULL) {
+    /* service location specified, try to resolve */
+    if ((hints != NULL) && (hints->ai_flags & AI_NUMERICHOST)) {
+      /* no DNS lookup, just parse for an address string */
+      if (!ipaddr_aton(nodename, &addr)) {
+        return EAI_NONAME;
+      }
+#if LWIP_IPV4 && LWIP_IPV6
+      if ((IP_IS_V6_VAL(addr) && ai_family == AF_INET) ||
+          (IP_IS_V4_VAL(addr) && ai_family == AF_INET6)) {
+        return EAI_NONAME;
+      }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+    } else {
+#if LWIP_IPV4 && LWIP_IPV6
+      /* AF_UNSPEC: prefer IPv4 */
+      u8_t type = NETCONN_DNS_IPV4_IPV6;
+      if (ai_family == AF_INET) {
+        type = NETCONN_DNS_IPV4;
+      } else if (ai_family == AF_INET6) {
+        type = NETCONN_DNS_IPV6;
+      }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+      err = netconn_gethostbyname_addrtype(nodename, &addr, type);
+      if (err != ERR_OK) {
+        return EAI_FAIL;
+      }
+    }
+  } else {
+    /* service location specified, use loopback address */
+    if ((hints != NULL) && (hints->ai_flags & AI_PASSIVE)) {
+      ip_addr_set_any_val(ai_family == AF_INET6, addr);
+    } else {
+      ip_addr_set_loopback_val(ai_family == AF_INET6, addr);
+    }
+  }
+
+  total_size = sizeof(struct addrinfo) + sizeof(struct sockaddr_storage);
+  if (nodename != NULL) {
+    namelen = strlen(nodename);
+    if (namelen > DNS_MAX_NAME_LENGTH) {
+      /* invalid name length */
+      return EAI_FAIL;
+    }
+    LWIP_ASSERT("namelen is too long", total_size + namelen + 1 > total_size);
+    total_size += namelen + 1;
+  }
+  /* If this fails, please report to lwip-devel! :-) */
+  LWIP_ASSERT("total_size <= NETDB_ELEM_SIZE: please report this!",
+              total_size <= NETDB_ELEM_SIZE);
+  ai = (struct addrinfo *)memp_malloc(MEMP_NETDB);
+  if (ai == NULL) {
+    return EAI_MEMORY;
+  }
+  memset(ai, 0, total_size);
+  /* cast through void* to get rid of alignment warnings */
+  sa = (struct sockaddr_storage *)(void *)((u8_t *)ai + sizeof(struct addrinfo));
+  if (IP_IS_V6_VAL(addr)) {
+#if LWIP_IPV6
+    struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)sa;
+    /* set up sockaddr */
+    inet6_addr_from_ip6addr(&sa6->sin6_addr, ip_2_ip6(&addr));
+    sa6->sin6_family = AF_INET6;
+    sa6->sin6_len = sizeof(struct sockaddr_in6);
+    sa6->sin6_port = lwip_htons((u16_t)port_nr);
+    sa6->sin6_scope_id = ip6_addr_zone(ip_2_ip6(&addr));
+    ai->ai_family = AF_INET6;
+#endif /* LWIP_IPV6 */
+  } else {
+#if LWIP_IPV4
+    struct sockaddr_in *sa4 = (struct sockaddr_in *)sa;
+    /* set up sockaddr */
+    inet_addr_from_ip4addr(&sa4->sin_addr, ip_2_ip4(&addr));
+    sa4->sin_family = AF_INET;
+    sa4->sin_len = sizeof(struct sockaddr_in);
+    sa4->sin_port = lwip_htons((u16_t)port_nr);
+    ai->ai_family = AF_INET;
+#endif /* LWIP_IPV4 */
+  }
+
+  /* set up addrinfo */
+  if (hints != NULL) {
+    /* copy socktype & protocol from hints if specified */
+    ai->ai_socktype = hints->ai_socktype;
+    ai->ai_protocol = hints->ai_protocol;
+  }
+  if (nodename != NULL) {
+    /* copy nodename to canonname if specified */
+    ai->ai_canonname = ((char *)ai + sizeof(struct addrinfo) + sizeof(struct sockaddr_storage));
+    MEMCPY(ai->ai_canonname, nodename, namelen);
+    ai->ai_canonname[namelen] = 0;
+  }
+  ai->ai_addrlen = sizeof(struct sockaddr_storage);
+  ai->ai_addr = (struct sockaddr *)sa;
+
+  *res = ai;
+
+  return 0;
+}
+
+#endif /* LWIP_DNS && LWIP_SOCKET */
diff --git a/lib/lwip/api/netifapi.c b/lib/lwip/api/netifapi.c
new file mode 100644
index 0000000000..25957cd5c3
--- /dev/null
+++ b/lib/lwip/api/netifapi.c
@@ -0,0 +1,380 @@
+/**
+ * @file
+ * Network Interface Sequential API module
+ *
+ * @defgroup netifapi NETIF API
+ * @ingroup sequential_api
+ * Thread-safe functions to be called from non-TCPIP threads
+ *
+ * @defgroup netifapi_netif NETIF related
+ * @ingroup netifapi
+ * To be called from non-TCPIP threads
+ */
+
+/*
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_NETIF_API /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/etharp.h"
+#include "lwip/netifapi.h"
+#include "lwip/memp.h"
+#include "lwip/priv/tcpip_priv.h"
+
+#include <string.h> /* strncpy */
+
+#define NETIFAPI_VAR_REF(name)      API_VAR_REF(name)
+#define NETIFAPI_VAR_DECLARE(name)  API_VAR_DECLARE(struct netifapi_msg, name)
+#define NETIFAPI_VAR_ALLOC(name)    API_VAR_ALLOC(struct netifapi_msg, MEMP_NETIFAPI_MSG, name, ERR_MEM)
+#define NETIFAPI_VAR_FREE(name)     API_VAR_FREE(MEMP_NETIFAPI_MSG, name)
+
+/**
+ * Call netif_add() inside the tcpip_thread context.
+ */
+static err_t
+netifapi_do_netif_add(struct tcpip_api_call_data *m)
+{
+  /* cast through void* to silence alignment warnings.
+   * We know it works because the structs have been instantiated as struct netifapi_msg */
+  struct netifapi_msg *msg = (struct netifapi_msg *)(void *)m;
+
+  if (!netif_add( msg->netif,
+#if LWIP_IPV4
+                  API_EXPR_REF(msg->msg.add.ipaddr),
+                  API_EXPR_REF(msg->msg.add.netmask),
+                  API_EXPR_REF(msg->msg.add.gw),
+#endif /* LWIP_IPV4 */
+                  msg->msg.add.state,
+                  msg->msg.add.init,
+                  msg->msg.add.input)) {
+    return ERR_IF;
+  } else {
+    return ERR_OK;
+  }
+}
+
+#if LWIP_IPV4
+/**
+ * Call netif_set_addr() inside the tcpip_thread context.
+ */
+static err_t
+netifapi_do_netif_set_addr(struct tcpip_api_call_data *m)
+{
+  /* cast through void* to silence alignment warnings.
+   * We know it works because the structs have been instantiated as struct netifapi_msg */
+  struct netifapi_msg *msg = (struct netifapi_msg *)(void *)m;
+
+  netif_set_addr( msg->netif,
+                  API_EXPR_REF(msg->msg.add.ipaddr),
+                  API_EXPR_REF(msg->msg.add.netmask),
+                  API_EXPR_REF(msg->msg.add.gw));
+  return ERR_OK;
+}
+#endif /* LWIP_IPV4 */
+
+/**
+* Call netif_name_to_index() inside the tcpip_thread context.
+*/
+static err_t
+netifapi_do_name_to_index(struct tcpip_api_call_data *m)
+{
+  /* cast through void* to silence alignment warnings.
+   * We know it works because the structs have been instantiated as struct netifapi_msg */
+  struct netifapi_msg *msg = (struct netifapi_msg *)(void *)m;
+
+  msg->msg.ifs.index = netif_name_to_index(msg->msg.ifs.name);
+  return ERR_OK;
+}
+
+/**
+* Call netif_index_to_name() inside the tcpip_thread context.
+*/
+static err_t
+netifapi_do_index_to_name(struct tcpip_api_call_data *m)
+{
+  /* cast through void* to silence alignment warnings.
+   * We know it works because the structs have been instantiated as struct netifapi_msg */
+  struct netifapi_msg *msg = (struct netifapi_msg *)(void *)m;
+
+  if (!netif_index_to_name(msg->msg.ifs.index, msg->msg.ifs.name)) {
+    /* return failure via empty name */
+    msg->msg.ifs.name[0] = '\0';
+  }
+  return ERR_OK;
+}
+
+/**
+ * Call the "errtfunc" (or the "voidfunc" if "errtfunc" is NULL) inside the
+ * tcpip_thread context.
+ */
+static err_t
+netifapi_do_netif_common(struct tcpip_api_call_data *m)
+{
+  /* cast through void* to silence alignment warnings.
+   * We know it works because the structs have been instantiated as struct netifapi_msg */
+  struct netifapi_msg *msg = (struct netifapi_msg *)(void *)m;
+
+  if (msg->msg.common.errtfunc != NULL) {
+    return msg->msg.common.errtfunc(msg->netif);
+  } else {
+    msg->msg.common.voidfunc(msg->netif);
+    return ERR_OK;
+  }
+}
+
+#if LWIP_ARP && LWIP_IPV4
+/**
+ * @ingroup netifapi_arp
+ * Add or update an entry in the ARP cache.
+ * For an update, ipaddr is used to find the cache entry.
+ *
+ * @param ipaddr IPv4 address of cache entry
+ * @param ethaddr hardware address mapped to ipaddr
+ * @param type type of ARP cache entry
+ * @return ERR_OK: entry added/updated, else error from err_t
+ */
+err_t
+netifapi_arp_add(const ip4_addr_t *ipaddr, struct eth_addr *ethaddr, enum netifapi_arp_entry type)
+{
+  err_t err;
+
+  /* We only support permanent entries currently */
+  LWIP_UNUSED_ARG(type);
+
+#if ETHARP_SUPPORT_STATIC_ENTRIES && LWIP_TCPIP_CORE_LOCKING
+  LOCK_TCPIP_CORE();
+  err = etharp_add_static_entry(ipaddr, ethaddr);
+  UNLOCK_TCPIP_CORE();
+#else
+  /* @todo add new vars to struct netifapi_msg and create a 'do' func */
+  LWIP_UNUSED_ARG(ipaddr);
+  LWIP_UNUSED_ARG(ethaddr);
+  err = ERR_VAL;
+#endif /* ETHARP_SUPPORT_STATIC_ENTRIES && LWIP_TCPIP_CORE_LOCKING */
+
+  return err;
+}
+
+/**
+ * @ingroup netifapi_arp
+ * Remove an entry in the ARP cache identified by ipaddr
+ *
+ * @param ipaddr IPv4 address of cache entry
+ * @param type type of ARP cache entry
+ * @return ERR_OK: entry removed, else error from err_t
+ */
+err_t
+netifapi_arp_remove(const ip4_addr_t *ipaddr, enum netifapi_arp_entry type)
+{
+  err_t err;
+
+  /* We only support permanent entries currently */
+  LWIP_UNUSED_ARG(type);
+
+#if ETHARP_SUPPORT_STATIC_ENTRIES && LWIP_TCPIP_CORE_LOCKING
+  LOCK_TCPIP_CORE();
+  err = etharp_remove_static_entry(ipaddr);
+  UNLOCK_TCPIP_CORE();
+#else
+  /* @todo add new vars to struct netifapi_msg and create a 'do' func */
+  LWIP_UNUSED_ARG(ipaddr);
+  err = ERR_VAL;
+#endif /* ETHARP_SUPPORT_STATIC_ENTRIES && LWIP_TCPIP_CORE_LOCKING */
+
+  return err;
+}
+#endif /* LWIP_ARP && LWIP_IPV4 */
+
+/**
+ * @ingroup netifapi_netif
+ * Call netif_add() in a thread-safe way by running that function inside the
+ * tcpip_thread context.
+ *
+ * @note for params @see netif_add()
+ */
+err_t
+netifapi_netif_add(struct netif *netif,
+#if LWIP_IPV4
+                   const ip4_addr_t *ipaddr, const ip4_addr_t *netmask, const ip4_addr_t *gw,
+#endif /* LWIP_IPV4 */
+                   void *state, netif_init_fn init, netif_input_fn input)
+{
+  err_t err;
+  NETIFAPI_VAR_DECLARE(msg);
+  NETIFAPI_VAR_ALLOC(msg);
+
+#if LWIP_IPV4
+  if (ipaddr == NULL) {
+    ipaddr = IP4_ADDR_ANY4;
+  }
+  if (netmask == NULL) {
+    netmask = IP4_ADDR_ANY4;
+  }
+  if (gw == NULL) {
+    gw = IP4_ADDR_ANY4;
+  }
+#endif /* LWIP_IPV4 */
+
+  NETIFAPI_VAR_REF(msg).netif = netif;
+#if LWIP_IPV4
+  NETIFAPI_VAR_REF(msg).msg.add.ipaddr  = NETIFAPI_VAR_REF(ipaddr);
+  NETIFAPI_VAR_REF(msg).msg.add.netmask = NETIFAPI_VAR_REF(netmask);
+  NETIFAPI_VAR_REF(msg).msg.add.gw      = NETIFAPI_VAR_REF(gw);
+#endif /* LWIP_IPV4 */
+  NETIFAPI_VAR_REF(msg).msg.add.state   = state;
+  NETIFAPI_VAR_REF(msg).msg.add.init    = init;
+  NETIFAPI_VAR_REF(msg).msg.add.input   = input;
+  err = tcpip_api_call(netifapi_do_netif_add, &API_VAR_REF(msg).call);
+  NETIFAPI_VAR_FREE(msg);
+  return err;
+}
+
+#if LWIP_IPV4
+/**
+ * @ingroup netifapi_netif
+ * Call netif_set_addr() in a thread-safe way by running that function inside the
+ * tcpip_thread context.
+ *
+ * @note for params @see netif_set_addr()
+ */
+err_t
+netifapi_netif_set_addr(struct netif *netif,
+                        const ip4_addr_t *ipaddr,
+                        const ip4_addr_t *netmask,
+                        const ip4_addr_t *gw)
+{
+  err_t err;
+  NETIFAPI_VAR_DECLARE(msg);
+  NETIFAPI_VAR_ALLOC(msg);
+
+  if (ipaddr == NULL) {
+    ipaddr = IP4_ADDR_ANY4;
+  }
+  if (netmask == NULL) {
+    netmask = IP4_ADDR_ANY4;
+  }
+  if (gw == NULL) {
+    gw = IP4_ADDR_ANY4;
+  }
+
+  NETIFAPI_VAR_REF(msg).netif = netif;
+  NETIFAPI_VAR_REF(msg).msg.add.ipaddr  = NETIFAPI_VAR_REF(ipaddr);
+  NETIFAPI_VAR_REF(msg).msg.add.netmask = NETIFAPI_VAR_REF(netmask);
+  NETIFAPI_VAR_REF(msg).msg.add.gw      = NETIFAPI_VAR_REF(gw);
+  err = tcpip_api_call(netifapi_do_netif_set_addr, &API_VAR_REF(msg).call);
+  NETIFAPI_VAR_FREE(msg);
+  return err;
+}
+#endif /* LWIP_IPV4 */
+
+/**
+ * call the "errtfunc" (or the "voidfunc" if "errtfunc" is NULL) in a thread-safe
+ * way by running that function inside the tcpip_thread context.
+ *
+ * @note use only for functions where there is only "netif" parameter.
+ */
+err_t
+netifapi_netif_common(struct netif *netif, netifapi_void_fn voidfunc,
+                      netifapi_errt_fn errtfunc)
+{
+  err_t err;
+  NETIFAPI_VAR_DECLARE(msg);
+  NETIFAPI_VAR_ALLOC(msg);
+
+  NETIFAPI_VAR_REF(msg).netif = netif;
+  NETIFAPI_VAR_REF(msg).msg.common.voidfunc = voidfunc;
+  NETIFAPI_VAR_REF(msg).msg.common.errtfunc = errtfunc;
+  err = tcpip_api_call(netifapi_do_netif_common, &API_VAR_REF(msg).call);
+  NETIFAPI_VAR_FREE(msg);
+  return err;
+}
+
+/**
+* @ingroup netifapi_netif
+* Call netif_name_to_index() in a thread-safe way by running that function inside the
+* tcpip_thread context.
+*
+* @param name the interface name of the netif
+* @param idx output index of the found netif
+*/
+err_t
+netifapi_netif_name_to_index(const char *name, u8_t *idx)
+{
+  err_t err;
+  NETIFAPI_VAR_DECLARE(msg);
+  NETIFAPI_VAR_ALLOC(msg);
+
+  *idx = 0;
+
+#if LWIP_MPU_COMPATIBLE
+  strncpy(NETIFAPI_VAR_REF(msg).msg.ifs.name, name, NETIF_NAMESIZE - 1);
+  NETIFAPI_VAR_REF(msg).msg.ifs.name[NETIF_NAMESIZE - 1] = '\0';
+#else
+  NETIFAPI_VAR_REF(msg).msg.ifs.name = LWIP_CONST_CAST(char *, name);
+#endif /* LWIP_MPU_COMPATIBLE */
+  err = tcpip_api_call(netifapi_do_name_to_index, &API_VAR_REF(msg).call);
+  if (!err) {
+    *idx = NETIFAPI_VAR_REF(msg).msg.ifs.index;
+  }
+  NETIFAPI_VAR_FREE(msg);
+  return err;
+}
+
+/**
+* @ingroup netifapi_netif
+* Call netif_index_to_name() in a thread-safe way by running that function inside the
+* tcpip_thread context.
+*
+* @param idx the interface index of the netif
+* @param name output name of the found netif, empty '\0' string if netif not found.
+*             name should be of at least NETIF_NAMESIZE bytes
+*/
+err_t
+netifapi_netif_index_to_name(u8_t idx, char *name)
+{
+  err_t err;
+  NETIFAPI_VAR_DECLARE(msg);
+  NETIFAPI_VAR_ALLOC(msg);
+
+  NETIFAPI_VAR_REF(msg).msg.ifs.index = idx;
+#if !LWIP_MPU_COMPATIBLE
+  NETIFAPI_VAR_REF(msg).msg.ifs.name = name;
+#endif /* LWIP_MPU_COMPATIBLE */
+  err = tcpip_api_call(netifapi_do_index_to_name, &API_VAR_REF(msg).call);
+#if LWIP_MPU_COMPATIBLE
+  if (!err) {
+    strncpy(name, NETIFAPI_VAR_REF(msg).msg.ifs.name, NETIF_NAMESIZE - 1);
+    name[NETIF_NAMESIZE - 1] = '\0';
+  }
+#endif /* LWIP_MPU_COMPATIBLE */
+  NETIFAPI_VAR_FREE(msg);
+  return err;
+}
+
+#endif /* LWIP_NETIF_API */
diff --git a/lib/lwip/api/sockets.c b/lib/lwip/api/sockets.c
new file mode 100644
index 0000000000..b9111943b6
--- /dev/null
+++ b/lib/lwip/api/sockets.c
@@ -0,0 +1,4205 @@
+/**
+ * @file
+ * Sockets BSD-Like API module
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ * Improved by Marc Boucher <marc@mbsi.ca> and David Haas <dhaas@alum.rpi.edu>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/sockets.h"
+#include "lwip/priv/sockets_priv.h"
+#include "lwip/api.h"
+#include "lwip/igmp.h"
+#include "lwip/inet.h"
+#include "lwip/tcp.h"
+#include "lwip/raw.h"
+#include "lwip/udp.h"
+#include "lwip/memp.h"
+#include "lwip/pbuf.h"
+#include "lwip/netif.h"
+#include "lwip/priv/tcpip_priv.h"
+#include "lwip/mld6.h"
+#if LWIP_CHECKSUM_ON_COPY
+#include "lwip/inet_chksum.h"
+#endif
+
+#if LWIP_COMPAT_SOCKETS == 2 && LWIP_POSIX_SOCKETS_IO_NAMES
+#include <stdarg.h>
+#endif
+
+#include <string.h>
+
+#ifdef LWIP_HOOK_FILENAME
+#include LWIP_HOOK_FILENAME
+#endif
+
+/* If the netconn API is not required publicly, then we include the necessary
+   files here to get the implementation */
+#if !LWIP_NETCONN
+#undef LWIP_NETCONN
+#define LWIP_NETCONN 1
+#include "api_msg.c"
+#include "api_lib.c"
+#include "netbuf.c"
+#undef LWIP_NETCONN
+#define LWIP_NETCONN 0
+#endif
+
+#define API_SELECT_CB_VAR_REF(name)               API_VAR_REF(name)
+#define API_SELECT_CB_VAR_DECLARE(name)           API_VAR_DECLARE(struct lwip_select_cb, name)
+#define API_SELECT_CB_VAR_ALLOC(name, retblock)   API_VAR_ALLOC_EXT(struct lwip_select_cb, MEMP_SELECT_CB, name, retblock)
+#define API_SELECT_CB_VAR_FREE(name)              API_VAR_FREE(MEMP_SELECT_CB, name)
+
+#ifndef LWIP_SOCKET_HAVE_SA_LEN
+#define LWIP_SOCKET_HAVE_SA_LEN 0
+#endif /* LWIP_SOCKET_HAVE_SA_LEN */
+
+/* Address length safe read and write */
+#if LWIP_SOCKET_HAVE_SA_LEN
+
+#if LWIP_IPV4
+#define IP4ADDR_SOCKADDR_SET_LEN(sin) \
+      (sin)->sin_len = sizeof(struct sockaddr_in)
+#endif /* LWIP_IPV4 */
+
+#if LWIP_IPV6
+#define IP6ADDR_SOCKADDR_SET_LEN(sin6) \
+      (sin6)->sin6_len = sizeof(struct sockaddr_in6)
+#endif /* LWIP_IPV6 */
+
+#define IPADDR_SOCKADDR_GET_LEN(addr) \
+      (addr)->sa.sa_len
+
+#else
+
+#if LWIP_IPV4
+#define IP4ADDR_SOCKADDR_SET_LEN(addr)
+#endif /* LWIP_IPV4 */
+
+#if LWIP_IPV6
+#define IP6ADDR_SOCKADDR_SET_LEN(addr)
+#endif /* LWIP_IPV6 */
+
+#if LWIP_IPV4 && LWIP_IPV6
+#define IPADDR_SOCKADDR_GET_LEN(addr) \
+      ((addr)->sa.sa_family == AF_INET ? sizeof(struct sockaddr_in) \
+        : ((addr)->sa.sa_family == AF_INET6 ? sizeof(struct sockaddr_in6) : 0))
+#elif LWIP_IPV4
+#define IPADDR_SOCKADDR_GET_LEN(addr) sizeof(struct sockaddr_in)
+#elif LWIP_IPV6
+#define IPADDR_SOCKADDR_GET_LEN(addr) sizeof(struct sockaddr_in6)
+#else
+#define IPADDR_SOCKADDR_GET_LEN(addr) sizeof(struct sockaddr)
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
+#endif /* LWIP_SOCKET_HAVE_SA_LEN */
+
+#if LWIP_IPV4
+#define IP4ADDR_PORT_TO_SOCKADDR(sin, ipaddr, port) do { \
+      IP4ADDR_SOCKADDR_SET_LEN(sin); \
+      (sin)->sin_family = AF_INET; \
+      (sin)->sin_port = lwip_htons((port)); \
+      inet_addr_from_ip4addr(&(sin)->sin_addr, ipaddr); \
+      memset((sin)->sin_zero, 0, SIN_ZERO_LEN); }while(0)
+#define SOCKADDR4_TO_IP4ADDR_PORT(sin, ipaddr, port) do { \
+    inet_addr_to_ip4addr(ip_2_ip4(ipaddr), &((sin)->sin_addr)); \
+    (port) = lwip_ntohs((sin)->sin_port); }while(0)
+#endif /* LWIP_IPV4 */
+
+#if LWIP_IPV6
+#define IP6ADDR_PORT_TO_SOCKADDR(sin6, ipaddr, port) do { \
+      IP6ADDR_SOCKADDR_SET_LEN(sin6); \
+      (sin6)->sin6_family = AF_INET6; \
+      (sin6)->sin6_port = lwip_htons((port)); \
+      (sin6)->sin6_flowinfo = 0; \
+      inet6_addr_from_ip6addr(&(sin6)->sin6_addr, ipaddr); \
+      (sin6)->sin6_scope_id = ip6_addr_zone(ipaddr); }while(0)
+#define SOCKADDR6_TO_IP6ADDR_PORT(sin6, ipaddr, port) do { \
+    inet6_addr_to_ip6addr(ip_2_ip6(ipaddr), &((sin6)->sin6_addr)); \
+    if (ip6_addr_has_scope(ip_2_ip6(ipaddr), IP6_UNKNOWN)) { \
+      ip6_addr_set_zone(ip_2_ip6(ipaddr), (u8_t)((sin6)->sin6_scope_id)); \
+    } \
+    (port) = lwip_ntohs((sin6)->sin6_port); }while(0)
+#endif /* LWIP_IPV6 */
+
+#if LWIP_IPV4 && LWIP_IPV6
+static void sockaddr_to_ipaddr_port(const struct sockaddr *sockaddr, ip_addr_t *ipaddr, u16_t *port);
+
+#define IS_SOCK_ADDR_LEN_VALID(namelen)  (((namelen) == sizeof(struct sockaddr_in)) || \
+                                         ((namelen) == sizeof(struct sockaddr_in6)))
+#define IS_SOCK_ADDR_TYPE_VALID(name)    (((name)->sa_family == AF_INET) || \
+                                         ((name)->sa_family == AF_INET6))
+#define SOCK_ADDR_TYPE_MATCH(name, sock) \
+       ((((name)->sa_family == AF_INET) && !(NETCONNTYPE_ISIPV6((sock)->conn->type))) || \
+       (((name)->sa_family == AF_INET6) && (NETCONNTYPE_ISIPV6((sock)->conn->type))))
+#define IPADDR_PORT_TO_SOCKADDR(sockaddr, ipaddr, port) do { \
+    if (IP_IS_ANY_TYPE_VAL(*ipaddr) || IP_IS_V6_VAL(*ipaddr)) { \
+      IP6ADDR_PORT_TO_SOCKADDR((struct sockaddr_in6*)(void*)(sockaddr), ip_2_ip6(ipaddr), port); \
+    } else { \
+      IP4ADDR_PORT_TO_SOCKADDR((struct sockaddr_in*)(void*)(sockaddr), ip_2_ip4(ipaddr), port); \
+    } } while(0)
+#define SOCKADDR_TO_IPADDR_PORT(sockaddr, ipaddr, port) sockaddr_to_ipaddr_port(sockaddr, ipaddr, &(port))
+#define DOMAIN_TO_NETCONN_TYPE(domain, type) (((domain) == AF_INET) ? \
+  (type) : (enum netconn_type)((type) | NETCONN_TYPE_IPV6))
+#elif LWIP_IPV6 /* LWIP_IPV4 && LWIP_IPV6 */
+#define IS_SOCK_ADDR_LEN_VALID(namelen)  ((namelen) == sizeof(struct sockaddr_in6))
+#define IS_SOCK_ADDR_TYPE_VALID(name)    ((name)->sa_family == AF_INET6)
+#define SOCK_ADDR_TYPE_MATCH(name, sock) 1
+#define IPADDR_PORT_TO_SOCKADDR(sockaddr, ipaddr, port) \
+        IP6ADDR_PORT_TO_SOCKADDR((struct sockaddr_in6*)(void*)(sockaddr), ip_2_ip6(ipaddr), port)
+#define SOCKADDR_TO_IPADDR_PORT(sockaddr, ipaddr, port) \
+        SOCKADDR6_TO_IP6ADDR_PORT((const struct sockaddr_in6*)(const void*)(sockaddr), ipaddr, port)
+#define DOMAIN_TO_NETCONN_TYPE(domain, netconn_type) (netconn_type)
+#else /*-> LWIP_IPV4: LWIP_IPV4 && LWIP_IPV6 */
+#define IS_SOCK_ADDR_LEN_VALID(namelen)  ((namelen) == sizeof(struct sockaddr_in))
+#define IS_SOCK_ADDR_TYPE_VALID(name)    ((name)->sa_family == AF_INET)
+#define SOCK_ADDR_TYPE_MATCH(name, sock) 1
+#define IPADDR_PORT_TO_SOCKADDR(sockaddr, ipaddr, port) \
+        IP4ADDR_PORT_TO_SOCKADDR((struct sockaddr_in*)(void*)(sockaddr), ip_2_ip4(ipaddr), port)
+#define SOCKADDR_TO_IPADDR_PORT(sockaddr, ipaddr, port) \
+        SOCKADDR4_TO_IP4ADDR_PORT((const struct sockaddr_in*)(const void*)(sockaddr), ipaddr, port)
+#define DOMAIN_TO_NETCONN_TYPE(domain, netconn_type) (netconn_type)
+#endif /* LWIP_IPV6 */
+
+#define IS_SOCK_ADDR_TYPE_VALID_OR_UNSPEC(name)    (((name)->sa_family == AF_UNSPEC) || \
+                                                    IS_SOCK_ADDR_TYPE_VALID(name))
+#define SOCK_ADDR_TYPE_MATCH_OR_UNSPEC(name, sock) (((name)->sa_family == AF_UNSPEC) || \
+                                                    SOCK_ADDR_TYPE_MATCH(name, sock))
+#define IS_SOCK_ADDR_ALIGNED(name)      ((((mem_ptr_t)(name)) % LWIP_MIN(4, MEM_ALIGNMENT)) == 0)
+
+
+#define LWIP_SOCKOPT_CHECK_OPTLEN(sock, optlen, opttype) do { if ((optlen) < sizeof(opttype)) { done_socket(sock); return EINVAL; }}while(0)
+#define LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, opttype) do { \
+  LWIP_SOCKOPT_CHECK_OPTLEN(sock, optlen, opttype); \
+  if ((sock)->conn == NULL) { done_socket(sock); return EINVAL; } }while(0)
+#define LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, opttype) do { \
+  LWIP_SOCKOPT_CHECK_OPTLEN(sock, optlen, opttype); \
+  if (((sock)->conn == NULL) || ((sock)->conn->pcb.tcp == NULL)) { done_socket(sock); return EINVAL; } }while(0)
+#define LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, opttype, netconntype) do { \
+  LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, opttype); \
+  if (NETCONNTYPE_GROUP(netconn_type((sock)->conn)) != netconntype) { done_socket(sock); return ENOPROTOOPT; } }while(0)
+
+
+#define LWIP_SETGETSOCKOPT_DATA_VAR_REF(name)     API_VAR_REF(name)
+#define LWIP_SETGETSOCKOPT_DATA_VAR_DECLARE(name) API_VAR_DECLARE(struct lwip_setgetsockopt_data, name)
+#define LWIP_SETGETSOCKOPT_DATA_VAR_FREE(name)    API_VAR_FREE(MEMP_SOCKET_SETGETSOCKOPT_DATA, name)
+#if LWIP_MPU_COMPATIBLE
+#define LWIP_SETGETSOCKOPT_DATA_VAR_ALLOC(name, sock) do { \
+  name = (struct lwip_setgetsockopt_data *)memp_malloc(MEMP_SOCKET_SETGETSOCKOPT_DATA); \
+  if (name == NULL) { \
+    set_errno(ENOMEM); \
+    done_socket(sock); \
+    return -1; \
+  } }while(0)
+#else /* LWIP_MPU_COMPATIBLE */
+#define LWIP_SETGETSOCKOPT_DATA_VAR_ALLOC(name, sock)
+#endif /* LWIP_MPU_COMPATIBLE */
+
+#if LWIP_SO_SNDRCVTIMEO_NONSTANDARD
+#define LWIP_SO_SNDRCVTIMEO_OPTTYPE int
+#define LWIP_SO_SNDRCVTIMEO_SET(optval, val) (*(int *)(optval) = (val))
+#define LWIP_SO_SNDRCVTIMEO_GET_MS(optval)   ((long)*(const int*)(optval))
+#else
+#define LWIP_SO_SNDRCVTIMEO_OPTTYPE struct timeval
+#define LWIP_SO_SNDRCVTIMEO_SET(optval, val)  do { \
+  u32_t loc = (val); \
+  ((struct timeval *)(optval))->tv_sec = (long)((loc) / 1000U); \
+  ((struct timeval *)(optval))->tv_usec = (long)(((loc) % 1000U) * 1000U); }while(0)
+#define LWIP_SO_SNDRCVTIMEO_GET_MS(optval) ((((const struct timeval *)(optval))->tv_sec * 1000) + (((const struct timeval *)(optval))->tv_usec / 1000))
+#endif
+
+
+/** A struct sockaddr replacement that has the same alignment as sockaddr_in/
+ *  sockaddr_in6 if instantiated.
+ */
+union sockaddr_aligned {
+  struct sockaddr sa;
+#if LWIP_IPV6
+  struct sockaddr_in6 sin6;
+#endif /* LWIP_IPV6 */
+#if LWIP_IPV4
+  struct sockaddr_in sin;
+#endif /* LWIP_IPV4 */
+};
+
+/* Define the number of IPv4 multicast memberships, default is one per socket */
+#ifndef LWIP_SOCKET_MAX_MEMBERSHIPS
+#define LWIP_SOCKET_MAX_MEMBERSHIPS NUM_SOCKETS
+#endif
+
+#if LWIP_IGMP
+/* This is to keep track of IP_ADD_MEMBERSHIP calls to drop the membership when
+   a socket is closed */
+struct lwip_socket_multicast_pair {
+  /** the socket */
+  struct lwip_sock *sock;
+  /** the interface address */
+  ip4_addr_t if_addr;
+  /** the group address */
+  ip4_addr_t multi_addr;
+};
+
+static struct lwip_socket_multicast_pair socket_ipv4_multicast_memberships[LWIP_SOCKET_MAX_MEMBERSHIPS];
+
+static int  lwip_socket_register_membership(int s, const ip4_addr_t *if_addr, const ip4_addr_t *multi_addr);
+static void lwip_socket_unregister_membership(int s, const ip4_addr_t *if_addr, const ip4_addr_t *multi_addr);
+static void lwip_socket_drop_registered_memberships(int s);
+#endif /* LWIP_IGMP */
+
+#if LWIP_IPV6_MLD
+/* This is to keep track of IP_JOIN_GROUP calls to drop the membership when
+   a socket is closed */
+struct lwip_socket_multicast_mld6_pair {
+  /** the socket */
+  struct lwip_sock *sock;
+  /** the interface index */
+  u8_t if_idx;
+  /** the group address */
+  ip6_addr_t multi_addr;
+};
+
+static struct lwip_socket_multicast_mld6_pair socket_ipv6_multicast_memberships[LWIP_SOCKET_MAX_MEMBERSHIPS];
+
+static int  lwip_socket_register_mld6_membership(int s, unsigned int if_idx, const ip6_addr_t *multi_addr);
+static void lwip_socket_unregister_mld6_membership(int s, unsigned int if_idx, const ip6_addr_t *multi_addr);
+static void lwip_socket_drop_registered_mld6_memberships(int s);
+#endif /* LWIP_IPV6_MLD */
+
+/** The global array of available sockets */
+static struct lwip_sock sockets[NUM_SOCKETS];
+
+#if LWIP_SOCKET_SELECT || LWIP_SOCKET_POLL
+#if LWIP_TCPIP_CORE_LOCKING
+/* protect the select_cb_list using core lock */
+#define LWIP_SOCKET_SELECT_DECL_PROTECT(lev)
+#define LWIP_SOCKET_SELECT_PROTECT(lev)   LOCK_TCPIP_CORE()
+#define LWIP_SOCKET_SELECT_UNPROTECT(lev) UNLOCK_TCPIP_CORE()
+#else /* LWIP_TCPIP_CORE_LOCKING */
+/* protect the select_cb_list using SYS_LIGHTWEIGHT_PROT */
+#define LWIP_SOCKET_SELECT_DECL_PROTECT(lev)  SYS_ARCH_DECL_PROTECT(lev)
+#define LWIP_SOCKET_SELECT_PROTECT(lev)       SYS_ARCH_PROTECT(lev)
+#define LWIP_SOCKET_SELECT_UNPROTECT(lev)     SYS_ARCH_UNPROTECT(lev)
+/** This counter is increased from lwip_select when the list is changed
+    and checked in select_check_waiters to see if it has changed. */
+static volatile int select_cb_ctr;
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+/** The global list of tasks waiting for select */
+static struct lwip_select_cb *select_cb_list;
+#endif /* LWIP_SOCKET_SELECT || LWIP_SOCKET_POLL */
+
+/* Forward declaration of some functions */
+#if LWIP_SOCKET_SELECT || LWIP_SOCKET_POLL
+static void event_callback(struct netconn *conn, enum netconn_evt evt, u16_t len);
+#define DEFAULT_SOCKET_EVENTCB event_callback
+static void select_check_waiters(int s, int has_recvevent, int has_sendevent, int has_errevent);
+#else
+#define DEFAULT_SOCKET_EVENTCB NULL
+#endif
+#if !LWIP_TCPIP_CORE_LOCKING
+static void lwip_getsockopt_callback(void *arg);
+static void lwip_setsockopt_callback(void *arg);
+#endif
+static int lwip_getsockopt_impl(int s, int level, int optname, void *optval, socklen_t *optlen);
+static int lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_t optlen);
+static int free_socket_locked(struct lwip_sock *sock, int is_tcp, struct netconn **conn,
+                              union lwip_sock_lastdata *lastdata);
+static void free_socket_free_elements(int is_tcp, struct netconn *conn, union lwip_sock_lastdata *lastdata);
+
+#if LWIP_IPV4 && LWIP_IPV6
+static void
+sockaddr_to_ipaddr_port(const struct sockaddr *sockaddr, ip_addr_t *ipaddr, u16_t *port)
+{
+  if ((sockaddr->sa_family) == AF_INET6) {
+    SOCKADDR6_TO_IP6ADDR_PORT((const struct sockaddr_in6 *)(const void *)(sockaddr), ipaddr, *port);
+    ipaddr->type = IPADDR_TYPE_V6;
+  } else {
+    SOCKADDR4_TO_IP4ADDR_PORT((const struct sockaddr_in *)(const void *)(sockaddr), ipaddr, *port);
+    ipaddr->type = IPADDR_TYPE_V4;
+  }
+}
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
+/** LWIP_NETCONN_SEM_PER_THREAD==1: initialize thread-local semaphore */
+void
+lwip_socket_thread_init(void)
+{
+  netconn_thread_init();
+}
+
+/** LWIP_NETCONN_SEM_PER_THREAD==1: destroy thread-local semaphore */
+void
+lwip_socket_thread_cleanup(void)
+{
+  netconn_thread_cleanup();
+}
+
+#if LWIP_NETCONN_FULLDUPLEX
+/* Thread-safe increment of sock->fd_used, with overflow check */
+static int
+sock_inc_used(struct lwip_sock *sock)
+{
+  int ret;
+  SYS_ARCH_DECL_PROTECT(lev);
+
+  LWIP_ASSERT("sock != NULL", sock != NULL);
+
+  SYS_ARCH_PROTECT(lev);
+  if (sock->fd_free_pending) {
+    /* prevent new usage of this socket if free is pending */
+    ret = 0;
+  } else {
+    ++sock->fd_used;
+    ret = 1;
+    LWIP_ASSERT("sock->fd_used != 0", sock->fd_used != 0);
+  }
+  SYS_ARCH_UNPROTECT(lev);
+  return ret;
+}
+
+/* Like sock_inc_used(), but called under SYS_ARCH_PROTECT lock. */
+static int
+sock_inc_used_locked(struct lwip_sock *sock)
+{
+  LWIP_ASSERT("sock != NULL", sock != NULL);
+
+  if (sock->fd_free_pending) {
+    LWIP_ASSERT("sock->fd_used != 0", sock->fd_used != 0);
+    return 0;
+  }
+
+  ++sock->fd_used;
+  LWIP_ASSERT("sock->fd_used != 0", sock->fd_used != 0);
+  return 1;
+}
+
+/* In full-duplex mode,sock->fd_used != 0 prevents a socket descriptor from being
+ * released (and possibly reused) when used from more than one thread
+ * (e.g. read-while-write or close-while-write, etc)
+ * This function is called at the end of functions using (try)get_socket*().
+ */
+static void
+done_socket(struct lwip_sock *sock)
+{
+  int freed = 0;
+  int is_tcp = 0;
+  struct netconn *conn = NULL;
+  union lwip_sock_lastdata lastdata;
+  SYS_ARCH_DECL_PROTECT(lev);
+  LWIP_ASSERT("sock != NULL", sock != NULL);
+
+  SYS_ARCH_PROTECT(lev);
+  LWIP_ASSERT("sock->fd_used > 0", sock->fd_used > 0);
+  if (--sock->fd_used == 0) {
+    if (sock->fd_free_pending) {
+      /* free the socket */
+      sock->fd_used = 1;
+      is_tcp = sock->fd_free_pending & LWIP_SOCK_FD_FREE_TCP;
+      freed = free_socket_locked(sock, is_tcp, &conn, &lastdata);
+    }
+  }
+  SYS_ARCH_UNPROTECT(lev);
+
+  if (freed) {
+    free_socket_free_elements(is_tcp, conn, &lastdata);
+  }
+}
+
+#else /* LWIP_NETCONN_FULLDUPLEX */
+#define sock_inc_used(sock)         1
+#define sock_inc_used_locked(sock)  1
+#define done_socket(sock)
+#endif /* LWIP_NETCONN_FULLDUPLEX */
+
+/* Translate a socket 'int' into a pointer (only fails if the index is invalid) */
+static struct lwip_sock *
+tryget_socket_unconn_nouse(int fd)
+{
+  int s = fd - LWIP_SOCKET_OFFSET;
+  if ((s < 0) || (s >= NUM_SOCKETS)) {
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("tryget_socket_unconn(%d): invalid\n", fd));
+    return NULL;
+  }
+  return &sockets[s];
+}
+
+struct lwip_sock *
+lwip_socket_dbg_get_socket(int fd)
+{
+  return tryget_socket_unconn_nouse(fd);
+}
+
+/* Translate a socket 'int' into a pointer (only fails if the index is invalid) */
+static struct lwip_sock *
+tryget_socket_unconn(int fd)
+{
+  struct lwip_sock *ret = tryget_socket_unconn_nouse(fd);
+  if (ret != NULL) {
+    if (!sock_inc_used(ret)) {
+      return NULL;
+    }
+  }
+  return ret;
+}
+
+/* Like tryget_socket_unconn(), but called under SYS_ARCH_PROTECT lock. */
+static struct lwip_sock *
+tryget_socket_unconn_locked(int fd)
+{
+  struct lwip_sock *ret = tryget_socket_unconn_nouse(fd);
+  if (ret != NULL) {
+    if (!sock_inc_used_locked(ret)) {
+      return NULL;
+    }
+  }
+  return ret;
+}
+
+/**
+ * Same as get_socket but doesn't set errno
+ *
+ * @param fd externally used socket index
+ * @return struct lwip_sock for the socket or NULL if not found
+ */
+static struct lwip_sock *
+tryget_socket(int fd)
+{
+  struct lwip_sock *sock = tryget_socket_unconn(fd);
+  if (sock != NULL) {
+    if (sock->conn) {
+      return sock;
+    }
+    done_socket(sock);
+  }
+  return NULL;
+}
+
+/**
+ * Map a externally used socket index to the internal socket representation.
+ *
+ * @param fd externally used socket index
+ * @return struct lwip_sock for the socket or NULL if not found
+ */
+static struct lwip_sock *
+get_socket(int fd)
+{
+  struct lwip_sock *sock = tryget_socket(fd);
+  if (!sock) {
+    if ((fd < LWIP_SOCKET_OFFSET) || (fd >= (LWIP_SOCKET_OFFSET + NUM_SOCKETS))) {
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("get_socket(%d): invalid\n", fd));
+    }
+    set_errno(EBADF);
+    return NULL;
+  }
+  return sock;
+}
+
+/**
+ * Allocate a new socket for a given netconn.
+ *
+ * @param newconn the netconn for which to allocate a socket
+ * @param accepted 1 if socket has been created by accept(),
+ *                 0 if socket has been created by socket()
+ * @return the index of the new socket; -1 on error
+ */
+static int
+alloc_socket(struct netconn *newconn, int accepted)
+{
+  int i;
+  SYS_ARCH_DECL_PROTECT(lev);
+  LWIP_UNUSED_ARG(accepted);
+
+  /* allocate a new socket identifier */
+  for (i = 0; i < NUM_SOCKETS; ++i) {
+    /* Protect socket array */
+    SYS_ARCH_PROTECT(lev);
+    if (!sockets[i].conn) {
+#if LWIP_NETCONN_FULLDUPLEX
+      if (sockets[i].fd_used) {
+        SYS_ARCH_UNPROTECT(lev);
+        continue;
+      }
+      sockets[i].fd_used    = 1;
+      sockets[i].fd_free_pending = 0;
+#endif
+      sockets[i].conn       = newconn;
+      /* The socket is not yet known to anyone, so no need to protect
+         after having marked it as used. */
+      SYS_ARCH_UNPROTECT(lev);
+      sockets[i].lastdata.pbuf = NULL;
+#if LWIP_SOCKET_SELECT || LWIP_SOCKET_POLL
+      LWIP_ASSERT("sockets[i].select_waiting == 0", sockets[i].select_waiting == 0);
+      sockets[i].rcvevent   = 0;
+      /* TCP sendbuf is empty, but the socket is not yet writable until connected
+       * (unless it has been created by accept()). */
+      sockets[i].sendevent  = (NETCONNTYPE_GROUP(newconn->type) == NETCONN_TCP ? (accepted != 0) : 1);
+      sockets[i].errevent   = 0;
+#endif /* LWIP_SOCKET_SELECT || LWIP_SOCKET_POLL */
+      return i + LWIP_SOCKET_OFFSET;
+    }
+    SYS_ARCH_UNPROTECT(lev);
+  }
+  return -1;
+}
+
+/** Free a socket (under lock)
+ *
+ * @param sock the socket to free
+ * @param is_tcp != 0 for TCP sockets, used to free lastdata
+ * @param conn the socekt's netconn is stored here, must be freed externally
+ * @param lastdata lastdata is stored here, must be freed externally
+ */
+static int
+free_socket_locked(struct lwip_sock *sock, int is_tcp, struct netconn **conn,
+                   union lwip_sock_lastdata *lastdata)
+{
+#if LWIP_NETCONN_FULLDUPLEX
+  LWIP_ASSERT("sock->fd_used > 0", sock->fd_used > 0);
+  sock->fd_used--;
+  if (sock->fd_used > 0) {
+    sock->fd_free_pending = LWIP_SOCK_FD_FREE_FREE | (is_tcp ? LWIP_SOCK_FD_FREE_TCP : 0);
+    return 0;
+  }
+#else /* LWIP_NETCONN_FULLDUPLEX */
+  LWIP_UNUSED_ARG(is_tcp);
+#endif /* LWIP_NETCONN_FULLDUPLEX */
+
+  *lastdata = sock->lastdata;
+  sock->lastdata.pbuf = NULL;
+  *conn = sock->conn;
+  sock->conn = NULL;
+  return 1;
+}
+
+/** Free a socket's leftover members.
+ */
+static void
+free_socket_free_elements(int is_tcp, struct netconn *conn, union lwip_sock_lastdata *lastdata)
+{
+  if (lastdata->pbuf != NULL) {
+    if (is_tcp) {
+      pbuf_free(lastdata->pbuf);
+    } else {
+      netbuf_delete(lastdata->netbuf);
+    }
+  }
+  if (conn != NULL) {
+    /* netconn_prepare_delete() has already been called, here we only free the conn */
+    netconn_delete(conn);
+  }
+}
+
+/** Free a socket. The socket's netconn must have been
+ * delete before!
+ *
+ * @param sock the socket to free
+ * @param is_tcp != 0 for TCP sockets, used to free lastdata
+ */
+static void
+free_socket(struct lwip_sock *sock, int is_tcp)
+{
+  int freed;
+  struct netconn *conn;
+  union lwip_sock_lastdata lastdata;
+  SYS_ARCH_DECL_PROTECT(lev);
+
+  /* Protect socket array */
+  SYS_ARCH_PROTECT(lev);
+
+  freed = free_socket_locked(sock, is_tcp, &conn, &lastdata);
+  SYS_ARCH_UNPROTECT(lev);
+  /* don't use 'sock' after this line, as another task might have allocated it */
+
+  if (freed) {
+    free_socket_free_elements(is_tcp, conn, &lastdata);
+  }
+}
+
+/* Below this, the well-known socket functions are implemented.
+ * Use google.com or opengroup.org to get a good description :-)
+ *
+ * Exceptions are documented!
+ */
+
+int
+lwip_accept(int s, struct sockaddr *addr, socklen_t *addrlen)
+{
+  struct lwip_sock *sock, *nsock;
+  struct netconn *newconn;
+  ip_addr_t naddr;
+  u16_t port = 0;
+  int newsock;
+  err_t err;
+  int recvevent;
+  SYS_ARCH_DECL_PROTECT(lev);
+
+  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d)...\n", s));
+  sock = get_socket(s);
+  if (!sock) {
+    return -1;
+  }
+
+  /* wait for a new connection */
+  err = netconn_accept(sock->conn, &newconn);
+  if (err != ERR_OK) {
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d): netconn_acept failed, err=%d\n", s, err));
+    if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
+      set_errno(EOPNOTSUPP);
+    } else if (err == ERR_CLSD) {
+      set_errno(EINVAL);
+    } else {
+      set_errno(err_to_errno(err));
+    }
+    done_socket(sock);
+    return -1;
+  }
+  LWIP_ASSERT("newconn != NULL", newconn != NULL);
+
+  newsock = alloc_socket(newconn, 1);
+  if (newsock == -1) {
+    netconn_delete(newconn);
+    set_errno(ENFILE);
+    done_socket(sock);
+    return -1;
+  }
+  LWIP_ASSERT("invalid socket index", (newsock >= LWIP_SOCKET_OFFSET) && (newsock < NUM_SOCKETS + LWIP_SOCKET_OFFSET));
+  nsock = &sockets[newsock - LWIP_SOCKET_OFFSET];
+
+  /* See event_callback: If data comes in right away after an accept, even
+   * though the server task might not have created a new socket yet.
+   * In that case, newconn->socket is counted down (newconn->socket--),
+   * so nsock->rcvevent is >= 1 here!
+   */
+  SYS_ARCH_PROTECT(lev);
+  recvevent = (s16_t)(-1 - newconn->callback_arg.socket);
+  newconn->callback_arg.socket = newsock;
+  SYS_ARCH_UNPROTECT(lev);
+
+  if (newconn->callback) {
+    LOCK_TCPIP_CORE();
+    while (recvevent > 0) {
+      recvevent--;
+      newconn->callback(newconn, NETCONN_EVT_RCVPLUS, 0);
+    }
+    UNLOCK_TCPIP_CORE();
+  }
+
+  /* Note that POSIX only requires us to check addr is non-NULL. addrlen must
+   * not be NULL if addr is valid.
+   */
+  if ((addr != NULL) && (addrlen != NULL)) {
+    union sockaddr_aligned tempaddr;
+    /* get the IP address and port of the remote host */
+    err = netconn_peer(newconn, &naddr, &port);
+    if (err != ERR_OK) {
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d): netconn_peer failed, err=%d\n", s, err));
+      free_socket(nsock, 1);
+      set_errno(err_to_errno(err));
+      done_socket(sock);
+      return -1;
+    }
+
+    IPADDR_PORT_TO_SOCKADDR(&tempaddr, &naddr, port);
+    if (*addrlen > IPADDR_SOCKADDR_GET_LEN(&tempaddr)) {
+      *addrlen = IPADDR_SOCKADDR_GET_LEN(&tempaddr);
+    }
+    MEMCPY(addr, &tempaddr, *addrlen);
+
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d) returning new sock=%d addr=", s, newsock));
+    ip_addr_debug_print_val(SOCKETS_DEBUG, naddr);
+    LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F"\n", port));
+  } else {
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d) returning new sock=%d\n", s, newsock));
+  }
+
+  set_errno(0);
+  done_socket(sock);
+  done_socket(nsock);
+  return newsock;
+}
+
+int
+lwip_bind(int s, const struct sockaddr *name, socklen_t namelen)
+{
+  struct lwip_sock *sock;
+  ip_addr_t local_addr;
+  u16_t local_port;
+  err_t err;
+
+  sock = get_socket(s);
+  if (!sock) {
+    return -1;
+  }
+
+  if (!SOCK_ADDR_TYPE_MATCH(name, sock)) {
+    /* sockaddr does not match socket type (IPv4/IPv6) */
+    set_errno(err_to_errno(ERR_VAL));
+    done_socket(sock);
+    return -1;
+  }
+
+  /* check size, family and alignment of 'name' */
+  LWIP_ERROR("lwip_bind: invalid address", (IS_SOCK_ADDR_LEN_VALID(namelen) &&
+             IS_SOCK_ADDR_TYPE_VALID(name) && IS_SOCK_ADDR_ALIGNED(name)),
+             set_errno(err_to_errno(ERR_ARG)); done_socket(sock); return -1;);
+  LWIP_UNUSED_ARG(namelen);
+
+  SOCKADDR_TO_IPADDR_PORT(name, &local_addr, local_port);
+  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d, addr=", s));
+  ip_addr_debug_print_val(SOCKETS_DEBUG, local_addr);
+  LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", local_port));
+
+#if LWIP_IPV4 && LWIP_IPV6
+  /* Dual-stack: Unmap IPv4 mapped IPv6 addresses */
+  if (IP_IS_V6_VAL(local_addr) && ip6_addr_isipv4mappedipv6(ip_2_ip6(&local_addr))) {
+    unmap_ipv4_mapped_ipv6(ip_2_ip4(&local_addr), ip_2_ip6(&local_addr));
+    IP_SET_TYPE_VAL(local_addr, IPADDR_TYPE_V4);
+  }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
+  err = netconn_bind(sock->conn, &local_addr, local_port);
+
+  if (err != ERR_OK) {
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d) failed, err=%d\n", s, err));
+    set_errno(err_to_errno(err));
+    done_socket(sock);
+    return -1;
+  }
+
+  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d) succeeded\n", s));
+  set_errno(0);
+  done_socket(sock);
+  return 0;
+}
+
+int
+lwip_close(int s)
+{
+  struct lwip_sock *sock;
+  int is_tcp = 0;
+  err_t err;
+
+  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_close(%d)\n", s));
+
+  sock = get_socket(s);
+  if (!sock) {
+    return -1;
+  }
+
+  if (sock->conn != NULL) {
+    is_tcp = NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP;
+  } else {
+    LWIP_ASSERT("sock->lastdata == NULL", sock->lastdata.pbuf == NULL);
+  }
+
+#if LWIP_IGMP
+  /* drop all possibly joined IGMP memberships */
+  lwip_socket_drop_registered_memberships(s);
+#endif /* LWIP_IGMP */
+#if LWIP_IPV6_MLD
+  /* drop all possibly joined MLD6 memberships */
+  lwip_socket_drop_registered_mld6_memberships(s);
+#endif /* LWIP_IPV6_MLD */
+
+  err = netconn_prepare_delete(sock->conn);
+  if (err != ERR_OK) {
+    set_errno(err_to_errno(err));
+    done_socket(sock);
+    return -1;
+  }
+
+  free_socket(sock, is_tcp);
+  set_errno(0);
+  return 0;
+}
+
+int
+lwip_connect(int s, const struct sockaddr *name, socklen_t namelen)
+{
+  struct lwip_sock *sock;
+  err_t err;
+
+  sock = get_socket(s);
+  if (!sock) {
+    return -1;
+  }
+
+  if (!SOCK_ADDR_TYPE_MATCH_OR_UNSPEC(name, sock)) {
+    /* sockaddr does not match socket type (IPv4/IPv6) */
+    set_errno(err_to_errno(ERR_VAL));
+    done_socket(sock);
+    return -1;
+  }
+
+  LWIP_UNUSED_ARG(namelen);
+  if (name->sa_family == AF_UNSPEC) {
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d, AF_UNSPEC)\n", s));
+    err = netconn_disconnect(sock->conn);
+  } else {
+    ip_addr_t remote_addr;
+    u16_t remote_port;
+
+    /* check size, family and alignment of 'name' */
+    LWIP_ERROR("lwip_connect: invalid address", IS_SOCK_ADDR_LEN_VALID(namelen) &&
+               IS_SOCK_ADDR_TYPE_VALID_OR_UNSPEC(name) && IS_SOCK_ADDR_ALIGNED(name),
+               set_errno(err_to_errno(ERR_ARG)); done_socket(sock); return -1;);
+
+    SOCKADDR_TO_IPADDR_PORT(name, &remote_addr, remote_port);
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d, addr=", s));
+    ip_addr_debug_print_val(SOCKETS_DEBUG, remote_addr);
+    LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", remote_port));
+
+#if LWIP_IPV4 && LWIP_IPV6
+    /* Dual-stack: Unmap IPv4 mapped IPv6 addresses */
+    if (IP_IS_V6_VAL(remote_addr) && ip6_addr_isipv4mappedipv6(ip_2_ip6(&remote_addr))) {
+      unmap_ipv4_mapped_ipv6(ip_2_ip4(&remote_addr), ip_2_ip6(&remote_addr));
+      IP_SET_TYPE_VAL(remote_addr, IPADDR_TYPE_V4);
+    }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
+    err = netconn_connect(sock->conn, &remote_addr, remote_port);
+  }
+
+  if (err != ERR_OK) {
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d) failed, err=%d\n", s, err));
+    set_errno(err_to_errno(err));
+    done_socket(sock);
+    return -1;
+  }
+
+  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d) succeeded\n", s));
+  set_errno(0);
+  done_socket(sock);
+  return 0;
+}
+
+/**
+ * Set a socket into listen mode.
+ * The socket may not have been used for another connection previously.
+ *
+ * @param s the socket to set to listening mode
+ * @param backlog (ATTENTION: needs TCP_LISTEN_BACKLOG=1)
+ * @return 0 on success, non-zero on failure
+ */
+int
+lwip_listen(int s, int backlog)
+{
+  struct lwip_sock *sock;
+  err_t err;
+
+  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_listen(%d, backlog=%d)\n", s, backlog));
+
+  sock = get_socket(s);
+  if (!sock) {
+    return -1;
+  }
+
+  /* limit the "backlog" parameter to fit in an u8_t */
+  backlog = LWIP_MIN(LWIP_MAX(backlog, 0), 0xff);
+
+  err = netconn_listen_with_backlog(sock->conn, (u8_t)backlog);
+
+  if (err != ERR_OK) {
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_listen(%d) failed, err=%d\n", s, err));
+    if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
+      set_errno(EOPNOTSUPP);
+    } else {
+      set_errno(err_to_errno(err));
+    }
+    done_socket(sock);
+    return -1;
+  }
+
+  set_errno(0);
+  done_socket(sock);
+  return 0;
+}
+
+#if LWIP_TCP
+/* Helper function to loop over receiving pbufs from netconn
+ * until "len" bytes are received or we're otherwise done.
+ * Keeps sock->lastdata for peeking or partly copying.
+ */
+static ssize_t
+lwip_recv_tcp(struct lwip_sock *sock, void *mem, size_t len, int flags)
+{
+  u8_t apiflags = NETCONN_NOAUTORCVD;
+  ssize_t recvd = 0;
+  ssize_t recv_left = (len <= SSIZE_MAX) ? (ssize_t)len : SSIZE_MAX;
+
+  LWIP_ASSERT("no socket given", sock != NULL);
+  LWIP_ASSERT("this should be checked internally", NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP);
+
+  if (flags & MSG_DONTWAIT) {
+    apiflags |= NETCONN_DONTBLOCK;
+  }
+
+  do {
+    struct pbuf *p;
+    err_t err;
+    u16_t copylen;
+
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recv_tcp: top while sock->lastdata=%p\n", (void *)sock->lastdata.pbuf));
+    /* Check if there is data left from the last recv operation. */
+    if (sock->lastdata.pbuf) {
+      p = sock->lastdata.pbuf;
+    } else {
+      /* No data was left from the previous operation, so we try to get
+         some from the network. */
+      err = netconn_recv_tcp_pbuf_flags(sock->conn, &p, apiflags);
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recv_tcp: netconn_recv err=%d, pbuf=%p\n",
+                                  err, (void *)p));
+
+      if (err != ERR_OK) {
+        if (recvd > 0) {
+          /* already received data, return that (this trusts in getting the same error from
+             netconn layer again next time netconn_recv is called) */
+          goto lwip_recv_tcp_done;
+        }
+        /* We should really do some error checking here. */
+        LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recv_tcp: p == NULL, error is \"%s\"!\n",
+                                    lwip_strerr(err)));
+        set_errno(err_to_errno(err));
+        if (err == ERR_CLSD) {
+          return 0;
+        } else {
+          return -1;
+        }
+      }
+      LWIP_ASSERT("p != NULL", p != NULL);
+      sock->lastdata.pbuf = p;
+    }
+
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recv_tcp: buflen=%"U16_F" recv_left=%d off=%d\n",
+                                p->tot_len, (int)recv_left, (int)recvd));
+
+    if (recv_left > p->tot_len) {
+      copylen = p->tot_len;
+    } else {
+      copylen = (u16_t)recv_left;
+    }
+    if (recvd > SSIZE_MAX - copylen) {
+      /* overflow */
+      copylen = (u16_t)(SSIZE_MAX - recvd);
+    }
+
+    /* copy the contents of the received buffer into
+    the supplied memory pointer mem */
+    pbuf_copy_partial(p, (u8_t *)mem + recvd, copylen, 0);
+
+    recvd += copylen;
+
+    /* TCP combines multiple pbufs for one recv */
+    LWIP_ASSERT("invalid copylen, len would underflow", recv_left >= copylen);
+    recv_left -= copylen;
+
+    /* Unless we peek the incoming message... */
+    if ((flags & MSG_PEEK) == 0) {
+      /* ... check if there is data left in the pbuf */
+      LWIP_ASSERT("invalid copylen", p->tot_len >= copylen);
+      if (p->tot_len - copylen > 0) {
+        /* If so, it should be saved in the sock structure for the next recv call.
+           We store the pbuf but hide/free the consumed data: */
+        sock->lastdata.pbuf = pbuf_free_header(p, copylen);
+        LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recv_tcp: lastdata now pbuf=%p\n", (void *)sock->lastdata.pbuf));
+      } else {
+        sock->lastdata.pbuf = NULL;
+        LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recv_tcp: deleting pbuf=%p\n", (void *)p));
+        pbuf_free(p);
+      }
+    }
+    /* once we have some data to return, only add more if we don't need to wait */
+    apiflags |= NETCONN_DONTBLOCK | NETCONN_NOFIN;
+    /* @todo: do we need to support peeking more than one pbuf? */
+  } while ((recv_left > 0) && !(flags & MSG_PEEK));
+lwip_recv_tcp_done:
+  if ((recvd > 0) && !(flags & MSG_PEEK)) {
+    /* ensure window update after copying all data */
+    netconn_tcp_recvd(sock->conn, (size_t)recvd);
+  }
+  set_errno(0);
+  return recvd;
+}
+#endif
+
+/* Convert a netbuf's address data to struct sockaddr */
+static int
+lwip_sock_make_addr(struct netconn *conn, ip_addr_t *fromaddr, u16_t port,
+                    struct sockaddr *from, socklen_t *fromlen)
+{
+  int truncated = 0;
+  union sockaddr_aligned saddr;
+
+  LWIP_UNUSED_ARG(conn);
+
+  LWIP_ASSERT("fromaddr != NULL", fromaddr != NULL);
+  LWIP_ASSERT("from != NULL", from != NULL);
+  LWIP_ASSERT("fromlen != NULL", fromlen != NULL);
+
+#if LWIP_IPV4 && LWIP_IPV6
+  /* Dual-stack: Map IPv4 addresses to IPv4 mapped IPv6 */
+  if (NETCONNTYPE_ISIPV6(netconn_type(conn)) && IP_IS_V4(fromaddr)) {
+    ip4_2_ipv4_mapped_ipv6(ip_2_ip6(fromaddr), ip_2_ip4(fromaddr));
+    IP_SET_TYPE(fromaddr, IPADDR_TYPE_V6);
+  }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
+  IPADDR_PORT_TO_SOCKADDR(&saddr, fromaddr, port);
+  if (*fromlen < IPADDR_SOCKADDR_GET_LEN(&saddr)) {
+    truncated = 1;
+  } else if (*fromlen > IPADDR_SOCKADDR_GET_LEN(&saddr)) {
+    *fromlen = IPADDR_SOCKADDR_GET_LEN(&saddr);
+  }
+  MEMCPY(from, &saddr, *fromlen);
+  return truncated;
+}
+
+#if LWIP_TCP
+/* Helper function to get a tcp socket's remote address info */
+static int
+lwip_recv_tcp_from(struct lwip_sock *sock, struct sockaddr *from, socklen_t *fromlen, const char *dbg_fn, int dbg_s, ssize_t dbg_ret)
+{
+  if (sock == NULL) {
+    return 0;
+  }
+  LWIP_UNUSED_ARG(dbg_fn);
+  LWIP_UNUSED_ARG(dbg_s);
+  LWIP_UNUSED_ARG(dbg_ret);
+
+#if !SOCKETS_DEBUG
+  if (from && fromlen)
+#endif /* !SOCKETS_DEBUG */
+  {
+    /* get remote addr/port from tcp_pcb */
+    u16_t port;
+    ip_addr_t tmpaddr;
+    netconn_getaddr(sock->conn, &tmpaddr, &port, 0);
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("%s(%d):  addr=", dbg_fn, dbg_s));
+    ip_addr_debug_print_val(SOCKETS_DEBUG, tmpaddr);
+    LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F" len=%d\n", port, (int)dbg_ret));
+    if (from && fromlen) {
+      return lwip_sock_make_addr(sock->conn, &tmpaddr, port, from, fromlen);
+    }
+  }
+  return 0;
+}
+#endif
+
+/* Helper function to receive a netbuf from a udp or raw netconn.
+ * Keeps sock->lastdata for peeking.
+ */
+static err_t
+lwip_recvfrom_udp_raw(struct lwip_sock *sock, int flags, struct msghdr *msg, u16_t *datagram_len, int dbg_s)
+{
+  struct netbuf *buf;
+  u8_t apiflags;
+  err_t err;
+  u16_t buflen, copylen, copied;
+  msg_iovlen_t i;
+
+  LWIP_UNUSED_ARG(dbg_s);
+  LWIP_ERROR("lwip_recvfrom_udp_raw: invalid arguments", (msg->msg_iov != NULL) || (msg->msg_iovlen <= 0), return ERR_ARG;);
+
+  if (flags & MSG_DONTWAIT) {
+    apiflags = NETCONN_DONTBLOCK;
+  } else {
+    apiflags = 0;
+  }
+
+  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom_udp_raw[UDP/RAW]: top sock->lastdata=%p\n", (void *)sock->lastdata.netbuf));
+  /* Check if there is data left from the last recv operation. */
+  buf = sock->lastdata.netbuf;
+  if (buf == NULL) {
+    /* No data was left from the previous operation, so we try to get
+        some from the network. */
+    err = netconn_recv_udp_raw_netbuf_flags(sock->conn, &buf, apiflags);
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom_udp_raw[UDP/RAW]: netconn_recv err=%d, netbuf=%p\n",
+                                err, (void *)buf));
+
+    if (err != ERR_OK) {
+      return err;
+    }
+    LWIP_ASSERT("buf != NULL", buf != NULL);
+    sock->lastdata.netbuf = buf;
+  }
+  buflen = buf->p->tot_len;
+  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom_udp_raw: buflen=%"U16_F"\n", buflen));
+
+  copied = 0;
+  /* copy the pbuf payload into the iovs */
+  for (i = 0; (i < msg->msg_iovlen) && (copied < buflen); i++) {
+    u16_t len_left = (u16_t)(buflen - copied);
+    if (msg->msg_iov[i].iov_len > len_left) {
+      copylen = len_left;
+    } else {
+      copylen = (u16_t)msg->msg_iov[i].iov_len;
+    }
+
+    /* copy the contents of the received buffer into
+        the supplied memory buffer */
+    pbuf_copy_partial(buf->p, (u8_t *)msg->msg_iov[i].iov_base, copylen, copied);
+    copied = (u16_t)(copied + copylen);
+  }
+
+  /* Check to see from where the data was.*/
+#if !SOCKETS_DEBUG
+  if (msg->msg_name && msg->msg_namelen)
+#endif /* !SOCKETS_DEBUG */
+  {
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom_udp_raw(%d):  addr=", dbg_s));
+    ip_addr_debug_print_val(SOCKETS_DEBUG, *netbuf_fromaddr(buf));
+    LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F" len=%d\n", netbuf_fromport(buf), copied));
+    if (msg->msg_name && msg->msg_namelen) {
+      lwip_sock_make_addr(sock->conn, netbuf_fromaddr(buf), netbuf_fromport(buf),
+                          (struct sockaddr *)msg->msg_name, &msg->msg_namelen);
+    }
+  }
+
+  /* Initialize flag output */
+  msg->msg_flags = 0;
+
+  if (msg->msg_control) {
+    u8_t wrote_msg = 0;
+#if LWIP_NETBUF_RECVINFO
+    /* Check if packet info was recorded */
+    if (buf->flags & NETBUF_FLAG_DESTADDR) {
+      if (IP_IS_V4(&buf->toaddr)) {
+#if LWIP_IPV4
+        if (msg->msg_controllen >= CMSG_SPACE(sizeof(struct in_pktinfo))) {
+          struct cmsghdr *chdr = CMSG_FIRSTHDR(msg); /* This will always return a header!! */
+          struct in_pktinfo *pkti = (struct in_pktinfo *)CMSG_DATA(chdr);
+          chdr->cmsg_level = IPPROTO_IP;
+          chdr->cmsg_type = IP_PKTINFO;
+          chdr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
+          pkti->ipi_ifindex = buf->p->if_idx;
+          inet_addr_from_ip4addr(&pkti->ipi_addr, ip_2_ip4(netbuf_destaddr(buf)));
+          msg->msg_controllen = CMSG_SPACE(sizeof(struct in_pktinfo));
+          wrote_msg = 1;
+        } else {
+          msg->msg_flags |= MSG_CTRUNC;
+        }
+#endif /* LWIP_IPV4 */
+      }
+    }
+#endif /* LWIP_NETBUF_RECVINFO */
+
+    if (!wrote_msg) {
+      msg->msg_controllen = 0;
+    }
+  }
+
+  /* If we don't peek the incoming message: zero lastdata pointer and free the netbuf */
+  if ((flags & MSG_PEEK) == 0) {
+    sock->lastdata.netbuf = NULL;
+    netbuf_delete(buf);
+  }
+  if (datagram_len) {
+    *datagram_len = buflen;
+  }
+  return ERR_OK;
+}
+
+ssize_t
+lwip_recvfrom(int s, void *mem, size_t len, int flags,
+              struct sockaddr *from, socklen_t *fromlen)
+{
+  struct lwip_sock *sock;
+  ssize_t ret;
+
+  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d, %p, %"SZT_F", 0x%x, ..)\n", s, mem, len, flags));
+  sock = get_socket(s);
+  if (!sock) {
+    return -1;
+  }
+#if LWIP_TCP
+  if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
+    ret = lwip_recv_tcp(sock, mem, len, flags);
+    lwip_recv_tcp_from(sock, from, fromlen, "lwip_recvfrom", s, ret);
+    done_socket(sock);
+    return ret;
+  } else
+#endif
+  {
+    u16_t datagram_len = 0;
+    struct iovec vec;
+    struct msghdr msg;
+    err_t err;
+    vec.iov_base = mem;
+    vec.iov_len = len;
+    msg.msg_control = NULL;
+    msg.msg_controllen = 0;
+    msg.msg_flags = 0;
+    msg.msg_iov = &vec;
+    msg.msg_iovlen = 1;
+    msg.msg_name = from;
+    msg.msg_namelen = (fromlen ? *fromlen : 0);
+    err = lwip_recvfrom_udp_raw(sock, flags, &msg, &datagram_len, s);
+    if (err != ERR_OK) {
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom[UDP/RAW](%d): buf == NULL, error is \"%s\"!\n",
+                                  s, lwip_strerr(err)));
+      set_errno(err_to_errno(err));
+      done_socket(sock);
+      return -1;
+    }
+    ret = (ssize_t)LWIP_MIN(LWIP_MIN(len, datagram_len), SSIZE_MAX);
+    if (fromlen) {
+      *fromlen = msg.msg_namelen;
+    }
+  }
+
+  set_errno(0);
+  done_socket(sock);
+  return ret;
+}
+
+ssize_t
+lwip_read(int s, void *mem, size_t len)
+{
+  return lwip_recvfrom(s, mem, len, 0, NULL, NULL);
+}
+
+ssize_t
+lwip_readv(int s, const struct iovec *iov, int iovcnt)
+{
+  struct msghdr msg;
+
+  msg.msg_name = NULL;
+  msg.msg_namelen = 0;
+  /* Hack: we have to cast via number to cast from 'const' pointer to non-const.
+     Blame the opengroup standard for this inconsistency. */
+  msg.msg_iov = LWIP_CONST_CAST(struct iovec *, iov);
+  msg.msg_iovlen = iovcnt;
+  msg.msg_control = NULL;
+  msg.msg_controllen = 0;
+  msg.msg_flags = 0;
+  return lwip_recvmsg(s, &msg, 0);
+}
+
+ssize_t
+lwip_recv(int s, void *mem, size_t len, int flags)
+{
+  return lwip_recvfrom(s, mem, len, flags, NULL, NULL);
+}
+
+ssize_t
+lwip_recvmsg(int s, struct msghdr *message, int flags)
+{
+  struct lwip_sock *sock;
+  msg_iovlen_t i;
+  ssize_t buflen;
+
+  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvmsg(%d, message=%p, flags=0x%x)\n", s, (void *)message, flags));
+  LWIP_ERROR("lwip_recvmsg: invalid message pointer", message != NULL, return ERR_ARG;);
+  LWIP_ERROR("lwip_recvmsg: unsupported flags", (flags & ~(MSG_PEEK|MSG_DONTWAIT)) == 0,
+             set_errno(EOPNOTSUPP); return -1;);
+
+  if ((message->msg_iovlen <= 0) || (message->msg_iovlen > IOV_MAX)) {
+    set_errno(EMSGSIZE);
+    return -1;
+  }
+
+  sock = get_socket(s);
+  if (!sock) {
+    return -1;
+  }
+
+  /* check for valid vectors */
+  buflen = 0;
+  for (i = 0; i < message->msg_iovlen; i++) {
+    if ((message->msg_iov[i].iov_base == NULL) || ((ssize_t)message->msg_iov[i].iov_len <= 0) ||
+        ((size_t)(ssize_t)message->msg_iov[i].iov_len != message->msg_iov[i].iov_len) ||
+        ((ssize_t)(buflen + (ssize_t)message->msg_iov[i].iov_len) <= 0)) {
+      set_errno(err_to_errno(ERR_VAL));
+      done_socket(sock);
+      return -1;
+    }
+    buflen = (ssize_t)(buflen + (ssize_t)message->msg_iov[i].iov_len);
+  }
+
+  if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
+#if LWIP_TCP
+    int recv_flags = flags;
+    message->msg_flags = 0;
+    /* recv the data */
+    buflen = 0;
+    for (i = 0; i < message->msg_iovlen; i++) {
+      /* try to receive into this vector's buffer */
+      ssize_t recvd_local = lwip_recv_tcp(sock, message->msg_iov[i].iov_base, message->msg_iov[i].iov_len, recv_flags);
+      if (recvd_local > 0) {
+        /* sum up received bytes */
+        buflen += recvd_local;
+      }
+      if ((recvd_local < 0) || (recvd_local < (int)message->msg_iov[i].iov_len) ||
+          (flags & MSG_PEEK)) {
+        /* returned prematurely (or peeking, which might actually be limitated to the first iov) */
+        if (buflen <= 0) {
+          /* nothing received at all, propagate the error */
+          buflen = recvd_local;
+        }
+        break;
+      }
+      /* pass MSG_DONTWAIT to lwip_recv_tcp() to prevent waiting for more data */
+      recv_flags |= MSG_DONTWAIT;
+    }
+    if (buflen > 0) {
+      /* reset socket error since we have received something */
+      set_errno(0);
+    }
+    /* " If the socket is connected, the msg_name and msg_namelen members shall be ignored." */
+    done_socket(sock);
+    return buflen;
+#else /* LWIP_TCP */
+    set_errno(err_to_errno(ERR_ARG));
+    done_socket(sock);
+    return -1;
+#endif /* LWIP_TCP */
+  }
+  /* else, UDP and RAW NETCONNs */
+#if LWIP_UDP || LWIP_RAW
+  {
+    u16_t datagram_len = 0;
+    err_t err;
+    err = lwip_recvfrom_udp_raw(sock, flags, message, &datagram_len, s);
+    if (err != ERR_OK) {
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvmsg[UDP/RAW](%d): buf == NULL, error is \"%s\"!\n",
+                                  s, lwip_strerr(err)));
+      set_errno(err_to_errno(err));
+      done_socket(sock);
+      return -1;
+    }
+    if (datagram_len > buflen) {
+      message->msg_flags |= MSG_TRUNC;
+    }
+
+    set_errno(0);
+    done_socket(sock);
+    return (int)datagram_len;
+  }
+#else /* LWIP_UDP || LWIP_RAW */
+  set_errno(err_to_errno(ERR_ARG));
+  done_socket(sock);
+  return -1;
+#endif /* LWIP_UDP || LWIP_RAW */
+}
+
+ssize_t
+lwip_send(int s, const void *data, size_t size, int flags)
+{
+  struct lwip_sock *sock;
+  err_t err;
+  u8_t write_flags;
+  size_t written;
+
+  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_send(%d, data=%p, size=%"SZT_F", flags=0x%x)\n",
+                              s, data, size, flags));
+
+  sock = get_socket(s);
+  if (!sock) {
+    return -1;
+  }
+
+  if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
+#if (LWIP_UDP || LWIP_RAW)
+    done_socket(sock);
+    return lwip_sendto(s, data, size, flags, NULL, 0);
+#else /* (LWIP_UDP || LWIP_RAW) */
+    set_errno(err_to_errno(ERR_ARG));
+    done_socket(sock);
+    return -1;
+#endif /* (LWIP_UDP || LWIP_RAW) */
+  }
+
+  write_flags = (u8_t)(NETCONN_COPY |
+                       ((flags & MSG_MORE)     ? NETCONN_MORE      : 0) |
+                       ((flags & MSG_DONTWAIT) ? NETCONN_DONTBLOCK : 0));
+  written = 0;
+  err = netconn_write_partly(sock->conn, data, size, write_flags, &written);
+
+  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_send(%d) err=%d written=%"SZT_F"\n", s, err, written));
+  set_errno(err_to_errno(err));
+  done_socket(sock);
+  /* casting 'written' to ssize_t is OK here since the netconn API limits it to SSIZE_MAX */
+  return (err == ERR_OK ? (ssize_t)written : -1);
+}
+
+ssize_t
+lwip_sendmsg(int s, const struct msghdr *msg, int flags)
+{
+  struct lwip_sock *sock;
+#if LWIP_TCP
+  u8_t write_flags;
+  size_t written;
+#endif
+  err_t err = ERR_OK;
+
+  sock = get_socket(s);
+  if (!sock) {
+    return -1;
+  }
+
+  LWIP_ERROR("lwip_sendmsg: invalid msghdr", msg != NULL,
+             set_errno(err_to_errno(ERR_ARG)); done_socket(sock); return -1;);
+  LWIP_ERROR("lwip_sendmsg: invalid msghdr iov", msg->msg_iov != NULL,
+             set_errno(err_to_errno(ERR_ARG)); done_socket(sock); return -1;);
+  LWIP_ERROR("lwip_sendmsg: maximum iovs exceeded", (msg->msg_iovlen > 0) && (msg->msg_iovlen <= IOV_MAX),
+             set_errno(EMSGSIZE); done_socket(sock); return -1;);
+  LWIP_ERROR("lwip_sendmsg: unsupported flags", (flags & ~(MSG_DONTWAIT | MSG_MORE)) == 0,
+             set_errno(EOPNOTSUPP); done_socket(sock); return -1;);
+
+  LWIP_UNUSED_ARG(msg->msg_control);
+  LWIP_UNUSED_ARG(msg->msg_controllen);
+  LWIP_UNUSED_ARG(msg->msg_flags);
+
+  if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
+#if LWIP_TCP
+    write_flags = (u8_t)(NETCONN_COPY |
+                         ((flags & MSG_MORE)     ? NETCONN_MORE      : 0) |
+                         ((flags & MSG_DONTWAIT) ? NETCONN_DONTBLOCK : 0));
+
+    written = 0;
+    err = netconn_write_vectors_partly(sock->conn, (struct netvector *)msg->msg_iov, (u16_t)msg->msg_iovlen, write_flags, &written);
+    set_errno(err_to_errno(err));
+    done_socket(sock);
+    /* casting 'written' to ssize_t is OK here since the netconn API limits it to SSIZE_MAX */
+    return (err == ERR_OK ? (ssize_t)written : -1);
+#else /* LWIP_TCP */
+    set_errno(err_to_errno(ERR_ARG));
+    done_socket(sock);
+    return -1;
+#endif /* LWIP_TCP */
+  }
+  /* else, UDP and RAW NETCONNs */
+#if LWIP_UDP || LWIP_RAW
+  {
+    struct netbuf chain_buf;
+    msg_iovlen_t i;
+    ssize_t size = 0;
+
+    LWIP_UNUSED_ARG(flags);
+    LWIP_ERROR("lwip_sendmsg: invalid msghdr name", (((msg->msg_name == NULL) && (msg->msg_namelen == 0)) ||
+               IS_SOCK_ADDR_LEN_VALID(msg->msg_namelen)),
+               set_errno(err_to_errno(ERR_ARG)); done_socket(sock); return -1;);
+
+    /* initialize chain buffer with destination */
+    memset(&chain_buf, 0, sizeof(struct netbuf));
+    if (msg->msg_name) {
+      u16_t remote_port;
+      SOCKADDR_TO_IPADDR_PORT((const struct sockaddr *)msg->msg_name, &chain_buf.addr, remote_port);
+      netbuf_fromport(&chain_buf) = remote_port;
+    }
+#if LWIP_NETIF_TX_SINGLE_PBUF
+    for (i = 0; i < msg->msg_iovlen; i++) {
+      size += msg->msg_iov[i].iov_len;
+      if ((msg->msg_iov[i].iov_len > INT_MAX) || (size < (int)msg->msg_iov[i].iov_len)) {
+        /* overflow */
+        goto sendmsg_emsgsize;
+      }
+    }
+    if (size > 0xFFFF) {
+      /* overflow */
+      goto sendmsg_emsgsize;
+    }
+    /* Allocate a new netbuf and copy the data into it. */
+    if (netbuf_alloc(&chain_buf, (u16_t)size) == NULL) {
+      err = ERR_MEM;
+    } else {
+      /* flatten the IO vectors */
+      size_t offset = 0;
+      for (i = 0; i < msg->msg_iovlen; i++) {
+        MEMCPY(&((u8_t *)chain_buf.p->payload)[offset], msg->msg_iov[i].iov_base, msg->msg_iov[i].iov_len);
+        offset += msg->msg_iov[i].iov_len;
+      }
+#if LWIP_CHECKSUM_ON_COPY
+      {
+        /* This can be improved by using LWIP_CHKSUM_COPY() and aggregating the checksum for each IO vector */
+        u16_t chksum = ~inet_chksum_pbuf(chain_buf.p);
+        netbuf_set_chksum(&chain_buf, chksum);
+      }
+#endif /* LWIP_CHECKSUM_ON_COPY */
+      err = ERR_OK;
+    }
+#else /* LWIP_NETIF_TX_SINGLE_PBUF */
+    /* create a chained netbuf from the IO vectors. NOTE: we assemble a pbuf chain
+       manually to avoid having to allocate, chain, and delete a netbuf for each iov */
+    for (i = 0; i < msg->msg_iovlen; i++) {
+      struct pbuf *p;
+      if (msg->msg_iov[i].iov_len > 0xFFFF) {
+        /* overflow */
+        goto sendmsg_emsgsize;
+      }
+      p = pbuf_alloc(PBUF_TRANSPORT, 0, PBUF_REF);
+      if (p == NULL) {
+        err = ERR_MEM; /* let netbuf_delete() cleanup chain_buf */
+        break;
+      }
+      p->payload = msg->msg_iov[i].iov_base;
+      p->len = p->tot_len = (u16_t)msg->msg_iov[i].iov_len;
+      /* netbuf empty, add new pbuf */
+      if (chain_buf.p == NULL) {
+        chain_buf.p = chain_buf.ptr = p;
+        /* add pbuf to existing pbuf chain */
+      } else {
+        if (chain_buf.p->tot_len + p->len > 0xffff) {
+          /* overflow */
+          pbuf_free(p);
+          goto sendmsg_emsgsize;
+        }
+        pbuf_cat(chain_buf.p, p);
+      }
+    }
+    /* save size of total chain */
+    if (err == ERR_OK) {
+      size = netbuf_len(&chain_buf);
+    }
+#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
+
+    if (err == ERR_OK) {
+#if LWIP_IPV4 && LWIP_IPV6
+      /* Dual-stack: Unmap IPv4 mapped IPv6 addresses */
+      if (IP_IS_V6_VAL(chain_buf.addr) && ip6_addr_isipv4mappedipv6(ip_2_ip6(&chain_buf.addr))) {
+        unmap_ipv4_mapped_ipv6(ip_2_ip4(&chain_buf.addr), ip_2_ip6(&chain_buf.addr));
+        IP_SET_TYPE_VAL(chain_buf.addr, IPADDR_TYPE_V4);
+      }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
+      /* send the data */
+      err = netconn_send(sock->conn, &chain_buf);
+    }
+
+    /* deallocated the buffer */
+    netbuf_free(&chain_buf);
+
+    set_errno(err_to_errno(err));
+    done_socket(sock);
+    return (err == ERR_OK ? size : -1);
+sendmsg_emsgsize:
+    set_errno(EMSGSIZE);
+    netbuf_free(&chain_buf);
+    done_socket(sock);
+    return -1;
+  }
+#else /* LWIP_UDP || LWIP_RAW */
+  set_errno(err_to_errno(ERR_ARG));
+  done_socket(sock);
+  return -1;
+#endif /* LWIP_UDP || LWIP_RAW */
+}
+
+ssize_t
+lwip_sendto(int s, const void *data, size_t size, int flags,
+            const struct sockaddr *to, socklen_t tolen)
+{
+  struct lwip_sock *sock;
+  err_t err;
+  u16_t short_size;
+  u16_t remote_port;
+  struct netbuf buf;
+
+  sock = get_socket(s);
+  if (!sock) {
+    return -1;
+  }
+
+  if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
+#if LWIP_TCP
+    done_socket(sock);
+    return lwip_send(s, data, size, flags);
+#else /* LWIP_TCP */
+    LWIP_UNUSED_ARG(flags);
+    set_errno(err_to_errno(ERR_ARG));
+    done_socket(sock);
+    return -1;
+#endif /* LWIP_TCP */
+  }
+
+  if (size > LWIP_MIN(0xFFFF, SSIZE_MAX)) {
+    /* cannot fit into one datagram (at least for us) */
+    set_errno(EMSGSIZE);
+    done_socket(sock);
+    return -1;
+  }
+  short_size = (u16_t)size;
+  LWIP_ERROR("lwip_sendto: invalid address", (((to == NULL) && (tolen == 0)) ||
+             (IS_SOCK_ADDR_LEN_VALID(tolen) &&
+              ((to != NULL) && (IS_SOCK_ADDR_TYPE_VALID(to) && IS_SOCK_ADDR_ALIGNED(to))))),
+             set_errno(err_to_errno(ERR_ARG)); done_socket(sock); return -1;);
+  LWIP_UNUSED_ARG(tolen);
+
+  /* initialize a buffer */
+  buf.p = buf.ptr = NULL;
+#if LWIP_CHECKSUM_ON_COPY
+  buf.flags = 0;
+#endif /* LWIP_CHECKSUM_ON_COPY */
+  if (to) {
+    SOCKADDR_TO_IPADDR_PORT(to, &buf.addr, remote_port);
+  } else {
+    remote_port = 0;
+    ip_addr_set_any(NETCONNTYPE_ISIPV6(netconn_type(sock->conn)), &buf.addr);
+  }
+  netbuf_fromport(&buf) = remote_port;
+
+
+  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_sendto(%d, data=%p, short_size=%"U16_F", flags=0x%x to=",
+                              s, data, short_size, flags));
+  ip_addr_debug_print_val(SOCKETS_DEBUG, buf.addr);
+  LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F"\n", remote_port));
+
+  /* make the buffer point to the data that should be sent */
+#if LWIP_NETIF_TX_SINGLE_PBUF
+  /* Allocate a new netbuf and copy the data into it. */
+  if (netbuf_alloc(&buf, short_size) == NULL) {
+    err = ERR_MEM;
+  } else {
+#if LWIP_CHECKSUM_ON_COPY
+    if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_RAW) {
+      u16_t chksum = LWIP_CHKSUM_COPY(buf.p->payload, data, short_size);
+      netbuf_set_chksum(&buf, chksum);
+    } else
+#endif /* LWIP_CHECKSUM_ON_COPY */
+    {
+      MEMCPY(buf.p->payload, data, short_size);
+    }
+    err = ERR_OK;
+  }
+#else /* LWIP_NETIF_TX_SINGLE_PBUF */
+  err = netbuf_ref(&buf, data, short_size);
+#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
+  if (err == ERR_OK) {
+#if LWIP_IPV4 && LWIP_IPV6
+    /* Dual-stack: Unmap IPv4 mapped IPv6 addresses */
+    if (IP_IS_V6_VAL(buf.addr) && ip6_addr_isipv4mappedipv6(ip_2_ip6(&buf.addr))) {
+      unmap_ipv4_mapped_ipv6(ip_2_ip4(&buf.addr), ip_2_ip6(&buf.addr));
+      IP_SET_TYPE_VAL(buf.addr, IPADDR_TYPE_V4);
+    }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
+    /* send the data */
+    err = netconn_send(sock->conn, &buf);
+  }
+
+  /* deallocated the buffer */
+  netbuf_free(&buf);
+
+  set_errno(err_to_errno(err));
+  done_socket(sock);
+  return (err == ERR_OK ? short_size : -1);
+}
+
+int
+lwip_socket(int domain, int type, int protocol)
+{
+  struct netconn *conn;
+  int i;
+
+  LWIP_UNUSED_ARG(domain); /* @todo: check this */
+
+  /* create a netconn */
+  switch (type) {
+    case SOCK_RAW:
+      conn = netconn_new_with_proto_and_callback(DOMAIN_TO_NETCONN_TYPE(domain, NETCONN_RAW),
+             (u8_t)protocol, DEFAULT_SOCKET_EVENTCB);
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_RAW, %d) = ",
+                                  domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol));
+      break;
+    case SOCK_DGRAM:
+      conn = netconn_new_with_callback(DOMAIN_TO_NETCONN_TYPE(domain,
+                                       ((protocol == IPPROTO_UDPLITE) ? NETCONN_UDPLITE : NETCONN_UDP)),
+                                       DEFAULT_SOCKET_EVENTCB);
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_DGRAM, %d) = ",
+                                  domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol));
+#if LWIP_NETBUF_RECVINFO
+      if (conn) {
+        /* netconn layer enables pktinfo by default, sockets default to off */
+        conn->flags &= ~NETCONN_FLAG_PKTINFO;
+      }
+#endif /* LWIP_NETBUF_RECVINFO */
+      break;
+    case SOCK_STREAM:
+      conn = netconn_new_with_callback(DOMAIN_TO_NETCONN_TYPE(domain, NETCONN_TCP), DEFAULT_SOCKET_EVENTCB);
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_STREAM, %d) = ",
+                                  domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol));
+      break;
+    default:
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%d, %d/UNKNOWN, %d) = -1\n",
+                                  domain, type, protocol));
+      set_errno(EINVAL);
+      return -1;
+  }
+
+  if (!conn) {
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("-1 / ENOBUFS (could not create netconn)\n"));
+    set_errno(ENOBUFS);
+    return -1;
+  }
+
+  i = alloc_socket(conn, 0);
+
+  if (i == -1) {
+    netconn_delete(conn);
+    set_errno(ENFILE);
+    return -1;
+  }
+  conn->callback_arg.socket = i;
+  done_socket(&sockets[i - LWIP_SOCKET_OFFSET]);
+  LWIP_DEBUGF(SOCKETS_DEBUG, ("%d\n", i));
+  set_errno(0);
+  return i;
+}
+
+ssize_t
+lwip_write(int s, const void *data, size_t size)
+{
+  return lwip_send(s, data, size, 0);
+}
+
+ssize_t
+lwip_writev(int s, const struct iovec *iov, int iovcnt)
+{
+  struct msghdr msg;
+
+  msg.msg_name = NULL;
+  msg.msg_namelen = 0;
+  /* Hack: we have to cast via number to cast from 'const' pointer to non-const.
+     Blame the opengroup standard for this inconsistency. */
+  msg.msg_iov = LWIP_CONST_CAST(struct iovec *, iov);
+  msg.msg_iovlen = iovcnt;
+  msg.msg_control = NULL;
+  msg.msg_controllen = 0;
+  msg.msg_flags = 0;
+  return lwip_sendmsg(s, &msg, 0);
+}
+
+#if LWIP_SOCKET_SELECT || LWIP_SOCKET_POLL
+/* Add select_cb to select_cb_list. */
+static void
+lwip_link_select_cb(struct lwip_select_cb *select_cb)
+{
+  LWIP_SOCKET_SELECT_DECL_PROTECT(lev);
+
+  /* Protect the select_cb_list */
+  LWIP_SOCKET_SELECT_PROTECT(lev);
+
+  /* Put this select_cb on top of list */
+  select_cb->next = select_cb_list;
+  if (select_cb_list != NULL) {
+    select_cb_list->prev = select_cb;
+  }
+  select_cb_list = select_cb;
+#if !LWIP_TCPIP_CORE_LOCKING
+  /* Increasing this counter tells select_check_waiters that the list has changed. */
+  select_cb_ctr++;
+#endif
+
+  /* Now we can safely unprotect */
+  LWIP_SOCKET_SELECT_UNPROTECT(lev);
+}
+
+/* Remove select_cb from select_cb_list. */
+static void
+lwip_unlink_select_cb(struct lwip_select_cb *select_cb)
+{
+  LWIP_SOCKET_SELECT_DECL_PROTECT(lev);
+
+  /* Take us off the list */
+  LWIP_SOCKET_SELECT_PROTECT(lev);
+  if (select_cb->next != NULL) {
+    select_cb->next->prev = select_cb->prev;
+  }
+  if (select_cb_list == select_cb) {
+    LWIP_ASSERT("select_cb->prev == NULL", select_cb->prev == NULL);
+    select_cb_list = select_cb->next;
+  } else {
+    LWIP_ASSERT("select_cb->prev != NULL", select_cb->prev != NULL);
+    select_cb->prev->next = select_cb->next;
+  }
+#if !LWIP_TCPIP_CORE_LOCKING
+  /* Increasing this counter tells select_check_waiters that the list has changed. */
+  select_cb_ctr++;
+#endif
+  LWIP_SOCKET_SELECT_UNPROTECT(lev);
+}
+#endif /* LWIP_SOCKET_SELECT || LWIP_SOCKET_POLL */
+
+#if LWIP_SOCKET_SELECT
+/**
+ * Go through the readset and writeset lists and see which socket of the sockets
+ * set in the sets has events. On return, readset, writeset and exceptset have
+ * the sockets enabled that had events.
+ *
+ * @param maxfdp1 the highest socket index in the sets
+ * @param readset_in    set of sockets to check for read events
+ * @param writeset_in   set of sockets to check for write events
+ * @param exceptset_in  set of sockets to check for error events
+ * @param readset_out   set of sockets that had read events
+ * @param writeset_out  set of sockets that had write events
+ * @param exceptset_out set os sockets that had error events
+ * @return number of sockets that had events (read/write/exception) (>= 0)
+ */
+static int
+lwip_selscan(int maxfdp1, fd_set *readset_in, fd_set *writeset_in, fd_set *exceptset_in,
+             fd_set *readset_out, fd_set *writeset_out, fd_set *exceptset_out)
+{
+  int i, nready = 0;
+  fd_set lreadset, lwriteset, lexceptset;
+  struct lwip_sock *sock;
+  SYS_ARCH_DECL_PROTECT(lev);
+
+  FD_ZERO(&lreadset);
+  FD_ZERO(&lwriteset);
+  FD_ZERO(&lexceptset);
+
+  /* Go through each socket in each list to count number of sockets which
+     currently match */
+  for (i = LWIP_SOCKET_OFFSET; i < maxfdp1; i++) {
+    /* if this FD is not in the set, continue */
+    if (!(readset_in && FD_ISSET(i, readset_in)) &&
+        !(writeset_in && FD_ISSET(i, writeset_in)) &&
+        !(exceptset_in && FD_ISSET(i, exceptset_in))) {
+      continue;
+    }
+    /* First get the socket's status (protected)... */
+    SYS_ARCH_PROTECT(lev);
+    sock = tryget_socket_unconn_locked(i);
+    if (sock != NULL) {
+      void *lastdata = sock->lastdata.pbuf;
+      s16_t rcvevent = sock->rcvevent;
+      u16_t sendevent = sock->sendevent;
+      u16_t errevent = sock->errevent;
+      SYS_ARCH_UNPROTECT(lev);
+
+      /* ... then examine it: */
+      /* See if netconn of this socket is ready for read */
+      if (readset_in && FD_ISSET(i, readset_in) && ((lastdata != NULL) || (rcvevent > 0))) {
+        FD_SET(i, &lreadset);
+        LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_selscan: fd=%d ready for reading\n", i));
+        nready++;
+      }
+      /* See if netconn of this socket is ready for write */
+      if (writeset_in && FD_ISSET(i, writeset_in) && (sendevent != 0)) {
+        FD_SET(i, &lwriteset);
+        LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_selscan: fd=%d ready for writing\n", i));
+        nready++;
+      }
+      /* See if netconn of this socket had an error */
+      if (exceptset_in && FD_ISSET(i, exceptset_in) && (errevent != 0)) {
+        FD_SET(i, &lexceptset);
+        LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_selscan: fd=%d ready for exception\n", i));
+        nready++;
+      }
+      done_socket(sock);
+    } else {
+      SYS_ARCH_UNPROTECT(lev);
+      /* no a valid open socket */
+      return -1;
+    }
+  }
+  /* copy local sets to the ones provided as arguments */
+  *readset_out = lreadset;
+  *writeset_out = lwriteset;
+  *exceptset_out = lexceptset;
+
+  LWIP_ASSERT("nready >= 0", nready >= 0);
+  return nready;
+}
+
+#if LWIP_NETCONN_FULLDUPLEX
+/* Mark all of the set sockets in one of the three fdsets passed to select as used.
+ * All sockets are marked (and later unmarked), whether they are open or not.
+ * This is OK as lwip_selscan aborts select when non-open sockets are found.
+ */
+static void
+lwip_select_inc_sockets_used_set(int maxfdp, fd_set *fdset, fd_set *used_sockets)
+{
+  SYS_ARCH_DECL_PROTECT(lev);
+  if (fdset) {
+    int i;
+    for (i = LWIP_SOCKET_OFFSET; i < maxfdp; i++) {
+      /* if this FD is in the set, lock it (unless already done) */
+      if (FD_ISSET(i, fdset) && !FD_ISSET(i, used_sockets)) {
+        struct lwip_sock *sock;
+        SYS_ARCH_PROTECT(lev);
+        sock = tryget_socket_unconn_locked(i);
+        if (sock != NULL) {
+          /* leave the socket used until released by lwip_select_dec_sockets_used */
+          FD_SET(i, used_sockets);
+        }
+        SYS_ARCH_UNPROTECT(lev);
+      }
+    }
+  }
+}
+
+/* Mark all sockets passed to select as used to prevent them from being freed
+ * from other threads while select is running.
+ * Marked sockets are added to 'used_sockets' to mark them only once an be able
+ * to unmark them correctly.
+ */
+static void
+lwip_select_inc_sockets_used(int maxfdp, fd_set *fdset1, fd_set *fdset2, fd_set *fdset3, fd_set *used_sockets)
+{
+  FD_ZERO(used_sockets);
+  lwip_select_inc_sockets_used_set(maxfdp, fdset1, used_sockets);
+  lwip_select_inc_sockets_used_set(maxfdp, fdset2, used_sockets);
+  lwip_select_inc_sockets_used_set(maxfdp, fdset3, used_sockets);
+}
+
+/* Let go all sockets that were marked as used when starting select */
+static void
+lwip_select_dec_sockets_used(int maxfdp, fd_set *used_sockets)
+{
+  int i;
+  for (i = LWIP_SOCKET_OFFSET; i < maxfdp; i++) {
+    /* if this FD is not in the set, continue */
+    if (FD_ISSET(i, used_sockets)) {
+      struct lwip_sock *sock = tryget_socket_unconn_nouse(i);
+      LWIP_ASSERT("socket gone at the end of select", sock != NULL);
+      if (sock != NULL) {
+        done_socket(sock);
+      }
+    }
+  }
+}
+#else /* LWIP_NETCONN_FULLDUPLEX */
+#define lwip_select_inc_sockets_used(maxfdp1, readset, writeset, exceptset, used_sockets)
+#define lwip_select_dec_sockets_used(maxfdp1, used_sockets)
+#endif /* LWIP_NETCONN_FULLDUPLEX */
+
+int
+lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset,
+            struct timeval *timeout)
+{
+  u32_t waitres = 0;
+  int nready;
+  fd_set lreadset, lwriteset, lexceptset;
+  u32_t msectimeout;
+  int i;
+  int maxfdp2;
+#if LWIP_NETCONN_SEM_PER_THREAD
+  int waited = 0;
+#endif
+#if LWIP_NETCONN_FULLDUPLEX
+  fd_set used_sockets;
+#endif
+  SYS_ARCH_DECL_PROTECT(lev);
+
+  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select(%d, %p, %p, %p, tvsec=%"S32_F" tvusec=%"S32_F")\n",
+                              maxfdp1, (void *)readset, (void *) writeset, (void *) exceptset,
+                              timeout ? (s32_t)timeout->tv_sec : (s32_t) - 1,
+                              timeout ? (s32_t)timeout->tv_usec : (s32_t) - 1));
+
+  if ((maxfdp1 < 0) || (maxfdp1 > LWIP_SELECT_MAXNFDS)) {
+    set_errno(EINVAL);
+    return -1;
+  }
+
+  lwip_select_inc_sockets_used(maxfdp1, readset, writeset, exceptset, &used_sockets);
+
+  /* Go through each socket in each list to count number of sockets which
+     currently match */
+  nready = lwip_selscan(maxfdp1, readset, writeset, exceptset, &lreadset, &lwriteset, &lexceptset);
+
+  if (nready < 0) {
+    /* one of the sockets in one of the fd_sets was invalid */
+    set_errno(EBADF);
+    lwip_select_dec_sockets_used(maxfdp1, &used_sockets);
+    return -1;
+  } else if (nready > 0) {
+    /* one or more sockets are set, no need to wait */
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: nready=%d\n", nready));
+  } else {
+    /* If we don't have any current events, then suspend if we are supposed to */
+    if (timeout && timeout->tv_sec == 0 && timeout->tv_usec == 0) {
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: no timeout, returning 0\n"));
+      /* This is OK as the local fdsets are empty and nready is zero,
+         or we would have returned earlier. */
+    } else {
+      /* None ready: add our semaphore to list:
+         We don't actually need any dynamic memory. Our entry on the
+         list is only valid while we are in this function, so it's ok
+         to use local variables (unless we're running in MPU compatible
+         mode). */
+      API_SELECT_CB_VAR_DECLARE(select_cb);
+      API_SELECT_CB_VAR_ALLOC(select_cb, set_errno(ENOMEM); lwip_select_dec_sockets_used(maxfdp1, &used_sockets); return -1);
+      memset(&API_SELECT_CB_VAR_REF(select_cb), 0, sizeof(struct lwip_select_cb));
+
+      API_SELECT_CB_VAR_REF(select_cb).readset = readset;
+      API_SELECT_CB_VAR_REF(select_cb).writeset = writeset;
+      API_SELECT_CB_VAR_REF(select_cb).exceptset = exceptset;
+#if LWIP_NETCONN_SEM_PER_THREAD
+      API_SELECT_CB_VAR_REF(select_cb).sem = LWIP_NETCONN_THREAD_SEM_GET();
+#else /* LWIP_NETCONN_SEM_PER_THREAD */
+      if (sys_sem_new(&API_SELECT_CB_VAR_REF(select_cb).sem, 0) != ERR_OK) {
+        /* failed to create semaphore */
+        set_errno(ENOMEM);
+        lwip_select_dec_sockets_used(maxfdp1, &used_sockets);
+        API_SELECT_CB_VAR_FREE(select_cb);
+        return -1;
+      }
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
+
+      lwip_link_select_cb(&API_SELECT_CB_VAR_REF(select_cb));
+
+      /* Increase select_waiting for each socket we are interested in */
+      maxfdp2 = maxfdp1;
+      for (i = LWIP_SOCKET_OFFSET; i < maxfdp1; i++) {
+        if ((readset && FD_ISSET(i, readset)) ||
+            (writeset && FD_ISSET(i, writeset)) ||
+            (exceptset && FD_ISSET(i, exceptset))) {
+          struct lwip_sock *sock;
+          SYS_ARCH_PROTECT(lev);
+          sock = tryget_socket_unconn_locked(i);
+          if (sock != NULL) {
+            sock->select_waiting++;
+            if (sock->select_waiting == 0) {
+              /* overflow - too many threads waiting */
+              sock->select_waiting--;
+              nready = -1;
+              maxfdp2 = i;
+              SYS_ARCH_UNPROTECT(lev);
+              done_socket(sock);
+              set_errno(EBUSY);
+              break;
+            }
+            SYS_ARCH_UNPROTECT(lev);
+            done_socket(sock);
+          } else {
+            /* Not a valid socket */
+            nready = -1;
+            maxfdp2 = i;
+            SYS_ARCH_UNPROTECT(lev);
+            set_errno(EBADF);
+            break;
+          }
+        }
+      }
+
+      if (nready >= 0) {
+        /* Call lwip_selscan again: there could have been events between
+           the last scan (without us on the list) and putting us on the list! */
+        nready = lwip_selscan(maxfdp1, readset, writeset, exceptset, &lreadset, &lwriteset, &lexceptset);
+        if (nready < 0) {
+          set_errno(EBADF);
+        } else if (!nready) {
+          /* Still none ready, just wait to be woken */
+          if (timeout == NULL) {
+            /* Wait forever */
+            msectimeout = 0;
+          } else {
+            long msecs_long = ((timeout->tv_sec * 1000) + ((timeout->tv_usec + 500) / 1000));
+            if (msecs_long <= 0) {
+              /* Wait 1ms at least (0 means wait forever) */
+              msectimeout = 1;
+            } else {
+              msectimeout = (u32_t)msecs_long;
+            }
+          }
+
+          waitres = sys_arch_sem_wait(SELECT_SEM_PTR(API_SELECT_CB_VAR_REF(select_cb).sem), msectimeout);
+#if LWIP_NETCONN_SEM_PER_THREAD
+          waited = 1;
+#endif
+        }
+      }
+
+      /* Decrease select_waiting for each socket we are interested in */
+      for (i = LWIP_SOCKET_OFFSET; i < maxfdp2; i++) {
+        if ((readset && FD_ISSET(i, readset)) ||
+            (writeset && FD_ISSET(i, writeset)) ||
+            (exceptset && FD_ISSET(i, exceptset))) {
+          struct lwip_sock *sock;
+          SYS_ARCH_PROTECT(lev);
+          sock = tryget_socket_unconn_nouse(i);
+          LWIP_ASSERT("socket gone at the end of select", sock != NULL);
+          if (sock != NULL) {
+            /* for now, handle select_waiting==0... */
+            LWIP_ASSERT("sock->select_waiting > 0", sock->select_waiting > 0);
+            if (sock->select_waiting > 0) {
+              sock->select_waiting--;
+            }
+            SYS_ARCH_UNPROTECT(lev);
+          } else {
+            SYS_ARCH_UNPROTECT(lev);
+            /* Not a valid socket */
+            nready = -1;
+            set_errno(EBADF);
+          }
+        }
+      }
+
+      lwip_unlink_select_cb(&API_SELECT_CB_VAR_REF(select_cb));
+
+#if LWIP_NETCONN_SEM_PER_THREAD
+      if (API_SELECT_CB_VAR_REF(select_cb).sem_signalled && (!waited || (waitres == SYS_ARCH_TIMEOUT))) {
+        /* don't leave the thread-local semaphore signalled */
+        sys_arch_sem_wait(API_SELECT_CB_VAR_REF(select_cb).sem, 1);
+      }
+#else /* LWIP_NETCONN_SEM_PER_THREAD */
+      sys_sem_free(&API_SELECT_CB_VAR_REF(select_cb).sem);
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
+      API_SELECT_CB_VAR_FREE(select_cb);
+
+      if (nready < 0) {
+        /* This happens when a socket got closed while waiting */
+        lwip_select_dec_sockets_used(maxfdp1, &used_sockets);
+        return -1;
+      }
+
+      if (waitres == SYS_ARCH_TIMEOUT) {
+        /* Timeout */
+        LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: timeout expired\n"));
+        /* This is OK as the local fdsets are empty and nready is zero,
+           or we would have returned earlier. */
+      } else {
+        /* See what's set now after waiting */
+        nready = lwip_selscan(maxfdp1, readset, writeset, exceptset, &lreadset, &lwriteset, &lexceptset);
+        LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: nready=%d\n", nready));
+        if (nready < 0) {
+          set_errno(EBADF);
+          lwip_select_dec_sockets_used(maxfdp1, &used_sockets);
+          return -1;
+        }
+      }
+    }
+  }
+
+  lwip_select_dec_sockets_used(maxfdp1, &used_sockets);
+  set_errno(0);
+  if (readset) {
+    *readset = lreadset;
+  }
+  if (writeset) {
+    *writeset = lwriteset;
+  }
+  if (exceptset) {
+    *exceptset = lexceptset;
+  }
+  return nready;
+}
+#endif /* LWIP_SOCKET_SELECT */
+
+#if LWIP_SOCKET_POLL
+/** Options for the lwip_pollscan function. */
+enum lwip_pollscan_opts
+{
+  /** Clear revents in each struct pollfd. */
+  LWIP_POLLSCAN_CLEAR = 1,
+
+  /** Increment select_waiting in each struct lwip_sock. */
+  LWIP_POLLSCAN_INC_WAIT = 2,
+
+  /** Decrement select_waiting in each struct lwip_sock. */
+  LWIP_POLLSCAN_DEC_WAIT = 4
+};
+
+/**
+ * Update revents in each struct pollfd.
+ * Optionally update select_waiting in struct lwip_sock.
+ *
+ * @param fds          array of structures to update
+ * @param nfds         number of structures in fds
+ * @param opts         what to update and how
+ * @return number of structures that have revents != 0
+ */
+static int
+lwip_pollscan(struct pollfd *fds, nfds_t nfds, enum lwip_pollscan_opts opts)
+{
+  int nready = 0;
+  nfds_t fdi;
+  struct lwip_sock *sock;
+  SYS_ARCH_DECL_PROTECT(lev);
+
+  /* Go through each struct pollfd in the array. */
+  for (fdi = 0; fdi < nfds; fdi++) {
+    if ((opts & LWIP_POLLSCAN_CLEAR) != 0) {
+      fds[fdi].revents = 0;
+    }
+
+    /* Negative fd means the caller wants us to ignore this struct.
+       POLLNVAL means we already detected that the fd is invalid;
+       if another thread has since opened a new socket with that fd,
+       we must not use that socket. */
+    if (fds[fdi].fd >= 0 && (fds[fdi].revents & POLLNVAL) == 0) {
+      /* First get the socket's status (protected)... */
+      SYS_ARCH_PROTECT(lev);
+      sock = tryget_socket_unconn_locked(fds[fdi].fd);
+      if (sock != NULL) {
+        void* lastdata = sock->lastdata.pbuf;
+        s16_t rcvevent = sock->rcvevent;
+        u16_t sendevent = sock->sendevent;
+        u16_t errevent = sock->errevent;
+
+        if ((opts & LWIP_POLLSCAN_INC_WAIT) != 0) {
+          sock->select_waiting++;
+          if (sock->select_waiting == 0) {
+            /* overflow - too many threads waiting */
+            sock->select_waiting--;
+            nready = -1;
+            SYS_ARCH_UNPROTECT(lev);
+            done_socket(sock);
+            break;
+          }
+        } else if ((opts & LWIP_POLLSCAN_DEC_WAIT) != 0) {
+          /* for now, handle select_waiting==0... */
+          LWIP_ASSERT("sock->select_waiting > 0", sock->select_waiting > 0);
+          if (sock->select_waiting > 0) {
+            sock->select_waiting--;
+          }
+        }
+        SYS_ARCH_UNPROTECT(lev);
+        done_socket(sock);
+
+        /* ... then examine it: */
+        /* See if netconn of this socket is ready for read */
+        if ((fds[fdi].events & POLLIN) != 0 && ((lastdata != NULL) || (rcvevent > 0))) {
+          fds[fdi].revents |= POLLIN;
+          LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_pollscan: fd=%d ready for reading\n", fds[fdi].fd));
+        }
+        /* See if netconn of this socket is ready for write */
+        if ((fds[fdi].events & POLLOUT) != 0 && (sendevent != 0)) {
+          fds[fdi].revents |= POLLOUT;
+          LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_pollscan: fd=%d ready for writing\n", fds[fdi].fd));
+        }
+        /* See if netconn of this socket had an error */
+        if (errevent != 0) {
+          /* POLLERR is output only. */
+          fds[fdi].revents |= POLLERR;
+          LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_pollscan: fd=%d ready for exception\n", fds[fdi].fd));
+        }
+      } else {
+        /* Not a valid socket */
+        SYS_ARCH_UNPROTECT(lev);
+        /* POLLNVAL is output only. */
+        fds[fdi].revents |= POLLNVAL;
+        return -1;
+      }
+    }
+
+    /* Will return the number of structures that have events,
+       not the number of events. */
+    if (fds[fdi].revents != 0) {
+      nready++;
+    }
+  }
+
+  LWIP_ASSERT("nready >= 0", nready >= 0);
+  return nready;
+}
+
+#if LWIP_NETCONN_FULLDUPLEX
+/* Mark all sockets as used.
+ *
+ * All sockets are marked (and later unmarked), whether they are open or not.
+ * This is OK as lwip_pollscan aborts select when non-open sockets are found.
+ */
+static void
+lwip_poll_inc_sockets_used(struct pollfd *fds, nfds_t nfds)
+{
+  nfds_t fdi;
+
+  if(fds) {
+    /* Go through each struct pollfd in the array. */
+    for (fdi = 0; fdi < nfds; fdi++) {
+      /* Increase the reference counter */
+      tryget_socket_unconn(fds[fdi].fd);
+    }
+  }
+}
+
+/* Let go all sockets that were marked as used when starting poll */
+static void
+lwip_poll_dec_sockets_used(struct pollfd *fds, nfds_t nfds)
+{
+  nfds_t fdi;
+
+  if(fds) {
+    /* Go through each struct pollfd in the array. */
+    for (fdi = 0; fdi < nfds; fdi++) {
+      struct lwip_sock *sock = tryget_socket_unconn_nouse(fds[fdi].fd);
+      if (sock != NULL) {
+        done_socket(sock);
+      }
+    }
+  }
+}
+#else /* LWIP_NETCONN_FULLDUPLEX */
+#define lwip_poll_inc_sockets_used(fds, nfds)
+#define lwip_poll_dec_sockets_used(fds, nfds)
+#endif /* LWIP_NETCONN_FULLDUPLEX */
+
+int
+lwip_poll(struct pollfd *fds, nfds_t nfds, int timeout)
+{
+  u32_t waitres = 0;
+  int nready;
+  u32_t msectimeout;
+#if LWIP_NETCONN_SEM_PER_THREAD
+  int waited = 0;
+#endif
+
+  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_poll(%p, %d, %d)\n",
+                  (void*)fds, (int)nfds, timeout));
+  LWIP_ERROR("lwip_poll: invalid fds", ((fds != NULL && nfds > 0) || (fds == NULL && nfds == 0)),
+             set_errno(EINVAL); return -1;);
+
+  lwip_poll_inc_sockets_used(fds, nfds);
+
+  /* Go through each struct pollfd to count number of structures
+     which currently match */
+  nready = lwip_pollscan(fds, nfds, LWIP_POLLSCAN_CLEAR);
+
+  if (nready < 0) {
+    lwip_poll_dec_sockets_used(fds, nfds);
+    return -1;
+  }
+
+  /* If we don't have any current events, then suspend if we are supposed to */
+  if (!nready) {
+    API_SELECT_CB_VAR_DECLARE(select_cb);
+
+    if (timeout == 0) {
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_poll: no timeout, returning 0\n"));
+      goto return_success;
+    }
+    API_SELECT_CB_VAR_ALLOC(select_cb, set_errno(EAGAIN); lwip_poll_dec_sockets_used(fds, nfds); return -1);
+    memset(&API_SELECT_CB_VAR_REF(select_cb), 0, sizeof(struct lwip_select_cb));
+
+    /* None ready: add our semaphore to list:
+       We don't actually need any dynamic memory. Our entry on the
+       list is only valid while we are in this function, so it's ok
+       to use local variables. */
+
+    API_SELECT_CB_VAR_REF(select_cb).poll_fds = fds;
+    API_SELECT_CB_VAR_REF(select_cb).poll_nfds = nfds;
+#if LWIP_NETCONN_SEM_PER_THREAD
+    API_SELECT_CB_VAR_REF(select_cb).sem = LWIP_NETCONN_THREAD_SEM_GET();
+#else /* LWIP_NETCONN_SEM_PER_THREAD */
+    if (sys_sem_new(&API_SELECT_CB_VAR_REF(select_cb).sem, 0) != ERR_OK) {
+      /* failed to create semaphore */
+      set_errno(EAGAIN);
+      lwip_poll_dec_sockets_used(fds, nfds);
+      API_SELECT_CB_VAR_FREE(select_cb);
+      return -1;
+    }
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
+
+    lwip_link_select_cb(&API_SELECT_CB_VAR_REF(select_cb));
+
+    /* Increase select_waiting for each socket we are interested in.
+       Also, check for events again: there could have been events between
+       the last scan (without us on the list) and putting us on the list! */
+    nready = lwip_pollscan(fds, nfds, LWIP_POLLSCAN_INC_WAIT);
+
+    if (!nready) {
+      /* Still none ready, just wait to be woken */
+      if (timeout < 0) {
+        /* Wait forever */
+        msectimeout = 0;
+      } else {
+        /* timeout == 0 would have been handled earlier. */
+        LWIP_ASSERT("timeout > 0", timeout > 0);
+        msectimeout = timeout;
+      }
+      waitres = sys_arch_sem_wait(SELECT_SEM_PTR(API_SELECT_CB_VAR_REF(select_cb).sem), msectimeout);
+#if LWIP_NETCONN_SEM_PER_THREAD
+      waited = 1;
+#endif
+    }
+
+    /* Decrease select_waiting for each socket we are interested in,
+       and check which events occurred while we waited. */
+    nready = lwip_pollscan(fds, nfds, LWIP_POLLSCAN_DEC_WAIT);
+
+    lwip_unlink_select_cb(&API_SELECT_CB_VAR_REF(select_cb));
+
+#if LWIP_NETCONN_SEM_PER_THREAD
+    if (select_cb.sem_signalled && (!waited || (waitres == SYS_ARCH_TIMEOUT))) {
+      /* don't leave the thread-local semaphore signalled */
+      sys_arch_sem_wait(API_SELECT_CB_VAR_REF(select_cb).sem, 1);
+    }
+#else /* LWIP_NETCONN_SEM_PER_THREAD */
+    sys_sem_free(&API_SELECT_CB_VAR_REF(select_cb).sem);
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
+    API_SELECT_CB_VAR_FREE(select_cb);
+
+    if (nready < 0) {
+      /* This happens when a socket got closed while waiting */
+      lwip_poll_dec_sockets_used(fds, nfds);
+      return -1;
+    }
+
+    if (waitres == SYS_ARCH_TIMEOUT) {
+      /* Timeout */
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_poll: timeout expired\n"));
+      goto return_success;
+    }
+  }
+
+  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_poll: nready=%d\n", nready));
+return_success:
+  lwip_poll_dec_sockets_used(fds, nfds);
+  set_errno(0);
+  return nready;
+}
+
+/**
+ * Check whether event_callback should wake up a thread waiting in
+ * lwip_poll.
+ */
+static int
+lwip_poll_should_wake(const struct lwip_select_cb *scb, int fd, int has_recvevent, int has_sendevent, int has_errevent)
+{
+  nfds_t fdi;
+  for (fdi = 0; fdi < scb->poll_nfds; fdi++) {
+    const struct pollfd *pollfd = &scb->poll_fds[fdi];
+    if (pollfd->fd == fd) {
+      /* Do not update pollfd->revents right here;
+         that would be a data race because lwip_pollscan
+         accesses revents without protecting. */
+      if (has_recvevent && (pollfd->events & POLLIN) != 0) {
+        return 1;
+      }
+      if (has_sendevent && (pollfd->events & POLLOUT) != 0) {
+        return 1;
+      }
+      if (has_errevent) {
+        /* POLLERR is output only. */
+        return 1;
+      }
+    }
+  }
+  return 0;
+}
+#endif /* LWIP_SOCKET_POLL */
+
+#if LWIP_SOCKET_SELECT || LWIP_SOCKET_POLL
+/**
+ * Callback registered in the netconn layer for each socket-netconn.
+ * Processes recvevent (data available) and wakes up tasks waiting for select.
+ *
+ * @note for LWIP_TCPIP_CORE_LOCKING any caller of this function
+ * must have the core lock held when signaling the following events
+ * as they might cause select_list_cb to be checked:
+ *   NETCONN_EVT_RCVPLUS
+ *   NETCONN_EVT_SENDPLUS
+ *   NETCONN_EVT_ERROR
+ * This requirement will be asserted in select_check_waiters()
+ */
+static void
+event_callback(struct netconn *conn, enum netconn_evt evt, u16_t len)
+{
+  int s, check_waiters;
+  struct lwip_sock *sock;
+  SYS_ARCH_DECL_PROTECT(lev);
+
+  LWIP_UNUSED_ARG(len);
+
+  /* Get socket */
+  if (conn) {
+    s = conn->callback_arg.socket;
+    if (s < 0) {
+      /* Data comes in right away after an accept, even though
+       * the server task might not have created a new socket yet.
+       * Just count down (or up) if that's the case and we
+       * will use the data later. Note that only receive events
+       * can happen before the new socket is set up. */
+      SYS_ARCH_PROTECT(lev);
+      if (conn->callback_arg.socket < 0) {
+        if (evt == NETCONN_EVT_RCVPLUS) {
+          /* conn->socket is -1 on initialization
+             lwip_accept adjusts sock->recvevent if conn->socket < -1 */
+          conn->callback_arg.socket--;
+        }
+        SYS_ARCH_UNPROTECT(lev);
+        return;
+      }
+      s = conn->callback_arg.socket;
+      SYS_ARCH_UNPROTECT(lev);
+    }
+
+    sock = get_socket(s);
+    if (!sock) {
+      return;
+    }
+  } else {
+    return;
+  }
+
+  check_waiters = 1;
+  SYS_ARCH_PROTECT(lev);
+  /* Set event as required */
+  switch (evt) {
+    case NETCONN_EVT_RCVPLUS:
+      sock->rcvevent++;
+      if (sock->rcvevent > 1) {
+        check_waiters = 0;
+      }
+      break;
+    case NETCONN_EVT_RCVMINUS:
+      sock->rcvevent--;
+      check_waiters = 0;
+      break;
+    case NETCONN_EVT_SENDPLUS:
+      if (sock->sendevent) {
+        check_waiters = 0;
+      }
+      sock->sendevent = 1;
+      break;
+    case NETCONN_EVT_SENDMINUS:
+      sock->sendevent = 0;
+      check_waiters = 0;
+      break;
+    case NETCONN_EVT_ERROR:
+      sock->errevent = 1;
+      break;
+    default:
+      LWIP_ASSERT("unknown event", 0);
+      break;
+  }
+
+  if (sock->select_waiting && check_waiters) {
+    /* Save which events are active */
+    int has_recvevent, has_sendevent, has_errevent;
+    has_recvevent = sock->rcvevent > 0;
+    has_sendevent = sock->sendevent != 0;
+    has_errevent = sock->errevent != 0;
+    SYS_ARCH_UNPROTECT(lev);
+    /* Check any select calls waiting on this socket */
+    select_check_waiters(s, has_recvevent, has_sendevent, has_errevent);
+  } else {
+    SYS_ARCH_UNPROTECT(lev);
+  }
+  done_socket(sock);
+}
+
+/**
+ * Check if any select waiters are waiting on this socket and its events
+ *
+ * @note on synchronization of select_cb_list:
+ * LWIP_TCPIP_CORE_LOCKING: the select_cb_list must only be accessed while holding
+ * the core lock. We do a single pass through the list and signal any waiters.
+ * Core lock should already be held when calling here!!!!
+
+ * !LWIP_TCPIP_CORE_LOCKING: we use SYS_ARCH_PROTECT but unlock on each iteration
+ * of the loop, thus creating a possibility where a thread could modify the
+ * select_cb_list during our UNPROTECT/PROTECT. We use a generational counter to
+ * detect this change and restart the list walk. The list is expected to be small
+ */
+static void select_check_waiters(int s, int has_recvevent, int has_sendevent, int has_errevent)
+{
+  struct lwip_select_cb *scb;
+#if !LWIP_TCPIP_CORE_LOCKING
+  int last_select_cb_ctr;
+  SYS_ARCH_DECL_PROTECT(lev);
+#endif /* !LWIP_TCPIP_CORE_LOCKING */
+
+  LWIP_ASSERT_CORE_LOCKED();
+
+#if !LWIP_TCPIP_CORE_LOCKING
+  SYS_ARCH_PROTECT(lev);
+again:
+  /* remember the state of select_cb_list to detect changes */
+  last_select_cb_ctr = select_cb_ctr;
+#endif /* !LWIP_TCPIP_CORE_LOCKING */
+  for (scb = select_cb_list; scb != NULL; scb = scb->next) {
+    if (scb->sem_signalled == 0) {
+      /* semaphore not signalled yet */
+      int do_signal = 0;
+#if LWIP_SOCKET_POLL
+      if (scb->poll_fds != NULL) {
+        do_signal = lwip_poll_should_wake(scb, s, has_recvevent, has_sendevent, has_errevent);
+      }
+#endif /* LWIP_SOCKET_POLL */
+#if LWIP_SOCKET_SELECT && LWIP_SOCKET_POLL
+      else
+#endif /* LWIP_SOCKET_SELECT && LWIP_SOCKET_POLL */
+#if LWIP_SOCKET_SELECT
+      {
+        /* Test this select call for our socket */
+        if (has_recvevent) {
+          if (scb->readset && FD_ISSET(s, scb->readset)) {
+            do_signal = 1;
+          }
+        }
+        if (has_sendevent) {
+          if (!do_signal && scb->writeset && FD_ISSET(s, scb->writeset)) {
+            do_signal = 1;
+          }
+        }
+        if (has_errevent) {
+          if (!do_signal && scb->exceptset && FD_ISSET(s, scb->exceptset)) {
+            do_signal = 1;
+          }
+        }
+      }
+#endif /* LWIP_SOCKET_SELECT */
+      if (do_signal) {
+        scb->sem_signalled = 1;
+        /* For !LWIP_TCPIP_CORE_LOCKING, we don't call SYS_ARCH_UNPROTECT() before signaling
+           the semaphore, as this might lead to the select thread taking itself off the list,
+           invalidating the semaphore. */
+        sys_sem_signal(SELECT_SEM_PTR(scb->sem));
+      }
+    }
+#if LWIP_TCPIP_CORE_LOCKING
+  }
+#else
+    /* unlock interrupts with each step */
+    SYS_ARCH_UNPROTECT(lev);
+    /* this makes sure interrupt protection time is short */
+    SYS_ARCH_PROTECT(lev);
+    if (last_select_cb_ctr != select_cb_ctr) {
+      /* someone has changed select_cb_list, restart at the beginning */
+      goto again;
+    }
+    /* remember the state of select_cb_list to detect changes */
+    last_select_cb_ctr = select_cb_ctr;
+  }
+  SYS_ARCH_UNPROTECT(lev);
+#endif
+}
+#endif /* LWIP_SOCKET_SELECT || LWIP_SOCKET_POLL */
+
+/**
+ * Close one end of a full-duplex connection.
+ */
+int
+lwip_shutdown(int s, int how)
+{
+  struct lwip_sock *sock;
+  err_t err;
+  u8_t shut_rx = 0, shut_tx = 0;
+
+  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_shutdown(%d, how=%d)\n", s, how));
+
+  sock = get_socket(s);
+  if (!sock) {
+    return -1;
+  }
+
+  if (sock->conn != NULL) {
+    if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
+      set_errno(EOPNOTSUPP);
+      done_socket(sock);
+      return -1;
+    }
+  } else {
+    set_errno(ENOTCONN);
+    done_socket(sock);
+    return -1;
+  }
+
+  if (how == SHUT_RD) {
+    shut_rx = 1;
+  } else if (how == SHUT_WR) {
+    shut_tx = 1;
+  } else if (how == SHUT_RDWR) {
+    shut_rx = 1;
+    shut_tx = 1;
+  } else {
+    set_errno(EINVAL);
+    done_socket(sock);
+    return -1;
+  }
+  err = netconn_shutdown(sock->conn, shut_rx, shut_tx);
+
+  set_errno(err_to_errno(err));
+  done_socket(sock);
+  return (err == ERR_OK ? 0 : -1);
+}
+
+static int
+lwip_getaddrname(int s, struct sockaddr *name, socklen_t *namelen, u8_t local)
+{
+  struct lwip_sock *sock;
+  union sockaddr_aligned saddr;
+  ip_addr_t naddr;
+  u16_t port;
+  err_t err;
+
+  sock = get_socket(s);
+  if (!sock) {
+    return -1;
+  }
+
+  /* get the IP address and port */
+  err = netconn_getaddr(sock->conn, &naddr, &port, local);
+  if (err != ERR_OK) {
+    set_errno(err_to_errno(err));
+    done_socket(sock);
+    return -1;
+  }
+
+#if LWIP_IPV4 && LWIP_IPV6
+  /* Dual-stack: Map IPv4 addresses to IPv4 mapped IPv6 */
+  if (NETCONNTYPE_ISIPV6(netconn_type(sock->conn)) &&
+      IP_IS_V4_VAL(naddr)) {
+    ip4_2_ipv4_mapped_ipv6(ip_2_ip6(&naddr), ip_2_ip4(&naddr));
+    IP_SET_TYPE_VAL(naddr, IPADDR_TYPE_V6);
+  }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
+  IPADDR_PORT_TO_SOCKADDR(&saddr, &naddr, port);
+
+  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getaddrname(%d, addr=", s));
+  ip_addr_debug_print_val(SOCKETS_DEBUG, naddr);
+  LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", port));
+
+  if (*namelen > IPADDR_SOCKADDR_GET_LEN(&saddr)) {
+    *namelen = IPADDR_SOCKADDR_GET_LEN(&saddr);
+  }
+  MEMCPY(name, &saddr, *namelen);
+
+  set_errno(0);
+  done_socket(sock);
+  return 0;
+}
+
+int
+lwip_getpeername(int s, struct sockaddr *name, socklen_t *namelen)
+{
+  return lwip_getaddrname(s, name, namelen, 0);
+}
+
+int
+lwip_getsockname(int s, struct sockaddr *name, socklen_t *namelen)
+{
+  return lwip_getaddrname(s, name, namelen, 1);
+}
+
+int
+lwip_getsockopt(int s, int level, int optname, void *optval, socklen_t *optlen)
+{
+  int err;
+  struct lwip_sock *sock = get_socket(s);
+#if !LWIP_TCPIP_CORE_LOCKING
+  err_t cberr;
+  LWIP_SETGETSOCKOPT_DATA_VAR_DECLARE(data);
+#endif /* !LWIP_TCPIP_CORE_LOCKING */
+
+  if (!sock) {
+    return -1;
+  }
+
+  if ((NULL == optval) || (NULL == optlen)) {
+    set_errno(EFAULT);
+    done_socket(sock);
+    return -1;
+  }
+
+#if LWIP_TCPIP_CORE_LOCKING
+  /* core-locking can just call the -impl function */
+  LOCK_TCPIP_CORE();
+  err = lwip_getsockopt_impl(s, level, optname, optval, optlen);
+  UNLOCK_TCPIP_CORE();
+
+#else /* LWIP_TCPIP_CORE_LOCKING */
+
+#if LWIP_MPU_COMPATIBLE
+  /* MPU_COMPATIBLE copies the optval data, so check for max size here */
+  if (*optlen > LWIP_SETGETSOCKOPT_MAXOPTLEN) {
+    set_errno(ENOBUFS);
+    done_socket(sock);
+    return -1;
+  }
+#endif /* LWIP_MPU_COMPATIBLE */
+
+  LWIP_SETGETSOCKOPT_DATA_VAR_ALLOC(data, sock);
+  LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).s = s;
+  LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).level = level;
+  LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optname = optname;
+  LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optlen = *optlen;
+#if !LWIP_MPU_COMPATIBLE
+  LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optval.p = optval;
+#endif /* !LWIP_MPU_COMPATIBLE */
+  LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).err = 0;
+#if LWIP_NETCONN_SEM_PER_THREAD
+  LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem = LWIP_NETCONN_THREAD_SEM_GET();
+#else
+  LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem = &sock->conn->op_completed;
+#endif
+  cberr = tcpip_callback(lwip_getsockopt_callback, &LWIP_SETGETSOCKOPT_DATA_VAR_REF(data));
+  if (cberr != ERR_OK) {
+    LWIP_SETGETSOCKOPT_DATA_VAR_FREE(data);
+    set_errno(err_to_errno(cberr));
+    done_socket(sock);
+    return -1;
+  }
+  sys_arch_sem_wait((sys_sem_t *)(LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem), 0);
+
+  /* write back optlen and optval */
+  *optlen = LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optlen;
+#if LWIP_MPU_COMPATIBLE
+  MEMCPY(optval, LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optval,
+         LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optlen);
+#endif /* LWIP_MPU_COMPATIBLE */
+
+  /* maybe lwip_getsockopt_impl has changed err */
+  err = LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).err;
+  LWIP_SETGETSOCKOPT_DATA_VAR_FREE(data);
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+
+  set_errno(err);
+  done_socket(sock);
+  return err ? -1 : 0;
+}
+
+#if !LWIP_TCPIP_CORE_LOCKING
+/** lwip_getsockopt_callback: only used without CORE_LOCKING
+ * to get into the tcpip_thread
+ */
+static void
+lwip_getsockopt_callback(void *arg)
+{
+  struct lwip_setgetsockopt_data *data;
+  LWIP_ASSERT("arg != NULL", arg != NULL);
+  data = (struct lwip_setgetsockopt_data *)arg;
+
+  data->err = lwip_getsockopt_impl(data->s, data->level, data->optname,
+#if LWIP_MPU_COMPATIBLE
+                                   data->optval,
+#else /* LWIP_MPU_COMPATIBLE */
+                                   data->optval.p,
+#endif /* LWIP_MPU_COMPATIBLE */
+                                   &data->optlen);
+
+  sys_sem_signal((sys_sem_t *)(data->completed_sem));
+}
+#endif  /* LWIP_TCPIP_CORE_LOCKING */
+
+static int
+lwip_sockopt_to_ipopt(int optname)
+{
+  /* Map SO_* values to our internal SOF_* values
+   * We should not rely on #defines in socket.h
+   * being in sync with ip.h.
+   */
+  switch (optname) {
+  case SO_BROADCAST:
+    return SOF_BROADCAST;
+  case SO_KEEPALIVE:
+    return SOF_KEEPALIVE;
+  case SO_REUSEADDR:
+    return SOF_REUSEADDR;
+  default:
+    LWIP_ASSERT("Unknown socket option", 0);
+    return 0;
+  }
+}
+
+/** lwip_getsockopt_impl: the actual implementation of getsockopt:
+ * same argument as lwip_getsockopt, either called directly or through callback
+ */
+static int
+lwip_getsockopt_impl(int s, int level, int optname, void *optval, socklen_t *optlen)
+{
+  int err = 0;
+  struct lwip_sock *sock = tryget_socket(s);
+  if (!sock) {
+    return EBADF;
+  }
+
+#ifdef LWIP_HOOK_SOCKETS_GETSOCKOPT
+  if (LWIP_HOOK_SOCKETS_GETSOCKOPT(s, sock, level, optname, optval, optlen, &err)) {
+    return err;
+  }
+#endif
+
+  switch (level) {
+
+    /* Level: SOL_SOCKET */
+    case SOL_SOCKET:
+      switch (optname) {
+
+#if LWIP_TCP
+        case SO_ACCEPTCONN:
+          LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, int);
+          if (NETCONNTYPE_GROUP(sock->conn->type) != NETCONN_TCP) {
+            done_socket(sock);
+            return ENOPROTOOPT;
+          }
+          if ((sock->conn->pcb.tcp != NULL) && (sock->conn->pcb.tcp->state == LISTEN)) {
+            *(int *)optval = 1;
+          } else {
+            *(int *)optval = 0;
+          }
+          break;
+#endif /* LWIP_TCP */
+
+        /* The option flags */
+        case SO_BROADCAST:
+        case SO_KEEPALIVE:
+#if SO_REUSE
+        case SO_REUSEADDR:
+#endif /* SO_REUSE */
+          if ((optname == SO_BROADCAST) &&
+              (NETCONNTYPE_GROUP(sock->conn->type) != NETCONN_UDP)) {
+            done_socket(sock);
+            return ENOPROTOOPT;
+          }
+
+          optname = lwip_sockopt_to_ipopt(optname);
+
+          LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, int);
+          *(int *)optval = ip_get_option(sock->conn->pcb.ip, optname);
+          LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, optname=0x%x, ..) = %s\n",
+                                      s, optname, (*(int *)optval ? "on" : "off")));
+          break;
+
+        case SO_TYPE:
+          LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, int);
+          switch (NETCONNTYPE_GROUP(netconn_type(sock->conn))) {
+            case NETCONN_RAW:
+              *(int *)optval = SOCK_RAW;
+              break;
+            case NETCONN_TCP:
+              *(int *)optval = SOCK_STREAM;
+              break;
+            case NETCONN_UDP:
+              *(int *)optval = SOCK_DGRAM;
+              break;
+            default: /* unrecognized socket type */
+              *(int *)optval = netconn_type(sock->conn);
+              LWIP_DEBUGF(SOCKETS_DEBUG,
+                          ("lwip_getsockopt(%d, SOL_SOCKET, SO_TYPE): unrecognized socket type %d\n",
+                           s, *(int *)optval));
+          }  /* switch (netconn_type(sock->conn)) */
+          LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, SO_TYPE) = %d\n",
+                                      s, *(int *)optval));
+          break;
+
+        case SO_ERROR:
+          LWIP_SOCKOPT_CHECK_OPTLEN(sock, *optlen, int);
+          *(int *)optval = err_to_errno(netconn_err(sock->conn));
+          LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, SO_ERROR) = %d\n",
+                                      s, *(int *)optval));
+          break;
+
+#if LWIP_SO_SNDTIMEO
+        case SO_SNDTIMEO:
+          LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, LWIP_SO_SNDRCVTIMEO_OPTTYPE);
+          LWIP_SO_SNDRCVTIMEO_SET(optval, netconn_get_sendtimeout(sock->conn));
+          break;
+#endif /* LWIP_SO_SNDTIMEO */
+#if LWIP_SO_RCVTIMEO
+        case SO_RCVTIMEO:
+          LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, LWIP_SO_SNDRCVTIMEO_OPTTYPE);
+          LWIP_SO_SNDRCVTIMEO_SET(optval, netconn_get_recvtimeout(sock->conn));
+          break;
+#endif /* LWIP_SO_RCVTIMEO */
+#if LWIP_SO_RCVBUF
+        case SO_RCVBUF:
+          LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, int);
+          *(int *)optval = netconn_get_recvbufsize(sock->conn);
+          break;
+#endif /* LWIP_SO_RCVBUF */
+#if LWIP_SO_LINGER
+        case SO_LINGER: {
+          s16_t conn_linger;
+          struct linger *linger = (struct linger *)optval;
+          LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, struct linger);
+          conn_linger = sock->conn->linger;
+          if (conn_linger >= 0) {
+            linger->l_onoff = 1;
+            linger->l_linger = (int)conn_linger;
+          } else {
+            linger->l_onoff = 0;
+            linger->l_linger = 0;
+          }
+        }
+        break;
+#endif /* LWIP_SO_LINGER */
+#if LWIP_UDP
+        case SO_NO_CHECK:
+          LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, *optlen, int, NETCONN_UDP);
+#if LWIP_UDPLITE
+          if (udp_is_flag_set(sock->conn->pcb.udp, UDP_FLAGS_UDPLITE)) {
+            /* this flag is only available for UDP, not for UDP lite */
+            done_socket(sock);
+            return EAFNOSUPPORT;
+          }
+#endif /* LWIP_UDPLITE */
+          *(int *)optval = udp_is_flag_set(sock->conn->pcb.udp, UDP_FLAGS_NOCHKSUM) ? 1 : 0;
+          break;
+#endif /* LWIP_UDP*/
+        default:
+          LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, UNIMPL: optname=0x%x, ..)\n",
+                                      s, optname));
+          err = ENOPROTOOPT;
+          break;
+      }  /* switch (optname) */
+      break;
+
+    /* Level: IPPROTO_IP */
+    case IPPROTO_IP:
+      switch (optname) {
+        case IP_TTL:
+          LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, int);
+          *(int *)optval = sock->conn->pcb.ip->ttl;
+          LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_TTL) = %d\n",
+                                      s, *(int *)optval));
+          break;
+        case IP_TOS:
+          LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, int);
+          *(int *)optval = sock->conn->pcb.ip->tos;
+          LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_TOS) = %d\n",
+                                      s, *(int *)optval));
+          break;
+#if LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS && LWIP_UDP
+        case IP_MULTICAST_TTL:
+          LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, u8_t);
+          if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) {
+            done_socket(sock);
+            return ENOPROTOOPT;
+          }
+          *(u8_t *)optval = udp_get_multicast_ttl(sock->conn->pcb.udp);
+          LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_TTL) = %d\n",
+                                      s, *(int *)optval));
+          break;
+        case IP_MULTICAST_IF:
+          LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, struct in_addr);
+          if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) {
+            done_socket(sock);
+            return ENOPROTOOPT;
+          }
+          inet_addr_from_ip4addr((struct in_addr *)optval, udp_get_multicast_netif_addr(sock->conn->pcb.udp));
+          LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_IF) = 0x%"X32_F"\n",
+                                      s, *(u32_t *)optval));
+          break;
+        case IP_MULTICAST_LOOP:
+          LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, u8_t);
+          if ((sock->conn->pcb.udp->flags & UDP_FLAGS_MULTICAST_LOOP) != 0) {
+            *(u8_t *)optval = 1;
+          } else {
+            *(u8_t *)optval = 0;
+          }
+          LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_LOOP) = %d\n",
+                                      s, *(int *)optval));
+          break;
+#endif /* LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS && LWIP_UDP */
+        default:
+          LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, UNIMPL: optname=0x%x, ..)\n",
+                                      s, optname));
+          err = ENOPROTOOPT;
+          break;
+      }  /* switch (optname) */
+      break;
+
+#if LWIP_TCP
+    /* Level: IPPROTO_TCP */
+    case IPPROTO_TCP:
+      /* Special case: all IPPROTO_TCP option take an int */
+      LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, *optlen, int, NETCONN_TCP);
+      if (sock->conn->pcb.tcp->state == LISTEN) {
+        done_socket(sock);
+        return EINVAL;
+      }
+      switch (optname) {
+        case TCP_NODELAY:
+          *(int *)optval = tcp_nagle_disabled(sock->conn->pcb.tcp);
+          LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, TCP_NODELAY) = %s\n",
+                                      s, (*(int *)optval) ? "on" : "off") );
+          break;
+        case TCP_KEEPALIVE:
+          *(int *)optval = (int)sock->conn->pcb.tcp->keep_idle;
+          LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, TCP_KEEPALIVE) = %d\n",
+                                      s, *(int *)optval));
+          break;
+
+#if LWIP_TCP_KEEPALIVE
+        case TCP_KEEPIDLE:
+          *(int *)optval = (int)(sock->conn->pcb.tcp->keep_idle / 1000);
+          LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, TCP_KEEPIDLE) = %d\n",
+                                      s, *(int *)optval));
+          break;
+        case TCP_KEEPINTVL:
+          *(int *)optval = (int)(sock->conn->pcb.tcp->keep_intvl / 1000);
+          LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, TCP_KEEPINTVL) = %d\n",
+                                      s, *(int *)optval));
+          break;
+        case TCP_KEEPCNT:
+          *(int *)optval = (int)sock->conn->pcb.tcp->keep_cnt;
+          LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, TCP_KEEPCNT) = %d\n",
+                                      s, *(int *)optval));
+          break;
+#endif /* LWIP_TCP_KEEPALIVE */
+        default:
+          LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, UNIMPL: optname=0x%x, ..)\n",
+                                      s, optname));
+          err = ENOPROTOOPT;
+          break;
+      }  /* switch (optname) */
+      break;
+#endif /* LWIP_TCP */
+
+#if LWIP_IPV6
+    /* Level: IPPROTO_IPV6 */
+    case IPPROTO_IPV6:
+      switch (optname) {
+        case IPV6_V6ONLY:
+          LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, int);
+          *(int *)optval = (netconn_get_ipv6only(sock->conn) ? 1 : 0);
+          LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, IPV6_V6ONLY) = %d\n",
+                                      s, *(int *)optval));
+          break;
+        default:
+          LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, UNIMPL: optname=0x%x, ..)\n",
+                                      s, optname));
+          err = ENOPROTOOPT;
+          break;
+      }  /* switch (optname) */
+      break;
+#endif /* LWIP_IPV6 */
+
+#if LWIP_UDP && LWIP_UDPLITE
+    /* Level: IPPROTO_UDPLITE */
+    case IPPROTO_UDPLITE:
+      /* Special case: all IPPROTO_UDPLITE option take an int */
+      LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, int);
+      /* If this is no UDP lite socket, ignore any options. */
+      if (!NETCONNTYPE_ISUDPLITE(netconn_type(sock->conn))) {
+        done_socket(sock);
+        return ENOPROTOOPT;
+      }
+      switch (optname) {
+        case UDPLITE_SEND_CSCOV:
+          *(int *)optval = sock->conn->pcb.udp->chksum_len_tx;
+          LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_UDPLITE, UDPLITE_SEND_CSCOV) = %d\n",
+                                      s, (*(int *)optval)) );
+          break;
+        case UDPLITE_RECV_CSCOV:
+          *(int *)optval = sock->conn->pcb.udp->chksum_len_rx;
+          LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_UDPLITE, UDPLITE_RECV_CSCOV) = %d\n",
+                                      s, (*(int *)optval)) );
+          break;
+        default:
+          LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_UDPLITE, UNIMPL: optname=0x%x, ..)\n",
+                                      s, optname));
+          err = ENOPROTOOPT;
+          break;
+      }  /* switch (optname) */
+      break;
+#endif /* LWIP_UDP */
+    /* Level: IPPROTO_RAW */
+    case IPPROTO_RAW:
+      switch (optname) {
+#if LWIP_IPV6 && LWIP_RAW
+        case IPV6_CHECKSUM:
+          LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, *optlen, int, NETCONN_RAW);
+          if (sock->conn->pcb.raw->chksum_reqd == 0) {
+            *(int *)optval = -1;
+          } else {
+            *(int *)optval = sock->conn->pcb.raw->chksum_offset;
+          }
+          LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_RAW, IPV6_CHECKSUM) = %d\n",
+                                      s, (*(int *)optval)) );
+          break;
+#endif /* LWIP_IPV6 && LWIP_RAW */
+        default:
+          LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_RAW, UNIMPL: optname=0x%x, ..)\n",
+                                      s, optname));
+          err = ENOPROTOOPT;
+          break;
+      }  /* switch (optname) */
+      break;
+    default:
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, level=0x%x, UNIMPL: optname=0x%x, ..)\n",
+                                  s, level, optname));
+      err = ENOPROTOOPT;
+      break;
+  } /* switch (level) */
+
+  done_socket(sock);
+  return err;
+}
+
+int
+lwip_setsockopt(int s, int level, int optname, const void *optval, socklen_t optlen)
+{
+  int err = 0;
+  struct lwip_sock *sock = get_socket(s);
+#if !LWIP_TCPIP_CORE_LOCKING
+  err_t cberr;
+  LWIP_SETGETSOCKOPT_DATA_VAR_DECLARE(data);
+#endif /* !LWIP_TCPIP_CORE_LOCKING */
+
+  if (!sock) {
+    return -1;
+  }
+
+  if (NULL == optval) {
+    set_errno(EFAULT);
+    done_socket(sock);
+    return -1;
+  }
+
+#if LWIP_TCPIP_CORE_LOCKING
+  /* core-locking can just call the -impl function */
+  LOCK_TCPIP_CORE();
+  err = lwip_setsockopt_impl(s, level, optname, optval, optlen);
+  UNLOCK_TCPIP_CORE();
+
+#else /* LWIP_TCPIP_CORE_LOCKING */
+
+#if LWIP_MPU_COMPATIBLE
+  /* MPU_COMPATIBLE copies the optval data, so check for max size here */
+  if (optlen > LWIP_SETGETSOCKOPT_MAXOPTLEN) {
+    set_errno(ENOBUFS);
+    done_socket(sock);
+    return -1;
+  }
+#endif /* LWIP_MPU_COMPATIBLE */
+
+  LWIP_SETGETSOCKOPT_DATA_VAR_ALLOC(data, sock);
+  LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).s = s;
+  LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).level = level;
+  LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optname = optname;
+  LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optlen = optlen;
+#if LWIP_MPU_COMPATIBLE
+  MEMCPY(LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optval, optval, optlen);
+#else /* LWIP_MPU_COMPATIBLE */
+  LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optval.pc = (const void *)optval;
+#endif /* LWIP_MPU_COMPATIBLE */
+  LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).err = 0;
+#if LWIP_NETCONN_SEM_PER_THREAD
+  LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem = LWIP_NETCONN_THREAD_SEM_GET();
+#else
+  LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem = &sock->conn->op_completed;
+#endif
+  cberr = tcpip_callback(lwip_setsockopt_callback, &LWIP_SETGETSOCKOPT_DATA_VAR_REF(data));
+  if (cberr != ERR_OK) {
+    LWIP_SETGETSOCKOPT_DATA_VAR_FREE(data);
+    set_errno(err_to_errno(cberr));
+    done_socket(sock);
+    return -1;
+  }
+  sys_arch_sem_wait((sys_sem_t *)(LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem), 0);
+
+  /* maybe lwip_setsockopt_impl has changed err */
+  err = LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).err;
+  LWIP_SETGETSOCKOPT_DATA_VAR_FREE(data);
+#endif  /* LWIP_TCPIP_CORE_LOCKING */
+
+  set_errno(err);
+  done_socket(sock);
+  return err ? -1 : 0;
+}
+
+#if !LWIP_TCPIP_CORE_LOCKING
+/** lwip_setsockopt_callback: only used without CORE_LOCKING
+ * to get into the tcpip_thread
+ */
+static void
+lwip_setsockopt_callback(void *arg)
+{
+  struct lwip_setgetsockopt_data *data;
+  LWIP_ASSERT("arg != NULL", arg != NULL);
+  data = (struct lwip_setgetsockopt_data *)arg;
+
+  data->err = lwip_setsockopt_impl(data->s, data->level, data->optname,
+#if LWIP_MPU_COMPATIBLE
+                                   data->optval,
+#else /* LWIP_MPU_COMPATIBLE */
+                                   data->optval.pc,
+#endif /* LWIP_MPU_COMPATIBLE */
+                                   data->optlen);
+
+  sys_sem_signal((sys_sem_t *)(data->completed_sem));
+}
+#endif  /* LWIP_TCPIP_CORE_LOCKING */
+
+/** lwip_setsockopt_impl: the actual implementation of setsockopt:
+ * same argument as lwip_setsockopt, either called directly or through callback
+ */
+static int
+lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_t optlen)
+{
+  int err = 0;
+  struct lwip_sock *sock = tryget_socket(s);
+  if (!sock) {
+    return EBADF;
+  }
+
+#ifdef LWIP_HOOK_SOCKETS_SETSOCKOPT
+  if (LWIP_HOOK_SOCKETS_SETSOCKOPT(s, sock, level, optname, optval, optlen, &err)) {
+    return err;
+  }
+#endif
+
+  switch (level) {
+
+    /* Level: SOL_SOCKET */
+    case SOL_SOCKET:
+      switch (optname) {
+
+        /* SO_ACCEPTCONN is get-only */
+
+        /* The option flags */
+        case SO_BROADCAST:
+        case SO_KEEPALIVE:
+#if SO_REUSE
+        case SO_REUSEADDR:
+#endif /* SO_REUSE */
+          if ((optname == SO_BROADCAST) &&
+              (NETCONNTYPE_GROUP(sock->conn->type) != NETCONN_UDP)) {
+            done_socket(sock);
+            return ENOPROTOOPT;
+          }
+
+          optname = lwip_sockopt_to_ipopt(optname);
+
+          LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, int);
+          if (*(const int *)optval) {
+            ip_set_option(sock->conn->pcb.ip, optname);
+          } else {
+            ip_reset_option(sock->conn->pcb.ip, optname);
+          }
+          LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, SOL_SOCKET, optname=0x%x, ..) -> %s\n",
+                                      s, optname, (*(const int *)optval ? "on" : "off")));
+          break;
+
+          /* SO_TYPE is get-only */
+          /* SO_ERROR is get-only */
+
+#if LWIP_SO_SNDTIMEO
+        case SO_SNDTIMEO: {
+          long ms_long;
+          LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, LWIP_SO_SNDRCVTIMEO_OPTTYPE);
+          ms_long = LWIP_SO_SNDRCVTIMEO_GET_MS(optval);
+          if (ms_long < 0) {
+            done_socket(sock);
+            return EINVAL;
+          }
+          netconn_set_sendtimeout(sock->conn, ms_long);
+          break;
+        }
+#endif /* LWIP_SO_SNDTIMEO */
+#if LWIP_SO_RCVTIMEO
+        case SO_RCVTIMEO: {
+          long ms_long;
+          LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, LWIP_SO_SNDRCVTIMEO_OPTTYPE);
+          ms_long = LWIP_SO_SNDRCVTIMEO_GET_MS(optval);
+          if (ms_long < 0) {
+            done_socket(sock);
+            return EINVAL;
+          }
+          netconn_set_recvtimeout(sock->conn, (u32_t)ms_long);
+          break;
+        }
+#endif /* LWIP_SO_RCVTIMEO */
+#if LWIP_SO_RCVBUF
+        case SO_RCVBUF:
+          LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, int);
+          netconn_set_recvbufsize(sock->conn, *(const int *)optval);
+          break;
+#endif /* LWIP_SO_RCVBUF */
+#if LWIP_SO_LINGER
+        case SO_LINGER: {
+          const struct linger *linger = (const struct linger *)optval;
+          LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, struct linger);
+          if (linger->l_onoff) {
+            int lingersec = linger->l_linger;
+            if (lingersec < 0) {
+              done_socket(sock);
+              return EINVAL;
+            }
+            if (lingersec > 0xFFFF) {
+              lingersec = 0xFFFF;
+            }
+            sock->conn->linger = (s16_t)lingersec;
+          } else {
+            sock->conn->linger = -1;
+          }
+        }
+        break;
+#endif /* LWIP_SO_LINGER */
+#if LWIP_UDP
+        case SO_NO_CHECK:
+          LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_UDP);
+#if LWIP_UDPLITE
+          if (udp_is_flag_set(sock->conn->pcb.udp, UDP_FLAGS_UDPLITE)) {
+            /* this flag is only available for UDP, not for UDP lite */
+            done_socket(sock);
+            return EAFNOSUPPORT;
+          }
+#endif /* LWIP_UDPLITE */
+          if (*(const int *)optval) {
+            udp_set_flags(sock->conn->pcb.udp, UDP_FLAGS_NOCHKSUM);
+          } else {
+            udp_clear_flags(sock->conn->pcb.udp, UDP_FLAGS_NOCHKSUM);
+          }
+          break;
+#endif /* LWIP_UDP */
+        case SO_BINDTODEVICE: {
+          const struct ifreq *iface;
+          struct netif *n = NULL;
+
+          LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, struct ifreq);
+
+          iface = (const struct ifreq *)optval;
+          if (iface->ifr_name[0] != 0) {
+            n = netif_find(iface->ifr_name);
+            if (n == NULL) {
+              done_socket(sock);
+              return ENODEV;
+            }
+          }
+
+          switch (NETCONNTYPE_GROUP(netconn_type(sock->conn))) {
+#if LWIP_TCP
+            case NETCONN_TCP:
+              tcp_bind_netif(sock->conn->pcb.tcp, n);
+              break;
+#endif
+#if LWIP_UDP
+            case NETCONN_UDP:
+              udp_bind_netif(sock->conn->pcb.udp, n);
+              break;
+#endif
+#if LWIP_RAW
+            case NETCONN_RAW:
+              raw_bind_netif(sock->conn->pcb.raw, n);
+              break;
+#endif
+            default:
+              LWIP_ASSERT("Unhandled netconn type in SO_BINDTODEVICE", 0);
+              break;
+          }
+        }
+        break;
+        default:
+          LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, SOL_SOCKET, UNIMPL: optname=0x%x, ..)\n",
+                                      s, optname));
+          err = ENOPROTOOPT;
+          break;
+      }  /* switch (optname) */
+      break;
+
+    /* Level: IPPROTO_IP */
+    case IPPROTO_IP:
+      switch (optname) {
+        case IP_TTL:
+          LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, int);
+          sock->conn->pcb.ip->ttl = (u8_t)(*(const int *)optval);
+          LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, IP_TTL, ..) -> %d\n",
+                                      s, sock->conn->pcb.ip->ttl));
+          break;
+        case IP_TOS:
+          LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, int);
+          sock->conn->pcb.ip->tos = (u8_t)(*(const int *)optval);
+          LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, IP_TOS, ..)-> %d\n",
+                                      s, sock->conn->pcb.ip->tos));
+          break;
+#if LWIP_NETBUF_RECVINFO
+        case IP_PKTINFO:
+          LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_UDP);
+          if (*(const int *)optval) {
+            sock->conn->flags |= NETCONN_FLAG_PKTINFO;
+          } else {
+            sock->conn->flags &= ~NETCONN_FLAG_PKTINFO;
+          }
+          break;
+#endif /* LWIP_NETBUF_RECVINFO */
+#if LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS && LWIP_UDP
+        case IP_MULTICAST_TTL:
+          LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, u8_t, NETCONN_UDP);
+          udp_set_multicast_ttl(sock->conn->pcb.udp, (u8_t)(*(const u8_t *)optval));
+          break;
+        case IP_MULTICAST_IF: {
+          ip4_addr_t if_addr;
+          LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, struct in_addr, NETCONN_UDP);
+          inet_addr_to_ip4addr(&if_addr, (const struct in_addr *)optval);
+          udp_set_multicast_netif_addr(sock->conn->pcb.udp, &if_addr);
+        }
+        break;
+        case IP_MULTICAST_LOOP:
+          LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, u8_t, NETCONN_UDP);
+          if (*(const u8_t *)optval) {
+            udp_set_flags(sock->conn->pcb.udp, UDP_FLAGS_MULTICAST_LOOP);
+          } else {
+            udp_clear_flags(sock->conn->pcb.udp, UDP_FLAGS_MULTICAST_LOOP);
+          }
+          break;
+#endif /* LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS && LWIP_UDP */
+#if LWIP_IGMP
+        case IP_ADD_MEMBERSHIP:
+        case IP_DROP_MEMBERSHIP: {
+          /* If this is a TCP or a RAW socket, ignore these options. */
+          err_t igmp_err;
+          const struct ip_mreq *imr = (const struct ip_mreq *)optval;
+          ip4_addr_t if_addr;
+          ip4_addr_t multi_addr;
+          LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, struct ip_mreq, NETCONN_UDP);
+          inet_addr_to_ip4addr(&if_addr, &imr->imr_interface);
+          inet_addr_to_ip4addr(&multi_addr, &imr->imr_multiaddr);
+          if (optname == IP_ADD_MEMBERSHIP) {
+            if (!lwip_socket_register_membership(s, &if_addr, &multi_addr)) {
+              /* cannot track membership (out of memory) */
+              err = ENOMEM;
+              igmp_err = ERR_OK;
+            } else {
+              igmp_err = igmp_joingroup(&if_addr, &multi_addr);
+            }
+          } else {
+            igmp_err = igmp_leavegroup(&if_addr, &multi_addr);
+            lwip_socket_unregister_membership(s, &if_addr, &multi_addr);
+          }
+          if (igmp_err != ERR_OK) {
+            err = EADDRNOTAVAIL;
+          }
+        }
+        break;
+#endif /* LWIP_IGMP */
+        default:
+          LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, UNIMPL: optname=0x%x, ..)\n",
+                                      s, optname));
+          err = ENOPROTOOPT;
+          break;
+      }  /* switch (optname) */
+      break;
+
+#if LWIP_TCP
+    /* Level: IPPROTO_TCP */
+    case IPPROTO_TCP:
+      /* Special case: all IPPROTO_TCP option take an int */
+      LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_TCP);
+      if (sock->conn->pcb.tcp->state == LISTEN) {
+        done_socket(sock);
+        return EINVAL;
+      }
+      switch (optname) {
+        case TCP_NODELAY:
+          if (*(const int *)optval) {
+            tcp_nagle_disable(sock->conn->pcb.tcp);
+          } else {
+            tcp_nagle_enable(sock->conn->pcb.tcp);
+          }
+          LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_NODELAY) -> %s\n",
+                                      s, (*(const int *)optval) ? "on" : "off") );
+          break;
+        case TCP_KEEPALIVE:
+          sock->conn->pcb.tcp->keep_idle = (u32_t)(*(const int *)optval);
+          LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPALIVE) -> %"U32_F"\n",
+                                      s, sock->conn->pcb.tcp->keep_idle));
+          break;
+
+#if LWIP_TCP_KEEPALIVE
+        case TCP_KEEPIDLE:
+          sock->conn->pcb.tcp->keep_idle = 1000 * (u32_t)(*(const int *)optval);
+          LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPIDLE) -> %"U32_F"\n",
+                                      s, sock->conn->pcb.tcp->keep_idle));
+          break;
+        case TCP_KEEPINTVL:
+          sock->conn->pcb.tcp->keep_intvl = 1000 * (u32_t)(*(const int *)optval);
+          LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPINTVL) -> %"U32_F"\n",
+                                      s, sock->conn->pcb.tcp->keep_intvl));
+          break;
+        case TCP_KEEPCNT:
+          sock->conn->pcb.tcp->keep_cnt = (u32_t)(*(const int *)optval);
+          LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPCNT) -> %"U32_F"\n",
+                                      s, sock->conn->pcb.tcp->keep_cnt));
+          break;
+#endif /* LWIP_TCP_KEEPALIVE */
+        default:
+          LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, UNIMPL: optname=0x%x, ..)\n",
+                                      s, optname));
+          err = ENOPROTOOPT;
+          break;
+      }  /* switch (optname) */
+      break;
+#endif /* LWIP_TCP*/
+
+#if LWIP_IPV6
+    /* Level: IPPROTO_IPV6 */
+    case IPPROTO_IPV6:
+      switch (optname) {
+        case IPV6_V6ONLY:
+          LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, int);
+          if (*(const int *)optval) {
+            netconn_set_ipv6only(sock->conn, 1);
+          } else {
+            netconn_set_ipv6only(sock->conn, 0);
+          }
+          LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IPV6, IPV6_V6ONLY, ..) -> %d\n",
+                                      s, (netconn_get_ipv6only(sock->conn) ? 1 : 0)));
+          break;
+#if LWIP_IPV6_MLD
+        case IPV6_JOIN_GROUP:
+        case IPV6_LEAVE_GROUP: {
+          /* If this is a TCP or a RAW socket, ignore these options. */
+          err_t mld6_err;
+          struct netif *netif;
+          ip6_addr_t multi_addr;
+          const struct ipv6_mreq *imr = (const struct ipv6_mreq *)optval;
+          LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, struct ipv6_mreq, NETCONN_UDP);
+          inet6_addr_to_ip6addr(&multi_addr, &imr->ipv6mr_multiaddr);
+          LWIP_ASSERT("Invalid netif index", imr->ipv6mr_interface <= 0xFFu);
+          netif = netif_get_by_index((u8_t)imr->ipv6mr_interface);
+          if (netif == NULL) {
+            err = EADDRNOTAVAIL;
+            break;
+          }
+
+          if (optname == IPV6_JOIN_GROUP) {
+            if (!lwip_socket_register_mld6_membership(s, imr->ipv6mr_interface, &multi_addr)) {
+              /* cannot track membership (out of memory) */
+              err = ENOMEM;
+              mld6_err = ERR_OK;
+            } else {
+              mld6_err = mld6_joingroup_netif(netif, &multi_addr);
+            }
+          } else {
+            mld6_err = mld6_leavegroup_netif(netif, &multi_addr);
+            lwip_socket_unregister_mld6_membership(s, imr->ipv6mr_interface, &multi_addr);
+          }
+          if (mld6_err != ERR_OK) {
+            err = EADDRNOTAVAIL;
+          }
+        }
+        break;
+#endif /* LWIP_IPV6_MLD */
+        default:
+          LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IPV6, UNIMPL: optname=0x%x, ..)\n",
+                                      s, optname));
+          err = ENOPROTOOPT;
+          break;
+      }  /* switch (optname) */
+      break;
+#endif /* LWIP_IPV6 */
+
+#if LWIP_UDP && LWIP_UDPLITE
+    /* Level: IPPROTO_UDPLITE */
+    case IPPROTO_UDPLITE:
+      /* Special case: all IPPROTO_UDPLITE option take an int */
+      LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, int);
+      /* If this is no UDP lite socket, ignore any options. */
+      if (!NETCONNTYPE_ISUDPLITE(netconn_type(sock->conn))) {
+        done_socket(sock);
+        return ENOPROTOOPT;
+      }
+      switch (optname) {
+        case UDPLITE_SEND_CSCOV:
+          if ((*(const int *)optval != 0) && ((*(const int *)optval < 8) || (*(const int *)optval > 0xffff))) {
+            /* don't allow illegal values! */
+            sock->conn->pcb.udp->chksum_len_tx = 8;
+          } else {
+            sock->conn->pcb.udp->chksum_len_tx = (u16_t) * (const int *)optval;
+          }
+          LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UDPLITE_SEND_CSCOV) -> %d\n",
+                                      s, (*(const int *)optval)) );
+          break;
+        case UDPLITE_RECV_CSCOV:
+          if ((*(const int *)optval != 0) && ((*(const int *)optval < 8) || (*(const int *)optval > 0xffff))) {
+            /* don't allow illegal values! */
+            sock->conn->pcb.udp->chksum_len_rx = 8;
+          } else {
+            sock->conn->pcb.udp->chksum_len_rx = (u16_t) * (const int *)optval;
+          }
+          LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UDPLITE_RECV_CSCOV) -> %d\n",
+                                      s, (*(const int *)optval)) );
+          break;
+        default:
+          LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UNIMPL: optname=0x%x, ..)\n",
+                                      s, optname));
+          err = ENOPROTOOPT;
+          break;
+      }  /* switch (optname) */
+      break;
+#endif /* LWIP_UDP */
+    /* Level: IPPROTO_RAW */
+    case IPPROTO_RAW:
+      switch (optname) {
+#if LWIP_IPV6 && LWIP_RAW
+        case IPV6_CHECKSUM:
+          /* It should not be possible to disable the checksum generation with ICMPv6
+           * as per RFC 3542 chapter 3.1 */
+          if (sock->conn->pcb.raw->protocol == IPPROTO_ICMPV6) {
+            done_socket(sock);
+            return EINVAL;
+          }
+
+          LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_RAW);
+          if (*(const int *)optval < 0) {
+            sock->conn->pcb.raw->chksum_reqd = 0;
+          } else if (*(const int *)optval & 1) {
+            /* Per RFC3542, odd offsets are not allowed */
+            done_socket(sock);
+            return EINVAL;
+          } else {
+            sock->conn->pcb.raw->chksum_reqd = 1;
+            sock->conn->pcb.raw->chksum_offset = (u16_t) * (const int *)optval;
+          }
+          LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_RAW, IPV6_CHECKSUM, ..) -> %d\n",
+                                      s, sock->conn->pcb.raw->chksum_reqd));
+          break;
+#endif /* LWIP_IPV6 && LWIP_RAW */
+        default:
+          LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_RAW, UNIMPL: optname=0x%x, ..)\n",
+                                      s, optname));
+          err = ENOPROTOOPT;
+          break;
+      }  /* switch (optname) */
+      break;
+    default:
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, level=0x%x, UNIMPL: optname=0x%x, ..)\n",
+                                  s, level, optname));
+      err = ENOPROTOOPT;
+      break;
+  }  /* switch (level) */
+
+  done_socket(sock);
+  return err;
+}
+
+int
+lwip_ioctl(int s, long cmd, void *argp)
+{
+  struct lwip_sock *sock = get_socket(s);
+  u8_t val;
+#if LWIP_SO_RCVBUF
+  int recv_avail;
+#endif /* LWIP_SO_RCVBUF */
+
+  if (!sock) {
+    return -1;
+  }
+
+  switch (cmd) {
+#if LWIP_SO_RCVBUF || LWIP_FIONREAD_LINUXMODE
+    case FIONREAD:
+      if (!argp) {
+        set_errno(EINVAL);
+        done_socket(sock);
+        return -1;
+      }
+#if LWIP_FIONREAD_LINUXMODE
+      if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
+        struct netbuf *nb;
+        if (sock->lastdata.netbuf) {
+          nb = sock->lastdata.netbuf;
+          *((int *)argp) = nb->p->tot_len;
+        } else {
+          struct netbuf *rxbuf;
+          err_t err = netconn_recv_udp_raw_netbuf_flags(sock->conn, &rxbuf, NETCONN_DONTBLOCK);
+          if (err != ERR_OK) {
+            *((int *)argp) = 0;
+          } else {
+            sock->lastdata.netbuf = rxbuf;
+            *((int *)argp) = rxbuf->p->tot_len;
+          }
+        }
+        done_socket(sock);
+        return 0;
+      }
+#endif /* LWIP_FIONREAD_LINUXMODE */
+
+#if LWIP_SO_RCVBUF
+      /* we come here if either LWIP_FIONREAD_LINUXMODE==0 or this is a TCP socket */
+      SYS_ARCH_GET(sock->conn->recv_avail, recv_avail);
+      if (recv_avail < 0) {
+        recv_avail = 0;
+      }
+
+      /* Check if there is data left from the last recv operation. /maq 041215 */
+      if (sock->lastdata.netbuf) {
+        if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
+          recv_avail += sock->lastdata.pbuf->tot_len;
+        } else {
+          recv_avail += sock->lastdata.netbuf->p->tot_len;
+        }
+      }
+      *((int *)argp) = recv_avail;
+
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, FIONREAD, %p) = %"U16_F"\n", s, argp, *((u16_t *)argp)));
+      set_errno(0);
+      done_socket(sock);
+      return 0;
+#else /* LWIP_SO_RCVBUF */
+      break;
+#endif /* LWIP_SO_RCVBUF */
+#endif /* LWIP_SO_RCVBUF || LWIP_FIONREAD_LINUXMODE */
+
+    case (long)FIONBIO:
+      val = 0;
+      if (argp && *(int *)argp) {
+        val = 1;
+      }
+      netconn_set_nonblocking(sock->conn, val);
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, FIONBIO, %d)\n", s, val));
+      set_errno(0);
+      done_socket(sock);
+      return 0;
+
+    default:
+      break;
+  } /* switch (cmd) */
+  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, UNIMPL: 0x%lx, %p)\n", s, cmd, argp));
+  set_errno(ENOSYS); /* not yet implemented */
+  done_socket(sock);
+  return -1;
+}
+
+/** A minimal implementation of fcntl.
+ * Currently only the commands F_GETFL and F_SETFL are implemented.
+ * The flag O_NONBLOCK and access modes are supported for F_GETFL, only
+ * the flag O_NONBLOCK is implemented for F_SETFL.
+ */
+int
+lwip_fcntl(int s, int cmd, int val)
+{
+  struct lwip_sock *sock = get_socket(s);
+  int ret = -1;
+  int op_mode = 0;
+
+  if (!sock) {
+    return -1;
+  }
+
+  switch (cmd) {
+    case F_GETFL:
+      ret = netconn_is_nonblocking(sock->conn) ? O_NONBLOCK : 0;
+      set_errno(0);
+
+      if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
+#if LWIP_TCPIP_CORE_LOCKING
+        LOCK_TCPIP_CORE();
+#else
+        SYS_ARCH_DECL_PROTECT(lev);
+        /* the proper thing to do here would be to get into the tcpip_thread,
+           but locking should be OK as well since we only *read* some flags */
+        SYS_ARCH_PROTECT(lev);
+#endif
+#if LWIP_TCP
+        if (sock->conn->pcb.tcp) {
+          if (!(sock->conn->pcb.tcp->flags & TF_RXCLOSED)) {
+            op_mode |= O_RDONLY;
+          }
+          if (!(sock->conn->pcb.tcp->flags & TF_FIN)) {
+            op_mode |= O_WRONLY;
+          }
+        }
+#endif
+#if LWIP_TCPIP_CORE_LOCKING
+        UNLOCK_TCPIP_CORE();
+#else
+        SYS_ARCH_UNPROTECT(lev);
+#endif
+      } else {
+        op_mode |= O_RDWR;
+      }
+
+      /* ensure O_RDWR for (O_RDONLY|O_WRONLY) != O_RDWR cases */
+      ret |= (op_mode == (O_RDONLY | O_WRONLY)) ? O_RDWR : op_mode;
+
+      break;
+    case F_SETFL:
+      /* Bits corresponding to the file access mode and the file creation flags [..] that are set in arg shall be ignored */
+      val &= ~(O_RDONLY | O_WRONLY | O_RDWR);
+      if ((val & ~O_NONBLOCK) == 0) {
+        /* only O_NONBLOCK, all other bits are zero */
+        netconn_set_nonblocking(sock->conn, val & O_NONBLOCK);
+        ret = 0;
+        set_errno(0);
+      } else {
+        set_errno(ENOSYS); /* not yet implemented */
+      }
+      break;
+    default:
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_fcntl(%d, UNIMPL: %d, %d)\n", s, cmd, val));
+      set_errno(ENOSYS); /* not yet implemented */
+      break;
+  }
+  done_socket(sock);
+  return ret;
+}
+
+#if LWIP_COMPAT_SOCKETS == 2 && LWIP_POSIX_SOCKETS_IO_NAMES
+int
+fcntl(int s, int cmd, ...)
+{
+  va_list ap;
+  int val;
+
+  va_start(ap, cmd);
+  val = va_arg(ap, int);
+  va_end(ap);
+  return lwip_fcntl(s, cmd, val);
+}
+#endif
+
+const char *
+lwip_inet_ntop(int af, const void *src, char *dst, socklen_t size)
+{
+  const char *ret = NULL;
+  int size_int = (int)size;
+  if (size_int < 0) {
+    set_errno(ENOSPC);
+    return NULL;
+  }
+  switch (af) {
+#if LWIP_IPV4
+    case AF_INET:
+      ret = ip4addr_ntoa_r((const ip4_addr_t *)src, dst, size_int);
+      if (ret == NULL) {
+        set_errno(ENOSPC);
+      }
+      break;
+#endif
+#if LWIP_IPV6
+    case AF_INET6:
+      ret = ip6addr_ntoa_r((const ip6_addr_t *)src, dst, size_int);
+      if (ret == NULL) {
+        set_errno(ENOSPC);
+      }
+      break;
+#endif
+    default:
+      set_errno(EAFNOSUPPORT);
+      break;
+  }
+  return ret;
+}
+
+int
+lwip_inet_pton(int af, const char *src, void *dst)
+{
+  int err;
+  switch (af) {
+#if LWIP_IPV4
+    case AF_INET:
+      err = ip4addr_aton(src, (ip4_addr_t *)dst);
+      break;
+#endif
+#if LWIP_IPV6
+    case AF_INET6: {
+      /* convert into temporary variable since ip6_addr_t might be larger
+         than in6_addr when scopes are enabled */
+      ip6_addr_t addr;
+      err = ip6addr_aton(src, &addr);
+      if (err) {
+        memcpy(dst, &addr.addr, sizeof(addr.addr));
+      }
+      break;
+    }
+#endif
+    default:
+      err = -1;
+      set_errno(EAFNOSUPPORT);
+      break;
+  }
+  return err;
+}
+
+#if LWIP_IGMP
+/** Register a new IGMP membership. On socket close, the membership is dropped automatically.
+ *
+ * ATTENTION: this function is called from tcpip_thread (or under CORE_LOCK).
+ *
+ * @return 1 on success, 0 on failure
+ */
+static int
+lwip_socket_register_membership(int s, const ip4_addr_t *if_addr, const ip4_addr_t *multi_addr)
+{
+  struct lwip_sock *sock = get_socket(s);
+  int i;
+
+  if (!sock) {
+    return 0;
+  }
+
+  for (i = 0; i < LWIP_SOCKET_MAX_MEMBERSHIPS; i++) {
+    if (socket_ipv4_multicast_memberships[i].sock == NULL) {
+      socket_ipv4_multicast_memberships[i].sock = sock;
+      ip4_addr_copy(socket_ipv4_multicast_memberships[i].if_addr, *if_addr);
+      ip4_addr_copy(socket_ipv4_multicast_memberships[i].multi_addr, *multi_addr);
+      done_socket(sock);
+      return 1;
+    }
+  }
+  done_socket(sock);
+  return 0;
+}
+
+/** Unregister a previously registered membership. This prevents dropping the membership
+ * on socket close.
+ *
+ * ATTENTION: this function is called from tcpip_thread (or under CORE_LOCK).
+ */
+static void
+lwip_socket_unregister_membership(int s, const ip4_addr_t *if_addr, const ip4_addr_t *multi_addr)
+{
+  struct lwip_sock *sock = get_socket(s);
+  int i;
+
+  if (!sock) {
+    return;
+  }
+
+  for (i = 0; i < LWIP_SOCKET_MAX_MEMBERSHIPS; i++) {
+    if ((socket_ipv4_multicast_memberships[i].sock == sock) &&
+        ip4_addr_eq(&socket_ipv4_multicast_memberships[i].if_addr, if_addr) &&
+        ip4_addr_eq(&socket_ipv4_multicast_memberships[i].multi_addr, multi_addr)) {
+      socket_ipv4_multicast_memberships[i].sock = NULL;
+      ip4_addr_set_zero(&socket_ipv4_multicast_memberships[i].if_addr);
+      ip4_addr_set_zero(&socket_ipv4_multicast_memberships[i].multi_addr);
+      break;
+    }
+  }
+  done_socket(sock);
+}
+
+/** Drop all memberships of a socket that were not dropped explicitly via setsockopt.
+ *
+ * ATTENTION: this function is NOT called from tcpip_thread (or under CORE_LOCK).
+ */
+static void
+lwip_socket_drop_registered_memberships(int s)
+{
+  struct lwip_sock *sock = get_socket(s);
+  int i;
+
+  if (!sock) {
+    return;
+  }
+
+  for (i = 0; i < LWIP_SOCKET_MAX_MEMBERSHIPS; i++) {
+    if (socket_ipv4_multicast_memberships[i].sock == sock) {
+      ip_addr_t multi_addr, if_addr;
+      ip_addr_copy_from_ip4(multi_addr, socket_ipv4_multicast_memberships[i].multi_addr);
+      ip_addr_copy_from_ip4(if_addr, socket_ipv4_multicast_memberships[i].if_addr);
+      socket_ipv4_multicast_memberships[i].sock = NULL;
+      ip4_addr_set_zero(&socket_ipv4_multicast_memberships[i].if_addr);
+      ip4_addr_set_zero(&socket_ipv4_multicast_memberships[i].multi_addr);
+
+      netconn_join_leave_group(sock->conn, &multi_addr, &if_addr, NETCONN_LEAVE);
+    }
+  }
+  done_socket(sock);
+}
+#endif /* LWIP_IGMP */
+
+#if LWIP_IPV6_MLD
+/** Register a new MLD6 membership. On socket close, the membership is dropped automatically.
+ *
+ * ATTENTION: this function is called from tcpip_thread (or under CORE_LOCK).
+ *
+ * @return 1 on success, 0 on failure
+ */
+static int
+lwip_socket_register_mld6_membership(int s, unsigned int if_idx, const ip6_addr_t *multi_addr)
+{
+  struct lwip_sock *sock = get_socket(s);
+  int i;
+
+  if (!sock) {
+    return 0;
+  }
+
+  for (i = 0; i < LWIP_SOCKET_MAX_MEMBERSHIPS; i++) {
+    if (socket_ipv6_multicast_memberships[i].sock == NULL) {
+      socket_ipv6_multicast_memberships[i].sock   = sock;
+      socket_ipv6_multicast_memberships[i].if_idx = (u8_t)if_idx;
+      ip6_addr_copy(socket_ipv6_multicast_memberships[i].multi_addr, *multi_addr);
+      done_socket(sock);
+      return 1;
+    }
+  }
+  done_socket(sock);
+  return 0;
+}
+
+/** Unregister a previously registered MLD6 membership. This prevents dropping the membership
+ * on socket close.
+ *
+ * ATTENTION: this function is called from tcpip_thread (or under CORE_LOCK).
+ */
+static void
+lwip_socket_unregister_mld6_membership(int s, unsigned int if_idx, const ip6_addr_t *multi_addr)
+{
+  struct lwip_sock *sock = get_socket(s);
+  int i;
+
+  if (!sock) {
+    return;
+  }
+
+  for (i = 0; i < LWIP_SOCKET_MAX_MEMBERSHIPS; i++) {
+    if ((socket_ipv6_multicast_memberships[i].sock   == sock) &&
+        (socket_ipv6_multicast_memberships[i].if_idx == if_idx) &&
+        ip6_addr_eq(&socket_ipv6_multicast_memberships[i].multi_addr, multi_addr)) {
+      socket_ipv6_multicast_memberships[i].sock   = NULL;
+      socket_ipv6_multicast_memberships[i].if_idx = NETIF_NO_INDEX;
+      ip6_addr_set_zero(&socket_ipv6_multicast_memberships[i].multi_addr);
+      break;
+    }
+  }
+  done_socket(sock);
+}
+
+/** Drop all MLD6 memberships of a socket that were not dropped explicitly via setsockopt.
+ *
+ * ATTENTION: this function is NOT called from tcpip_thread (or under CORE_LOCK).
+ */
+static void
+lwip_socket_drop_registered_mld6_memberships(int s)
+{
+  struct lwip_sock *sock = get_socket(s);
+  int i;
+
+  if (!sock) {
+    return;
+  }
+
+  for (i = 0; i < LWIP_SOCKET_MAX_MEMBERSHIPS; i++) {
+    if (socket_ipv6_multicast_memberships[i].sock == sock) {
+      ip_addr_t multi_addr;
+      u8_t if_idx;
+
+      ip_addr_copy_from_ip6(multi_addr, socket_ipv6_multicast_memberships[i].multi_addr);
+      if_idx = socket_ipv6_multicast_memberships[i].if_idx;
+
+      socket_ipv6_multicast_memberships[i].sock   = NULL;
+      socket_ipv6_multicast_memberships[i].if_idx = NETIF_NO_INDEX;
+      ip6_addr_set_zero(&socket_ipv6_multicast_memberships[i].multi_addr);
+
+      netconn_join_leave_group_netif(sock->conn, &multi_addr, if_idx, NETCONN_LEAVE);
+    }
+  }
+  done_socket(sock);
+}
+#endif /* LWIP_IPV6_MLD */
+
+#endif /* LWIP_SOCKET */
diff --git a/lib/lwip/api/tcpip.c b/lib/lwip/api/tcpip.c
new file mode 100644
index 0000000000..3aecbd432c
--- /dev/null
+++ b/lib/lwip/api/tcpip.c
@@ -0,0 +1,706 @@
+/**
+ * @file
+ * Sequential API Main thread module
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if !NO_SYS /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/priv/tcpip_priv.h"
+#include "lwip/sys.h"
+#include "lwip/memp.h"
+#include "lwip/mem.h"
+#include "lwip/init.h"
+#include "lwip/ip.h"
+#include "lwip/pbuf.h"
+#include "lwip/etharp.h"
+#include "netif/ethernet.h"
+
+#define TCPIP_MSG_VAR_REF(name)     API_VAR_REF(name)
+#define TCPIP_MSG_VAR_DECLARE(name) API_VAR_DECLARE(struct tcpip_msg, name)
+#define TCPIP_MSG_VAR_ALLOC(name)   API_VAR_ALLOC(struct tcpip_msg, MEMP_TCPIP_MSG_API, name, ERR_MEM)
+#define TCPIP_MSG_VAR_FREE(name)    API_VAR_FREE(MEMP_TCPIP_MSG_API, name)
+
+/* global variables */
+static tcpip_init_done_fn tcpip_init_done;
+static void *tcpip_init_done_arg;
+static sys_mbox_t tcpip_mbox;
+
+#if LWIP_TCPIP_CORE_LOCKING
+/** The global semaphore to lock the stack. */
+sys_mutex_t lock_tcpip_core;
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+
+static void tcpip_thread_handle_msg(struct tcpip_msg *msg);
+
+#if !LWIP_TIMERS
+/* wait for a message with timers disabled (e.g. pass a timer-check trigger into tcpip_thread) */
+#define TCPIP_MBOX_FETCH(mbox, msg) sys_mbox_fetch(mbox, msg)
+#else /* !LWIP_TIMERS */
+/* wait for a message, timeouts are processed while waiting */
+#define TCPIP_MBOX_FETCH(mbox, msg) tcpip_timeouts_mbox_fetch(mbox, msg)
+/**
+ * Wait (forever) for a message to arrive in an mbox.
+ * While waiting, timeouts are processed.
+ *
+ * @param mbox the mbox to fetch the message from
+ * @param msg the place to store the message
+ */
+static void
+tcpip_timeouts_mbox_fetch(sys_mbox_t *mbox, void **msg)
+{
+  u32_t sleeptime, res;
+
+again:
+  LWIP_ASSERT_CORE_LOCKED();
+
+  sleeptime = sys_timeouts_sleeptime();
+  if (sleeptime == SYS_TIMEOUTS_SLEEPTIME_INFINITE) {
+    UNLOCK_TCPIP_CORE();
+    sys_arch_mbox_fetch(mbox, msg, 0);
+    LOCK_TCPIP_CORE();
+    return;
+  } else if (sleeptime == 0) {
+    sys_check_timeouts();
+    /* We try again to fetch a message from the mbox. */
+    goto again;
+  }
+
+  UNLOCK_TCPIP_CORE();
+  res = sys_arch_mbox_fetch(mbox, msg, sleeptime);
+  LOCK_TCPIP_CORE();
+  if (res == SYS_ARCH_TIMEOUT) {
+    /* If a SYS_ARCH_TIMEOUT value is returned, a timeout occurred
+       before a message could be fetched. */
+    sys_check_timeouts();
+    /* We try again to fetch a message from the mbox. */
+    goto again;
+  }
+}
+#endif /* !LWIP_TIMERS */
+
+/**
+ * The main lwIP thread. This thread has exclusive access to lwIP core functions
+ * (unless access to them is not locked). Other threads communicate with this
+ * thread using message boxes.
+ *
+ * It also starts all the timers to make sure they are running in the right
+ * thread context.
+ *
+ * @param arg unused argument
+ */
+static void
+tcpip_thread(void *arg)
+{
+  struct tcpip_msg *msg;
+  LWIP_UNUSED_ARG(arg);
+
+  LWIP_MARK_TCPIP_THREAD();
+
+  LOCK_TCPIP_CORE();
+  if (tcpip_init_done != NULL) {
+    tcpip_init_done(tcpip_init_done_arg);
+  }
+
+  while (1) {                          /* MAIN Loop */
+    LWIP_TCPIP_THREAD_ALIVE();
+    /* wait for a message, timeouts are processed while waiting */
+    TCPIP_MBOX_FETCH(&tcpip_mbox, (void **)&msg);
+    if (msg == NULL) {
+      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: invalid message: NULL\n"));
+      LWIP_ASSERT("tcpip_thread: invalid message", 0);
+      continue;
+    }
+    tcpip_thread_handle_msg(msg);
+  }
+}
+
+/* Handle a single tcpip_msg
+ * This is in its own function for access by tests only.
+ */
+static void
+tcpip_thread_handle_msg(struct tcpip_msg *msg)
+{
+  switch (msg->type) {
+#if !LWIP_TCPIP_CORE_LOCKING
+    case TCPIP_MSG_API:
+      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: API message %p\n", (void *)msg));
+      msg->msg.api_msg.function(msg->msg.api_msg.msg);
+      break;
+    case TCPIP_MSG_API_CALL:
+      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: API CALL message %p\n", (void *)msg));
+      msg->msg.api_call.arg->err = msg->msg.api_call.function(msg->msg.api_call.arg);
+      sys_sem_signal(msg->msg.api_call.sem);
+      break;
+    case TCPIP_MSG_CALLBACK_STATIC_WAIT:
+      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: CALLBACK WAIT message %p\n", (void *)msg));
+      msg->msg.cb_wait.function(msg->msg.cb_wait.ctx);
+      sys_sem_signal(msg->msg.cb_wait.sem);
+      break;
+#endif /* !LWIP_TCPIP_CORE_LOCKING */
+
+#if !LWIP_TCPIP_CORE_LOCKING_INPUT
+    case TCPIP_MSG_INPKT:
+      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: PACKET %p\n", (void *)msg));
+      if (msg->msg.inp.input_fn(msg->msg.inp.p, msg->msg.inp.netif) != ERR_OK) {
+        pbuf_free(msg->msg.inp.p);
+      }
+      memp_free(MEMP_TCPIP_MSG_INPKT, msg);
+      break;
+#endif /* !LWIP_TCPIP_CORE_LOCKING_INPUT */
+
+#if LWIP_TCPIP_TIMEOUT && LWIP_TIMERS
+    case TCPIP_MSG_TIMEOUT:
+      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: TIMEOUT %p\n", (void *)msg));
+      sys_timeout(msg->msg.tmo.msecs, msg->msg.tmo.h, msg->msg.tmo.arg);
+      memp_free(MEMP_TCPIP_MSG_API, msg);
+      break;
+    case TCPIP_MSG_UNTIMEOUT:
+      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: UNTIMEOUT %p\n", (void *)msg));
+      sys_untimeout(msg->msg.tmo.h, msg->msg.tmo.arg);
+      memp_free(MEMP_TCPIP_MSG_API, msg);
+      break;
+#endif /* LWIP_TCPIP_TIMEOUT && LWIP_TIMERS */
+
+    case TCPIP_MSG_CALLBACK:
+      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: CALLBACK %p\n", (void *)msg));
+      msg->msg.cb.function(msg->msg.cb.ctx);
+      memp_free(MEMP_TCPIP_MSG_API, msg);
+      break;
+
+    case TCPIP_MSG_CALLBACK_STATIC:
+      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: CALLBACK_STATIC %p\n", (void *)msg));
+      msg->msg.cb.function(msg->msg.cb.ctx);
+      break;
+
+    default:
+      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: invalid message: %d\n", msg->type));
+      LWIP_ASSERT("tcpip_thread: invalid message", 0);
+      break;
+  }
+}
+
+#ifdef TCPIP_THREAD_TEST
+/** Work on queued items in single-threaded test mode */
+int
+tcpip_thread_poll_one(void)
+{
+  int ret = 0;
+  struct tcpip_msg *msg;
+
+  if (sys_arch_mbox_tryfetch(&tcpip_mbox, (void **)&msg) != SYS_MBOX_EMPTY) {
+    LOCK_TCPIP_CORE();
+    if (msg != NULL) {
+      tcpip_thread_handle_msg(msg);
+      ret = 1;
+    }
+    UNLOCK_TCPIP_CORE();
+  }
+  return ret;
+}
+#endif
+
+/**
+ * Pass a received packet to tcpip_thread for input processing
+ *
+ * @param p the received packet
+ * @param inp the network interface on which the packet was received
+ * @param input_fn input function to call
+ */
+err_t
+tcpip_inpkt(struct pbuf *p, struct netif *inp, netif_input_fn input_fn)
+{
+#if LWIP_TCPIP_CORE_LOCKING_INPUT
+  err_t ret;
+  LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_inpkt: PACKET %p/%p\n", (void *)p, (void *)inp));
+  LOCK_TCPIP_CORE();
+  ret = input_fn(p, inp);
+  UNLOCK_TCPIP_CORE();
+  return ret;
+#else /* LWIP_TCPIP_CORE_LOCKING_INPUT */
+  struct tcpip_msg *msg;
+
+  LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(tcpip_mbox));
+
+  msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_INPKT);
+  if (msg == NULL) {
+    return ERR_MEM;
+  }
+
+  msg->type = TCPIP_MSG_INPKT;
+  msg->msg.inp.p = p;
+  msg->msg.inp.netif = inp;
+  msg->msg.inp.input_fn = input_fn;
+  if (sys_mbox_trypost(&tcpip_mbox, msg) != ERR_OK) {
+    memp_free(MEMP_TCPIP_MSG_INPKT, msg);
+    return ERR_MEM;
+  }
+  return ERR_OK;
+#endif /* LWIP_TCPIP_CORE_LOCKING_INPUT */
+}
+
+/**
+ * @ingroup lwip_os
+ * Pass a received packet to tcpip_thread for input processing with
+ * ethernet_input or ip_input. Don't call directly, pass to netif_add()
+ * and call netif->input().
+ *
+ * @param p the received packet, p->payload pointing to the Ethernet header or
+ *          to an IP header (if inp doesn't have NETIF_FLAG_ETHARP or
+ *          NETIF_FLAG_ETHERNET flags)
+ * @param inp the network interface on which the packet was received
+ */
+err_t
+tcpip_input(struct pbuf *p, struct netif *inp)
+{
+#if LWIP_ETHERNET
+  if (inp->flags & (NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET)) {
+    return tcpip_inpkt(p, inp, ethernet_input);
+  } else
+#endif /* LWIP_ETHERNET */
+    return tcpip_inpkt(p, inp, ip_input);
+}
+
+/**
+ * @ingroup lwip_os
+ * Call a specific function in the thread context of
+ * tcpip_thread for easy access synchronization.
+ * A function called in that way may access lwIP core code
+ * without fearing concurrent access.
+ * Blocks until the request is posted.
+ * Must not be called from interrupt context!
+ *
+ * @param function the function to call
+ * @param ctx parameter passed to f
+ * @return ERR_OK if the function was called, another err_t if not
+ *
+ * @see tcpip_try_callback
+ */
+err_t
+tcpip_callback(tcpip_callback_fn function, void *ctx)
+{
+  struct tcpip_msg *msg;
+
+  LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(tcpip_mbox));
+
+  msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API);
+  if (msg == NULL) {
+    return ERR_MEM;
+  }
+
+  msg->type = TCPIP_MSG_CALLBACK;
+  msg->msg.cb.function = function;
+  msg->msg.cb.ctx = ctx;
+
+  sys_mbox_post(&tcpip_mbox, msg);
+  return ERR_OK;
+}
+
+/**
+ * @ingroup lwip_os
+ * Call a specific function in the thread context of
+ * tcpip_thread for easy access synchronization.
+ * A function called in that way may access lwIP core code
+ * without fearing concurrent access.
+ * Does NOT block when the request cannot be posted because the
+ * tcpip_mbox is full, but returns ERR_MEM instead.
+ * Can be called from interrupt context.
+ *
+ * @param function the function to call
+ * @param ctx parameter passed to f
+ * @return ERR_OK if the function was called, another err_t if not
+ *
+ * @see tcpip_callback
+ */
+err_t
+tcpip_try_callback(tcpip_callback_fn function, void *ctx)
+{
+  struct tcpip_msg *msg;
+
+  LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(tcpip_mbox));
+
+  msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API);
+  if (msg == NULL) {
+    return ERR_MEM;
+  }
+
+  msg->type = TCPIP_MSG_CALLBACK;
+  msg->msg.cb.function = function;
+  msg->msg.cb.ctx = ctx;
+
+  if (sys_mbox_trypost(&tcpip_mbox, msg) != ERR_OK) {
+    memp_free(MEMP_TCPIP_MSG_API, msg);
+    return ERR_MEM;
+  }
+  return ERR_OK;
+}
+
+#if LWIP_TCPIP_TIMEOUT && LWIP_TIMERS
+/**
+ * call sys_timeout in tcpip_thread
+ *
+ * @param msecs time in milliseconds for timeout
+ * @param h function to be called on timeout
+ * @param arg argument to pass to timeout function h
+ * @return ERR_MEM on memory error, ERR_OK otherwise
+ */
+err_t
+tcpip_timeout(u32_t msecs, sys_timeout_handler h, void *arg)
+{
+  struct tcpip_msg *msg;
+
+  LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(tcpip_mbox));
+
+  msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API);
+  if (msg == NULL) {
+    return ERR_MEM;
+  }
+
+  msg->type = TCPIP_MSG_TIMEOUT;
+  msg->msg.tmo.msecs = msecs;
+  msg->msg.tmo.h = h;
+  msg->msg.tmo.arg = arg;
+  sys_mbox_post(&tcpip_mbox, msg);
+  return ERR_OK;
+}
+
+/**
+ * call sys_untimeout in tcpip_thread
+ *
+ * @param h function to be called on timeout
+ * @param arg argument to pass to timeout function h
+ * @return ERR_MEM on memory error, ERR_OK otherwise
+ */
+err_t
+tcpip_untimeout(sys_timeout_handler h, void *arg)
+{
+  struct tcpip_msg *msg;
+
+  LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(tcpip_mbox));
+
+  msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API);
+  if (msg == NULL) {
+    return ERR_MEM;
+  }
+
+  msg->type = TCPIP_MSG_UNTIMEOUT;
+  msg->msg.tmo.h = h;
+  msg->msg.tmo.arg = arg;
+  sys_mbox_post(&tcpip_mbox, msg);
+  return ERR_OK;
+}
+#endif /* LWIP_TCPIP_TIMEOUT && LWIP_TIMERS */
+
+
+/**
+ * Sends a message to TCPIP thread to call a function. Caller thread blocks on
+ * on a provided semaphore, which ist NOT automatically signalled by TCPIP thread,
+ * this has to be done by the user.
+ * It is recommended to use LWIP_TCPIP_CORE_LOCKING since this is the way
+ * with least runtime overhead.
+ *
+ * @param fn function to be called from TCPIP thread
+ * @param apimsg argument to API function
+ * @param sem semaphore to wait on
+ * @return ERR_OK if the function was called, another err_t if not
+ */
+err_t
+tcpip_send_msg_wait_sem(tcpip_callback_fn fn, void *apimsg, sys_sem_t *sem)
+{
+#if LWIP_TCPIP_CORE_LOCKING
+  LWIP_UNUSED_ARG(sem);
+  LOCK_TCPIP_CORE();
+  fn(apimsg);
+  UNLOCK_TCPIP_CORE();
+  return ERR_OK;
+#else /* LWIP_TCPIP_CORE_LOCKING */
+  TCPIP_MSG_VAR_DECLARE(msg);
+
+  LWIP_ASSERT("semaphore not initialized", sys_sem_valid(sem));
+  LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(tcpip_mbox));
+
+  TCPIP_MSG_VAR_ALLOC(msg);
+  TCPIP_MSG_VAR_REF(msg).type = TCPIP_MSG_API;
+  TCPIP_MSG_VAR_REF(msg).msg.api_msg.function = fn;
+  TCPIP_MSG_VAR_REF(msg).msg.api_msg.msg = apimsg;
+  sys_mbox_post(&tcpip_mbox, &TCPIP_MSG_VAR_REF(msg));
+  sys_arch_sem_wait(sem, 0);
+  TCPIP_MSG_VAR_FREE(msg);
+  return ERR_OK;
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+}
+
+/**
+ * Synchronously calls function in TCPIP thread and waits for its completion.
+ * It is recommended to use LWIP_TCPIP_CORE_LOCKING (preferred) or
+ * LWIP_NETCONN_SEM_PER_THREAD.
+ * If not, a semaphore is created and destroyed on every call which is usually
+ * an expensive/slow operation.
+ * @param fn Function to call
+ * @param call Call parameters
+ * @return Return value from tcpip_api_call_fn
+ */
+err_t
+tcpip_api_call(tcpip_api_call_fn fn, struct tcpip_api_call_data *call)
+{
+#if LWIP_TCPIP_CORE_LOCKING
+  err_t err;
+  LOCK_TCPIP_CORE();
+  err = fn(call);
+  UNLOCK_TCPIP_CORE();
+  return err;
+#else /* LWIP_TCPIP_CORE_LOCKING */
+  TCPIP_MSG_VAR_DECLARE(msg);
+
+#if !LWIP_NETCONN_SEM_PER_THREAD
+  err_t err = sys_sem_new(&call->sem, 0);
+  if (err != ERR_OK) {
+    return err;
+  }
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
+
+  LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(tcpip_mbox));
+
+  TCPIP_MSG_VAR_ALLOC(msg);
+  TCPIP_MSG_VAR_REF(msg).type = TCPIP_MSG_API_CALL;
+  TCPIP_MSG_VAR_REF(msg).msg.api_call.arg = call;
+  TCPIP_MSG_VAR_REF(msg).msg.api_call.function = fn;
+#if LWIP_NETCONN_SEM_PER_THREAD
+  TCPIP_MSG_VAR_REF(msg).msg.api_call.sem = LWIP_NETCONN_THREAD_SEM_GET();
+#else /* LWIP_NETCONN_SEM_PER_THREAD */
+  TCPIP_MSG_VAR_REF(msg).msg.api_call.sem = &call->sem;
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
+  sys_mbox_post(&tcpip_mbox, &TCPIP_MSG_VAR_REF(msg));
+  sys_arch_sem_wait(TCPIP_MSG_VAR_REF(msg).msg.api_call.sem, 0);
+  TCPIP_MSG_VAR_FREE(msg);
+
+#if !LWIP_NETCONN_SEM_PER_THREAD
+  sys_sem_free(&call->sem);
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
+
+  return call->err;
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+}
+
+/**
+ * @ingroup lwip_os
+ * Allocate a structure for a static callback message and initialize it.
+ * The message has a special type such that lwIP never frees it.
+ * This is intended to be used to send "static" messages from interrupt context,
+ * e.g. the message is allocated once and posted several times from an IRQ
+ * using tcpip_callbackmsg_trycallback().
+ * Example usage: Trigger execution of an ethernet IRQ DPC routine in lwIP thread context.
+ *
+ * @param function the function to call
+ * @param ctx parameter passed to function
+ * @return a struct pointer to pass to tcpip_callbackmsg_trycallback().
+ *
+ * @see tcpip_callbackmsg_trycallback()
+ * @see tcpip_callbackmsg_delete()
+ */
+struct tcpip_callback_msg *
+tcpip_callbackmsg_new(tcpip_callback_fn function, void *ctx)
+{
+  struct tcpip_msg *msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API);
+  if (msg == NULL) {
+    return NULL;
+  }
+  msg->type = TCPIP_MSG_CALLBACK_STATIC;
+  msg->msg.cb.function = function;
+  msg->msg.cb.ctx = ctx;
+  return (struct tcpip_callback_msg *)msg;
+}
+
+/**
+ * @ingroup lwip_os
+ * Free a callback message allocated by tcpip_callbackmsg_new().
+ *
+ * @param msg the message to free
+ *
+ * @see tcpip_callbackmsg_new()
+ */
+void
+tcpip_callbackmsg_delete(struct tcpip_callback_msg *msg)
+{
+  memp_free(MEMP_TCPIP_MSG_API, msg);
+}
+
+/**
+ * @ingroup lwip_os
+ * Try to post a callback-message to the tcpip_thread tcpip_mbox.
+ *
+ * @param msg pointer to the message to post
+ * @return sys_mbox_trypost() return code
+ *
+ * @see tcpip_callbackmsg_new()
+ */
+err_t
+tcpip_callbackmsg_trycallback(struct tcpip_callback_msg *msg)
+{
+  LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(tcpip_mbox));
+  return sys_mbox_trypost(&tcpip_mbox, msg);
+}
+
+/**
+ * @ingroup lwip_os
+ * Try to post a callback-message to the tcpip_thread mbox.
+ * Same as @ref tcpip_callbackmsg_trycallback but calls sys_mbox_trypost_fromisr(),
+ * mainly to help FreeRTOS, where calls differ between task level and ISR level.
+ *
+ * @param msg pointer to the message to post
+ * @return sys_mbox_trypost_fromisr() return code (without change, so this
+ *         knowledge can be used to e.g. propagate "bool needs_scheduling")
+ *
+ * @see tcpip_callbackmsg_new()
+ */
+err_t
+tcpip_callbackmsg_trycallback_fromisr(struct tcpip_callback_msg *msg)
+{
+  LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(tcpip_mbox));
+  return sys_mbox_trypost_fromisr(&tcpip_mbox, msg);
+}
+
+/**
+ * Sends a message to TCPIP thread to call a function. Caller thread blocks
+ * until the function returns.
+ * It is recommended to use LWIP_TCPIP_CORE_LOCKING (preferred) or
+ * LWIP_NETCONN_SEM_PER_THREAD.
+ * If not, a semaphore is created and destroyed on every call which is usually
+ * an expensive/slow operation.
+ *
+ * @param function the function to call
+ * @param ctx parameter passed to f
+ * @return ERR_OK if the function was called, another err_t if not
+ */
+err_t
+tcpip_callback_wait(tcpip_callback_fn function, void *ctx)
+{
+#if LWIP_TCPIP_CORE_LOCKING
+  LOCK_TCPIP_CORE();
+  function(ctx);
+  UNLOCK_TCPIP_CORE();
+  return ERR_OK;
+#else /* LWIP_TCPIP_CORE_LOCKING */
+  err_t err;
+  sys_sem_t sem;
+  struct tcpip_msg msg;
+
+  LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(tcpip_mbox));
+
+  err = sys_sem_new(&sem, 0);
+  if (err != ERR_OK) {
+    return err;
+  }
+
+  msg.type = TCPIP_MSG_CALLBACK_STATIC_WAIT;
+  msg.msg.cb_wait.function = function;
+  msg.msg.cb_wait.ctx = ctx;
+  msg.msg.cb_wait.sem = &sem;
+  sys_mbox_post(&tcpip_mbox, &msg);
+  sys_arch_sem_wait(&sem, 0);
+  sys_sem_free(&sem);
+  return ERR_OK;
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+}
+
+/**
+ * @ingroup lwip_os
+ * Initialize this module:
+ * - initialize all sub modules
+ * - start the tcpip_thread
+ *
+ * @param initfunc a function to call when tcpip_thread is running and finished initializing
+ * @param arg argument to pass to initfunc
+ */
+void
+tcpip_init(tcpip_init_done_fn initfunc, void *arg)
+{
+  lwip_init();
+
+  tcpip_init_done = initfunc;
+  tcpip_init_done_arg = arg;
+  if (sys_mbox_new(&tcpip_mbox, TCPIP_MBOX_SIZE) != ERR_OK) {
+    LWIP_ASSERT("failed to create tcpip_thread mbox", 0);
+  }
+#if LWIP_TCPIP_CORE_LOCKING
+  if (sys_mutex_new(&lock_tcpip_core) != ERR_OK) {
+    LWIP_ASSERT("failed to create lock_tcpip_core", 0);
+  }
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+
+  sys_thread_new(TCPIP_THREAD_NAME, tcpip_thread, NULL, TCPIP_THREAD_STACKSIZE, TCPIP_THREAD_PRIO);
+}
+
+/**
+ * Simple callback function used with tcpip_callback to free a pbuf
+ * (pbuf_free has a wrong signature for tcpip_callback)
+ *
+ * @param p The pbuf (chain) to be dereferenced.
+ */
+static void
+pbuf_free_int(void *p)
+{
+  struct pbuf *q = (struct pbuf *)p;
+  pbuf_free(q);
+}
+
+/**
+ * A simple wrapper function that allows you to free a pbuf from interrupt context.
+ *
+ * @param p The pbuf (chain) to be dereferenced.
+ * @return ERR_OK if callback could be enqueued, an err_t if not
+ */
+err_t
+pbuf_free_callback(struct pbuf *p)
+{
+  return tcpip_try_callback(pbuf_free_int, p);
+}
+
+/**
+ * A simple wrapper function that allows you to free heap memory from
+ * interrupt context.
+ *
+ * @param m the heap memory to free
+ * @return ERR_OK if callback could be enqueued, an err_t if not
+ */
+err_t
+mem_free_callback(void *m)
+{
+  return tcpip_try_callback(mem_free, m);
+}
+
+#endif /* !NO_SYS */
diff --git a/lib/lwip/apps/http/http_client.c b/lib/lwip/apps/http/http_client.c
new file mode 100644
index 0000000000..1973e79e72
--- /dev/null
+++ b/lib/lwip/apps/http/http_client.c
@@ -0,0 +1,911 @@
+/**
+ * @file
+ * HTTP client
+ */
+
+/*
+ * Copyright (c) 2018 Simon Goldschmidt <goldsimon@gmx.de>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt <goldsimon@gmx.de>
+ */
+
+/**
+ * @defgroup httpc HTTP client
+ * @ingroup apps
+ * @todo:
+ * - persistent connections
+ * - select outgoing http version
+ * - optionally follow redirect
+ * - check request uri for invalid characters? (e.g. encode spaces)
+ * - IPv6 support
+ */
+
+#include "lwip/apps/http_client.h"
+
+#include "lwip/altcp_tcp.h"
+#include "lwip/dns.h"
+#include "lwip/debug.h"
+#include "lwip/mem.h"
+#include "lwip/altcp_tls.h"
+#include "lwip/init.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if LWIP_TCP && LWIP_CALLBACK_API
+
+/**
+ * HTTPC_DEBUG: Enable debugging for HTTP client.
+ */
+#ifndef HTTPC_DEBUG
+#define HTTPC_DEBUG                 LWIP_DBG_OFF
+#endif
+
+/** Set this to 1 to keep server name and uri in request state */
+#ifndef HTTPC_DEBUG_REQUEST
+#define HTTPC_DEBUG_REQUEST         0
+#endif
+
+/** This string is passed in the HTTP header as "User-Agent: " */
+#ifndef HTTPC_CLIENT_AGENT
+#define HTTPC_CLIENT_AGENT "lwIP/" LWIP_VERSION_STRING " (http://savannah.nongnu.org/projects/lwip)"
+#endif
+
+/* the various debug levels for this file */
+#define HTTPC_DEBUG_TRACE        (HTTPC_DEBUG | LWIP_DBG_TRACE)
+#define HTTPC_DEBUG_STATE        (HTTPC_DEBUG | LWIP_DBG_STATE)
+#define HTTPC_DEBUG_WARN         (HTTPC_DEBUG | LWIP_DBG_LEVEL_WARNING)
+#define HTTPC_DEBUG_WARN_STATE   (HTTPC_DEBUG | LWIP_DBG_LEVEL_WARNING | LWIP_DBG_STATE)
+#define HTTPC_DEBUG_SERIOUS      (HTTPC_DEBUG | LWIP_DBG_LEVEL_SERIOUS)
+
+#define HTTPC_POLL_INTERVAL     1
+#define HTTPC_POLL_TIMEOUT      30 /* 15 seconds */
+
+#define HTTPC_CONTENT_LEN_INVALID 0xFFFFFFFF
+
+/* GET request basic */
+#define HTTPC_REQ_11 "GET %s HTTP/1.1\r\n" /* URI */\
+    "User-Agent: %s\r\n" /* User-Agent */ \
+    "Accept: */*\r\n" \
+    "Connection: Close\r\n" /* we don't support persistent connections, yet */ \
+    "\r\n"
+#define HTTPC_REQ_11_FORMAT(uri) HTTPC_REQ_11, uri, HTTPC_CLIENT_AGENT
+
+/* GET request with host */
+#define HTTPC_REQ_11_HOST "GET %s HTTP/1.1\r\n" /* URI */\
+    "User-Agent: %s\r\n" /* User-Agent */ \
+    "Accept: */*\r\n" \
+    "Host: %s\r\n" /* server name */ \
+    "Connection: Close\r\n" /* we don't support persistent connections, yet */ \
+    "\r\n"
+#define HTTPC_REQ_11_HOST_FORMAT(uri, srv_name) HTTPC_REQ_11_HOST, uri, HTTPC_CLIENT_AGENT, srv_name
+
+/* GET request with proxy */
+#define HTTPC_REQ_11_PROXY "GET http://%s%s HTTP/1.1\r\n" /* HOST, URI */\
+    "User-Agent: %s\r\n" /* User-Agent */ \
+    "Accept: */*\r\n" \
+    "Host: %s\r\n" /* server name */ \
+    "Connection: Close\r\n" /* we don't support persistent connections, yet */ \
+    "\r\n"
+#define HTTPC_REQ_11_PROXY_FORMAT(host, uri, srv_name) HTTPC_REQ_11_PROXY, host, uri, HTTPC_CLIENT_AGENT, srv_name
+
+/* GET request with proxy (non-default server port) */
+#define HTTPC_REQ_11_PROXY_PORT "GET http://%s:%d%s HTTP/1.1\r\n" /* HOST, host-port, URI */\
+    "User-Agent: %s\r\n" /* User-Agent */ \
+    "Accept: */*\r\n" \
+    "Host: %s\r\n" /* server name */ \
+    "Connection: Close\r\n" /* we don't support persistent connections, yet */ \
+    "\r\n"
+#define HTTPC_REQ_11_PROXY_PORT_FORMAT(host, host_port, uri, srv_name) HTTPC_REQ_11_PROXY_PORT, host, host_port, uri, HTTPC_CLIENT_AGENT, srv_name
+
+typedef enum ehttpc_parse_state {
+  HTTPC_PARSE_WAIT_FIRST_LINE = 0,
+  HTTPC_PARSE_WAIT_HEADERS,
+  HTTPC_PARSE_RX_DATA
+} httpc_parse_state_t;
+
+typedef struct _httpc_state
+{
+  struct altcp_pcb* pcb;
+  ip_addr_t remote_addr;
+  u16_t remote_port;
+  int timeout_ticks;
+  struct pbuf *request;
+  struct pbuf *rx_hdrs;
+  u16_t rx_http_version;
+  u16_t rx_status;
+  altcp_recv_fn recv_fn;
+  const httpc_connection_t *conn_settings;
+  void* callback_arg;
+  u32_t rx_content_len;
+  u32_t hdr_content_len;
+  httpc_parse_state_t parse_state;
+#if HTTPC_DEBUG_REQUEST
+  char* server_name;
+  char* uri;
+#endif
+} httpc_state_t;
+
+/** Free http client state and deallocate all resources within */
+static err_t
+httpc_free_state(httpc_state_t* req)
+{
+  struct altcp_pcb* tpcb;
+
+  if (req->request != NULL) {
+    pbuf_free(req->request);
+    req->request = NULL;
+  }
+  if (req->rx_hdrs != NULL) {
+    pbuf_free(req->rx_hdrs);
+    req->rx_hdrs = NULL;
+  }
+
+  tpcb = req->pcb;
+  mem_free(req);
+  req = NULL;
+
+  if (tpcb != NULL) {
+    err_t r;
+    altcp_arg(tpcb, NULL);
+    altcp_recv(tpcb, NULL);
+    altcp_err(tpcb, NULL);
+    altcp_poll(tpcb, NULL, 0);
+    altcp_sent(tpcb, NULL);
+    r = altcp_close(tpcb);
+    if (r != ERR_OK) {
+      altcp_abort(tpcb);
+      return ERR_ABRT;
+    }
+  }
+  return ERR_OK;
+}
+
+/** Close the connection: call finished callback and free the state */
+static err_t
+httpc_close(httpc_state_t* req, httpc_result_t result, u32_t server_response, err_t err)
+{
+  if (req != NULL) {
+    if (req->conn_settings != NULL) {
+      if (req->conn_settings->result_fn != NULL) {
+        req->conn_settings->result_fn(req->callback_arg, result, req->rx_content_len, server_response, err);
+      }
+    }
+    return httpc_free_state(req);
+  }
+  return ERR_OK;
+}
+
+/** Parse http header response line 1 */
+static err_t
+http_parse_response_status(struct pbuf *p, u16_t *http_version, u16_t *http_status, u16_t *http_status_str_offset)
+{
+  u16_t end1 = pbuf_memfind(p, "\r\n", 2, 0);
+  if (end1 != 0xFFFF) {
+    /* get parts of first line */
+    u16_t space1, space2;
+    space1 = pbuf_memfind(p, " ", 1, 0);
+    if (space1 != 0xFFFF) {
+      if ((pbuf_memcmp(p, 0, "HTTP/", 5) == 0)  && (pbuf_get_at(p, 6) == '.')) {
+        char status_num[10];
+        size_t status_num_len;
+        /* parse http version */
+        u16_t version = pbuf_get_at(p, 5) - '0';
+        version <<= 8;
+        version |= pbuf_get_at(p, 7) - '0';
+        *http_version = version;
+
+        /* parse http status number */
+        space2 = pbuf_memfind(p, " ", 1, space1 + 1);
+        if (space2 != 0xFFFF) {
+          *http_status_str_offset = space2 + 1;
+          status_num_len = space2 - space1 - 1;
+        } else {
+          status_num_len = end1 - space1 - 1;
+        }
+        memset(status_num, 0, sizeof(status_num));
+        if (pbuf_copy_partial(p, status_num, (u16_t)status_num_len, space1 + 1) == status_num_len) {
+          int status = atoi(status_num);
+          if ((status > 0) && (status <= 0xFFFF)) {
+            *http_status = (u16_t)status;
+            return ERR_OK;
+          }
+        }
+      }
+    }
+  }
+  return ERR_VAL;
+}
+
+/** Wait for all headers to be received, return its length and content-length (if available) */
+static err_t
+http_wait_headers(struct pbuf *p, u32_t *content_length, u16_t *total_header_len)
+{
+  u16_t end1 = pbuf_memfind(p, "\r\n\r\n", 4, 0);
+  if (end1 < (0xFFFF - 2)) {
+    /* all headers received */
+    /* check if we have a content length (@todo: case insensitive?) */
+    u16_t content_len_hdr;
+    *content_length = HTTPC_CONTENT_LEN_INVALID;
+    *total_header_len = end1 + 4;
+
+    content_len_hdr = pbuf_memfind(p, "Content-Length: ", 16, 0);
+    if (content_len_hdr != 0xFFFF) {
+      u16_t content_len_line_end = pbuf_memfind(p, "\r\n", 2, content_len_hdr);
+      if (content_len_line_end != 0xFFFF) {
+        char content_len_num[16];
+        u16_t content_len_num_len = (u16_t)(content_len_line_end - content_len_hdr - 16);
+        memset(content_len_num, 0, sizeof(content_len_num));
+        if (pbuf_copy_partial(p, content_len_num, content_len_num_len, content_len_hdr + 16) == content_len_num_len) {
+          int len = atoi(content_len_num);
+          if ((len >= 0) && ((u32_t)len < HTTPC_CONTENT_LEN_INVALID)) {
+            *content_length = (u32_t)len;
+          }
+        }
+      }
+    }
+    return ERR_OK;
+  }
+  return ERR_VAL;
+}
+
+/** http client tcp recv callback */
+static err_t
+httpc_tcp_recv(void *arg, struct altcp_pcb *pcb, struct pbuf *p, err_t r)
+{
+  httpc_state_t* req = (httpc_state_t*)arg;
+  LWIP_UNUSED_ARG(r);
+
+  if (p == NULL) {
+    httpc_result_t result;
+    if (req->parse_state != HTTPC_PARSE_RX_DATA) {
+      /* did not get RX data yet */
+      result = HTTPC_RESULT_ERR_CLOSED;
+    } else if ((req->hdr_content_len != HTTPC_CONTENT_LEN_INVALID) &&
+      (req->hdr_content_len != req->rx_content_len)) {
+      /* header has been received with content length but not all data received */
+      result = HTTPC_RESULT_ERR_CONTENT_LEN;
+    } else {
+      /* receiving data and either all data received or no content length header */
+      result = HTTPC_RESULT_OK;
+    }
+    return httpc_close(req, result, req->rx_status, ERR_OK);
+  }
+  if (req->parse_state != HTTPC_PARSE_RX_DATA) {
+    if (req->rx_hdrs == NULL) {
+      req->rx_hdrs = p;
+    } else {
+      pbuf_cat(req->rx_hdrs, p);
+    }
+    if (req->parse_state == HTTPC_PARSE_WAIT_FIRST_LINE) {
+      u16_t status_str_off;
+      err_t err = http_parse_response_status(req->rx_hdrs, &req->rx_http_version, &req->rx_status, &status_str_off);
+      if (err == ERR_OK) {
+        /* don't care status string */
+        req->parse_state = HTTPC_PARSE_WAIT_HEADERS;
+      }
+    }
+    if (req->parse_state == HTTPC_PARSE_WAIT_HEADERS) {
+      u16_t total_header_len;
+      err_t err = http_wait_headers(req->rx_hdrs, &req->hdr_content_len, &total_header_len);
+      if (err == ERR_OK) {
+        struct pbuf *q;
+        /* full header received, send window update for header bytes and call into client callback */
+        altcp_recved(pcb, total_header_len);
+        if (req->conn_settings) {
+          if (req->conn_settings->headers_done_fn) {
+            err = req->conn_settings->headers_done_fn(req, req->callback_arg, req->rx_hdrs, total_header_len, req->hdr_content_len);
+            if (err != ERR_OK) {
+              return httpc_close(req, HTTPC_RESULT_LOCAL_ABORT, req->rx_status, err);
+            }
+          }
+        }
+        /* hide header bytes in pbuf */
+        q = pbuf_free_header(req->rx_hdrs, total_header_len);
+        p = q;
+        req->rx_hdrs = NULL;
+        /* go on with data */
+        req->parse_state = HTTPC_PARSE_RX_DATA;
+      }
+    }
+  }
+  if ((p != NULL) && (req->parse_state == HTTPC_PARSE_RX_DATA)) {
+    req->rx_content_len += p->tot_len;
+    /* received valid data: reset timeout */
+    req->timeout_ticks = HTTPC_POLL_TIMEOUT;
+    if (req->recv_fn != NULL) {
+      /* directly return here: the connection might already be aborted from the callback! */
+      return req->recv_fn(req->callback_arg, pcb, p, r);
+    } else {
+      altcp_recved(pcb, p->tot_len);
+      pbuf_free(p);
+    }
+  }
+  return ERR_OK;
+}
+
+/** http client tcp err callback */
+static void
+httpc_tcp_err(void *arg, err_t err)
+{
+  httpc_state_t* req = (httpc_state_t*)arg;
+  if (req != NULL) {
+    /* pcb has already been deallocated */
+    req->pcb = NULL;
+    httpc_close(req, HTTPC_RESULT_ERR_CLOSED, 0, err);
+  }
+}
+
+/** http client tcp poll callback */
+static err_t
+httpc_tcp_poll(void *arg, struct altcp_pcb *pcb)
+{
+  /* implement timeout */
+  httpc_state_t* req = (httpc_state_t*)arg;
+  LWIP_UNUSED_ARG(pcb);
+  if (req != NULL) {
+    if (req->timeout_ticks) {
+      req->timeout_ticks--;
+    }
+    if (!req->timeout_ticks) {
+      return httpc_close(req, HTTPC_RESULT_ERR_TIMEOUT, 0, ERR_OK);
+    }
+  }
+  return ERR_OK;
+}
+
+/** http client tcp sent callback */
+static err_t
+httpc_tcp_sent(void *arg, struct altcp_pcb *pcb, u16_t len)
+{
+  /* nothing to do here for now */
+  LWIP_UNUSED_ARG(arg);
+  LWIP_UNUSED_ARG(pcb);
+  LWIP_UNUSED_ARG(len);
+  return ERR_OK;
+}
+
+/** http client tcp connected callback */
+static err_t
+httpc_tcp_connected(void *arg, struct altcp_pcb *pcb, err_t err)
+{
+  err_t r;
+  httpc_state_t* req = (httpc_state_t*)arg;
+  LWIP_UNUSED_ARG(pcb);
+  LWIP_UNUSED_ARG(err);
+
+  /* send request; last char is zero termination */
+  r = altcp_write(req->pcb, req->request->payload, req->request->len - 1, TCP_WRITE_FLAG_COPY);
+  if (r != ERR_OK) {
+     /* could not write the single small request -> fail, don't retry */
+     return httpc_close(req, HTTPC_RESULT_ERR_MEM, 0, r);
+  }
+  /* everything written, we can free the request */
+  pbuf_free(req->request);
+  req->request = NULL;
+
+  altcp_output(req->pcb);
+  return ERR_OK;
+}
+
+/** Start the http request when the server IP addr is known */
+static err_t
+httpc_get_internal_addr(httpc_state_t* req, const ip_addr_t *ipaddr)
+{
+  err_t err;
+  LWIP_ASSERT("req != NULL", req != NULL);
+
+  if (&req->remote_addr != ipaddr) {
+    /* fill in remote addr if called externally */
+    req->remote_addr = *ipaddr;
+  }
+
+  err = altcp_connect(req->pcb, &req->remote_addr, req->remote_port, httpc_tcp_connected);
+  if (err == ERR_OK) {
+    return ERR_OK;
+  }
+  LWIP_DEBUGF(HTTPC_DEBUG_WARN_STATE, ("tcp_connect failed: %d\n", (int)err));
+  return err;
+}
+
+#if LWIP_DNS
+/** DNS callback
+ * If ipaddr is non-NULL, resolving succeeded and the request can be sent, otherwise it failed.
+ */
+static void
+httpc_dns_found(const char* hostname, const ip_addr_t *ipaddr, void *arg)
+{
+  httpc_state_t* req = (httpc_state_t*)arg;
+  err_t err;
+  httpc_result_t result;
+
+  LWIP_UNUSED_ARG(hostname);
+
+  if (ipaddr != NULL) {
+    err = httpc_get_internal_addr(req, ipaddr);
+    if (err == ERR_OK) {
+      return;
+    }
+    result = HTTPC_RESULT_ERR_CONNECT;
+  } else {
+    LWIP_DEBUGF(HTTPC_DEBUG_WARN_STATE, ("httpc_dns_found: failed to resolve hostname: %s\n",
+      hostname));
+    result = HTTPC_RESULT_ERR_HOSTNAME;
+    err = ERR_ARG;
+  }
+  httpc_close(req, result, 0, err);
+}
+#endif /* LWIP_DNS */
+
+/** Start the http request after converting 'server_name' to ip address (DNS or address string) */
+static err_t
+httpc_get_internal_dns(httpc_state_t* req, const char* server_name)
+{
+  err_t err;
+  LWIP_ASSERT("req != NULL", req != NULL);
+
+#if LWIP_DNS
+  err = dns_gethostbyname(server_name, &req->remote_addr, httpc_dns_found, req);
+#else
+  err = ipaddr_aton(server_name, &req->remote_addr) ? ERR_OK : ERR_ARG;
+#endif
+
+  if (err == ERR_OK) {
+    /* cached or IP-string */
+    err = httpc_get_internal_addr(req, &req->remote_addr);
+  } else if (err == ERR_INPROGRESS) {
+    return ERR_OK;
+  }
+  return err;
+}
+
+static int
+httpc_create_request_string(const httpc_connection_t *settings, const char* server_name, int server_port, const char* uri,
+                            int use_host, char *buffer, size_t buffer_size)
+{
+  if (settings->use_proxy) {
+    LWIP_ASSERT("server_name != NULL", server_name != NULL);
+    if (server_port != HTTP_DEFAULT_PORT) {
+      return snprintf(buffer, buffer_size, HTTPC_REQ_11_PROXY_PORT_FORMAT(server_name, server_port, uri, server_name));
+    } else {
+      return snprintf(buffer, buffer_size, HTTPC_REQ_11_PROXY_FORMAT(server_name, uri, server_name));
+    }
+  } else if (use_host) {
+    LWIP_ASSERT("server_name != NULL", server_name != NULL);
+    return snprintf(buffer, buffer_size, HTTPC_REQ_11_HOST_FORMAT(uri, server_name));
+  } else {
+    return snprintf(buffer, buffer_size, HTTPC_REQ_11_FORMAT(uri));
+  }
+}
+
+/** Initialize the connection struct */
+static err_t
+httpc_init_connection_common(httpc_state_t **connection, const httpc_connection_t *settings, const char* server_name,
+                      u16_t server_port, const char* uri, altcp_recv_fn recv_fn, void* callback_arg, int use_host)
+{
+  size_t alloc_len;
+  mem_size_t mem_alloc_len;
+  int req_len, req_len2;
+  httpc_state_t *req;
+#if HTTPC_DEBUG_REQUEST
+  size_t server_name_len, uri_len;
+#endif
+
+  LWIP_ASSERT("uri != NULL", uri != NULL);
+
+  /* get request len */
+  req_len = httpc_create_request_string(settings, server_name, server_port, uri, use_host, NULL, 0);
+  if ((req_len < 0) || (req_len > 0xFFFF)) {
+    return ERR_VAL;
+  }
+  /* alloc state and request in one block */
+  alloc_len = sizeof(httpc_state_t);
+#if HTTPC_DEBUG_REQUEST
+  server_name_len = server_name ? strlen(server_name) : 0;
+  uri_len = strlen(uri);
+  alloc_len += server_name_len + 1 + uri_len + 1;
+#endif
+  mem_alloc_len = (mem_size_t)alloc_len;
+  if ((mem_alloc_len < alloc_len) || (req_len + 1 > 0xFFFF)) {
+    return ERR_VAL;
+  }
+
+  req = (httpc_state_t*)mem_malloc((mem_size_t)alloc_len);
+  if(req == NULL) {
+    return ERR_MEM;
+  }
+  memset(req, 0, sizeof(httpc_state_t));
+  req->timeout_ticks = HTTPC_POLL_TIMEOUT;
+  req->request = pbuf_alloc(PBUF_RAW, (u16_t)(req_len + 1), PBUF_RAM);
+  if (req->request == NULL) {
+    httpc_free_state(req);
+    return ERR_MEM;
+  }
+  if (req->request->next != NULL) {
+    /* need a pbuf in one piece */
+    httpc_free_state(req);
+    return ERR_MEM;
+  }
+  req->hdr_content_len = HTTPC_CONTENT_LEN_INVALID;
+#if HTTPC_DEBUG_REQUEST
+  req->server_name = (char*)(req + 1);
+  if (server_name) {
+    memcpy(req->server_name, server_name, server_name_len + 1);
+  }
+  req->uri = req->server_name + server_name_len + 1;
+  memcpy(req->uri, uri, uri_len + 1);
+#endif
+  req->pcb = altcp_new(settings->altcp_allocator);
+  if(req->pcb == NULL) {
+    httpc_free_state(req);
+    return ERR_MEM;
+  }
+  req->remote_port = settings->use_proxy ? settings->proxy_port : server_port;
+  altcp_arg(req->pcb, req);
+  altcp_recv(req->pcb, httpc_tcp_recv);
+  altcp_err(req->pcb, httpc_tcp_err);
+  altcp_poll(req->pcb, httpc_tcp_poll, HTTPC_POLL_INTERVAL);
+  altcp_sent(req->pcb, httpc_tcp_sent);
+
+  /* set up request buffer */
+  req_len2 = httpc_create_request_string(settings, server_name, server_port, uri, use_host,
+    (char *)req->request->payload, req_len + 1);
+  if (req_len2 != req_len) {
+    httpc_free_state(req);
+    return ERR_VAL;
+  }
+
+  req->recv_fn = recv_fn;
+  req->conn_settings = settings;
+  req->callback_arg = callback_arg;
+
+  *connection = req;
+  return ERR_OK;
+}
+
+/**
+ * Initialize the connection struct
+ */
+static err_t
+httpc_init_connection(httpc_state_t **connection, const httpc_connection_t *settings, const char* server_name,
+                      u16_t server_port, const char* uri, altcp_recv_fn recv_fn, void* callback_arg)
+{
+  return httpc_init_connection_common(connection, settings, server_name, server_port, uri, recv_fn, callback_arg, 1);
+}
+
+
+/**
+ * Initialize the connection struct (from IP address)
+ */
+static err_t
+httpc_init_connection_addr(httpc_state_t **connection, const httpc_connection_t *settings,
+                           const ip_addr_t* server_addr, u16_t server_port, const char* uri,
+                           altcp_recv_fn recv_fn, void* callback_arg)
+{
+  char *server_addr_str = ipaddr_ntoa(server_addr);
+  if (server_addr_str == NULL) {
+    return ERR_VAL;
+  }
+  return httpc_init_connection_common(connection, settings, server_addr_str, server_port, uri,
+    recv_fn, callback_arg, 1);
+}
+
+/**
+ * @ingroup httpc
+ * HTTP client API: get a file by passing server IP address
+ *
+ * @param server_addr IP address of the server to connect
+ * @param port tcp port of the server
+ * @param uri uri to get from the server, remember leading "/"!
+ * @param settings connection settings (callbacks, proxy, etc.)
+ * @param recv_fn the http body (not the headers) are passed to this callback
+ * @param callback_arg argument passed to all the callbacks
+ * @param connection retreives the connection handle (to match in callbacks)
+ * @return ERR_OK if starting the request succeeds (callback_fn will be called later)
+ *         or an error code
+ */
+err_t
+httpc_get_file(const ip_addr_t* server_addr, u16_t port, const char* uri, const httpc_connection_t *settings,
+               altcp_recv_fn recv_fn, void* callback_arg, httpc_state_t **connection)
+{
+  err_t err;
+  httpc_state_t* req;
+
+  LWIP_ERROR("invalid parameters", (server_addr != NULL) && (uri != NULL) && (recv_fn != NULL), return ERR_ARG;);
+
+  err = httpc_init_connection_addr(&req, settings, server_addr, port,
+    uri, recv_fn, callback_arg);
+  if (err != ERR_OK) {
+    return err;
+  }
+
+  if (settings->use_proxy) {
+    err = httpc_get_internal_addr(req, &settings->proxy_addr);
+  } else {
+    err = httpc_get_internal_addr(req, server_addr);
+  }
+  if(err != ERR_OK) {
+    httpc_free_state(req);
+    return err;
+  }
+
+  if (connection != NULL) {
+    *connection = req;
+  }
+  return ERR_OK;
+}
+
+/**
+ * @ingroup httpc
+ * HTTP client API: get a file by passing server name as string (DNS name or IP address string)
+ *
+ * @param server_name server name as string (DNS name or IP address string)
+ * @param port tcp port of the server
+ * @param uri uri to get from the server, remember leading "/"!
+ * @param settings connection settings (callbacks, proxy, etc.)
+ * @param recv_fn the http body (not the headers) are passed to this callback
+ * @param callback_arg argument passed to all the callbacks
+ * @param connection retreives the connection handle (to match in callbacks)
+ * @return ERR_OK if starting the request succeeds (callback_fn will be called later)
+ *         or an error code
+ */
+err_t
+httpc_get_file_dns(const char* server_name, u16_t port, const char* uri, const httpc_connection_t *settings,
+                   altcp_recv_fn recv_fn, void* callback_arg, httpc_state_t **connection)
+{
+  err_t err;
+  httpc_state_t* req;
+
+  LWIP_ERROR("invalid parameters", (server_name != NULL) && (uri != NULL) && (recv_fn != NULL), return ERR_ARG;);
+
+  err = httpc_init_connection(&req, settings, server_name, port, uri, recv_fn, callback_arg);
+  if (err != ERR_OK) {
+    return err;
+  }
+
+  if (settings->use_proxy) {
+    err = httpc_get_internal_addr(req, &settings->proxy_addr);
+  } else {
+    err = httpc_get_internal_dns(req, server_name);
+  }
+  if(err != ERR_OK) {
+    httpc_free_state(req);
+    return err;
+  }
+
+  if (connection != NULL) {
+    *connection = req;
+  }
+  return ERR_OK;
+}
+
+#if LWIP_HTTPC_HAVE_FILE_IO
+/* Implementation to disk via fopen/fwrite/fclose follows */
+
+typedef struct _httpc_filestate
+{
+  const char* local_file_name;
+  FILE *file;
+  httpc_connection_t settings;
+  const httpc_connection_t *client_settings;
+  void *callback_arg;
+} httpc_filestate_t;
+
+static void httpc_fs_result(void *arg, httpc_result_t httpc_result, u32_t rx_content_len,
+  u32_t srv_res, err_t err);
+
+/** Initialize http client state for download to file system */
+static err_t
+httpc_fs_init(httpc_filestate_t **filestate_out, const char* local_file_name,
+              const httpc_connection_t *settings, void* callback_arg)
+{
+  httpc_filestate_t *filestate;
+  size_t file_len, alloc_len;
+  FILE *f;
+
+  file_len = strlen(local_file_name);
+  alloc_len = sizeof(httpc_filestate_t) + file_len + 1;
+
+  filestate = (httpc_filestate_t *)mem_malloc((mem_size_t)alloc_len);
+  if (filestate == NULL) {
+    return ERR_MEM;
+  }
+  memset(filestate, 0, sizeof(httpc_filestate_t));
+  filestate->local_file_name = (const char *)(filestate + 1);
+  memcpy((char *)(filestate + 1), local_file_name, file_len + 1);
+  filestate->file = NULL;
+  filestate->client_settings = settings;
+  filestate->callback_arg = callback_arg;
+  /* copy client settings but override result callback */
+  memcpy(&filestate->settings, settings, sizeof(httpc_connection_t));
+  filestate->settings.result_fn = httpc_fs_result;
+
+  f = fopen(local_file_name, "wb");
+  if(f == NULL) {
+    /* could not open file */
+    mem_free(filestate);
+    return ERR_VAL;
+  }
+  filestate->file = f;
+  *filestate_out = filestate;
+  return ERR_OK;
+}
+
+/** Free http client state for download to file system */
+static void
+httpc_fs_free(httpc_filestate_t *filestate)
+{
+  if (filestate != NULL) {
+    if (filestate->file != NULL) {
+      fclose(filestate->file);
+      filestate->file = NULL;
+    }
+    mem_free(filestate);
+  }
+}
+
+/** Connection closed (success or error) */
+static void
+httpc_fs_result(void *arg, httpc_result_t httpc_result, u32_t rx_content_len,
+                u32_t srv_res, err_t err)
+{
+  httpc_filestate_t *filestate = (httpc_filestate_t *)arg;
+  if (filestate != NULL) {
+    if (filestate->client_settings->result_fn != NULL) {
+      filestate->client_settings->result_fn(filestate->callback_arg, httpc_result, rx_content_len,
+        srv_res, err);
+    }
+    httpc_fs_free(filestate);
+  }
+}
+
+/** tcp recv callback */
+static err_t
+httpc_fs_tcp_recv(void *arg, struct altcp_pcb *pcb, struct pbuf *p, err_t err)
+{
+  httpc_filestate_t *filestate = (httpc_filestate_t*)arg;
+  struct pbuf* q;
+  LWIP_UNUSED_ARG(err);
+
+  LWIP_ASSERT("p != NULL", p != NULL);
+
+  for (q = p; q != NULL; q = q->next) {
+    fwrite(q->payload, 1, q->len, filestate->file);
+  }
+  altcp_recved(pcb, p->tot_len);
+  pbuf_free(p);
+  return ERR_OK;
+}
+
+/**
+ * @ingroup httpc
+ * HTTP client API: get a file to disk by passing server IP address
+ *
+ * @param server_addr IP address of the server to connect
+ * @param port tcp port of the server
+ * @param uri uri to get from the server, remember leading "/"!
+ * @param settings connection settings (callbacks, proxy, etc.)
+ * @param callback_arg argument passed to all the callbacks
+ * @param connection retreives the connection handle (to match in callbacks)
+ * @return ERR_OK if starting the request succeeds (callback_fn will be called later)
+ *         or an error code
+ */
+err_t
+httpc_get_file_to_disk(const ip_addr_t* server_addr, u16_t port, const char* uri, const httpc_connection_t *settings,
+                       void* callback_arg, const char* local_file_name, httpc_state_t **connection)
+{
+  err_t err;
+  httpc_state_t* req;
+  httpc_filestate_t *filestate;
+
+  LWIP_ERROR("invalid parameters", (server_addr != NULL) && (uri != NULL) && (local_file_name != NULL), return ERR_ARG;);
+
+  err = httpc_fs_init(&filestate, local_file_name, settings, callback_arg);
+  if (err != ERR_OK) {
+    return err;
+  }
+
+  err = httpc_init_connection_addr(&req, &filestate->settings, server_addr, port,
+    uri, httpc_fs_tcp_recv, filestate);
+  if (err != ERR_OK) {
+    httpc_fs_free(filestate);
+    return err;
+  }
+
+  if (settings->use_proxy) {
+    err = httpc_get_internal_addr(req, &settings->proxy_addr);
+  } else {
+    err = httpc_get_internal_addr(req, server_addr);
+  }
+  if(err != ERR_OK) {
+    httpc_fs_free(filestate);
+    httpc_free_state(req);
+    return err;
+  }
+
+  if (connection != NULL) {
+    *connection = req;
+  }
+  return ERR_OK;
+}
+
+/**
+ * @ingroup httpc
+ * HTTP client API: get a file to disk by passing server name as string (DNS name or IP address string)
+ *
+ * @param server_name server name as string (DNS name or IP address string)
+ * @param port tcp port of the server
+ * @param uri uri to get from the server, remember leading "/"!
+ * @param settings connection settings (callbacks, proxy, etc.)
+ * @param callback_arg argument passed to all the callbacks
+ * @param connection retreives the connection handle (to match in callbacks)
+ * @return ERR_OK if starting the request succeeds (callback_fn will be called later)
+ *         or an error code
+ */
+err_t
+httpc_get_file_dns_to_disk(const char* server_name, u16_t port, const char* uri, const httpc_connection_t *settings,
+                           void* callback_arg, const char* local_file_name, httpc_state_t **connection)
+{
+  err_t err;
+  httpc_state_t* req;
+  httpc_filestate_t *filestate;
+
+  LWIP_ERROR("invalid parameters", (server_name != NULL) && (uri != NULL) && (local_file_name != NULL), return ERR_ARG;);
+
+  err = httpc_fs_init(&filestate, local_file_name, settings, callback_arg);
+  if (err != ERR_OK) {
+    return err;
+  }
+
+  err = httpc_init_connection(&req, &filestate->settings, server_name, port,
+    uri, httpc_fs_tcp_recv, filestate);
+  if (err != ERR_OK) {
+    httpc_fs_free(filestate);
+    return err;
+  }
+
+  if (settings->use_proxy) {
+    err = httpc_get_internal_addr(req, &settings->proxy_addr);
+  } else {
+    err = httpc_get_internal_dns(req, server_name);
+  }
+  if(err != ERR_OK) {
+    httpc_fs_free(filestate);
+    httpc_free_state(req);
+    return err;
+  }
+
+  if (connection != NULL) {
+    *connection = req;
+  }
+  return ERR_OK;
+}
+#endif /* LWIP_HTTPC_HAVE_FILE_IO */
+
+#endif /* LWIP_TCP && LWIP_CALLBACK_API */
diff --git a/lib/lwip/apps/tftp/tftp.c b/lib/lwip/apps/tftp/tftp.c
new file mode 100644
index 0000000000..ddfdbfc0c1
--- /dev/null
+++ b/lib/lwip/apps/tftp/tftp.c
@@ -0,0 +1,548 @@
+/**
+ *
+ * @file tftp.c
+ *
+ * @author   Logan Gunthorpe <logang@deltatee.com>
+ *           Dirk Ziegelmeier <dziegel@gmx.de>
+ *
+ * @brief    Trivial File Transfer Protocol (RFC 1350)
+ *
+ * Copyright (c) Deltatee Enterprises Ltd. 2013
+ * All rights reserved.
+ *
+ */
+
+/*
+ * Redistribution and use in source and binary forms, with or without
+ * modification,are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Author: Logan Gunthorpe <logang@deltatee.com>
+ *         Dirk Ziegelmeier <dziegel@gmx.de>
+ *
+ */
+
+/**
+ * @defgroup tftp TFTP client/server
+ * @ingroup apps
+ *
+ * This is simple TFTP client/server for the lwIP raw API.
+ * You need to increase MEMP_NUM_SYS_TIMEOUT by one if you use TFTP!
+ */
+
+#include "lwip/apps/tftp_client.h"
+#include "lwip/apps/tftp_server.h"
+
+#if LWIP_UDP
+
+#include "lwip/udp.h"
+#include "lwip/timeouts.h"
+#include "lwip/debug.h"
+
+#define TFTP_MAX_PAYLOAD_SIZE 512
+#define TFTP_HEADER_LENGTH    4
+
+#define TFTP_RRQ   1
+#define TFTP_WRQ   2
+#define TFTP_DATA  3
+#define TFTP_ACK   4
+#define TFTP_ERROR 5
+
+enum tftp_error {
+  TFTP_ERROR_FILE_NOT_FOUND    = 1,
+  TFTP_ERROR_ACCESS_VIOLATION  = 2,
+  TFTP_ERROR_DISK_FULL         = 3,
+  TFTP_ERROR_ILLEGAL_OPERATION = 4,
+  TFTP_ERROR_UNKNOWN_TRFR_ID   = 5,
+  TFTP_ERROR_FILE_EXISTS       = 6,
+  TFTP_ERROR_NO_SUCH_USER      = 7
+};
+
+#include <string.h>
+
+struct tftp_state {
+  const struct tftp_context *ctx;
+  void *handle;
+  struct pbuf *last_data;
+  struct udp_pcb *upcb;
+  ip_addr_t addr;
+  u16_t port;
+  int timer;
+  int last_pkt;
+  u16_t blknum;
+  u8_t retries;
+  u8_t mode_write;
+  u8_t tftp_mode;
+};
+
+static struct tftp_state tftp_state;
+
+static void tftp_tmr(void *arg);
+
+static void
+close_handle(void)
+{
+  tftp_state.port = 0;
+  ip_addr_set_any(0, &tftp_state.addr);
+
+  if (tftp_state.last_data != NULL) {
+    pbuf_free(tftp_state.last_data);
+    tftp_state.last_data = NULL;
+  }
+
+  sys_untimeout(tftp_tmr, NULL);
+
+  if (tftp_state.handle) {
+    tftp_state.ctx->close(tftp_state.handle);
+    tftp_state.handle = NULL;
+    LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp: closing\n"));
+  }
+}
+
+static struct pbuf*
+init_packet(u16_t opcode, u16_t extra, size_t size)
+{
+  struct pbuf* p = pbuf_alloc(PBUF_TRANSPORT, (u16_t)(TFTP_HEADER_LENGTH + size), PBUF_RAM);
+  u16_t* payload;
+
+  if (p != NULL) {
+    payload = (u16_t*) p->payload;
+    payload[0] = PP_HTONS(opcode);
+    payload[1] = lwip_htons(extra);
+  }
+
+  return p;
+}
+
+static err_t
+send_request(const ip_addr_t *addr, u16_t port, u16_t opcode, const char* fname, const char* mode)
+{
+  size_t fname_length = strlen(fname)+1;
+  size_t mode_length = strlen(mode)+1;
+  struct pbuf* p = init_packet(opcode, 0, fname_length + mode_length - 2);
+  char* payload;
+  err_t ret;
+
+  if (p == NULL) {
+    return ERR_MEM;
+  }
+
+  payload = (char*) p->payload;
+  MEMCPY(payload+2,              fname, fname_length);
+  MEMCPY(payload+2+fname_length, mode,  mode_length);
+
+  ret = udp_sendto(tftp_state.upcb, p, addr, port);
+  pbuf_free(p);
+  return ret;
+}
+
+static err_t
+send_error(const ip_addr_t *addr, u16_t port, enum tftp_error code, const char *str)
+{
+  int str_length = strlen(str);
+  struct pbuf *p;
+  u16_t *payload;
+  err_t ret;
+
+  p = init_packet(TFTP_ERROR, code, str_length + 1);
+  if (p == NULL) {
+    return ERR_MEM;
+  }
+
+  payload = (u16_t *) p->payload;
+  MEMCPY(&payload[2], str, str_length + 1);
+
+  ret = udp_sendto(tftp_state.upcb, p, addr, port);
+  pbuf_free(p);
+  return ret;
+}
+
+static err_t
+send_ack(const ip_addr_t *addr, u16_t port, u16_t blknum)
+{
+  struct pbuf *p;
+  err_t ret;
+
+  p = init_packet(TFTP_ACK, blknum, 0);
+  if (p == NULL) {
+    return ERR_MEM;
+  }
+
+  ret = udp_sendto(tftp_state.upcb, p, addr, port);
+  pbuf_free(p);
+  return ret;
+}
+
+static err_t
+resend_data(const ip_addr_t *addr, u16_t port)
+{
+  err_t ret;
+  struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, tftp_state.last_data->len, PBUF_RAM);
+  if (p == NULL) {
+    return ERR_MEM;
+  }
+
+  ret = pbuf_copy(p, tftp_state.last_data);
+  if (ret != ERR_OK) {
+    pbuf_free(p);
+    return ret;
+  }
+
+  ret = udp_sendto(tftp_state.upcb, p, addr, port);
+  pbuf_free(p);
+  return ret;
+}
+
+static void
+send_data(const ip_addr_t *addr, u16_t port)
+{
+  u16_t *payload;
+  int ret;
+
+  if (tftp_state.last_data != NULL) {
+    pbuf_free(tftp_state.last_data);
+  }
+
+  tftp_state.last_data = init_packet(TFTP_DATA, tftp_state.blknum, TFTP_MAX_PAYLOAD_SIZE);
+  if (tftp_state.last_data == NULL) {
+    return;
+  }
+
+  payload = (u16_t *) tftp_state.last_data->payload;
+
+  ret = tftp_state.ctx->read(tftp_state.handle, &payload[2], TFTP_MAX_PAYLOAD_SIZE);
+  if (ret < 0) {
+    send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Error occurred while reading the file.");
+    close_handle();
+    return;
+  }
+
+  pbuf_realloc(tftp_state.last_data, (u16_t)(TFTP_HEADER_LENGTH + ret));
+  resend_data(addr, port);
+}
+
+static void
+tftp_recv(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
+{
+  u16_t *sbuf = (u16_t *) p->payload;
+  int opcode;
+
+  LWIP_UNUSED_ARG(arg);
+  LWIP_UNUSED_ARG(upcb);
+
+  if (((tftp_state.port != 0) && (port != tftp_state.port)) ||
+      (!ip_addr_isany_val(tftp_state.addr) && !ip_addr_eq(&tftp_state.addr, addr))) {
+    send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Only one connection at a time is supported");
+    pbuf_free(p);
+    return;
+  }
+
+  opcode = sbuf[0];
+
+  tftp_state.last_pkt = tftp_state.timer;
+  tftp_state.retries = 0;
+
+  switch (opcode) {
+    case PP_HTONS(TFTP_RRQ): /* fall through */
+    case PP_HTONS(TFTP_WRQ): {
+      const char tftp_null = 0;
+      char filename[TFTP_MAX_FILENAME_LEN + 1];
+      char mode[TFTP_MAX_MODE_LEN + 1];
+      u16_t filename_end_offset;
+      u16_t mode_end_offset;
+
+      if (tftp_state.handle != NULL) {
+        send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Only one connection at a time is supported");
+        break;
+      }
+
+      if ((tftp_state.tftp_mode & LWIP_TFTP_MODE_SERVER) == 0) {
+        send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "TFTP server not enabled");
+        break;
+      }
+
+      sys_timeout(TFTP_TIMER_MSECS, tftp_tmr, NULL);
+
+      /* find \0 in pbuf -> end of filename string */
+      filename_end_offset = pbuf_memfind(p, &tftp_null, sizeof(tftp_null), 2);
+      if ((u16_t)(filename_end_offset - 1) > sizeof(filename)) {
+        send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Filename too long/not NULL terminated");
+        break;
+      }
+      pbuf_copy_partial(p, filename, filename_end_offset - 1, 2);
+
+      /* find \0 in pbuf -> end of mode string */
+      mode_end_offset = pbuf_memfind(p, &tftp_null, sizeof(tftp_null), filename_end_offset + 1);
+      if ((u16_t)(mode_end_offset - filename_end_offset) > sizeof(mode)) {
+        send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Mode too long/not NULL terminated");
+        break;
+      }
+      pbuf_copy_partial(p, mode, mode_end_offset - filename_end_offset, filename_end_offset + 1);
+
+      tftp_state.handle = tftp_state.ctx->open(filename, mode, opcode == PP_HTONS(TFTP_WRQ));
+      tftp_state.blknum = 1;
+
+      if (!tftp_state.handle) {
+        send_error(addr, port, TFTP_ERROR_FILE_NOT_FOUND, "Unable to open requested file.");
+        break;
+      }
+
+      LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp: %s request from ", (opcode == PP_HTONS(TFTP_WRQ)) ? "write" : "read"));
+      ip_addr_debug_print(TFTP_DEBUG | LWIP_DBG_STATE, addr);
+      LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, (" for '%s' mode '%s'\n", filename, mode));
+
+      ip_addr_copy(tftp_state.addr, *addr);
+      tftp_state.port = port;
+
+      if (opcode == PP_HTONS(TFTP_WRQ)) {
+        tftp_state.mode_write = 1;
+        send_ack(addr, port, 0);
+      } else {
+        tftp_state.mode_write = 0;
+        send_data(addr, port);
+      }
+
+      break;
+    }
+
+    case PP_HTONS(TFTP_DATA): {
+      int ret;
+      u16_t blknum;
+
+      if (tftp_state.handle == NULL) {
+        send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "No connection");
+        break;
+      }
+
+      if (tftp_state.mode_write != 1) {
+        send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Not a write connection");
+        break;
+      }
+
+      blknum = lwip_ntohs(sbuf[1]);
+      if (blknum == tftp_state.blknum) {
+        pbuf_remove_header(p, TFTP_HEADER_LENGTH);
+
+        ret = tftp_state.ctx->write(tftp_state.handle, p);
+        if (ret < 0) {
+          send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "error writing file");
+          close_handle();
+        } else {
+          send_ack(addr, port, blknum);
+        }
+
+        if (p->tot_len < TFTP_MAX_PAYLOAD_SIZE) {
+          close_handle();
+        } else {
+          tftp_state.blknum++;
+        }
+      } else if ((u16_t)(blknum + 1) == tftp_state.blknum) {
+        /* retransmit of previous block, ack again (casting to u16_t to care for overflow) */
+        send_ack(addr, port, blknum);
+      } else {
+        send_error(addr, port, TFTP_ERROR_UNKNOWN_TRFR_ID, "Wrong block number");
+      }
+      break;
+    }
+
+    case PP_HTONS(TFTP_ACK): {
+      u16_t blknum;
+      int lastpkt;
+
+      if (tftp_state.handle == NULL) {
+        send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "No connection");
+        break;
+      }
+
+      if (tftp_state.mode_write != 0) {
+        send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Not a read connection");
+        break;
+      }
+
+      blknum = lwip_ntohs(sbuf[1]);
+      if (blknum != tftp_state.blknum) {
+        send_error(addr, port, TFTP_ERROR_UNKNOWN_TRFR_ID, "Wrong block number");
+        break;
+      }
+
+      lastpkt = 0;
+
+      if (tftp_state.last_data != NULL) {
+        lastpkt = tftp_state.last_data->tot_len != (TFTP_MAX_PAYLOAD_SIZE + TFTP_HEADER_LENGTH);
+      }
+
+      if (!lastpkt) {
+        tftp_state.blknum++;
+        send_data(addr, port);
+      } else {
+        close_handle();
+      }
+
+      break;
+    }
+    case PP_HTONS(TFTP_ERROR):
+      if (tftp_state.handle != NULL) {
+        pbuf_remove_header(p, TFTP_HEADER_LENGTH);
+        tftp_state.ctx->error(tftp_state.handle, sbuf[1], (const char*)p->payload, p->len);
+        close_handle();
+      }
+      break;
+    default:
+      send_error(addr, port, TFTP_ERROR_ILLEGAL_OPERATION, "Unknown operation");
+      break;
+  }
+
+  pbuf_free(p);
+}
+
+static void
+tftp_tmr(void *arg)
+{
+  LWIP_UNUSED_ARG(arg);
+
+  tftp_state.timer++;
+
+  if (tftp_state.handle == NULL) {
+    return;
+  }
+
+  sys_timeout(TFTP_TIMER_MSECS, tftp_tmr, NULL);
+
+  if ((tftp_state.timer - tftp_state.last_pkt) > (TFTP_TIMEOUT_MSECS / TFTP_TIMER_MSECS)) {
+    if ((tftp_state.last_data != NULL) && (tftp_state.retries < TFTP_MAX_RETRIES)) {
+      LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp: timeout, retrying\n"));
+      resend_data(&tftp_state.addr, tftp_state.port);
+      tftp_state.retries++;
+    } else {
+      LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp: timeout\n"));
+      close_handle();
+    }
+  }
+}
+
+/**
+ * Initialize TFTP client/server.
+ * @param mode TFTP mode (client/server)
+ * @param ctx TFTP callback struct
+ */
+err_t
+tftp_init_common(u8_t mode, const struct tftp_context *ctx)
+{
+  err_t ret;
+
+  /* LWIP_ASSERT_CORE_LOCKED(); is checked by udp_new() */
+  struct udp_pcb *pcb = udp_new_ip_type(IPADDR_TYPE_ANY);
+  if (pcb == NULL) {
+    return ERR_MEM;
+  }
+
+  ret = udp_bind(pcb, IP_ANY_TYPE, TFTP_PORT);
+  if (ret != ERR_OK) {
+    udp_remove(pcb);
+    return ret;
+  }
+
+  tftp_state.handle    = NULL;
+  tftp_state.port      = 0;
+  tftp_state.ctx       = ctx;
+  tftp_state.timer     = 0;
+  tftp_state.last_data = NULL;
+  tftp_state.upcb      = pcb;
+  tftp_state.tftp_mode = mode;
+
+  udp_recv(pcb, tftp_recv, NULL);
+
+  return ERR_OK;
+}
+
+/** @ingroup tftp
+ * Initialize TFTP server.
+ * @param ctx TFTP callback struct
+ */
+err_t
+tftp_init_server(const struct tftp_context *ctx)
+{
+  return tftp_init_common(LWIP_TFTP_MODE_SERVER, ctx);
+}
+
+/** @ingroup tftp
+ * Initialize TFTP client.
+ * @param ctx TFTP callback struct
+ */
+err_t
+tftp_init_client(const struct tftp_context *ctx)
+{
+  return tftp_init_common(LWIP_TFTP_MODE_CLIENT, ctx);
+}
+
+/** @ingroup tftp
+ * Deinitialize ("turn off") TFTP client/server.
+ */
+void tftp_cleanup(void)
+{
+  LWIP_ASSERT("Cleanup called on non-initialized TFTP", tftp_state.upcb != NULL);
+  udp_remove(tftp_state.upcb);
+  close_handle();
+  memset(&tftp_state, 0, sizeof(tftp_state));
+}
+
+static const char *
+mode_to_string(enum tftp_transfer_mode mode)
+{
+  if (mode == TFTP_MODE_OCTET) {
+    return "octet";
+  }
+  if (mode == TFTP_MODE_NETASCII) {
+    return "netascii";
+  }
+  if (mode == TFTP_MODE_BINARY) {
+    return "binary";
+  }
+  return NULL;
+}
+
+err_t
+tftp_get(void* handle, const ip_addr_t *addr, u16_t port, const char* fname, enum tftp_transfer_mode mode)
+{
+  LWIP_ERROR("TFTP client is not enabled (tftp_init)", (tftp_state.tftp_mode & LWIP_TFTP_MODE_CLIENT) != 0, return ERR_VAL);
+  LWIP_ERROR("tftp_get: invalid file name", fname != NULL, return ERR_VAL);
+  LWIP_ERROR("tftp_get: invalid mode", mode <= TFTP_MODE_BINARY, return ERR_VAL);
+
+  tftp_state.handle = handle;
+  tftp_state.blknum = 1;
+  tftp_state.mode_write = 1; /* We want to receive data */
+  return send_request(addr, port, TFTP_RRQ, fname, mode_to_string(mode));
+}
+
+err_t
+tftp_put(void* handle, const ip_addr_t *addr, u16_t port, const char* fname, enum tftp_transfer_mode mode)
+{
+  LWIP_ERROR("TFTP client is not enabled (tftp_init)", (tftp_state.tftp_mode & LWIP_TFTP_MODE_CLIENT) != 0, return ERR_VAL);
+  LWIP_ERROR("tftp_put: invalid file name", fname != NULL, return ERR_VAL);
+  LWIP_ERROR("tftp_put: invalid mode", mode <= TFTP_MODE_BINARY, return ERR_VAL);
+
+  tftp_state.handle = handle;
+  tftp_state.blknum = 1;
+  tftp_state.mode_write = 0; /* We want to send data */
+  return send_request(addr, port, TFTP_WRQ, fname, mode_to_string(mode));
+}
+
+#endif /* LWIP_UDP */
diff --git a/lib/lwip/core/altcp.c b/lib/lwip/core/altcp.c
new file mode 100644
index 0000000000..9644e82d7d
--- /dev/null
+++ b/lib/lwip/core/altcp.c
@@ -0,0 +1,717 @@
+/**
+ * @file
+ * @defgroup altcp Application layered TCP Functions
+ * @ingroup altcp_api
+ *
+ * This file contains the common functions for altcp to work.
+ * For more details see @ref altcp_api.
+ */
+
+/**
+ * @defgroup altcp_api Application layered TCP Introduction
+ * @ingroup callbackstyle_api
+ *
+ * Overview
+ * --------
+ * altcp (application layered TCP connection API; to be used from TCPIP thread)
+ * is an abstraction layer that prevents applications linking hard against the
+ * @ref tcp.h functions while providing the same functionality. It is used to
+ * e.g. add SSL/TLS (see LWIP_ALTCP_TLS) or proxy-connect support to an application
+ * written for the tcp callback API without that application knowing the
+ * protocol details.
+ *
+ * * This interface mimics the tcp callback API to the application while preventing
+ *   direct linking (much like virtual functions).
+ * * This way, an application can make use of other application layer protocols
+ *   on top of TCP without knowing the details (e.g. TLS, proxy connection).
+ * * This is achieved by simply including "lwip/altcp.h" instead of "lwip/tcp.h",
+ *   replacing "struct tcp_pcb" with "struct altcp_pcb" and prefixing all functions
+ *   with "altcp_" instead of "tcp_".
+ *
+ * With altcp support disabled (LWIP_ALTCP==0), applications written against the
+ * altcp API can still be compiled but are directly linked against the tcp.h
+ * callback API and then cannot use layered protocols. To minimize code changes
+ * in this case, the use of altcp_allocators is strongly suggested.
+ *
+ * Usage
+ * -----
+ * To make use of this API from an existing tcp raw API application:
+ * * Include "lwip/altcp.h" instead of "lwip/tcp.h"
+ * * Replace "struct tcp_pcb" with "struct altcp_pcb"
+ * * Prefix all called tcp API functions with "altcp_" instead of "tcp_" to link
+ *   against the altcp functions
+ * * @ref altcp_new (and @ref altcp_new_ip_type / @ref altcp_new_ip6) take
+ *   an @ref altcp_allocator_t as an argument, whereas the original tcp API
+ *   functions take no arguments.
+ * * An @ref altcp_allocator_t allocator is an object that holds a pointer to an
+ *   allocator object and a corresponding state (e.g. for TLS, the corresponding
+ *   state may hold certificates or keys). This way, the application does not
+ *   even need to know if it uses TLS or pure TCP, this is handled at runtime
+ *   by passing a specific allocator.
+ * * An application can alternatively bind hard to the altcp_tls API by calling
+ *   @ref altcp_tls_new or @ref altcp_tls_wrap.
+ * * The TLS layer is not directly implemented by lwIP, but a port to mbedTLS is
+ *   provided.
+ * * Another altcp layer is proxy-connect to use TLS behind a HTTP proxy (see
+ *   @ref altcp_proxyconnect.h)
+ *
+ * altcp_allocator_t
+ * -----------------
+ * An altcp allocator is created by the application by combining an allocator
+ * callback function and a corresponding state, e.g.:\code{.c}
+ * static const unsigned char cert[] = {0x2D, ... (see mbedTLS doc for how to create this)};
+ * struct altcp_tls_config * conf = altcp_tls_create_config_client(cert, sizeof(cert));
+ * altcp_allocator_t tls_allocator = {
+ *   altcp_tls_alloc, conf
+ * };
+ * \endcode
+ *
+ *
+ * struct altcp_tls_config
+ * -----------------------
+ * The struct altcp_tls_config holds state that is needed to create new TLS client
+ * or server connections (e.g. certificates and private keys).
+ *
+ * It is not defined by lwIP itself but by the TLS port (e.g. altcp_tls to mbedTLS
+ * adaption). However, the parameters used to create it are defined in @ref
+ * altcp_tls.h (see @ref altcp_tls_create_config_server_privkey_cert for servers
+ * and @ref altcp_tls_create_config_client / @ref altcp_tls_create_config_client_2wayauth
+ * for clients).
+ *
+ * For mbedTLS, ensure that certificates can be parsed by 'mbedtls_x509_crt_parse()' and
+ * private keys can be parsed by 'mbedtls_pk_parse_key()'.
+ */
+
+/*
+ * Copyright (c) 2017 Simon Goldschmidt
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt <goldsimon@gmx.de>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_ALTCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/altcp.h"
+#include "lwip/priv/altcp_priv.h"
+#include "lwip/altcp_tcp.h"
+#include "lwip/tcp.h"
+#include "lwip/mem.h"
+
+#include <string.h>
+
+extern const struct altcp_functions altcp_tcp_functions;
+
+/**
+ * For altcp layer implementations only: allocate a new struct altcp_pcb from the pool
+ * and zero the memory
+ */
+struct altcp_pcb *
+altcp_alloc(void)
+{
+  struct altcp_pcb *ret = (struct altcp_pcb *)memp_malloc(MEMP_ALTCP_PCB);
+  if (ret != NULL) {
+    memset(ret, 0, sizeof(struct altcp_pcb));
+  }
+  return ret;
+}
+
+/**
+ * For altcp layer implementations only: return a struct altcp_pcb to the pool
+ */
+void
+altcp_free(struct altcp_pcb *conn)
+{
+  if (conn) {
+    if (conn->fns && conn->fns->dealloc) {
+      conn->fns->dealloc(conn);
+    }
+    memp_free(MEMP_ALTCP_PCB, conn);
+  }
+}
+
+/**
+ * @ingroup altcp
+ * altcp_new_ip6: @ref altcp_new for IPv6
+ */
+struct altcp_pcb *
+altcp_new_ip6(altcp_allocator_t *allocator)
+{
+  return altcp_new_ip_type(allocator, IPADDR_TYPE_V6);
+}
+
+/**
+ * @ingroup altcp
+ * altcp_new: @ref altcp_new for IPv4
+ */
+struct altcp_pcb *
+altcp_new(altcp_allocator_t *allocator)
+{
+  return altcp_new_ip_type(allocator, IPADDR_TYPE_V4);
+}
+
+/**
+ * @ingroup altcp
+ * altcp_new_ip_type: called by applications to allocate a new pcb with the help of an
+ * allocator function.
+ *
+ * @param allocator allocator function and argument
+ * @param ip_type IP version of the pcb (@ref lwip_ip_addr_type)
+ * @return a new altcp_pcb or NULL on error
+ */
+struct altcp_pcb *
+altcp_new_ip_type(altcp_allocator_t *allocator, u8_t ip_type)
+{
+  struct altcp_pcb *conn;
+  if (allocator == NULL) {
+    /* no allocator given, create a simple TCP connection */
+    return altcp_tcp_new_ip_type(ip_type);
+  }
+  if (allocator->alloc == NULL) {
+    /* illegal allocator */
+    return NULL;
+  }
+  conn = allocator->alloc(allocator->arg, ip_type);
+  if (conn == NULL) {
+    /* allocation failed */
+    return NULL;
+  }
+  return conn;
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_arg()
+ */
+void
+altcp_arg(struct altcp_pcb *conn, void *arg)
+{
+  if (conn) {
+    conn->arg = arg;
+  }
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_accept()
+ */
+void
+altcp_accept(struct altcp_pcb *conn, altcp_accept_fn accept)
+{
+  if (conn != NULL) {
+    conn->accept = accept;
+  }
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_recv()
+ */
+void
+altcp_recv(struct altcp_pcb *conn, altcp_recv_fn recv)
+{
+  if (conn) {
+    conn->recv = recv;
+  }
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_sent()
+ */
+void
+altcp_sent(struct altcp_pcb *conn, altcp_sent_fn sent)
+{
+  if (conn) {
+    conn->sent = sent;
+  }
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_poll()
+ */
+void
+altcp_poll(struct altcp_pcb *conn, altcp_poll_fn poll, u8_t interval)
+{
+  if (conn) {
+    conn->poll = poll;
+    conn->pollinterval = interval;
+    if (conn->fns && conn->fns->set_poll) {
+      conn->fns->set_poll(conn, interval);
+    }
+  }
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_err()
+ */
+void
+altcp_err(struct altcp_pcb *conn, altcp_err_fn err)
+{
+  if (conn) {
+    conn->err = err;
+  }
+}
+
+/* Generic functions calling the "virtual" ones */
+
+/**
+ * @ingroup altcp
+ * @see tcp_recved()
+ */
+void
+altcp_recved(struct altcp_pcb *conn, u16_t len)
+{
+  if (conn && conn->fns && conn->fns->recved) {
+    conn->fns->recved(conn, len);
+  }
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_bind()
+ */
+err_t
+altcp_bind(struct altcp_pcb *conn, const ip_addr_t *ipaddr, u16_t port)
+{
+  if (conn && conn->fns && conn->fns->bind) {
+    return conn->fns->bind(conn, ipaddr, port);
+  }
+  return ERR_VAL;
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_connect()
+ */
+err_t
+altcp_connect(struct altcp_pcb *conn, const ip_addr_t *ipaddr, u16_t port, altcp_connected_fn connected)
+{
+  if (conn && conn->fns && conn->fns->connect) {
+    return conn->fns->connect(conn, ipaddr, port, connected);
+  }
+  return ERR_VAL;
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_listen_with_backlog_and_err()
+ */
+struct altcp_pcb *
+altcp_listen_with_backlog_and_err(struct altcp_pcb *conn, u8_t backlog, err_t *err)
+{
+  if (conn && conn->fns && conn->fns->listen) {
+    return conn->fns->listen(conn, backlog, err);
+  }
+  return NULL;
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_abort()
+ */
+void
+altcp_abort(struct altcp_pcb *conn)
+{
+  if (conn && conn->fns && conn->fns->abort) {
+    conn->fns->abort(conn);
+  }
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_close()
+ */
+err_t
+altcp_close(struct altcp_pcb *conn)
+{
+  if (conn && conn->fns && conn->fns->close) {
+    return conn->fns->close(conn);
+  }
+  return ERR_VAL;
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_shutdown()
+ */
+err_t
+altcp_shutdown(struct altcp_pcb *conn, int shut_rx, int shut_tx)
+{
+  if (conn && conn->fns && conn->fns->shutdown) {
+    return conn->fns->shutdown(conn, shut_rx, shut_tx);
+  }
+  return ERR_VAL;
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_write()
+ */
+err_t
+altcp_write(struct altcp_pcb *conn, const void *dataptr, u16_t len, u8_t apiflags)
+{
+  if (conn && conn->fns && conn->fns->write) {
+    return conn->fns->write(conn, dataptr, len, apiflags);
+  }
+  return ERR_VAL;
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_output()
+ */
+err_t
+altcp_output(struct altcp_pcb *conn)
+{
+  if (conn && conn->fns && conn->fns->output) {
+    return conn->fns->output(conn);
+  }
+  return ERR_VAL;
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_mss()
+ */
+u16_t
+altcp_mss(struct altcp_pcb *conn)
+{
+  if (conn && conn->fns && conn->fns->mss) {
+    return conn->fns->mss(conn);
+  }
+  return 0;
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_sndbuf()
+ */
+u16_t
+altcp_sndbuf(struct altcp_pcb *conn)
+{
+  if (conn && conn->fns && conn->fns->sndbuf) {
+    return conn->fns->sndbuf(conn);
+  }
+  return 0;
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_sndqueuelen()
+ */
+u16_t
+altcp_sndqueuelen(struct altcp_pcb *conn)
+{
+  if (conn && conn->fns && conn->fns->sndqueuelen) {
+    return conn->fns->sndqueuelen(conn);
+  }
+  return 0;
+}
+
+void
+altcp_nagle_disable(struct altcp_pcb *conn)
+{
+  if (conn && conn->fns && conn->fns->nagle_disable) {
+    conn->fns->nagle_disable(conn);
+  }
+}
+
+void
+altcp_nagle_enable(struct altcp_pcb *conn)
+{
+  if (conn && conn->fns && conn->fns->nagle_enable) {
+    conn->fns->nagle_enable(conn);
+  }
+}
+
+int
+altcp_nagle_disabled(struct altcp_pcb *conn)
+{
+  if (conn && conn->fns && conn->fns->nagle_disabled) {
+    return conn->fns->nagle_disabled(conn);
+  }
+  return 0;
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_setprio()
+ */
+void
+altcp_setprio(struct altcp_pcb *conn, u8_t prio)
+{
+  if (conn && conn->fns && conn->fns->setprio) {
+    conn->fns->setprio(conn, prio);
+  }
+}
+
+err_t
+altcp_get_tcp_addrinfo(struct altcp_pcb *conn, int local, ip_addr_t *addr, u16_t *port)
+{
+  if (conn && conn->fns && conn->fns->addrinfo) {
+    return conn->fns->addrinfo(conn, local, addr, port);
+  }
+  return ERR_VAL;
+}
+
+ip_addr_t *
+altcp_get_ip(struct altcp_pcb *conn, int local)
+{
+  if (conn && conn->fns && conn->fns->getip) {
+    return conn->fns->getip(conn, local);
+  }
+  return NULL;
+}
+
+u16_t
+altcp_get_port(struct altcp_pcb *conn, int local)
+{
+  if (conn && conn->fns && conn->fns->getport) {
+    return conn->fns->getport(conn, local);
+  }
+  return 0;
+}
+
+#if LWIP_TCP_KEEPALIVE
+void
+altcp_keepalive_disable(struct altcp_pcb *conn)
+{
+  if (conn && conn->fns && conn->fns->keepalive_disable) {
+    conn->fns->keepalive_disable(conn);
+  }
+}
+
+void
+altcp_keepalive_enable(struct altcp_pcb *conn, u32_t idle, u32_t intvl, u32_t count)
+{
+  if (conn && conn->fns && conn->fns->keepalive_enable) {
+      conn->fns->keepalive_enable(conn, idle, intvl, count);
+  }
+}
+#endif
+
+#ifdef LWIP_DEBUG
+enum tcp_state
+altcp_dbg_get_tcp_state(struct altcp_pcb *conn)
+{
+  if (conn && conn->fns && conn->fns->dbg_get_tcp_state) {
+    return conn->fns->dbg_get_tcp_state(conn);
+  }
+  return CLOSED;
+}
+#endif
+
+/* Default implementations for the "virtual" functions */
+
+void
+altcp_default_set_poll(struct altcp_pcb *conn, u8_t interval)
+{
+  if (conn && conn->inner_conn) {
+    altcp_poll(conn->inner_conn, conn->poll, interval);
+  }
+}
+
+void
+altcp_default_recved(struct altcp_pcb *conn, u16_t len)
+{
+  if (conn && conn->inner_conn) {
+    altcp_recved(conn->inner_conn, len);
+  }
+}
+
+err_t
+altcp_default_bind(struct altcp_pcb *conn, const ip_addr_t *ipaddr, u16_t port)
+{
+  if (conn && conn->inner_conn) {
+    return altcp_bind(conn->inner_conn, ipaddr, port);
+  }
+  return ERR_VAL;
+}
+
+err_t
+altcp_default_shutdown(struct altcp_pcb *conn, int shut_rx, int shut_tx)
+{
+  if (conn) {
+    if (shut_rx && shut_tx && conn->fns && conn->fns->close) {
+      /* default shutdown for both sides is close */
+      return conn->fns->close(conn);
+    }
+    if (conn->inner_conn) {
+      return altcp_shutdown(conn->inner_conn, shut_rx, shut_tx);
+    }
+  }
+  return ERR_VAL;
+}
+
+err_t
+altcp_default_write(struct altcp_pcb *conn, const void *dataptr, u16_t len, u8_t apiflags)
+{
+  if (conn && conn->inner_conn) {
+    return altcp_write(conn->inner_conn, dataptr, len, apiflags);
+  }
+  return ERR_VAL;
+}
+
+err_t
+altcp_default_output(struct altcp_pcb *conn)
+{
+  if (conn && conn->inner_conn) {
+    return altcp_output(conn->inner_conn);
+  }
+  return ERR_VAL;
+}
+
+u16_t
+altcp_default_mss(struct altcp_pcb *conn)
+{
+  if (conn && conn->inner_conn) {
+    return altcp_mss(conn->inner_conn);
+  }
+  return 0;
+}
+
+u16_t
+altcp_default_sndbuf(struct altcp_pcb *conn)
+{
+  if (conn && conn->inner_conn) {
+    return altcp_sndbuf(conn->inner_conn);
+  }
+  return 0;
+}
+
+u16_t
+altcp_default_sndqueuelen(struct altcp_pcb *conn)
+{
+  if (conn && conn->inner_conn) {
+    return altcp_sndqueuelen(conn->inner_conn);
+  }
+  return 0;
+}
+
+void
+altcp_default_nagle_disable(struct altcp_pcb *conn)
+{
+  if (conn && conn->inner_conn) {
+    altcp_nagle_disable(conn->inner_conn);
+  }
+}
+
+void
+altcp_default_nagle_enable(struct altcp_pcb *conn)
+{
+  if (conn && conn->inner_conn) {
+    altcp_nagle_enable(conn->inner_conn);
+  }
+}
+
+int
+altcp_default_nagle_disabled(struct altcp_pcb *conn)
+{
+  if (conn && conn->inner_conn) {
+    return altcp_nagle_disabled(conn->inner_conn);
+  }
+  return 0;
+}
+
+void
+altcp_default_setprio(struct altcp_pcb *conn, u8_t prio)
+{
+  if (conn && conn->inner_conn) {
+    altcp_setprio(conn->inner_conn, prio);
+  }
+}
+
+void
+altcp_default_dealloc(struct altcp_pcb *conn)
+{
+  LWIP_UNUSED_ARG(conn);
+  /* nothing to do */
+}
+
+err_t
+altcp_default_get_tcp_addrinfo(struct altcp_pcb *conn, int local, ip_addr_t *addr, u16_t *port)
+{
+  if (conn && conn->inner_conn) {
+    return altcp_get_tcp_addrinfo(conn->inner_conn, local, addr, port);
+  }
+  return ERR_VAL;
+}
+
+ip_addr_t *
+altcp_default_get_ip(struct altcp_pcb *conn, int local)
+{
+  if (conn && conn->inner_conn) {
+    return altcp_get_ip(conn->inner_conn, local);
+  }
+  return NULL;
+}
+
+u16_t
+altcp_default_get_port(struct altcp_pcb *conn, int local)
+{
+  if (conn && conn->inner_conn) {
+    return altcp_get_port(conn->inner_conn, local);
+  }
+  return 0;
+}
+
+#if LWIP_TCP_KEEPALIVE
+void
+altcp_default_keepalive_disable(struct altcp_pcb *conn)
+{
+  if (conn && conn->inner_conn) {
+    altcp_keepalive_disable(conn->inner_conn);
+  }
+}
+
+void
+altcp_default_keepalive_enable(struct altcp_pcb *conn, u32_t idle, u32_t intvl, u32_t count)
+{
+  if (conn && conn->inner_conn) {
+      altcp_keepalive_enable(conn->inner_conn, idle, intvl, count);
+  }
+}
+#endif
+
+#ifdef LWIP_DEBUG
+enum tcp_state
+altcp_default_dbg_get_tcp_state(struct altcp_pcb *conn)
+{
+  if (conn && conn->inner_conn) {
+    return altcp_dbg_get_tcp_state(conn->inner_conn);
+  }
+  return CLOSED;
+}
+#endif
+
+
+#endif /* LWIP_ALTCP */
diff --git a/lib/lwip/core/altcp_alloc.c b/lib/lwip/core/altcp_alloc.c
new file mode 100644
index 0000000000..06ed90d976
--- /dev/null
+++ b/lib/lwip/core/altcp_alloc.c
@@ -0,0 +1,87 @@
+/**
+ * @file
+ * Application layered TCP connection API (to be used from TCPIP thread)<br>
+ * This interface mimics the tcp callback API to the application while preventing
+ * direct linking (much like virtual functions).
+ * This way, an application can make use of other application layer protocols
+ * on top of TCP without knowing the details (e.g. TLS, proxy connection).
+ *
+ * This file contains allocation implementation that combine several layers.
+ */
+
+/*
+ * Copyright (c) 2017 Simon Goldschmidt
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt <goldsimon@gmx.de>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_ALTCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/altcp.h"
+#include "lwip/altcp_tcp.h"
+#include "lwip/altcp_tls.h"
+#include "lwip/priv/altcp_priv.h"
+#include "lwip/mem.h"
+
+#include <string.h>
+
+#if LWIP_ALTCP_TLS
+
+/** This standard allocator function creates an altcp pcb for
+ * TLS over TCP */
+struct altcp_pcb *
+altcp_tls_new(struct altcp_tls_config *config, u8_t ip_type)
+{
+  struct altcp_pcb *inner_conn, *ret;
+  LWIP_UNUSED_ARG(ip_type);
+
+  inner_conn = altcp_tcp_new_ip_type(ip_type);
+  if (inner_conn == NULL) {
+    return NULL;
+  }
+  ret = altcp_tls_wrap(config, inner_conn);
+  if (ret == NULL) {
+    altcp_close(inner_conn);
+  }
+  return ret;
+}
+
+/** This standard allocator function creates an altcp pcb for
+ * TLS over TCP */
+struct altcp_pcb *
+altcp_tls_alloc(void *arg, u8_t ip_type)
+{
+  return altcp_tls_new((struct altcp_tls_config *)arg, ip_type);
+}
+
+#endif /* LWIP_ALTCP_TLS */
+
+#endif /* LWIP_ALTCP */
diff --git a/lib/lwip/core/altcp_tcp.c b/lib/lwip/core/altcp_tcp.c
new file mode 100644
index 0000000000..4f21b703aa
--- /dev/null
+++ b/lib/lwip/core/altcp_tcp.c
@@ -0,0 +1,578 @@
+/**
+ * @file
+ * Application layered TCP connection API (to be used from TCPIP thread)
+ *
+ * This interface mimics the tcp callback API to the application while preventing
+ * direct linking (much like virtual functions).
+ * This way, an application can make use of other application layer protocols
+ * on top of TCP without knowing the details (e.g. TLS, proxy connection).
+ *
+ * This file contains the base implementation calling into tcp.
+ */
+
+/*
+ * Copyright (c) 2017 Simon Goldschmidt
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt <goldsimon@gmx.de>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_ALTCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/altcp.h"
+#include "lwip/altcp_tcp.h"
+#include "lwip/priv/altcp_priv.h"
+#include "lwip/tcp.h"
+#include "lwip/priv/tcp_priv.h"
+#include "lwip/mem.h"
+
+#include <string.h>
+
+#define ALTCP_TCP_ASSERT_CONN(conn) do { \
+  LWIP_ASSERT("conn->inner_conn == NULL", (conn)->inner_conn == NULL); \
+  LWIP_UNUSED_ARG(conn); /* for LWIP_NOASSERT */ } while(0)
+#define ALTCP_TCP_ASSERT_CONN_PCB(conn, tpcb) do { \
+  LWIP_ASSERT("pcb mismatch", (conn)->state == tpcb); \
+  LWIP_UNUSED_ARG(tpcb); /* for LWIP_NOASSERT */ \
+  ALTCP_TCP_ASSERT_CONN(conn); } while(0)
+
+
+/* Variable prototype, the actual declaration is at the end of this file
+   since it contains pointers to static functions declared here */
+extern const struct altcp_functions altcp_tcp_functions;
+
+static void altcp_tcp_setup(struct altcp_pcb *conn, struct tcp_pcb *tpcb);
+
+/* callback functions for TCP */
+static err_t
+altcp_tcp_accept(void *arg, struct tcp_pcb *new_tpcb, err_t err)
+{
+  struct altcp_pcb *listen_conn = (struct altcp_pcb *)arg;
+  if (new_tpcb && listen_conn && listen_conn->accept) {
+    /* create a new altcp_conn to pass to the next 'accept' callback */
+    struct altcp_pcb *new_conn = altcp_alloc();
+    if (new_conn == NULL) {
+      return ERR_MEM;
+    }
+    altcp_tcp_setup(new_conn, new_tpcb);
+    return listen_conn->accept(listen_conn->arg, new_conn, err);
+  }
+  return ERR_ARG;
+}
+
+static err_t
+altcp_tcp_connected(void *arg, struct tcp_pcb *tpcb, err_t err)
+{
+  struct altcp_pcb *conn = (struct altcp_pcb *)arg;
+  if (conn) {
+    ALTCP_TCP_ASSERT_CONN_PCB(conn, tpcb);
+    if (conn->connected) {
+      return conn->connected(conn->arg, conn, err);
+    }
+  }
+  return ERR_OK;
+}
+
+static err_t
+altcp_tcp_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
+{
+  struct altcp_pcb *conn = (struct altcp_pcb *)arg;
+  if (conn) {
+    ALTCP_TCP_ASSERT_CONN_PCB(conn, tpcb);
+    if (conn->recv) {
+      return conn->recv(conn->arg, conn, p, err);
+    }
+  }
+  if (p != NULL) {
+    /* prevent memory leaks */
+    pbuf_free(p);
+  }
+  return ERR_OK;
+}
+
+static err_t
+altcp_tcp_sent(void *arg, struct tcp_pcb *tpcb, u16_t len)
+{
+  struct altcp_pcb *conn = (struct altcp_pcb *)arg;
+  if (conn) {
+    ALTCP_TCP_ASSERT_CONN_PCB(conn, tpcb);
+    if (conn->sent) {
+      return conn->sent(conn->arg, conn, len);
+    }
+  }
+  return ERR_OK;
+}
+
+static err_t
+altcp_tcp_poll(void *arg, struct tcp_pcb *tpcb)
+{
+  struct altcp_pcb *conn = (struct altcp_pcb *)arg;
+  if (conn) {
+    ALTCP_TCP_ASSERT_CONN_PCB(conn, tpcb);
+    if (conn->poll) {
+      return conn->poll(conn->arg, conn);
+    }
+  }
+  return ERR_OK;
+}
+
+static void
+altcp_tcp_err(void *arg, err_t err)
+{
+  struct altcp_pcb *conn = (struct altcp_pcb *)arg;
+  if (conn) {
+    conn->state = NULL; /* already freed */
+    if (conn->err) {
+      conn->err(conn->arg, err);
+    }
+    altcp_free(conn);
+  }
+}
+
+/* setup functions */
+
+static void
+altcp_tcp_remove_callbacks(struct tcp_pcb *tpcb)
+{
+  tcp_arg(tpcb, NULL);
+  if (tpcb->state != LISTEN) {
+    tcp_recv(tpcb, NULL);
+    tcp_sent(tpcb, NULL);
+    tcp_err(tpcb, NULL);
+    tcp_poll(tpcb, NULL, tpcb->pollinterval);
+  }
+}
+
+static void
+altcp_tcp_setup_callbacks(struct altcp_pcb *conn, struct tcp_pcb *tpcb)
+{
+  tcp_arg(tpcb, conn);
+  /* this might be called for LISTN when close fails... */
+  if (tpcb->state != LISTEN) {
+    tcp_recv(tpcb, altcp_tcp_recv);
+    tcp_sent(tpcb, altcp_tcp_sent);
+    tcp_err(tpcb, altcp_tcp_err);
+    /* tcp_poll is set when interval is set by application */
+  }
+}
+
+static void
+altcp_tcp_setup(struct altcp_pcb *conn, struct tcp_pcb *tpcb)
+{
+  altcp_tcp_setup_callbacks(conn, tpcb);
+  conn->state = tpcb;
+  conn->fns = &altcp_tcp_functions;
+}
+
+struct altcp_pcb *
+altcp_tcp_new_ip_type(u8_t ip_type)
+{
+  /* Allocate the tcp pcb first to invoke the priority handling code
+     if we're out of pcbs */
+  struct tcp_pcb *tpcb = tcp_new_ip_type(ip_type);
+  if (tpcb != NULL) {
+    struct altcp_pcb *ret = altcp_alloc();
+    if (ret != NULL) {
+      altcp_tcp_setup(ret, tpcb);
+      return ret;
+    } else {
+      /* altcp_pcb allocation failed -> free the tcp_pcb too */
+      tcp_close(tpcb);
+    }
+  }
+  return NULL;
+}
+
+/** altcp_tcp allocator function fitting to @ref altcp_allocator_t / @ref altcp_new.
+*
+* arg pointer is not used for TCP.
+*/
+struct altcp_pcb *
+altcp_tcp_alloc(void *arg, u8_t ip_type)
+{
+  LWIP_UNUSED_ARG(arg);
+  return altcp_tcp_new_ip_type(ip_type);
+}
+
+struct altcp_pcb *
+altcp_tcp_wrap(struct tcp_pcb *tpcb)
+{
+  if (tpcb != NULL) {
+    struct altcp_pcb *ret = altcp_alloc();
+    if (ret != NULL) {
+      altcp_tcp_setup(ret, tpcb);
+      return ret;
+    }
+  }
+  return NULL;
+}
+
+
+/* "virtual" functions calling into tcp */
+static void
+altcp_tcp_set_poll(struct altcp_pcb *conn, u8_t interval)
+{
+  if (conn != NULL) {
+    struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
+    ALTCP_TCP_ASSERT_CONN(conn);
+    tcp_poll(pcb, altcp_tcp_poll, interval);
+  }
+}
+
+static void
+altcp_tcp_recved(struct altcp_pcb *conn, u16_t len)
+{
+  if (conn != NULL) {
+    struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
+    ALTCP_TCP_ASSERT_CONN(conn);
+    tcp_recved(pcb, len);
+  }
+}
+
+static err_t
+altcp_tcp_bind(struct altcp_pcb *conn, const ip_addr_t *ipaddr, u16_t port)
+{
+  struct tcp_pcb *pcb;
+  if (conn == NULL) {
+    return ERR_VAL;
+  }
+  ALTCP_TCP_ASSERT_CONN(conn);
+  pcb = (struct tcp_pcb *)conn->state;
+  return tcp_bind(pcb, ipaddr, port);
+}
+
+static err_t
+altcp_tcp_connect(struct altcp_pcb *conn, const ip_addr_t *ipaddr, u16_t port, altcp_connected_fn connected)
+{
+  struct tcp_pcb *pcb;
+  if (conn == NULL) {
+    return ERR_VAL;
+  }
+  ALTCP_TCP_ASSERT_CONN(conn);
+  conn->connected = connected;
+  pcb = (struct tcp_pcb *)conn->state;
+  return tcp_connect(pcb, ipaddr, port, altcp_tcp_connected);
+}
+
+static struct altcp_pcb *
+altcp_tcp_listen(struct altcp_pcb *conn, u8_t backlog, err_t *err)
+{
+  struct tcp_pcb *pcb;
+  struct tcp_pcb *lpcb;
+  if (conn == NULL) {
+    return NULL;
+  }
+  ALTCP_TCP_ASSERT_CONN(conn);
+  pcb = (struct tcp_pcb *)conn->state;
+  lpcb = tcp_listen_with_backlog_and_err(pcb, backlog, err);
+  if (lpcb != NULL) {
+    conn->state = lpcb;
+    tcp_accept(lpcb, altcp_tcp_accept);
+    return conn;
+  }
+  return NULL;
+}
+
+static void
+altcp_tcp_abort(struct altcp_pcb *conn)
+{
+  if (conn != NULL) {
+    struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
+    ALTCP_TCP_ASSERT_CONN(conn);
+    if (pcb) {
+      tcp_abort(pcb);
+    }
+  }
+}
+
+static err_t
+altcp_tcp_close(struct altcp_pcb *conn)
+{
+  struct tcp_pcb *pcb;
+  if (conn == NULL) {
+    return ERR_VAL;
+  }
+  ALTCP_TCP_ASSERT_CONN(conn);
+  pcb = (struct tcp_pcb *)conn->state;
+  if (pcb) {
+    err_t err;
+    tcp_poll_fn oldpoll = pcb->poll;
+    altcp_tcp_remove_callbacks(pcb);
+    err = tcp_close(pcb);
+    if (err != ERR_OK) {
+      /* not closed, set up all callbacks again */
+      altcp_tcp_setup_callbacks(conn, pcb);
+      /* poll callback is not included in the above */
+      tcp_poll(pcb, oldpoll, pcb->pollinterval);
+      return err;
+    }
+    conn->state = NULL; /* unsafe to reference pcb after tcp_close(). */
+  }
+  altcp_free(conn);
+  return ERR_OK;
+}
+
+static err_t
+altcp_tcp_shutdown(struct altcp_pcb *conn, int shut_rx, int shut_tx)
+{
+  struct tcp_pcb *pcb;
+  if (conn == NULL) {
+    return ERR_VAL;
+  }
+  ALTCP_TCP_ASSERT_CONN(conn);
+  pcb = (struct tcp_pcb *)conn->state;
+  return tcp_shutdown(pcb, shut_rx, shut_tx);
+}
+
+static err_t
+altcp_tcp_write(struct altcp_pcb *conn, const void *dataptr, u16_t len, u8_t apiflags)
+{
+  struct tcp_pcb *pcb;
+  if (conn == NULL) {
+    return ERR_VAL;
+  }
+  ALTCP_TCP_ASSERT_CONN(conn);
+  pcb = (struct tcp_pcb *)conn->state;
+  return tcp_write(pcb, dataptr, len, apiflags);
+}
+
+static err_t
+altcp_tcp_output(struct altcp_pcb *conn)
+{
+  struct tcp_pcb *pcb;
+  if (conn == NULL) {
+    return ERR_VAL;
+  }
+  ALTCP_TCP_ASSERT_CONN(conn);
+  pcb = (struct tcp_pcb *)conn->state;
+  return tcp_output(pcb);
+}
+
+static u16_t
+altcp_tcp_mss(struct altcp_pcb *conn)
+{
+  struct tcp_pcb *pcb;
+  if (conn == NULL) {
+    return 0;
+  }
+  ALTCP_TCP_ASSERT_CONN(conn);
+  pcb = (struct tcp_pcb *)conn->state;
+  return tcp_mss(pcb);
+}
+
+static u16_t
+altcp_tcp_sndbuf(struct altcp_pcb *conn)
+{
+  struct tcp_pcb *pcb;
+  if (conn == NULL) {
+    return 0;
+  }
+  ALTCP_TCP_ASSERT_CONN(conn);
+  pcb = (struct tcp_pcb *)conn->state;
+  return tcp_sndbuf(pcb);
+}
+
+static u16_t
+altcp_tcp_sndqueuelen(struct altcp_pcb *conn)
+{
+  struct tcp_pcb *pcb;
+  if (conn == NULL) {
+    return 0;
+  }
+  ALTCP_TCP_ASSERT_CONN(conn);
+  pcb = (struct tcp_pcb *)conn->state;
+  return tcp_sndqueuelen(pcb);
+}
+
+static void
+altcp_tcp_nagle_disable(struct altcp_pcb *conn)
+{
+  if (conn && conn->state) {
+    struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
+    ALTCP_TCP_ASSERT_CONN(conn);
+    tcp_nagle_disable(pcb);
+  }
+}
+
+static void
+altcp_tcp_nagle_enable(struct altcp_pcb *conn)
+{
+  if (conn && conn->state) {
+    struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
+    ALTCP_TCP_ASSERT_CONN(conn);
+    tcp_nagle_enable(pcb);
+  }
+}
+
+static int
+altcp_tcp_nagle_disabled(struct altcp_pcb *conn)
+{
+  if (conn && conn->state) {
+    struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
+    ALTCP_TCP_ASSERT_CONN(conn);
+    return tcp_nagle_disabled(pcb);
+  }
+  return 0;
+}
+
+static void
+altcp_tcp_setprio(struct altcp_pcb *conn, u8_t prio)
+{
+  if (conn != NULL) {
+    struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
+    ALTCP_TCP_ASSERT_CONN(conn);
+    tcp_setprio(pcb, prio);
+  }
+}
+
+#if LWIP_TCP_KEEPALIVE
+static void
+altcp_tcp_keepalive_disable(struct altcp_pcb *conn)
+{
+  if (conn && conn->state) {
+    struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
+    ALTCP_TCP_ASSERT_CONN(conn);
+    ip_reset_option(pcb, SOF_KEEPALIVE);
+  }
+}
+
+static void
+altcp_tcp_keepalive_enable(struct altcp_pcb *conn, u32_t idle, u32_t intvl, u32_t cnt)
+{
+  if (conn && conn->state) {
+    struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
+    ALTCP_TCP_ASSERT_CONN(conn);
+    ip_set_option(pcb, SOF_KEEPALIVE);
+    pcb->keep_idle = idle ? idle : TCP_KEEPIDLE_DEFAULT;
+    pcb->keep_intvl = intvl ? intvl : TCP_KEEPINTVL_DEFAULT;
+    pcb->keep_cnt = cnt ? cnt : TCP_KEEPCNT_DEFAULT;
+  }
+}
+#endif
+
+static void
+altcp_tcp_dealloc(struct altcp_pcb *conn)
+{
+  LWIP_UNUSED_ARG(conn);
+  ALTCP_TCP_ASSERT_CONN(conn);
+  /* no private state to clean up */
+}
+
+static err_t
+altcp_tcp_get_tcp_addrinfo(struct altcp_pcb *conn, int local, ip_addr_t *addr, u16_t *port)
+{
+  if (conn) {
+    struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
+    ALTCP_TCP_ASSERT_CONN(conn);
+    return tcp_tcp_get_tcp_addrinfo(pcb, local, addr, port);
+  }
+  return ERR_VAL;
+}
+
+static ip_addr_t *
+altcp_tcp_get_ip(struct altcp_pcb *conn, int local)
+{
+  if (conn) {
+    struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
+    ALTCP_TCP_ASSERT_CONN(conn);
+    if (pcb) {
+      if (local) {
+        return &pcb->local_ip;
+      } else {
+        return &pcb->remote_ip;
+      }
+    }
+  }
+  return NULL;
+}
+
+static u16_t
+altcp_tcp_get_port(struct altcp_pcb *conn, int local)
+{
+  if (conn) {
+    struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
+    ALTCP_TCP_ASSERT_CONN(conn);
+    if (pcb) {
+      if (local) {
+        return pcb->local_port;
+      } else {
+        return pcb->remote_port;
+      }
+    }
+  }
+  return 0;
+}
+
+#ifdef LWIP_DEBUG
+static enum tcp_state
+altcp_tcp_dbg_get_tcp_state(struct altcp_pcb *conn)
+{
+  if (conn) {
+    struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
+    ALTCP_TCP_ASSERT_CONN(conn);
+    if (pcb) {
+      return pcb->state;
+    }
+  }
+  return CLOSED;
+}
+#endif
+const struct altcp_functions altcp_tcp_functions = {
+  altcp_tcp_set_poll,
+  altcp_tcp_recved,
+  altcp_tcp_bind,
+  altcp_tcp_connect,
+  altcp_tcp_listen,
+  altcp_tcp_abort,
+  altcp_tcp_close,
+  altcp_tcp_shutdown,
+  altcp_tcp_write,
+  altcp_tcp_output,
+  altcp_tcp_mss,
+  altcp_tcp_sndbuf,
+  altcp_tcp_sndqueuelen,
+  altcp_tcp_nagle_disable,
+  altcp_tcp_nagle_enable,
+  altcp_tcp_nagle_disabled,
+  altcp_tcp_setprio,
+  altcp_tcp_dealloc,
+  altcp_tcp_get_tcp_addrinfo,
+  altcp_tcp_get_ip,
+  altcp_tcp_get_port
+#if LWIP_TCP_KEEPALIVE
+  , altcp_tcp_keepalive_disable
+  , altcp_tcp_keepalive_enable
+#endif
+#ifdef LWIP_DEBUG
+  , altcp_tcp_dbg_get_tcp_state
+#endif
+};
+
+#endif /* LWIP_ALTCP */
diff --git a/lib/lwip/core/def.c b/lib/lwip/core/def.c
new file mode 100644
index 0000000000..282fb41de3
--- /dev/null
+++ b/lib/lwip/core/def.c
@@ -0,0 +1,263 @@
+/**
+ * @file
+ * Common functions used throughout the stack.
+ *
+ * These are reference implementations of the byte swapping functions.
+ * Again with the aim of being simple, correct and fully portable.
+ * Byte swapping is the second thing you would want to optimize. You will
+ * need to port it to your architecture and in your cc.h:
+ *
+ * \#define lwip_htons(x) your_htons
+ * \#define lwip_htonl(x) your_htonl
+ *
+ * Note lwip_ntohs() and lwip_ntohl() are merely references to the htonx counterparts.
+ *
+ * If you \#define them to htons() and htonl(), you should
+ * \#define LWIP_DONT_PROVIDE_BYTEORDER_FUNCTIONS to prevent lwIP from
+ * defining htonx/ntohx compatibility macros.
+
+ * @defgroup sys_nonstandard Non-standard functions
+ * @ingroup sys_layer
+ * lwIP provides default implementations for non-standard functions.
+ * These can be mapped to OS functions to reduce code footprint if desired.
+ * All defines related to this section must not be placed in lwipopts.h,
+ * but in arch/cc.h!
+ * These options cannot be \#defined in lwipopts.h since they are not options
+ * of lwIP itself, but options of the lwIP port to your system.
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt
+ *
+ */
+
+#include "lwip/opt.h"
+#include "lwip/def.h"
+
+#include <string.h>
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+
+#if !defined(lwip_htons)
+/**
+ * Convert an u16_t from host- to network byte order.
+ *
+ * @param n u16_t in host byte order
+ * @return n in network byte order
+ */
+u16_t
+lwip_htons(u16_t n)
+{
+  return PP_HTONS(n);
+}
+#endif /* lwip_htons */
+
+#if !defined(lwip_htonl)
+/**
+ * Convert an u32_t from host- to network byte order.
+ *
+ * @param n u32_t in host byte order
+ * @return n in network byte order
+ */
+u32_t
+lwip_htonl(u32_t n)
+{
+  return PP_HTONL(n);
+}
+#endif /* lwip_htonl */
+
+#endif /* BYTE_ORDER == LITTLE_ENDIAN */
+
+#ifndef lwip_strnstr
+/**
+ * @ingroup sys_nonstandard
+ * lwIP default implementation for strnstr() non-standard function.
+ * This can be \#defined to strnstr() depending on your platform port.
+ */
+char *
+lwip_strnstr(const char *buffer, const char *token, size_t n)
+{
+  const char *p;
+  size_t tokenlen = strlen(token);
+  if (tokenlen == 0) {
+    return LWIP_CONST_CAST(char *, buffer);
+  }
+  for (p = buffer; *p && (p + tokenlen <= buffer + n); p++) {
+    if ((*p == *token) && (strncmp(p, token, tokenlen) == 0)) {
+      return LWIP_CONST_CAST(char *, p);
+    }
+  }
+  return NULL;
+}
+#endif
+
+#ifndef lwip_strnistr
+/**
+ * @ingroup sys_nonstandard
+ * lwIP default implementation for strnistr() non-standard function.
+ * This can be \#defined to strnistr() depending on your platform port.
+ */
+char *
+lwip_strnistr(const char *buffer, const char *token, size_t n)
+{
+  const char *p;
+  size_t tokenlen = strlen(token);
+  if (tokenlen == 0) {
+    return LWIP_CONST_CAST(char *, buffer);
+  }
+  for (p = buffer; *p && (p + tokenlen <= buffer + n); p++) {
+    if (lwip_strnicmp(p, token, tokenlen) == 0) {
+      return LWIP_CONST_CAST(char *, p);
+    }
+  }
+  return NULL;
+}
+#endif
+
+#ifndef lwip_stricmp
+/**
+ * @ingroup sys_nonstandard
+ * lwIP default implementation for stricmp() non-standard function.
+ * This can be \#defined to stricmp() depending on your platform port.
+ */
+int
+lwip_stricmp(const char *str1, const char *str2)
+{
+  char c1, c2;
+
+  do {
+    c1 = *str1++;
+    c2 = *str2++;
+    if (c1 != c2) {
+      char c1_upc = c1 | 0x20;
+      if ((c1_upc >= 'a') && (c1_upc <= 'z')) {
+        /* characters are not equal an one is in the alphabet range:
+        downcase both chars and check again */
+        char c2_upc = c2 | 0x20;
+        if (c1_upc != c2_upc) {
+          /* still not equal */
+          /* don't care for < or > */
+          return 1;
+        }
+      } else {
+        /* characters are not equal but none is in the alphabet range */
+        return 1;
+      }
+    }
+  } while (c1 != 0);
+  return 0;
+}
+#endif
+
+#ifndef lwip_strnicmp
+/**
+ * @ingroup sys_nonstandard
+ * lwIP default implementation for strnicmp() non-standard function.
+ * This can be \#defined to strnicmp() depending on your platform port.
+ */
+int
+lwip_strnicmp(const char *str1, const char *str2, size_t len)
+{
+  char c1, c2;
+
+  do {
+    c1 = *str1++;
+    c2 = *str2++;
+    if (c1 != c2) {
+      char c1_upc = c1 | 0x20;
+      if ((c1_upc >= 'a') && (c1_upc <= 'z')) {
+        /* characters are not equal an one is in the alphabet range:
+        downcase both chars and check again */
+        char c2_upc = c2 | 0x20;
+        if (c1_upc != c2_upc) {
+          /* still not equal */
+          /* don't care for < or > */
+          return 1;
+        }
+      } else {
+        /* characters are not equal but none is in the alphabet range */
+        return 1;
+      }
+    }
+    len--;
+  } while ((len != 0) && (c1 != 0));
+  return 0;
+}
+#endif
+
+#ifndef lwip_itoa
+/**
+ * @ingroup sys_nonstandard
+ * lwIP default implementation for itoa() non-standard function.
+ * This can be \#defined to itoa() or snprintf(result, bufsize, "%d", number) depending on your platform port.
+ */
+void
+lwip_itoa(char *result, size_t bufsize, int number)
+{
+  char *res = result;
+  char *tmp = result + bufsize - 1;
+  int n = (number >= 0) ? number : -number;
+
+  /* handle invalid bufsize */
+  if (bufsize < 2) {
+    if (bufsize == 1) {
+      *result = 0;
+    }
+    return;
+  }
+
+  /* First, add sign */
+  if (number < 0) {
+    *res++ = '-';
+  }
+  /* Then create the string from the end and stop if buffer full,
+     and ensure output string is zero terminated */
+  *tmp = 0;
+  while ((n != 0) && (tmp > res)) {
+    char val = (char)('0' + (n % 10));
+    tmp--;
+    *tmp = val;
+    n = n / 10;
+  }
+  if (n) {
+    /* buffer is too small */
+    *result = 0;
+    return;
+  }
+  if (*tmp == 0) {
+    /* Nothing added? */
+    *res++ = '0';
+    *res++ = 0;
+    return;
+  }
+  /* move from temporary buffer to output buffer (sign is not moved) */
+  memmove(res, tmp, (size_t)((result + bufsize) - tmp));
+}
+#endif
diff --git a/lib/lwip/core/dns.c b/lib/lwip/core/dns.c
new file mode 100644
index 0000000000..6540f143ba
--- /dev/null
+++ b/lib/lwip/core/dns.c
@@ -0,0 +1,1657 @@
+/**
+ * @file
+ * DNS - host name to IP address resolver.
+ *
+ * @defgroup dns DNS
+ * @ingroup callbackstyle_api
+ *
+ * Implements a DNS host name to IP address resolver.
+ *
+ * The lwIP DNS resolver functions are used to lookup a host name and
+ * map it to a numerical IP address. It maintains a list of resolved
+ * hostnames that can be queried with the dns_lookup() function.
+ * New hostnames can be resolved using the dns_query() function.
+ *
+ * The lwIP version of the resolver also adds a non-blocking version of
+ * gethostbyname() that will work with a raw API application. This function
+ * checks for an IP address string first and converts it if it is valid.
+ * gethostbyname() then does a dns_lookup() to see if the name is
+ * already in the table. If so, the IP is returned. If not, a query is
+ * issued and the function returns with a ERR_INPROGRESS status. The app
+ * using the dns client must then go into a waiting state.
+ *
+ * Once a hostname has been resolved (or found to be non-existent),
+ * the resolver code calls a specified callback function (which
+ * must be implemented by the module that uses the resolver).
+ *
+ * Multicast DNS queries are supported for names ending on ".local".
+ * However, only "One-Shot Multicast DNS Queries" are supported (RFC 6762
+ * chapter 5.1), this is not a fully compliant implementation of continuous
+ * mDNS querying!
+ *
+ * All functions must be called from TCPIP thread.
+ *
+ * @see DNS_MAX_SERVERS
+ * @see LWIP_DHCP_MAX_DNS_SERVERS
+ * @see @ref netconn_common for thread-safe access.
+ */
+
+/*
+ * Port to lwIP from uIP
+ * by Jim Pettinato April 2007
+ *
+ * security fixes and more by Simon Goldschmidt
+ *
+ * uIP version Copyright (c) 2002-2003, Adam Dunkels.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*-----------------------------------------------------------------------------
+ * RFC 1035 - Domain names - implementation and specification
+ * RFC 2181 - Clarifications to the DNS Specification
+ *----------------------------------------------------------------------------*/
+
+/** @todo: define good default values (rfc compliance) */
+/** @todo: improve answer parsing, more checkings... */
+/** @todo: check RFC1035 - 7.3. Processing responses */
+/** @todo: one-shot mDNS: dual-stack fallback to another IP version */
+
+/*-----------------------------------------------------------------------------
+ * Includes
+ *----------------------------------------------------------------------------*/
+
+#include "lwip/opt.h"
+
+#if LWIP_DNS /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/def.h"
+#include "lwip/udp.h"
+#include "lwip/mem.h"
+#include "lwip/memp.h"
+#include "lwip/dns.h"
+#include "lwip/prot/dns.h"
+
+#include <string.h>
+
+/** Random generator function to create random TXIDs and source ports for queries */
+#ifndef DNS_RAND_TXID
+#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_XID) != 0)
+#define DNS_RAND_TXID LWIP_RAND
+#else
+static u16_t dns_txid;
+#define DNS_RAND_TXID() (++dns_txid)
+#endif
+#endif
+
+/** Limits the source port to be >= 1024 by default */
+#ifndef DNS_PORT_ALLOWED
+#define DNS_PORT_ALLOWED(port) ((port) >= 1024)
+#endif
+
+/** DNS resource record max. TTL (one week as default) */
+#ifndef DNS_MAX_TTL
+#define DNS_MAX_TTL               604800
+#elif DNS_MAX_TTL > 0x7FFFFFFF
+#error DNS_MAX_TTL must be a positive 32-bit value
+#endif
+
+#if DNS_TABLE_SIZE > 255
+#error DNS_TABLE_SIZE must fit into an u8_t
+#endif
+#if DNS_MAX_SERVERS > 255
+#error DNS_MAX_SERVERS must fit into an u8_t
+#endif
+
+/* The number of parallel requests (i.e. calls to dns_gethostbyname
+ * that cannot be answered from the DNS table.
+ * This is set to the table size by default.
+ */
+#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING) != 0)
+#ifndef DNS_MAX_REQUESTS
+#define DNS_MAX_REQUESTS          DNS_TABLE_SIZE
+#else
+#if DNS_MAX_REQUESTS > 255
+#error DNS_MAX_REQUESTS must fit into an u8_t
+#endif
+#endif
+#else
+/* In this configuration, both arrays have to have the same size and are used
+ * like one entry (used/free) */
+#define DNS_MAX_REQUESTS          DNS_TABLE_SIZE
+#endif
+
+/* The number of UDP source ports used in parallel */
+#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0)
+#ifndef DNS_MAX_SOURCE_PORTS
+#define DNS_MAX_SOURCE_PORTS      DNS_MAX_REQUESTS
+#else
+#if DNS_MAX_SOURCE_PORTS > 255
+#error DNS_MAX_SOURCE_PORTS must fit into an u8_t
+#endif
+#endif
+#else
+#ifdef DNS_MAX_SOURCE_PORTS
+#undef DNS_MAX_SOURCE_PORTS
+#endif
+#define DNS_MAX_SOURCE_PORTS      1
+#endif
+
+#if LWIP_IPV4 && LWIP_IPV6
+#define LWIP_DNS_ADDRTYPE_IS_IPV6(t) (((t) == LWIP_DNS_ADDRTYPE_IPV6_IPV4) || ((t) == LWIP_DNS_ADDRTYPE_IPV6))
+#define LWIP_DNS_ADDRTYPE_MATCH_IP(t, ip) (IP_IS_V6_VAL(ip) ? LWIP_DNS_ADDRTYPE_IS_IPV6(t) : (!LWIP_DNS_ADDRTYPE_IS_IPV6(t)))
+#define LWIP_DNS_ADDRTYPE_ARG(x) , x
+#define LWIP_DNS_ADDRTYPE_ARG_OR_ZERO(x) x
+#define LWIP_DNS_SET_ADDRTYPE(x, y) do { x = y; } while(0)
+#else
+#if LWIP_IPV6
+#define LWIP_DNS_ADDRTYPE_IS_IPV6(t) 1
+#else
+#define LWIP_DNS_ADDRTYPE_IS_IPV6(t) 0
+#endif
+#define LWIP_DNS_ADDRTYPE_MATCH_IP(t, ip) 1
+#define LWIP_DNS_ADDRTYPE_ARG(x)
+#define LWIP_DNS_ADDRTYPE_ARG_OR_ZERO(x) 0
+#define LWIP_DNS_SET_ADDRTYPE(x, y)
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
+#if LWIP_DNS_SUPPORT_MDNS_QUERIES
+#define LWIP_DNS_ISMDNS_ARG(x) , x
+#else
+#define LWIP_DNS_ISMDNS_ARG(x)
+#endif
+
+/** DNS query message structure.
+    No packing needed: only used locally on the stack. */
+struct dns_query {
+  /* DNS query record starts with either a domain name or a pointer
+     to a name already present somewhere in the packet. */
+  u16_t type;
+  u16_t cls;
+};
+#define SIZEOF_DNS_QUERY 4
+
+/** DNS answer message structure.
+    No packing needed: only used locally on the stack. */
+struct dns_answer {
+  /* DNS answer record starts with either a domain name or a pointer
+     to a name already present somewhere in the packet. */
+  u16_t type;
+  u16_t cls;
+  u32_t ttl;
+  u16_t len;
+};
+#define SIZEOF_DNS_ANSWER 10
+/* maximum allowed size for the struct due to non-packed */
+#define SIZEOF_DNS_ANSWER_ASSERT 12
+
+/* DNS table entry states */
+typedef enum {
+  DNS_STATE_UNUSED           = 0,
+  DNS_STATE_NEW              = 1,
+  DNS_STATE_ASKING           = 2,
+  DNS_STATE_DONE             = 3
+} dns_state_enum_t;
+
+/** DNS table entry */
+struct dns_table_entry {
+  u32_t ttl;
+  ip_addr_t ipaddr;
+  u16_t txid;
+  u8_t  state;
+  u8_t  server_idx;
+  u8_t  tmr;
+  u8_t  retries;
+  u8_t  seqno;
+#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0)
+  u8_t pcb_idx;
+#endif
+  char name[DNS_MAX_NAME_LENGTH];
+#if LWIP_IPV4 && LWIP_IPV6
+  u8_t reqaddrtype;
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+#if LWIP_DNS_SUPPORT_MDNS_QUERIES
+  u8_t is_mdns;
+#endif
+};
+
+/** DNS request table entry: used when dns_gehostbyname cannot answer the
+ * request from the DNS table */
+struct dns_req_entry {
+  /* pointer to callback on DNS query done */
+  dns_found_callback found;
+  /* argument passed to the callback function */
+  void *arg;
+#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING) != 0)
+  u8_t dns_table_idx;
+#endif
+#if LWIP_IPV4 && LWIP_IPV6
+  u8_t reqaddrtype;
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+};
+
+#if DNS_LOCAL_HOSTLIST
+
+#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC
+/** Local host-list. For hostnames in this list, no
+ *  external name resolution is performed */
+static struct local_hostlist_entry *local_hostlist_dynamic;
+#else /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
+
+/** Defining this allows the local_hostlist_static to be placed in a different
+ * linker section (e.g. FLASH) */
+#ifndef DNS_LOCAL_HOSTLIST_STORAGE_PRE
+#define DNS_LOCAL_HOSTLIST_STORAGE_PRE static
+#endif /* DNS_LOCAL_HOSTLIST_STORAGE_PRE */
+/** Defining this allows the local_hostlist_static to be placed in a different
+ * linker section (e.g. FLASH) */
+#ifndef DNS_LOCAL_HOSTLIST_STORAGE_POST
+#define DNS_LOCAL_HOSTLIST_STORAGE_POST
+#endif /* DNS_LOCAL_HOSTLIST_STORAGE_POST */
+DNS_LOCAL_HOSTLIST_STORAGE_PRE struct local_hostlist_entry local_hostlist_static[]
+  DNS_LOCAL_HOSTLIST_STORAGE_POST = DNS_LOCAL_HOSTLIST_INIT;
+
+#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
+
+static void dns_init_local(void);
+static err_t dns_lookup_local(const char *hostname, size_t hostnamelen, ip_addr_t *addr LWIP_DNS_ADDRTYPE_ARG(u8_t dns_addrtype));
+#endif /* DNS_LOCAL_HOSTLIST */
+
+
+/* forward declarations */
+static void dns_recv(void *s, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port);
+static void dns_check_entries(void);
+static void dns_call_found(u8_t idx, ip_addr_t *addr);
+
+/*-----------------------------------------------------------------------------
+ * Globals
+ *----------------------------------------------------------------------------*/
+
+/* DNS variables */
+static struct udp_pcb        *dns_pcbs[DNS_MAX_SOURCE_PORTS];
+#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0)
+static u8_t                   dns_last_pcb_idx;
+#endif
+static u8_t                   dns_seqno;
+static struct dns_table_entry dns_table[DNS_TABLE_SIZE];
+static struct dns_req_entry   dns_requests[DNS_MAX_REQUESTS];
+static ip_addr_t              dns_servers[DNS_MAX_SERVERS];
+
+#if LWIP_IPV4
+const ip_addr_t dns_mquery_v4group = DNS_MQUERY_IPV4_GROUP_INIT;
+#endif /* LWIP_IPV4 */
+#if LWIP_IPV6
+const ip_addr_t dns_mquery_v6group = DNS_MQUERY_IPV6_GROUP_INIT;
+#endif /* LWIP_IPV6 */
+
+/**
+ * Initialize the resolver: set up the UDP pcb and configure the default server
+ * (if DNS_SERVER_ADDRESS is set).
+ */
+void
+dns_init(void)
+{
+#ifdef DNS_SERVER_ADDRESS
+  /* initialize default DNS server address */
+  ip_addr_t dnsserver;
+  DNS_SERVER_ADDRESS(&dnsserver);
+  dns_setserver(0, &dnsserver);
+#endif /* DNS_SERVER_ADDRESS */
+
+  LWIP_ASSERT("sanity check SIZEOF_DNS_QUERY",
+              sizeof(struct dns_query) == SIZEOF_DNS_QUERY);
+  LWIP_ASSERT("sanity check SIZEOF_DNS_ANSWER",
+              sizeof(struct dns_answer) <= SIZEOF_DNS_ANSWER_ASSERT);
+
+  LWIP_DEBUGF(DNS_DEBUG, ("dns_init: initializing\n"));
+
+  /* if dns client not yet initialized... */
+#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) == 0)
+  if (dns_pcbs[0] == NULL) {
+    dns_pcbs[0] = udp_new_ip_type(IPADDR_TYPE_ANY);
+    LWIP_ASSERT("dns_pcbs[0] != NULL", dns_pcbs[0] != NULL);
+
+    /* initialize DNS table not needed (initialized to zero since it is a
+     * global variable) */
+    LWIP_ASSERT("For implicit initialization to work, DNS_STATE_UNUSED needs to be 0",
+                DNS_STATE_UNUSED == 0);
+
+    /* initialize DNS client */
+    udp_bind(dns_pcbs[0], IP_ANY_TYPE, 0);
+    udp_recv(dns_pcbs[0], dns_recv, NULL);
+  }
+#endif
+
+#if DNS_LOCAL_HOSTLIST
+  dns_init_local();
+#endif
+}
+
+/**
+ * @ingroup dns
+ * Initialize one of the DNS servers.
+ *
+ * @param numdns the index of the DNS server to set must be < DNS_MAX_SERVERS
+ * @param dnsserver IP address of the DNS server to set
+ */
+void
+dns_setserver(u8_t numdns, const ip_addr_t *dnsserver)
+{
+  if (numdns < DNS_MAX_SERVERS) {
+    if (dnsserver != NULL) {
+      dns_servers[numdns] = (*dnsserver);
+    } else {
+      dns_servers[numdns] = *IP_ADDR_ANY;
+    }
+  }
+}
+
+/**
+ * @ingroup dns
+ * Obtain one of the currently configured DNS server.
+ *
+ * @param numdns the index of the DNS server
+ * @return IP address of the indexed DNS server or "ip_addr_any" if the DNS
+ *         server has not been configured.
+ */
+const ip_addr_t *
+dns_getserver(u8_t numdns)
+{
+  if (numdns < DNS_MAX_SERVERS) {
+    return &dns_servers[numdns];
+  } else {
+    return IP_ADDR_ANY;
+  }
+}
+
+/**
+ * The DNS resolver client timer - handle retries and timeouts and should
+ * be called every DNS_TMR_INTERVAL milliseconds (every second by default).
+ */
+void
+dns_tmr(void)
+{
+  LWIP_DEBUGF(DNS_DEBUG, ("dns_tmr: dns_check_entries\n"));
+  dns_check_entries();
+}
+
+#if DNS_LOCAL_HOSTLIST
+static void
+dns_init_local(void)
+{
+#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC && defined(DNS_LOCAL_HOSTLIST_INIT)
+  size_t i;
+  struct local_hostlist_entry *entry;
+  /* Dynamic: copy entries from DNS_LOCAL_HOSTLIST_INIT to list */
+  struct local_hostlist_entry local_hostlist_init[] = DNS_LOCAL_HOSTLIST_INIT;
+  size_t namelen;
+  for (i = 0; i < LWIP_ARRAYSIZE(local_hostlist_init); i++) {
+    struct local_hostlist_entry *init_entry = &local_hostlist_init[i];
+    LWIP_ASSERT("invalid host name (NULL)", init_entry->name != NULL);
+    namelen = strlen(init_entry->name);
+    LWIP_ASSERT("namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN", namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN);
+    entry = (struct local_hostlist_entry *)memp_malloc(MEMP_LOCALHOSTLIST);
+    LWIP_ASSERT("mem-error in dns_init_local", entry != NULL);
+    if (entry != NULL) {
+      char *entry_name = (char *)entry + sizeof(struct local_hostlist_entry);
+      MEMCPY(entry_name, init_entry->name, namelen);
+      entry_name[namelen] = 0;
+      entry->name = entry_name;
+      entry->addr = init_entry->addr;
+      entry->next = local_hostlist_dynamic;
+      local_hostlist_dynamic = entry;
+    }
+  }
+#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC && defined(DNS_LOCAL_HOSTLIST_INIT) */
+}
+
+/**
+ * @ingroup dns
+ * Iterate the local host-list for a hostname.
+ *
+ * @param iterator_fn a function that is called for every entry in the local host-list
+ * @param iterator_arg 3rd argument passed to iterator_fn
+ * @return the number of entries in the local host-list
+ */
+size_t
+dns_local_iterate(dns_found_callback iterator_fn, void *iterator_arg)
+{
+  size_t i;
+#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC
+  struct local_hostlist_entry *entry = local_hostlist_dynamic;
+  i = 0;
+  while (entry != NULL) {
+    if (iterator_fn != NULL) {
+      iterator_fn(entry->name, &entry->addr, iterator_arg);
+    }
+    i++;
+    entry = entry->next;
+  }
+#else /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
+  for (i = 0; i < LWIP_ARRAYSIZE(local_hostlist_static); i++) {
+    if (iterator_fn != NULL) {
+      iterator_fn(local_hostlist_static[i].name, &local_hostlist_static[i].addr, iterator_arg);
+    }
+  }
+#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
+  return i;
+}
+
+/**
+ * @ingroup dns
+ * Scans the local host-list for a hostname.
+ *
+ * @param hostname Hostname to look for in the local host-list
+ * @param addr the first IP address for the hostname in the local host-list or
+ *         IPADDR_NONE if not found.
+ * @param dns_addrtype - LWIP_DNS_ADDRTYPE_IPV4_IPV6: try to resolve IPv4 (ATTENTION: no fallback here!)
+ *                     - LWIP_DNS_ADDRTYPE_IPV6_IPV4: try to resolve IPv6 (ATTENTION: no fallback here!)
+ *                     - LWIP_DNS_ADDRTYPE_IPV4: try to resolve IPv4 only
+ *                     - LWIP_DNS_ADDRTYPE_IPV6: try to resolve IPv6 only
+ * @return ERR_OK if found, ERR_ARG if not found
+ */
+err_t
+dns_local_lookup(const char *hostname, ip_addr_t *addr, u8_t dns_addrtype)
+{
+  size_t hostnamelen;
+  LWIP_UNUSED_ARG(dns_addrtype);
+  if ((addr == NULL) ||
+      (!hostname) || (!hostname[0])) {
+    return ERR_ARG;
+  }
+  hostnamelen = strlen(hostname);
+  if (hostname[hostnamelen - 1] == '.') {
+    hostnamelen--;
+  }
+  if (hostnamelen >= DNS_MAX_NAME_LENGTH) {
+    LWIP_DEBUGF(DNS_DEBUG, ("dns_local_lookup: name too long to resolve\n"));
+    return ERR_ARG;
+  }
+  return dns_lookup_local(hostname, hostnamelen, addr LWIP_DNS_ADDRTYPE_ARG(dns_addrtype));
+}
+
+/* Internal implementation for dns_local_lookup and dns_lookup */
+static err_t
+dns_lookup_local(const char *hostname, size_t hostnamelen, ip_addr_t *addr LWIP_DNS_ADDRTYPE_ARG(u8_t dns_addrtype))
+{
+#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC
+  struct local_hostlist_entry *entry = local_hostlist_dynamic;
+  while (entry != NULL) {
+    if ((lwip_strnicmp(entry->name, hostname, hostnamelen) == 0) &&
+        !entry->name[hostnamelen] &&
+        LWIP_DNS_ADDRTYPE_MATCH_IP(dns_addrtype, entry->addr)) {
+      if (addr) {
+        ip_addr_copy(*addr, entry->addr);
+      }
+      return ERR_OK;
+    }
+    entry = entry->next;
+  }
+#else /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
+  size_t i;
+  for (i = 0; i < LWIP_ARRAYSIZE(local_hostlist_static); i++) {
+    if ((lwip_strnicmp(local_hostlist_static[i].name, hostname, hostnamelen) == 0) &&
+        !local_hostlist_static[i].name[hostnamelen] &&
+        LWIP_DNS_ADDRTYPE_MATCH_IP(dns_addrtype, local_hostlist_static[i].addr)) {
+      if (addr) {
+        ip_addr_copy(*addr, local_hostlist_static[i].addr);
+      }
+      return ERR_OK;
+    }
+  }
+#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
+  return ERR_ARG;
+}
+
+#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC
+/**
+ * @ingroup dns
+ * Remove all entries from the local host-list for a specific hostname
+ * and/or IP address
+ *
+ * @param hostname hostname for which entries shall be removed from the local
+ *                 host-list
+ * @param addr address for which entries shall be removed from the local host-list
+ * @return the number of removed entries
+ */
+int
+dns_local_removehost(const char *hostname, const ip_addr_t *addr)
+{
+  int removed = 0;
+  struct local_hostlist_entry *entry = local_hostlist_dynamic;
+  struct local_hostlist_entry *last_entry = NULL;
+  while (entry != NULL) {
+    if (((hostname == NULL) || !lwip_stricmp(entry->name, hostname)) &&
+        ((addr == NULL) || ip_addr_eq(&entry->addr, addr))) {
+      struct local_hostlist_entry *free_entry;
+      if (last_entry != NULL) {
+        last_entry->next = entry->next;
+      } else {
+        local_hostlist_dynamic = entry->next;
+      }
+      free_entry = entry;
+      entry = entry->next;
+      memp_free(MEMP_LOCALHOSTLIST, free_entry);
+      removed++;
+    } else {
+      last_entry = entry;
+      entry = entry->next;
+    }
+  }
+  return removed;
+}
+
+/**
+ * @ingroup dns
+ * Add a hostname/IP address pair to the local host-list.
+ * Duplicates are not checked.
+ *
+ * @param hostname hostname of the new entry
+ * @param addr IP address of the new entry
+ * @return ERR_OK if succeeded or ERR_MEM on memory error
+ */
+err_t
+dns_local_addhost(const char *hostname, const ip_addr_t *addr)
+{
+  struct local_hostlist_entry *entry;
+  size_t namelen;
+  char *entry_name;
+  LWIP_ASSERT("invalid host name (NULL)", hostname != NULL);
+  namelen = strlen(hostname);
+  LWIP_ASSERT("namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN", namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN);
+  entry = (struct local_hostlist_entry *)memp_malloc(MEMP_LOCALHOSTLIST);
+  if (entry == NULL) {
+    return ERR_MEM;
+  }
+  entry_name = (char *)entry + sizeof(struct local_hostlist_entry);
+  MEMCPY(entry_name, hostname, namelen);
+  entry_name[namelen] = 0;
+  entry->name = entry_name;
+  ip_addr_copy(entry->addr, *addr);
+  entry->next = local_hostlist_dynamic;
+  local_hostlist_dynamic = entry;
+  return ERR_OK;
+}
+#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC*/
+#endif /* DNS_LOCAL_HOSTLIST */
+
+/**
+ * @ingroup dns
+ * Look up a hostname in the array of known hostnames.
+ *
+ * @note This function only looks in the internal array of known
+ * hostnames, it does not send out a query for the hostname if none
+ * was found. The function dns_enqueue() can be used to send a query
+ * for a hostname.
+ *
+ * @param name the hostname to look up
+ * @param hostnamelen length of the hostname
+ * @param addr the hostname's IP address, as u32_t (instead of ip_addr_t to
+ *         better check for failure: != IPADDR_NONE) or IPADDR_NONE if the hostname
+ *         was not found in the cached dns_table.
+ * @return ERR_OK if found, ERR_ARG if not found
+ */
+static err_t
+dns_lookup(const char *name, size_t hostnamelen, ip_addr_t *addr LWIP_DNS_ADDRTYPE_ARG(u8_t dns_addrtype))
+{
+  size_t namelen;
+  u8_t i;
+#if DNS_LOCAL_HOSTLIST
+  if (dns_lookup_local(name, hostnamelen, addr LWIP_DNS_ADDRTYPE_ARG(dns_addrtype)) == ERR_OK) {
+    return ERR_OK;
+  }
+#endif /* DNS_LOCAL_HOSTLIST */
+#ifdef DNS_LOOKUP_LOCAL_EXTERN
+  if (DNS_LOOKUP_LOCAL_EXTERN(name, hostnamelen, addr, LWIP_DNS_ADDRTYPE_ARG_OR_ZERO(dns_addrtype)) == ERR_OK) {
+    return ERR_OK;
+  }
+#endif /* DNS_LOOKUP_LOCAL_EXTERN */
+
+  namelen = LWIP_MIN(hostnamelen, DNS_MAX_NAME_LENGTH - 1);
+  /* Walk through name list, return entry if found. If not, return NULL. */
+  for (i = 0; i < DNS_TABLE_SIZE; ++i) {
+    if ((dns_table[i].state == DNS_STATE_DONE) &&
+        (lwip_strnicmp(name, dns_table[i].name, namelen) == 0) &&
+        !dns_table[i].name[namelen] &&
+        LWIP_DNS_ADDRTYPE_MATCH_IP(dns_addrtype, dns_table[i].ipaddr)) {
+      LWIP_DEBUGF(DNS_DEBUG, ("dns_lookup: \"%s\": found = ", name));
+      ip_addr_debug_print_val(DNS_DEBUG, dns_table[i].ipaddr);
+      LWIP_DEBUGF(DNS_DEBUG, ("\n"));
+      if (addr) {
+        ip_addr_copy(*addr, dns_table[i].ipaddr);
+      }
+      return ERR_OK;
+    }
+  }
+
+  return ERR_ARG;
+}
+
+/**
+ * Compare the "dotted" name "query" with the encoded name "response"
+ * to make sure an answer from the DNS server matches the current dns_table
+ * entry (otherwise, answers might arrive late for hostname not on the list
+ * any more).
+ *
+ * For now, this function compares case-insensitive to cope with all kinds of
+ * servers. This also means that "dns 0x20 bit encoding" must be checked
+ * externally, if we want to implement it.
+ * Currently, the request is sent exactly as passed in by he user request.
+ *
+ * @param query hostname (not encoded) from the dns_table
+ * @param p pbuf containing the encoded hostname in the DNS response
+ * @param start_offset offset into p where the name starts
+ * @return 0xFFFF: names differ, other: names equal -> offset behind name
+ */
+static u16_t
+dns_compare_name(const char *query, struct pbuf *p, u16_t start_offset)
+{
+  int n;
+  u16_t response_offset = start_offset;
+
+  do {
+    n = pbuf_try_get_at(p, response_offset);
+    if ((n < 0) || (response_offset == 0xFFFF)) {
+      /* error or overflow */
+      return 0xFFFF;
+    }
+    response_offset++;
+    /** @see RFC 1035 - 4.1.4. Message compression */
+    if ((n & 0xc0) == 0xc0) {
+      /* Compressed name: cannot be equal since we don't send them */
+      return 0xFFFF;
+    } else {
+      /* Not compressed name */
+      while (n > 0) {
+        int c = pbuf_try_get_at(p, response_offset);
+        if (c < 0) {
+          return 0xFFFF;
+        }
+        if (lwip_tolower((*query)) != lwip_tolower((u8_t)c)) {
+          return 0xFFFF;
+        }
+        if (response_offset == 0xFFFF) {
+          /* would overflow */
+          return 0xFFFF;
+        }
+        response_offset++;
+        ++query;
+        --n;
+      }
+      ++query;
+    }
+    n = pbuf_try_get_at(p, response_offset);
+    if (n < 0) {
+      return 0xFFFF;
+    }
+  } while (n != 0);
+
+  if (response_offset == 0xFFFF) {
+    /* would overflow */
+    return 0xFFFF;
+  }
+  return (u16_t)(response_offset + 1);
+}
+
+/**
+ * Walk through a compact encoded DNS name and return the end of the name.
+ *
+ * @param p pbuf containing the name
+ * @param query_idx start index into p pointing to encoded DNS name in the DNS server response
+ * @return index to end of the name
+ */
+static u16_t
+dns_skip_name(struct pbuf *p, u16_t query_idx)
+{
+  int n;
+  u16_t offset = query_idx;
+
+  do {
+    n = pbuf_try_get_at(p, offset++);
+    if ((n < 0) || (offset == 0)) {
+      return 0xFFFF;
+    }
+    /** @see RFC 1035 - 4.1.4. Message compression */
+    if ((n & 0xc0) == 0xc0) {
+      /* Compressed name: since we only want to skip it (not check it), stop here */
+      break;
+    } else {
+      /* Not compressed name */
+      if (offset + n >= p->tot_len) {
+        return 0xFFFF;
+      }
+      offset = (u16_t)(offset + n);
+    }
+    n = pbuf_try_get_at(p, offset);
+    if (n < 0) {
+      return 0xFFFF;
+    }
+  } while (n != 0);
+
+  if (offset == 0xFFFF) {
+    return 0xFFFF;
+  }
+  return (u16_t)(offset + 1);
+}
+
+/**
+ * Send a DNS query packet.
+ *
+ * @param idx the DNS table entry index for which to send a request
+ * @return ERR_OK if packet is sent; an err_t indicating the problem otherwise
+ */
+static err_t
+dns_send(u8_t idx)
+{
+  err_t err;
+  struct dns_hdr hdr;
+  struct dns_query qry;
+  struct pbuf *p;
+  u16_t query_idx, copy_len;
+  const char *hostname, *hostname_part;
+  u8_t n;
+  u8_t pcb_idx;
+  struct dns_table_entry *entry = &dns_table[idx];
+
+  LWIP_DEBUGF(DNS_DEBUG, ("dns_send: dns_servers[%"U16_F"] \"%s\": request\n",
+                          (u16_t)(entry->server_idx), entry->name));
+  LWIP_ASSERT("dns server out of array", entry->server_idx < DNS_MAX_SERVERS);
+  if (ip_addr_isany_val(dns_servers[entry->server_idx])
+#if LWIP_DNS_SUPPORT_MDNS_QUERIES
+      && !entry->is_mdns
+#endif
+     ) {
+    /* DNS server not valid anymore, e.g. PPP netif has been shut down */
+    /* call specified callback function if provided */
+    dns_call_found(idx, NULL);
+    /* flush this entry */
+    entry->state = DNS_STATE_UNUSED;
+    return ERR_OK;
+  }
+
+  /* if here, we have either a new query or a retry on a previous query to process */
+  p = pbuf_alloc(PBUF_TRANSPORT, (u16_t)(SIZEOF_DNS_HDR + strlen(entry->name) + 2 +
+                                         SIZEOF_DNS_QUERY), PBUF_RAM);
+  if (p != NULL) {
+    const ip_addr_t *dst;
+    u16_t dst_port;
+    /* fill dns header */
+    memset(&hdr, 0, SIZEOF_DNS_HDR);
+    hdr.id = lwip_htons(entry->txid);
+    hdr.flags1 = DNS_FLAG1_RD;
+    hdr.numquestions = PP_HTONS(1);
+    pbuf_take(p, &hdr, SIZEOF_DNS_HDR);
+    hostname = entry->name;
+    --hostname;
+
+    /* convert hostname into suitable query format. */
+    query_idx = SIZEOF_DNS_HDR;
+    do {
+      ++hostname;
+      hostname_part = hostname;
+      for (n = 0; *hostname != '.' && *hostname != 0; ++hostname) {
+        ++n;
+      }
+      copy_len = (u16_t)(hostname - hostname_part);
+      if (query_idx + n + 1 > 0xFFFF) {
+        /* u16_t overflow */
+        goto overflow_return;
+      }
+      pbuf_put_at(p, query_idx, n);
+      pbuf_take_at(p, hostname_part, copy_len, (u16_t)(query_idx + 1));
+      query_idx = (u16_t)(query_idx + n + 1);
+    } while (*hostname != 0);
+    pbuf_put_at(p, query_idx, 0);
+    query_idx++;
+
+    /* fill dns query */
+    if (LWIP_DNS_ADDRTYPE_IS_IPV6(entry->reqaddrtype)) {
+      qry.type = PP_HTONS(DNS_RRTYPE_AAAA);
+    } else {
+      qry.type = PP_HTONS(DNS_RRTYPE_A);
+    }
+    qry.cls = PP_HTONS(DNS_RRCLASS_IN);
+    pbuf_take_at(p, &qry, SIZEOF_DNS_QUERY, query_idx);
+
+#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0)
+    pcb_idx = entry->pcb_idx;
+#else
+    pcb_idx = 0;
+#endif
+    /* send dns packet */
+    LWIP_DEBUGF(DNS_DEBUG, ("sending DNS request ID %d for name \"%s\" to server %d\r\n",
+                            entry->txid, entry->name, entry->server_idx));
+#if LWIP_DNS_SUPPORT_MDNS_QUERIES
+    if (entry->is_mdns) {
+      dst_port = DNS_MQUERY_PORT;
+#if LWIP_IPV6
+      if (LWIP_DNS_ADDRTYPE_IS_IPV6(entry->reqaddrtype)) {
+        dst = &dns_mquery_v6group;
+      }
+#endif
+#if LWIP_IPV4 && LWIP_IPV6
+      else
+#endif
+#if LWIP_IPV4
+      {
+        dst = &dns_mquery_v4group;
+      }
+#endif
+    } else
+#endif /* LWIP_DNS_SUPPORT_MDNS_QUERIES */
+    {
+      dst_port = DNS_SERVER_PORT;
+      dst = &dns_servers[entry->server_idx];
+    }
+    err = udp_sendto(dns_pcbs[pcb_idx], p, dst, dst_port);
+
+    /* free pbuf */
+    pbuf_free(p);
+  } else {
+    err = ERR_MEM;
+  }
+
+  return err;
+overflow_return:
+  pbuf_free(p);
+  return ERR_VAL;
+}
+
+#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0)
+static struct udp_pcb *
+dns_alloc_random_port(void)
+{
+  err_t err;
+  struct udp_pcb *pcb;
+
+  pcb = udp_new_ip_type(IPADDR_TYPE_ANY);
+  if (pcb == NULL) {
+    /* out of memory, have to reuse an existing pcb */
+    return NULL;
+  }
+  do {
+    u16_t port = (u16_t)DNS_RAND_TXID();
+    if (DNS_PORT_ALLOWED(port)) {
+      err = udp_bind(pcb, IP_ANY_TYPE, port);
+    } else {
+      /* this port is not allowed, try again */
+      err = ERR_USE;
+    }
+  } while (err == ERR_USE);
+  if (err != ERR_OK) {
+    udp_remove(pcb);
+    return NULL;
+  }
+  udp_recv(pcb, dns_recv, NULL);
+  return pcb;
+}
+
+/**
+ * dns_alloc_pcb() - allocates a new pcb (or reuses an existing one) to be used
+ * for sending a request
+ *
+ * @return an index into dns_pcbs
+ */
+static u8_t
+dns_alloc_pcb(void)
+{
+  u8_t i;
+  u8_t idx;
+
+  for (i = 0; i < DNS_MAX_SOURCE_PORTS; i++) {
+    if (dns_pcbs[i] == NULL) {
+      break;
+    }
+  }
+  if (i < DNS_MAX_SOURCE_PORTS) {
+    dns_pcbs[i] = dns_alloc_random_port();
+    if (dns_pcbs[i] != NULL) {
+      /* succeeded */
+      dns_last_pcb_idx = i;
+      return i;
+    }
+  }
+  /* if we come here, creating a new UDP pcb failed, so we have to use
+     an already existing one (so overflow is no issue) */
+  for (i = 0, idx = (u8_t)(dns_last_pcb_idx + 1); i < DNS_MAX_SOURCE_PORTS; i++, idx++) {
+    if (idx >= DNS_MAX_SOURCE_PORTS) {
+      idx = 0;
+    }
+    if (dns_pcbs[idx] != NULL) {
+      dns_last_pcb_idx = idx;
+      return idx;
+    }
+  }
+  return DNS_MAX_SOURCE_PORTS;
+}
+#endif /* ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0) */
+
+/**
+ * dns_call_found() - call the found callback and check if there are duplicate
+ * entries for the given hostname. If there are any, their found callback will
+ * be called and they will be removed.
+ *
+ * @param idx dns table index of the entry that is resolved or removed
+ * @param addr IP address for the hostname (or NULL on error or memory shortage)
+ */
+static void
+dns_call_found(u8_t idx, ip_addr_t *addr)
+{
+#if ((LWIP_DNS_SECURE & (LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING | LWIP_DNS_SECURE_RAND_SRC_PORT)) != 0)
+  u8_t i;
+#endif
+
+#if LWIP_IPV4 && LWIP_IPV6
+  if (addr != NULL) {
+    /* check that address type matches the request and adapt the table entry */
+    if (IP_IS_V6_VAL(*addr)) {
+      LWIP_ASSERT("invalid response", LWIP_DNS_ADDRTYPE_IS_IPV6(dns_table[idx].reqaddrtype));
+      dns_table[idx].reqaddrtype = LWIP_DNS_ADDRTYPE_IPV6;
+    } else {
+      LWIP_ASSERT("invalid response", !LWIP_DNS_ADDRTYPE_IS_IPV6(dns_table[idx].reqaddrtype));
+      dns_table[idx].reqaddrtype = LWIP_DNS_ADDRTYPE_IPV4;
+    }
+  }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
+#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING) != 0)
+  for (i = 0; i < DNS_MAX_REQUESTS; i++) {
+    if (dns_requests[i].found && (dns_requests[i].dns_table_idx == idx)) {
+      (*dns_requests[i].found)(dns_table[idx].name, addr, dns_requests[i].arg);
+      /* flush this entry */
+      dns_requests[i].found = NULL;
+    }
+  }
+#else
+  if (dns_requests[idx].found) {
+    (*dns_requests[idx].found)(dns_table[idx].name, addr, dns_requests[idx].arg);
+  }
+  dns_requests[idx].found = NULL;
+#endif
+#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0)
+  /* close the pcb used unless other request are using it */
+  for (i = 0; i < DNS_MAX_REQUESTS; i++) {
+    if (i == idx) {
+      continue; /* only check other requests */
+    }
+    if (dns_table[i].state == DNS_STATE_ASKING) {
+      if (dns_table[i].pcb_idx == dns_table[idx].pcb_idx) {
+        /* another request is still using the same pcb */
+        dns_table[idx].pcb_idx = DNS_MAX_SOURCE_PORTS;
+        break;
+      }
+    }
+  }
+  if (dns_table[idx].pcb_idx < DNS_MAX_SOURCE_PORTS) {
+    /* if we come here, the pcb is not used any more and can be removed */
+    udp_remove(dns_pcbs[dns_table[idx].pcb_idx]);
+    dns_pcbs[dns_table[idx].pcb_idx] = NULL;
+    dns_table[idx].pcb_idx = DNS_MAX_SOURCE_PORTS;
+  }
+#endif
+}
+
+/* Create a query transmission ID that is unique for all outstanding queries */
+static u16_t
+dns_create_txid(void)
+{
+  u16_t txid;
+  u8_t i;
+
+again:
+  txid = (u16_t)DNS_RAND_TXID();
+
+  /* check whether the ID is unique */
+  for (i = 0; i < DNS_TABLE_SIZE; i++) {
+    if ((dns_table[i].state == DNS_STATE_ASKING) &&
+        (dns_table[i].txid == txid)) {
+      /* ID already used by another pending query */
+      goto again;
+    }
+  }
+
+  return txid;
+}
+
+/**
+ * Check whether there are other backup DNS servers available to try
+ */
+static u8_t
+dns_backupserver_available(struct dns_table_entry *pentry)
+{
+  u8_t ret = 0;
+
+  if (pentry) {
+    if ((pentry->server_idx + 1 < DNS_MAX_SERVERS) && !ip_addr_isany_val(dns_servers[pentry->server_idx + 1])) {
+      ret = 1;
+    }
+  }
+
+  return ret;
+}
+
+/**
+ * dns_check_entry() - see if entry has not yet been queried and, if so, sends out a query.
+ * Check an entry in the dns_table:
+ * - send out query for new entries
+ * - retry old pending entries on timeout (also with different servers)
+ * - remove completed entries from the table if their TTL has expired
+ *
+ * @param i index of the dns_table entry to check
+ */
+static void
+dns_check_entry(u8_t i)
+{
+  err_t err;
+  struct dns_table_entry *entry = &dns_table[i];
+
+  LWIP_ASSERT("array index out of bounds", i < DNS_TABLE_SIZE);
+
+  switch (entry->state) {
+    case DNS_STATE_NEW:
+      /* initialize new entry */
+      entry->txid = dns_create_txid();
+      entry->state = DNS_STATE_ASKING;
+      entry->server_idx = 0;
+      entry->tmr = 1;
+      entry->retries = 0;
+
+      /* send DNS packet for this entry */
+      err = dns_send(i);
+      if (err != ERR_OK) {
+        LWIP_DEBUGF(DNS_DEBUG | LWIP_DBG_LEVEL_WARNING,
+                    ("dns_send returned error: %s\n", lwip_strerr(err)));
+      }
+      break;
+    case DNS_STATE_ASKING:
+      if (--entry->tmr == 0) {
+        if (++entry->retries == DNS_MAX_RETRIES) {
+          if (dns_backupserver_available(entry)
+#if LWIP_DNS_SUPPORT_MDNS_QUERIES
+              && !entry->is_mdns
+#endif /* LWIP_DNS_SUPPORT_MDNS_QUERIES */
+             ) {
+            /* change of server */
+            entry->server_idx++;
+            entry->tmr = 1;
+            entry->retries = 0;
+          } else {
+            LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entry: \"%s\": timeout\n", entry->name));
+            /* call specified callback function if provided */
+            dns_call_found(i, NULL);
+            /* flush this entry */
+            entry->state = DNS_STATE_UNUSED;
+            break;
+          }
+        } else {
+          /* wait longer for the next retry */
+          entry->tmr = entry->retries;
+        }
+
+        /* send DNS packet for this entry */
+        err = dns_send(i);
+        if (err != ERR_OK) {
+          LWIP_DEBUGF(DNS_DEBUG | LWIP_DBG_LEVEL_WARNING,
+                      ("dns_send returned error: %s\n", lwip_strerr(err)));
+        }
+      }
+      break;
+    case DNS_STATE_DONE:
+      /* if the time to live is nul */
+      if ((entry->ttl == 0) || (--entry->ttl == 0)) {
+        LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entry: \"%s\": flush\n", entry->name));
+        /* flush this entry, there cannot be any related pending entries in this state */
+        entry->state = DNS_STATE_UNUSED;
+      }
+      break;
+    case DNS_STATE_UNUSED:
+      /* nothing to do */
+      break;
+    default:
+      LWIP_ASSERT("unknown dns_table entry state:", 0);
+      break;
+  }
+}
+
+/**
+ * Call dns_check_entry for each entry in dns_table - check all entries.
+ */
+static void
+dns_check_entries(void)
+{
+  u8_t i;
+
+  for (i = 0; i < DNS_TABLE_SIZE; ++i) {
+    dns_check_entry(i);
+  }
+}
+
+/**
+ * Save TTL and call dns_call_found for correct response.
+ */
+static void
+dns_correct_response(u8_t idx, u32_t ttl)
+{
+  struct dns_table_entry *entry = &dns_table[idx];
+
+  entry->state = DNS_STATE_DONE;
+
+  LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response = ", entry->name));
+  ip_addr_debug_print_val(DNS_DEBUG, entry->ipaddr);
+  LWIP_DEBUGF(DNS_DEBUG, ("\n"));
+
+  /* read the answer resource record's TTL, and maximize it if needed */
+  entry->ttl = ttl;
+  if (entry->ttl > DNS_MAX_TTL) {
+    entry->ttl = DNS_MAX_TTL;
+  }
+  dns_call_found(idx, &entry->ipaddr);
+
+  if (entry->ttl == 0) {
+    /* RFC 883, page 29: "Zero values are
+       interpreted to mean that the RR can only be used for the
+       transaction in progress, and should not be cached."
+       -> flush this entry now */
+    /* entry reused during callback? */
+    if (entry->state == DNS_STATE_DONE) {
+      entry->state = DNS_STATE_UNUSED;
+    }
+  }
+}
+
+/**
+ * Receive input function for DNS response packets arriving for the dns UDP pcb.
+ */
+static void
+dns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
+{
+  u8_t i;
+  u16_t txid;
+  u16_t res_idx;
+  struct dns_hdr hdr;
+  struct dns_answer ans;
+  struct dns_query qry;
+  u16_t nquestions, nanswers;
+
+  LWIP_UNUSED_ARG(arg);
+  LWIP_UNUSED_ARG(pcb);
+  LWIP_UNUSED_ARG(port);
+
+  /* is the dns message big enough ? */
+  if (p->tot_len < (SIZEOF_DNS_HDR + SIZEOF_DNS_QUERY)) {
+    LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: pbuf too small\n"));
+    /* free pbuf and return */
+    goto ignore_packet;
+  }
+
+  /* copy dns payload inside static buffer for processing */
+  if (pbuf_copy_partial(p, &hdr, SIZEOF_DNS_HDR, 0) == SIZEOF_DNS_HDR) {
+    /* Match the ID in the DNS header with the name table. */
+    txid = lwip_htons(hdr.id);
+    for (i = 0; i < DNS_TABLE_SIZE; i++) {
+      struct dns_table_entry *entry = &dns_table[i];
+      if ((entry->state == DNS_STATE_ASKING) &&
+          (entry->txid == txid)) {
+
+        /* We only care about the question(s) and the answers. The authrr
+           and the extrarr are simply discarded. */
+        nquestions = lwip_htons(hdr.numquestions);
+        nanswers   = lwip_htons(hdr.numanswers);
+
+        /* Check for correct response. */
+        if ((hdr.flags1 & DNS_FLAG1_RESPONSE) == 0) {
+          LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": not a response\n", entry->name));
+          goto ignore_packet; /* ignore this packet */
+        }
+        if (nquestions != 1) {
+          LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response not match to query\n", entry->name));
+          goto ignore_packet; /* ignore this packet */
+        }
+
+#if LWIP_DNS_SUPPORT_MDNS_QUERIES
+        if (!entry->is_mdns)
+#endif /* LWIP_DNS_SUPPORT_MDNS_QUERIES */
+        {
+          /* Check whether response comes from the same network address to which the
+             question was sent. (RFC 5452) */
+          if (!ip_addr_eq(addr, &dns_servers[entry->server_idx])) {
+            goto ignore_packet; /* ignore this packet */
+          }
+        }
+
+        /* Check if the name in the "question" part match with the name in the entry and
+           skip it if equal. */
+        res_idx = dns_compare_name(entry->name, p, SIZEOF_DNS_HDR);
+        if (res_idx == 0xFFFF) {
+          LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response not match to query\n", entry->name));
+          goto ignore_packet; /* ignore this packet */
+        }
+
+        /* check if "question" part matches the request */
+        if (pbuf_copy_partial(p, &qry, SIZEOF_DNS_QUERY, res_idx) != SIZEOF_DNS_QUERY) {
+          goto ignore_packet; /* ignore this packet */
+        }
+        if ((qry.cls != PP_HTONS(DNS_RRCLASS_IN)) ||
+            (LWIP_DNS_ADDRTYPE_IS_IPV6(entry->reqaddrtype) && (qry.type != PP_HTONS(DNS_RRTYPE_AAAA))) ||
+            (!LWIP_DNS_ADDRTYPE_IS_IPV6(entry->reqaddrtype) && (qry.type != PP_HTONS(DNS_RRTYPE_A)))) {
+          LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response not match to query\n", entry->name));
+          goto ignore_packet; /* ignore this packet */
+        }
+        /* skip the rest of the "question" part */
+        if (res_idx + SIZEOF_DNS_QUERY > 0xFFFF) {
+          goto ignore_packet;
+        }
+        res_idx = (u16_t)(res_idx + SIZEOF_DNS_QUERY);
+
+        /* Check for error. If so, call callback to inform. */
+        if (hdr.flags2 & DNS_FLAG2_ERR_MASK) {
+          LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": error in flags\n", entry->name));
+
+          /* if there is another backup DNS server to try
+           * then don't stop the DNS request
+           */
+          if (dns_backupserver_available(entry)) {
+            /* avoid retrying the same server */
+            entry->retries = DNS_MAX_RETRIES-1;
+            entry->tmr     = 1;
+
+            /* contact next available server for this entry */
+            dns_check_entry(i);
+
+            goto ignore_packet;
+          }
+        } else {
+          while ((nanswers > 0) && (res_idx < p->tot_len)) {
+            /* skip answer resource record's host name */
+            res_idx = dns_skip_name(p, res_idx);
+            if (res_idx == 0xFFFF) {
+              goto ignore_packet; /* ignore this packet */
+            }
+
+            /* Check for IP address type and Internet class. Others are discarded. */
+            if (pbuf_copy_partial(p, &ans, SIZEOF_DNS_ANSWER, res_idx) != SIZEOF_DNS_ANSWER) {
+              goto ignore_packet; /* ignore this packet */
+            }
+            if (res_idx + SIZEOF_DNS_ANSWER > 0xFFFF) {
+              goto ignore_packet;
+            }
+            res_idx = (u16_t)(res_idx + SIZEOF_DNS_ANSWER);
+
+            if (ans.cls == PP_HTONS(DNS_RRCLASS_IN)) {
+#if LWIP_IPV4
+              if ((ans.type == PP_HTONS(DNS_RRTYPE_A)) && (ans.len == PP_HTONS(sizeof(ip4_addr_t)))) {
+#if LWIP_IPV4 && LWIP_IPV6
+                if (!LWIP_DNS_ADDRTYPE_IS_IPV6(entry->reqaddrtype))
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+                {
+                  ip4_addr_t ip4addr;
+                  /* read the IP address after answer resource record's header */
+                  if (pbuf_copy_partial(p, &ip4addr, sizeof(ip4_addr_t), res_idx) != sizeof(ip4_addr_t)) {
+                    goto ignore_packet; /* ignore this packet */
+                  }
+                  ip_addr_copy_from_ip4(dns_table[i].ipaddr, ip4addr);
+                  pbuf_free(p);
+                  /* handle correct response */
+                  dns_correct_response(i, lwip_ntohl(ans.ttl));
+                  return;
+                }
+              }
+#endif /* LWIP_IPV4 */
+#if LWIP_IPV6
+              if ((ans.type == PP_HTONS(DNS_RRTYPE_AAAA)) && (ans.len == PP_HTONS(sizeof(ip6_addr_p_t)))) {
+#if LWIP_IPV4 && LWIP_IPV6
+                if (LWIP_DNS_ADDRTYPE_IS_IPV6(entry->reqaddrtype))
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+                {
+                  ip6_addr_p_t ip6addr;
+                  /* read the IP address after answer resource record's header */
+                  if (pbuf_copy_partial(p, &ip6addr, sizeof(ip6_addr_p_t), res_idx) != sizeof(ip6_addr_p_t)) {
+                    goto ignore_packet; /* ignore this packet */
+                  }
+                  /* @todo: scope ip6addr? Might be required for link-local addresses at least? */
+                  ip_addr_copy_from_ip6_packed(dns_table[i].ipaddr, ip6addr);
+                  pbuf_free(p);
+                  /* handle correct response */
+                  dns_correct_response(i, lwip_ntohl(ans.ttl));
+                  return;
+                }
+              }
+#endif /* LWIP_IPV6 */
+            }
+            /* skip this answer */
+            if ((int)(res_idx + lwip_htons(ans.len)) > 0xFFFF) {
+              goto ignore_packet; /* ignore this packet */
+            }
+            res_idx = (u16_t)(res_idx + lwip_htons(ans.len));
+            --nanswers;
+          }
+#if LWIP_IPV4 && LWIP_IPV6
+          if ((entry->reqaddrtype == LWIP_DNS_ADDRTYPE_IPV4_IPV6) ||
+              (entry->reqaddrtype == LWIP_DNS_ADDRTYPE_IPV6_IPV4)) {
+            if (entry->reqaddrtype == LWIP_DNS_ADDRTYPE_IPV4_IPV6) {
+              /* IPv4 failed, try IPv6 */
+              dns_table[i].reqaddrtype = LWIP_DNS_ADDRTYPE_IPV6;
+            } else {
+              /* IPv6 failed, try IPv4 */
+              dns_table[i].reqaddrtype = LWIP_DNS_ADDRTYPE_IPV4;
+            }
+            pbuf_free(p);
+            dns_table[i].state = DNS_STATE_NEW;
+            dns_check_entry(i);
+            return;
+          }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+          LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": error in response\n", entry->name));
+        }
+        /* call callback to indicate error, clean up memory and return */
+        pbuf_free(p);
+        dns_call_found(i, NULL);
+        dns_table[i].state = DNS_STATE_UNUSED;
+        return;
+      }
+    }
+  }
+
+ignore_packet:
+  /* deallocate memory and return */
+  pbuf_free(p);
+  return;
+}
+
+/**
+ * Queues a new hostname to resolve and sends out a DNS query for that hostname
+ *
+ * @param name the hostname that is to be queried
+ * @param hostnamelen length of the hostname
+ * @param found a callback function to be called on success, failure or timeout
+ * @param callback_arg argument to pass to the callback function
+ * @return err_t return code.
+ */
+static err_t
+dns_enqueue(const char *name, size_t hostnamelen, dns_found_callback found,
+            void *callback_arg LWIP_DNS_ADDRTYPE_ARG(u8_t dns_addrtype) LWIP_DNS_ISMDNS_ARG(u8_t is_mdns))
+{
+  u8_t i;
+  u8_t lseq, lseqi;
+  struct dns_table_entry *entry = NULL;
+  size_t namelen;
+  struct dns_req_entry *req;
+#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING) != 0)
+  u8_t r;
+#endif
+
+  namelen = LWIP_MIN(hostnamelen, DNS_MAX_NAME_LENGTH - 1);
+
+#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING) != 0)
+  /* check for duplicate entries */
+  for (i = 0; i < DNS_TABLE_SIZE; i++) {
+    if ((dns_table[i].state == DNS_STATE_ASKING) &&
+        (lwip_strnicmp(name, dns_table[i].name, namelen) == 0) &&
+        !dns_table[i].name[namelen]) {
+#if LWIP_IPV4 && LWIP_IPV6
+      if (dns_table[i].reqaddrtype != dns_addrtype) {
+        /* requested address types don't match
+           this can lead to 2 concurrent requests, but mixing the address types
+           for the same host should not be that common */
+        continue;
+      }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+      /* this is a duplicate entry, find a free request entry */
+      for (r = 0; r < DNS_MAX_REQUESTS; r++) {
+        if (dns_requests[r].found == NULL) {
+          dns_requests[r].found = found;
+          dns_requests[r].arg = callback_arg;
+          dns_requests[r].dns_table_idx = i;
+          LWIP_DNS_SET_ADDRTYPE(dns_requests[r].reqaddrtype, dns_addrtype);
+          LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": duplicate request\n", name));
+          return ERR_INPROGRESS;
+        }
+      }
+    }
+  }
+  /* no duplicate entries found */
+#endif
+
+  /* search an unused entry, or the oldest one */
+  lseq = 0;
+  lseqi = DNS_TABLE_SIZE;
+  for (i = 0; i < DNS_TABLE_SIZE; ++i) {
+    entry = &dns_table[i];
+    /* is it an unused entry ? */
+    if (entry->state == DNS_STATE_UNUSED) {
+      break;
+    }
+    /* check if this is the oldest completed entry */
+    if (entry->state == DNS_STATE_DONE) {
+      u8_t age = (u8_t)(dns_seqno - entry->seqno);
+      if (age > lseq) {
+        lseq = age;
+        lseqi = i;
+      }
+    }
+  }
+
+  /* if we don't have found an unused entry, use the oldest completed one */
+  if (i == DNS_TABLE_SIZE) {
+    if ((lseqi >= DNS_TABLE_SIZE) || (dns_table[lseqi].state != DNS_STATE_DONE)) {
+      /* no entry can be used now, table is full */
+      LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": DNS entries table is full\n", name));
+      return ERR_MEM;
+    } else {
+      /* use the oldest completed one */
+      i = lseqi;
+      entry = &dns_table[i];
+    }
+  }
+
+#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING) != 0)
+  /* find a free request entry */
+  req = NULL;
+  for (r = 0; r < DNS_MAX_REQUESTS; r++) {
+    if (dns_requests[r].found == NULL) {
+      req = &dns_requests[r];
+      break;
+    }
+  }
+  if (req == NULL) {
+    /* no request entry can be used now, table is full */
+    LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": DNS request entries table is full\n", name));
+    return ERR_MEM;
+  }
+  req->dns_table_idx = i;
+#else
+  /* in this configuration, the entry index is the same as the request index */
+  req = &dns_requests[i];
+#endif
+
+  /* use this entry */
+  LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": use DNS entry %"U16_F"\n", name, (u16_t)(i)));
+
+  /* fill the entry */
+  entry->state = DNS_STATE_NEW;
+  entry->seqno = dns_seqno;
+  LWIP_DNS_SET_ADDRTYPE(entry->reqaddrtype, dns_addrtype);
+  LWIP_DNS_SET_ADDRTYPE(req->reqaddrtype, dns_addrtype);
+  req->found = found;
+  req->arg   = callback_arg;
+  MEMCPY(entry->name, name, namelen);
+  entry->name[namelen] = 0;
+
+#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0)
+  entry->pcb_idx = dns_alloc_pcb();
+  if (entry->pcb_idx >= DNS_MAX_SOURCE_PORTS) {
+    /* failed to get a UDP pcb */
+    LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": failed to allocate a pcb\n", name));
+    entry->state = DNS_STATE_UNUSED;
+    req->found = NULL;
+    return ERR_MEM;
+  }
+  LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": use DNS pcb %"U16_F"\n", name, (u16_t)(entry->pcb_idx)));
+#endif
+
+#if LWIP_DNS_SUPPORT_MDNS_QUERIES
+  entry->is_mdns = is_mdns;
+#endif
+
+  dns_seqno++;
+
+  /* force to send query without waiting timer */
+  dns_check_entry(i);
+
+  /* dns query is enqueued */
+  return ERR_INPROGRESS;
+}
+
+/**
+ * @ingroup dns
+ * Resolve a hostname (string) into an IP address.
+ * NON-BLOCKING callback version for use with raw API!!!
+ *
+ * Returns immediately with one of err_t return codes:
+ * - ERR_OK if hostname is a valid IP address string or the host
+ *   name is already in the local names table.
+ * - ERR_INPROGRESS enqueue a request to be sent to the DNS server
+ *   for resolution if no errors are present.
+ * - ERR_ARG: dns client not initialized or invalid hostname
+ *
+ * @param hostname the hostname that is to be queried
+ * @param addr pointer to a ip_addr_t where to store the address if it is already
+ *             cached in the dns_table (only valid if ERR_OK is returned!)
+ * @param found a callback function to be called on success, failure or timeout (only if
+ *              ERR_INPROGRESS is returned!)
+ * @param callback_arg argument to pass to the callback function
+ * @return a err_t return code.
+ */
+err_t
+dns_gethostbyname(const char *hostname, ip_addr_t *addr, dns_found_callback found,
+                  void *callback_arg)
+{
+  return dns_gethostbyname_addrtype(hostname, addr, found, callback_arg, LWIP_DNS_ADDRTYPE_DEFAULT);
+}
+
+/**
+ * @ingroup dns
+ * Like dns_gethostbyname, but returned address type can be controlled:
+ * @param hostname the hostname that is to be queried
+ * @param addr pointer to a ip_addr_t where to store the address if it is already
+ *             cached in the dns_table (only valid if ERR_OK is returned!)
+ * @param found a callback function to be called on success, failure or timeout (only if
+ *              ERR_INPROGRESS is returned!)
+ * @param callback_arg argument to pass to the callback function
+ * @param dns_addrtype - LWIP_DNS_ADDRTYPE_IPV4_IPV6: try to resolve IPv4 first, try IPv6 if IPv4 fails only
+ *                     - LWIP_DNS_ADDRTYPE_IPV6_IPV4: try to resolve IPv6 first, try IPv4 if IPv6 fails only
+ *                     - LWIP_DNS_ADDRTYPE_IPV4: try to resolve IPv4 only
+ *                     - LWIP_DNS_ADDRTYPE_IPV6: try to resolve IPv6 only
+ */
+err_t
+dns_gethostbyname_addrtype(const char *hostname, ip_addr_t *addr, dns_found_callback found,
+                           void *callback_arg, u8_t dns_addrtype)
+{
+  size_t hostnamelen;
+#if LWIP_DNS_SUPPORT_MDNS_QUERIES
+  u8_t is_mdns;
+#endif
+  /* not initialized or no valid server yet, or invalid addr pointer
+   * or invalid hostname or invalid hostname length */
+  if ((addr == NULL) ||
+      (!hostname) || (!hostname[0])) {
+    return ERR_ARG;
+  }
+#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) == 0)
+  if (dns_pcbs[0] == NULL) {
+    return ERR_ARG;
+  }
+#endif
+  hostnamelen = strlen(hostname);
+  if (hostname[hostnamelen - 1] == '.') {
+    hostnamelen--;
+  }
+  if (hostnamelen >= DNS_MAX_NAME_LENGTH) {
+    LWIP_DEBUGF(DNS_DEBUG, ("dns_gethostbyname: name too long to resolve\n"));
+    return ERR_ARG;
+  }
+
+
+#if LWIP_HAVE_LOOPIF
+  if (strcmp(hostname, "localhost") == 0) {
+    ip_addr_set_loopback(LWIP_DNS_ADDRTYPE_IS_IPV6(dns_addrtype), addr);
+    return ERR_OK;
+  }
+#endif /* LWIP_HAVE_LOOPIF */
+
+  /* host name already in octet notation? set ip addr and return ERR_OK */
+  if (ipaddr_aton(hostname, addr)) {
+#if LWIP_IPV4 && LWIP_IPV6
+    if ((IP_IS_V6(addr) && (dns_addrtype != LWIP_DNS_ADDRTYPE_IPV4)) ||
+        (IP_IS_V4(addr) && (dns_addrtype != LWIP_DNS_ADDRTYPE_IPV6)))
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+    {
+      return ERR_OK;
+    }
+  }
+  /* already have this address cached? */
+  if (dns_lookup(hostname, hostnamelen, addr LWIP_DNS_ADDRTYPE_ARG(dns_addrtype)) == ERR_OK) {
+    return ERR_OK;
+  }
+#if LWIP_IPV4 && LWIP_IPV6
+  if ((dns_addrtype == LWIP_DNS_ADDRTYPE_IPV4_IPV6) || (dns_addrtype == LWIP_DNS_ADDRTYPE_IPV6_IPV4)) {
+    /* fallback to 2nd IP type and try again to lookup */
+    u8_t fallback;
+    if (dns_addrtype == LWIP_DNS_ADDRTYPE_IPV4_IPV6) {
+      fallback = LWIP_DNS_ADDRTYPE_IPV6;
+    } else {
+      fallback = LWIP_DNS_ADDRTYPE_IPV4;
+    }
+    if (dns_lookup(hostname, hostnamelen, addr LWIP_DNS_ADDRTYPE_ARG(fallback)) == ERR_OK) {
+      return ERR_OK;
+    }
+  }
+#else /* LWIP_IPV4 && LWIP_IPV6 */
+  LWIP_UNUSED_ARG(dns_addrtype);
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
+#if LWIP_DNS_SUPPORT_MDNS_QUERIES
+  if (strstr(hostname, ".local") == &hostname[hostnamelen] - 6) {
+    is_mdns = 1;
+  } else {
+    is_mdns = 0;
+  }
+
+  if (!is_mdns)
+#endif /* LWIP_DNS_SUPPORT_MDNS_QUERIES */
+  {
+    /* prevent calling found callback if no server is set, return error instead */
+    if (ip_addr_isany_val(dns_servers[0])) {
+      return ERR_VAL;
+    }
+  }
+
+  /* queue query with specified callback */
+  return dns_enqueue(hostname, hostnamelen, found, callback_arg LWIP_DNS_ADDRTYPE_ARG(dns_addrtype)
+                     LWIP_DNS_ISMDNS_ARG(is_mdns));
+}
+
+#endif /* LWIP_DNS */
diff --git a/lib/lwip/core/inet_chksum.c b/lib/lwip/core/inet_chksum.c
new file mode 100644
index 0000000000..6a343ed730
--- /dev/null
+++ b/lib/lwip/core/inet_chksum.c
@@ -0,0 +1,608 @@
+/**
+ * @file
+ * Internet checksum functions.
+ *
+ * These are some reference implementations of the checksum algorithm, with the
+ * aim of being simple, correct and fully portable. Checksumming is the
+ * first thing you would want to optimize for your platform. If you create
+ * your own version, link it in and in your cc.h put:
+ *
+ * \#define LWIP_CHKSUM your_checksum_routine
+ *
+ * Or you can select from the implementations below by defining
+ * LWIP_CHKSUM_ALGORITHM to 1, 2 or 3.
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#include "lwip/inet_chksum.h"
+#include "lwip/def.h"
+#include "lwip/ip_addr.h"
+
+#include <string.h>
+
+#ifndef LWIP_CHKSUM
+# define LWIP_CHKSUM lwip_standard_chksum
+# ifndef LWIP_CHKSUM_ALGORITHM
+#  define LWIP_CHKSUM_ALGORITHM 2
+# endif
+u16_t lwip_standard_chksum(const void *dataptr, int len);
+#endif
+/* If none set: */
+#ifndef LWIP_CHKSUM_ALGORITHM
+# define LWIP_CHKSUM_ALGORITHM 0
+#endif
+
+#if (LWIP_CHKSUM_ALGORITHM == 1) /* Version #1 */
+/**
+ * lwip checksum
+ *
+ * @param dataptr points to start of data to be summed at any boundary
+ * @param len length of data to be summed
+ * @return host order (!) lwip checksum (non-inverted Internet sum)
+ *
+ * @note accumulator size limits summable length to 64k
+ * @note host endianness is irrelevant (p3 RFC1071)
+ */
+u16_t
+lwip_standard_chksum(const void *dataptr, int len)
+{
+  u32_t acc;
+  u16_t src;
+  const u8_t *octetptr;
+
+  acc = 0;
+  /* dataptr may be at odd or even addresses */
+  octetptr = (const u8_t *)dataptr;
+  while (len > 1) {
+    /* declare first octet as most significant
+       thus assume network order, ignoring host order */
+    src = (*octetptr) << 8;
+    octetptr++;
+    /* declare second octet as least significant */
+    src |= (*octetptr);
+    octetptr++;
+    acc += src;
+    len -= 2;
+  }
+  if (len > 0) {
+    /* accumulate remaining octet */
+    src = (*octetptr) << 8;
+    acc += src;
+  }
+  /* add deferred carry bits */
+  acc = (acc >> 16) + (acc & 0x0000ffffUL);
+  if ((acc & 0xffff0000UL) != 0) {
+    acc = (acc >> 16) + (acc & 0x0000ffffUL);
+  }
+  /* This maybe a little confusing: reorder sum using lwip_htons()
+     instead of lwip_ntohs() since it has a little less call overhead.
+     The caller must invert bits for Internet sum ! */
+  return lwip_htons((u16_t)acc);
+}
+#endif
+
+#if (LWIP_CHKSUM_ALGORITHM == 2) /* Alternative version #2 */
+/*
+ * Curt McDowell
+ * Broadcom Corp.
+ * csm@broadcom.com
+ *
+ * IP checksum two bytes at a time with support for
+ * unaligned buffer.
+ * Works for len up to and including 0x20000.
+ * by Curt McDowell, Broadcom Corp. 12/08/2005
+ *
+ * @param dataptr points to start of data to be summed at any boundary
+ * @param len length of data to be summed
+ * @return host order (!) lwip checksum (non-inverted Internet sum)
+ */
+u16_t
+lwip_standard_chksum(const void *dataptr, int len)
+{
+  const u8_t *pb = (const u8_t *)dataptr;
+  const u16_t *ps;
+  u16_t t = 0;
+  u32_t sum = 0;
+  int odd = ((mem_ptr_t)pb & 1);
+
+  /* Get aligned to u16_t */
+  if (odd && len > 0) {
+    ((u8_t *)&t)[1] = *pb++;
+    len--;
+  }
+
+  /* Add the bulk of the data */
+  ps = (const u16_t *)(const void *)pb;
+  while (len > 1) {
+    sum += *ps++;
+    len -= 2;
+  }
+
+  /* Consume left-over byte, if any */
+  if (len > 0) {
+    ((u8_t *)&t)[0] = *(const u8_t *)ps;
+  }
+
+  /* Add end bytes */
+  sum += t;
+
+  /* Fold 32-bit sum to 16 bits
+     calling this twice is probably faster than if statements... */
+  sum = FOLD_U32T(sum);
+  sum = FOLD_U32T(sum);
+
+  /* Swap if alignment was odd */
+  if (odd) {
+    sum = SWAP_BYTES_IN_WORD(sum);
+  }
+
+  return (u16_t)sum;
+}
+#endif
+
+#if (LWIP_CHKSUM_ALGORITHM == 3) /* Alternative version #3 */
+/**
+ * An optimized checksum routine. Basically, it uses loop-unrolling on
+ * the checksum loop, treating the head and tail bytes specially, whereas
+ * the inner loop acts on 8 bytes at a time.
+ *
+ * @arg start of buffer to be checksummed. May be an odd byte address.
+ * @len number of bytes in the buffer to be checksummed.
+ * @return host order (!) lwip checksum (non-inverted Internet sum)
+ *
+ * by Curt McDowell, Broadcom Corp. December 8th, 2005
+ */
+u16_t
+lwip_standard_chksum(const void *dataptr, int len)
+{
+  const u8_t *pb = (const u8_t *)dataptr;
+  const u16_t *ps;
+  u16_t t = 0;
+  const u32_t *pl;
+  u32_t sum = 0, tmp;
+  /* starts at odd byte address? */
+  int odd = ((mem_ptr_t)pb & 1);
+
+  if (odd && len > 0) {
+    ((u8_t *)&t)[1] = *pb++;
+    len--;
+  }
+
+  ps = (const u16_t *)(const void *)pb;
+
+  if (((mem_ptr_t)ps & 3) && len > 1) {
+    sum += *ps++;
+    len -= 2;
+  }
+
+  pl = (const u32_t *)(const void *)ps;
+
+  while (len > 7)  {
+    tmp = sum + *pl++;          /* ping */
+    if (tmp < sum) {
+      tmp++;                    /* add back carry */
+    }
+
+    sum = tmp + *pl++;          /* pong */
+    if (sum < tmp) {
+      sum++;                    /* add back carry */
+    }
+
+    len -= 8;
+  }
+
+  /* make room in upper bits */
+  sum = FOLD_U32T(sum);
+
+  ps = (const u16_t *)pl;
+
+  /* 16-bit aligned word remaining? */
+  while (len > 1) {
+    sum += *ps++;
+    len -= 2;
+  }
+
+  /* dangling tail byte remaining? */
+  if (len > 0) {                /* include odd byte */
+    ((u8_t *)&t)[0] = *(const u8_t *)ps;
+  }
+
+  sum += t;                     /* add end bytes */
+
+  /* Fold 32-bit sum to 16 bits
+     calling this twice is probably faster than if statements... */
+  sum = FOLD_U32T(sum);
+  sum = FOLD_U32T(sum);
+
+  if (odd) {
+    sum = SWAP_BYTES_IN_WORD(sum);
+  }
+
+  return (u16_t)sum;
+}
+#endif
+
+/** Parts of the pseudo checksum which are common to IPv4 and IPv6 */
+static u16_t
+inet_cksum_pseudo_base(struct pbuf *p, u8_t proto, u16_t proto_len, u32_t acc)
+{
+  struct pbuf *q;
+  int swapped = 0;
+
+  /* iterate through all pbuf in chain */
+  for (q = p; q != NULL; q = q->next) {
+    LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): checksumming pbuf %p (has next %p) \n",
+                             (void *)q, (void *)q->next));
+    acc += LWIP_CHKSUM(q->payload, q->len);
+    /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): unwrapped lwip_chksum()=%"X32_F" \n", acc));*/
+    /* just executing this next line is probably faster that the if statement needed
+       to check whether we really need to execute it, and does no harm */
+    acc = FOLD_U32T(acc);
+    if (q->len % 2 != 0) {
+      swapped = !swapped;
+      acc = SWAP_BYTES_IN_WORD(acc);
+    }
+    /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): wrapped lwip_chksum()=%"X32_F" \n", acc));*/
+  }
+
+  if (swapped) {
+    acc = SWAP_BYTES_IN_WORD(acc);
+  }
+
+  acc += (u32_t)lwip_htons((u16_t)proto);
+  acc += (u32_t)lwip_htons(proto_len);
+
+  /* Fold 32-bit sum to 16 bits
+     calling this twice is probably faster than if statements... */
+  acc = FOLD_U32T(acc);
+  acc = FOLD_U32T(acc);
+  LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): pbuf chain lwip_chksum()=%"X32_F"\n", acc));
+  return (u16_t)~(acc & 0xffffUL);
+}
+
+#if LWIP_IPV4
+/* inet_chksum_pseudo:
+ *
+ * Calculates the IPv4 pseudo Internet checksum used by TCP and UDP for a pbuf chain.
+ * IP addresses are expected to be in network byte order.
+ *
+ * @param p chain of pbufs over that a checksum should be calculated (ip data part)
+ * @param src source ip address (used for checksum of pseudo header)
+ * @param dst destination ip address (used for checksum of pseudo header)
+ * @param proto ip protocol (used for checksum of pseudo header)
+ * @param proto_len length of the ip data part (used for checksum of pseudo header)
+ * @return checksum (as u16_t) to be saved directly in the protocol header
+ */
+u16_t
+inet_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len,
+                   const ip4_addr_t *src, const ip4_addr_t *dest)
+{
+  u32_t acc;
+  u32_t addr;
+
+  addr = ip4_addr_get_u32(src);
+  acc = (addr & 0xffffUL);
+  acc = (u32_t)(acc + ((addr >> 16) & 0xffffUL));
+  addr = ip4_addr_get_u32(dest);
+  acc = (u32_t)(acc + (addr & 0xffffUL));
+  acc = (u32_t)(acc + ((addr >> 16) & 0xffffUL));
+  /* fold down to 16 bits */
+  acc = FOLD_U32T(acc);
+  acc = FOLD_U32T(acc);
+
+  return inet_cksum_pseudo_base(p, proto, proto_len, acc);
+}
+#endif /* LWIP_IPV4 */
+
+#if LWIP_IPV6
+/**
+ * Calculates the checksum with IPv6 pseudo header used by TCP and UDP for a pbuf chain.
+ * IPv6 addresses are expected to be in network byte order.
+ *
+ * @param p chain of pbufs over that a checksum should be calculated (ip data part)
+ * @param proto ipv6 protocol/next header (used for checksum of pseudo header)
+ * @param proto_len length of the ipv6 payload (used for checksum of pseudo header)
+ * @param src source ipv6 address (used for checksum of pseudo header)
+ * @param dest destination ipv6 address (used for checksum of pseudo header)
+ * @return checksum (as u16_t) to be saved directly in the protocol header
+ */
+u16_t
+ip6_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len,
+                  const ip6_addr_t *src, const ip6_addr_t *dest)
+{
+  u32_t acc = 0;
+  u32_t addr;
+  u8_t addr_part;
+
+  for (addr_part = 0; addr_part < 4; addr_part++) {
+    addr = src->addr[addr_part];
+    acc = (u32_t)(acc + (addr & 0xffffUL));
+    acc = (u32_t)(acc + ((addr >> 16) & 0xffffUL));
+    addr = dest->addr[addr_part];
+    acc = (u32_t)(acc + (addr & 0xffffUL));
+    acc = (u32_t)(acc + ((addr >> 16) & 0xffffUL));
+  }
+  /* fold down to 16 bits */
+  acc = FOLD_U32T(acc);
+  acc = FOLD_U32T(acc);
+
+  return inet_cksum_pseudo_base(p, proto, proto_len, acc);
+}
+#endif /* LWIP_IPV6 */
+
+/* ip_chksum_pseudo:
+ *
+ * Calculates the IPv4 or IPv6 pseudo Internet checksum used by TCP and UDP for a pbuf chain.
+ * IP addresses are expected to be in network byte order.
+ *
+ * @param p chain of pbufs over that a checksum should be calculated (ip data part)
+ * @param src source ip address (used for checksum of pseudo header)
+ * @param dst destination ip address (used for checksum of pseudo header)
+ * @param proto ip protocol (used for checksum of pseudo header)
+ * @param proto_len length of the ip data part (used for checksum of pseudo header)
+ * @return checksum (as u16_t) to be saved directly in the protocol header
+ */
+u16_t
+ip_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len,
+                 const ip_addr_t *src, const ip_addr_t *dest)
+{
+#if LWIP_IPV6
+  if (IP_IS_V6(dest)) {
+    return ip6_chksum_pseudo(p, proto, proto_len, ip_2_ip6(src), ip_2_ip6(dest));
+  }
+#endif /* LWIP_IPV6 */
+#if LWIP_IPV4 && LWIP_IPV6
+  else
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+#if LWIP_IPV4
+  {
+    return inet_chksum_pseudo(p, proto, proto_len, ip_2_ip4(src), ip_2_ip4(dest));
+  }
+#endif /* LWIP_IPV4 */
+}
+
+/** Parts of the pseudo checksum which are common to IPv4 and IPv6 */
+static u16_t
+inet_cksum_pseudo_partial_base(struct pbuf *p, u8_t proto, u16_t proto_len,
+                               u16_t chksum_len, u32_t acc)
+{
+  struct pbuf *q;
+  int swapped = 0;
+  u16_t chklen;
+
+  /* iterate through all pbuf in chain */
+  for (q = p; (q != NULL) && (chksum_len > 0); q = q->next) {
+    LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): checksumming pbuf %p (has next %p) \n",
+                             (void *)q, (void *)q->next));
+    chklen = q->len;
+    if (chklen > chksum_len) {
+      chklen = chksum_len;
+    }
+    acc += LWIP_CHKSUM(q->payload, chklen);
+    chksum_len = (u16_t)(chksum_len - chklen);
+    LWIP_ASSERT("delete me", chksum_len < 0x7fff);
+    /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): unwrapped lwip_chksum()=%"X32_F" \n", acc));*/
+    /* fold the upper bit down */
+    acc = FOLD_U32T(acc);
+    if (q->len % 2 != 0) {
+      swapped = !swapped;
+      acc = SWAP_BYTES_IN_WORD(acc);
+    }
+    /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): wrapped lwip_chksum()=%"X32_F" \n", acc));*/
+  }
+
+  if (swapped) {
+    acc = SWAP_BYTES_IN_WORD(acc);
+  }
+
+  acc += (u32_t)lwip_htons((u16_t)proto);
+  acc += (u32_t)lwip_htons(proto_len);
+
+  /* Fold 32-bit sum to 16 bits
+     calling this twice is probably faster than if statements... */
+  acc = FOLD_U32T(acc);
+  acc = FOLD_U32T(acc);
+  LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): pbuf chain lwip_chksum()=%"X32_F"\n", acc));
+  return (u16_t)~(acc & 0xffffUL);
+}
+
+#if LWIP_IPV4
+/* inet_chksum_pseudo_partial:
+ *
+ * Calculates the IPv4 pseudo Internet checksum used by TCP and UDP for a pbuf chain.
+ * IP addresses are expected to be in network byte order.
+ *
+ * @param p chain of pbufs over that a checksum should be calculated (ip data part)
+ * @param src source ip address (used for checksum of pseudo header)
+ * @param dst destination ip address (used for checksum of pseudo header)
+ * @param proto ip protocol (used for checksum of pseudo header)
+ * @param proto_len length of the ip data part (used for checksum of pseudo header)
+ * @return checksum (as u16_t) to be saved directly in the protocol header
+ */
+u16_t
+inet_chksum_pseudo_partial(struct pbuf *p, u8_t proto, u16_t proto_len,
+                           u16_t chksum_len, const ip4_addr_t *src, const ip4_addr_t *dest)
+{
+  u32_t acc;
+  u32_t addr;
+
+  addr = ip4_addr_get_u32(src);
+  acc = (addr & 0xffffUL);
+  acc = (u32_t)(acc + ((addr >> 16) & 0xffffUL));
+  addr = ip4_addr_get_u32(dest);
+  acc = (u32_t)(acc + (addr & 0xffffUL));
+  acc = (u32_t)(acc + ((addr >> 16) & 0xffffUL));
+  /* fold down to 16 bits */
+  acc = FOLD_U32T(acc);
+  acc = FOLD_U32T(acc);
+
+  return inet_cksum_pseudo_partial_base(p, proto, proto_len, chksum_len, acc);
+}
+#endif /* LWIP_IPV4 */
+
+#if LWIP_IPV6
+/**
+ * Calculates the checksum with IPv6 pseudo header used by TCP and UDP for a pbuf chain.
+ * IPv6 addresses are expected to be in network byte order. Will only compute for a
+ * portion of the payload.
+ *
+ * @param p chain of pbufs over that a checksum should be calculated (ip data part)
+ * @param proto ipv6 protocol/next header (used for checksum of pseudo header)
+ * @param proto_len length of the ipv6 payload (used for checksum of pseudo header)
+ * @param chksum_len number of payload bytes used to compute chksum
+ * @param src source ipv6 address (used for checksum of pseudo header)
+ * @param dest destination ipv6 address (used for checksum of pseudo header)
+ * @return checksum (as u16_t) to be saved directly in the protocol header
+ */
+u16_t
+ip6_chksum_pseudo_partial(struct pbuf *p, u8_t proto, u16_t proto_len,
+                          u16_t chksum_len, const ip6_addr_t *src, const ip6_addr_t *dest)
+{
+  u32_t acc = 0;
+  u32_t addr;
+  u8_t addr_part;
+
+  for (addr_part = 0; addr_part < 4; addr_part++) {
+    addr = src->addr[addr_part];
+    acc = (u32_t)(acc + (addr & 0xffffUL));
+    acc = (u32_t)(acc + ((addr >> 16) & 0xffffUL));
+    addr = dest->addr[addr_part];
+    acc = (u32_t)(acc + (addr & 0xffffUL));
+    acc = (u32_t)(acc + ((addr >> 16) & 0xffffUL));
+  }
+  /* fold down to 16 bits */
+  acc = FOLD_U32T(acc);
+  acc = FOLD_U32T(acc);
+
+  return inet_cksum_pseudo_partial_base(p, proto, proto_len, chksum_len, acc);
+}
+#endif /* LWIP_IPV6 */
+
+/* ip_chksum_pseudo_partial:
+ *
+ * Calculates the IPv4 or IPv6 pseudo Internet checksum used by TCP and UDP for a pbuf chain.
+ *
+ * @param p chain of pbufs over that a checksum should be calculated (ip data part)
+ * @param src source ip address (used for checksum of pseudo header)
+ * @param dst destination ip address (used for checksum of pseudo header)
+ * @param proto ip protocol (used for checksum of pseudo header)
+ * @param proto_len length of the ip data part (used for checksum of pseudo header)
+ * @return checksum (as u16_t) to be saved directly in the protocol header
+ */
+u16_t
+ip_chksum_pseudo_partial(struct pbuf *p, u8_t proto, u16_t proto_len,
+                         u16_t chksum_len, const ip_addr_t *src, const ip_addr_t *dest)
+{
+#if LWIP_IPV6
+  if (IP_IS_V6(dest)) {
+    return ip6_chksum_pseudo_partial(p, proto, proto_len, chksum_len, ip_2_ip6(src), ip_2_ip6(dest));
+  }
+#endif /* LWIP_IPV6 */
+#if LWIP_IPV4 && LWIP_IPV6
+  else
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+#if LWIP_IPV4
+  {
+    return inet_chksum_pseudo_partial(p, proto, proto_len, chksum_len, ip_2_ip4(src), ip_2_ip4(dest));
+  }
+#endif /* LWIP_IPV4 */
+}
+
+/* inet_chksum:
+ *
+ * Calculates the Internet checksum over a portion of memory. Used primarily for IP
+ * and ICMP.
+ *
+ * @param dataptr start of the buffer to calculate the checksum (no alignment needed)
+ * @param len length of the buffer to calculate the checksum
+ * @return checksum (as u16_t) to be saved directly in the protocol header
+ */
+
+u16_t
+inet_chksum(const void *dataptr, u16_t len)
+{
+  return (u16_t)~(unsigned int)LWIP_CHKSUM(dataptr, len);
+}
+
+/**
+ * Calculate a checksum over a chain of pbufs (without pseudo-header, much like
+ * inet_chksum only pbufs are used).
+ *
+ * @param p pbuf chain over that the checksum should be calculated
+ * @return checksum (as u16_t) to be saved directly in the protocol header
+ */
+u16_t
+inet_chksum_pbuf(struct pbuf *p)
+{
+  u32_t acc;
+  struct pbuf *q;
+  int swapped = 0;
+
+  acc = 0;
+  for (q = p; q != NULL; q = q->next) {
+    acc += LWIP_CHKSUM(q->payload, q->len);
+    acc = FOLD_U32T(acc);
+    if (q->len % 2 != 0) {
+      swapped = !swapped;
+      acc = SWAP_BYTES_IN_WORD(acc);
+    }
+  }
+
+  if (swapped) {
+    acc = SWAP_BYTES_IN_WORD(acc);
+  }
+  return (u16_t)~(acc & 0xffffUL);
+}
+
+/* These are some implementations for LWIP_CHKSUM_COPY, which copies data
+ * like MEMCPY but generates a checksum at the same time. Since this is a
+ * performance-sensitive function, you might want to create your own version
+ * in assembly targeted at your hardware by defining it in lwipopts.h:
+ *   #define LWIP_CHKSUM_COPY(dst, src, len) your_chksum_copy(dst, src, len)
+ */
+
+#if (LWIP_CHKSUM_COPY_ALGORITHM == 1) /* Version #1 */
+/** Safe but slow: first call MEMCPY, then call LWIP_CHKSUM.
+ * For architectures with big caches, data might still be in cache when
+ * generating the checksum after copying.
+ */
+u16_t
+lwip_chksum_copy(void *dst, const void *src, u16_t len)
+{
+  MEMCPY(dst, src, len);
+  return LWIP_CHKSUM(dst, len);
+}
+#endif /* (LWIP_CHKSUM_COPY_ALGORITHM == 1) */
diff --git a/lib/lwip/core/init.c b/lib/lwip/core/init.c
new file mode 100644
index 0000000000..0013a8998e
--- /dev/null
+++ b/lib/lwip/core/init.c
@@ -0,0 +1,387 @@
+/**
+ * @file
+ * Modules initialization
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ */
+
+#include "lwip/opt.h"
+
+#include "lwip/init.h"
+#include "lwip/stats.h"
+#include "lwip/sys.h"
+#include "lwip/mem.h"
+#include "lwip/memp.h"
+#include "lwip/pbuf.h"
+#include "lwip/netif.h"
+#include "lwip/sockets.h"
+#include "lwip/ip.h"
+#include "lwip/raw.h"
+#include "lwip/udp.h"
+#include "lwip/priv/tcp_priv.h"
+#include "lwip/igmp.h"
+#include "lwip/dns.h"
+#include "lwip/timeouts.h"
+#include "lwip/etharp.h"
+#include "lwip/ip6.h"
+#include "lwip/nd6.h"
+#include "lwip/mld6.h"
+#include "lwip/api.h"
+
+#include "netif/ppp/ppp_opts.h"
+#include "netif/ppp/ppp_impl.h"
+
+#ifndef LWIP_SKIP_PACKING_CHECK
+
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct packed_struct_test {
+  PACK_STRUCT_FLD_8(u8_t  dummy1);
+  PACK_STRUCT_FIELD(u32_t dummy2);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
+#define PACKED_STRUCT_TEST_EXPECTED_SIZE 5
+
+#endif
+
+/* Compile-time sanity checks for configuration errors.
+ * These can be done independently of LWIP_DEBUG, without penalty.
+ */
+#ifndef BYTE_ORDER
+#error "BYTE_ORDER is not defined, you have to define it in your cc.h"
+#endif
+#if (!IP_SOF_BROADCAST && IP_SOF_BROADCAST_RECV)
+#error "If you want to use broadcast filter per pcb on recv operations, you have to define IP_SOF_BROADCAST=1 in your lwipopts.h"
+#endif
+#if (!LWIP_UDP && LWIP_UDPLITE)
+#error "If you want to use UDP Lite, you have to define LWIP_UDP=1 in your lwipopts.h"
+#endif
+#if (!LWIP_UDP && LWIP_DHCP)
+#error "If you want to use DHCP, you have to define LWIP_UDP=1 in your lwipopts.h"
+#endif
+#if (!LWIP_UDP && !LWIP_RAW && LWIP_MULTICAST_TX_OPTIONS)
+#error "If you want to use LWIP_MULTICAST_TX_OPTIONS, you have to define LWIP_UDP=1 and/or LWIP_RAW=1 in your lwipopts.h"
+#endif
+#if (!LWIP_UDP && LWIP_DNS)
+#error "If you want to use DNS, you have to define LWIP_UDP=1 in your lwipopts.h"
+#endif
+#if !MEMP_MEM_MALLOC /* MEMP_NUM_* checks are disabled when not using the pool allocator */
+#if (LWIP_ARP && ARP_QUEUEING && (MEMP_NUM_ARP_QUEUE<=0))
+#error "If you want to use ARP Queueing, you have to define MEMP_NUM_ARP_QUEUE>=1 in your lwipopts.h"
+#endif
+#if (LWIP_RAW && (MEMP_NUM_RAW_PCB<=0))
+#error "If you want to use RAW, you have to define MEMP_NUM_RAW_PCB>=1 in your lwipopts.h"
+#endif
+#if (LWIP_UDP && (MEMP_NUM_UDP_PCB<=0))
+#error "If you want to use UDP, you have to define MEMP_NUM_UDP_PCB>=1 in your lwipopts.h"
+#endif
+#if (LWIP_TCP && (MEMP_NUM_TCP_PCB<=0))
+#error "If you want to use TCP, you have to define MEMP_NUM_TCP_PCB>=1 in your lwipopts.h"
+#endif
+#if (LWIP_IGMP && (MEMP_NUM_IGMP_GROUP<=1))
+#error "If you want to use IGMP, you have to define MEMP_NUM_IGMP_GROUP>1 in your lwipopts.h"
+#endif
+#if (LWIP_IGMP && !LWIP_MULTICAST_TX_OPTIONS)
+#error "If you want to use IGMP, you have to define LWIP_MULTICAST_TX_OPTIONS==1 in your lwipopts.h"
+#endif
+#if (LWIP_IGMP && !LWIP_IPV4)
+#error "IGMP needs LWIP_IPV4 enabled in your lwipopts.h"
+#endif
+#if ((LWIP_NETCONN || LWIP_SOCKET) && (MEMP_NUM_TCPIP_MSG_API<=0))
+#error "If you want to use Sequential API, you have to define MEMP_NUM_TCPIP_MSG_API>=1 in your lwipopts.h"
+#endif
+/* There must be sufficient timeouts, taking into account requirements of the subsystems. */
+#if LWIP_TIMERS && (MEMP_NUM_SYS_TIMEOUT < LWIP_NUM_SYS_TIMEOUT_INTERNAL)
+#error "MEMP_NUM_SYS_TIMEOUT is too low to accommodate all required timeouts"
+#endif
+#if (IP_REASSEMBLY && (MEMP_NUM_REASSDATA > IP_REASS_MAX_PBUFS))
+#error "MEMP_NUM_REASSDATA > IP_REASS_MAX_PBUFS doesn't make sense since each struct ip_reassdata must hold 2 pbufs at least!"
+#endif
+#endif /* !MEMP_MEM_MALLOC */
+#if LWIP_WND_SCALE
+#if (LWIP_TCP && (TCP_WND > 0xffffffff))
+#error "If you want to use TCP, TCP_WND must fit in an u32_t, so, you have to reduce it in your lwipopts.h"
+#endif
+#if (LWIP_TCP && (TCP_RCV_SCALE > 14))
+#error "The maximum valid window scale value is 14!"
+#endif
+#if (LWIP_TCP && (TCP_WND > (0xFFFFU << TCP_RCV_SCALE)))
+#error "TCP_WND is bigger than the configured LWIP_WND_SCALE allows!"
+#endif
+#if (LWIP_TCP && ((TCP_WND >> TCP_RCV_SCALE) == 0))
+#error "TCP_WND is too small for the configured LWIP_WND_SCALE (results in zero window)!"
+#endif
+#else /* LWIP_WND_SCALE */
+#if (LWIP_TCP && (TCP_WND > 0xffff))
+#error "If you want to use TCP, TCP_WND must fit in an u16_t, so, you have to reduce it in your lwipopts.h (or enable window scaling)"
+#endif
+#endif /* LWIP_WND_SCALE */
+#if (LWIP_TCP && (TCP_SND_QUEUELEN > 0xffff))
+#error "If you want to use TCP, TCP_SND_QUEUELEN must fit in an u16_t, so, you have to reduce it in your lwipopts.h"
+#endif
+#if (LWIP_TCP && (TCP_SND_QUEUELEN < 2))
+#error "TCP_SND_QUEUELEN must be at least 2 for no-copy TCP writes to work"
+#endif
+#if (LWIP_TCP && ((TCP_MAXRTX > 12) || (TCP_SYNMAXRTX > 12)))
+#error "If you want to use TCP, TCP_MAXRTX and TCP_SYNMAXRTX must less or equal to 12 (due to tcp_backoff table), so, you have to reduce them in your lwipopts.h"
+#endif
+#if (LWIP_TCP && TCP_LISTEN_BACKLOG && ((TCP_DEFAULT_LISTEN_BACKLOG < 0) || (TCP_DEFAULT_LISTEN_BACKLOG > 0xff)))
+#error "If you want to use TCP backlog, TCP_DEFAULT_LISTEN_BACKLOG must fit into an u8_t"
+#endif
+#if (LWIP_TCP && LWIP_TCP_SACK_OUT && !TCP_QUEUE_OOSEQ)
+#error "To use LWIP_TCP_SACK_OUT, TCP_QUEUE_OOSEQ needs to be enabled"
+#endif
+#if (LWIP_TCP && LWIP_TCP_SACK_OUT && (LWIP_TCP_MAX_SACK_NUM < 1))
+#error "LWIP_TCP_MAX_SACK_NUM must be greater than 0"
+#endif
+#if (LWIP_NETIF_API && (NO_SYS==1))
+#error "If you want to use NETIF API, you have to define NO_SYS=0 in your lwipopts.h"
+#endif
+#if ((LWIP_SOCKET || LWIP_NETCONN) && (NO_SYS==1))
+#error "If you want to use Sequential API, you have to define NO_SYS=0 in your lwipopts.h"
+#endif
+#if (LWIP_PPP_API && (NO_SYS==1))
+#error "If you want to use PPP API, you have to define NO_SYS=0 in your lwipopts.h"
+#endif
+#if (LWIP_PPP_API && (PPP_SUPPORT==0))
+#error "If you want to use PPP API, you have to enable PPP_SUPPORT in your lwipopts.h"
+#endif
+#if (((!LWIP_DHCP) || (!LWIP_AUTOIP)) && LWIP_DHCP_AUTOIP_COOP)
+#error "If you want to use DHCP/AUTOIP cooperation mode, you have to define LWIP_DHCP=1 and LWIP_AUTOIP=1 in your lwipopts.h"
+#endif
+#if (((!LWIP_DHCP) || (!LWIP_ARP) || (!LWIP_ACD)) && LWIP_DHCP_DOES_ACD_CHECK)
+#error "If you want to use DHCP ACD checking, you have to define LWIP_DHCP=1, LWIP_ARP=1 and LWIP_ACD=1 in your lwipopts.h"
+#endif
+#if (!LWIP_ARP && LWIP_AUTOIP)
+#error "If you want to use AUTOIP, you have to define LWIP_ARP=1 in your lwipopts.h"
+#endif
+#if (LWIP_TCP && ((LWIP_EVENT_API && LWIP_CALLBACK_API) || (!LWIP_EVENT_API && !LWIP_CALLBACK_API)))
+#error "One and exactly one of LWIP_EVENT_API and LWIP_CALLBACK_API has to be enabled in your lwipopts.h"
+#endif
+#if (LWIP_ALTCP && LWIP_EVENT_API)
+#error "The application layered tcp API does not work with LWIP_EVENT_API"
+#endif
+#if (MEM_LIBC_MALLOC && MEM_USE_POOLS)
+#error "MEM_LIBC_MALLOC and MEM_USE_POOLS may not both be simultaneously enabled in your lwipopts.h"
+#endif
+#if (MEM_USE_POOLS && !MEMP_USE_CUSTOM_POOLS)
+#error "MEM_USE_POOLS requires custom pools (MEMP_USE_CUSTOM_POOLS) to be enabled in your lwipopts.h"
+#endif
+#if (PBUF_POOL_BUFSIZE <= MEM_ALIGNMENT)
+#error "PBUF_POOL_BUFSIZE must be greater than MEM_ALIGNMENT or the offset may take the full first pbuf"
+#endif
+#if (DNS_LOCAL_HOSTLIST && !DNS_LOCAL_HOSTLIST_IS_DYNAMIC && !(defined(DNS_LOCAL_HOSTLIST_INIT)))
+#error "you have to define define DNS_LOCAL_HOSTLIST_INIT {{'host1', 0x123}, {'host2', 0x234}} to initialize DNS_LOCAL_HOSTLIST"
+#endif
+#if PPP_SUPPORT && !PPPOS_SUPPORT && !PPPOE_SUPPORT && !PPPOL2TP_SUPPORT
+#error "PPP_SUPPORT needs at least one of PPPOS_SUPPORT, PPPOE_SUPPORT or PPPOL2TP_SUPPORT turned on"
+#endif
+#if PPP_SUPPORT && !PPP_IPV4_SUPPORT && !PPP_IPV6_SUPPORT
+#error "PPP_SUPPORT needs PPP_IPV4_SUPPORT and/or PPP_IPV6_SUPPORT turned on"
+#endif
+#if PPP_SUPPORT && PPP_IPV4_SUPPORT && !LWIP_IPV4
+#error "PPP_IPV4_SUPPORT needs LWIP_IPV4 turned on"
+#endif
+#if PPP_SUPPORT && PPP_IPV6_SUPPORT && !LWIP_IPV6
+#error "PPP_IPV6_SUPPORT needs LWIP_IPV6 turned on"
+#endif
+#if PPP_SUPPORT && CCP_SUPPORT && !MPPE_SUPPORT
+#error "CCP_SUPPORT needs MPPE_SUPPORT turned on"
+#endif
+#if !LWIP_ETHERNET && (LWIP_ARP || PPPOE_SUPPORT)
+#error "LWIP_ETHERNET needs to be turned on for LWIP_ARP or PPPOE_SUPPORT"
+#endif
+#if LWIP_TCPIP_CORE_LOCKING_INPUT && !LWIP_TCPIP_CORE_LOCKING
+#error "When using LWIP_TCPIP_CORE_LOCKING_INPUT, LWIP_TCPIP_CORE_LOCKING must be enabled, too"
+#endif
+#if LWIP_TCP && LWIP_NETIF_TX_SINGLE_PBUF && !TCP_OVERSIZE
+#error "LWIP_NETIF_TX_SINGLE_PBUF needs TCP_OVERSIZE enabled to create single-pbuf TCP packets"
+#endif
+#if LWIP_NETCONN && LWIP_TCP
+#if NETCONN_COPY != TCP_WRITE_FLAG_COPY
+#error "NETCONN_COPY != TCP_WRITE_FLAG_COPY"
+#endif
+#if NETCONN_MORE != TCP_WRITE_FLAG_MORE
+#error "NETCONN_MORE != TCP_WRITE_FLAG_MORE"
+#endif
+#endif /* LWIP_NETCONN && LWIP_TCP */
+#if LWIP_NETCONN_FULLDUPLEX && !LWIP_NETCONN_SEM_PER_THREAD
+#error "For LWIP_NETCONN_FULLDUPLEX to work, LWIP_NETCONN_SEM_PER_THREAD is required"
+#endif
+
+
+/* Compile-time checks for deprecated options.
+ */
+#ifdef MEMP_NUM_TCPIP_MSG
+#error "MEMP_NUM_TCPIP_MSG option is deprecated. Remove it from your lwipopts.h."
+#endif
+#ifdef TCP_REXMIT_DEBUG
+#error "TCP_REXMIT_DEBUG option is deprecated. Remove it from your lwipopts.h."
+#endif
+#ifdef RAW_STATS
+#error "RAW_STATS option is deprecated. Remove it from your lwipopts.h."
+#endif
+#ifdef ETHARP_QUEUE_FIRST
+#error "ETHARP_QUEUE_FIRST option is deprecated. Remove it from your lwipopts.h."
+#endif
+#ifdef ETHARP_ALWAYS_INSERT
+#error "ETHARP_ALWAYS_INSERT option is deprecated. Remove it from your lwipopts.h."
+#endif
+#if !NO_SYS && LWIP_TCPIP_CORE_LOCKING && LWIP_COMPAT_MUTEX && !defined(LWIP_COMPAT_MUTEX_ALLOWED)
+#error "LWIP_COMPAT_MUTEX cannot prevent priority inversion. It is recommended to implement priority-aware mutexes. (Define LWIP_COMPAT_MUTEX_ALLOWED to disable this error.)"
+#endif
+
+#ifndef LWIP_DISABLE_TCP_SANITY_CHECKS
+#define LWIP_DISABLE_TCP_SANITY_CHECKS  0
+#endif
+#ifndef LWIP_DISABLE_MEMP_SANITY_CHECKS
+#define LWIP_DISABLE_MEMP_SANITY_CHECKS 0
+#endif
+
+/* MEMP sanity checks */
+#if MEMP_MEM_MALLOC
+#if !LWIP_DISABLE_MEMP_SANITY_CHECKS
+#if LWIP_NETCONN || LWIP_SOCKET
+#if !MEMP_NUM_NETCONN && LWIP_SOCKET
+#error "lwip_sanity_check: WARNING: MEMP_NUM_NETCONN cannot be 0 when using sockets!"
+#endif
+#else /* MEMP_MEM_MALLOC */
+#if MEMP_NUM_NETCONN > (MEMP_NUM_TCP_PCB+MEMP_NUM_TCP_PCB_LISTEN+MEMP_NUM_UDP_PCB+MEMP_NUM_RAW_PCB)
+#error "lwip_sanity_check: WARNING: MEMP_NUM_NETCONN should be less than the sum of MEMP_NUM_{TCP,RAW,UDP}_PCB+MEMP_NUM_TCP_PCB_LISTEN. If you know what you are doing, define LWIP_DISABLE_MEMP_SANITY_CHECKS to 1 to disable this error."
+#endif
+#endif /* LWIP_NETCONN || LWIP_SOCKET */
+#endif /* !LWIP_DISABLE_MEMP_SANITY_CHECKS */
+#if MEM_USE_POOLS
+#error "MEMP_MEM_MALLOC and MEM_USE_POOLS cannot be enabled at the same time"
+#endif
+#ifdef LWIP_HOOK_MEMP_AVAILABLE
+#error "LWIP_HOOK_MEMP_AVAILABLE doesn't make sense with MEMP_MEM_MALLOC"
+#endif
+#endif /* MEMP_MEM_MALLOC */
+
+/* TCP sanity checks */
+#if !LWIP_DISABLE_TCP_SANITY_CHECKS
+#if LWIP_TCP
+#if !MEMP_MEM_MALLOC && (MEMP_NUM_TCP_SEG < TCP_SND_QUEUELEN)
+#error "lwip_sanity_check: WARNING: MEMP_NUM_TCP_SEG should be at least as big as TCP_SND_QUEUELEN. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error."
+#endif
+#if TCP_SND_BUF < (2 * TCP_MSS)
+#error "lwip_sanity_check: WARNING: TCP_SND_BUF must be at least as much as (2 * TCP_MSS) for things to work smoothly. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error."
+#endif
+#if TCP_SND_QUEUELEN < (2 * (TCP_SND_BUF / TCP_MSS))
+#error "lwip_sanity_check: WARNING: TCP_SND_QUEUELEN must be at least as much as (2 * TCP_SND_BUF/TCP_MSS) for things to work. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error."
+#endif
+#if TCP_SNDLOWAT >= TCP_SND_BUF
+#error "lwip_sanity_check: WARNING: TCP_SNDLOWAT must be less than TCP_SND_BUF. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error."
+#endif
+#if TCP_MSS >= ((16 * 1024) - 1)
+#error "lwip_sanity_check: WARNING: TCP_MSS must be <= 16382 to prevent u16_t underflow in TCP_SNDLOWAT calculation!"
+#endif
+#if TCP_SNDLOWAT >= (0xFFFF - (4 * TCP_MSS))
+#error "lwip_sanity_check: WARNING: TCP_SNDLOWAT must at least be 4*MSS below u16_t overflow!"
+#endif
+#if TCP_SNDQUEUELOWAT >= TCP_SND_QUEUELEN
+#error "lwip_sanity_check: WARNING: TCP_SNDQUEUELOWAT must be less than TCP_SND_QUEUELEN. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error."
+#endif
+#if !MEMP_MEM_MALLOC && PBUF_POOL_SIZE && (PBUF_POOL_BUFSIZE <= (PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN))
+#error "lwip_sanity_check: WARNING: PBUF_POOL_BUFSIZE does not provide enough space for protocol headers. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error."
+#endif
+#if !MEMP_MEM_MALLOC && PBUF_POOL_SIZE && (TCP_WND > (PBUF_POOL_SIZE * (PBUF_POOL_BUFSIZE - (PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN))))
+#error "lwip_sanity_check: WARNING: TCP_WND is larger than space provided by PBUF_POOL_SIZE * (PBUF_POOL_BUFSIZE - protocol headers). If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error."
+#endif
+#if TCP_WND < TCP_MSS
+#error "lwip_sanity_check: WARNING: TCP_WND is smaller than MSS. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error."
+#endif
+#endif /* LWIP_TCP */
+#endif /* !LWIP_DISABLE_TCP_SANITY_CHECKS */
+
+/**
+ * @ingroup lwip_nosys
+ * Initialize all modules.
+ * Use this in NO_SYS mode. Use tcpip_init() otherwise.
+ */
+void
+lwip_init(void)
+{
+#ifndef LWIP_SKIP_CONST_CHECK
+  int a = 0;
+  LWIP_UNUSED_ARG(a);
+  LWIP_ASSERT("LWIP_CONST_CAST not implemented correctly. Check your lwIP port.", LWIP_CONST_CAST(void *, &a) == &a);
+#endif
+#ifndef LWIP_SKIP_PACKING_CHECK
+  LWIP_ASSERT("Struct packing not implemented correctly. Check your lwIP port.", sizeof(struct packed_struct_test) == PACKED_STRUCT_TEST_EXPECTED_SIZE);
+#endif
+
+  /* Modules initialization */
+  stats_init();
+#if !NO_SYS
+  sys_init();
+#endif /* !NO_SYS */
+  mem_init();
+  memp_init();
+  pbuf_init();
+  netif_init();
+#if LWIP_IPV4
+  ip_init();
+#if LWIP_ARP
+  etharp_init();
+#endif /* LWIP_ARP */
+#endif /* LWIP_IPV4 */
+#if LWIP_RAW
+  raw_init();
+#endif /* LWIP_RAW */
+#if LWIP_UDP
+  udp_init();
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+  tcp_init();
+#endif /* LWIP_TCP */
+#if LWIP_IGMP
+  igmp_init();
+#endif /* LWIP_IGMP */
+#if LWIP_DNS
+  dns_init();
+#endif /* LWIP_DNS */
+#if PPP_SUPPORT
+  ppp_init();
+#endif
+
+#if LWIP_TIMERS
+  sys_timeouts_init();
+#endif /* LWIP_TIMERS */
+}
diff --git a/lib/lwip/core/ip.c b/lib/lwip/core/ip.c
new file mode 100644
index 0000000000..18514cf32c
--- /dev/null
+++ b/lib/lwip/core/ip.c
@@ -0,0 +1,167 @@
+/**
+ * @file
+ * Common IPv4 and IPv6 code
+ *
+ * @defgroup ip IP
+ * @ingroup callbackstyle_api
+ *
+ * @defgroup ip4 IPv4
+ * @ingroup ip
+ *
+ * @defgroup ip6 IPv6
+ * @ingroup ip
+ *
+ * @defgroup ipaddr IP address handling
+ * @ingroup infrastructure
+ *
+ * @defgroup ip4addr IPv4 only
+ * @ingroup ipaddr
+ *
+ * @defgroup ip6addr IPv6 only
+ * @ingroup ipaddr
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV4 || LWIP_IPV6
+
+#include "lwip/ip_addr.h"
+#include "lwip/ip.h"
+
+/** Global data for both IPv4 and IPv6 */
+struct ip_globals ip_data;
+
+#if LWIP_IPV4 && LWIP_IPV6
+
+const ip_addr_t ip_addr_any_type = IPADDR_ANY_TYPE_INIT;
+
+/**
+ * @ingroup ipaddr
+ * Convert numeric IP address (both versions) into ASCII representation.
+ * returns ptr to static buffer; not reentrant!
+ *
+ * @param addr ip address in network order to convert
+ * @return pointer to a global static (!) buffer that holds the ASCII
+ *         representation of addr
+ */
+char *ipaddr_ntoa(const ip_addr_t *addr)
+{
+  if (addr == NULL) {
+    return NULL;
+  }
+  if (IP_IS_V6(addr)) {
+    return ip6addr_ntoa(ip_2_ip6(addr));
+  } else {
+    return ip4addr_ntoa(ip_2_ip4(addr));
+  }
+}
+
+/**
+ * @ingroup ipaddr
+ * Same as ipaddr_ntoa, but reentrant since a user-supplied buffer is used.
+ *
+ * @param addr ip address in network order to convert
+ * @param buf target buffer where the string is stored
+ * @param buflen length of buf
+ * @return either pointer to buf which now holds the ASCII
+ *         representation of addr or NULL if buf was too small
+ */
+char *ipaddr_ntoa_r(const ip_addr_t *addr, char *buf, int buflen)
+{
+  if (addr == NULL) {
+    return NULL;
+  }
+  if (IP_IS_V6(addr)) {
+    return ip6addr_ntoa_r(ip_2_ip6(addr), buf, buflen);
+  } else {
+    return ip4addr_ntoa_r(ip_2_ip4(addr), buf, buflen);
+  }
+}
+
+/**
+ * @ingroup ipaddr
+ * Convert IP address string (both versions) to numeric.
+ * The version is auto-detected from the string.
+ *
+ * @param cp IP address string to convert
+ * @param addr conversion result is stored here
+ * @return 1 on success, 0 on error
+ */
+int
+ipaddr_aton(const char *cp, ip_addr_t *addr)
+{
+  if (cp != NULL) {
+    const char *c;
+    for (c = cp; *c != 0; c++) {
+      if (*c == ':') {
+        /* contains a colon: IPv6 address */
+        if (addr) {
+          IP_SET_TYPE_VAL(*addr, IPADDR_TYPE_V6);
+        }
+        return ip6addr_aton(cp, ip_2_ip6(addr));
+      } else if (*c == '.') {
+        /* contains a dot: IPv4 address */
+        break;
+      }
+    }
+    /* call ip4addr_aton as fallback or if IPv4 was found */
+    if (addr) {
+      IP_SET_TYPE_VAL(*addr, IPADDR_TYPE_V4);
+    }
+    return ip4addr_aton(cp, ip_2_ip4(addr));
+  }
+  return 0;
+}
+
+/**
+ * @ingroup lwip_nosys
+ * If both IP versions are enabled, this function can dispatch packets to the correct one.
+ * Don't call directly, pass to netif_add() and call netif->input().
+ */
+err_t
+ip_input(struct pbuf *p, struct netif *inp)
+{
+  if (p != NULL) {
+    if (IP_HDR_GET_VERSION(p->payload) == 6) {
+      return ip6_input(p, inp);
+    }
+    return ip4_input(p, inp);
+  }
+  return ERR_VAL;
+}
+
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
+#endif /* LWIP_IPV4 || LWIP_IPV6 */
diff --git a/lib/lwip/core/ipv4/acd.c b/lib/lwip/core/ipv4/acd.c
new file mode 100644
index 0000000000..2c7d867caf
--- /dev/null
+++ b/lib/lwip/core/ipv4/acd.c
@@ -0,0 +1,557 @@
+/**
+ * @file
+ *
+ * ACD IPv4 Address Conflict Detection
+ *
+ * This is an IPv4 address conflict detection implementation for the lwIP TCP/IP
+ * stack. It aims to be conform to RFC5227.
+ *
+ * @defgroup acd ACD
+ * @ingroup ip4
+ * ACD related functions
+ * USAGE:
+ *
+ * define @ref LWIP_ACD 1 in your lwipopts.h
+ * Options:
+ * ACD_TMR_INTERVAL msecs,
+ *   I recommend a value of 100. The value must divide 1000 with a remainder almost 0.
+ *   Possible values are 1000, 500, 333, 250, 200, 166, 142, 125, 111, 100 ....
+ *
+ * For fixed IP:
+ * - call acd_start after selecting an IP address. The caller will be informed
+ *   on conflict status via the callback function.
+ *
+ * With AUTOIP:
+ * - will be called from the autoip module. No extra's needed.
+ *
+ * With DHCP:
+ * - enable LWIP_DHCP_DOES_ACD_CHECK. Then it will be called from the dhcp module.
+ *   No extra's needed.
+ */
+
+/*
+ *
+ * Copyright (c) 2007 Dominik Spies <kontakt@dspies.de>
+ * Copyright (c) 2018 Jasper Verschueren <jasper.verschueren@apart-audio.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Jasper Verschueren <jasper.verschueren@apart-audio.com>
+ * Author: Dominik Spies <kontakt@dspies.de>
+ */
+
+#include "lwip/opt.h"
+
+/* don't build if not configured for use in lwipopts.h */
+#if LWIP_IPV4 && LWIP_ACD
+
+#include <string.h>
+
+#include "lwip/acd.h"
+#include "lwip/prot/acd.h"
+
+#define ACD_FOREACH(acd, acd_list) for ((acd) = acd_list; (acd) != NULL; (acd) = (acd)->next)
+
+#define ACD_TICKS_PER_SECOND  (1000 / ACD_TMR_INTERVAL)
+
+/* Define good random function (LWIP_RAND) in lwipopts.h */
+#ifdef LWIP_RAND
+#define LWIP_ACD_RAND(netif, acd)    LWIP_RAND()
+#else /* LWIP_RAND */
+#ifdef LWIP_AUTOIP_RAND
+#include "lwip/autoip.h"
+#define LWIP_ACD_RAND(netif, acd)    LWIP_AUTOIP_RAND(netif) /* for backwards compatibility */
+#else
+#define LWIP_ACD_RAND(netif, acd) ((((u32_t)((netif->hwaddr[5]) & 0xff) << 24) | \
+                                    ((u32_t)((netif->hwaddr[3]) & 0xff) << 16) | \
+                                    ((u32_t)((netif->hwaddr[2]) & 0xff) << 8) | \
+                                    ((u32_t)((netif->hwaddr[4]) & 0xff))) + \
+                                    (acd->sent_num))
+#endif /* LWIP_AUTOIP_RAND */
+#endif /* LWIP_RAND */
+
+
+#define ACD_RANDOM_PROBE_WAIT(netif, acd) (LWIP_ACD_RAND(netif, acd) % \
+                                    (PROBE_WAIT * ACD_TICKS_PER_SECOND))
+
+#define ACD_RANDOM_PROBE_INTERVAL(netif, acd) ((LWIP_ACD_RAND(netif, acd) % \
+                                    ((PROBE_MAX - PROBE_MIN) * ACD_TICKS_PER_SECOND)) + \
+                                    (PROBE_MIN * ACD_TICKS_PER_SECOND ))
+
+/* Function definitions */
+static void acd_restart(struct netif *netif, struct acd *acd);
+static void acd_handle_arp_conflict(struct netif *netif, struct acd *acd);
+static void acd_put_in_passive_mode(struct netif *netif, struct acd *acd);
+
+/**
+ * @ingroup acd
+ * Add ACD client to the client list and initialize callback function
+ *
+ * @param netif                 network interface on which to start the acd
+ *                              client
+ * @param acd                   acd module to be added to the list
+ * @param acd_conflict_callback callback to be called when conflict information
+ *                              is available
+ */
+err_t
+acd_add(struct netif *netif, struct acd *acd,
+         acd_conflict_callback_t acd_conflict_callback)
+{
+  struct acd *acd2;
+
+  /* Set callback */
+  LWIP_ASSERT_CORE_LOCKED();
+  LWIP_ASSERT("acd_conflict_callback != NULL", acd_conflict_callback != NULL);
+  acd->acd_conflict_callback = acd_conflict_callback;
+
+  /* Check if the acd struct is already added */
+  for (acd2 = netif->acd_list; acd2 != NULL; acd2 = acd2->next) {
+    if (acd2 == acd) {
+      LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+                  ("acd_add(): acd already added to list\n"));
+      return ERR_OK;
+    }
+  }
+
+  /* add acd struct to the list */
+  acd->next = netif->acd_list;
+  netif->acd_list = acd;
+
+  return ERR_OK;
+}
+
+/**
+ * @ingroup acd
+ * Remvoe ACD client from the client list
+ *
+ * @param netif network interface from which to remove the acd client
+ * @param acd   acd module to be removed from the list
+ */
+void
+acd_remove(struct netif *netif, struct acd *acd)
+{
+  struct acd *acd2, *prev = NULL;
+
+  LWIP_ASSERT_CORE_LOCKED();
+
+  for (acd2 = netif->acd_list; acd2 != NULL; acd2 = acd2->next) {
+    if (acd2 == acd) {
+      if (prev) {
+        prev->next = acd->next;
+      } else {
+        netif->acd_list = acd->next;
+      }
+      return;
+    }
+    prev = acd2;
+  }
+  LWIP_ASSERT(("acd_remove(): acd not on list\n"), 0);
+}
+
+
+/**
+ * @ingroup acd
+ * Start ACD client
+ *
+ * @param netif   network interface on which to start the acd client
+ * @param acd     acd module to start
+ * @param ipaddr  ip address to perform acd on
+ */
+err_t
+acd_start(struct netif *netif, struct acd *acd, ip4_addr_t ipaddr)
+{
+  err_t result = ERR_OK;
+
+  LWIP_UNUSED_ARG(netif);
+  LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+              ("acd_start(netif=%p) %c%c%"U16_F"\n",
+              (void *)netif, netif->name[0],
+               netif->name[1], (u16_t)netif->num));
+
+  /* init probing state */
+  acd->sent_num = 0;
+  acd->lastconflict = 0;
+  ip4_addr_copy(acd->ipaddr, ipaddr);
+  acd->state = ACD_STATE_PROBE_WAIT;
+
+  acd->ttw = (u16_t)(ACD_RANDOM_PROBE_WAIT(netif, acd));
+
+  return result;
+}
+
+/**
+ * @ingroup acd
+ * Stop ACD client
+ *
+ * @param acd   acd module to stop
+ */
+err_t
+acd_stop(struct acd *acd)
+{
+  LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("acd_stop\n"));
+
+  if (acd != NULL) {
+    acd->state = ACD_STATE_OFF;
+  }
+  return ERR_OK;
+}
+
+/**
+ * @ingroup acd
+ * Inform the ACD modules when the link goes down
+ *
+ * @param netif network interface on which to inform the ACD clients
+ */
+void
+acd_network_changed_link_down(struct netif *netif)
+{
+  struct acd *acd;
+  /* loop over the acd's*/
+  ACD_FOREACH(acd, netif->acd_list) {
+    acd_stop(acd);
+  }
+}
+
+/**
+ * Has to be called in loop every ACD_TMR_INTERVAL milliseconds
+ */
+void
+acd_tmr(void)
+{
+  struct netif *netif;
+  struct acd *acd;
+  /* loop through netif's */
+  NETIF_FOREACH(netif) {
+    ACD_FOREACH(acd, netif->acd_list) {
+      if (acd->lastconflict > 0) {
+        acd->lastconflict--;
+      }
+
+      LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE,
+                  ("acd_tmr() ACD-State: %"U16_F", ttw=%"U16_F"\n",
+                   (u16_t)(acd->state), acd->ttw));
+
+      if (acd->ttw > 0) {
+        acd->ttw--;
+      }
+
+      switch (acd->state) {
+        case ACD_STATE_PROBE_WAIT:
+        case ACD_STATE_PROBING:
+          if (acd->ttw == 0) {
+            acd->state = ACD_STATE_PROBING;
+            etharp_acd_probe(netif, &acd->ipaddr);
+            LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE,
+                        ("acd_tmr() PROBING Sent Probe\n"));
+            acd->sent_num++;
+            if (acd->sent_num >= PROBE_NUM) {
+              /* Switch to ANNOUNCE_WAIT: last probe is sent*/
+              acd->state = ACD_STATE_ANNOUNCE_WAIT;
+
+              acd->sent_num = 0;
+
+              /* calculate time to wait before announcing */
+              acd->ttw = (u16_t)(ANNOUNCE_WAIT * ACD_TICKS_PER_SECOND);
+            } else {
+              /* calculate time to wait to next probe */
+              acd->ttw = (u16_t)(ACD_RANDOM_PROBE_INTERVAL(netif, acd));
+            }
+          }
+          break;
+
+        case ACD_STATE_ANNOUNCE_WAIT:
+        case ACD_STATE_ANNOUNCING:
+          if (acd->ttw == 0) {
+            if (acd->sent_num == 0) {
+              acd->state = ACD_STATE_ANNOUNCING;
+
+              /* reset conflict count to ensure fast re-probing after announcing */
+              acd->num_conflicts = 0;
+
+              LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+                    ("acd_tmr(): changing state to ANNOUNCING: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+                     ip4_addr1_16(&acd->ipaddr), ip4_addr2_16(&acd->ipaddr),
+                     ip4_addr3_16(&acd->ipaddr), ip4_addr4_16(&acd->ipaddr)));
+            }
+
+            etharp_acd_announce(netif, &acd->ipaddr);
+            LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE,
+                        ("acd_tmr() ANNOUNCING Sent Announce\n"));
+            acd->ttw = ANNOUNCE_INTERVAL * ACD_TICKS_PER_SECOND;
+            acd->sent_num++;
+
+            if (acd->sent_num >= ANNOUNCE_NUM) {
+              acd->state = ACD_STATE_ONGOING;
+              acd->sent_num = 0;
+              acd->ttw = 0;
+              LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+                    ("acd_tmr(): changing state to ONGOING: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+                     ip4_addr1_16(&acd->ipaddr), ip4_addr2_16(&acd->ipaddr),
+                     ip4_addr3_16(&acd->ipaddr), ip4_addr4_16(&acd->ipaddr)));
+
+              /* finally, let acd user know that the address is good and can be used */
+              acd->acd_conflict_callback(netif, ACD_IP_OK);
+            }
+          }
+          break;
+
+        case ACD_STATE_RATE_LIMIT:
+          if (acd->ttw == 0) {
+            /* acd should be stopped because ipaddr isn't valid any more */
+            acd_stop(acd);
+            /* let the acd user (after rate limit interval) know that their is
+             * a conflict detected. So it can restart the address acquiring
+             * process.*/
+            acd->acd_conflict_callback(netif, ACD_RESTART_CLIENT);
+          }
+          break;
+
+        default:
+          /* nothing to do in other states */
+          break;
+      }
+    }
+  }
+}
+
+/**
+ * Restarts the acd module
+ *
+ * The number of conflicts is increased and the upper layer is informed.
+ */
+static void
+acd_restart(struct netif *netif, struct acd *acd)
+{
+  /* increase conflict counter. */
+  acd->num_conflicts++;
+
+  /* Decline the address */
+  acd->acd_conflict_callback(netif, ACD_DECLINE);
+
+  /* if we tried more then MAX_CONFLICTS we must limit our rate for
+   * acquiring and probing addresses. compliant to RFC 5227 Section 2.1.1 */
+  if (acd->num_conflicts >= MAX_CONFLICTS) {
+    acd->state = ACD_STATE_RATE_LIMIT;
+    acd->ttw = (u16_t)(RATE_LIMIT_INTERVAL * ACD_TICKS_PER_SECOND);
+    LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING,
+                ("acd_restart(): rate limiting initiated. too many conflicts\n"));
+  }
+  else {
+    /* acd should be stopped because ipaddr isn't valid any more */
+    acd_stop(acd);
+    /* let the acd user know right away that their is a conflict detected.
+     * So it can restart the address acquiring process. */
+    acd->acd_conflict_callback(netif, ACD_RESTART_CLIENT);
+  }
+}
+
+/**
+ * Handles every incoming ARP Packet, called by etharp_input().
+ *
+ * @param netif network interface to use for acd processing
+ * @param hdr   Incoming ARP packet
+ */
+void
+acd_arp_reply(struct netif *netif, struct etharp_hdr *hdr)
+{
+  struct acd *acd;
+  ip4_addr_t sipaddr, dipaddr;
+  struct eth_addr netifaddr;
+  SMEMCPY(netifaddr.addr, netif->hwaddr, ETH_HWADDR_LEN);
+
+  /* Copy struct ip4_addr_wordaligned to aligned ip4_addr, to support
+   * compilers without structure packing (not using structure copy which
+   * breaks strict-aliasing rules).
+   */
+  IPADDR_WORDALIGNED_COPY_TO_IP4_ADDR_T(&sipaddr, &hdr->sipaddr);
+  IPADDR_WORDALIGNED_COPY_TO_IP4_ADDR_T(&dipaddr, &hdr->dipaddr);
+
+  LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE, ("acd_arp_reply()\n"));
+
+  /* loop over the acd's*/
+  ACD_FOREACH(acd, netif->acd_list) {
+    switch(acd->state) {
+      case ACD_STATE_OFF:
+      case ACD_STATE_RATE_LIMIT:
+      default:
+        /* do nothing */
+        break;
+
+      case ACD_STATE_PROBE_WAIT:
+      case ACD_STATE_PROBING:
+      case ACD_STATE_ANNOUNCE_WAIT:
+        /* RFC 5227 Section 2.1.1:
+         * from beginning to after ANNOUNCE_WAIT seconds we have a conflict if
+         * ip.src == ipaddr (someone is already using the address)
+         * OR
+         * ip.dst == ipaddr && hw.src != own hwaddr (someone else is probing it)
+         */
+        if ((ip4_addr_eq(&sipaddr, &acd->ipaddr)) ||
+            (ip4_addr_isany_val(sipaddr) &&
+             ip4_addr_eq(&dipaddr, &acd->ipaddr) &&
+             !eth_addr_eq(&netifaddr, &hdr->shwaddr))) {
+          LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING,
+                      ("acd_arp_reply(): Probe Conflict detected\n"));
+          acd_restart(netif, acd);
+        }
+        break;
+
+      case ACD_STATE_ANNOUNCING:
+      case ACD_STATE_ONGOING:
+      case ACD_STATE_PASSIVE_ONGOING:
+        /* RFC 5227 Section 2.4:
+         * in any state we have a conflict if
+         * ip.src == ipaddr && hw.src != own hwaddr (someone is using our address)
+         */
+        if (ip4_addr_eq(&sipaddr, &acd->ipaddr) &&
+            !eth_addr_eq(&netifaddr, &hdr->shwaddr)) {
+          LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING,
+                      ("acd_arp_reply(): Conflicting ARP-Packet detected\n"));
+          acd_handle_arp_conflict(netif, acd);
+        }
+        break;
+    }
+  }
+}
+
+/**
+ * Handle a IP address conflict after an ARP conflict detection
+ */
+static void
+acd_handle_arp_conflict(struct netif *netif, struct acd *acd)
+{
+  /* RFC5227, 2.4 "Ongoing Address Conflict Detection and Address Defense"
+     allows three options where:
+     a) means retreat on the first conflict,
+     b) allows to keep an already configured address when having only one
+        conflict in DEFEND_INTERVAL seconds and
+     c) the host will not give up it's address and defend it indefinitely
+
+     We use option b) when the acd module represents the netif address, since it
+     helps to improve the chance that one of the two conflicting hosts may be
+     able to retain its address. while we are flexible enough to help network
+     performance
+
+     We use option a) when the acd module does not represent the netif address,
+     since we cannot have the acd module announcing or restarting. This
+     situation occurs for the LL acd module when a routable address is used on
+     the netif but the LL address is still open in the background. */
+
+  if (acd->state == ACD_STATE_PASSIVE_ONGOING) {
+    /* Immediately back off on a conflict. */
+    LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+      ("acd_handle_arp_conflict(): conflict when we are in passive mode -> back off\n"));
+    acd_stop(acd);
+    acd->acd_conflict_callback(netif, ACD_DECLINE);
+  }
+  else {
+    if (acd->lastconflict > 0) {
+      /* retreat, there was a conflicting ARP in the last DEFEND_INTERVAL seconds */
+      LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+        ("acd_handle_arp_conflict(): conflict within DEFEND_INTERVAL -> retreating\n"));
+
+      /* Active TCP sessions are aborted when removing the ip address but a bad
+       * connection was inevitable anyway with conflicting hosts */
+       acd_restart(netif, acd);
+    } else {
+      LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+          ("acd_handle_arp_conflict(): we are defending, send ARP Announce\n"));
+      etharp_acd_announce(netif, &acd->ipaddr);
+      acd->lastconflict = DEFEND_INTERVAL * ACD_TICKS_PER_SECOND;
+    }
+  }
+}
+
+/**
+ * Put the acd module in passive ongoing conflict detection.
+ */
+static void
+acd_put_in_passive_mode(struct netif *netif, struct acd *acd)
+{
+  switch(acd->state) {
+    case ACD_STATE_OFF:
+    case ACD_STATE_PASSIVE_ONGOING:
+    default:
+      /* do nothing */
+      break;
+
+    case ACD_STATE_PROBE_WAIT:
+    case ACD_STATE_PROBING:
+    case ACD_STATE_ANNOUNCE_WAIT:
+    case ACD_STATE_RATE_LIMIT:
+      acd_stop(acd);
+      acd->acd_conflict_callback(netif, ACD_DECLINE);
+      break;
+
+    case ACD_STATE_ANNOUNCING:
+    case ACD_STATE_ONGOING:
+      acd->state = ACD_STATE_PASSIVE_ONGOING;
+      LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+        ("acd_put_in_passive_mode()\n"));
+      break;
+  }
+}
+
+/**
+ * @ingroup acd
+ * Inform the ACD modules of address changes
+ *
+ * @param netif     network interface on which the address is changing
+ * @param old_addr  old ip address
+ * @param new_addr  new ip address
+ */
+void
+acd_netif_ip_addr_changed(struct netif *netif, const ip_addr_t *old_addr,
+                          const ip_addr_t *new_addr)
+{
+  struct acd *acd;
+
+  LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+    ("acd_netif_ip_addr_changed(): Address changed\n"));
+
+  LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+    ("acd_netif_ip_addr_changed(): old address = %s\n", ipaddr_ntoa(old_addr)));
+  LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+    ("acd_netif_ip_addr_changed(): new address = %s\n", ipaddr_ntoa(new_addr)));
+
+  /* If we change from ANY to an IP or from an IP to ANY we do nothing */
+  if (ip_addr_isany(old_addr) || ip_addr_isany(new_addr)) {
+    return;
+  }
+
+  ACD_FOREACH(acd, netif->acd_list) {
+    /* Find ACD module of old address */
+    if(ip4_addr_eq(&acd->ipaddr, ip_2_ip4(old_addr))) {
+      /* Did we change from a LL address to a routable address? */
+      if (ip_addr_islinklocal(old_addr) && !ip_addr_islinklocal(new_addr)) {
+        LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+          ("acd_netif_ip_addr_changed(): changed from LL to routable address\n"));
+        /* Put the module in passive conflict detection mode */
+        acd_put_in_passive_mode(netif, acd);
+      }
+    }
+  }
+}
+
+#endif /* LWIP_IPV4 && LWIP_ACD */
diff --git a/lib/lwip/core/ipv4/autoip.c b/lib/lwip/core/ipv4/autoip.c
new file mode 100644
index 0000000000..461a005338
--- /dev/null
+++ b/lib/lwip/core/ipv4/autoip.c
@@ -0,0 +1,379 @@
+/**
+ * @file
+ * AutoIP Automatic LinkLocal IP Configuration
+ *
+ * This is a AutoIP implementation for the lwIP TCP/IP stack. It aims to conform
+ * with RFC 3927. It uses IPv4 address conflict detection to evaluate the chosen
+ * address. The ACD module aims to be conform to RFC 5227.
+ * RFC 5227 is extracted out of RFC 3927 so the acd module fits nicely in autoip.
+ *
+ * @defgroup autoip AUTOIP
+ * @ingroup ip4
+ * AUTOIP related functions
+ * USAGE:
+ *
+ * define @ref LWIP_AUTOIP 1 in your lwipopts.h
+ *
+ * Without DHCP:
+ * - Call autoip_start() after netif_add().
+ *
+ * With DHCP:
+ * - define @ref LWIP_DHCP_AUTOIP_COOP 1 in your lwipopts.h.
+ * - Configure your DHCP Client.
+ *
+ * @see netifapi_autoip
+ */
+
+/*
+ *
+ * Copyright (c) 2007 Dominik Spies <kontakt@dspies.de>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Dominik Spies <kontakt@dspies.de>
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV4 && LWIP_AUTOIP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/mem.h"
+/* #include "lwip/udp.h" */
+#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+#include "lwip/autoip.h"
+#include "lwip/acd.h"
+#include "lwip/etharp.h"
+#include "lwip/prot/autoip.h"
+
+#include <string.h>
+
+/**
+ * Macro that generates the initial IP address to be tried by AUTOIP.
+ * If you want to override this, define it to something else in lwipopts.h.
+ */
+#ifndef LWIP_AUTOIP_CREATE_SEED_ADDR
+#define LWIP_AUTOIP_CREATE_SEED_ADDR(netif) \
+  lwip_htonl(AUTOIP_RANGE_START + ((u32_t)(((u8_t)(netif->hwaddr[4])) | \
+                 ((u32_t)((u8_t)(netif->hwaddr[5]))) << 8)))
+#endif /* LWIP_AUTOIP_CREATE_SEED_ADDR */
+
+/* Function definitions */
+static void autoip_restart(struct netif *netif);
+static void autoip_create_addr(struct netif *netif, ip4_addr_t *ipaddr);
+static err_t autoip_bind(struct netif *netif);
+static void autoip_conflict_callback(struct netif *netif,
+                                     acd_callback_enum_t state);
+
+/**
+ * @ingroup autoip
+ * Set a statically allocated struct autoip to work with.
+ * Using this prevents autoip_start to allocate it using mem_malloc.
+ *
+ * @param netif the netif for which to set the struct autoip
+ * @param autoip (uninitialised) autoip struct allocated by the application
+ */
+void
+autoip_set_struct(struct netif *netif, struct autoip *autoip)
+{
+  LWIP_ASSERT_CORE_LOCKED();
+  LWIP_ASSERT("netif != NULL", netif != NULL);
+  LWIP_ASSERT("autoip != NULL", autoip != NULL);
+  LWIP_ASSERT("netif already has a struct autoip set",
+              netif_autoip_data(netif) == NULL);
+
+  /* clear data structure */
+  memset(autoip, 0, sizeof(struct autoip));
+  /* autoip->state = AUTOIP_STATE_OFF; */
+  netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_AUTOIP, autoip);
+}
+
+/**
+ * @ingroup autoip
+ * Remove a struct autoip previously set to the netif using autoip_set_struct()
+ *
+ * @param netif the netif for which to set the struct autoip
+ */
+void
+autoip_remove_struct(struct netif *netif)
+{
+  LWIP_ASSERT_CORE_LOCKED();
+  LWIP_ASSERT("netif != NULL", netif != NULL);
+  LWIP_ASSERT("netif has no struct autoip set",
+              netif_autoip_data(netif) != NULL);
+
+  netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_AUTOIP, NULL);
+}
+
+/** Restart AutoIP client and check the next address (conflict detected)
+ *
+ * @param netif The netif under AutoIP control
+ */
+static void
+autoip_restart(struct netif *netif)
+{
+  struct autoip *autoip = netif_autoip_data(netif);
+  autoip->tried_llipaddr++;
+  autoip_start(netif);
+}
+
+
+/**
+ * Create an IP-Address out of range 169.254.1.0 to 169.254.254.255
+ *
+ * @param netif network interface on which create the IP-Address
+ * @param ipaddr ip address to initialize
+ */
+static void
+autoip_create_addr(struct netif *netif, ip4_addr_t *ipaddr)
+{
+  struct autoip *autoip = netif_autoip_data(netif);
+
+  /* Here we create an IP-Address out of range 169.254.1.0 to 169.254.254.255
+   * compliant to RFC 3927 Section 2.1
+   * We have 254 * 256 possibilities */
+
+  u32_t addr = lwip_ntohl(LWIP_AUTOIP_CREATE_SEED_ADDR(netif));
+  addr += autoip->tried_llipaddr;
+  addr = AUTOIP_NET | (addr & 0xffff);
+  /* Now, 169.254.0.0 <= addr <= 169.254.255.255 */
+
+  if (addr < AUTOIP_RANGE_START) {
+    addr += AUTOIP_RANGE_END - AUTOIP_RANGE_START + 1;
+  }
+  if (addr > AUTOIP_RANGE_END) {
+    addr -= AUTOIP_RANGE_END - AUTOIP_RANGE_START + 1;
+  }
+  LWIP_ASSERT("AUTOIP address not in range", (addr >= AUTOIP_RANGE_START) &&
+              (addr <= AUTOIP_RANGE_END));
+  ip4_addr_set_u32(ipaddr, lwip_htonl(addr));
+
+  LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+              ("autoip_create_addr(): tried_llipaddr=%"U16_F", %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+               (u16_t)(autoip->tried_llipaddr), ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr),
+               ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr)));
+}
+
+
+/**
+ * Configure interface for use with current LL IP-Address
+ *
+ * @param netif network interface to configure with current LL IP-Address
+ */
+static err_t
+autoip_bind(struct netif *netif)
+{
+  struct autoip *autoip = netif_autoip_data(netif);
+  ip4_addr_t sn_mask, gw_addr;
+
+  autoip->state = AUTOIP_STATE_BOUND;
+
+  LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
+              ("autoip_bind(netif=%p) %c%c%"U16_F" %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+               (void *)netif, netif->name[0], netif->name[1], (u16_t)netif->num,
+               ip4_addr1_16(&autoip->llipaddr), ip4_addr2_16(&autoip->llipaddr),
+               ip4_addr3_16(&autoip->llipaddr), ip4_addr4_16(&autoip->llipaddr)));
+
+  IP4_ADDR(&sn_mask, 255, 255, 0, 0);
+  IP4_ADDR(&gw_addr, 0, 0, 0, 0);
+
+  netif_set_addr(netif, &autoip->llipaddr, &sn_mask, &gw_addr);
+  /* interface is used by routing now that an address is set */
+
+  return ERR_OK;
+}
+
+/**
+* Handle conflict information from ACD module
+*
+* @param netif   network interface to handle conflict information on
+* @param state   acd_callback_enum_t
+ */
+static void
+autoip_conflict_callback(struct netif *netif, acd_callback_enum_t state)
+{
+  struct autoip *autoip = netif_autoip_data(netif);
+
+  switch (state) {
+    case ACD_IP_OK:
+      autoip_bind(netif);
+      break;
+    case ACD_RESTART_CLIENT:
+      autoip_restart(netif);
+      break;
+    case ACD_DECLINE:
+      /* "delete" conflicting address so a new one will be selected in
+       * autoip_start() */
+      ip4_addr_set_any(&autoip->llipaddr);
+      autoip_stop(netif);
+      break;
+      default:
+      break;
+  }
+}
+
+/**
+ * @ingroup autoip
+ * Start AutoIP client
+ *
+ * @param netif network interface on which start the AutoIP client
+ */
+err_t
+autoip_start(struct netif *netif)
+{
+  struct autoip *autoip = netif_autoip_data(netif);
+  err_t result = ERR_OK;
+
+  LWIP_ASSERT_CORE_LOCKED();
+  LWIP_ERROR("netif is not up, old style port?", netif_is_up(netif), return ERR_ARG;);
+
+  if (autoip == NULL) {
+    /* no AutoIP client attached yet? */
+    LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
+                ("autoip_start(): starting new AUTOIP client\n"));
+    autoip = (struct autoip *)mem_calloc(1, sizeof(struct autoip));
+    if (autoip == NULL) {
+      LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
+                  ("autoip_start(): could not allocate autoip\n"));
+      return ERR_MEM;
+    }
+    /* store this AutoIP client in the netif */
+    netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_AUTOIP, autoip);
+    LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, ("autoip_start(): allocated autoip\n"));
+  }
+
+  if (autoip->state == AUTOIP_STATE_OFF) {
+    LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+                ("autoip_start(netif=%p) %c%c%"U16_F"\n", (void *)netif, netif->name[0],
+                 netif->name[1], (u16_t)netif->num));
+
+    /* add acd struct to list*/
+    acd_add(netif, &autoip->acd, autoip_conflict_callback);
+
+    /* In accordance to RFC3927 section 2.1:
+     * Keep using the same link local address as much as possible.
+     * Only when there is none or when there was a conflict, select a new one.
+     */
+    if (!ip4_addr_islinklocal(&autoip->llipaddr)) {
+      autoip_create_addr(netif, &(autoip->llipaddr));
+    }
+    autoip->state = AUTOIP_STATE_CHECKING;
+    acd_start(netif, &autoip->acd, autoip->llipaddr);
+  } else {
+    LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+                ("autoip_start(): already started on netif=%p %c%c%"U16_F"\n",
+                (void *)netif, netif->name[0],
+                 netif->name[1], (u16_t)netif->num));
+  }
+
+  return result;
+}
+
+
+/**
+ * Handle a possible change in the network configuration: link up
+ *
+ * If there is an AutoIP address configured and AutoIP is not in cooperation
+ * with DHCP, start probing for previous address.
+ */
+void
+autoip_network_changed_link_up(struct netif *netif)
+{
+  struct autoip *autoip = netif_autoip_data(netif);
+
+  if (autoip && (autoip->state != AUTOIP_STATE_OFF) && !LWIP_DHCP_AUTOIP_COOP) {
+    LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
+                ("autoip_network_changed_link_up(): start acd\n"));
+    autoip->state = AUTOIP_STATE_CHECKING;
+    /* Start acd check again for the last used address */
+    acd_start(netif, &autoip->acd, autoip->llipaddr);
+  }
+}
+
+/**
+ * Handle a possible change in the network configuration: link down
+ *
+ * If there is an AutoIP address configured and AutoIP is in cooperation
+ * with DHCP, then stop the autoip module. When the link goes up, we do not want
+ * the autoip module to start again. DHCP will initiate autoip when needed.
+ */
+void
+autoip_network_changed_link_down(struct netif *netif)
+{
+  struct autoip *autoip = netif_autoip_data(netif);
+
+  if (autoip && (autoip->state != AUTOIP_STATE_OFF) && LWIP_DHCP_AUTOIP_COOP) {
+    LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
+                ("autoip_network_changed_link_down(): stop autoip\n"));
+    autoip_stop(netif);
+  }
+}
+
+/**
+ * @ingroup autoip
+ * Stop AutoIP client
+ *
+ * @param netif network interface on which stop the AutoIP client
+ */
+err_t
+autoip_stop(struct netif *netif)
+{
+  struct autoip *autoip = netif_autoip_data(netif);
+
+  LWIP_ASSERT_CORE_LOCKED();
+  if (autoip != NULL) {
+    autoip->state = AUTOIP_STATE_OFF;
+    if (ip4_addr_islinklocal(netif_ip4_addr(netif))) {
+      netif_set_addr(netif, IP4_ADDR_ANY4, IP4_ADDR_ANY4, IP4_ADDR_ANY4);
+    }
+    LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,("autoip_stop()\n"));
+  }
+  return ERR_OK;
+}
+
+/** check if AutoIP supplied netif->ip_addr
+ *
+ * @param netif the netif to check
+ * @return 1 if AutoIP supplied netif->ip_addr (state BOUND),
+ *         0 otherwise
+ */
+u8_t
+autoip_supplied_address(struct netif *netif)
+{
+  struct autoip *autoip = netif_autoip_data(netif);
+  return     (autoip != NULL)
+          && (ip4_addr_eq(netif_ip4_addr(netif), &(autoip->llipaddr)))
+          && (autoip->state == AUTOIP_STATE_BOUND);
+}
+
+u8_t
+autoip_accept_packet(struct netif *netif, const ip4_addr_t *addr)
+{
+  struct autoip *autoip = netif_autoip_data(netif);
+  return     (autoip != NULL)
+          && (ip4_addr_eq(addr, &(autoip->llipaddr)))
+          && (autoip->state == AUTOIP_STATE_BOUND);
+}
+
+#endif /* LWIP_IPV4 && LWIP_AUTOIP */
diff --git a/lib/lwip/core/ipv4/dhcp.c b/lib/lwip/core/ipv4/dhcp.c
new file mode 100644
index 0000000000..d9ed8b0f37
--- /dev/null
+++ b/lib/lwip/core/ipv4/dhcp.c
@@ -0,0 +1,1999 @@
+/**
+ * @file
+ * Dynamic Host Configuration Protocol client
+ *
+ * @defgroup dhcp4 DHCPv4
+ * @ingroup ip4
+ * DHCP (IPv4) related functions
+ * This is a DHCP client for the lwIP TCP/IP stack. It aims to conform
+ * with RFC 2131 and RFC 2132.
+ *
+ * @todo:
+ * - Support for interfaces other than Ethernet (SLIP, PPP, ...)
+ *
+ * Options:
+ * @ref DHCP_COARSE_TIMER_SECS (recommended 60 which is a minute)
+ * @ref DHCP_FINE_TIMER_MSECS (recommended 500 which equals TCP coarse timer)
+ *
+ * dhcp_start() starts a DHCP client instance which
+ * configures the interface by obtaining an IP address lease and maintaining it.
+ *
+ * Use dhcp_release() to end the lease and use dhcp_stop()
+ * to remove the DHCP client.
+ *
+ * @see LWIP_HOOK_DHCP_APPEND_OPTIONS
+ * @see LWIP_HOOK_DHCP_PARSE_OPTION
+ *
+ * @see netifapi_dhcp4
+ */
+
+/*
+ * Copyright (c) 2001-2004 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * Copyright (c) 2001-2004 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ * The Swedish Institute of Computer Science and Adam Dunkels
+ * are specifically granted permission to redistribute this
+ * source code.
+ *
+ * Author: Leon Woestenberg <leon.woestenberg@gmx.net>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV4 && LWIP_DHCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/stats.h"
+#include "lwip/mem.h"
+#include "lwip/udp.h"
+#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+#include "lwip/def.h"
+#include "lwip/dhcp.h"
+#include "lwip/autoip.h"
+#include "lwip/acd.h"
+#include "lwip/dns.h"
+#include "lwip/etharp.h"
+#include "lwip/prot/dhcp.h"
+#include "lwip/prot/iana.h"
+
+#include <string.h>
+
+#ifdef LWIP_HOOK_FILENAME
+#include LWIP_HOOK_FILENAME
+#endif
+#ifndef LWIP_HOOK_DHCP_APPEND_OPTIONS
+#define LWIP_HOOK_DHCP_APPEND_OPTIONS(netif, dhcp, state, msg, msg_type, options_len_ptr)
+#endif
+#ifndef LWIP_HOOK_DHCP_PARSE_OPTION
+#define LWIP_HOOK_DHCP_PARSE_OPTION(netif, dhcp, state, msg, msg_type, option, len, pbuf, offset) do { LWIP_UNUSED_ARG(msg); } while(0)
+#endif
+
+/** DHCP_ADD_EXTRA_REQUEST_OPTIONS: Additional options added to the list of options
+ * that the client requests from the servers (opt 55: DHCP_OPTION_PARAMETER_REQUEST_LIST)
+ * If additional options are requested, define this macro as a comma separated list, with leading comma.
+ * This macro is useful for example when requested vendor specific ids (VCI/VSI options), here is an example
+ * of requesting the VSI option (option 43) (yes, the notation is a bit strange, but it works :)
+ * (NOTE: the space between # and define is required because of doxygen...)
+ * # define DHCP_ADD_EXTRA_REQUEST_OPTIONS ,43
+ */
+#ifndef DHCP_ADD_EXTRA_REQUEST_OPTIONS
+#define DHCP_ADD_EXTRA_REQUEST_OPTIONS
+#endif
+
+/** DHCP_DEFINE_CUSTOM_TIMEOUTS: if this is defined then you can customize various DHCP timeouts using these macros:
+      - DHCP_SET_TIMEOUT_FROM_OFFERED_T0_LEASE() to adjust the t0 lease timeout from the offered value
+      - DHCP_SET_TIMEOUT_FROM_OFFERED_T1_RENEW() same for t1 renew
+      - DHCP_SET_TIMEOUT_FROM_OFFERED_T2_REBIND() same for t2 rebind
+      - DHCP_NEXT_TIMEOUT_THRESHOLD to adjust the period of the next timeout
+      - DHCP_REQUEST_BACKOFF_SEQUENCE to adjust back-off times based on DHCP request attempts
+ */
+#ifndef DHCP_DEFINE_CUSTOM_TIMEOUTS
+#define SET_TIMEOUT_FROM_OFFERED(result, offered, min, max) do { \
+  u32_t timeout = (offered + DHCP_COARSE_TIMER_SECS / 2) / DHCP_COARSE_TIMER_SECS;  \
+  if (timeout > max) {    \
+    timeout = max;        \
+  }                       \
+  if (timeout == min) {   \
+    timeout = 1;          \
+  } \
+  result = (dhcp_timeout_t)timeout; \
+} while(0)
+
+#define DHCP_SET_TIMEOUT_FROM_OFFERED_T0_LEASE(res, dhcp)  SET_TIMEOUT_FROM_OFFERED(res, (dhcp)->offered_t0_lease, 0, 0xffff)
+#define DHCP_SET_TIMEOUT_FROM_OFFERED_T1_RENEW(res, dhcp)  SET_TIMEOUT_FROM_OFFERED(res, (dhcp)->offered_t1_renew, 0, 0xffff)
+#define DHCP_SET_TIMEOUT_FROM_OFFERED_T2_REBIND(res, dhcp) SET_TIMEOUT_FROM_OFFERED(res, (dhcp)->offered_t2_rebind, 0, 0xffff)
+
+#define DHCP_NEXT_TIMEOUT_THRESHOLD ((60 + DHCP_COARSE_TIMER_SECS / 2) / DHCP_COARSE_TIMER_SECS)
+#define DHCP_REQUEST_BACKOFF_SEQUENCE(tries)   (u16_t)(( (tries) < 6 ? 1 << (tries) : 60) * 1000)
+
+#endif /* DHCP_DEFINE_CUSTOM_TIMEOUTS */
+
+/** DHCP_CREATE_RAND_XID: if this is set to 1, the xid is created using
+ * LWIP_RAND() (this overrides DHCP_GLOBAL_XID)
+ */
+#ifndef DHCP_CREATE_RAND_XID
+#define DHCP_CREATE_RAND_XID        1
+#endif
+
+/** Default for DHCP_GLOBAL_XID is 0xABCD0000
+ * This can be changed by defining DHCP_GLOBAL_XID and DHCP_GLOBAL_XID_HEADER, e.g.
+ *  \#define DHCP_GLOBAL_XID_HEADER "stdlib.h"
+ *  \#define DHCP_GLOBAL_XID rand()
+ */
+#ifdef DHCP_GLOBAL_XID_HEADER
+#include DHCP_GLOBAL_XID_HEADER /* include optional starting XID generation prototypes */
+#endif
+
+/** DHCP_OPTION_MAX_MSG_SIZE is set to the MTU
+ * MTU is checked to be big enough in dhcp_start */
+#define DHCP_MAX_MSG_LEN(netif)        (netif->mtu)
+#define DHCP_MAX_MSG_LEN_MIN_REQUIRED  576
+/** Minimum length for reply before packet is parsed */
+#define DHCP_MIN_REPLY_LEN             44
+
+#define REBOOT_TRIES                2
+
+#if LWIP_DNS && LWIP_DHCP_MAX_DNS_SERVERS
+#if DNS_MAX_SERVERS > LWIP_DHCP_MAX_DNS_SERVERS
+#define LWIP_DHCP_PROVIDE_DNS_SERVERS LWIP_DHCP_MAX_DNS_SERVERS
+#else
+#define LWIP_DHCP_PROVIDE_DNS_SERVERS DNS_MAX_SERVERS
+#endif
+#else
+#define LWIP_DHCP_PROVIDE_DNS_SERVERS 0
+#endif
+
+#ifndef LWIP_DHCP_INPUT_ERROR
+#define LWIP_DHCP_INPUT_ERROR(message, expression, handler) do { if (!(expression)) { \
+  handler;} } while(0)
+#endif
+
+/** Option handling: options are parsed in dhcp_parse_reply
+ * and saved in an array where other functions can load them from.
+ * This might be moved into the struct dhcp (not necessarily since
+ * lwIP is single-threaded and the array is only used while in recv
+ * callback). */
+enum dhcp_option_idx {
+  DHCP_OPTION_IDX_OVERLOAD = 0,
+  DHCP_OPTION_IDX_MSG_TYPE,
+  DHCP_OPTION_IDX_SERVER_ID,
+  DHCP_OPTION_IDX_LEASE_TIME,
+  DHCP_OPTION_IDX_T1,
+  DHCP_OPTION_IDX_T2,
+  DHCP_OPTION_IDX_SUBNET_MASK,
+  DHCP_OPTION_IDX_ROUTER,
+#if LWIP_DHCP_PROVIDE_DNS_SERVERS
+  DHCP_OPTION_IDX_DNS_SERVER,
+  DHCP_OPTION_IDX_DNS_SERVER_LAST = DHCP_OPTION_IDX_DNS_SERVER + LWIP_DHCP_PROVIDE_DNS_SERVERS - 1,
+#endif /* LWIP_DHCP_PROVIDE_DNS_SERVERS */
+#if LWIP_DHCP_GET_NTP_SRV
+  DHCP_OPTION_IDX_NTP_SERVER,
+  DHCP_OPTION_IDX_NTP_SERVER_LAST = DHCP_OPTION_IDX_NTP_SERVER + LWIP_DHCP_MAX_NTP_SERVERS - 1,
+#endif /* LWIP_DHCP_GET_NTP_SRV */
+  DHCP_OPTION_IDX_MAX
+};
+
+/** Holds the decoded option values, only valid while in dhcp_recv.
+    @todo: move this into struct dhcp? */
+static u32_t dhcp_rx_options_val[DHCP_OPTION_IDX_MAX];
+/** Holds a flag which option was received and is contained in dhcp_rx_options_val,
+    only valid while in dhcp_recv.
+    @todo: move this into struct dhcp? */
+static u8_t  dhcp_rx_options_given[DHCP_OPTION_IDX_MAX];
+
+static u8_t dhcp_discover_request_options[] = {
+  DHCP_OPTION_SUBNET_MASK,
+  DHCP_OPTION_ROUTER,
+  DHCP_OPTION_BROADCAST
+#if LWIP_DHCP_PROVIDE_DNS_SERVERS
+  , DHCP_OPTION_DNS_SERVER
+#endif /* LWIP_DHCP_PROVIDE_DNS_SERVERS */
+#if LWIP_DHCP_GET_NTP_SRV
+  , DHCP_OPTION_NTP
+#endif /* LWIP_DHCP_GET_NTP_SRV */
+  DHCP_ADD_EXTRA_REQUEST_OPTIONS
+};
+
+#ifdef DHCP_GLOBAL_XID
+static u32_t xid;
+static u8_t xid_initialised;
+#endif /* DHCP_GLOBAL_XID */
+
+#define dhcp_option_given(dhcp, idx)          (dhcp_rx_options_given[idx] != 0)
+#define dhcp_got_option(dhcp, idx)            (dhcp_rx_options_given[idx] = 1)
+#define dhcp_clear_option(dhcp, idx)          (dhcp_rx_options_given[idx] = 0)
+#define dhcp_clear_all_options(dhcp)          (memset(dhcp_rx_options_given, 0, sizeof(dhcp_rx_options_given)))
+#define dhcp_get_option_value(dhcp, idx)      (dhcp_rx_options_val[idx])
+#define dhcp_set_option_value(dhcp, idx, val) (dhcp_rx_options_val[idx] = (val))
+
+static struct udp_pcb *dhcp_pcb;
+static u8_t dhcp_pcb_refcount;
+
+/* DHCP client state machine functions */
+static err_t dhcp_discover(struct netif *netif);
+static err_t dhcp_select(struct netif *netif);
+static void dhcp_bind(struct netif *netif);
+#if LWIP_DHCP_DOES_ACD_CHECK
+static err_t dhcp_decline(struct netif *netif);
+#endif /* LWIP_DHCP_DOES_ACD_CHECK */
+static err_t dhcp_rebind(struct netif *netif);
+static err_t dhcp_reboot(struct netif *netif);
+static void dhcp_set_state(struct dhcp *dhcp, u8_t new_state);
+
+/* receive, unfold, parse and free incoming messages */
+static void dhcp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port);
+
+/* set the DHCP timers */
+static void dhcp_timeout(struct netif *netif);
+static void dhcp_t1_timeout(struct netif *netif);
+static void dhcp_t2_timeout(struct netif *netif);
+
+/* build outgoing messages */
+/* create a DHCP message, fill in common headers */
+static struct pbuf *dhcp_create_msg(struct netif *netif, struct dhcp *dhcp, u8_t message_type, u16_t *options_out_len);
+/* add a DHCP option (type, then length in bytes) */
+static u16_t dhcp_option(u16_t options_out_len, u8_t *options, u8_t option_type, u8_t option_len);
+/* add option values */
+static u16_t dhcp_option_byte(u16_t options_out_len, u8_t *options, u8_t value);
+static u16_t dhcp_option_short(u16_t options_out_len, u8_t *options, u16_t value);
+static u16_t dhcp_option_long(u16_t options_out_len, u8_t *options, u32_t value);
+#if LWIP_NETIF_HOSTNAME
+static u16_t dhcp_option_hostname(u16_t options_out_len, u8_t *options, struct netif *netif);
+#endif /* LWIP_NETIF_HOSTNAME */
+/* always add the DHCP options trailer to end and pad */
+static void dhcp_option_trailer(u16_t options_out_len, u8_t *options, struct pbuf *p_out);
+
+/** Ensure DHCP PCB is allocated and bound */
+static err_t
+dhcp_inc_pcb_refcount(void)
+{
+  if (dhcp_pcb_refcount == 0) {
+    LWIP_ASSERT("dhcp_inc_pcb_refcount(): memory leak", dhcp_pcb == NULL);
+
+    /* allocate UDP PCB */
+    dhcp_pcb = udp_new();
+
+    if (dhcp_pcb == NULL) {
+      return ERR_MEM;
+    }
+
+    ip_set_option(dhcp_pcb, SOF_BROADCAST);
+
+    /* set up local and remote port for the pcb -> listen on all interfaces on all src/dest IPs */
+    udp_bind(dhcp_pcb, IP4_ADDR_ANY, LWIP_IANA_PORT_DHCP_CLIENT);
+    udp_connect(dhcp_pcb, IP4_ADDR_ANY, LWIP_IANA_PORT_DHCP_SERVER);
+    udp_recv(dhcp_pcb, dhcp_recv, NULL);
+  }
+
+  dhcp_pcb_refcount++;
+
+  return ERR_OK;
+}
+
+/** Free DHCP PCB if the last netif stops using it */
+static void
+dhcp_dec_pcb_refcount(void)
+{
+  LWIP_ASSERT("dhcp_pcb_refcount(): refcount error", (dhcp_pcb_refcount > 0));
+  dhcp_pcb_refcount--;
+
+  if (dhcp_pcb_refcount == 0) {
+    udp_remove(dhcp_pcb);
+    dhcp_pcb = NULL;
+  }
+}
+
+/**
+ * Back-off the DHCP client (because of a received NAK response).
+ *
+ * Back-off the DHCP client because of a received NAK. Receiving a
+ * NAK means the client asked for something non-sensible, for
+ * example when it tries to renew a lease obtained on another network.
+ *
+ * We clear any existing set IP address and restart DHCP negotiation
+ * afresh (as per RFC2131 3.2.3).
+ *
+ * @param netif the netif under DHCP control
+ */
+static void
+dhcp_handle_nak(struct netif *netif)
+{
+  struct dhcp *dhcp = netif_dhcp_data(netif);
+
+  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_handle_nak(netif=%p) %c%c%"U16_F"\n",
+              (void *)netif, netif->name[0], netif->name[1], (u16_t)netif->num));
+  /* Change to a defined state - set this before assigning the address
+     to ensure the callback can use dhcp_supplied_address() */
+  dhcp_set_state(dhcp, DHCP_STATE_BACKING_OFF);
+  /* remove IP address from interface (must no longer be used, as per RFC2131) */
+  netif_set_addr(netif, IP4_ADDR_ANY4, IP4_ADDR_ANY4, IP4_ADDR_ANY4);
+  /* We can immediately restart discovery */
+  dhcp_discover(netif);
+}
+
+#if LWIP_DHCP_DOES_ACD_CHECK
+/**
+* Handle conflict information from ACD module
+*
+* @param netif   network interface to handle conflict information on
+* @param state   acd_callback_enum_t
+ */
+static void
+dhcp_conflict_callback(struct netif *netif, acd_callback_enum_t state)
+{
+  struct dhcp *dhcp = netif_dhcp_data(netif);
+  u16_t msecs;
+
+  LWIP_ASSERT("DHCP should be enabled at this point, but it is not!",
+              (dhcp != NULL) && (dhcp->state != DHCP_STATE_OFF));
+
+  switch (state) {
+    case ACD_IP_OK:
+      dhcp_bind(netif);
+      break;
+    case ACD_RESTART_CLIENT:
+      /* wait 10s before restarting
+       * According to RFC2131 section 3.1 point 5:
+       * If the client detects that the address is already in use (e.g., through
+       * the use of ARP), the client MUST send a DHCPDECLINE message to the
+       * server and restarts the configuration process.  The client SHOULD wait
+       * a minimum of ten seconds before restarting the configuration process to
+       * avoid excessive network traffic in case of looping. */
+       dhcp_set_state(dhcp, DHCP_STATE_BACKING_OFF);
+       msecs = 10 * 1000;
+       dhcp->request_timeout = (u16_t)((msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS);
+       LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_decline(): set request timeout %"U16_F" msecs\n", msecs));
+      break;
+    case ACD_DECLINE:
+      /* remove IP address from interface
+       * (prevents routing from selecting this interface) */
+      netif_set_addr(netif, IP4_ADDR_ANY4, IP4_ADDR_ANY4, IP4_ADDR_ANY4);
+      /* Let the DHCP server know we will not use the address */
+      dhcp_decline(netif);
+      break;
+    default:
+      break;
+  }
+}
+
+/**
+ * Checks if the offered IP address is already in use.
+ *
+ * It does this according to the address conflict detection method described in
+ * RFC5227.
+ *
+ * @param netif the netif under DHCP control
+ */
+static void
+dhcp_check(struct netif *netif)
+{
+  struct dhcp *dhcp = netif_dhcp_data(netif);
+
+  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_check(netif=%p) %c%c\n", (void *)netif, (s16_t)netif->name[0],
+              (s16_t)netif->name[1]));
+  dhcp_set_state(dhcp, DHCP_STATE_CHECKING);
+
+  /* start ACD module */
+  acd_start(netif, &dhcp->acd, dhcp->offered_ip_addr);
+}
+#endif /* LWIP_DHCP_DOES_ACD_CHECK */
+
+/**
+ * Remember the configuration offered by a DHCP server.
+ *
+ * @param netif the netif under DHCP control
+ */
+static void
+dhcp_handle_offer(struct netif *netif, struct dhcp_msg *msg_in)
+{
+  struct dhcp *dhcp = netif_dhcp_data(netif);
+
+  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_handle_offer(netif=%p) %c%c%"U16_F"\n",
+              (void *)netif, netif->name[0], netif->name[1], (u16_t)netif->num));
+  /* obtain the server address */
+  if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_SERVER_ID)) {
+    dhcp->request_timeout = 0; /* stop timer */
+
+    ip_addr_set_ip4_u32(&dhcp->server_ip_addr, lwip_htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_SERVER_ID)));
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_handle_offer(): server 0x%08"X32_F"\n",
+                ip4_addr_get_u32(ip_2_ip4(&dhcp->server_ip_addr))));
+    /* remember offered address */
+    ip4_addr_copy(dhcp->offered_ip_addr, msg_in->yiaddr);
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_handle_offer(): offer for 0x%08"X32_F"\n",
+                ip4_addr_get_u32(&dhcp->offered_ip_addr)));
+
+    dhcp_select(netif);
+  } else {
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
+                ("dhcp_handle_offer(netif=%p) did not get server ID!\n", (void *)netif));
+  }
+}
+
+/**
+ * Select a DHCP server offer out of all offers.
+ *
+ * Simply select the first offer received.
+ *
+ * @param netif the netif under DHCP control
+ * @return lwIP specific error (see error.h)
+ */
+static err_t
+dhcp_select(struct netif *netif)
+{
+  struct dhcp *dhcp;
+  err_t result;
+  u16_t msecs;
+  u8_t i;
+  struct pbuf *p_out;
+  u16_t options_out_len;
+
+  LWIP_ERROR("dhcp_select: netif != NULL", (netif != NULL), return ERR_ARG;);
+  dhcp = netif_dhcp_data(netif);
+  LWIP_ERROR("dhcp_select: dhcp != NULL", (dhcp != NULL), return ERR_VAL;);
+
+  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_select(netif=%p) %c%c%"U16_F"\n", (void *)netif, netif->name[0], netif->name[1], (u16_t)netif->num));
+  dhcp_set_state(dhcp, DHCP_STATE_REQUESTING);
+
+  /* create and initialize the DHCP message header */
+  p_out = dhcp_create_msg(netif, dhcp, DHCP_REQUEST, &options_out_len);
+  if (p_out != NULL) {
+    struct dhcp_msg *msg_out = (struct dhcp_msg *)p_out->payload;
+    options_out_len = dhcp_option(options_out_len, msg_out->options, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
+    options_out_len = dhcp_option_short(options_out_len, msg_out->options, DHCP_MAX_MSG_LEN(netif));
+
+    /* MUST request the offered IP address */
+    options_out_len = dhcp_option(options_out_len, msg_out->options, DHCP_OPTION_REQUESTED_IP, 4);
+    options_out_len = dhcp_option_long(options_out_len, msg_out->options, lwip_ntohl(ip4_addr_get_u32(&dhcp->offered_ip_addr)));
+
+    options_out_len = dhcp_option(options_out_len, msg_out->options, DHCP_OPTION_SERVER_ID, 4);
+    options_out_len = dhcp_option_long(options_out_len, msg_out->options, lwip_ntohl(ip4_addr_get_u32(ip_2_ip4(&dhcp->server_ip_addr))));
+
+    options_out_len = dhcp_option(options_out_len, msg_out->options, DHCP_OPTION_PARAMETER_REQUEST_LIST, LWIP_ARRAYSIZE(dhcp_discover_request_options));
+    for (i = 0; i < LWIP_ARRAYSIZE(dhcp_discover_request_options); i++) {
+      options_out_len = dhcp_option_byte(options_out_len, msg_out->options, dhcp_discover_request_options[i]);
+    }
+
+#if LWIP_NETIF_HOSTNAME
+    options_out_len = dhcp_option_hostname(options_out_len, msg_out->options, netif);
+#endif /* LWIP_NETIF_HOSTNAME */
+
+    LWIP_HOOK_DHCP_APPEND_OPTIONS(netif, dhcp, DHCP_STATE_REQUESTING, msg_out, DHCP_REQUEST, &options_out_len);
+    dhcp_option_trailer(options_out_len, msg_out->options, p_out);
+
+    /* send broadcast to any DHCP server */
+    result = udp_sendto_if_src(dhcp_pcb, p_out, IP_ADDR_BROADCAST, LWIP_IANA_PORT_DHCP_SERVER, netif, IP4_ADDR_ANY);
+    pbuf_free(p_out);
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_select: REQUESTING\n"));
+  } else {
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("dhcp_select: could not allocate DHCP request\n"));
+    result = ERR_MEM;
+  }
+  if (dhcp->tries < 255) {
+    dhcp->tries++;
+  }
+  msecs = (u16_t)((dhcp->tries < 6 ? 1 << dhcp->tries : 60) * 1000);
+  dhcp->request_timeout = (u16_t)((msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS);
+  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_select(): set request timeout %"U16_F" msecs\n", msecs));
+  return result;
+}
+
+/**
+ * The DHCP timer that checks for lease renewal/rebind timeouts.
+ * Must be called once a minute (see @ref DHCP_COARSE_TIMER_SECS).
+ */
+void
+dhcp_coarse_tmr(void)
+{
+  struct netif *netif;
+  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_coarse_tmr()\n"));
+  /* iterate through all network interfaces */
+  NETIF_FOREACH(netif) {
+    /* only act on DHCP configured interfaces */
+    struct dhcp *dhcp = netif_dhcp_data(netif);
+    if ((dhcp != NULL) && (dhcp->state != DHCP_STATE_OFF)) {
+      /* compare lease time to expire timeout */
+      if (dhcp->t0_timeout && (++dhcp->lease_used == dhcp->t0_timeout)) {
+        LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_coarse_tmr(): t0 timeout\n"));
+        /* this clients' lease time has expired */
+        dhcp_release_and_stop(netif);
+        dhcp_start(netif);
+        /* timer is active (non zero), and triggers (zeroes) now? */
+      } else if (dhcp->t2_rebind_time && (dhcp->t2_rebind_time-- == 1)) {
+        LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_coarse_tmr(): t2 timeout\n"));
+        /* this clients' rebind timeout triggered */
+        dhcp_t2_timeout(netif);
+        /* timer is active (non zero), and triggers (zeroes) now */
+      } else if (dhcp->t1_renew_time && (dhcp->t1_renew_time-- == 1)) {
+        LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_coarse_tmr(): t1 timeout\n"));
+        /* this clients' renewal timeout triggered */
+        dhcp_t1_timeout(netif);
+      }
+    }
+  }
+}
+
+/**
+ * DHCP transaction timeout handling (this function must be called every 500ms,
+ * see @ref DHCP_FINE_TIMER_MSECS).
+ *
+ * A DHCP server is expected to respond within a short period of time.
+ * This timer checks whether an outstanding DHCP request is timed out.
+ */
+void
+dhcp_fine_tmr(void)
+{
+  struct netif *netif;
+  /* loop through netif's */
+  NETIF_FOREACH(netif) {
+    struct dhcp *dhcp = netif_dhcp_data(netif);
+    /* only act on DHCP configured interfaces */
+    if (dhcp != NULL) {
+      /* timer is active (non zero), and is about to trigger now */
+      if (dhcp->request_timeout > 1) {
+        dhcp->request_timeout--;
+      } else if (dhcp->request_timeout == 1) {
+        dhcp->request_timeout--;
+        /* { dhcp->request_timeout == 0 } */
+        LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_fine_tmr(): request timeout\n"));
+        /* this client's request timeout triggered */
+        dhcp_timeout(netif);
+      }
+    }
+  }
+}
+
+/**
+ * A DHCP negotiation transaction, or ARP request, has timed out.
+ *
+ * The timer that was started with the DHCP or ARP request has
+ * timed out, indicating no response was received in time.
+ *
+ * @param netif the netif under DHCP control
+ */
+static void
+dhcp_timeout(struct netif *netif)
+{
+  struct dhcp *dhcp = netif_dhcp_data(netif);
+
+  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_timeout()\n"));
+  /* back-off period has passed, or server selection timed out */
+  if ((dhcp->state == DHCP_STATE_BACKING_OFF) || (dhcp->state == DHCP_STATE_SELECTING)) {
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_timeout(): restarting discovery\n"));
+    dhcp_discover(netif);
+    /* receiving the requested lease timed out */
+  } else if (dhcp->state == DHCP_STATE_REQUESTING) {
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): REQUESTING, DHCP request timed out\n"));
+    if (dhcp->tries <= 5) {
+      dhcp_select(netif);
+    } else {
+      LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): REQUESTING, releasing, restarting\n"));
+      dhcp_release_and_stop(netif);
+      dhcp_start(netif);
+    }
+  } else if (dhcp->state == DHCP_STATE_REBOOTING) {
+    if (dhcp->tries < REBOOT_TRIES) {
+      dhcp_reboot(netif);
+    } else {
+      dhcp_discover(netif);
+    }
+  }
+}
+
+/**
+ * The renewal period has timed out.
+ *
+ * @param netif the netif under DHCP control
+ */
+static void
+dhcp_t1_timeout(struct netif *netif)
+{
+  struct dhcp *dhcp = netif_dhcp_data(netif);
+
+  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_t1_timeout()\n"));
+  if ((dhcp->state == DHCP_STATE_REQUESTING) || (dhcp->state == DHCP_STATE_BOUND) ||
+      (dhcp->state == DHCP_STATE_RENEWING)) {
+    /* just retry to renew - note that the rebind timer (t2) will
+     * eventually time-out if renew tries fail. */
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+                ("dhcp_t1_timeout(): must renew\n"));
+    /* This slightly different to RFC2131: DHCPREQUEST will be sent from state
+       DHCP_STATE_RENEWING, not DHCP_STATE_BOUND */
+    dhcp_renew(netif);
+    /* Calculate next timeout */
+    if (((dhcp->t2_timeout - dhcp->lease_used) / 2) >= DHCP_NEXT_TIMEOUT_THRESHOLD) {
+      dhcp->t1_renew_time = (dhcp_timeout_t)((dhcp->t2_timeout - dhcp->lease_used) / 2);
+    }
+  }
+}
+
+/**
+ * The rebind period has timed out.
+ *
+ * @param netif the netif under DHCP control
+ */
+static void
+dhcp_t2_timeout(struct netif *netif)
+{
+  struct dhcp *dhcp = netif_dhcp_data(netif);
+
+  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_t2_timeout()\n"));
+  if ((dhcp->state == DHCP_STATE_REQUESTING) || (dhcp->state == DHCP_STATE_BOUND) ||
+      (dhcp->state == DHCP_STATE_RENEWING) || (dhcp->state == DHCP_STATE_REBINDING)) {
+    /* just retry to rebind */
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+                ("dhcp_t2_timeout(): must rebind\n"));
+    /* This slightly different to RFC2131: DHCPREQUEST will be sent from state
+       DHCP_STATE_REBINDING, not DHCP_STATE_BOUND */
+    dhcp_rebind(netif);
+    /* Calculate next timeout */
+    if (((dhcp->t0_timeout - dhcp->lease_used) / 2) >= DHCP_NEXT_TIMEOUT_THRESHOLD) {
+      dhcp->t2_rebind_time = (dhcp_timeout_t)((dhcp->t0_timeout - dhcp->lease_used) / 2);
+    }
+  }
+}
+
+/**
+ * Handle a DHCP ACK packet
+ *
+ * @param netif the netif under DHCP control
+ */
+static void
+dhcp_handle_ack(struct netif *netif, struct dhcp_msg *msg_in)
+{
+  struct dhcp *dhcp = netif_dhcp_data(netif);
+
+#if LWIP_DHCP_PROVIDE_DNS_SERVERS || LWIP_DHCP_GET_NTP_SRV
+  u8_t n;
+#endif /* LWIP_DHCP_PROVIDE_DNS_SERVERS || LWIP_DHCP_GET_NTP_SRV */
+#if LWIP_DHCP_GET_NTP_SRV
+  ip4_addr_t ntp_server_addrs[LWIP_DHCP_MAX_NTP_SERVERS];
+#endif
+
+  /* clear options we might not get from the ACK */
+  ip4_addr_set_zero(&dhcp->offered_sn_mask);
+  ip4_addr_set_zero(&dhcp->offered_gw_addr);
+#if LWIP_DHCP_BOOTP_FILE
+  ip4_addr_set_zero(&dhcp->offered_si_addr);
+#endif /* LWIP_DHCP_BOOTP_FILE */
+
+  /* lease time given? */
+  if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_LEASE_TIME)) {
+    /* remember offered lease time */
+    dhcp->offered_t0_lease = dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_LEASE_TIME);
+  }
+  /* renewal period given? */
+  if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_T1)) {
+    /* remember given renewal period */
+    dhcp->offered_t1_renew = dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_T1);
+  } else {
+    /* calculate safe periods for renewal */
+    dhcp->offered_t1_renew = dhcp->offered_t0_lease / 2;
+  }
+
+  /* renewal period given? */
+  if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_T2)) {
+    /* remember given rebind period */
+    dhcp->offered_t2_rebind = dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_T2);
+  } else {
+    /* calculate safe periods for rebinding (offered_t0_lease * 0.875 -> 87.5%)*/
+    dhcp->offered_t2_rebind = (dhcp->offered_t0_lease * 7U) / 8U;
+  }
+
+  /* (y)our internet address */
+  ip4_addr_copy(dhcp->offered_ip_addr, msg_in->yiaddr);
+
+#if LWIP_DHCP_BOOTP_FILE
+  /* copy boot server address,
+     boot file name copied in dhcp_parse_reply if not overloaded */
+  ip4_addr_copy(dhcp->offered_si_addr, msg_in->siaddr);
+#endif /* LWIP_DHCP_BOOTP_FILE */
+
+  /* subnet mask given? */
+  if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_SUBNET_MASK)) {
+    /* remember given subnet mask */
+    ip4_addr_set_u32(&dhcp->offered_sn_mask, lwip_htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_SUBNET_MASK)));
+    dhcp->flags |= DHCP_FLAG_SUBNET_MASK_GIVEN;
+  } else {
+    dhcp->flags &= ~DHCP_FLAG_SUBNET_MASK_GIVEN;
+  }
+
+  /* gateway router */
+  if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_ROUTER)) {
+    ip4_addr_set_u32(&dhcp->offered_gw_addr, lwip_htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_ROUTER)));
+  }
+
+#if LWIP_DHCP_GET_NTP_SRV
+  /* NTP servers */
+  for (n = 0; (n < LWIP_DHCP_MAX_NTP_SERVERS) && dhcp_option_given(dhcp, DHCP_OPTION_IDX_NTP_SERVER + n); n++) {
+    ip4_addr_set_u32(&ntp_server_addrs[n], lwip_htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_NTP_SERVER + n)));
+  }
+  dhcp_set_ntp_servers(n, ntp_server_addrs);
+#endif /* LWIP_DHCP_GET_NTP_SRV */
+
+#if LWIP_DHCP_PROVIDE_DNS_SERVERS
+  /* DNS servers */
+  for (n = 0; (n < LWIP_DHCP_PROVIDE_DNS_SERVERS) && dhcp_option_given(dhcp, DHCP_OPTION_IDX_DNS_SERVER + n); n++) {
+    ip_addr_t dns_addr;
+    ip_addr_set_ip4_u32_val(dns_addr, lwip_htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_DNS_SERVER + n)));
+    dns_setserver(n, &dns_addr);
+  }
+#endif /* LWIP_DHCP_PROVIDE_DNS_SERVERS */
+}
+
+/**
+ * @ingroup dhcp4
+ * Set a statically allocated struct dhcp to work with.
+ * Using this prevents dhcp_start to allocate it using mem_malloc.
+ *
+ * @param netif the netif for which to set the struct dhcp
+ * @param dhcp (uninitialised) dhcp struct allocated by the application
+ */
+void
+dhcp_set_struct(struct netif *netif, struct dhcp *dhcp)
+{
+  LWIP_ASSERT_CORE_LOCKED();
+  LWIP_ASSERT("netif != NULL", netif != NULL);
+  LWIP_ASSERT("dhcp != NULL", dhcp != NULL);
+  LWIP_ASSERT("netif already has a struct dhcp set", netif_dhcp_data(netif) == NULL);
+
+  /* clear data structure */
+  memset(dhcp, 0, sizeof(struct dhcp));
+  /* mark this as externally allocated */
+  dhcp->flags |= DHCP_FLAG_EXTERNAL_MEM;
+  /* dhcp_set_state(&dhcp, DHCP_STATE_OFF); */
+  netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP, dhcp);
+}
+
+/**
+ * @ingroup dhcp4
+ * Removes a struct dhcp from a netif.
+ *
+ * ATTENTION: Only use this when not using dhcp_set_struct() to allocate the
+ *            struct dhcp since the memory is passed back to the heap.
+ *
+ * @param netif the netif from which to remove the struct dhcp
+ */
+void dhcp_cleanup(struct netif *netif)
+{
+  struct dhcp *dhcp;
+  LWIP_ASSERT_CORE_LOCKED();
+  LWIP_ASSERT("netif != NULL", netif != NULL);
+
+  dhcp = netif_dhcp_data(netif);
+  if (dhcp != NULL) {
+    if (!(dhcp->flags & DHCP_FLAG_EXTERNAL_MEM)) {
+      mem_free(dhcp);
+    }
+    netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP, NULL);
+  }
+}
+
+/**
+ * @ingroup dhcp4
+ * Start DHCP negotiation for a network interface.
+ *
+ * If no DHCP client instance was attached to this interface,
+ * a new client is created first. If a DHCP client instance
+ * was already present, it restarts negotiation.
+ *
+ * @param netif The lwIP network interface
+ * @return lwIP error code
+ * - ERR_OK - No error
+ * - ERR_MEM - Out of memory
+ */
+err_t
+dhcp_start(struct netif *netif)
+{
+  struct dhcp *dhcp;
+  err_t result;
+
+  LWIP_ASSERT_CORE_LOCKED();
+  LWIP_ERROR("netif != NULL", (netif != NULL), return ERR_ARG;);
+  LWIP_ERROR("netif is not up, old style port?", netif_is_up(netif), return ERR_ARG;);
+  dhcp = netif_dhcp_data(netif);
+  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_start(netif=%p) %c%c%"U16_F"\n", (void *)netif, netif->name[0], netif->name[1], (u16_t)netif->num));
+
+  /* check MTU of the netif */
+  if (netif->mtu < DHCP_MAX_MSG_LEN_MIN_REQUIRED) {
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): Cannot use this netif with DHCP: MTU is too small\n"));
+    return ERR_MEM;
+  }
+
+  /* no DHCP client attached yet? */
+  if (dhcp == NULL) {
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): mallocing new DHCP client\n"));
+    dhcp = (struct dhcp *)mem_malloc(sizeof(struct dhcp));
+    if (dhcp == NULL) {
+      LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): could not allocate dhcp\n"));
+      return ERR_MEM;
+    }
+
+    /* store this dhcp client in the netif */
+    netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP, dhcp);
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): allocated dhcp\n"));
+    /* already has DHCP client attached */
+  } else {
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_start(): restarting DHCP configuration\n"));
+
+    if (dhcp->pcb_allocated != 0) {
+      dhcp_dec_pcb_refcount(); /* free DHCP PCB if not needed any more */
+    }
+    /* dhcp is cleared below, no need to reset flag*/
+  }
+
+  /* clear data structure */
+  memset(dhcp, 0, sizeof(struct dhcp));
+  /* dhcp_set_state(&dhcp, DHCP_STATE_OFF); */
+
+
+#if LWIP_DHCP_DOES_ACD_CHECK
+  /* add acd struct to list*/
+  acd_add(netif, &dhcp->acd, dhcp_conflict_callback);
+#endif /* LWIP_DHCP_DOES_ACD_CHECK */
+
+
+  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): starting DHCP configuration\n"));
+
+  if (dhcp_inc_pcb_refcount() != ERR_OK) { /* ensure DHCP PCB is allocated */
+    return ERR_MEM;
+  }
+  dhcp->pcb_allocated = 1;
+
+  if (!netif_is_link_up(netif)) {
+    /* set state INIT and wait for dhcp_network_changed() to call dhcp_discover() */
+    dhcp_set_state(dhcp, DHCP_STATE_INIT);
+    return ERR_OK;
+  }
+
+  /* (re)start the DHCP negotiation */
+  result = dhcp_discover(netif);
+  if (result != ERR_OK) {
+    /* free resources allocated above */
+    dhcp_release_and_stop(netif);
+    return ERR_MEM;
+  }
+  return result;
+}
+
+/**
+ * @ingroup dhcp4
+ * Inform a DHCP server of our manual configuration.
+ *
+ * This informs DHCP servers of our fixed IP address configuration
+ * by sending an INFORM message. It does not involve DHCP address
+ * configuration, it is just here to be nice to the network.
+ *
+ * @param netif The lwIP network interface
+ */
+void
+dhcp_inform(struct netif *netif)
+{
+  struct dhcp dhcp;
+  struct pbuf *p_out;
+  u16_t options_out_len;
+
+  LWIP_ASSERT_CORE_LOCKED();
+  LWIP_ERROR("netif != NULL", (netif != NULL), return;);
+
+  if (dhcp_inc_pcb_refcount() != ERR_OK) { /* ensure DHCP PCB is allocated */
+    return;
+  }
+
+  memset(&dhcp, 0, sizeof(struct dhcp));
+  dhcp_set_state(&dhcp, DHCP_STATE_INFORMING);
+
+  /* create and initialize the DHCP message header */
+  p_out = dhcp_create_msg(netif, &dhcp, DHCP_INFORM, &options_out_len);
+  if (p_out != NULL) {
+    struct dhcp_msg *msg_out = (struct dhcp_msg *)p_out->payload;
+    options_out_len = dhcp_option(options_out_len, msg_out->options, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
+    options_out_len = dhcp_option_short(options_out_len, msg_out->options, DHCP_MAX_MSG_LEN(netif));
+
+    LWIP_HOOK_DHCP_APPEND_OPTIONS(netif, &dhcp, DHCP_STATE_INFORMING, msg_out, DHCP_INFORM, &options_out_len);
+    dhcp_option_trailer(options_out_len, msg_out->options, p_out);
+
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_inform: INFORMING\n"));
+
+    udp_sendto_if(dhcp_pcb, p_out, IP_ADDR_BROADCAST, LWIP_IANA_PORT_DHCP_SERVER, netif);
+
+    pbuf_free(p_out);
+  } else {
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_inform: could not allocate DHCP request\n"));
+  }
+
+  dhcp_dec_pcb_refcount(); /* delete DHCP PCB if not needed any more */
+}
+
+/** Handle a possible change in the network configuration.
+ *
+ * This enters the REBOOTING state to verify that the currently bound
+ * address is still valid.
+ */
+void
+dhcp_network_changed_link_up(struct netif *netif)
+{
+  struct dhcp *dhcp = netif_dhcp_data(netif);
+
+  if (!dhcp) {
+    return;
+  }
+  switch (dhcp->state) {
+    case DHCP_STATE_REBINDING:
+    case DHCP_STATE_RENEWING:
+    case DHCP_STATE_BOUND:
+    case DHCP_STATE_REBOOTING:
+      dhcp->tries = 0;
+      dhcp_reboot(netif);
+      break;
+    case DHCP_STATE_OFF:
+      /* stay off */
+      break;
+    default:
+      LWIP_ASSERT("invalid dhcp->state", dhcp->state <= DHCP_STATE_BACKING_OFF);
+      /* INIT/REQUESTING/CHECKING/BACKING_OFF restart with new 'rid' because the
+         state changes, SELECTING: continue with current 'rid' as we stay in the
+         same state */
+      /* ensure we start with short timeouts, even if already discovering */
+      dhcp->tries = 0;
+      dhcp_discover(netif);
+      break;
+  }
+}
+
+#if LWIP_DHCP_DOES_ACD_CHECK
+/**
+ * Decline an offered lease.
+ *
+ * Tell the DHCP server we do not accept the offered address.
+ * One reason to decline the lease is when we find out the address
+ * is already in use by another host (through ARP).
+ *
+ * @param netif the netif under DHCP control
+ */
+static err_t
+dhcp_decline(struct netif *netif)
+{
+  struct dhcp *dhcp = netif_dhcp_data(netif);
+  err_t result;
+  struct pbuf *p_out;
+  u16_t options_out_len;
+
+  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_decline()\n"));
+  dhcp_set_state(dhcp, DHCP_STATE_BACKING_OFF);
+
+  /* create and initialize the DHCP message header */
+  p_out = dhcp_create_msg(netif, dhcp, DHCP_DECLINE, &options_out_len);
+  if (p_out != NULL) {
+    struct dhcp_msg *msg_out = (struct dhcp_msg *)p_out->payload;
+    options_out_len = dhcp_option(options_out_len, msg_out->options, DHCP_OPTION_REQUESTED_IP, 4);
+    options_out_len = dhcp_option_long(options_out_len, msg_out->options, lwip_ntohl(ip4_addr_get_u32(&dhcp->offered_ip_addr)));
+
+    LWIP_HOOK_DHCP_APPEND_OPTIONS(netif, dhcp, DHCP_STATE_BACKING_OFF, msg_out, DHCP_DECLINE, &options_out_len);
+    dhcp_option_trailer(options_out_len, msg_out->options, p_out);
+
+    /* per section 4.4.4, broadcast DECLINE messages */
+    result = udp_sendto_if_src(dhcp_pcb, p_out, IP_ADDR_BROADCAST, LWIP_IANA_PORT_DHCP_SERVER, netif, IP4_ADDR_ANY);
+    pbuf_free(p_out);
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_decline: BACKING OFF\n"));
+  } else {
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
+                ("dhcp_decline: could not allocate DHCP request\n"));
+    result = ERR_MEM;
+  }
+  return result;
+}
+#endif /* LWIP_DHCP_DOES_ACD_CHECK */
+
+
+/**
+ * Start the DHCP process, discover a DHCP server.
+ *
+ * @param netif the netif under DHCP control
+ */
+static err_t
+dhcp_discover(struct netif *netif)
+{
+  struct dhcp *dhcp = netif_dhcp_data(netif);
+  err_t result = ERR_OK;
+  u16_t msecs;
+  u8_t i;
+  struct pbuf *p_out;
+  u16_t options_out_len;
+
+  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover()\n"));
+
+#if LWIP_DHCP_AUTOIP_COOP
+  if (dhcp->tries >= LWIP_DHCP_AUTOIP_COOP_TRIES) {
+    autoip_start(netif);
+  }
+#endif /* LWIP_DHCP_AUTOIP_COOP */
+
+  ip4_addr_set_any(&dhcp->offered_ip_addr);
+  dhcp_set_state(dhcp, DHCP_STATE_SELECTING);
+  /* create and initialize the DHCP message header */
+  p_out = dhcp_create_msg(netif, dhcp, DHCP_DISCOVER, &options_out_len);
+  if (p_out != NULL) {
+    struct dhcp_msg *msg_out = (struct dhcp_msg *)p_out->payload;
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: making request\n"));
+
+    options_out_len = dhcp_option(options_out_len, msg_out->options, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
+    options_out_len = dhcp_option_short(options_out_len, msg_out->options, DHCP_MAX_MSG_LEN(netif));
+
+#if LWIP_NETIF_HOSTNAME && LWIP_DHCP_DISCOVER_ADD_HOSTNAME
+    options_out_len = dhcp_option_hostname(options_out_len, msg_out->options, netif);
+#endif /* LWIP NETIF HOSTNAME && LWIP_DHCP_DISCOVER_ADD_HOSTNAME */
+
+    options_out_len = dhcp_option(options_out_len, msg_out->options, DHCP_OPTION_PARAMETER_REQUEST_LIST, LWIP_ARRAYSIZE(dhcp_discover_request_options));
+    for (i = 0; i < LWIP_ARRAYSIZE(dhcp_discover_request_options); i++) {
+      options_out_len = dhcp_option_byte(options_out_len, msg_out->options, dhcp_discover_request_options[i]);
+    }
+    LWIP_HOOK_DHCP_APPEND_OPTIONS(netif, dhcp, DHCP_STATE_SELECTING, msg_out, DHCP_DISCOVER, &options_out_len);
+    dhcp_option_trailer(options_out_len, msg_out->options, p_out);
+
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: sendto(DISCOVER, IP_ADDR_BROADCAST, LWIP_IANA_PORT_DHCP_SERVER)\n"));
+    udp_sendto_if_src(dhcp_pcb, p_out, IP_ADDR_BROADCAST, LWIP_IANA_PORT_DHCP_SERVER, netif, IP4_ADDR_ANY);
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: deleting()\n"));
+    pbuf_free(p_out);
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_discover: SELECTING\n"));
+  } else {
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_discover: could not allocate DHCP request\n"));
+  }
+
+  if (dhcp->tries < 255) {
+    dhcp->tries++;
+  }
+  msecs = DHCP_REQUEST_BACKOFF_SEQUENCE(dhcp->tries);
+  dhcp->request_timeout = (u16_t)((msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS);
+  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_discover(): set request timeout %"U16_F" msecs\n", msecs));
+  return result;
+}
+
+
+/**
+ * Bind the interface to the offered IP address.
+ *
+ * @param netif network interface to bind to the offered address
+ */
+static void
+dhcp_bind(struct netif *netif)
+{
+  struct dhcp *dhcp;
+  ip4_addr_t sn_mask, gw_addr;
+  LWIP_ERROR("dhcp_bind: netif != NULL", (netif != NULL), return;);
+  dhcp = netif_dhcp_data(netif);
+  LWIP_ERROR("dhcp_bind: dhcp != NULL", (dhcp != NULL), return;);
+  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_bind(netif=%p) %c%c%"U16_F"\n", (void *)netif, netif->name[0], netif->name[1], (u16_t)netif->num));
+
+  /* reset time used of lease */
+  dhcp->lease_used = 0;
+
+  if (dhcp->offered_t0_lease != 0xffffffffUL) {
+    /* set renewal period timer */
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_bind(): t0 renewal timer %"U32_F" secs\n", dhcp->offered_t0_lease));
+    DHCP_SET_TIMEOUT_FROM_OFFERED_T0_LEASE(dhcp->t0_timeout, dhcp);
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_bind(): set request timeout %"U32_F" msecs\n", dhcp->offered_t0_lease * 1000));
+  }
+
+  /* temporary DHCP lease? */
+  if (dhcp->offered_t1_renew != 0xffffffffUL) {
+    /* set renewal period timer */
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_bind(): t1 renewal timer %"U32_F" secs\n", dhcp->offered_t1_renew));
+    DHCP_SET_TIMEOUT_FROM_OFFERED_T1_RENEW(dhcp->t1_timeout, dhcp);
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_bind(): set request timeout %"U32_F" msecs\n", dhcp->offered_t1_renew * 1000));
+    dhcp->t1_renew_time = dhcp->t1_timeout;
+  }
+  /* set renewal period timer */
+  if (dhcp->offered_t2_rebind != 0xffffffffUL) {
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_bind(): t2 rebind timer %"U32_F" secs\n", dhcp->offered_t2_rebind));
+    DHCP_SET_TIMEOUT_FROM_OFFERED_T2_REBIND(dhcp->t2_timeout, dhcp);
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_bind(): set request timeout %"U32_F" msecs\n", dhcp->offered_t2_rebind * 1000));
+    dhcp->t2_rebind_time = dhcp->t2_timeout;
+  }
+
+  /* If we have sub 1 minute lease, t2 and t1 will kick in at the same time. */
+  if ((dhcp->t1_timeout >= dhcp->t2_timeout) && (dhcp->t2_timeout > 0)) {
+    dhcp->t1_timeout = 0;
+  }
+
+  if (dhcp->flags & DHCP_FLAG_SUBNET_MASK_GIVEN) {
+    /* copy offered network mask */
+    ip4_addr_copy(sn_mask, dhcp->offered_sn_mask);
+  } else {
+    /* subnet mask not given, choose a safe subnet mask given the network class */
+    u8_t first_octet = ip4_addr1(&dhcp->offered_ip_addr);
+    if (first_octet <= 127) {
+      ip4_addr_set_u32(&sn_mask, PP_HTONL(0xff000000UL));
+    } else if (first_octet >= 192) {
+      ip4_addr_set_u32(&sn_mask, PP_HTONL(0xffffff00UL));
+    } else {
+      ip4_addr_set_u32(&sn_mask, PP_HTONL(0xffff0000UL));
+    }
+  }
+
+  ip4_addr_copy(gw_addr, dhcp->offered_gw_addr);
+
+  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_bind(): IP: 0x%08"X32_F" SN: 0x%08"X32_F" GW: 0x%08"X32_F"\n",
+              ip4_addr_get_u32(&dhcp->offered_ip_addr), ip4_addr_get_u32(&sn_mask), ip4_addr_get_u32(&gw_addr)));
+  /* netif is now bound to DHCP leased address - set this before assigning the address
+     to ensure the callback can use dhcp_supplied_address() */
+  dhcp_set_state(dhcp, DHCP_STATE_BOUND);
+
+  netif_set_addr(netif, &dhcp->offered_ip_addr, &sn_mask, &gw_addr);
+  /* interface is used by routing now that an address is set */
+}
+
+/**
+ * @ingroup dhcp4
+ * Renew an existing DHCP lease at the involved DHCP server.
+ *
+ * @param netif network interface which must renew its lease
+ */
+err_t
+dhcp_renew(struct netif *netif)
+{
+  struct dhcp *dhcp = netif_dhcp_data(netif);
+  err_t result;
+  u16_t msecs;
+  u8_t i;
+  struct pbuf *p_out;
+  u16_t options_out_len;
+
+  LWIP_ASSERT_CORE_LOCKED();
+  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_renew()\n"));
+  dhcp_set_state(dhcp, DHCP_STATE_RENEWING);
+
+  /* create and initialize the DHCP message header */
+  p_out = dhcp_create_msg(netif, dhcp, DHCP_REQUEST, &options_out_len);
+  if (p_out != NULL) {
+    struct dhcp_msg *msg_out = (struct dhcp_msg *)p_out->payload;
+    options_out_len = dhcp_option(options_out_len, msg_out->options, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
+    options_out_len = dhcp_option_short(options_out_len, msg_out->options, DHCP_MAX_MSG_LEN(netif));
+
+    options_out_len = dhcp_option(options_out_len, msg_out->options, DHCP_OPTION_PARAMETER_REQUEST_LIST, LWIP_ARRAYSIZE(dhcp_discover_request_options));
+    for (i = 0; i < LWIP_ARRAYSIZE(dhcp_discover_request_options); i++) {
+      options_out_len = dhcp_option_byte(options_out_len, msg_out->options, dhcp_discover_request_options[i]);
+    }
+
+#if LWIP_NETIF_HOSTNAME
+    options_out_len = dhcp_option_hostname(options_out_len, msg_out->options, netif);
+#endif /* LWIP_NETIF_HOSTNAME */
+
+    LWIP_HOOK_DHCP_APPEND_OPTIONS(netif, dhcp, DHCP_STATE_RENEWING, msg_out, DHCP_REQUEST, &options_out_len);
+    dhcp_option_trailer(options_out_len, msg_out->options, p_out);
+
+    result = udp_sendto_if(dhcp_pcb, p_out, &dhcp->server_ip_addr, LWIP_IANA_PORT_DHCP_SERVER, netif);
+    pbuf_free(p_out);
+
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_renew: RENEWING\n"));
+  } else {
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_renew: could not allocate DHCP request\n"));
+    result = ERR_MEM;
+  }
+  if (dhcp->tries < 255) {
+    dhcp->tries++;
+  }
+  /* back-off on retries, but to a maximum of 20 seconds */
+  msecs = (u16_t)(dhcp->tries < 10 ? dhcp->tries * 2000 : 20 * 1000);
+  dhcp->request_timeout = (u16_t)((msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS);
+  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_renew(): set request timeout %"U16_F" msecs\n", msecs));
+  return result;
+}
+
+/**
+ * Rebind with a DHCP server for an existing DHCP lease.
+ *
+ * @param netif network interface which must rebind with a DHCP server
+ */
+static err_t
+dhcp_rebind(struct netif *netif)
+{
+  struct dhcp *dhcp = netif_dhcp_data(netif);
+  err_t result;
+  u16_t msecs;
+  u8_t i;
+  struct pbuf *p_out;
+  u16_t options_out_len;
+
+  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_rebind()\n"));
+  dhcp_set_state(dhcp, DHCP_STATE_REBINDING);
+
+  /* create and initialize the DHCP message header */
+  p_out = dhcp_create_msg(netif, dhcp, DHCP_REQUEST, &options_out_len);
+  if (p_out != NULL) {
+    struct dhcp_msg *msg_out = (struct dhcp_msg *)p_out->payload;
+    options_out_len = dhcp_option(options_out_len, msg_out->options, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
+    options_out_len = dhcp_option_short(options_out_len, msg_out->options, DHCP_MAX_MSG_LEN(netif));
+
+    options_out_len = dhcp_option(options_out_len, msg_out->options, DHCP_OPTION_PARAMETER_REQUEST_LIST, LWIP_ARRAYSIZE(dhcp_discover_request_options));
+    for (i = 0; i < LWIP_ARRAYSIZE(dhcp_discover_request_options); i++) {
+      options_out_len = dhcp_option_byte(options_out_len, msg_out->options, dhcp_discover_request_options[i]);
+    }
+
+#if LWIP_NETIF_HOSTNAME
+    options_out_len = dhcp_option_hostname(options_out_len, msg_out->options, netif);
+#endif /* LWIP_NETIF_HOSTNAME */
+
+    LWIP_HOOK_DHCP_APPEND_OPTIONS(netif, dhcp, DHCP_STATE_REBINDING, msg_out, DHCP_DISCOVER, &options_out_len);
+    dhcp_option_trailer(options_out_len, msg_out->options, p_out);
+
+    /* broadcast to server */
+    result = udp_sendto_if(dhcp_pcb, p_out, IP_ADDR_BROADCAST, LWIP_IANA_PORT_DHCP_SERVER, netif);
+    pbuf_free(p_out);
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_rebind: REBINDING\n"));
+  } else {
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_rebind: could not allocate DHCP request\n"));
+    result = ERR_MEM;
+  }
+  if (dhcp->tries < 255) {
+    dhcp->tries++;
+  }
+  msecs = (u16_t)(dhcp->tries < 10 ? dhcp->tries * 1000 : 10 * 1000);
+  dhcp->request_timeout = (u16_t)((msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS);
+  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_rebind(): set request timeout %"U16_F" msecs\n", msecs));
+  return result;
+}
+
+/**
+ * Enter REBOOTING state to verify an existing lease
+ *
+ * @param netif network interface which must reboot
+ */
+static err_t
+dhcp_reboot(struct netif *netif)
+{
+  struct dhcp *dhcp = netif_dhcp_data(netif);
+  err_t result;
+  u16_t msecs;
+  u8_t i;
+  struct pbuf *p_out;
+  u16_t options_out_len;
+
+  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_reboot()\n"));
+  dhcp_set_state(dhcp, DHCP_STATE_REBOOTING);
+
+  /* create and initialize the DHCP message header */
+  p_out = dhcp_create_msg(netif, dhcp, DHCP_REQUEST, &options_out_len);
+  if (p_out != NULL) {
+    struct dhcp_msg *msg_out = (struct dhcp_msg *)p_out->payload;
+    options_out_len = dhcp_option(options_out_len, msg_out->options, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
+    options_out_len = dhcp_option_short(options_out_len, msg_out->options, DHCP_MAX_MSG_LEN_MIN_REQUIRED);
+
+    options_out_len = dhcp_option(options_out_len, msg_out->options, DHCP_OPTION_REQUESTED_IP, 4);
+    options_out_len = dhcp_option_long(options_out_len, msg_out->options, lwip_ntohl(ip4_addr_get_u32(&dhcp->offered_ip_addr)));
+
+    options_out_len = dhcp_option(options_out_len, msg_out->options, DHCP_OPTION_PARAMETER_REQUEST_LIST, LWIP_ARRAYSIZE(dhcp_discover_request_options));
+    for (i = 0; i < LWIP_ARRAYSIZE(dhcp_discover_request_options); i++) {
+      options_out_len = dhcp_option_byte(options_out_len, msg_out->options, dhcp_discover_request_options[i]);
+    }
+
+#if LWIP_NETIF_HOSTNAME
+    options_out_len = dhcp_option_hostname(options_out_len, msg_out->options, netif);
+#endif /* LWIP_NETIF_HOSTNAME */
+
+    LWIP_HOOK_DHCP_APPEND_OPTIONS(netif, dhcp, DHCP_STATE_REBOOTING, msg_out, DHCP_REQUEST, &options_out_len);
+    dhcp_option_trailer(options_out_len, msg_out->options, p_out);
+
+    /* broadcast to server */
+    result = udp_sendto_if(dhcp_pcb, p_out, IP_ADDR_BROADCAST, LWIP_IANA_PORT_DHCP_SERVER, netif);
+    pbuf_free(p_out);
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_reboot: REBOOTING\n"));
+  } else {
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_reboot: could not allocate DHCP request\n"));
+    result = ERR_MEM;
+  }
+  if (dhcp->tries < 255) {
+    dhcp->tries++;
+  }
+  msecs = (u16_t)(dhcp->tries < 10 ? dhcp->tries * 1000 : 10 * 1000);
+  dhcp->request_timeout = (u16_t)((msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS);
+  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_reboot(): set request timeout %"U16_F" msecs\n", msecs));
+  return result;
+}
+
+/**
+ * @ingroup dhcp4
+ * Release a DHCP lease and stop DHCP statemachine (and AUTOIP if LWIP_DHCP_AUTOIP_COOP).
+ *
+ * @param netif network interface
+ */
+void
+dhcp_release_and_stop(struct netif *netif)
+{
+  struct dhcp *dhcp = netif_dhcp_data(netif);
+  ip_addr_t server_ip_addr;
+
+  LWIP_ASSERT_CORE_LOCKED();
+  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_release_and_stop()\n"));
+  if (dhcp == NULL) {
+    return;
+  }
+
+  /* already off? -> nothing to do */
+  if (dhcp->state == DHCP_STATE_OFF) {
+    return;
+  }
+
+  ip_addr_copy(server_ip_addr, dhcp->server_ip_addr);
+
+  /* clean old DHCP offer */
+  ip_addr_set_zero_ip4(&dhcp->server_ip_addr);
+  ip4_addr_set_zero(&dhcp->offered_ip_addr);
+  ip4_addr_set_zero(&dhcp->offered_sn_mask);
+  ip4_addr_set_zero(&dhcp->offered_gw_addr);
+#if LWIP_DHCP_BOOTP_FILE
+  ip4_addr_set_zero(&dhcp->offered_si_addr);
+#endif /* LWIP_DHCP_BOOTP_FILE */
+  dhcp->offered_t0_lease = dhcp->offered_t1_renew = dhcp->offered_t2_rebind = 0;
+  dhcp->t1_renew_time = dhcp->t2_rebind_time = dhcp->lease_used = dhcp->t0_timeout = 0;
+
+  /* send release message when current IP was assigned via DHCP */
+  if (dhcp_supplied_address(netif)) {
+    /* create and initialize the DHCP message header */
+    struct pbuf *p_out;
+    u16_t options_out_len;
+    dhcp_set_state(dhcp, DHCP_STATE_OFF);
+    p_out = dhcp_create_msg(netif, dhcp, DHCP_RELEASE, &options_out_len);
+    if (p_out != NULL) {
+      struct dhcp_msg *msg_out = (struct dhcp_msg *)p_out->payload;
+      options_out_len = dhcp_option(options_out_len, msg_out->options, DHCP_OPTION_SERVER_ID, 4);
+      options_out_len = dhcp_option_long(options_out_len, msg_out->options, lwip_ntohl(ip4_addr_get_u32(ip_2_ip4(&server_ip_addr))));
+
+      LWIP_HOOK_DHCP_APPEND_OPTIONS(netif, dhcp, dhcp->state, msg_out, DHCP_RELEASE, &options_out_len);
+      dhcp_option_trailer(options_out_len, msg_out->options, p_out);
+
+      udp_sendto_if(dhcp_pcb, p_out, &server_ip_addr, LWIP_IANA_PORT_DHCP_SERVER, netif);
+      pbuf_free(p_out);
+      LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_release: RELEASED, DHCP_STATE_OFF\n"));
+    } else {
+      /* sending release failed, but that's not a problem since the correct behaviour of dhcp does not rely on release */
+      LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_release: could not allocate DHCP request\n"));
+    }
+
+    /* remove IP address from interface (prevents routing from selecting this interface) */
+    netif_set_addr(netif, IP4_ADDR_ANY4, IP4_ADDR_ANY4, IP4_ADDR_ANY4);
+  } else {
+     dhcp_set_state(dhcp, DHCP_STATE_OFF);
+  }
+
+#if LWIP_DHCP_DOES_ACD_CHECK
+  /* stop acd because we may be in checking state and the callback would trigger a bind */
+  acd_remove(netif, &dhcp->acd);
+#endif
+
+  if (dhcp->pcb_allocated != 0) {
+    dhcp_dec_pcb_refcount(); /* free DHCP PCB if not needed any more */
+    dhcp->pcb_allocated = 0;
+  }
+}
+
+/**
+ * @ingroup dhcp4
+ * This function calls dhcp_release_and_stop() internally.
+ * @deprecated Use dhcp_release_and_stop() instead.
+ */
+err_t
+dhcp_release(struct netif *netif)
+{
+  dhcp_release_and_stop(netif);
+  return ERR_OK;
+}
+
+/**
+ * @ingroup dhcp4
+ * This function calls dhcp_release_and_stop() internally.
+ * @deprecated Use dhcp_release_and_stop() instead.
+ */
+void
+dhcp_stop(struct netif *netif)
+{
+  dhcp_release_and_stop(netif);
+}
+
+/*
+ * Set the DHCP state of a DHCP client.
+ *
+ * If the state changed, reset the number of tries.
+ */
+static void
+dhcp_set_state(struct dhcp *dhcp, u8_t new_state)
+{
+  if (new_state != dhcp->state) {
+    dhcp->state = new_state;
+    dhcp->tries = 0;
+    dhcp->request_timeout = 0;
+  }
+}
+
+/*
+ * Concatenate an option type and length field to the outgoing
+ * DHCP message.
+ *
+ */
+static u16_t
+dhcp_option(u16_t options_out_len, u8_t *options, u8_t option_type, u8_t option_len)
+{
+  LWIP_ASSERT("dhcp_option: options_out_len + 2 + option_len <= DHCP_OPTIONS_LEN", options_out_len + 2U + option_len <= DHCP_OPTIONS_LEN);
+  options[options_out_len++] = option_type;
+  options[options_out_len++] = option_len;
+  return options_out_len;
+}
+/*
+ * Concatenate a single byte to the outgoing DHCP message.
+ *
+ */
+static u16_t
+dhcp_option_byte(u16_t options_out_len, u8_t *options, u8_t value)
+{
+  LWIP_ASSERT("dhcp_option_byte: options_out_len < DHCP_OPTIONS_LEN", options_out_len < DHCP_OPTIONS_LEN);
+  options[options_out_len++] = value;
+  return options_out_len;
+}
+
+static u16_t
+dhcp_option_short(u16_t options_out_len, u8_t *options, u16_t value)
+{
+  LWIP_ASSERT("dhcp_option_short: options_out_len + 2 <= DHCP_OPTIONS_LEN", options_out_len + 2U <= DHCP_OPTIONS_LEN);
+  options[options_out_len++] = (u8_t)((value & 0xff00U) >> 8);
+  options[options_out_len++] = (u8_t) (value & 0x00ffU);
+  return options_out_len;
+}
+
+static u16_t
+dhcp_option_long(u16_t options_out_len, u8_t *options, u32_t value)
+{
+  LWIP_ASSERT("dhcp_option_long: options_out_len + 4 <= DHCP_OPTIONS_LEN", options_out_len + 4U <= DHCP_OPTIONS_LEN);
+  options[options_out_len++] = (u8_t)((value & 0xff000000UL) >> 24);
+  options[options_out_len++] = (u8_t)((value & 0x00ff0000UL) >> 16);
+  options[options_out_len++] = (u8_t)((value & 0x0000ff00UL) >> 8);
+  options[options_out_len++] = (u8_t)((value & 0x000000ffUL));
+  return options_out_len;
+}
+
+#if LWIP_NETIF_HOSTNAME
+static u16_t
+dhcp_option_hostname(u16_t options_out_len, u8_t *options, struct netif *netif)
+{
+  if (netif->hostname != NULL) {
+    size_t namelen = strlen(netif->hostname);
+    if (namelen > 0) {
+      size_t len;
+      const char *p = netif->hostname;
+      /* Shrink len to available bytes (need 2 bytes for OPTION_HOSTNAME
+         and 1 byte for trailer) */
+      size_t available = DHCP_OPTIONS_LEN - options_out_len - 3;
+      LWIP_ASSERT("DHCP: hostname is too long!", namelen <= available);
+      len = LWIP_MIN(namelen, available);
+      LWIP_ASSERT("DHCP: hostname is too long!", len <= 0xFF);
+      options_out_len = dhcp_option(options_out_len, options, DHCP_OPTION_HOSTNAME, (u8_t)len);
+      while (len--) {
+        options_out_len = dhcp_option_byte(options_out_len, options, *p++);
+      }
+    }
+  }
+  return options_out_len;
+}
+#endif /* LWIP_NETIF_HOSTNAME */
+
+/**
+ * Extract the DHCP message and the DHCP options.
+ *
+ * Extract the DHCP message and the DHCP options, each into a contiguous
+ * piece of memory. As a DHCP message is variable sized by its options,
+ * and also allows overriding some fields for options, the easy approach
+ * is to first unfold the options into a contiguous piece of memory, and
+ * use that further on.
+ *
+ */
+static err_t
+dhcp_parse_reply(struct pbuf *p, struct dhcp *dhcp)
+{
+  u8_t *options;
+  u16_t offset;
+  u16_t offset_max;
+  u16_t options_offset;
+  u16_t options_idx;
+  u16_t options_idx_max;
+  struct pbuf *q;
+  int parse_file_as_options = 0;
+  int parse_sname_as_options = 0;
+  struct dhcp_msg *msg_in;
+#if LWIP_DHCP_BOOTP_FILE
+  int file_overloaded = 0;
+#endif
+
+  LWIP_UNUSED_ARG(dhcp);
+
+  /* clear received options */
+  dhcp_clear_all_options(dhcp);
+  /* check that beginning of dhcp_msg (up to and including chaddr) is in first pbuf */
+  if (p->len < DHCP_SNAME_OFS) {
+    return ERR_BUF;
+  }
+  msg_in = (struct dhcp_msg *)p->payload;
+#if LWIP_DHCP_BOOTP_FILE
+  /* clear boot file name */
+  dhcp->boot_file_name[0] = 0;
+#endif /* LWIP_DHCP_BOOTP_FILE */
+
+  /* parse options */
+
+  /* start with options field */
+  options_idx = DHCP_OPTIONS_OFS;
+  /* parse options to the end of the received packet */
+  options_idx_max = p->tot_len;
+again:
+  q = p;
+  options_offset = options_idx;
+  while ((q != NULL) && (options_idx >= q->len)) {
+    options_idx = (u16_t)(options_idx - q->len);
+    options_idx_max = (u16_t)(options_idx_max - q->len);
+    q = q->next;
+  }
+  if (q == NULL) {
+    return ERR_BUF;
+  }
+  offset = options_idx;
+  offset_max = options_idx_max;
+  options = (u8_t *)q->payload;
+  /* at least 1 byte to read and no end marker, then at least 3 bytes to read? */
+  while ((q != NULL) && (offset < offset_max) && (options[offset] != DHCP_OPTION_END)) {
+    u8_t op = options[offset];
+    u8_t len;
+    u8_t decode_len = 0;
+    int decode_idx = -1;
+    u16_t val_offset = (u16_t)(offset + 2);
+    if (val_offset < offset) {
+      /* overflow */
+      return ERR_BUF;
+    }
+    /* len byte might be in the next pbuf */
+    if ((offset + 1) < q->len) {
+      len = options[offset + 1];
+    } else {
+      len = (q->next != NULL ? ((u8_t *)q->next->payload)[0] : 0);
+    }
+    /* LWIP_DEBUGF(DHCP_DEBUG, ("msg_offset=%"U16_F", q->len=%"U16_F"\n", msg_offset, q->len)); */
+    decode_len = len;
+    switch (op) {
+      /* case(DHCP_OPTION_END): handled above */
+      case (DHCP_OPTION_PAD):
+        /* special option: no len encoded */
+        decode_len = len = 0;
+        /* will be increased below */
+        break;
+      case (DHCP_OPTION_SUBNET_MASK):
+        LWIP_DHCP_INPUT_ERROR("len == 4", len == 4, return ERR_VAL;);
+        decode_idx = DHCP_OPTION_IDX_SUBNET_MASK;
+        break;
+      case (DHCP_OPTION_ROUTER):
+        decode_len = 4; /* only copy the first given router */
+        LWIP_DHCP_INPUT_ERROR("len >= decode_len", len >= decode_len, return ERR_VAL;);
+        decode_idx = DHCP_OPTION_IDX_ROUTER;
+        break;
+#if LWIP_DHCP_PROVIDE_DNS_SERVERS
+      case (DHCP_OPTION_DNS_SERVER):
+        /* special case: there might be more than one server */
+        LWIP_DHCP_INPUT_ERROR("len %% 4 == 0", len % 4 == 0, return ERR_VAL;);
+        /* limit number of DNS servers */
+        decode_len = LWIP_MIN(len, 4 * DNS_MAX_SERVERS);
+        LWIP_DHCP_INPUT_ERROR("len >= decode_len", len >= decode_len, return ERR_VAL;);
+        decode_idx = DHCP_OPTION_IDX_DNS_SERVER;
+        break;
+#endif /* LWIP_DHCP_PROVIDE_DNS_SERVERS */
+      case (DHCP_OPTION_LEASE_TIME):
+        LWIP_DHCP_INPUT_ERROR("len == 4", len == 4, return ERR_VAL;);
+        decode_idx = DHCP_OPTION_IDX_LEASE_TIME;
+        break;
+#if LWIP_DHCP_GET_NTP_SRV
+      case (DHCP_OPTION_NTP):
+        /* special case: there might be more than one server */
+        LWIP_DHCP_INPUT_ERROR("len %% 4 == 0", len % 4 == 0, return ERR_VAL;);
+        /* limit number of NTP servers */
+        decode_len = LWIP_MIN(len, 4 * LWIP_DHCP_MAX_NTP_SERVERS);
+        LWIP_DHCP_INPUT_ERROR("len >= decode_len", len >= decode_len, return ERR_VAL;);
+        decode_idx = DHCP_OPTION_IDX_NTP_SERVER;
+        break;
+#endif /* LWIP_DHCP_GET_NTP_SRV*/
+      case (DHCP_OPTION_OVERLOAD):
+        LWIP_DHCP_INPUT_ERROR("len == 1", len == 1, return ERR_VAL;);
+        /* decode overload only in options, not in file/sname: invalid packet */
+        LWIP_DHCP_INPUT_ERROR("overload in file/sname", options_offset == DHCP_OPTIONS_OFS, return ERR_VAL;);
+        decode_idx = DHCP_OPTION_IDX_OVERLOAD;
+        break;
+      case (DHCP_OPTION_MESSAGE_TYPE):
+        LWIP_DHCP_INPUT_ERROR("len == 1", len == 1, return ERR_VAL;);
+        decode_idx = DHCP_OPTION_IDX_MSG_TYPE;
+        break;
+      case (DHCP_OPTION_SERVER_ID):
+        LWIP_DHCP_INPUT_ERROR("len == 4", len == 4, return ERR_VAL;);
+        decode_idx = DHCP_OPTION_IDX_SERVER_ID;
+        break;
+      case (DHCP_OPTION_T1):
+        LWIP_DHCP_INPUT_ERROR("len == 4", len == 4, return ERR_VAL;);
+        decode_idx = DHCP_OPTION_IDX_T1;
+        break;
+      case (DHCP_OPTION_T2):
+        LWIP_DHCP_INPUT_ERROR("len == 4", len == 4, return ERR_VAL;);
+        decode_idx = DHCP_OPTION_IDX_T2;
+        break;
+      default:
+        decode_len = 0;
+        LWIP_DEBUGF(DHCP_DEBUG, ("skipping option %"U16_F" in options\n", (u16_t)op));
+        LWIP_HOOK_DHCP_PARSE_OPTION(ip_current_netif(), dhcp, dhcp->state, msg_in,
+                                    dhcp_option_given(dhcp, DHCP_OPTION_IDX_MSG_TYPE) ? (u8_t)dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_MSG_TYPE) : 0,
+                                    op, len, q, val_offset);
+        break;
+    }
+    if (op == DHCP_OPTION_PAD) {
+      offset++;
+    } else {
+      if (offset + len + 2 > 0xFFFF) {
+        /* overflow */
+        return ERR_BUF;
+      }
+      offset = (u16_t)(offset + len + 2);
+      if (decode_len > 0) {
+        u32_t value = 0;
+        u16_t copy_len;
+decode_next:
+        LWIP_ASSERT("check decode_idx", decode_idx >= 0 && decode_idx < DHCP_OPTION_IDX_MAX);
+        if (!dhcp_option_given(dhcp, decode_idx)) {
+          copy_len = LWIP_MIN(decode_len, 4);
+          if (pbuf_copy_partial(q, &value, copy_len, val_offset) != copy_len) {
+            return ERR_BUF;
+          }
+          if (decode_len > 4) {
+            /* decode more than one u32_t */
+            u16_t next_val_offset;
+            LWIP_DHCP_INPUT_ERROR("decode_len %% 4 == 0", decode_len % 4 == 0, return ERR_VAL;);
+            dhcp_got_option(dhcp, decode_idx);
+            dhcp_set_option_value(dhcp, decode_idx, lwip_htonl(value));
+            decode_len = (u8_t)(decode_len - 4);
+            next_val_offset = (u16_t)(val_offset + 4);
+            if (next_val_offset < val_offset) {
+              /* overflow */
+              return ERR_BUF;
+            }
+            val_offset = next_val_offset;
+            decode_idx++;
+            goto decode_next;
+          } else if (decode_len == 4) {
+            value = lwip_ntohl(value);
+          } else {
+            LWIP_DHCP_INPUT_ERROR("invalid decode_len", decode_len == 1, return ERR_VAL;);
+            value = ((u8_t *)&value)[0];
+          }
+          dhcp_got_option(dhcp, decode_idx);
+          dhcp_set_option_value(dhcp, decode_idx, value);
+        }
+      }
+    }
+    if (offset >= q->len) {
+      offset = (u16_t)(offset - q->len);
+      offset_max = (u16_t)(offset_max - q->len);
+      if (offset < offset_max) {
+        q = q->next;
+        LWIP_DHCP_INPUT_ERROR("next pbuf was null", q != NULL, return ERR_VAL;);
+        options = (u8_t *)q->payload;
+      } else {
+        /* We've run out of bytes, probably no end marker. Don't proceed. */
+        return ERR_BUF;
+      }
+    }
+  }
+  /* is this an overloaded message? */
+  if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_OVERLOAD)) {
+    u32_t overload = dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_OVERLOAD);
+    dhcp_clear_option(dhcp, DHCP_OPTION_IDX_OVERLOAD);
+    if (overload == DHCP_OVERLOAD_FILE) {
+      parse_file_as_options = 1;
+      LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("overloaded file field\n"));
+    } else if (overload == DHCP_OVERLOAD_SNAME) {
+      parse_sname_as_options = 1;
+      LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("overloaded sname field\n"));
+    } else if (overload == DHCP_OVERLOAD_SNAME_FILE) {
+      parse_sname_as_options = 1;
+      parse_file_as_options = 1;
+      LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("overloaded sname and file field\n"));
+    } else {
+      LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("invalid overload option: %d\n", (int)overload));
+    }
+  }
+  if (parse_file_as_options) {
+    /* if both are overloaded, parse file first and then sname (RFC 2131 ch. 4.1) */
+    parse_file_as_options = 0;
+    options_idx = DHCP_FILE_OFS;
+    options_idx_max = DHCP_FILE_OFS + DHCP_FILE_LEN;
+#if LWIP_DHCP_BOOTP_FILE
+    file_overloaded = 1;
+#endif
+    goto again;
+  } else if (parse_sname_as_options) {
+    parse_sname_as_options = 0;
+    options_idx = DHCP_SNAME_OFS;
+    options_idx_max = DHCP_SNAME_OFS + DHCP_SNAME_LEN;
+    goto again;
+  }
+#if LWIP_DHCP_BOOTP_FILE
+  if (!file_overloaded) {
+    /* only do this for ACK messages */
+    if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_MSG_TYPE) &&
+      (dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_MSG_TYPE) == DHCP_ACK))
+    /* copy bootp file name, don't care for sname (server hostname) */
+    if (pbuf_copy_partial(p, dhcp->boot_file_name, DHCP_FILE_LEN-1, DHCP_FILE_OFS) != (DHCP_FILE_LEN-1)) {
+      return ERR_BUF;
+    }
+    /* make sure the string is really NULL-terminated */
+    dhcp->boot_file_name[DHCP_FILE_LEN-1] = 0;
+  }
+#endif /* LWIP_DHCP_BOOTP_FILE */
+  return ERR_OK;
+}
+
+/**
+ * If an incoming DHCP message is in response to us, then trigger the state machine
+ */
+static void
+dhcp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
+{
+  struct netif *netif = ip_current_input_netif();
+  struct dhcp *dhcp = netif_dhcp_data(netif);
+  struct dhcp_msg *reply_msg = (struct dhcp_msg *)p->payload;
+  u8_t msg_type;
+  u8_t i;
+  struct dhcp_msg *msg_in;
+
+  LWIP_UNUSED_ARG(arg);
+
+  /* Caught DHCP message from netif that does not have DHCP enabled? -> not interested */
+  if ((dhcp == NULL) || (dhcp->pcb_allocated == 0)) {
+    goto free_pbuf_and_return;
+  }
+
+  LWIP_ASSERT("invalid server address type", IP_IS_V4(addr));
+
+  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_recv(pbuf = %p) from DHCP server %"U16_F".%"U16_F".%"U16_F".%"U16_F" port %"U16_F"\n", (void *)p,
+              ip4_addr1_16(ip_2_ip4(addr)), ip4_addr2_16(ip_2_ip4(addr)), ip4_addr3_16(ip_2_ip4(addr)), ip4_addr4_16(ip_2_ip4(addr)), port));
+  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("pbuf->len = %"U16_F"\n", p->len));
+  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("pbuf->tot_len = %"U16_F"\n", p->tot_len));
+  /* prevent warnings about unused arguments */
+  LWIP_UNUSED_ARG(pcb);
+  LWIP_UNUSED_ARG(addr);
+  LWIP_UNUSED_ARG(port);
+
+  if (p->len < DHCP_MIN_REPLY_LEN) {
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("DHCP reply message or pbuf too short\n"));
+    goto free_pbuf_and_return;
+  }
+
+  if (reply_msg->op != DHCP_BOOTREPLY) {
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("not a DHCP reply message, but type %"U16_F"\n", (u16_t)reply_msg->op));
+    goto free_pbuf_and_return;
+  }
+  /* iterate through hardware address and match against DHCP message */
+  for (i = 0; i < netif->hwaddr_len && i < LWIP_MIN(DHCP_CHADDR_LEN, NETIF_MAX_HWADDR_LEN); i++) {
+    if (netif->hwaddr[i] != reply_msg->chaddr[i]) {
+      LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
+                  ("netif->hwaddr[%"U16_F"]==%02"X16_F" != reply_msg->chaddr[%"U16_F"]==%02"X16_F"\n",
+                   (u16_t)i, (u16_t)netif->hwaddr[i], (u16_t)i, (u16_t)reply_msg->chaddr[i]));
+      goto free_pbuf_and_return;
+    }
+  }
+  /* match transaction ID against what we expected */
+  if (lwip_ntohl(reply_msg->xid) != dhcp->xid) {
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
+                ("transaction id mismatch reply_msg->xid(%"X32_F")!=dhcp->xid(%"X32_F")\n", lwip_ntohl(reply_msg->xid), dhcp->xid));
+    goto free_pbuf_and_return;
+  }
+  /* option fields could be unfold? */
+  if (dhcp_parse_reply(p, dhcp) != ERR_OK) {
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
+                ("problem unfolding DHCP message - too short on memory?\n"));
+    goto free_pbuf_and_return;
+  }
+
+  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("searching DHCP_OPTION_MESSAGE_TYPE\n"));
+  /* obtain pointer to DHCP message type */
+  if (!dhcp_option_given(dhcp, DHCP_OPTION_IDX_MSG_TYPE)) {
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("DHCP_OPTION_MESSAGE_TYPE option not found\n"));
+    goto free_pbuf_and_return;
+  }
+
+  msg_in = (struct dhcp_msg *)p->payload;
+  /* read DHCP message type */
+  msg_type = (u8_t)dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_MSG_TYPE);
+  /* message type is DHCP ACK? */
+  if (msg_type == DHCP_ACK) {
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("DHCP_ACK received\n"));
+    /* in requesting state or just reconnected to the network? */
+    if ((dhcp->state == DHCP_STATE_REQUESTING) ||
+        (dhcp->state == DHCP_STATE_REBOOTING)) {
+      dhcp_handle_ack(netif, msg_in);
+#if LWIP_DHCP_DOES_ACD_CHECK
+      if ((netif->flags & NETIF_FLAG_ETHARP) != 0) {
+        /* check if the acknowledged lease address is already in use */
+        dhcp_check(netif);
+      } else {
+        /* bind interface to the acknowledged lease address */
+        dhcp_bind(netif);
+      }
+#else
+      /* bind interface to the acknowledged lease address */
+      dhcp_bind(netif);
+#endif
+    }
+    /* already bound to the given lease address and using it? */
+    else if ((dhcp->state == DHCP_STATE_REBINDING) ||
+             (dhcp->state == DHCP_STATE_RENEWING)) {
+      dhcp_handle_ack(netif, msg_in);
+      dhcp_bind(netif);
+    }
+  }
+  /* received a DHCP_NAK in appropriate state? */
+  else if ((msg_type == DHCP_NAK) &&
+           ((dhcp->state == DHCP_STATE_REBOOTING) || (dhcp->state == DHCP_STATE_REQUESTING) ||
+            (dhcp->state == DHCP_STATE_REBINDING) || (dhcp->state == DHCP_STATE_RENEWING  ))) {
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("DHCP_NAK received\n"));
+    dhcp_handle_nak(netif);
+  }
+  /* received a DHCP_OFFER in DHCP_STATE_SELECTING state? */
+  else if ((msg_type == DHCP_OFFER) && (dhcp->state == DHCP_STATE_SELECTING)) {
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("DHCP_OFFER received in DHCP_STATE_SELECTING state\n"));
+    /* remember offered lease */
+    dhcp_handle_offer(netif, msg_in);
+  }
+
+free_pbuf_and_return:
+  pbuf_free(p);
+}
+
+/**
+ * Create a DHCP request, fill in common headers
+ *
+ * @param netif the netif under DHCP control
+ * @param dhcp dhcp control struct
+ * @param message_type message type of the request
+ */
+static struct pbuf *
+dhcp_create_msg(struct netif *netif, struct dhcp *dhcp, u8_t message_type, u16_t *options_out_len)
+{
+  u16_t i;
+  struct pbuf *p_out;
+  struct dhcp_msg *msg_out;
+  u16_t options_out_len_loc;
+
+#ifndef DHCP_GLOBAL_XID
+  /** default global transaction identifier starting value (easy to match
+   *  with a packet analyser). We simply increment for each new request.
+   *  Predefine DHCP_GLOBAL_XID to a better value or a function call to generate one
+   *  at runtime, any supporting function prototypes can be defined in DHCP_GLOBAL_XID_HEADER */
+#if DHCP_CREATE_RAND_XID && defined(LWIP_RAND)
+  static u32_t xid;
+#else /* DHCP_CREATE_RAND_XID && defined(LWIP_RAND) */
+  static u32_t xid = 0xABCD0000;
+#endif /* DHCP_CREATE_RAND_XID && defined(LWIP_RAND) */
+#else
+  if (!xid_initialised) {
+    xid = DHCP_GLOBAL_XID;
+    xid_initialised = !xid_initialised;
+  }
+#endif
+  LWIP_ERROR("dhcp_create_msg: netif != NULL", (netif != NULL), return NULL;);
+  LWIP_ERROR("dhcp_create_msg: dhcp != NULL", (dhcp != NULL), return NULL;);
+  p_out = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct dhcp_msg), PBUF_RAM);
+  if (p_out == NULL) {
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
+                ("dhcp_create_msg(): could not allocate pbuf\n"));
+    return NULL;
+  }
+  LWIP_ASSERT("dhcp_create_msg: check that first pbuf can hold struct dhcp_msg",
+              (p_out->len >= sizeof(struct dhcp_msg)));
+
+  /* DHCP_REQUEST should reuse 'xid' from DHCPOFFER */
+  if ((message_type != DHCP_REQUEST) || (dhcp->state == DHCP_STATE_REBOOTING)) {
+    /* reuse transaction identifier in retransmissions */
+    if (dhcp->tries == 0) {
+#if DHCP_CREATE_RAND_XID && defined(LWIP_RAND)
+      xid = LWIP_RAND();
+#else /* DHCP_CREATE_RAND_XID && defined(LWIP_RAND) */
+      xid++;
+#endif /* DHCP_CREATE_RAND_XID && defined(LWIP_RAND) */
+    }
+    dhcp->xid = xid;
+  }
+  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE,
+              ("transaction id xid(%"X32_F")\n", xid));
+
+  msg_out = (struct dhcp_msg *)p_out->payload;
+  memset(msg_out, 0, sizeof(struct dhcp_msg));
+
+  msg_out->op = DHCP_BOOTREQUEST;
+  /* @todo: make link layer independent */
+  msg_out->htype = LWIP_IANA_HWTYPE_ETHERNET;
+  msg_out->hlen = netif->hwaddr_len;
+  msg_out->xid = lwip_htonl(dhcp->xid);
+  /* we don't need the broadcast flag since we can receive unicast traffic
+     before being fully configured! */
+  /* set ciaddr to netif->ip_addr based on message_type and state */
+  if ((message_type == DHCP_INFORM) || (message_type == DHCP_DECLINE) || (message_type == DHCP_RELEASE) ||
+      ((message_type == DHCP_REQUEST) && /* DHCP_STATE_BOUND not used for sending! */
+       ((dhcp->state == DHCP_STATE_RENEWING) || dhcp->state == DHCP_STATE_REBINDING))) {
+    ip4_addr_copy(msg_out->ciaddr, *netif_ip4_addr(netif));
+  }
+  for (i = 0; i < LWIP_MIN(DHCP_CHADDR_LEN, NETIF_MAX_HWADDR_LEN); i++) {
+    /* copy netif hardware address (padded with zeroes through memset already) */
+    msg_out->chaddr[i] = netif->hwaddr[i];
+  }
+  msg_out->cookie = PP_HTONL(DHCP_MAGIC_COOKIE);
+  /* Add option MESSAGE_TYPE */
+  options_out_len_loc = dhcp_option(0, msg_out->options, DHCP_OPTION_MESSAGE_TYPE, DHCP_OPTION_MESSAGE_TYPE_LEN);
+  options_out_len_loc = dhcp_option_byte(options_out_len_loc, msg_out->options, message_type);
+  if (options_out_len) {
+    *options_out_len = options_out_len_loc;
+  }
+  return p_out;
+}
+
+/**
+ * Add a DHCP message trailer
+ *
+ * Adds the END option to the DHCP message, and if
+ * necessary, up to three padding bytes.
+ */
+static void
+dhcp_option_trailer(u16_t options_out_len, u8_t *options, struct pbuf *p_out)
+{
+  options[options_out_len++] = DHCP_OPTION_END;
+  /* packet is too small, or not 4 byte aligned? */
+  while (((options_out_len < DHCP_MIN_OPTIONS_LEN) || (options_out_len & 3)) &&
+         (options_out_len < DHCP_OPTIONS_LEN)) {
+    /* add a fill/padding byte */
+    options[options_out_len++] = 0;
+  }
+  /* shrink the pbuf to the actual content length */
+  pbuf_realloc(p_out, (u16_t)(sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + options_out_len));
+}
+
+/** check if DHCP supplied netif->ip_addr
+ *
+ * @param netif the netif to check
+ * @return 1 if DHCP supplied netif->ip_addr (states BOUND or RENEWING),
+ *         0 otherwise
+ */
+u8_t
+dhcp_supplied_address(const struct netif *netif)
+{
+  if ((netif != NULL) && (netif_dhcp_data(netif) != NULL)) {
+    struct dhcp *dhcp = netif_dhcp_data(netif);
+    return (dhcp->state == DHCP_STATE_BOUND) || (dhcp->state == DHCP_STATE_RENEWING) ||
+           (dhcp->state == DHCP_STATE_REBINDING);
+  }
+  return 0;
+}
+
+#endif /* LWIP_IPV4 && LWIP_DHCP */
diff --git a/lib/lwip/core/ipv4/etharp.c b/lib/lwip/core/ipv4/etharp.c
new file mode 100644
index 0000000000..3092dc944a
--- /dev/null
+++ b/lib/lwip/core/ipv4/etharp.c
@@ -0,0 +1,1251 @@
+/**
+ * @file
+ * Address Resolution Protocol module for IP over Ethernet
+ *
+ * Functionally, ARP is divided into two parts. The first maps an IP address
+ * to a physical address when sending a packet, and the second part answers
+ * requests from other machines for our physical address.
+ *
+ * This implementation complies with RFC 826 (Ethernet ARP). It supports
+ * Gratuitous ARP from RFC3220 (IP Mobility Support for IPv4) section 4.6
+ * if an interface calls etharp_gratuitous(our_netif) upon address change.
+ */
+
+/*
+ * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
+ * Copyright (c) 2003-2004 Leon Woestenberg <leon.woestenberg@axon.tv>
+ * Copyright (c) 2003-2004 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV4 && LWIP_ARP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/etharp.h"
+#include "lwip/stats.h"
+#include "lwip/snmp.h"
+#include "lwip/dhcp.h"
+#include "lwip/autoip.h"
+#include "lwip/acd.h"
+#include "lwip/prot/iana.h"
+#include "netif/ethernet.h"
+
+#include <string.h>
+
+#ifdef LWIP_HOOK_FILENAME
+#include LWIP_HOOK_FILENAME
+#endif
+
+/** Re-request a used ARP entry 1 minute before it would expire to prevent
+ *  breaking a steadily used connection because the ARP entry timed out. */
+#define ARP_AGE_REREQUEST_USED_UNICAST   (ARP_MAXAGE - 30)
+#define ARP_AGE_REREQUEST_USED_BROADCAST (ARP_MAXAGE - 15)
+
+/** the time an ARP entry stays pending after first request,
+ *  for ARP_TMR_INTERVAL = 1000, this is
+ *  10 seconds.
+ *
+ *  @internal Keep this number at least 2, otherwise it might
+ *  run out instantly if the timeout occurs directly after a request.
+ */
+#define ARP_MAXPENDING 5
+
+/** ARP states */
+enum etharp_state {
+  ETHARP_STATE_EMPTY = 0,
+  ETHARP_STATE_PENDING,
+  ETHARP_STATE_STABLE,
+  ETHARP_STATE_STABLE_REREQUESTING_1,
+  ETHARP_STATE_STABLE_REREQUESTING_2
+#if ETHARP_SUPPORT_STATIC_ENTRIES
+  , ETHARP_STATE_STATIC
+#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
+};
+
+struct etharp_entry {
+#if ARP_QUEUEING
+  /** Pointer to queue of pending outgoing packets on this ARP entry. */
+  struct etharp_q_entry *q;
+#else /* ARP_QUEUEING */
+  /** Pointer to a single pending outgoing packet on this ARP entry. */
+  struct pbuf *q;
+#endif /* ARP_QUEUEING */
+  ip4_addr_t ipaddr;
+  struct netif *netif;
+  struct eth_addr ethaddr;
+  u16_t ctime;
+  u8_t state;
+};
+
+static struct etharp_entry arp_table[ARP_TABLE_SIZE];
+
+#if !LWIP_NETIF_HWADDRHINT
+static netif_addr_idx_t etharp_cached_entry;
+#endif /* !LWIP_NETIF_HWADDRHINT */
+
+/** Try hard to create a new entry - we want the IP address to appear in
+    the cache (even if this means removing an active entry or so). */
+#define ETHARP_FLAG_TRY_HARD     1
+#define ETHARP_FLAG_FIND_ONLY    2
+#if ETHARP_SUPPORT_STATIC_ENTRIES
+#define ETHARP_FLAG_STATIC_ENTRY 4
+#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
+
+#if LWIP_NETIF_HWADDRHINT
+#define ETHARP_SET_ADDRHINT(netif, addrhint)  do { if (((netif) != NULL) && ((netif)->hints != NULL)) { \
+                                              (netif)->hints->addr_hint = (addrhint); }} while(0)
+#else /* LWIP_NETIF_HWADDRHINT */
+#define ETHARP_SET_ADDRHINT(netif, addrhint)  (etharp_cached_entry = (addrhint))
+#endif /* LWIP_NETIF_HWADDRHINT */
+
+
+/* Check for maximum ARP_TABLE_SIZE */
+#if (ARP_TABLE_SIZE > NETIF_ADDR_IDX_MAX)
+#error "ARP_TABLE_SIZE must fit in an s16_t, you have to reduce it in your lwipopts.h"
+#endif
+
+
+static err_t etharp_request_dst(struct netif *netif, const ip4_addr_t *ipaddr, const struct eth_addr *hw_dst_addr);
+static err_t etharp_raw(struct netif *netif,
+                        const struct eth_addr *ethsrc_addr, const struct eth_addr *ethdst_addr,
+                        const struct eth_addr *hwsrc_addr, const ip4_addr_t *ipsrc_addr,
+                        const struct eth_addr *hwdst_addr, const ip4_addr_t *ipdst_addr,
+                        const u16_t opcode);
+
+#if ARP_QUEUEING
+/**
+ * Free a complete queue of etharp entries
+ *
+ * @param q a queue of etharp_q_entry's to free
+ */
+static void
+free_etharp_q(struct etharp_q_entry *q)
+{
+  struct etharp_q_entry *r;
+  LWIP_ASSERT("q != NULL", q != NULL);
+  while (q) {
+    r = q;
+    q = q->next;
+    LWIP_ASSERT("r->p != NULL", (r->p != NULL));
+    pbuf_free(r->p);
+    memp_free(MEMP_ARP_QUEUE, r);
+  }
+}
+#else /* ARP_QUEUEING */
+
+/** Compatibility define: free the queued pbuf */
+#define free_etharp_q(q) pbuf_free(q)
+
+#endif /* ARP_QUEUEING */
+
+/** Clean up ARP table entries */
+static void
+etharp_free_entry(int i)
+{
+  /* remove from SNMP ARP index tree */
+  mib2_remove_arp_entry(arp_table[i].netif, &arp_table[i].ipaddr);
+  /* and empty packet queue */
+  if (arp_table[i].q != NULL) {
+    /* remove all queued packets */
+    LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_free_entry: freeing entry %"U16_F", packet queue %p.\n", (u16_t)i, (void *)(arp_table[i].q)));
+    free_etharp_q(arp_table[i].q);
+    arp_table[i].q = NULL;
+  }
+  /* recycle entry for re-use */
+  arp_table[i].state = ETHARP_STATE_EMPTY;
+#ifdef LWIP_DEBUG
+  /* for debugging, clean out the complete entry */
+  arp_table[i].ctime = 0;
+  arp_table[i].netif = NULL;
+  ip4_addr_set_zero(&arp_table[i].ipaddr);
+  arp_table[i].ethaddr = ethzero;
+#endif /* LWIP_DEBUG */
+}
+
+/**
+ * Clears expired entries in the ARP table.
+ *
+ * This function should be called every ARP_TMR_INTERVAL milliseconds (1 second),
+ * in order to expire entries in the ARP table.
+ */
+void
+etharp_tmr(void)
+{
+  int i;
+
+  LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_timer\n"));
+  /* remove expired entries from the ARP table */
+  for (i = 0; i < ARP_TABLE_SIZE; ++i) {
+    u8_t state = arp_table[i].state;
+    if (state != ETHARP_STATE_EMPTY
+#if ETHARP_SUPPORT_STATIC_ENTRIES
+        && (state != ETHARP_STATE_STATIC)
+#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
+       ) {
+      arp_table[i].ctime++;
+      if ((arp_table[i].ctime >= ARP_MAXAGE) ||
+          ((arp_table[i].state == ETHARP_STATE_PENDING)  &&
+           (arp_table[i].ctime >= ARP_MAXPENDING))) {
+        /* pending or stable entry has become old! */
+        LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_timer: expired %s entry %d.\n",
+                                   arp_table[i].state >= ETHARP_STATE_STABLE ? "stable" : "pending", i));
+        /* clean up entries that have just been expired */
+        etharp_free_entry(i);
+      } else if (arp_table[i].state == ETHARP_STATE_STABLE_REREQUESTING_1) {
+        /* Don't send more than one request every 2 seconds. */
+        arp_table[i].state = ETHARP_STATE_STABLE_REREQUESTING_2;
+      } else if (arp_table[i].state == ETHARP_STATE_STABLE_REREQUESTING_2) {
+        /* Reset state to stable, so that the next transmitted packet will
+           re-send an ARP request. */
+        arp_table[i].state = ETHARP_STATE_STABLE;
+      } else if (arp_table[i].state == ETHARP_STATE_PENDING) {
+        /* still pending, resend an ARP query */
+        etharp_request(arp_table[i].netif, &arp_table[i].ipaddr);
+      }
+    }
+  }
+}
+
+/**
+ * Search the ARP table for a matching or new entry.
+ *
+ * If an IP address is given, return a pending or stable ARP entry that matches
+ * the address. If no match is found, create a new entry with this address set,
+ * but in state ETHARP_EMPTY. The caller must check and possibly change the
+ * state of the returned entry.
+ *
+ * If ipaddr is NULL, return a initialized new entry in state ETHARP_EMPTY.
+ *
+ * In all cases, attempt to create new entries from an empty entry. If no
+ * empty entries are available and ETHARP_FLAG_TRY_HARD flag is set, recycle
+ * old entries. Heuristic choose the least important entry for recycling.
+ *
+ * @param ipaddr IP address to find in ARP cache, or to add if not found.
+ * @param flags See @ref etharp_state
+ * @param netif netif related to this address (used for NETIF_HWADDRHINT)
+ *
+ * @return The ARP entry index that matched or is created, ERR_MEM if no
+ * entry is found or could be recycled.
+ */
+static s16_t
+etharp_find_entry(const ip4_addr_t *ipaddr, u8_t flags, struct netif *netif)
+{
+  s16_t old_pending = ARP_TABLE_SIZE, old_stable = ARP_TABLE_SIZE;
+  s16_t empty = ARP_TABLE_SIZE;
+  s16_t i = 0;
+  /* oldest entry with packets on queue */
+  s16_t old_queue = ARP_TABLE_SIZE;
+  /* its age */
+  u16_t age_queue = 0, age_pending = 0, age_stable = 0;
+
+  LWIP_UNUSED_ARG(netif);
+
+  /**
+   * a) do a search through the cache, remember candidates
+   * b) select candidate entry
+   * c) create new entry
+   */
+
+  /* a) in a single search sweep, do all of this
+   * 1) remember the first empty entry (if any)
+   * 2) remember the oldest stable entry (if any)
+   * 3) remember the oldest pending entry without queued packets (if any)
+   * 4) remember the oldest pending entry with queued packets (if any)
+   * 5) search for a matching IP entry, either pending or stable
+   *    until 5 matches, or all entries are searched for.
+   */
+
+  for (i = 0; i < ARP_TABLE_SIZE; ++i) {
+    u8_t state = arp_table[i].state;
+    /* no empty entry found yet and now we do find one? */
+    if ((empty == ARP_TABLE_SIZE) && (state == ETHARP_STATE_EMPTY)) {
+      LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_find_entry: found empty entry %d\n", (int)i));
+      /* remember first empty entry */
+      empty = i;
+    } else if (state != ETHARP_STATE_EMPTY) {
+      LWIP_ASSERT("state == ETHARP_STATE_PENDING || state >= ETHARP_STATE_STABLE",
+                  state == ETHARP_STATE_PENDING || state >= ETHARP_STATE_STABLE);
+      /* if given, does IP address match IP address in ARP entry? */
+      if (ipaddr && ip4_addr_eq(ipaddr, &arp_table[i].ipaddr)
+#if ETHARP_TABLE_MATCH_NETIF
+          && ((netif == NULL) || (netif == arp_table[i].netif))
+#endif /* ETHARP_TABLE_MATCH_NETIF */
+         ) {
+        LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: found matching entry %d\n", (int)i));
+        /* found exact IP address match, simply bail out */
+        return i;
+      }
+      /* pending entry? */
+      if (state == ETHARP_STATE_PENDING) {
+        /* pending with queued packets? */
+        if (arp_table[i].q != NULL) {
+          if (arp_table[i].ctime >= age_queue) {
+            old_queue = i;
+            age_queue = arp_table[i].ctime;
+          }
+        } else
+          /* pending without queued packets? */
+        {
+          if (arp_table[i].ctime >= age_pending) {
+            old_pending = i;
+            age_pending = arp_table[i].ctime;
+          }
+        }
+        /* stable entry? */
+      } else if (state >= ETHARP_STATE_STABLE) {
+#if ETHARP_SUPPORT_STATIC_ENTRIES
+        /* don't record old_stable for static entries since they never expire */
+        if (state < ETHARP_STATE_STATIC)
+#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
+        {
+          /* remember entry with oldest stable entry in oldest, its age in maxtime */
+          if (arp_table[i].ctime >= age_stable) {
+            old_stable = i;
+            age_stable = arp_table[i].ctime;
+          }
+        }
+      }
+    }
+  }
+  /* { we have no match } => try to create a new entry */
+
+  /* don't create new entry, only search? */
+  if (((flags & ETHARP_FLAG_FIND_ONLY) != 0) ||
+      /* or no empty entry found and not allowed to recycle? */
+      ((empty == ARP_TABLE_SIZE) && ((flags & ETHARP_FLAG_TRY_HARD) == 0))) {
+    LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: no empty entry found and not allowed to recycle\n"));
+    return (s16_t)ERR_MEM;
+  }
+
+  /* b) choose the least destructive entry to recycle:
+   * 1) empty entry
+   * 2) oldest stable entry
+   * 3) oldest pending entry without queued packets
+   * 4) oldest pending entry with queued packets
+   *
+   * { ETHARP_FLAG_TRY_HARD is set at this point }
+   */
+
+  /* 1) empty entry available? */
+  if (empty < ARP_TABLE_SIZE) {
+    i = empty;
+    LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: selecting empty entry %d\n", (int)i));
+  } else {
+    /* 2) found recyclable stable entry? */
+    if (old_stable < ARP_TABLE_SIZE) {
+      /* recycle oldest stable*/
+      i = old_stable;
+      LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: selecting oldest stable entry %d\n", (int)i));
+      /* no queued packets should exist on stable entries */
+      LWIP_ASSERT("arp_table[i].q == NULL", arp_table[i].q == NULL);
+      /* 3) found recyclable pending entry without queued packets? */
+    } else if (old_pending < ARP_TABLE_SIZE) {
+      /* recycle oldest pending */
+      i = old_pending;
+      LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: selecting oldest pending entry %d (without queue)\n", (int)i));
+      /* 4) found recyclable pending entry with queued packets? */
+    } else if (old_queue < ARP_TABLE_SIZE) {
+      /* recycle oldest pending (queued packets are free in etharp_free_entry) */
+      i = old_queue;
+      LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: selecting oldest pending entry %d, freeing packet queue %p\n", (int)i, (void *)(arp_table[i].q)));
+      /* no empty or recyclable entries found */
+    } else {
+      LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: no empty or recyclable entries found\n"));
+      return (s16_t)ERR_MEM;
+    }
+
+    /* { empty or recyclable entry found } */
+    LWIP_ASSERT("i < ARP_TABLE_SIZE", i < ARP_TABLE_SIZE);
+    etharp_free_entry(i);
+  }
+
+  LWIP_ASSERT("i < ARP_TABLE_SIZE", i < ARP_TABLE_SIZE);
+  LWIP_ASSERT("arp_table[i].state == ETHARP_STATE_EMPTY",
+              arp_table[i].state == ETHARP_STATE_EMPTY);
+
+  /* IP address given? */
+  if (ipaddr != NULL) {
+    /* set IP address */
+    ip4_addr_copy(arp_table[i].ipaddr, *ipaddr);
+  }
+  arp_table[i].ctime = 0;
+#if ETHARP_TABLE_MATCH_NETIF
+  arp_table[i].netif = netif;
+#endif /* ETHARP_TABLE_MATCH_NETIF */
+  return (s16_t)i;
+}
+
+/**
+ * Update (or insert) a IP/MAC address pair in the ARP cache.
+ *
+ * If a pending entry is resolved, any queued packets will be sent
+ * at this point.
+ *
+ * @param netif netif related to this entry (used for NETIF_ADDRHINT)
+ * @param ipaddr IP address of the inserted ARP entry.
+ * @param ethaddr Ethernet address of the inserted ARP entry.
+ * @param flags See @ref etharp_state
+ *
+ * @return
+ * - ERR_OK Successfully updated ARP cache.
+ * - ERR_MEM If we could not add a new ARP entry when ETHARP_FLAG_TRY_HARD was set.
+ * - ERR_ARG Non-unicast address given, those will not appear in ARP cache.
+ *
+ * @see pbuf_free()
+ */
+static err_t
+etharp_update_arp_entry(struct netif *netif, const ip4_addr_t *ipaddr, struct eth_addr *ethaddr, u8_t flags)
+{
+  s16_t i;
+  LWIP_ASSERT("netif->hwaddr_len == ETH_HWADDR_LEN", netif->hwaddr_len == ETH_HWADDR_LEN);
+  LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_update_arp_entry: %"U16_F".%"U16_F".%"U16_F".%"U16_F" - %02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F"\n",
+              ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr),
+              (u16_t)ethaddr->addr[0], (u16_t)ethaddr->addr[1], (u16_t)ethaddr->addr[2],
+              (u16_t)ethaddr->addr[3], (u16_t)ethaddr->addr[4], (u16_t)ethaddr->addr[5]));
+  /* non-unicast address? */
+  if (ip4_addr_isany(ipaddr) ||
+      ip4_addr_isbroadcast(ipaddr, netif) ||
+      ip4_addr_ismulticast(ipaddr)) {
+    LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_update_arp_entry: will not add non-unicast IP address to ARP cache\n"));
+    return ERR_ARG;
+  }
+  /* find or create ARP entry */
+  i = etharp_find_entry(ipaddr, flags, netif);
+  /* bail out if no entry could be found */
+  if (i < 0) {
+    return (err_t)i;
+  }
+
+#if ETHARP_SUPPORT_STATIC_ENTRIES
+  if (flags & ETHARP_FLAG_STATIC_ENTRY) {
+    /* record static type */
+    arp_table[i].state = ETHARP_STATE_STATIC;
+  } else if (arp_table[i].state == ETHARP_STATE_STATIC) {
+    /* found entry is a static type, don't overwrite it */
+    return ERR_VAL;
+  } else
+#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
+  {
+    /* mark it stable */
+    arp_table[i].state = ETHARP_STATE_STABLE;
+  }
+
+  /* record network interface */
+  arp_table[i].netif = netif;
+  /* insert in SNMP ARP index tree */
+  mib2_add_arp_entry(netif, &arp_table[i].ipaddr);
+
+  LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_update_arp_entry: updating stable entry %"S16_F"\n", i));
+  /* update address */
+  SMEMCPY(&arp_table[i].ethaddr, ethaddr, ETH_HWADDR_LEN);
+  /* reset time stamp */
+  arp_table[i].ctime = 0;
+  /* this is where we will send out queued packets! */
+#if ARP_QUEUEING
+  while (arp_table[i].q != NULL) {
+    struct pbuf *p;
+    /* remember remainder of queue */
+    struct etharp_q_entry *q = arp_table[i].q;
+    /* pop first item off the queue */
+    arp_table[i].q = q->next;
+    /* get the packet pointer */
+    p = q->p;
+    /* now queue entry can be freed */
+    memp_free(MEMP_ARP_QUEUE, q);
+#else /* ARP_QUEUEING */
+  if (arp_table[i].q != NULL) {
+    struct pbuf *p = arp_table[i].q;
+    arp_table[i].q = NULL;
+#endif /* ARP_QUEUEING */
+    /* send the queued IP packet */
+    ethernet_output(netif, p, (struct eth_addr *)(netif->hwaddr), ethaddr, ETHTYPE_IP);
+    /* free the queued IP packet */
+    pbuf_free(p);
+  }
+  return ERR_OK;
+}
+
+#if ETHARP_SUPPORT_STATIC_ENTRIES
+/** Add a new static entry to the ARP table. If an entry exists for the
+ * specified IP address, this entry is overwritten.
+ * If packets are queued for the specified IP address, they are sent out.
+ *
+ * @param ipaddr IP address for the new static entry
+ * @param ethaddr ethernet address for the new static entry
+ * @return See return values of etharp_add_static_entry
+ */
+err_t
+etharp_add_static_entry(const ip4_addr_t *ipaddr, struct eth_addr *ethaddr)
+{
+  struct netif *netif;
+  LWIP_ASSERT_CORE_LOCKED();
+  LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_add_static_entry: %"U16_F".%"U16_F".%"U16_F".%"U16_F" - %02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F"\n",
+              ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr),
+              (u16_t)ethaddr->addr[0], (u16_t)ethaddr->addr[1], (u16_t)ethaddr->addr[2],
+              (u16_t)ethaddr->addr[3], (u16_t)ethaddr->addr[4], (u16_t)ethaddr->addr[5]));
+
+  netif = ip4_route(ipaddr);
+  if (netif == NULL) {
+    return ERR_RTE;
+  }
+
+  return etharp_update_arp_entry(netif, ipaddr, ethaddr, ETHARP_FLAG_TRY_HARD | ETHARP_FLAG_STATIC_ENTRY);
+}
+
+/** Remove a static entry from the ARP table previously added with a call to
+ * etharp_add_static_entry.
+ *
+ * @param ipaddr IP address of the static entry to remove
+ * @return ERR_OK: entry removed
+ *         ERR_MEM: entry wasn't found
+ *         ERR_ARG: entry wasn't a static entry but a dynamic one
+ */
+err_t
+etharp_remove_static_entry(const ip4_addr_t *ipaddr)
+{
+  s16_t i;
+  LWIP_ASSERT_CORE_LOCKED();
+  LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_remove_static_entry: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+              ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr)));
+
+  /* find or create ARP entry */
+  i = etharp_find_entry(ipaddr, ETHARP_FLAG_FIND_ONLY, NULL);
+  /* bail out if no entry could be found */
+  if (i < 0) {
+    return (err_t)i;
+  }
+
+  if (arp_table[i].state != ETHARP_STATE_STATIC) {
+    /* entry wasn't a static entry, cannot remove it */
+    return ERR_ARG;
+  }
+  /* entry found, free it */
+  etharp_free_entry(i);
+  return ERR_OK;
+}
+#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
+
+/**
+ * Remove all ARP table entries of the specified netif.
+ *
+ * @param netif points to a network interface
+ */
+void
+etharp_cleanup_netif(struct netif *netif)
+{
+  int i;
+
+  for (i = 0; i < ARP_TABLE_SIZE; ++i) {
+    u8_t state = arp_table[i].state;
+    if ((state != ETHARP_STATE_EMPTY) && (arp_table[i].netif == netif)) {
+      etharp_free_entry(i);
+    }
+  }
+}
+
+/**
+ * Finds (stable) ethernet/IP address pair from ARP table
+ * using interface and IP address index.
+ * @note the addresses in the ARP table are in network order!
+ *
+ * @param netif points to interface index
+ * @param ipaddr points to the (network order) IP address index
+ * @param eth_ret points to return pointer
+ * @param ip_ret points to return pointer
+ * @return table index if found, -1 otherwise
+ */
+ssize_t
+etharp_find_addr(struct netif *netif, const ip4_addr_t *ipaddr,
+                 struct eth_addr **eth_ret, const ip4_addr_t **ip_ret)
+{
+  s16_t i;
+
+  LWIP_ASSERT("eth_ret != NULL && ip_ret != NULL",
+              eth_ret != NULL && ip_ret != NULL);
+
+  LWIP_UNUSED_ARG(netif);
+
+  i = etharp_find_entry(ipaddr, ETHARP_FLAG_FIND_ONLY, netif);
+  if ((i >= 0) && (arp_table[i].state >= ETHARP_STATE_STABLE)) {
+    *eth_ret = &arp_table[i].ethaddr;
+    *ip_ret = &arp_table[i].ipaddr;
+    return i;
+  }
+  return -1;
+}
+
+/**
+ * Possibility to iterate over stable ARP table entries
+ *
+ * @param i entry number, 0 to ARP_TABLE_SIZE
+ * @param ipaddr return value: IP address
+ * @param netif return value: points to interface
+ * @param eth_ret return value: ETH address
+ * @return 1 on valid index, 0 otherwise
+ */
+int
+etharp_get_entry(size_t i, ip4_addr_t **ipaddr, struct netif **netif, struct eth_addr **eth_ret)
+{
+  LWIP_ASSERT("ipaddr != NULL", ipaddr != NULL);
+  LWIP_ASSERT("netif != NULL", netif != NULL);
+  LWIP_ASSERT("eth_ret != NULL", eth_ret != NULL);
+
+  if ((i < ARP_TABLE_SIZE) && (arp_table[i].state >= ETHARP_STATE_STABLE)) {
+    *ipaddr  = &arp_table[i].ipaddr;
+    *netif   = arp_table[i].netif;
+    *eth_ret = &arp_table[i].ethaddr;
+    return 1;
+  } else {
+    return 0;
+  }
+}
+
+/**
+ * Responds to ARP requests to us. Upon ARP replies to us, add entry to cache
+ * send out queued IP packets. Updates cache with snooped address pairs.
+ *
+ * Should be called for incoming ARP packets. The pbuf in the argument
+ * is freed by this function.
+ *
+ * @param p The ARP packet that arrived on netif. Is freed by this function.
+ * @param netif The lwIP network interface on which the ARP packet pbuf arrived.
+ *
+ * @see pbuf_free()
+ */
+void
+etharp_input(struct pbuf *p, struct netif *netif)
+{
+  struct etharp_hdr *hdr;
+  /* these are aligned properly, whereas the ARP header fields might not be */
+  ip4_addr_t sipaddr, dipaddr;
+  u8_t for_us, from_us;
+
+  LWIP_ASSERT_CORE_LOCKED();
+
+  LWIP_ERROR("netif != NULL", (netif != NULL), return;);
+
+  hdr = (struct etharp_hdr *)p->payload;
+
+  /* RFC 826 "Packet Reception": */
+  if ((hdr->hwtype != PP_HTONS(LWIP_IANA_HWTYPE_ETHERNET)) ||
+      (hdr->hwlen != ETH_HWADDR_LEN) ||
+      (hdr->protolen != sizeof(ip4_addr_t)) ||
+      (hdr->proto != PP_HTONS(ETHTYPE_IP)))  {
+    LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
+                ("etharp_input: packet dropped, wrong hw type, hwlen, proto, protolen or ethernet type (%"U16_F"/%"U16_F"/%"U16_F"/%"U16_F")\n",
+                 hdr->hwtype, (u16_t)hdr->hwlen, hdr->proto, (u16_t)hdr->protolen));
+    ETHARP_STATS_INC(etharp.proterr);
+    ETHARP_STATS_INC(etharp.drop);
+    pbuf_free(p);
+    return;
+  }
+  ETHARP_STATS_INC(etharp.recv);
+
+#if LWIP_ACD
+  /* We have to check if a host already has configured our ip address and
+   * continuously check if there is a host with this IP-address so we can
+   * detect collisions.
+   * acd_arp_reply ensures the detection of conflicts. It will handle possible
+   * defending or retreating and will make sure a new IP address is selected.
+   * etharp_input does not need to handle packets that originate "from_us".
+   */
+  acd_arp_reply(netif, hdr);
+#endif /* LWIP_ACD */
+
+  /* Copy struct ip4_addr_wordaligned to aligned ip4_addr, to support compilers without
+   * structure packing (not using structure copy which breaks strict-aliasing rules). */
+  IPADDR_WORDALIGNED_COPY_TO_IP4_ADDR_T(&sipaddr, &hdr->sipaddr);
+  IPADDR_WORDALIGNED_COPY_TO_IP4_ADDR_T(&dipaddr, &hdr->dipaddr);
+
+  /* this interface is not configured? */
+  if (ip4_addr_isany_val(*netif_ip4_addr(netif))) {
+    for_us = 0;
+    from_us = 0;
+  } else {
+    /* ARP packet directed to us? */
+    for_us = (u8_t)ip4_addr_eq(&dipaddr, netif_ip4_addr(netif));
+    /* ARP packet from us? */
+    from_us = (u8_t)ip4_addr_eq(&sipaddr, netif_ip4_addr(netif));
+  }
+
+  /* ARP message directed to us?
+      -> add IP address in ARP cache; assume requester wants to talk to us,
+         can result in directly sending the queued packets for this host.
+     ARP message not directed to us?
+      ->  update the source IP address in the cache, if present */
+  etharp_update_arp_entry(netif, &sipaddr, &(hdr->shwaddr),
+                          for_us ? ETHARP_FLAG_TRY_HARD : ETHARP_FLAG_FIND_ONLY);
+
+  /* now act on the message itself */
+  switch (hdr->opcode) {
+    /* ARP request? */
+    case PP_HTONS(ARP_REQUEST):
+      /* ARP request. If it asked for our address, we send out a
+       * reply. In any case, we time-stamp any existing ARP entry,
+       * and possibly send out an IP packet that was queued on it. */
+
+      LWIP_DEBUGF (ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_input: incoming ARP request\n"));
+      /* ARP request for our address? */
+      if (for_us && !from_us) {
+        /* send ARP response */
+        etharp_raw(netif,
+                   (struct eth_addr *)netif->hwaddr, &hdr->shwaddr,
+                   (struct eth_addr *)netif->hwaddr, netif_ip4_addr(netif),
+                   &hdr->shwaddr, &sipaddr,
+                   ARP_REPLY);
+        /* we are not configured? */
+      } else if (ip4_addr_isany_val(*netif_ip4_addr(netif))) {
+        /* { for_us == 0 and netif->ip_addr.addr == 0 } */
+        LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_input: we are unconfigured, ARP request ignored.\n"));
+        /* request was not directed to us */
+      } else {
+        /* { for_us == 0 and netif->ip_addr.addr != 0 } */
+        LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_input: ARP request was not for us.\n"));
+      }
+      break;
+    case PP_HTONS(ARP_REPLY):
+      /* ARP reply. We already updated the ARP cache earlier. */
+      LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_input: incoming ARP reply\n"));
+      break;
+    default:
+      LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_input: ARP unknown opcode type %"S16_F"\n", lwip_htons(hdr->opcode)));
+      ETHARP_STATS_INC(etharp.err);
+      break;
+  }
+  /* free ARP packet */
+  pbuf_free(p);
+}
+
+/** Just a small helper function that sends a pbuf to an ethernet address
+ * in the arp_table specified by the index 'arp_idx'.
+ */
+static err_t
+etharp_output_to_arp_index(struct netif *netif, struct pbuf *q, netif_addr_idx_t arp_idx)
+{
+  LWIP_ASSERT("arp_table[arp_idx].state >= ETHARP_STATE_STABLE",
+              arp_table[arp_idx].state >= ETHARP_STATE_STABLE);
+  /* if arp table entry is about to expire: re-request it,
+     but only if its state is ETHARP_STATE_STABLE to prevent flooding the
+     network with ARP requests if this address is used frequently. */
+  if (arp_table[arp_idx].state == ETHARP_STATE_STABLE) {
+    if (arp_table[arp_idx].ctime >= ARP_AGE_REREQUEST_USED_BROADCAST) {
+      /* issue a standard request using broadcast */
+      if (etharp_request(netif, &arp_table[arp_idx].ipaddr) == ERR_OK) {
+        arp_table[arp_idx].state = ETHARP_STATE_STABLE_REREQUESTING_1;
+      }
+    } else if (arp_table[arp_idx].ctime >= ARP_AGE_REREQUEST_USED_UNICAST) {
+      /* issue a unicast request (for 15 seconds) to prevent unnecessary broadcast */
+      if (etharp_request_dst(netif, &arp_table[arp_idx].ipaddr, &arp_table[arp_idx].ethaddr) == ERR_OK) {
+        arp_table[arp_idx].state = ETHARP_STATE_STABLE_REREQUESTING_1;
+      }
+    }
+  }
+
+  return ethernet_output(netif, q, (struct eth_addr *)(netif->hwaddr), &arp_table[arp_idx].ethaddr, ETHTYPE_IP);
+}
+
+/**
+ * Resolve and fill-in Ethernet address header for outgoing IP packet.
+ *
+ * For IP multicast and broadcast, corresponding Ethernet addresses
+ * are selected and the packet is transmitted on the link.
+ *
+ * For unicast addresses, the packet is submitted to etharp_query(). In
+ * case the IP address is outside the local network, the IP address of
+ * the gateway is used.
+ *
+ * @param netif The lwIP network interface which the IP packet will be sent on.
+ * @param q The pbuf(s) containing the IP packet to be sent.
+ * @param ipaddr The IP address of the packet destination.
+ *
+ * @return
+ * - ERR_RTE No route to destination (no gateway to external networks),
+ * or the return type of either etharp_query() or ethernet_output().
+ */
+err_t
+etharp_output(struct netif *netif, struct pbuf *q, const ip4_addr_t *ipaddr)
+{
+  const struct eth_addr *dest;
+  struct eth_addr mcastaddr;
+  const ip4_addr_t *dst_addr = ipaddr;
+
+  LWIP_ASSERT_CORE_LOCKED();
+  LWIP_ASSERT("netif != NULL", netif != NULL);
+  LWIP_ASSERT("q != NULL", q != NULL);
+  LWIP_ASSERT("ipaddr != NULL", ipaddr != NULL);
+
+  /* Determine on destination hardware address. Broadcasts and multicasts
+   * are special, other IP addresses are looked up in the ARP table. */
+
+  /* broadcast destination IP address? */
+  if (ip4_addr_isbroadcast(ipaddr, netif)) {
+    /* broadcast on Ethernet also */
+    dest = (const struct eth_addr *)&ethbroadcast;
+    /* multicast destination IP address? */
+  } else if (ip4_addr_ismulticast(ipaddr)) {
+    /* Hash IP multicast address to MAC address.*/
+    mcastaddr.addr[0] = LL_IP4_MULTICAST_ADDR_0;
+    mcastaddr.addr[1] = LL_IP4_MULTICAST_ADDR_1;
+    mcastaddr.addr[2] = LL_IP4_MULTICAST_ADDR_2;
+    mcastaddr.addr[3] = ip4_addr2(ipaddr) & 0x7f;
+    mcastaddr.addr[4] = ip4_addr3(ipaddr);
+    mcastaddr.addr[5] = ip4_addr4(ipaddr);
+    /* destination Ethernet address is multicast */
+    dest = &mcastaddr;
+    /* unicast destination IP address? */
+  } else {
+    netif_addr_idx_t i;
+    /* outside local network? if so, this can neither be a global broadcast nor
+       a subnet broadcast. */
+    if (!ip4_addr_net_eq(ipaddr, netif_ip4_addr(netif), netif_ip4_netmask(netif)) &&
+        !ip4_addr_islinklocal(ipaddr)) {
+#if LWIP_AUTOIP
+      struct ip_hdr *iphdr = LWIP_ALIGNMENT_CAST(struct ip_hdr *, q->payload);
+      /* According to RFC 3297, chapter 2.6.2 (Forwarding Rules), a packet with
+         a link-local source address must always be "directly to its destination
+         on the same physical link. The host MUST NOT send the packet to any
+         router for forwarding". */
+      if (!ip4_addr_islinklocal(&iphdr->src))
+#endif /* LWIP_AUTOIP */
+      {
+#ifdef LWIP_HOOK_ETHARP_GET_GW
+        /* For advanced routing, a single default gateway might not be enough, so get
+           the IP address of the gateway to handle the current destination address. */
+        dst_addr = LWIP_HOOK_ETHARP_GET_GW(netif, ipaddr);
+        if (dst_addr == NULL)
+#endif /* LWIP_HOOK_ETHARP_GET_GW */
+        {
+          /* interface has default gateway? */
+          if (!ip4_addr_isany_val(*netif_ip4_gw(netif))) {
+            /* send to hardware address of default gateway IP address */
+            dst_addr = netif_ip4_gw(netif);
+            /* no default gateway available */
+          } else {
+            /* no route to destination error (default gateway missing) */
+            return ERR_RTE;
+          }
+        }
+      }
+    }
+#if LWIP_NETIF_HWADDRHINT
+    if (netif->hints != NULL) {
+      /* per-pcb cached entry was given */
+      netif_addr_idx_t etharp_cached_entry = netif->hints->addr_hint;
+      if (etharp_cached_entry < ARP_TABLE_SIZE) {
+#endif /* LWIP_NETIF_HWADDRHINT */
+        if ((arp_table[etharp_cached_entry].state >= ETHARP_STATE_STABLE) &&
+#if ETHARP_TABLE_MATCH_NETIF
+            (arp_table[etharp_cached_entry].netif == netif) &&
+#endif
+            (ip4_addr_eq(dst_addr, &arp_table[etharp_cached_entry].ipaddr))) {
+          /* the per-pcb-cached entry is stable and the right one! */
+          ETHARP_STATS_INC(etharp.cachehit);
+          return etharp_output_to_arp_index(netif, q, etharp_cached_entry);
+        }
+#if LWIP_NETIF_HWADDRHINT
+      }
+    }
+#endif /* LWIP_NETIF_HWADDRHINT */
+
+    /* find stable entry: do this here since this is a critical path for
+       throughput and etharp_find_entry() is kind of slow */
+    for (i = 0; i < ARP_TABLE_SIZE; i++) {
+      if ((arp_table[i].state >= ETHARP_STATE_STABLE) &&
+#if ETHARP_TABLE_MATCH_NETIF
+          (arp_table[i].netif == netif) &&
+#endif
+          (ip4_addr_eq(dst_addr, &arp_table[i].ipaddr))) {
+        /* found an existing, stable entry */
+        ETHARP_SET_ADDRHINT(netif, i);
+        return etharp_output_to_arp_index(netif, q, i);
+      }
+    }
+    /* no stable entry found, use the (slower) query function:
+       queue on destination Ethernet address belonging to ipaddr */
+    return etharp_query(netif, dst_addr, q);
+  }
+
+  /* continuation for multicast/broadcast destinations */
+  /* obtain source Ethernet address of the given interface */
+  /* send packet directly on the link */
+  return ethernet_output(netif, q, (struct eth_addr *)(netif->hwaddr), dest, ETHTYPE_IP);
+}
+
+/**
+ * Send an ARP request for the given IP address and/or queue a packet.
+ *
+ * If the IP address was not yet in the cache, a pending ARP cache entry
+ * is added and an ARP request is sent for the given address. The packet
+ * is queued on this entry.
+ *
+ * If the IP address was already pending in the cache, a new ARP request
+ * is sent for the given address. The packet is queued on this entry.
+ *
+ * If the IP address was already stable in the cache, and a packet is
+ * given, it is directly sent and no ARP request is sent out.
+ *
+ * If the IP address was already stable in the cache, and no packet is
+ * given, an ARP request is sent out.
+ *
+ * @param netif The lwIP network interface on which ipaddr
+ * must be queried for.
+ * @param ipaddr The IP address to be resolved.
+ * @param q If non-NULL, a pbuf that must be delivered to the IP address.
+ * q is not freed by this function.
+ *
+ * @note q must only be ONE packet, not a packet queue!
+ *
+ * @return
+ * - ERR_BUF Could not make room for Ethernet header.
+ * - ERR_MEM Hardware address unknown, and no more ARP entries available
+ *   to query for address or queue the packet.
+ * - ERR_MEM Could not queue packet due to memory shortage.
+ * - ERR_RTE No route to destination (no gateway to external networks).
+ * - ERR_ARG Non-unicast address given, those will not appear in ARP cache.
+ *
+ */
+err_t
+etharp_query(struct netif *netif, const ip4_addr_t *ipaddr, struct pbuf *q)
+{
+  struct eth_addr *srcaddr = (struct eth_addr *)netif->hwaddr;
+  err_t result = ERR_MEM;
+  int is_new_entry = 0;
+  s16_t i_err;
+  netif_addr_idx_t i;
+
+  /* non-unicast address? */
+  if (ip4_addr_isbroadcast(ipaddr, netif) ||
+      ip4_addr_ismulticast(ipaddr) ||
+      ip4_addr_isany(ipaddr)) {
+    LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: will not add non-unicast IP address to ARP cache\n"));
+    return ERR_ARG;
+  }
+
+  /* find entry in ARP cache, ask to create entry if queueing packet */
+  i_err = etharp_find_entry(ipaddr, ETHARP_FLAG_TRY_HARD, netif);
+
+  /* could not find or create entry? */
+  if (i_err < 0) {
+    LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: could not create ARP entry\n"));
+    if (q) {
+      LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: packet dropped\n"));
+      ETHARP_STATS_INC(etharp.memerr);
+    }
+    return (err_t)i_err;
+  }
+  LWIP_ASSERT("type overflow", (size_t)i_err < NETIF_ADDR_IDX_MAX);
+  i = (netif_addr_idx_t)i_err;
+
+  /* mark a fresh entry as pending (we just sent a request) */
+  if (arp_table[i].state == ETHARP_STATE_EMPTY) {
+    is_new_entry = 1;
+    arp_table[i].state = ETHARP_STATE_PENDING;
+    /* record network interface for re-sending arp request in etharp_tmr */
+    arp_table[i].netif = netif;
+  }
+
+  /* { i is either a STABLE or (new or existing) PENDING entry } */
+  LWIP_ASSERT("arp_table[i].state == PENDING or STABLE",
+              ((arp_table[i].state == ETHARP_STATE_PENDING) ||
+               (arp_table[i].state >= ETHARP_STATE_STABLE)));
+
+  /* do we have a new entry? or an implicit query request? */
+  if (is_new_entry || (q == NULL)) {
+    /* try to resolve it; send out ARP request */
+    result = etharp_request(netif, ipaddr);
+    if (result != ERR_OK) {
+      /* ARP request couldn't be sent */
+      /* We don't re-send arp request in etharp_tmr, but we still queue packets,
+         since this failure could be temporary, and the next packet calling
+         etharp_query again could lead to sending the queued packets. */
+    } else {
+      /* ARP request successfully sent */
+      if ((arp_table[i].state == ETHARP_STATE_PENDING) && !is_new_entry) {
+        /* A new ARP request has been sent for a pending entry. Reset the ctime to
+           not let it expire too fast. */
+        LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: reset ctime for entry %"S16_F"\n", (s16_t)i));
+        arp_table[i].ctime = 0;
+      }
+    }
+    if (q == NULL) {
+      return result;
+    }
+  }
+
+  /* packet given? */
+  LWIP_ASSERT("q != NULL", q != NULL);
+  /* stable entry? */
+  if (arp_table[i].state >= ETHARP_STATE_STABLE) {
+    /* we have a valid IP->Ethernet address mapping */
+    ETHARP_SET_ADDRHINT(netif, i);
+    /* send the packet */
+    result = ethernet_output(netif, q, srcaddr, &(arp_table[i].ethaddr), ETHTYPE_IP);
+    /* pending entry? (either just created or already pending */
+  } else if (arp_table[i].state == ETHARP_STATE_PENDING) {
+    /* entry is still pending, queue the given packet 'q' */
+    struct pbuf *p;
+    int copy_needed = 0;
+    /* IF q includes a pbuf that must be copied, copy the whole chain into a
+     * new PBUF_RAM. See the definition of PBUF_NEEDS_COPY for details. */
+    p = q;
+    while (p) {
+      LWIP_ASSERT("no packet queues allowed!", (p->len != p->tot_len) || (p->next == NULL));
+      if (PBUF_NEEDS_COPY(p)) {
+        copy_needed = 1;
+        break;
+      }
+      p = p->next;
+    }
+    if (copy_needed) {
+      /* copy the whole packet into new pbufs */
+      p = pbuf_clone(PBUF_LINK, PBUF_RAM, q);
+    } else {
+      /* referencing the old pbuf is enough */
+      p = q;
+      pbuf_ref(p);
+    }
+    /* packet could be taken over? */
+    if (p != NULL) {
+      /* queue packet ... */
+#if ARP_QUEUEING
+      struct etharp_q_entry *new_entry;
+      /* allocate a new arp queue entry */
+      new_entry = (struct etharp_q_entry *)memp_malloc(MEMP_ARP_QUEUE);
+      if (new_entry != NULL) {
+        unsigned int qlen = 0;
+        new_entry->next = NULL;
+        new_entry->p = p;
+        if (arp_table[i].q != NULL) {
+          /* queue was already existent, append the new entry to the end */
+          struct etharp_q_entry *r;
+          r = arp_table[i].q;
+          qlen++;
+          while (r->next != NULL) {
+            r = r->next;
+            qlen++;
+          }
+          r->next = new_entry;
+        } else {
+          /* queue did not exist, first item in queue */
+          arp_table[i].q = new_entry;
+        }
+#if ARP_QUEUE_LEN
+        if (qlen >= ARP_QUEUE_LEN) {
+          struct etharp_q_entry *old;
+          old = arp_table[i].q;
+          arp_table[i].q = arp_table[i].q->next;
+          pbuf_free(old->p);
+          memp_free(MEMP_ARP_QUEUE, old);
+        }
+#endif
+        LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: queued packet %p on ARP entry %"U16_F"\n", (void *)q, i));
+        result = ERR_OK;
+      } else {
+        /* the pool MEMP_ARP_QUEUE is empty */
+        pbuf_free(p);
+        LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: could not queue a copy of PBUF_REF packet %p (out of memory)\n", (void *)q));
+        result = ERR_MEM;
+      }
+#else /* ARP_QUEUEING */
+      /* always queue one packet per ARP request only, freeing a previously queued packet */
+      if (arp_table[i].q != NULL) {
+        LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: dropped previously queued packet %p for ARP entry %"U16_F"\n", (void *)q, (u16_t)i));
+        pbuf_free(arp_table[i].q);
+      }
+      arp_table[i].q = p;
+      result = ERR_OK;
+      LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: queued packet %p on ARP entry %"U16_F"\n", (void *)q, (u16_t)i));
+#endif /* ARP_QUEUEING */
+    } else {
+      ETHARP_STATS_INC(etharp.memerr);
+      LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: could not queue a copy of PBUF_REF packet %p (out of memory)\n", (void *)q));
+      result = ERR_MEM;
+    }
+  }
+  return result;
+}
+
+/**
+ * Send a raw ARP packet (opcode and all addresses can be modified)
+ *
+ * @param netif the lwip network interface on which to send the ARP packet
+ * @param ethsrc_addr the source MAC address for the ethernet header
+ * @param ethdst_addr the destination MAC address for the ethernet header
+ * @param hwsrc_addr the source MAC address for the ARP protocol header
+ * @param ipsrc_addr the source IP address for the ARP protocol header
+ * @param hwdst_addr the destination MAC address for the ARP protocol header
+ * @param ipdst_addr the destination IP address for the ARP protocol header
+ * @param opcode the type of the ARP packet
+ * @return ERR_OK if the ARP packet has been sent
+ *         ERR_MEM if the ARP packet couldn't be allocated
+ *         any other err_t on failure
+ */
+static err_t
+etharp_raw(struct netif *netif, const struct eth_addr *ethsrc_addr,
+           const struct eth_addr *ethdst_addr,
+           const struct eth_addr *hwsrc_addr, const ip4_addr_t *ipsrc_addr,
+           const struct eth_addr *hwdst_addr, const ip4_addr_t *ipdst_addr,
+           const u16_t opcode)
+{
+  struct pbuf *p;
+  err_t result = ERR_OK;
+  struct etharp_hdr *hdr;
+
+  LWIP_ASSERT("netif != NULL", netif != NULL);
+
+  /* allocate a pbuf for the outgoing ARP request packet */
+  p = pbuf_alloc(PBUF_LINK, SIZEOF_ETHARP_HDR, PBUF_RAM);
+  /* could allocate a pbuf for an ARP request? */
+  if (p == NULL) {
+    LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
+                ("etharp_raw: could not allocate pbuf for ARP request.\n"));
+    ETHARP_STATS_INC(etharp.memerr);
+    return ERR_MEM;
+  }
+  LWIP_ASSERT("check that first pbuf can hold struct etharp_hdr",
+              (p->len >= SIZEOF_ETHARP_HDR));
+
+  hdr = (struct etharp_hdr *)p->payload;
+  LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_raw: sending raw ARP packet.\n"));
+  hdr->opcode = lwip_htons(opcode);
+
+  LWIP_ASSERT("netif->hwaddr_len must be the same as ETH_HWADDR_LEN for etharp!",
+              (netif->hwaddr_len == ETH_HWADDR_LEN));
+
+  /* Write the ARP MAC-Addresses */
+  SMEMCPY(&hdr->shwaddr, hwsrc_addr, ETH_HWADDR_LEN);
+  SMEMCPY(&hdr->dhwaddr, hwdst_addr, ETH_HWADDR_LEN);
+  /* Copy struct ip4_addr_wordaligned to aligned ip4_addr, to support compilers without
+   * structure packing. */
+  IPADDR_WORDALIGNED_COPY_FROM_IP4_ADDR_T(&hdr->sipaddr, ipsrc_addr);
+  IPADDR_WORDALIGNED_COPY_FROM_IP4_ADDR_T(&hdr->dipaddr, ipdst_addr);
+
+  hdr->hwtype = PP_HTONS(LWIP_IANA_HWTYPE_ETHERNET);
+  hdr->proto = PP_HTONS(ETHTYPE_IP);
+  /* set hwlen and protolen */
+  hdr->hwlen = ETH_HWADDR_LEN;
+  hdr->protolen = sizeof(ip4_addr_t);
+
+  /* send ARP query */
+#if LWIP_AUTOIP
+  /* If we are using Link-Local, all ARP packets that contain a Link-Local
+   * 'sender IP address' MUST be sent using link-layer broadcast instead of
+   * link-layer unicast. (See RFC3927 Section 2.5, last paragraph) */
+  if (ip4_addr_islinklocal(ipsrc_addr)) {
+    ethernet_output(netif, p, ethsrc_addr, &ethbroadcast, ETHTYPE_ARP);
+  } else
+#endif /* LWIP_AUTOIP */
+  {
+    ethernet_output(netif, p, ethsrc_addr, ethdst_addr, ETHTYPE_ARP);
+  }
+
+  ETHARP_STATS_INC(etharp.xmit);
+  /* free ARP query packet */
+  pbuf_free(p);
+  p = NULL;
+  /* could not allocate pbuf for ARP request */
+
+  return result;
+}
+
+/**
+ * Send an ARP request packet asking for ipaddr to a specific eth address.
+ * Used to send unicast request to refresh the ARP table just before an entry
+ * times out
+ *
+ * @param netif the lwip network interface on which to send the request
+ * @param ipaddr the IP address for which to ask
+ * @param hw_dst_addr the ethernet address to send this packet to
+ * @return ERR_OK if the request has been sent
+ *         ERR_MEM if the ARP packet couldn't be allocated
+ *         any other err_t on failure
+ */
+static err_t
+etharp_request_dst(struct netif *netif, const ip4_addr_t *ipaddr, const struct eth_addr *hw_dst_addr)
+{
+  return etharp_raw(netif, (struct eth_addr *)netif->hwaddr, hw_dst_addr,
+                    (struct eth_addr *)netif->hwaddr, netif_ip4_addr(netif), &ethzero,
+                    ipaddr, ARP_REQUEST);
+}
+
+/**
+ * Send an ARP request packet asking for ipaddr.
+ *
+ * @param netif the lwip network interface on which to send the request
+ * @param ipaddr the IP address for which to ask
+ * @return ERR_OK if the request has been sent
+ *         ERR_MEM if the ARP packet couldn't be allocated
+ *         any other err_t on failure
+ */
+err_t
+etharp_request(struct netif *netif, const ip4_addr_t *ipaddr)
+{
+  LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_request: sending ARP request.\n"));
+  return etharp_request_dst(netif, ipaddr, &ethbroadcast);
+}
+
+#if LWIP_ACD
+/**
+ * Send an ARP request packet probing for an ipaddr.
+ * Used to send probe messages for address conflict detection.
+ *
+ * @param netif the lwip network interface on which to send the request
+ * @param ipaddr the IP address to probe
+ * @return ERR_OK if the request has been sent
+ *         ERR_MEM if the ARP packet couldn't be allocated
+ *         any other err_t on failure
+ */
+err_t
+etharp_acd_probe(struct netif *netif, const ip4_addr_t *ipaddr)
+{
+  return etharp_raw(netif, (struct eth_addr *)netif->hwaddr, &ethbroadcast,
+                    (struct eth_addr *)netif->hwaddr, IP4_ADDR_ANY4, &ethzero,
+                    ipaddr, ARP_REQUEST);
+}
+
+/**
+ * Send an ARP request packet announcing an ipaddr.
+ * Used to send announce messages for address conflict detection.
+ *
+ * @param netif the lwip network interface on which to send the request
+ * @param ipaddr the IP address to announce
+ * @return ERR_OK if the request has been sent
+ *         ERR_MEM if the ARP packet couldn't be allocated
+ *         any other err_t on failure
+ */
+err_t
+etharp_acd_announce(struct netif *netif, const ip4_addr_t *ipaddr)
+{
+  return etharp_raw(netif, (struct eth_addr *)netif->hwaddr, &ethbroadcast,
+                    (struct eth_addr *)netif->hwaddr, ipaddr, &ethzero,
+                    ipaddr, ARP_REQUEST);
+}
+#endif /* LWIP_ACD */
+
+#endif /* LWIP_IPV4 && LWIP_ARP */
diff --git a/lib/lwip/core/ipv4/icmp.c b/lib/lwip/core/ipv4/icmp.c
new file mode 100644
index 0000000000..9a82a67aa9
--- /dev/null
+++ b/lib/lwip/core/ipv4/icmp.c
@@ -0,0 +1,407 @@
+/**
+ * @file
+ * ICMP - Internet Control Message Protocol
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+/* Some ICMP messages should be passed to the transport protocols. This
+   is not implemented. */
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV4 && LWIP_ICMP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/icmp.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/ip.h"
+#include "lwip/def.h"
+#include "lwip/stats.h"
+
+#include <string.h>
+
+#ifdef LWIP_HOOK_FILENAME
+#include LWIP_HOOK_FILENAME
+#endif
+
+/** Small optimization: set to 0 if incoming PBUF_POOL pbuf always can be
+ * used to modify and send a response packet (and to 1 if this is not the case,
+ * e.g. when link header is stripped off when receiving) */
+#ifndef LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN
+#define LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN 1
+#endif /* LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN */
+
+/* The maximum amount of data from the original packet to return in a dest-unreachable */
+#define ICMP_DEST_UNREACH_DATASIZE 8
+
+static void icmp_send_response(struct pbuf *p, u8_t type, u8_t code);
+
+/**
+ * Processes ICMP input packets, called from ip_input().
+ *
+ * Currently only processes icmp echo requests and sends
+ * out the echo response.
+ *
+ * @param p the icmp echo request packet, p->payload pointing to the icmp header
+ * @param inp the netif on which this packet was received
+ */
+void
+icmp_input(struct pbuf *p, struct netif *inp)
+{
+  u8_t type;
+#ifdef LWIP_DEBUG
+  u8_t code;
+#endif /* LWIP_DEBUG */
+  struct icmp_echo_hdr *iecho;
+  const struct ip_hdr *iphdr_in;
+  u16_t hlen;
+  const ip4_addr_t *src;
+
+  ICMP_STATS_INC(icmp.recv);
+  MIB2_STATS_INC(mib2.icmpinmsgs);
+
+  iphdr_in = ip4_current_header();
+  hlen = IPH_HL_BYTES(iphdr_in);
+  if (hlen < IP_HLEN) {
+    LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: short IP header (%"S16_F" bytes) received\n", hlen));
+    goto lenerr;
+  }
+  if (p->len < sizeof(u16_t) * 2) {
+    LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: short ICMP (%"U16_F" bytes) received\n", p->tot_len));
+    goto lenerr;
+  }
+
+  type = *((u8_t *)p->payload);
+#ifdef LWIP_DEBUG
+  code = *(((u8_t *)p->payload) + 1);
+  /* if debug is enabled but debug statement below is somehow disabled: */
+  LWIP_UNUSED_ARG(code);
+#endif /* LWIP_DEBUG */
+  switch (type) {
+    case ICMP_ER:
+      /* This is OK, echo reply might have been parsed by a raw PCB
+         (as obviously, an echo request has been sent, too). */
+      MIB2_STATS_INC(mib2.icmpinechoreps);
+      break;
+    case ICMP_ECHO:
+      MIB2_STATS_INC(mib2.icmpinechos);
+      src = ip4_current_dest_addr();
+      /* multicast destination address? */
+      if (ip4_addr_ismulticast(ip4_current_dest_addr())) {
+#if LWIP_MULTICAST_PING
+        /* For multicast, use address of receiving interface as source address */
+        src = netif_ip4_addr(inp);
+#else /* LWIP_MULTICAST_PING */
+        LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: Not echoing to multicast pings\n"));
+        goto icmperr;
+#endif /* LWIP_MULTICAST_PING */
+      }
+      /* broadcast destination address? */
+      if (ip4_addr_isbroadcast(ip4_current_dest_addr(), ip_current_netif())) {
+#if LWIP_BROADCAST_PING
+        /* For broadcast, use address of receiving interface as source address */
+        src = netif_ip4_addr(inp);
+#else /* LWIP_BROADCAST_PING */
+        LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: Not echoing to broadcast pings\n"));
+        goto icmperr;
+#endif /* LWIP_BROADCAST_PING */
+      }
+      LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ping\n"));
+      if (p->tot_len < sizeof(struct icmp_echo_hdr)) {
+        LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: bad ICMP echo received\n"));
+        goto lenerr;
+      }
+#if CHECKSUM_CHECK_ICMP
+      IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_CHECK_ICMP) {
+        if (inet_chksum_pbuf(p) != 0) {
+          LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: checksum failed for received ICMP echo\n"));
+          pbuf_free(p);
+          ICMP_STATS_INC(icmp.chkerr);
+          MIB2_STATS_INC(mib2.icmpinerrors);
+          return;
+        }
+      }
+#endif
+#if LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN
+      if (pbuf_add_header(p, hlen + PBUF_LINK_HLEN + PBUF_LINK_ENCAPSULATION_HLEN)) {
+        /* p is not big enough to contain link headers
+         * allocate a new one and copy p into it
+         */
+        struct pbuf *r;
+        u16_t alloc_len = (u16_t)(p->tot_len + hlen);
+        if (alloc_len < p->tot_len) {
+          LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: allocating new pbuf failed (tot_len overflow)\n"));
+          goto icmperr;
+        }
+        /* allocate new packet buffer with space for link headers */
+        r = pbuf_alloc(PBUF_LINK, alloc_len, PBUF_RAM);
+        if (r == NULL) {
+          LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: allocating new pbuf failed\n"));
+          goto icmperr;
+        }
+        if (r->len < hlen + sizeof(struct icmp_echo_hdr)) {
+          LWIP_DEBUGF(ICMP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("first pbuf cannot hold the ICMP header\n"));
+          pbuf_free(r);
+          goto icmperr;
+        }
+        /* copy the ip header */
+        MEMCPY(r->payload, iphdr_in, hlen);
+        /* switch r->payload back to icmp header (cannot fail) */
+        if (pbuf_remove_header(r, hlen)) {
+          LWIP_ASSERT("icmp_input: moving r->payload to icmp header failed", 0);
+          pbuf_free(r);
+          goto icmperr;
+        }
+        /* copy the rest of the packet without ip header */
+        if (pbuf_copy(r, p) != ERR_OK) {
+          LWIP_DEBUGF(ICMP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("icmp_input: copying to new pbuf failed\n"));
+          pbuf_free(r);
+          goto icmperr;
+        }
+        /* free the original p */
+        pbuf_free(p);
+        /* we now have an identical copy of p that has room for link headers */
+        p = r;
+      } else {
+        /* restore p->payload to point to icmp header (cannot fail) */
+        if (pbuf_remove_header(p, hlen + PBUF_LINK_HLEN + PBUF_LINK_ENCAPSULATION_HLEN)) {
+          LWIP_ASSERT("icmp_input: restoring original p->payload failed", 0);
+          goto icmperr;
+        }
+      }
+#endif /* LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN */
+      /* At this point, all checks are OK. */
+      /* We generate an answer by switching the dest and src ip addresses,
+       * setting the icmp type to ECHO_RESPONSE and updating the checksum. */
+      iecho = (struct icmp_echo_hdr *)p->payload;
+      if (pbuf_add_header(p, hlen)) {
+        LWIP_DEBUGF(ICMP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("Can't move over header in packet\n"));
+      } else {
+        err_t ret;
+        struct ip_hdr *iphdr = (struct ip_hdr *)p->payload;
+        ip4_addr_copy(iphdr->src, *src);
+        ip4_addr_copy(iphdr->dest, *ip4_current_src_addr());
+        ICMPH_TYPE_SET(iecho, ICMP_ER);
+#if CHECKSUM_GEN_ICMP
+        IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_GEN_ICMP) {
+          /* adjust the checksum */
+          if (iecho->chksum > PP_HTONS(0xffffU - (ICMP_ECHO << 8))) {
+            iecho->chksum = (u16_t)(iecho->chksum + PP_HTONS((u16_t)(ICMP_ECHO << 8)) + 1);
+          } else {
+            iecho->chksum = (u16_t)(iecho->chksum + PP_HTONS(ICMP_ECHO << 8));
+          }
+        }
+#if LWIP_CHECKSUM_CTRL_PER_NETIF
+        else {
+          iecho->chksum = 0;
+        }
+#endif /* LWIP_CHECKSUM_CTRL_PER_NETIF */
+#else /* CHECKSUM_GEN_ICMP */
+        iecho->chksum = 0;
+#endif /* CHECKSUM_GEN_ICMP */
+
+        /* Set the correct TTL and recalculate the header checksum. */
+        IPH_TTL_SET(iphdr, ICMP_TTL);
+        IPH_CHKSUM_SET(iphdr, 0);
+#if CHECKSUM_GEN_IP
+        IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_GEN_IP) {
+          IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, hlen));
+        }
+#endif /* CHECKSUM_GEN_IP */
+
+        ICMP_STATS_INC(icmp.xmit);
+        /* increase number of messages attempted to send */
+        MIB2_STATS_INC(mib2.icmpoutmsgs);
+        /* increase number of echo replies attempted to send */
+        MIB2_STATS_INC(mib2.icmpoutechoreps);
+
+        /* send an ICMP packet */
+        ret = ip4_output_if(p, src, LWIP_IP_HDRINCL,
+                            ICMP_TTL, 0, IP_PROTO_ICMP, inp);
+        if (ret != ERR_OK) {
+          LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ip_output_if returned an error: %s\n", lwip_strerr(ret)));
+        }
+      }
+      break;
+    default:
+      if (type == ICMP_DUR) {
+        MIB2_STATS_INC(mib2.icmpindestunreachs);
+      } else if (type == ICMP_TE) {
+        MIB2_STATS_INC(mib2.icmpintimeexcds);
+      } else if (type == ICMP_PP) {
+        MIB2_STATS_INC(mib2.icmpinparmprobs);
+      } else if (type == ICMP_SQ) {
+        MIB2_STATS_INC(mib2.icmpinsrcquenchs);
+      } else if (type == ICMP_RD) {
+        MIB2_STATS_INC(mib2.icmpinredirects);
+      } else if (type == ICMP_TS) {
+        MIB2_STATS_INC(mib2.icmpintimestamps);
+      } else if (type == ICMP_TSR) {
+        MIB2_STATS_INC(mib2.icmpintimestampreps);
+      } else if (type == ICMP_AM) {
+        MIB2_STATS_INC(mib2.icmpinaddrmasks);
+      } else if (type == ICMP_AMR) {
+        MIB2_STATS_INC(mib2.icmpinaddrmaskreps);
+      }
+      LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ICMP type %"S16_F" code %"S16_F" not supported.\n",
+                               (s16_t)type, (s16_t)code));
+      ICMP_STATS_INC(icmp.proterr);
+      ICMP_STATS_INC(icmp.drop);
+  }
+  pbuf_free(p);
+  return;
+lenerr:
+  pbuf_free(p);
+  ICMP_STATS_INC(icmp.lenerr);
+  MIB2_STATS_INC(mib2.icmpinerrors);
+  return;
+#if LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN || !LWIP_MULTICAST_PING || !LWIP_BROADCAST_PING
+icmperr:
+  pbuf_free(p);
+  ICMP_STATS_INC(icmp.err);
+  MIB2_STATS_INC(mib2.icmpinerrors);
+  return;
+#endif /* LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN || !LWIP_MULTICAST_PING || !LWIP_BROADCAST_PING */
+}
+
+/**
+ * Send an icmp 'destination unreachable' packet, called from ip_input() if
+ * the transport layer protocol is unknown and from udp_input() if the local
+ * port is not bound.
+ *
+ * @param p the input packet for which the 'unreachable' should be sent,
+ *          p->payload pointing to the IP header
+ * @param t type of the 'unreachable' packet
+ */
+void
+icmp_dest_unreach(struct pbuf *p, enum icmp_dur_type t)
+{
+  MIB2_STATS_INC(mib2.icmpoutdestunreachs);
+  icmp_send_response(p, ICMP_DUR, t);
+}
+
+#if IP_FORWARD || IP_REASSEMBLY
+/**
+ * Send a 'time exceeded' packet, called from ip_forward() if TTL is 0.
+ *
+ * @param p the input packet for which the 'time exceeded' should be sent,
+ *          p->payload pointing to the IP header
+ * @param t type of the 'time exceeded' packet
+ */
+void
+icmp_time_exceeded(struct pbuf *p, enum icmp_te_type t)
+{
+  MIB2_STATS_INC(mib2.icmpouttimeexcds);
+  icmp_send_response(p, ICMP_TE, t);
+}
+
+#endif /* IP_FORWARD || IP_REASSEMBLY */
+
+/**
+ * Send an icmp packet in response to an incoming packet.
+ *
+ * @param p the input packet for which the 'unreachable' should be sent,
+ *          p->payload pointing to the IP header
+ * @param type Type of the ICMP header
+ * @param code Code of the ICMP header
+ */
+static void
+icmp_send_response(struct pbuf *p, u8_t type, u8_t code)
+{
+  struct pbuf *q;
+  struct ip_hdr *iphdr;
+  struct icmp_hdr *icmphdr;
+  ip4_addr_t iphdr_src;
+  struct netif *netif;
+  u16_t response_pkt_len;
+
+  /* increase number of messages attempted to send */
+  MIB2_STATS_INC(mib2.icmpoutmsgs);
+
+  /* Keep IP header + up to 8 bytes */
+  response_pkt_len = IP_HLEN + ICMP_DEST_UNREACH_DATASIZE;
+  if (p->tot_len < response_pkt_len) {
+    response_pkt_len = p->tot_len;
+  }
+
+  /* ICMP header + part of original packet */
+  q = pbuf_alloc(PBUF_IP, sizeof(struct icmp_hdr) + response_pkt_len, PBUF_RAM);
+  if (q == NULL) {
+    LWIP_DEBUGF(ICMP_DEBUG, ("icmp_send_response: failed to allocate pbuf for ICMP packet.\n"));
+    MIB2_STATS_INC(mib2.icmpouterrors);
+    return;
+  }
+  LWIP_ASSERT("check that first pbuf can hold icmp message",
+              (q->len >= (sizeof(struct icmp_hdr) + response_pkt_len)));
+
+  iphdr = (struct ip_hdr *)p->payload;
+  LWIP_DEBUGF(ICMP_DEBUG, ("icmp_send_response: Sending ICMP type %02X for packet from ", type));
+  ip4_addr_debug_print_val(ICMP_DEBUG, iphdr->src);
+  LWIP_DEBUGF(ICMP_DEBUG, (" to "));
+  ip4_addr_debug_print_val(ICMP_DEBUG, iphdr->dest);
+  LWIP_DEBUGF(ICMP_DEBUG, ("\n"));
+
+  icmphdr = (struct icmp_hdr *)q->payload;
+  icmphdr->type = type;
+  icmphdr->code = code;
+  icmphdr->data = 0;
+
+  /* copy fields from original packet */
+  pbuf_copy_partial_pbuf(q, p, response_pkt_len, sizeof(struct icmp_hdr));
+
+  ip4_addr_copy(iphdr_src, iphdr->src);
+#ifdef LWIP_HOOK_IP4_ROUTE_SRC
+  {
+    ip4_addr_t iphdr_dst;
+    ip4_addr_copy(iphdr_dst, iphdr->dest);
+    netif = ip4_route_src(&iphdr_dst, &iphdr_src);
+  }
+#else
+  netif = ip4_route(&iphdr_src);
+#endif
+  if (netif != NULL) {
+    /* calculate checksum */
+    icmphdr->chksum = 0;
+#if CHECKSUM_GEN_ICMP
+    IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_ICMP) {
+      icmphdr->chksum = inet_chksum(icmphdr, q->len);
+    }
+#endif
+    ICMP_STATS_INC(icmp.xmit);
+    ip4_output_if(q, NULL, &iphdr_src, ICMP_TTL, 0, IP_PROTO_ICMP, netif);
+  }
+  pbuf_free(q);
+}
+
+#endif /* LWIP_IPV4 && LWIP_ICMP */
diff --git a/lib/lwip/core/ipv4/igmp.c b/lib/lwip/core/ipv4/igmp.c
new file mode 100644
index 0000000000..a74df16faf
--- /dev/null
+++ b/lib/lwip/core/ipv4/igmp.c
@@ -0,0 +1,801 @@
+/**
+ * @file
+ * IGMP - Internet Group Management Protocol
+ *
+ * @defgroup igmp IGMP
+ * @ingroup ip4
+ * To be called from TCPIP thread
+ */
+
+/*
+ * Copyright (c) 2002 CITEL Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of CITEL Technologies Ltd nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY CITEL TECHNOLOGIES AND CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL CITEL TECHNOLOGIES OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This file is a contribution to the lwIP TCP/IP stack.
+ * The Swedish Institute of Computer Science and Adam Dunkels
+ * are specifically granted permission to redistribute this
+ * source code.
+*/
+
+/*-------------------------------------------------------------
+Note 1)
+Although the rfc requires V1 AND V2 capability
+we will only support v2 since now V1 is very old (August 1989)
+V1 can be added if required
+
+a debug print and statistic have been implemented to
+show this up.
+-------------------------------------------------------------
+-------------------------------------------------------------
+Note 2)
+A query for a specific group address (as opposed to ALLHOSTS)
+has now been implemented as I am unsure if it is required
+
+a debug print and statistic have been implemented to
+show this up.
+-------------------------------------------------------------
+-------------------------------------------------------------
+Note 3)
+The router alert rfc 2113 is implemented in outgoing packets
+but not checked rigorously incoming
+-------------------------------------------------------------
+Steve Reynolds
+------------------------------------------------------------*/
+
+/*-----------------------------------------------------------------------------
+ * RFC 988  - Host extensions for IP multicasting                         - V0
+ * RFC 1054 - Host extensions for IP multicasting                         -
+ * RFC 1112 - Host extensions for IP multicasting                         - V1
+ * RFC 2236 - Internet Group Management Protocol, Version 2               - V2  <- this code is based on this RFC (it's the "de facto" standard)
+ * RFC 3376 - Internet Group Management Protocol, Version 3               - V3
+ * RFC 4604 - Using Internet Group Management Protocol Version 3...       - V3+
+ * RFC 2113 - IP Router Alert Option                                      -
+ *----------------------------------------------------------------------------*/
+
+/*-----------------------------------------------------------------------------
+ * Includes
+ *----------------------------------------------------------------------------*/
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV4 && LWIP_IGMP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/igmp.h"
+#include "lwip/debug.h"
+#include "lwip/def.h"
+#include "lwip/mem.h"
+#include "lwip/ip.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/netif.h"
+#include "lwip/stats.h"
+#include "lwip/prot/igmp.h"
+
+#include <string.h>
+
+static struct igmp_group *igmp_lookup_group(struct netif *ifp, const ip4_addr_t *addr);
+static err_t  igmp_remove_group(struct netif *netif, struct igmp_group *group);
+static void   igmp_timeout(struct netif *netif, struct igmp_group *group);
+static void   igmp_start_timer(struct igmp_group *group, u8_t max_time);
+static void   igmp_delaying_member(struct igmp_group *group, u8_t maxresp);
+static err_t  igmp_ip_output_if(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest, struct netif *netif);
+static void   igmp_send(struct netif *netif, struct igmp_group *group, u8_t type);
+
+static ip4_addr_t     allsystems;
+static ip4_addr_t     allrouters;
+
+/**
+ * Initialize the IGMP module
+ */
+void
+igmp_init(void)
+{
+  LWIP_DEBUGF(IGMP_DEBUG, ("igmp_init: initializing\n"));
+
+  IP4_ADDR(&allsystems, 224, 0, 0, 1);
+  IP4_ADDR(&allrouters, 224, 0, 0, 2);
+}
+
+/**
+ * Start IGMP processing on interface
+ *
+ * @param netif network interface on which start IGMP processing
+ */
+err_t
+igmp_start(struct netif *netif)
+{
+  struct igmp_group *group;
+
+  LWIP_DEBUGF(IGMP_DEBUG, ("igmp_start: starting IGMP processing on if %p\n", (void *)netif));
+
+  group = igmp_lookup_group(netif, &allsystems);
+
+  if (group != NULL) {
+    group->group_state = IGMP_GROUP_IDLE_MEMBER;
+    group->use++;
+
+    /* Allow the igmp messages at the MAC level */
+    if (netif->igmp_mac_filter != NULL) {
+      LWIP_DEBUGF(IGMP_DEBUG, ("igmp_start: igmp_mac_filter(ADD "));
+      ip4_addr_debug_print_val(IGMP_DEBUG, allsystems);
+      LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", (void *)netif));
+      netif->igmp_mac_filter(netif, &allsystems, NETIF_ADD_MAC_FILTER);
+    }
+
+    return ERR_OK;
+  }
+
+  return ERR_MEM;
+}
+
+/**
+ * Stop IGMP processing on interface
+ *
+ * @param netif network interface on which stop IGMP processing
+ */
+err_t
+igmp_stop(struct netif *netif)
+{
+  struct igmp_group *group = netif_igmp_data(netif);
+
+  netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_IGMP, NULL);
+
+  while (group != NULL) {
+    struct igmp_group *next = group->next; /* avoid use-after-free below */
+
+    /* disable the group at the MAC level */
+    if (netif->igmp_mac_filter != NULL) {
+      LWIP_DEBUGF(IGMP_DEBUG, ("igmp_stop: igmp_mac_filter(DEL "));
+      ip4_addr_debug_print_val(IGMP_DEBUG, group->group_address);
+      LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", (void *)netif));
+      netif->igmp_mac_filter(netif, &(group->group_address), NETIF_DEL_MAC_FILTER);
+    }
+
+    /* free group */
+    memp_free(MEMP_IGMP_GROUP, group);
+
+    /* move to "next" */
+    group = next;
+  }
+  return ERR_OK;
+}
+
+/**
+ * Report IGMP memberships for this interface
+ *
+ * @param netif network interface on which report IGMP memberships
+ */
+void
+igmp_report_groups(struct netif *netif)
+{
+  struct igmp_group *group = netif_igmp_data(netif);
+
+  LWIP_DEBUGF(IGMP_DEBUG, ("igmp_report_groups: sending IGMP reports on if %p\n", (void *)netif));
+
+  /* Skip the first group in the list, it is always the allsystems group added in igmp_start() */
+  if (group != NULL) {
+    group = group->next;
+  }
+
+  while (group != NULL) {
+    igmp_delaying_member(group, IGMP_JOIN_DELAYING_MEMBER_TMR);
+    group = group->next;
+  }
+}
+
+/**
+ * Search for a group in the netif's igmp group list
+ *
+ * @param ifp the network interface for which to look
+ * @param addr the group ip address to search for
+ * @return a struct igmp_group* if the group has been found,
+ *         NULL if the group wasn't found.
+ */
+struct igmp_group *
+igmp_lookfor_group(struct netif *ifp, const ip4_addr_t *addr)
+{
+  struct igmp_group *group = netif_igmp_data(ifp);
+
+  while (group != NULL) {
+    if (ip4_addr_eq(&(group->group_address), addr)) {
+      return group;
+    }
+    group = group->next;
+  }
+
+  /* to be clearer, we return NULL here instead of
+   * 'group' (which is also NULL at this point).
+   */
+  return NULL;
+}
+
+/**
+ * Search for a specific igmp group and create a new one if not found-
+ *
+ * @param ifp the network interface for which to look
+ * @param addr the group ip address to search
+ * @return a struct igmp_group*,
+ *         NULL on memory error.
+ */
+static struct igmp_group *
+igmp_lookup_group(struct netif *ifp, const ip4_addr_t *addr)
+{
+  struct igmp_group *group;
+  struct igmp_group *list_head = netif_igmp_data(ifp);
+
+  /* Search if the group already exists */
+  group = igmp_lookfor_group(ifp, addr);
+  if (group != NULL) {
+    /* Group already exists. */
+    return group;
+  }
+
+  /* Group doesn't exist yet, create a new one */
+  group = (struct igmp_group *)memp_malloc(MEMP_IGMP_GROUP);
+  if (group != NULL) {
+    ip4_addr_set(&(group->group_address), addr);
+    group->timer              = 0; /* Not running */
+    group->group_state        = IGMP_GROUP_NON_MEMBER;
+    group->last_reporter_flag = 0;
+    group->use                = 0;
+
+    /* Ensure allsystems group is always first in list */
+    if (list_head == NULL) {
+      /* this is the first entry in linked list */
+      LWIP_ASSERT("igmp_lookup_group: first group must be allsystems",
+                  (ip4_addr_eq(addr, &allsystems) != 0));
+      group->next = NULL;
+      netif_set_client_data(ifp, LWIP_NETIF_CLIENT_DATA_INDEX_IGMP, group);
+    } else {
+      /* append _after_ first entry */
+      LWIP_ASSERT("igmp_lookup_group: all except first group must not be allsystems",
+                  (ip4_addr_eq(addr, &allsystems) == 0));
+      group->next = list_head->next;
+      list_head->next = group;
+    }
+  }
+
+  LWIP_DEBUGF(IGMP_DEBUG, ("igmp_lookup_group: %sallocated a new group with address ", (group ? "" : "impossible to ")));
+  ip4_addr_debug_print(IGMP_DEBUG, addr);
+  LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", (void *)ifp));
+
+  return group;
+}
+
+/**
+ * Remove a group from netif's igmp group list, but don't free it yet
+ *
+ * @param group the group to remove from the netif's igmp group list
+ * @return ERR_OK if group was removed from the list, an err_t otherwise
+ */
+static err_t
+igmp_remove_group(struct netif *netif, struct igmp_group *group)
+{
+  err_t err = ERR_OK;
+  struct igmp_group *tmp_group;
+
+  /* Skip the first group in the list, it is always the allsystems group added in igmp_start() */
+  for (tmp_group = netif_igmp_data(netif); tmp_group != NULL; tmp_group = tmp_group->next) {
+    if (tmp_group->next == group) {
+      tmp_group->next = group->next;
+      break;
+    }
+  }
+  /* Group not found in netif's igmp group list */
+  if (tmp_group == NULL) {
+    err = ERR_ARG;
+  }
+
+  return err;
+}
+
+/**
+ * Called from ip_input() if a new IGMP packet is received.
+ *
+ * @param p received igmp packet, p->payload pointing to the igmp header
+ * @param inp network interface on which the packet was received
+ * @param dest destination ip address of the igmp packet
+ */
+void
+igmp_input(struct pbuf *p, struct netif *inp, const ip4_addr_t *dest)
+{
+  struct igmp_msg   *igmp;
+  struct igmp_group *group;
+  struct igmp_group *groupref;
+
+  IGMP_STATS_INC(igmp.recv);
+
+  /* Note that the length CAN be greater than 8 but only 8 are used - All are included in the checksum */
+  if (p->len < IGMP_MINLEN) {
+    pbuf_free(p);
+    IGMP_STATS_INC(igmp.lenerr);
+    LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: length error\n"));
+    return;
+  }
+
+  LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: message from "));
+  ip4_addr_debug_print_val(IGMP_DEBUG, ip4_current_header()->src);
+  LWIP_DEBUGF(IGMP_DEBUG, (" to address "));
+  ip4_addr_debug_print_val(IGMP_DEBUG, ip4_current_header()->dest);
+  LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", (void *)inp));
+
+  /* Now calculate and check the checksum */
+  igmp = (struct igmp_msg *)p->payload;
+  if (inet_chksum(igmp, p->len)) {
+    pbuf_free(p);
+    IGMP_STATS_INC(igmp.chkerr);
+    LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: checksum error\n"));
+    return;
+  }
+
+  /* Packet is ok so find an existing group */
+  group = igmp_lookfor_group(inp, dest); /* use the destination IP address of incoming packet */
+
+  /* If group can be found or create... */
+  if (!group) {
+    pbuf_free(p);
+    IGMP_STATS_INC(igmp.drop);
+    LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP frame not for us\n"));
+    return;
+  }
+
+  /* NOW ACT ON THE INCOMING MESSAGE TYPE... */
+  switch (igmp->igmp_msgtype) {
+    case IGMP_MEMB_QUERY:
+      /* IGMP_MEMB_QUERY to the "all systems" address ? */
+      if ((ip4_addr_eq(dest, &allsystems)) && ip4_addr_isany(&igmp->igmp_group_address)) {
+        /* THIS IS THE GENERAL QUERY */
+        LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: General IGMP_MEMB_QUERY on \"ALL SYSTEMS\" address (224.0.0.1) [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp)));
+
+        if (igmp->igmp_maxresp == 0) {
+          IGMP_STATS_INC(igmp.rx_v1);
+          LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: got an all hosts query with time== 0 - this is V1 and not implemented - treat as v2\n"));
+          igmp->igmp_maxresp = IGMP_V1_DELAYING_MEMBER_TMR;
+        } else {
+          IGMP_STATS_INC(igmp.rx_general);
+        }
+
+        groupref = netif_igmp_data(inp);
+
+        /* Do not send messages on the all systems group address! */
+        /* Skip the first group in the list, it is always the allsystems group added in igmp_start() */
+        if (groupref != NULL) {
+          groupref = groupref->next;
+        }
+
+        while (groupref) {
+          igmp_delaying_member(groupref, igmp->igmp_maxresp);
+          groupref = groupref->next;
+        }
+      } else {
+        /* IGMP_MEMB_QUERY to a specific group ? */
+        if (!ip4_addr_isany(&igmp->igmp_group_address)) {
+          LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_MEMB_QUERY to a specific group "));
+          ip4_addr_debug_print_val(IGMP_DEBUG, igmp->igmp_group_address);
+          if (ip4_addr_eq(dest, &allsystems)) {
+            ip4_addr_t groupaddr;
+            LWIP_DEBUGF(IGMP_DEBUG, (" using \"ALL SYSTEMS\" address (224.0.0.1) [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp)));
+            /* we first need to re-look for the group since we used dest last time */
+            ip4_addr_copy(groupaddr, igmp->igmp_group_address);
+            group = igmp_lookfor_group(inp, &groupaddr);
+          } else {
+            LWIP_DEBUGF(IGMP_DEBUG, (" with the group address as destination [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp)));
+          }
+
+          if (group != NULL) {
+            IGMP_STATS_INC(igmp.rx_group);
+            igmp_delaying_member(group, igmp->igmp_maxresp);
+          } else {
+            IGMP_STATS_INC(igmp.drop);
+          }
+        } else {
+          IGMP_STATS_INC(igmp.proterr);
+        }
+      }
+      break;
+    case IGMP_V2_MEMB_REPORT:
+      LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_V2_MEMB_REPORT\n"));
+      IGMP_STATS_INC(igmp.rx_report);
+      if (group->group_state == IGMP_GROUP_DELAYING_MEMBER) {
+        /* This is on a specific group we have already looked up */
+        group->timer = 0; /* stopped */
+        group->group_state = IGMP_GROUP_IDLE_MEMBER;
+        group->last_reporter_flag = 0;
+      }
+      break;
+    default:
+      LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: unexpected msg %d in state %d on group %p on if %p\n",
+                               igmp->igmp_msgtype, group->group_state, (void *)&group, (void *)inp));
+      IGMP_STATS_INC(igmp.proterr);
+      break;
+  }
+
+  pbuf_free(p);
+  return;
+}
+
+/**
+ * @ingroup igmp
+ * Join a group on one network interface.
+ *
+ * @param ifaddr ip address of the network interface which should join a new group
+ * @param groupaddr the ip address of the group which to join
+ * @return ERR_OK if group was joined on the netif(s), an err_t otherwise
+ */
+err_t
+igmp_joingroup(const ip4_addr_t *ifaddr, const ip4_addr_t *groupaddr)
+{
+  err_t err = ERR_VAL; /* no matching interface */
+  struct netif *netif;
+
+  LWIP_ASSERT_CORE_LOCKED();
+
+  /* make sure it is multicast address */
+  LWIP_ERROR("igmp_joingroup: attempt to join non-multicast address", ip4_addr_ismulticast(groupaddr), return ERR_VAL;);
+  LWIP_ERROR("igmp_joingroup: attempt to join allsystems address", (!ip4_addr_eq(groupaddr, &allsystems)), return ERR_VAL;);
+
+  /* loop through netif's */
+  NETIF_FOREACH(netif) {
+    /* Should we join this interface ? */
+    if ((netif->flags & NETIF_FLAG_IGMP) && ((ip4_addr_isany(ifaddr) || ip4_addr_eq(netif_ip4_addr(netif), ifaddr)))) {
+      err = igmp_joingroup_netif(netif, groupaddr);
+      if (err != ERR_OK) {
+        /* Return an error even if some network interfaces are joined */
+        /** @todo undo any other netif already joined */
+        return err;
+      }
+    }
+  }
+
+  return err;
+}
+
+/**
+ * @ingroup igmp
+ * Join a group on one network interface.
+ *
+ * @param netif the network interface which should join a new group
+ * @param groupaddr the ip address of the group which to join
+ * @return ERR_OK if group was joined on the netif, an err_t otherwise
+ */
+err_t
+igmp_joingroup_netif(struct netif *netif, const ip4_addr_t *groupaddr)
+{
+  struct igmp_group *group;
+
+  LWIP_ASSERT_CORE_LOCKED();
+
+  /* make sure it is multicast address */
+  LWIP_ERROR("igmp_joingroup_netif: attempt to join non-multicast address", ip4_addr_ismulticast(groupaddr), return ERR_VAL;);
+  LWIP_ERROR("igmp_joingroup_netif: attempt to join allsystems address", (!ip4_addr_eq(groupaddr, &allsystems)), return ERR_VAL;);
+
+  /* make sure it is an igmp-enabled netif */
+  LWIP_ERROR("igmp_joingroup_netif: attempt to join on non-IGMP netif", netif->flags & NETIF_FLAG_IGMP, return ERR_VAL;);
+
+  /* find group or create a new one if not found */
+  group = igmp_lookup_group(netif, groupaddr);
+
+  if (group != NULL) {
+    /* This should create a new group, check the state to make sure */
+    if (group->group_state != IGMP_GROUP_NON_MEMBER) {
+      LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup_netif: join to group not in state IGMP_GROUP_NON_MEMBER\n"));
+    } else {
+      /* OK - it was new group */
+      LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup_netif: join to new group: "));
+      ip4_addr_debug_print(IGMP_DEBUG, groupaddr);
+      LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
+
+      /* If first use of the group, allow the group at the MAC level */
+      if ((group->use == 0) && (netif->igmp_mac_filter != NULL)) {
+        LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup_netif: igmp_mac_filter(ADD "));
+        ip4_addr_debug_print(IGMP_DEBUG, groupaddr);
+        LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", (void *)netif));
+        netif->igmp_mac_filter(netif, groupaddr, NETIF_ADD_MAC_FILTER);
+      }
+
+      IGMP_STATS_INC(igmp.tx_join);
+      igmp_send(netif, group, IGMP_V2_MEMB_REPORT);
+
+      igmp_start_timer(group, IGMP_JOIN_DELAYING_MEMBER_TMR);
+
+      /* Need to work out where this timer comes from */
+      group->group_state = IGMP_GROUP_DELAYING_MEMBER;
+    }
+    /* Increment group use */
+    group->use++;
+    /* Join on this interface */
+    return ERR_OK;
+  } else {
+    LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup_netif: Not enough memory to join to group\n"));
+    return ERR_MEM;
+  }
+}
+
+/**
+ * @ingroup igmp
+ * Leave a group on one network interface.
+ *
+ * @param ifaddr ip address of the network interface which should leave a group
+ * @param groupaddr the ip address of the group which to leave
+ * @return ERR_OK if group was left on the netif(s), an err_t otherwise
+ */
+err_t
+igmp_leavegroup(const ip4_addr_t *ifaddr, const ip4_addr_t *groupaddr)
+{
+  err_t err = ERR_VAL; /* no matching interface */
+  struct netif *netif;
+
+  LWIP_ASSERT_CORE_LOCKED();
+
+  /* make sure it is multicast address */
+  LWIP_ERROR("igmp_leavegroup: attempt to leave non-multicast address", ip4_addr_ismulticast(groupaddr), return ERR_VAL;);
+  LWIP_ERROR("igmp_leavegroup: attempt to leave allsystems address", (!ip4_addr_eq(groupaddr, &allsystems)), return ERR_VAL;);
+
+  /* loop through netif's */
+  NETIF_FOREACH(netif) {
+    /* Should we leave this interface ? */
+    if ((netif->flags & NETIF_FLAG_IGMP) && ((ip4_addr_isany(ifaddr) || ip4_addr_eq(netif_ip4_addr(netif), ifaddr)))) {
+      err_t res = igmp_leavegroup_netif(netif, groupaddr);
+      if (err != ERR_OK) {
+        /* Store this result if we have not yet gotten a success */
+        err = res;
+      }
+    }
+  }
+
+  return err;
+}
+
+/**
+ * @ingroup igmp
+ * Leave a group on one network interface.
+ *
+ * @param netif the network interface which should leave a group
+ * @param groupaddr the ip address of the group which to leave
+ * @return ERR_OK if group was left on the netif, an err_t otherwise
+ */
+err_t
+igmp_leavegroup_netif(struct netif *netif, const ip4_addr_t *groupaddr)
+{
+  struct igmp_group *group;
+
+  LWIP_ASSERT_CORE_LOCKED();
+
+  /* make sure it is multicast address */
+  LWIP_ERROR("igmp_leavegroup_netif: attempt to leave non-multicast address", ip4_addr_ismulticast(groupaddr), return ERR_VAL;);
+  LWIP_ERROR("igmp_leavegroup_netif: attempt to leave allsystems address", (!ip4_addr_eq(groupaddr, &allsystems)), return ERR_VAL;);
+
+  /* make sure it is an igmp-enabled netif */
+  LWIP_ERROR("igmp_leavegroup_netif: attempt to leave on non-IGMP netif", netif->flags & NETIF_FLAG_IGMP, return ERR_VAL;);
+
+  /* find group */
+  group = igmp_lookfor_group(netif, groupaddr);
+
+  if (group != NULL) {
+    /* Only send a leave if the flag is set according to the state diagram */
+    LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup_netif: Leaving group: "));
+    ip4_addr_debug_print(IGMP_DEBUG, groupaddr);
+    LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
+
+    /* If there is no other use of the group */
+    if (group->use <= 1) {
+      /* Remove the group from the list */
+      igmp_remove_group(netif, group);
+
+      /* If we are the last reporter for this group */
+      if (group->last_reporter_flag) {
+        LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup_netif: sending leaving group\n"));
+        IGMP_STATS_INC(igmp.tx_leave);
+        igmp_send(netif, group, IGMP_LEAVE_GROUP);
+      }
+
+      /* Disable the group at the MAC level */
+      if (netif->igmp_mac_filter != NULL) {
+        LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup_netif: igmp_mac_filter(DEL "));
+        ip4_addr_debug_print(IGMP_DEBUG, groupaddr);
+        LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", (void *)netif));
+        netif->igmp_mac_filter(netif, groupaddr, NETIF_DEL_MAC_FILTER);
+      }
+
+      /* Free group struct */
+      memp_free(MEMP_IGMP_GROUP, group);
+    } else {
+      /* Decrement group use */
+      group->use--;
+    }
+    return ERR_OK;
+  } else {
+    LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup_netif: not member of group\n"));
+    return ERR_VAL;
+  }
+}
+
+/**
+ * The igmp timer function (both for NO_SYS=1 and =0)
+ * Should be called every IGMP_TMR_INTERVAL milliseconds (100 ms is default).
+ */
+void
+igmp_tmr(void)
+{
+  struct netif *netif;
+
+  NETIF_FOREACH(netif) {
+    struct igmp_group *group = netif_igmp_data(netif);
+
+    while (group != NULL) {
+      if (group->timer > 0) {
+        group->timer--;
+        if (group->timer == 0) {
+          igmp_timeout(netif, group);
+        }
+      }
+      group = group->next;
+    }
+  }
+}
+
+/**
+ * Called if a timeout for one group is reached.
+ * Sends a report for this group.
+ *
+ * @param group an igmp_group for which a timeout is reached
+ */
+static void
+igmp_timeout(struct netif *netif, struct igmp_group *group)
+{
+  /* If the state is IGMP_GROUP_DELAYING_MEMBER then we send a report for this group
+     (unless it is the allsystems group) */
+  if ((group->group_state == IGMP_GROUP_DELAYING_MEMBER) &&
+      (!(ip4_addr_eq(&(group->group_address), &allsystems)))) {
+    LWIP_DEBUGF(IGMP_DEBUG, ("igmp_timeout: report membership for group with address "));
+    ip4_addr_debug_print_val(IGMP_DEBUG, group->group_address);
+    LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", (void *)netif));
+
+    group->group_state = IGMP_GROUP_IDLE_MEMBER;
+
+    IGMP_STATS_INC(igmp.tx_report);
+    igmp_send(netif, group, IGMP_V2_MEMB_REPORT);
+  }
+}
+
+/**
+ * Start a timer for an igmp group
+ *
+ * @param group the igmp_group for which to start a timer
+ * @param max_time the time in multiples of IGMP_TMR_INTERVAL (decrease with
+ *        every call to igmp_tmr())
+ */
+static void
+igmp_start_timer(struct igmp_group *group, u8_t max_time)
+{
+#ifdef LWIP_RAND
+  group->timer = (u16_t)(max_time > 2 ? (LWIP_RAND() % max_time) : 1);
+#else /* LWIP_RAND */
+  /* ATTENTION: use this only if absolutely necessary! */
+  group->timer = max_time / 2;
+#endif /* LWIP_RAND */
+
+  if (group->timer == 0) {
+    group->timer = 1;
+  }
+}
+
+/**
+ * Delaying membership report for a group if necessary
+ *
+ * @param group the igmp_group for which "delaying" membership report
+ * @param maxresp query delay
+ */
+static void
+igmp_delaying_member(struct igmp_group *group, u8_t maxresp)
+{
+  if ((group->group_state == IGMP_GROUP_IDLE_MEMBER) ||
+      ((group->group_state == IGMP_GROUP_DELAYING_MEMBER) &&
+       ((group->timer == 0) || (maxresp < group->timer)))) {
+    igmp_start_timer(group, maxresp);
+    group->group_state = IGMP_GROUP_DELAYING_MEMBER;
+  }
+}
+
+
+/**
+ * Sends an IP packet on a network interface. This function constructs the IP header
+ * and calculates the IP header checksum. If the source IP address is NULL,
+ * the IP address of the outgoing network interface is filled in as source address.
+ *
+ * @param p the packet to send (p->payload points to the data, e.g. next
+            protocol header; if dest == LWIP_IP_HDRINCL, p already includes an
+            IP header and p->payload points to that IP header)
+ * @param src the source IP address to send from (if src == IP4_ADDR_ANY, the
+ *         IP  address of the netif used to send is used as source address)
+ * @param dest the destination IP address to send the packet to
+ * @param netif the netif on which to send this packet
+ * @return ERR_OK if the packet was sent OK
+ *         ERR_BUF if p doesn't have enough space for IP/LINK headers
+ *         returns errors returned by netif->output
+ */
+static err_t
+igmp_ip_output_if(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest, struct netif *netif)
+{
+  /* This is the "router alert" option */
+  u16_t ra[2];
+  ra[0] = PP_HTONS(ROUTER_ALERT);
+  ra[1] = 0x0000; /* Router shall examine packet */
+  IGMP_STATS_INC(igmp.xmit);
+  return ip4_output_if_opt(p, src, dest, IGMP_TTL, 0, IP_PROTO_IGMP, netif, ra, ROUTER_ALERTLEN);
+}
+
+/**
+ * Send an igmp packet to a specific group.
+ *
+ * @param group the group to which to send the packet
+ * @param type the type of igmp packet to send
+ */
+static void
+igmp_send(struct netif *netif, struct igmp_group *group, u8_t type)
+{
+  struct pbuf     *p    = NULL;
+  struct igmp_msg *igmp = NULL;
+  ip4_addr_t   src  = *IP4_ADDR_ANY4;
+  ip4_addr_t  *dest = NULL;
+
+  /* IP header + "router alert" option + IGMP header */
+  p = pbuf_alloc(PBUF_TRANSPORT, IGMP_MINLEN, PBUF_RAM);
+
+  if (p) {
+    igmp = (struct igmp_msg *)p->payload;
+    LWIP_ASSERT("igmp_send: check that first pbuf can hold struct igmp_msg",
+                (p->len >= sizeof(struct igmp_msg)));
+    ip4_addr_copy(src, *netif_ip4_addr(netif));
+
+    if (type == IGMP_V2_MEMB_REPORT) {
+      dest = &(group->group_address);
+      ip4_addr_copy(igmp->igmp_group_address, group->group_address);
+      group->last_reporter_flag = 1; /* Remember we were the last to report */
+    } else {
+      if (type == IGMP_LEAVE_GROUP) {
+        dest = &allrouters;
+        ip4_addr_copy(igmp->igmp_group_address, group->group_address);
+      }
+    }
+
+    if ((type == IGMP_V2_MEMB_REPORT) || (type == IGMP_LEAVE_GROUP)) {
+      igmp->igmp_msgtype  = type;
+      igmp->igmp_maxresp  = 0;
+      igmp->igmp_checksum = 0;
+      igmp->igmp_checksum = inet_chksum(igmp, IGMP_MINLEN);
+
+      igmp_ip_output_if(p, &src, dest, netif);
+    }
+
+    pbuf_free(p);
+  } else {
+    LWIP_DEBUGF(IGMP_DEBUG, ("igmp_send: not enough memory for igmp_send\n"));
+    IGMP_STATS_INC(igmp.memerr);
+  }
+}
+
+#endif /* LWIP_IPV4 && LWIP_IGMP */
diff --git a/lib/lwip/core/ipv4/ip4.c b/lib/lwip/core/ipv4/ip4.c
new file mode 100644
index 0000000000..e044bff2df
--- /dev/null
+++ b/lib/lwip/core/ipv4/ip4.c
@@ -0,0 +1,1166 @@
+/**
+ * @file
+ * This is the IPv4 layer implementation for incoming and outgoing IP traffic.
+ *
+ * @see ip_frag.c
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV4
+
+#include "lwip/ip.h"
+#include "lwip/def.h"
+#include "lwip/mem.h"
+#include "lwip/ip4_frag.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/netif.h"
+#include "lwip/icmp.h"
+#include "lwip/igmp.h"
+#include "lwip/priv/raw_priv.h"
+#include "lwip/udp.h"
+#include "lwip/priv/tcp_priv.h"
+#include "lwip/autoip.h"
+#include "lwip/stats.h"
+#include "lwip/prot/iana.h"
+
+#include <string.h>
+
+#ifdef LWIP_HOOK_FILENAME
+#include LWIP_HOOK_FILENAME
+#endif
+
+/** Set this to 0 in the rare case of wanting to call an extra function to
+ * generate the IP checksum (in contrast to calculating it on-the-fly). */
+#ifndef LWIP_INLINE_IP_CHKSUM
+#if LWIP_CHECKSUM_CTRL_PER_NETIF
+#define LWIP_INLINE_IP_CHKSUM   0
+#else /* LWIP_CHECKSUM_CTRL_PER_NETIF */
+#define LWIP_INLINE_IP_CHKSUM   1
+#endif /* LWIP_CHECKSUM_CTRL_PER_NETIF */
+#endif
+
+#if LWIP_INLINE_IP_CHKSUM && CHECKSUM_GEN_IP
+#define CHECKSUM_GEN_IP_INLINE  1
+#else
+#define CHECKSUM_GEN_IP_INLINE  0
+#endif
+
+#if LWIP_DHCP || defined(LWIP_IP_ACCEPT_UDP_PORT)
+#define IP_ACCEPT_LINK_LAYER_ADDRESSING 1
+
+/** Some defines for DHCP to let link-layer-addressed packets through while the
+ * netif is down.
+ * To use this in your own application/protocol, define LWIP_IP_ACCEPT_UDP_PORT(port)
+ * to return 1 if the port is accepted and 0 if the port is not accepted.
+ */
+#if LWIP_DHCP && defined(LWIP_IP_ACCEPT_UDP_PORT)
+/* accept DHCP client port and custom port */
+#define IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(port) (((port) == PP_NTOHS(LWIP_IANA_PORT_DHCP_CLIENT)) \
+         || (LWIP_IP_ACCEPT_UDP_PORT(port)))
+#elif defined(LWIP_IP_ACCEPT_UDP_PORT) /* LWIP_DHCP && defined(LWIP_IP_ACCEPT_UDP_PORT) */
+/* accept custom port only */
+#define IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(port) (LWIP_IP_ACCEPT_UDP_PORT(port))
+#else /* LWIP_DHCP && defined(LWIP_IP_ACCEPT_UDP_PORT) */
+/* accept DHCP client port only */
+#define IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(port) ((port) == PP_NTOHS(LWIP_IANA_PORT_DHCP_CLIENT))
+#endif /* LWIP_DHCP && defined(LWIP_IP_ACCEPT_UDP_PORT) */
+
+#else /* LWIP_DHCP */
+#define IP_ACCEPT_LINK_LAYER_ADDRESSING 0
+#endif /* LWIP_DHCP */
+
+/** The IP header ID of the next outgoing IP packet */
+static u16_t ip_id;
+
+#if LWIP_MULTICAST_TX_OPTIONS
+/** The default netif used for multicast */
+static struct netif *ip4_default_multicast_netif;
+
+/**
+ * @ingroup ip4
+ * Set a default netif for IPv4 multicast. */
+void
+ip4_set_default_multicast_netif(struct netif *default_multicast_netif)
+{
+  ip4_default_multicast_netif = default_multicast_netif;
+}
+#endif /* LWIP_MULTICAST_TX_OPTIONS */
+
+#ifdef LWIP_HOOK_IP4_ROUTE_SRC
+/**
+ * Source based IPv4 routing must be fully implemented in
+ * LWIP_HOOK_IP4_ROUTE_SRC(). This function only provides the parameters.
+ */
+struct netif *
+ip4_route_src(const ip4_addr_t *src, const ip4_addr_t *dest)
+{
+  if (src != NULL) {
+    /* when src==NULL, the hook is called from ip4_route(dest) */
+    struct netif *netif = LWIP_HOOK_IP4_ROUTE_SRC(src, dest);
+    if (netif != NULL) {
+      return netif;
+    }
+  }
+  return ip4_route(dest);
+}
+#endif /* LWIP_HOOK_IP4_ROUTE_SRC */
+
+/**
+ * Finds the appropriate network interface for a given IP address. It
+ * searches the list of network interfaces linearly. A match is found
+ * if the masked IP address of the network interface equals the masked
+ * IP address given to the function.
+ *
+ * @param dest the destination IP address for which to find the route
+ * @return the netif on which to send to reach dest
+ */
+struct netif *
+ip4_route(const ip4_addr_t *dest)
+{
+#if !LWIP_SINGLE_NETIF
+  struct netif *netif;
+
+  LWIP_ASSERT_CORE_LOCKED();
+
+#if LWIP_MULTICAST_TX_OPTIONS
+  /* Use administratively selected interface for multicast by default */
+  if (ip4_addr_ismulticast(dest) && ip4_default_multicast_netif) {
+    return ip4_default_multicast_netif;
+  }
+#endif /* LWIP_MULTICAST_TX_OPTIONS */
+
+  /* bug #54569: in case LWIP_SINGLE_NETIF=1 and LWIP_DEBUGF() disabled, the following loop is optimized away */
+  LWIP_UNUSED_ARG(dest);
+
+  /* iterate through netifs */
+  NETIF_FOREACH(netif) {
+    /* is the netif up, does it have a link and a valid address? */
+    if (netif_is_up(netif) && netif_is_link_up(netif) && !ip4_addr_isany_val(*netif_ip4_addr(netif))) {
+      /* network mask matches? */
+      if (ip4_addr_net_eq(dest, netif_ip4_addr(netif), netif_ip4_netmask(netif))) {
+        /* return netif on which to forward IP packet */
+        return netif;
+      }
+      /* gateway matches on a non broadcast interface? (i.e. peer in a point to point interface) */
+      if (((netif->flags & NETIF_FLAG_BROADCAST) == 0) && ip4_addr_eq(dest, netif_ip4_gw(netif))) {
+        /* return netif on which to forward IP packet */
+        return netif;
+      }
+    }
+  }
+
+#if LWIP_NETIF_LOOPBACK && !LWIP_HAVE_LOOPIF
+  /* loopif is disabled, loopback traffic is passed through any netif */
+  if (ip4_addr_isloopback(dest)) {
+    /* don't check for link on loopback traffic */
+    if ((netif_default != NULL) && netif_is_up(netif_default)) {
+      return netif_default;
+    }
+    /* default netif is not up, just use any netif for loopback traffic */
+    NETIF_FOREACH(netif) {
+      if (netif_is_up(netif)) {
+        return netif;
+      }
+    }
+    return NULL;
+  }
+#endif /* LWIP_NETIF_LOOPBACK && !LWIP_HAVE_LOOPIF */
+
+#ifdef LWIP_HOOK_IP4_ROUTE_SRC
+  netif = LWIP_HOOK_IP4_ROUTE_SRC(NULL, dest);
+  if (netif != NULL) {
+    return netif;
+  }
+#elif defined(LWIP_HOOK_IP4_ROUTE)
+  netif = LWIP_HOOK_IP4_ROUTE(dest);
+  if (netif != NULL) {
+    return netif;
+  }
+#endif
+#endif /* !LWIP_SINGLE_NETIF */
+
+  if ((netif_default == NULL) || !netif_is_up(netif_default) || !netif_is_link_up(netif_default) ||
+      ip4_addr_isany_val(*netif_ip4_addr(netif_default)) || ip4_addr_isloopback(dest)) {
+    /* No matching netif found and default netif is not usable.
+       If this is not good enough for you, use LWIP_HOOK_IP4_ROUTE() */
+    LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip4_route: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+                ip4_addr1_16(dest), ip4_addr2_16(dest), ip4_addr3_16(dest), ip4_addr4_16(dest)));
+    IP_STATS_INC(ip.rterr);
+    MIB2_STATS_INC(mib2.ipoutnoroutes);
+    return NULL;
+  }
+
+  return netif_default;
+}
+
+#if IP_FORWARD
+/**
+ * Determine whether an IP address is in a reserved set of addresses
+ * that may not be forwarded, or whether datagrams to that destination
+ * may be forwarded.
+ * @param p the packet to forward
+ * @return 1: can forward 0: discard
+ */
+static int
+ip4_canforward(struct pbuf *p)
+{
+  u32_t addr = lwip_htonl(ip4_addr_get_u32(ip4_current_dest_addr()));
+
+#ifdef LWIP_HOOK_IP4_CANFORWARD
+  int ret = LWIP_HOOK_IP4_CANFORWARD(p, addr);
+  if (ret >= 0) {
+    return ret;
+  }
+#endif /* LWIP_HOOK_IP4_CANFORWARD */
+
+  if (p->flags & PBUF_FLAG_LLBCAST) {
+    /* don't route link-layer broadcasts */
+    return 0;
+  }
+  if ((p->flags & PBUF_FLAG_LLMCAST) || IP_MULTICAST(addr)) {
+    /* don't route link-layer multicasts (use LWIP_HOOK_IP4_CANFORWARD instead) */
+    return 0;
+  }
+  if (IP_EXPERIMENTAL(addr)) {
+    return 0;
+  }
+  if (IP_CLASSA(addr)) {
+    u32_t net = addr & IP_CLASSA_NET;
+    if ((net == 0) || (net == ((u32_t)IP_LOOPBACKNET << IP_CLASSA_NSHIFT))) {
+      /* don't route loopback packets */
+      return 0;
+    }
+  }
+  return 1;
+}
+
+/**
+ * Forwards an IP packet. It finds an appropriate route for the
+ * packet, decrements the TTL value of the packet, adjusts the
+ * checksum and outputs the packet on the appropriate interface.
+ *
+ * @param p the packet to forward (p->payload points to IP header)
+ * @param iphdr the IP header of the input packet
+ * @param inp the netif on which this packet was received
+ */
+static void
+ip4_forward(struct pbuf *p, struct ip_hdr *iphdr, struct netif *inp)
+{
+  struct netif *netif;
+
+  PERF_START;
+  LWIP_UNUSED_ARG(inp);
+
+  if (!ip4_canforward(p)) {
+    goto return_noroute;
+  }
+
+  /* RFC3927 2.7: do not forward link-local addresses */
+  if (ip4_addr_islinklocal(ip4_current_dest_addr())) {
+    LWIP_DEBUGF(IP_DEBUG, ("ip4_forward: not forwarding LLA %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+                           ip4_addr1_16(ip4_current_dest_addr()), ip4_addr2_16(ip4_current_dest_addr()),
+                           ip4_addr3_16(ip4_current_dest_addr()), ip4_addr4_16(ip4_current_dest_addr())));
+    goto return_noroute;
+  }
+
+  /* Find network interface where to forward this IP packet to. */
+  netif = ip4_route_src(ip4_current_src_addr(), ip4_current_dest_addr());
+  if (netif == NULL) {
+    LWIP_DEBUGF(IP_DEBUG, ("ip4_forward: no forwarding route for %"U16_F".%"U16_F".%"U16_F".%"U16_F" found\n",
+                           ip4_addr1_16(ip4_current_dest_addr()), ip4_addr2_16(ip4_current_dest_addr()),
+                           ip4_addr3_16(ip4_current_dest_addr()), ip4_addr4_16(ip4_current_dest_addr())));
+    /* @todo: send ICMP_DUR_NET? */
+    goto return_noroute;
+  }
+#if !IP_FORWARD_ALLOW_TX_ON_RX_NETIF
+  /* Do not forward packets onto the same network interface on which
+   * they arrived. */
+  if (netif == inp) {
+    LWIP_DEBUGF(IP_DEBUG, ("ip4_forward: not bouncing packets back on incoming interface.\n"));
+    goto return_noroute;
+  }
+#endif /* IP_FORWARD_ALLOW_TX_ON_RX_NETIF */
+
+  /* decrement TTL */
+  IPH_TTL_SET(iphdr, IPH_TTL(iphdr) - 1);
+  /* send ICMP if TTL == 0 */
+  if (IPH_TTL(iphdr) == 0) {
+    MIB2_STATS_INC(mib2.ipinhdrerrors);
+#if LWIP_ICMP
+    /* Don't send ICMP messages in response to ICMP messages */
+    if (IPH_PROTO(iphdr) != IP_PROTO_ICMP) {
+      icmp_time_exceeded(p, ICMP_TE_TTL);
+    }
+#endif /* LWIP_ICMP */
+    return;
+  }
+
+  /* Incrementally update the IP checksum. */
+  if (IPH_CHKSUM(iphdr) >= PP_HTONS(0xffffU - 0x100)) {
+    IPH_CHKSUM_SET(iphdr, (u16_t)(IPH_CHKSUM(iphdr) + PP_HTONS(0x100) + 1));
+  } else {
+    IPH_CHKSUM_SET(iphdr, (u16_t)(IPH_CHKSUM(iphdr) + PP_HTONS(0x100)));
+  }
+
+  /* Take care of setting checksums to 0 for checksum offload netifs */
+  if (CHECKSUM_GEN_IP || NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_GEN_IP)) {
+    IPH_CHKSUM_SET(iphdr, 0);
+  }
+  switch (IPH_PROTO(iphdr)) {
+#if LWIP_UDP
+#if LWIP_UDPLITE
+  case IP_PROTO_UDPLITE:
+#endif
+  case IP_PROTO_UDP:
+    if (CHECKSUM_GEN_UDP || NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_GEN_UDP)) {
+      ((struct udp_hdr *)((u8_t *)iphdr + IPH_HL_BYTES(iphdr)))->chksum = 0;
+    }
+    break;
+#endif
+#if LWIP_TCP
+  case IP_PROTO_TCP:
+    if (CHECKSUM_GEN_TCP || NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_GEN_TCP)) {
+      ((struct tcp_hdr *)((u8_t *)iphdr + IPH_HL_BYTES(iphdr)))->chksum = 0;
+    }
+    break;
+#endif
+#if LWIP_ICMP
+  case IP_PROTO_ICMP:
+    if (CHECKSUM_GEN_ICMP || NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_GEN_ICMP)) {
+      ((struct icmp_hdr *)((u8_t *)iphdr + IPH_HL_BYTES(iphdr)))->chksum = 0;
+    }
+    break;
+#endif
+  default:
+    /* there's really nothing to do here other than satisfying 'switch-default' */
+    break;
+  }
+
+  LWIP_DEBUGF(IP_DEBUG, ("ip4_forward: forwarding packet to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+                         ip4_addr1_16(ip4_current_dest_addr()), ip4_addr2_16(ip4_current_dest_addr()),
+                         ip4_addr3_16(ip4_current_dest_addr()), ip4_addr4_16(ip4_current_dest_addr())));
+
+  IP_STATS_INC(ip.fw);
+  MIB2_STATS_INC(mib2.ipforwdatagrams);
+  IP_STATS_INC(ip.xmit);
+
+  PERF_STOP("ip4_forward");
+  /* don't fragment if interface has mtu set to 0 [loopif] */
+  if (netif->mtu && (p->tot_len > netif->mtu)) {
+    if ((IPH_OFFSET(iphdr) & PP_NTOHS(IP_DF)) == 0) {
+#if IP_FRAG
+      ip4_frag(p, netif, ip4_current_dest_addr());
+#else /* IP_FRAG */
+      /* @todo: send ICMP Destination Unreachable code 13 "Communication administratively prohibited"? */
+#endif /* IP_FRAG */
+    } else {
+#if LWIP_ICMP
+      /* send ICMP Destination Unreachable code 4: "Fragmentation Needed and DF Set" */
+      icmp_dest_unreach(p, ICMP_DUR_FRAG);
+#endif /* LWIP_ICMP */
+    }
+    return;
+  }
+  /* transmit pbuf on chosen interface */
+  netif->output(netif, p, ip4_current_dest_addr());
+  return;
+return_noroute:
+  MIB2_STATS_INC(mib2.ipoutnoroutes);
+}
+#endif /* IP_FORWARD */
+
+/** Return true if the current input packet should be accepted on this netif */
+static int
+ip4_input_accept(struct netif *netif)
+{
+  LWIP_DEBUGF(IP_DEBUG, ("ip_input: iphdr->dest 0x%"X32_F" netif->ip_addr 0x%"X32_F" (0x%"X32_F", 0x%"X32_F", 0x%"X32_F")\n",
+                         ip4_addr_get_u32(ip4_current_dest_addr()), ip4_addr_get_u32(netif_ip4_addr(netif)),
+                         ip4_addr_get_u32(ip4_current_dest_addr()) & ip4_addr_get_u32(netif_ip4_netmask(netif)),
+                         ip4_addr_get_u32(netif_ip4_addr(netif)) & ip4_addr_get_u32(netif_ip4_netmask(netif)),
+                         ip4_addr_get_u32(ip4_current_dest_addr()) & ~ip4_addr_get_u32(netif_ip4_netmask(netif))));
+
+  /* interface is up and configured? */
+  if ((netif_is_up(netif)) && (!ip4_addr_isany_val(*netif_ip4_addr(netif)))) {
+    /* unicast to this interface address? */
+    if (ip4_addr_eq(ip4_current_dest_addr(), netif_ip4_addr(netif)) ||
+        /* or broadcast on this interface network address? */
+        ip4_addr_isbroadcast(ip4_current_dest_addr(), netif)
+#if LWIP_NETIF_LOOPBACK && !LWIP_HAVE_LOOPIF
+        || (ip4_addr_get_u32(ip4_current_dest_addr()) == PP_HTONL(IPADDR_LOOPBACK))
+#endif /* LWIP_NETIF_LOOPBACK && !LWIP_HAVE_LOOPIF */
+       ) {
+      LWIP_DEBUGF(IP_DEBUG, ("ip4_input: packet accepted on interface %c%c\n",
+                             netif->name[0], netif->name[1]));
+      /* accept on this netif */
+      return 1;
+    }
+#if LWIP_AUTOIP
+    /* connections to link-local addresses must persist after changing
+        the netif's address (RFC3927 ch. 1.9) */
+    if (autoip_accept_packet(netif, ip4_current_dest_addr())) {
+      LWIP_DEBUGF(IP_DEBUG, ("ip4_input: LLA packet accepted on interface %c%c\n",
+                             netif->name[0], netif->name[1]));
+      /* accept on this netif */
+      return 1;
+    }
+#endif /* LWIP_AUTOIP */
+  }
+  return 0;
+}
+
+/**
+ * This function is called by the network interface device driver when
+ * an IP packet is received. The function does the basic checks of the
+ * IP header such as packet size being at least larger than the header
+ * size etc. If the packet was not destined for us, the packet is
+ * forwarded (using ip_forward). The IP checksum is always checked.
+ *
+ * Finally, the packet is sent to the upper layer protocol input function.
+ *
+ * @param p the received IP packet (p->payload points to IP header)
+ * @param inp the netif on which this packet was received
+ * @return ERR_OK if the packet was processed (could return ERR_* if it wasn't
+ *         processed, but currently always returns ERR_OK)
+ */
+err_t
+ip4_input(struct pbuf *p, struct netif *inp)
+{
+  const struct ip_hdr *iphdr;
+  struct netif *netif;
+  u16_t iphdr_hlen;
+  u16_t iphdr_len;
+#if IP_ACCEPT_LINK_LAYER_ADDRESSING || LWIP_IGMP
+  int check_ip_src = 1;
+#endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING || LWIP_IGMP */
+#if LWIP_RAW
+  raw_input_state_t raw_status;
+#endif /* LWIP_RAW */
+
+  LWIP_ASSERT_CORE_LOCKED();
+
+  IP_STATS_INC(ip.recv);
+  MIB2_STATS_INC(mib2.ipinreceives);
+
+  /* identify the IP header */
+  iphdr = (struct ip_hdr *)p->payload;
+  if (IPH_V(iphdr) != 4) {
+    LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_WARNING, ("IP packet dropped due to bad version number %"U16_F"\n", (u16_t)IPH_V(iphdr)));
+    ip4_debug_print(p);
+    pbuf_free(p);
+    IP_STATS_INC(ip.err);
+    IP_STATS_INC(ip.drop);
+    MIB2_STATS_INC(mib2.ipinhdrerrors);
+    return ERR_OK;
+  }
+
+#ifdef LWIP_HOOK_IP4_INPUT
+  if (LWIP_HOOK_IP4_INPUT(p, inp)) {
+    /* the packet has been eaten */
+    return ERR_OK;
+  }
+#endif
+
+  /* obtain IP header length in bytes */
+  iphdr_hlen = IPH_HL_BYTES(iphdr);
+  /* obtain ip length in bytes */
+  iphdr_len = lwip_ntohs(IPH_LEN(iphdr));
+
+  /* Trim pbuf. This is especially required for packets < 60 bytes. */
+  if (iphdr_len < p->tot_len) {
+    pbuf_realloc(p, iphdr_len);
+  }
+
+  /* header length exceeds first pbuf length, or ip length exceeds total pbuf length? */
+  if ((iphdr_hlen > p->len) || (iphdr_len > p->tot_len) || (iphdr_hlen < IP_HLEN)) {
+    if (iphdr_hlen < IP_HLEN) {
+      LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+                  ("ip4_input: short IP header (%"U16_F" bytes) received, IP packet dropped\n", iphdr_hlen));
+    }
+    if (iphdr_hlen > p->len) {
+      LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+                  ("IP header (len %"U16_F") does not fit in first pbuf (len %"U16_F"), IP packet dropped.\n",
+                   iphdr_hlen, p->len));
+    }
+    if (iphdr_len > p->tot_len) {
+      LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+                  ("IP (len %"U16_F") is longer than pbuf (len %"U16_F"), IP packet dropped.\n",
+                   iphdr_len, p->tot_len));
+    }
+    /* free (drop) packet pbufs */
+    pbuf_free(p);
+    IP_STATS_INC(ip.lenerr);
+    IP_STATS_INC(ip.drop);
+    MIB2_STATS_INC(mib2.ipindiscards);
+    return ERR_OK;
+  }
+
+  /* verify checksum */
+#if CHECKSUM_CHECK_IP
+  IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_CHECK_IP) {
+    if (inet_chksum(iphdr, iphdr_hlen) != 0) {
+
+      LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+                  ("Checksum (0x%"X16_F") failed, IP packet dropped.\n", inet_chksum(iphdr, iphdr_hlen)));
+      ip4_debug_print(p);
+      pbuf_free(p);
+      IP_STATS_INC(ip.chkerr);
+      IP_STATS_INC(ip.drop);
+      MIB2_STATS_INC(mib2.ipinhdrerrors);
+      return ERR_OK;
+    }
+  }
+#endif
+
+  /* copy IP addresses to aligned ip_addr_t */
+  ip_addr_copy_from_ip4(ip_data.current_iphdr_dest, iphdr->dest);
+  ip_addr_copy_from_ip4(ip_data.current_iphdr_src, iphdr->src);
+
+  /* match packet against an interface, i.e. is this packet for us? */
+  if (ip4_addr_ismulticast(ip4_current_dest_addr())) {
+#if LWIP_IGMP
+    if ((inp->flags & NETIF_FLAG_IGMP) && (igmp_lookfor_group(inp, ip4_current_dest_addr()))) {
+      /* IGMP snooping switches need 0.0.0.0 to be allowed as source address (RFC 4541) */
+      ip4_addr_t allsystems;
+      IP4_ADDR(&allsystems, 224, 0, 0, 1);
+      if (ip4_addr_eq(ip4_current_dest_addr(), &allsystems) &&
+          ip4_addr_isany(ip4_current_src_addr())) {
+        check_ip_src = 0;
+      }
+      netif = inp;
+    } else {
+      netif = NULL;
+    }
+#else /* LWIP_IGMP */
+    if ((netif_is_up(inp)) && (!ip4_addr_isany_val(*netif_ip4_addr(inp)))) {
+      netif = inp;
+    } else {
+      netif = NULL;
+    }
+#endif /* LWIP_IGMP */
+  } else {
+    /* start trying with inp. if that's not acceptable, start walking the
+       list of configured netifs. */
+    if (ip4_input_accept(inp)) {
+      netif = inp;
+    } else {
+      netif = NULL;
+#if !LWIP_NETIF_LOOPBACK || LWIP_HAVE_LOOPIF
+      /* Packets sent to the loopback address must not be accepted on an
+       * interface that does not have the loopback address assigned to it,
+       * unless a non-loopback interface is used for loopback traffic. */
+      if (!ip4_addr_isloopback(ip4_current_dest_addr()))
+#endif /* !LWIP_NETIF_LOOPBACK || LWIP_HAVE_LOOPIF */
+      {
+#if !LWIP_SINGLE_NETIF
+        NETIF_FOREACH(netif) {
+          if (netif == inp) {
+            /* we checked that before already */
+            continue;
+          }
+          if (ip4_input_accept(netif)) {
+            break;
+          }
+        }
+#endif /* !LWIP_SINGLE_NETIF */
+      }
+    }
+  }
+
+#if IP_ACCEPT_LINK_LAYER_ADDRESSING
+  /* Pass DHCP messages regardless of destination address. DHCP traffic is addressed
+   * using link layer addressing (such as Ethernet MAC) so we must not filter on IP.
+   * According to RFC 1542 section 3.1.1, referred by RFC 2131).
+   *
+   * If you want to accept private broadcast communication while a netif is down,
+   * define LWIP_IP_ACCEPT_UDP_PORT(dst_port), e.g.:
+   *
+   * #define LWIP_IP_ACCEPT_UDP_PORT(dst_port) ((dst_port) == PP_NTOHS(12345))
+   */
+  if (netif == NULL) {
+    /* remote port is DHCP server? */
+    if (IPH_PROTO(iphdr) == IP_PROTO_UDP) {
+      const struct udp_hdr *udphdr = (const struct udp_hdr *)((const u8_t *)iphdr + iphdr_hlen);
+      LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE, ("ip4_input: UDP packet to DHCP client port %"U16_F"\n",
+                                              lwip_ntohs(udphdr->dest)));
+      if (IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(udphdr->dest)) {
+        LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE, ("ip4_input: DHCP packet accepted.\n"));
+        netif = inp;
+        check_ip_src = 0;
+      }
+    }
+  }
+#endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING */
+
+  /* broadcast or multicast packet source address? Compliant with RFC 1122: 3.2.1.3 */
+#if LWIP_IGMP || IP_ACCEPT_LINK_LAYER_ADDRESSING
+  if (check_ip_src
+#if IP_ACCEPT_LINK_LAYER_ADDRESSING
+      /* DHCP servers need 0.0.0.0 to be allowed as source address (RFC 1.1.2.2: 3.2.1.3/a) */
+      && !ip4_addr_isany_val(*ip4_current_src_addr())
+#endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING */
+     )
+#endif /* LWIP_IGMP || IP_ACCEPT_LINK_LAYER_ADDRESSING */
+  {
+    if ((ip4_addr_isbroadcast(ip4_current_src_addr(), inp)) ||
+        (ip4_addr_ismulticast(ip4_current_src_addr()))) {
+      /* packet source is not valid */
+      LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("ip4_input: packet source is not valid.\n"));
+      /* free (drop) packet pbufs */
+      pbuf_free(p);
+      IP_STATS_INC(ip.drop);
+      MIB2_STATS_INC(mib2.ipinaddrerrors);
+      MIB2_STATS_INC(mib2.ipindiscards);
+      return ERR_OK;
+    }
+  }
+
+  /* packet not for us? */
+  if (netif == NULL) {
+    /* packet not for us, route or discard */
+    LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE, ("ip4_input: packet not for us.\n"));
+#if IP_FORWARD
+    /* non-broadcast packet? */
+    if (!ip4_addr_isbroadcast(ip4_current_dest_addr(), inp)) {
+      /* try to forward IP packet on (other) interfaces */
+      ip4_forward(p, (struct ip_hdr *)p->payload, inp);
+    } else
+#endif /* IP_FORWARD */
+    {
+      IP_STATS_INC(ip.drop);
+      MIB2_STATS_INC(mib2.ipinaddrerrors);
+      MIB2_STATS_INC(mib2.ipindiscards);
+    }
+    pbuf_free(p);
+    return ERR_OK;
+  }
+  /* packet consists of multiple fragments? */
+  if ((IPH_OFFSET(iphdr) & PP_HTONS(IP_OFFMASK | IP_MF)) != 0) {
+#if IP_REASSEMBLY /* packet fragment reassembly code present? */
+    LWIP_DEBUGF(IP_DEBUG, ("IP packet is a fragment (id=0x%04"X16_F" tot_len=%"U16_F" len=%"U16_F" MF=%"U16_F" offset=%"U16_F"), calling ip4_reass()\n",
+                           lwip_ntohs(IPH_ID(iphdr)), p->tot_len, lwip_ntohs(IPH_LEN(iphdr)), (u16_t)!!(IPH_OFFSET(iphdr) & PP_HTONS(IP_MF)), (u16_t)((lwip_ntohs(IPH_OFFSET(iphdr)) & IP_OFFMASK) * 8)));
+    /* reassemble the packet*/
+    p = ip4_reass(p);
+    /* packet not fully reassembled yet? */
+    if (p == NULL) {
+      return ERR_OK;
+    }
+    iphdr = (const struct ip_hdr *)p->payload;
+#else /* IP_REASSEMBLY == 0, no packet fragment reassembly code present */
+    pbuf_free(p);
+    LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("IP packet dropped since it was fragmented (0x%"X16_F") (while IP_REASSEMBLY == 0).\n",
+                lwip_ntohs(IPH_OFFSET(iphdr))));
+    IP_STATS_INC(ip.opterr);
+    IP_STATS_INC(ip.drop);
+    /* unsupported protocol feature */
+    MIB2_STATS_INC(mib2.ipinunknownprotos);
+    return ERR_OK;
+#endif /* IP_REASSEMBLY */
+  }
+
+#if IP_OPTIONS_ALLOWED == 0 /* no support for IP options in the IP header? */
+
+#if LWIP_IGMP
+  /* there is an extra "router alert" option in IGMP messages which we allow for but do not police */
+  if ((iphdr_hlen > IP_HLEN) &&  (IPH_PROTO(iphdr) != IP_PROTO_IGMP)) {
+#else
+  if (iphdr_hlen > IP_HLEN) {
+#endif /* LWIP_IGMP */
+    LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("IP packet dropped since there were IP options (while IP_OPTIONS_ALLOWED == 0).\n"));
+    pbuf_free(p);
+    IP_STATS_INC(ip.opterr);
+    IP_STATS_INC(ip.drop);
+    /* unsupported protocol feature */
+    MIB2_STATS_INC(mib2.ipinunknownprotos);
+    return ERR_OK;
+  }
+#endif /* IP_OPTIONS_ALLOWED == 0 */
+
+  /* send to upper layers */
+  LWIP_DEBUGF(IP_DEBUG, ("ip4_input: \n"));
+  ip4_debug_print(p);
+  LWIP_DEBUGF(IP_DEBUG, ("ip4_input: p->len %"U16_F" p->tot_len %"U16_F"\n", p->len, p->tot_len));
+
+  ip_data.current_netif = netif;
+  ip_data.current_input_netif = inp;
+  ip_data.current_ip4_header = iphdr;
+  ip_data.current_ip_header_tot_len = IPH_HL_BYTES(iphdr);
+
+#if LWIP_RAW
+  /* raw input did not eat the packet? */
+  raw_status = raw_input(p, inp);
+  if (raw_status != RAW_INPUT_EATEN)
+#endif /* LWIP_RAW */
+  {
+    pbuf_remove_header(p, iphdr_hlen); /* Move to payload, no check necessary. */
+
+    switch (IPH_PROTO(iphdr)) {
+#if LWIP_UDP
+      case IP_PROTO_UDP:
+#if LWIP_UDPLITE
+      case IP_PROTO_UDPLITE:
+#endif /* LWIP_UDPLITE */
+        MIB2_STATS_INC(mib2.ipindelivers);
+        udp_input(p, inp);
+        break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+      case IP_PROTO_TCP:
+        MIB2_STATS_INC(mib2.ipindelivers);
+        tcp_input(p, inp);
+        break;
+#endif /* LWIP_TCP */
+#if LWIP_ICMP
+      case IP_PROTO_ICMP:
+        MIB2_STATS_INC(mib2.ipindelivers);
+        icmp_input(p, inp);
+        break;
+#endif /* LWIP_ICMP */
+#if LWIP_IGMP
+      case IP_PROTO_IGMP:
+        igmp_input(p, inp, ip4_current_dest_addr());
+        break;
+#endif /* LWIP_IGMP */
+      default:
+#if LWIP_RAW
+        if (raw_status == RAW_INPUT_DELIVERED) {
+          MIB2_STATS_INC(mib2.ipindelivers);
+        } else
+#endif /* LWIP_RAW */
+        {
+#if LWIP_ICMP
+          /* send ICMP destination protocol unreachable unless is was a broadcast */
+          if (!ip4_addr_isbroadcast(ip4_current_dest_addr(), netif) &&
+              !ip4_addr_ismulticast(ip4_current_dest_addr())) {
+            pbuf_header_force(p, (s16_t)iphdr_hlen); /* Move to ip header, no check necessary. */
+            icmp_dest_unreach(p, ICMP_DUR_PROTO);
+          }
+#endif /* LWIP_ICMP */
+
+          LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("Unsupported transport protocol %"U16_F"\n", (u16_t)IPH_PROTO(iphdr)));
+
+          IP_STATS_INC(ip.proterr);
+          IP_STATS_INC(ip.drop);
+          MIB2_STATS_INC(mib2.ipinunknownprotos);
+        }
+        pbuf_free(p);
+        break;
+    }
+  }
+
+  /* @todo: this is not really necessary... */
+  ip_data.current_netif = NULL;
+  ip_data.current_input_netif = NULL;
+  ip_data.current_ip4_header = NULL;
+  ip_data.current_ip_header_tot_len = 0;
+  ip4_addr_set_any(ip4_current_src_addr());
+  ip4_addr_set_any(ip4_current_dest_addr());
+
+  return ERR_OK;
+}
+
+/**
+ * Sends an IP packet on a network interface. This function constructs
+ * the IP header and calculates the IP header checksum. If the source
+ * IP address is NULL, the IP address of the outgoing network
+ * interface is filled in as source address.
+ * If the destination IP address is LWIP_IP_HDRINCL, p is assumed to already
+ * include an IP header and p->payload points to it instead of the data.
+ *
+ * @param p the packet to send (p->payload points to the data, e.g. next
+            protocol header; if dest == LWIP_IP_HDRINCL, p already includes an
+            IP header and p->payload points to that IP header)
+ * @param src the source IP address to send from (if src == IP4_ADDR_ANY, the
+ *         IP  address of the netif used to send is used as source address)
+ * @param dest the destination IP address to send the packet to
+ * @param ttl the TTL value to be set in the IP header
+ * @param tos the TOS value to be set in the IP header
+ * @param proto the PROTOCOL to be set in the IP header
+ * @param netif the netif on which to send this packet
+ * @return ERR_OK if the packet was sent OK
+ *         ERR_BUF if p doesn't have enough space for IP/LINK headers
+ *         returns errors returned by netif->output
+ *
+ * @note ip_id: RFC791 "some host may be able to simply use
+ *  unique identifiers independent of destination"
+ */
+err_t
+ip4_output_if(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest,
+              u8_t ttl, u8_t tos,
+              u8_t proto, struct netif *netif)
+{
+#if IP_OPTIONS_SEND
+  return ip4_output_if_opt(p, src, dest, ttl, tos, proto, netif, NULL, 0);
+}
+
+/**
+ * Same as ip_output_if() but with the possibility to include IP options:
+ *
+ * @ param ip_options pointer to the IP options, copied into the IP header
+ * @ param optlen length of ip_options
+ */
+err_t
+ip4_output_if_opt(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest,
+                  u8_t ttl, u8_t tos, u8_t proto, struct netif *netif, void *ip_options,
+                  u16_t optlen)
+{
+#endif /* IP_OPTIONS_SEND */
+  const ip4_addr_t *src_used = src;
+  if (dest != LWIP_IP_HDRINCL) {
+    if (ip4_addr_isany(src)) {
+      src_used = netif_ip4_addr(netif);
+    }
+  }
+
+#if IP_OPTIONS_SEND
+  return ip4_output_if_opt_src(p, src_used, dest, ttl, tos, proto, netif,
+                               ip_options, optlen);
+#else /* IP_OPTIONS_SEND */
+  return ip4_output_if_src(p, src_used, dest, ttl, tos, proto, netif);
+#endif /* IP_OPTIONS_SEND */
+}
+
+/**
+ * Same as ip_output_if() but 'src' address is not replaced by netif address
+ * when it is 'any'.
+ */
+err_t
+ip4_output_if_src(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest,
+                  u8_t ttl, u8_t tos,
+                  u8_t proto, struct netif *netif)
+{
+#if IP_OPTIONS_SEND
+  return ip4_output_if_opt_src(p, src, dest, ttl, tos, proto, netif, NULL, 0);
+}
+
+/**
+ * Same as ip_output_if_opt() but 'src' address is not replaced by netif address
+ * when it is 'any'.
+ */
+err_t
+ip4_output_if_opt_src(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest,
+                      u8_t ttl, u8_t tos, u8_t proto, struct netif *netif, void *ip_options,
+                      u16_t optlen)
+{
+#endif /* IP_OPTIONS_SEND */
+  struct ip_hdr *iphdr;
+  ip4_addr_t dest_addr;
+#if CHECKSUM_GEN_IP_INLINE
+  u32_t chk_sum = 0;
+#endif /* CHECKSUM_GEN_IP_INLINE */
+
+  LWIP_ASSERT_CORE_LOCKED();
+  LWIP_IP_CHECK_PBUF_REF_COUNT_FOR_TX(p);
+
+  MIB2_STATS_INC(mib2.ipoutrequests);
+
+  /* Should the IP header be generated or is it already included in p? */
+  if (dest != LWIP_IP_HDRINCL) {
+    u16_t ip_hlen = IP_HLEN;
+#if IP_OPTIONS_SEND
+    u16_t optlen_aligned = 0;
+    if (optlen != 0) {
+#if CHECKSUM_GEN_IP_INLINE
+      int i;
+#endif /* CHECKSUM_GEN_IP_INLINE */
+      if (optlen > (IP_HLEN_MAX - IP_HLEN)) {
+        /* optlen too long */
+        LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip4_output_if_opt: optlen too long\n"));
+        IP_STATS_INC(ip.err);
+        MIB2_STATS_INC(mib2.ipoutdiscards);
+        return ERR_VAL;
+      }
+      /* round up to a multiple of 4 */
+      optlen_aligned = (u16_t)((optlen + 3) & ~3);
+      ip_hlen = (u16_t)(ip_hlen + optlen_aligned);
+      /* First write in the IP options */
+      if (pbuf_add_header(p, optlen_aligned)) {
+        LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip4_output_if_opt: not enough room for IP options in pbuf\n"));
+        IP_STATS_INC(ip.err);
+        MIB2_STATS_INC(mib2.ipoutdiscards);
+        return ERR_BUF;
+      }
+      MEMCPY(p->payload, ip_options, optlen);
+      if (optlen < optlen_aligned) {
+        /* zero the remaining bytes */
+        memset(((char *)p->payload) + optlen, 0, (size_t)(optlen_aligned - optlen));
+      }
+#if CHECKSUM_GEN_IP_INLINE
+      for (i = 0; i < optlen_aligned / 2; i++) {
+        chk_sum += ((u16_t *)p->payload)[i];
+      }
+#endif /* CHECKSUM_GEN_IP_INLINE */
+    }
+#endif /* IP_OPTIONS_SEND */
+    /* generate IP header */
+    if (pbuf_add_header(p, IP_HLEN)) {
+      LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip4_output: not enough room for IP header in pbuf\n"));
+
+      IP_STATS_INC(ip.err);
+      MIB2_STATS_INC(mib2.ipoutdiscards);
+      return ERR_BUF;
+    }
+
+    iphdr = (struct ip_hdr *)p->payload;
+    LWIP_ASSERT("check that first pbuf can hold struct ip_hdr",
+                (p->len >= sizeof(struct ip_hdr)));
+
+    IPH_TTL_SET(iphdr, ttl);
+    IPH_PROTO_SET(iphdr, proto);
+#if CHECKSUM_GEN_IP_INLINE
+    chk_sum += PP_NTOHS(proto | (ttl << 8));
+#endif /* CHECKSUM_GEN_IP_INLINE */
+
+    /* dest cannot be NULL here */
+    ip4_addr_copy(iphdr->dest, *dest);
+#if CHECKSUM_GEN_IP_INLINE
+    chk_sum += ip4_addr_get_u32(&iphdr->dest) & 0xFFFF;
+    chk_sum += ip4_addr_get_u32(&iphdr->dest) >> 16;
+#endif /* CHECKSUM_GEN_IP_INLINE */
+
+    IPH_VHL_SET(iphdr, 4, ip_hlen / 4);
+    IPH_TOS_SET(iphdr, tos);
+#if CHECKSUM_GEN_IP_INLINE
+    chk_sum += PP_NTOHS(tos | (iphdr->_v_hl << 8));
+#endif /* CHECKSUM_GEN_IP_INLINE */
+    IPH_LEN_SET(iphdr, lwip_htons(p->tot_len));
+#if CHECKSUM_GEN_IP_INLINE
+    chk_sum += iphdr->_len;
+#endif /* CHECKSUM_GEN_IP_INLINE */
+    IPH_OFFSET_SET(iphdr, 0);
+    IPH_ID_SET(iphdr, lwip_htons(ip_id));
+#if CHECKSUM_GEN_IP_INLINE
+    chk_sum += iphdr->_id;
+#endif /* CHECKSUM_GEN_IP_INLINE */
+    ++ip_id;
+
+    if (src == NULL) {
+      ip4_addr_copy(iphdr->src, *IP4_ADDR_ANY4);
+    } else {
+      /* src cannot be NULL here */
+      ip4_addr_copy(iphdr->src, *src);
+    }
+
+#if CHECKSUM_GEN_IP_INLINE
+    chk_sum += ip4_addr_get_u32(&iphdr->src) & 0xFFFF;
+    chk_sum += ip4_addr_get_u32(&iphdr->src) >> 16;
+    chk_sum = (chk_sum >> 16) + (chk_sum & 0xFFFF);
+    chk_sum = (chk_sum >> 16) + chk_sum;
+    chk_sum = ~chk_sum;
+    IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_IP) {
+      iphdr->_chksum = (u16_t)chk_sum; /* network order */
+    }
+#if LWIP_CHECKSUM_CTRL_PER_NETIF
+    else {
+      IPH_CHKSUM_SET(iphdr, 0);
+    }
+#endif /* LWIP_CHECKSUM_CTRL_PER_NETIF*/
+#else /* CHECKSUM_GEN_IP_INLINE */
+    IPH_CHKSUM_SET(iphdr, 0);
+#if CHECKSUM_GEN_IP
+    IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_IP) {
+      IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, ip_hlen));
+    }
+#endif /* CHECKSUM_GEN_IP */
+#endif /* CHECKSUM_GEN_IP_INLINE */
+  } else {
+    /* IP header already included in p */
+    if (p->len < IP_HLEN) {
+      LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip4_output: LWIP_IP_HDRINCL but pbuf is too short\n"));
+      IP_STATS_INC(ip.err);
+      MIB2_STATS_INC(mib2.ipoutdiscards);
+      return ERR_BUF;
+    }
+    iphdr = (struct ip_hdr *)p->payload;
+    ip4_addr_copy(dest_addr, iphdr->dest);
+    dest = &dest_addr;
+  }
+
+  IP_STATS_INC(ip.xmit);
+
+  LWIP_DEBUGF(IP_DEBUG, ("ip4_output_if: %c%c%"U16_F"\n", netif->name[0], netif->name[1], (u16_t)netif->num));
+  ip4_debug_print(p);
+
+#if ENABLE_LOOPBACK
+  if (ip4_addr_eq(dest, netif_ip4_addr(netif))
+#if !LWIP_HAVE_LOOPIF
+      || ip4_addr_isloopback(dest)
+#endif /* !LWIP_HAVE_LOOPIF */
+     ) {
+    /* Packet to self, enqueue it for loopback */
+    LWIP_DEBUGF(IP_DEBUG, ("netif_loop_output()\n"));
+    return netif_loop_output(netif, p);
+  }
+#if LWIP_MULTICAST_TX_OPTIONS
+  if ((p->flags & PBUF_FLAG_MCASTLOOP) != 0) {
+    netif_loop_output(netif, p);
+  }
+#endif /* LWIP_MULTICAST_TX_OPTIONS */
+#endif /* ENABLE_LOOPBACK */
+#if IP_FRAG
+  /* don't fragment if interface has mtu set to 0 [loopif] */
+  if (netif->mtu && (p->tot_len > netif->mtu)) {
+    return ip4_frag(p, netif, dest);
+  }
+#endif /* IP_FRAG */
+
+  LWIP_DEBUGF(IP_DEBUG, ("ip4_output_if: call netif->output()\n"));
+  return netif->output(netif, p, dest);
+}
+
+/**
+ * Simple interface to ip_output_if. It finds the outgoing network
+ * interface and calls upon ip_output_if to do the actual work.
+ *
+ * @param p the packet to send (p->payload points to the data, e.g. next
+            protocol header; if dest == LWIP_IP_HDRINCL, p already includes an
+            IP header and p->payload points to that IP header)
+ * @param src the source IP address to send from (if src == IP4_ADDR_ANY, the
+ *         IP  address of the netif used to send is used as source address)
+ * @param dest the destination IP address to send the packet to
+ * @param ttl the TTL value to be set in the IP header
+ * @param tos the TOS value to be set in the IP header
+ * @param proto the PROTOCOL to be set in the IP header
+ *
+ * @return ERR_RTE if no route is found
+ *         see ip_output_if() for more return values
+ */
+err_t
+ip4_output(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest,
+           u8_t ttl, u8_t tos, u8_t proto)
+{
+  struct netif *netif;
+
+  LWIP_IP_CHECK_PBUF_REF_COUNT_FOR_TX(p);
+
+  if ((netif = ip4_route_src(src, dest)) == NULL) {
+    LWIP_DEBUGF(IP_DEBUG, ("ip4_output: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+                           ip4_addr1_16(dest), ip4_addr2_16(dest), ip4_addr3_16(dest), ip4_addr4_16(dest)));
+    IP_STATS_INC(ip.rterr);
+    return ERR_RTE;
+  }
+
+  return ip4_output_if(p, src, dest, ttl, tos, proto, netif);
+}
+
+#if LWIP_NETIF_USE_HINTS
+/** Like ip_output, but takes and addr_hint pointer that is passed on to netif->addr_hint
+ *  before calling ip_output_if.
+ *
+ * @param p the packet to send (p->payload points to the data, e.g. next
+            protocol header; if dest == LWIP_IP_HDRINCL, p already includes an
+            IP header and p->payload points to that IP header)
+ * @param src the source IP address to send from (if src == IP4_ADDR_ANY, the
+ *         IP  address of the netif used to send is used as source address)
+ * @param dest the destination IP address to send the packet to
+ * @param ttl the TTL value to be set in the IP header
+ * @param tos the TOS value to be set in the IP header
+ * @param proto the PROTOCOL to be set in the IP header
+ * @param netif_hint netif output hint pointer set to netif->hint before
+ *        calling ip_output_if()
+ *
+ * @return ERR_RTE if no route is found
+ *         see ip_output_if() for more return values
+ */
+err_t
+ip4_output_hinted(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest,
+                  u8_t ttl, u8_t tos, u8_t proto, struct netif_hint *netif_hint)
+{
+  struct netif *netif;
+  err_t err;
+
+  LWIP_IP_CHECK_PBUF_REF_COUNT_FOR_TX(p);
+
+  if ((netif = ip4_route_src(src, dest)) == NULL) {
+    LWIP_DEBUGF(IP_DEBUG, ("ip4_output: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+                           ip4_addr1_16(dest), ip4_addr2_16(dest), ip4_addr3_16(dest), ip4_addr4_16(dest)));
+    IP_STATS_INC(ip.rterr);
+    return ERR_RTE;
+  }
+
+  NETIF_SET_HINTS(netif, netif_hint);
+  err = ip4_output_if(p, src, dest, ttl, tos, proto, netif);
+  NETIF_RESET_HINTS(netif);
+
+  return err;
+}
+#endif /* LWIP_NETIF_USE_HINTS*/
+
+#if IP_DEBUG
+/* Print an IP header by using LWIP_DEBUGF
+ * @param p an IP packet, p->payload pointing to the IP header
+ */
+void
+ip4_debug_print(struct pbuf *p)
+{
+  struct ip_hdr *iphdr = (struct ip_hdr *)p->payload;
+
+  LWIP_DEBUGF(IP_DEBUG, ("IP header:\n"));
+  LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
+  LWIP_DEBUGF(IP_DEBUG, ("|%2"S16_F" |%2"S16_F" |  0x%02"X16_F" |     %5"U16_F"     | (v, hl, tos, len)\n",
+                         (u16_t)IPH_V(iphdr),
+                         (u16_t)IPH_HL(iphdr),
+                         (u16_t)IPH_TOS(iphdr),
+                         lwip_ntohs(IPH_LEN(iphdr))));
+  LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
+  LWIP_DEBUGF(IP_DEBUG, ("|    %5"U16_F"      |%"U16_F"%"U16_F"%"U16_F"|    %4"U16_F"   | (id, flags, offset)\n",
+                         lwip_ntohs(IPH_ID(iphdr)),
+                         (u16_t)(lwip_ntohs(IPH_OFFSET(iphdr)) >> 15 & 1),
+                         (u16_t)(lwip_ntohs(IPH_OFFSET(iphdr)) >> 14 & 1),
+                         (u16_t)(lwip_ntohs(IPH_OFFSET(iphdr)) >> 13 & 1),
+                         (u16_t)(lwip_ntohs(IPH_OFFSET(iphdr)) & IP_OFFMASK)));
+  LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
+  LWIP_DEBUGF(IP_DEBUG, ("|  %3"U16_F"  |  %3"U16_F"  |    0x%04"X16_F"     | (ttl, proto, chksum)\n",
+                         (u16_t)IPH_TTL(iphdr),
+                         (u16_t)IPH_PROTO(iphdr),
+                         lwip_ntohs(IPH_CHKSUM(iphdr))));
+  LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
+  LWIP_DEBUGF(IP_DEBUG, ("|  %3"U16_F"  |  %3"U16_F"  |  %3"U16_F"  |  %3"U16_F"  | (src)\n",
+                         ip4_addr1_16_val(iphdr->src),
+                         ip4_addr2_16_val(iphdr->src),
+                         ip4_addr3_16_val(iphdr->src),
+                         ip4_addr4_16_val(iphdr->src)));
+  LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
+  LWIP_DEBUGF(IP_DEBUG, ("|  %3"U16_F"  |  %3"U16_F"  |  %3"U16_F"  |  %3"U16_F"  | (dest)\n",
+                         ip4_addr1_16_val(iphdr->dest),
+                         ip4_addr2_16_val(iphdr->dest),
+                         ip4_addr3_16_val(iphdr->dest),
+                         ip4_addr4_16_val(iphdr->dest)));
+  LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
+}
+#endif /* IP_DEBUG */
+
+#endif /* LWIP_IPV4 */
diff --git a/lib/lwip/core/ipv4/ip4_addr.c b/lib/lwip/core/ipv4/ip4_addr.c
new file mode 100644
index 0000000000..79127ac953
--- /dev/null
+++ b/lib/lwip/core/ipv4/ip4_addr.c
@@ -0,0 +1,323 @@
+/**
+ * @file
+ * This is the IPv4 address tools implementation.
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV4
+
+#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+
+/* used by IP4_ADDR_ANY and IP_ADDR_BROADCAST in ip_addr.h */
+const ip_addr_t ip_addr_any = IPADDR4_INIT(IPADDR_ANY);
+const ip_addr_t ip_addr_broadcast = IPADDR4_INIT(IPADDR_BROADCAST);
+
+/**
+ * Determine if an address is a broadcast address on a network interface
+ *
+ * @param addr address to be checked
+ * @param netif the network interface against which the address is checked
+ * @return returns non-zero if the address is a broadcast address
+ */
+u8_t
+ip4_addr_isbroadcast_u32(u32_t addr, const struct netif *netif)
+{
+  ip4_addr_t ipaddr;
+  ip4_addr_set_u32(&ipaddr, addr);
+
+  /* all ones (broadcast) or all zeroes (old skool broadcast) */
+  if ((~addr == IPADDR_ANY) ||
+      (addr == IPADDR_ANY)) {
+    return 1;
+    /* no broadcast support on this network interface? */
+  } else if ((netif->flags & NETIF_FLAG_BROADCAST) == 0) {
+    /* the given address cannot be a broadcast address
+     * nor can we check against any broadcast addresses */
+    return 0;
+    /* address matches network interface address exactly? => no broadcast */
+  } else if (addr == ip4_addr_get_u32(netif_ip4_addr(netif))) {
+    return 0;
+    /*  on the same (sub) network... */
+  } else if (ip4_addr_net_eq(&ipaddr, netif_ip4_addr(netif), netif_ip4_netmask(netif))
+             /* ...and host identifier bits are all ones? =>... */
+             && ((addr & ~ip4_addr_get_u32(netif_ip4_netmask(netif))) ==
+                 (IPADDR_BROADCAST & ~ip4_addr_get_u32(netif_ip4_netmask(netif))))) {
+    /* => network broadcast address */
+    return 1;
+  } else {
+    return 0;
+  }
+}
+
+/** Checks if a netmask is valid (starting with ones, then only zeros)
+ *
+ * @param netmask the IPv4 netmask to check (in network byte order!)
+ * @return 1 if the netmask is valid, 0 if it is not
+ */
+u8_t
+ip4_addr_netmask_valid(u32_t netmask)
+{
+  u32_t mask;
+  u32_t nm_hostorder = lwip_htonl(netmask);
+
+  /* first, check for the first zero */
+  for (mask = 1UL << 31 ; mask != 0; mask >>= 1) {
+    if ((nm_hostorder & mask) == 0) {
+      break;
+    }
+  }
+  /* then check that there is no one */
+  for (; mask != 0; mask >>= 1) {
+    if ((nm_hostorder & mask) != 0) {
+      /* there is a one after the first zero -> invalid */
+      return 0;
+    }
+  }
+  /* no one after the first zero -> valid */
+  return 1;
+}
+
+/**
+ * Ascii internet address interpretation routine.
+ * The value returned is in network order.
+ *
+ * @param cp IP address in ascii representation (e.g. "127.0.0.1")
+ * @return ip address in network order
+ */
+u32_t
+ipaddr_addr(const char *cp)
+{
+  ip4_addr_t val;
+
+  if (ip4addr_aton(cp, &val)) {
+    return ip4_addr_get_u32(&val);
+  }
+  return (IPADDR_NONE);
+}
+
+/**
+ * Check whether "cp" is a valid ascii representation
+ * of an Internet address and convert to a binary address.
+ * Returns 1 if the address is valid, 0 if not.
+ * This replaces inet_addr, the return value from which
+ * cannot distinguish between failure and a local broadcast address.
+ *
+ * @param cp IP address in ascii representation (e.g. "127.0.0.1")
+ * @param addr pointer to which to save the ip address in network order
+ * @return 1 if cp could be converted to addr, 0 on failure
+ */
+int
+ip4addr_aton(const char *cp, ip4_addr_t *addr)
+{
+  u32_t val;
+  u8_t base;
+  char c;
+  u32_t parts[4];
+  u32_t *pp = parts;
+
+  c = *cp;
+  for (;;) {
+    /*
+     * Collect number up to ``.''.
+     * Values are specified as for C:
+     * 0x=hex, 0=octal, 1-9=decimal.
+     */
+    if (!lwip_isdigit(c)) {
+      return 0;
+    }
+    val = 0;
+    base = 10;
+    if (c == '0') {
+      c = *++cp;
+      if (c == 'x' || c == 'X') {
+        base = 16;
+        c = *++cp;
+      } else {
+        base = 8;
+      }
+    }
+    for (;;) {
+      if (lwip_isdigit(c)) {
+        if((base == 8) && ((u32_t)(c - '0') >= 8))
+          break;
+        val = (val * base) + (u32_t)(c - '0');
+        c = *++cp;
+      } else if (base == 16 && lwip_isxdigit(c)) {
+        val = (val << 4) | (u32_t)(c + 10 - (lwip_islower(c) ? 'a' : 'A'));
+        c = *++cp;
+      } else {
+        break;
+      }
+    }
+    if (c == '.') {
+      /*
+       * Internet format:
+       *  a.b.c.d
+       *  a.b.c   (with c treated as 16 bits)
+       *  a.b (with b treated as 24 bits)
+       */
+      if (pp >= parts + 3) {
+        return 0;
+      }
+      *pp++ = val;
+      c = *++cp;
+    } else {
+      break;
+    }
+  }
+  /*
+   * Check for trailing characters.
+   */
+  if (c != '\0' && !lwip_isspace(c)) {
+    return 0;
+  }
+  /*
+   * Concoct the address according to
+   * the number of parts specified.
+   */
+  switch (pp - parts + 1) {
+
+    case 0:
+      return 0;       /* initial nondigit */
+
+    case 1:             /* a -- 32 bits */
+      break;
+
+    case 2:             /* a.b -- 8.24 bits */
+      if (val > 0xffffffUL) {
+        return 0;
+      }
+      if (parts[0] > 0xff) {
+        return 0;
+      }
+      val |= parts[0] << 24;
+      break;
+
+    case 3:             /* a.b.c -- 8.8.16 bits */
+      if (val > 0xffff) {
+        return 0;
+      }
+      if ((parts[0] > 0xff) || (parts[1] > 0xff)) {
+        return 0;
+      }
+      val |= (parts[0] << 24) | (parts[1] << 16);
+      break;
+
+    case 4:             /* a.b.c.d -- 8.8.8.8 bits */
+      if (val > 0xff) {
+        return 0;
+      }
+      if ((parts[0] > 0xff) || (parts[1] > 0xff) || (parts[2] > 0xff)) {
+        return 0;
+      }
+      val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8);
+      break;
+    default:
+      LWIP_ASSERT("unhandled", 0);
+      break;
+  }
+  if (addr) {
+    ip4_addr_set_u32(addr, lwip_htonl(val));
+  }
+  return 1;
+}
+
+/**
+ * Convert numeric IP address into decimal dotted ASCII representation.
+ * returns ptr to static buffer; not reentrant!
+ *
+ * @param addr ip address in network order to convert
+ * @return pointer to a global static (!) buffer that holds the ASCII
+ *         representation of addr
+ */
+char *
+ip4addr_ntoa(const ip4_addr_t *addr)
+{
+  static char str[IP4ADDR_STRLEN_MAX];
+  return ip4addr_ntoa_r(addr, str, IP4ADDR_STRLEN_MAX);
+}
+
+/**
+ * Same as ip4addr_ntoa, but reentrant since a user-supplied buffer is used.
+ *
+ * @param addr ip address in network order to convert
+ * @param buf target buffer where the string is stored
+ * @param buflen length of buf
+ * @return either pointer to buf which now holds the ASCII
+ *         representation of addr or NULL if buf was too small
+ */
+char *
+ip4addr_ntoa_r(const ip4_addr_t *addr, char *buf, int buflen)
+{
+  u32_t s_addr;
+  char inv[3];
+  char *rp;
+  u8_t *ap;
+  u8_t rem;
+  u8_t n;
+  u8_t i;
+  int len = 0;
+
+  s_addr = ip4_addr_get_u32(addr);
+
+  rp = buf;
+  ap = (u8_t *)&s_addr;
+  for (n = 0; n < 4; n++) {
+    i = 0;
+    do {
+      rem = *ap % (u8_t)10;
+      *ap /= (u8_t)10;
+      inv[i++] = (char)('0' + rem);
+    } while (*ap);
+    while (i--) {
+      if (len++ >= buflen) {
+        return NULL;
+      }
+      *rp++ = inv[i];
+    }
+    if (len++ >= buflen) {
+      return NULL;
+    }
+    *rp++ = '.';
+    ap++;
+  }
+  *--rp = 0;
+  return buf;
+}
+
+#endif /* LWIP_IPV4 */
diff --git a/lib/lwip/core/ipv4/ip4_frag.c b/lib/lwip/core/ipv4/ip4_frag.c
new file mode 100644
index 0000000000..530314471f
--- /dev/null
+++ b/lib/lwip/core/ipv4/ip4_frag.c
@@ -0,0 +1,894 @@
+/**
+ * @file
+ * This is the IPv4 packet segmentation and reassembly implementation.
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Jani Monoses <jani@iv.ro>
+ *         Simon Goldschmidt
+ * original reassembly code by Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV4
+
+#include "lwip/ip4_frag.h"
+#include "lwip/def.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/netif.h"
+#include "lwip/stats.h"
+#include "lwip/icmp.h"
+
+#include <string.h>
+
+#if IP_REASSEMBLY
+/**
+ * The IP reassembly code currently has the following limitations:
+ * - IP header options are not supported
+ * - fragments must not overlap (e.g. due to different routes),
+ *   currently, overlapping or duplicate fragments are thrown away
+ *   if IP_REASS_CHECK_OVERLAP=1 (the default)!
+ *
+ * @todo: work with IP header options
+ */
+
+/** Setting this to 0, you can turn off checking the fragments for overlapping
+ * regions. The code gets a little smaller. Only use this if you know that
+ * overlapping won't occur on your network! */
+#ifndef IP_REASS_CHECK_OVERLAP
+#define IP_REASS_CHECK_OVERLAP 1
+#endif /* IP_REASS_CHECK_OVERLAP */
+
+/** Set to 0 to prevent freeing the oldest datagram when the reassembly buffer is
+ * full (IP_REASS_MAX_PBUFS pbufs are enqueued). The code gets a little smaller.
+ * Datagrams will be freed by timeout only. Especially useful when MEMP_NUM_REASSDATA
+ * is set to 1, so one datagram can be reassembled at a time, only. */
+#ifndef IP_REASS_FREE_OLDEST
+#define IP_REASS_FREE_OLDEST 1
+#endif /* IP_REASS_FREE_OLDEST */
+
+#define IP_REASS_FLAG_LASTFRAG 0x01
+
+#define IP_REASS_VALIDATE_TELEGRAM_FINISHED  1
+#define IP_REASS_VALIDATE_PBUF_QUEUED        0
+#define IP_REASS_VALIDATE_PBUF_DROPPED       -1
+
+/** This is a helper struct which holds the starting
+ * offset and the ending offset of this fragment to
+ * easily chain the fragments.
+ * It has the same packing requirements as the IP header, since it replaces
+ * the IP header in memory in incoming fragments (after copying it) to keep
+ * track of the various fragments. (-> If the IP header doesn't need packing,
+ * this struct doesn't need packing, too.)
+ */
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct ip_reass_helper {
+  PACK_STRUCT_FIELD(struct pbuf *next_pbuf);
+  PACK_STRUCT_FIELD(u16_t start);
+  PACK_STRUCT_FIELD(u16_t end);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
+
+#define IP_ADDRESSES_AND_ID_MATCH(iphdrA, iphdrB)  \
+  (ip4_addr_eq(&(iphdrA)->src, &(iphdrB)->src) && \
+   ip4_addr_eq(&(iphdrA)->dest, &(iphdrB)->dest) && \
+   IPH_ID(iphdrA) == IPH_ID(iphdrB)) ? 1 : 0
+
+/* global variables */
+static struct ip_reassdata *reassdatagrams;
+static u16_t ip_reass_pbufcount;
+
+/* function prototypes */
+static void ip_reass_dequeue_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev);
+static int ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev);
+
+/**
+ * Reassembly timer base function
+ * for both NO_SYS == 0 and 1 (!).
+ *
+ * Should be called every 1000 msec (defined by IP_TMR_INTERVAL).
+ */
+void
+ip_reass_tmr(void)
+{
+  struct ip_reassdata *r, *prev = NULL;
+
+  r = reassdatagrams;
+  while (r != NULL) {
+    /* Decrement the timer. Once it reaches 0,
+     * clean up the incomplete fragment assembly */
+    if (r->timer > 0) {
+      r->timer--;
+      LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass_tmr: timer dec %"U16_F"\n", (u16_t)r->timer));
+      prev = r;
+      r = r->next;
+    } else {
+      /* reassembly timed out */
+      struct ip_reassdata *tmp;
+      LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass_tmr: timer timed out\n"));
+      tmp = r;
+      /* get the next pointer before freeing */
+      r = r->next;
+      /* free the helper struct and all enqueued pbufs */
+      ip_reass_free_complete_datagram(tmp, prev);
+    }
+  }
+}
+
+/**
+ * Free a datagram (struct ip_reassdata) and all its pbufs.
+ * Updates the total count of enqueued pbufs (ip_reass_pbufcount),
+ * SNMP counters and sends an ICMP time exceeded packet.
+ *
+ * @param ipr datagram to free
+ * @param prev the previous datagram in the linked list
+ * @return the number of pbufs freed
+ */
+static int
+ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev)
+{
+  u16_t pbufs_freed = 0;
+  u16_t clen;
+  struct pbuf *p;
+  struct ip_reass_helper *iprh;
+
+  LWIP_ASSERT("prev != ipr", prev != ipr);
+  if (prev != NULL) {
+    LWIP_ASSERT("prev->next == ipr", prev->next == ipr);
+  }
+
+  MIB2_STATS_INC(mib2.ipreasmfails);
+#if LWIP_ICMP
+  iprh = (struct ip_reass_helper *)ipr->p->payload;
+  if (iprh->start == 0) {
+    /* The first fragment was received, send ICMP time exceeded. */
+    /* First, de-queue the first pbuf from r->p. */
+    p = ipr->p;
+    ipr->p = iprh->next_pbuf;
+    /* Then, copy the original header into it. */
+    SMEMCPY(p->payload, &ipr->iphdr, IP_HLEN);
+    icmp_time_exceeded(p, ICMP_TE_FRAG);
+    clen = pbuf_clen(p);
+    LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff);
+    pbufs_freed = (u16_t)(pbufs_freed + clen);
+    pbuf_free(p);
+  }
+#endif /* LWIP_ICMP */
+
+  /* First, free all received pbufs.  The individual pbufs need to be released
+     separately as they have not yet been chained */
+  p = ipr->p;
+  while (p != NULL) {
+    struct pbuf *pcur;
+    iprh = (struct ip_reass_helper *)p->payload;
+    pcur = p;
+    /* get the next pointer before freeing */
+    p = iprh->next_pbuf;
+    clen = pbuf_clen(pcur);
+    LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff);
+    pbufs_freed = (u16_t)(pbufs_freed + clen);
+    pbuf_free(pcur);
+  }
+  /* Then, unchain the struct ip_reassdata from the list and free it. */
+  ip_reass_dequeue_datagram(ipr, prev);
+  LWIP_ASSERT("ip_reass_pbufcount >= pbufs_freed", ip_reass_pbufcount >= pbufs_freed);
+  ip_reass_pbufcount = (u16_t)(ip_reass_pbufcount - pbufs_freed);
+
+  return pbufs_freed;
+}
+
+#if IP_REASS_FREE_OLDEST
+/**
+ * Free the oldest datagram to make room for enqueueing new fragments.
+ * The datagram 'fraghdr' belongs to is not freed!
+ *
+ * @param fraghdr IP header of the current fragment
+ * @param pbufs_needed number of pbufs needed to enqueue
+ *        (used for freeing other datagrams if not enough space)
+ * @return the number of pbufs freed
+ */
+static int
+ip_reass_remove_oldest_datagram(struct ip_hdr *fraghdr, int pbufs_needed)
+{
+  /* @todo Can't we simply remove the last datagram in the
+   *       linked list behind reassdatagrams?
+   */
+  struct ip_reassdata *r, *oldest, *prev, *oldest_prev;
+  int pbufs_freed = 0, pbufs_freed_current;
+  int other_datagrams;
+
+  /* Free datagrams until being allowed to enqueue 'pbufs_needed' pbufs,
+   * but don't free the datagram that 'fraghdr' belongs to! */
+  do {
+    oldest = NULL;
+    prev = NULL;
+    oldest_prev = NULL;
+    other_datagrams = 0;
+    r = reassdatagrams;
+    while (r != NULL) {
+      if (!IP_ADDRESSES_AND_ID_MATCH(&r->iphdr, fraghdr)) {
+        /* Not the same datagram as fraghdr */
+        other_datagrams++;
+        if (oldest == NULL) {
+          oldest = r;
+          oldest_prev = prev;
+        } else if (r->timer <= oldest->timer) {
+          /* older than the previous oldest */
+          oldest = r;
+          oldest_prev = prev;
+        }
+      }
+      if (r->next != NULL) {
+        prev = r;
+      }
+      r = r->next;
+    }
+    if (oldest != NULL) {
+      pbufs_freed_current = ip_reass_free_complete_datagram(oldest, oldest_prev);
+      pbufs_freed += pbufs_freed_current;
+    }
+  } while ((pbufs_freed < pbufs_needed) && (other_datagrams > 1));
+  return pbufs_freed;
+}
+#endif /* IP_REASS_FREE_OLDEST */
+
+/**
+ * Enqueues a new fragment into the fragment queue
+ * @param fraghdr points to the new fragments IP hdr
+ * @param clen number of pbufs needed to enqueue (used for freeing other datagrams if not enough space)
+ * @return A pointer to the queue location into which the fragment was enqueued
+ */
+static struct ip_reassdata *
+ip_reass_enqueue_new_datagram(struct ip_hdr *fraghdr, int clen)
+{
+  struct ip_reassdata *ipr;
+#if ! IP_REASS_FREE_OLDEST
+  LWIP_UNUSED_ARG(clen);
+#endif
+
+  /* No matching previous fragment found, allocate a new reassdata struct */
+  ipr = (struct ip_reassdata *)memp_malloc(MEMP_REASSDATA);
+  if (ipr == NULL) {
+#if IP_REASS_FREE_OLDEST
+    if (ip_reass_remove_oldest_datagram(fraghdr, clen) >= clen) {
+      ipr = (struct ip_reassdata *)memp_malloc(MEMP_REASSDATA);
+    }
+    if (ipr == NULL)
+#endif /* IP_REASS_FREE_OLDEST */
+    {
+      IPFRAG_STATS_INC(ip_frag.memerr);
+      LWIP_DEBUGF(IP_REASS_DEBUG, ("Failed to alloc reassdata struct\n"));
+      return NULL;
+    }
+  }
+  memset(ipr, 0, sizeof(struct ip_reassdata));
+  ipr->timer = IP_REASS_MAXAGE;
+
+  /* enqueue the new structure to the front of the list */
+  ipr->next = reassdatagrams;
+  reassdatagrams = ipr;
+  /* copy the ip header for later tests and input */
+  /* @todo: no ip options supported? */
+  SMEMCPY(&(ipr->iphdr), fraghdr, IP_HLEN);
+  return ipr;
+}
+
+/**
+ * Dequeues a datagram from the datagram queue. Doesn't deallocate the pbufs.
+ * @param ipr points to the queue entry to dequeue
+ */
+static void
+ip_reass_dequeue_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev)
+{
+  /* dequeue the reass struct  */
+  if (reassdatagrams == ipr) {
+    /* it was the first in the list */
+    reassdatagrams = ipr->next;
+  } else {
+    /* it wasn't the first, so it must have a valid 'prev' */
+    LWIP_ASSERT("sanity check linked list", prev != NULL);
+    prev->next = ipr->next;
+  }
+
+  /* now we can free the ip_reassdata struct */
+  memp_free(MEMP_REASSDATA, ipr);
+}
+
+/**
+ * Chain a new pbuf into the pbuf list that composes the datagram.  The pbuf list
+ * will grow over time as  new pbufs are rx.
+ * Also checks that the datagram passes basic continuity checks (if the last
+ * fragment was received at least once).
+ * @param ipr points to the reassembly state
+ * @param new_p points to the pbuf for the current fragment
+ * @param is_last is 1 if this pbuf has MF==0 (ipr->flags not updated yet)
+ * @return see IP_REASS_VALIDATE_* defines
+ */
+static int
+ip_reass_chain_frag_into_datagram_and_validate(struct ip_reassdata *ipr, struct pbuf *new_p, int is_last)
+{
+  struct ip_reass_helper *iprh, *iprh_tmp, *iprh_prev = NULL;
+  struct pbuf *q;
+  u16_t offset, len;
+  u8_t hlen;
+  struct ip_hdr *fraghdr;
+  int valid = 1;
+
+  /* Extract length and fragment offset from current fragment */
+  fraghdr = (struct ip_hdr *)new_p->payload;
+  len = lwip_ntohs(IPH_LEN(fraghdr));
+  hlen = IPH_HL_BYTES(fraghdr);
+  if (hlen > len) {
+    /* invalid datagram */
+    return IP_REASS_VALIDATE_PBUF_DROPPED;
+  }
+  len = (u16_t)(len - hlen);
+  offset = IPH_OFFSET_BYTES(fraghdr);
+
+  /* overwrite the fragment's ip header from the pbuf with our helper struct,
+   * and setup the embedded helper structure. */
+  /* make sure the struct ip_reass_helper fits into the IP header */
+  LWIP_ASSERT("sizeof(struct ip_reass_helper) <= IP_HLEN",
+              sizeof(struct ip_reass_helper) <= IP_HLEN);
+  iprh = (struct ip_reass_helper *)new_p->payload;
+  iprh->next_pbuf = NULL;
+  iprh->start = offset;
+  iprh->end = (u16_t)(offset + len);
+  if (iprh->end < offset) {
+    /* u16_t overflow, cannot handle this */
+    return IP_REASS_VALIDATE_PBUF_DROPPED;
+  }
+
+  /* Iterate through until we either get to the end of the list (append),
+   * or we find one with a larger offset (insert). */
+  for (q = ipr->p; q != NULL;) {
+    iprh_tmp = (struct ip_reass_helper *)q->payload;
+    if (iprh->start < iprh_tmp->start) {
+      /* the new pbuf should be inserted before this */
+      iprh->next_pbuf = q;
+      if (iprh_prev != NULL) {
+        /* not the fragment with the lowest offset */
+#if IP_REASS_CHECK_OVERLAP
+        if ((iprh->start < iprh_prev->end) || (iprh->end > iprh_tmp->start)) {
+          /* fragment overlaps with previous or following, throw away */
+          return IP_REASS_VALIDATE_PBUF_DROPPED;
+        }
+#endif /* IP_REASS_CHECK_OVERLAP */
+        iprh_prev->next_pbuf = new_p;
+        if (iprh_prev->end != iprh->start) {
+          /* There is a fragment missing between the current
+           * and the previous fragment */
+          valid = 0;
+        }
+      } else {
+#if IP_REASS_CHECK_OVERLAP
+        if (iprh->end > iprh_tmp->start) {
+          /* fragment overlaps with following, throw away */
+          return IP_REASS_VALIDATE_PBUF_DROPPED;
+        }
+#endif /* IP_REASS_CHECK_OVERLAP */
+        /* fragment with the lowest offset */
+        ipr->p = new_p;
+      }
+      break;
+    } else if (iprh->start == iprh_tmp->start) {
+      /* received the same datagram twice: no need to keep the datagram */
+      return IP_REASS_VALIDATE_PBUF_DROPPED;
+#if IP_REASS_CHECK_OVERLAP
+    } else if (iprh->start < iprh_tmp->end) {
+      /* overlap: no need to keep the new datagram */
+      return IP_REASS_VALIDATE_PBUF_DROPPED;
+#endif /* IP_REASS_CHECK_OVERLAP */
+    } else {
+      /* Check if the fragments received so far have no holes. */
+      if (iprh_prev != NULL) {
+        if (iprh_prev->end != iprh_tmp->start) {
+          /* There is a fragment missing between the current
+           * and the previous fragment */
+          valid = 0;
+        }
+      }
+    }
+    q = iprh_tmp->next_pbuf;
+    iprh_prev = iprh_tmp;
+  }
+
+  /* If q is NULL, then we made it to the end of the list. Determine what to do now */
+  if (q == NULL) {
+    if (iprh_prev != NULL) {
+      /* this is (for now), the fragment with the highest offset:
+       * chain it to the last fragment */
+#if IP_REASS_CHECK_OVERLAP
+      LWIP_ASSERT("check fragments don't overlap", iprh_prev->end <= iprh->start);
+#endif /* IP_REASS_CHECK_OVERLAP */
+      iprh_prev->next_pbuf = new_p;
+      if (iprh_prev->end != iprh->start) {
+        valid = 0;
+      }
+    } else {
+#if IP_REASS_CHECK_OVERLAP
+      LWIP_ASSERT("no previous fragment, this must be the first fragment!",
+                  ipr->p == NULL);
+#endif /* IP_REASS_CHECK_OVERLAP */
+      /* this is the first fragment we ever received for this ip datagram */
+      ipr->p = new_p;
+    }
+  }
+
+  /* At this point, the validation part begins: */
+  /* If we already received the last fragment */
+  if (is_last || ((ipr->flags & IP_REASS_FLAG_LASTFRAG) != 0)) {
+    /* and had no holes so far */
+    if (valid) {
+      /* then check if the rest of the fragments is here */
+      /* Check if the queue starts with the first datagram */
+      if ((ipr->p == NULL) || (((struct ip_reass_helper *)ipr->p->payload)->start != 0)) {
+        valid = 0;
+      } else {
+        /* and check that there are no holes after this datagram */
+        iprh_prev = iprh;
+        q = iprh->next_pbuf;
+        while (q != NULL) {
+          iprh = (struct ip_reass_helper *)q->payload;
+          if (iprh_prev->end != iprh->start) {
+            valid = 0;
+            break;
+          }
+          iprh_prev = iprh;
+          q = iprh->next_pbuf;
+        }
+        /* if still valid, all fragments are received
+         * (because to the MF==0 already arrived */
+        if (valid) {
+          LWIP_ASSERT("sanity check", ipr->p != NULL);
+          LWIP_ASSERT("sanity check",
+                      ((struct ip_reass_helper *)ipr->p->payload) != iprh);
+          LWIP_ASSERT("validate_datagram:next_pbuf!=NULL",
+                      iprh->next_pbuf == NULL);
+        }
+      }
+    }
+    /* If valid is 0 here, there are some fragments missing in the middle
+     * (since MF == 0 has already arrived). Such datagrams simply time out if
+     * no more fragments are received... */
+    return valid ? IP_REASS_VALIDATE_TELEGRAM_FINISHED : IP_REASS_VALIDATE_PBUF_QUEUED;
+  }
+  /* If we come here, not all fragments were received, yet! */
+  return IP_REASS_VALIDATE_PBUF_QUEUED; /* not yet valid! */
+}
+
+/**
+ * Reassembles incoming IP fragments into an IP datagram.
+ *
+ * @param p points to a pbuf chain of the fragment
+ * @return NULL if reassembly is incomplete, ? otherwise
+ */
+struct pbuf *
+ip4_reass(struct pbuf *p)
+{
+  struct pbuf *r;
+  struct ip_hdr *fraghdr;
+  struct ip_reassdata *ipr;
+  struct ip_reass_helper *iprh;
+  u16_t offset, len, clen;
+  u8_t hlen;
+  int valid;
+  int is_last;
+
+  IPFRAG_STATS_INC(ip_frag.recv);
+  MIB2_STATS_INC(mib2.ipreasmreqds);
+
+  fraghdr = (struct ip_hdr *)p->payload;
+
+  if (IPH_HL_BYTES(fraghdr) != IP_HLEN) {
+    LWIP_DEBUGF(IP_REASS_DEBUG, ("ip4_reass: IP options currently not supported!\n"));
+    IPFRAG_STATS_INC(ip_frag.err);
+    goto nullreturn;
+  }
+
+  offset = IPH_OFFSET_BYTES(fraghdr);
+  len = lwip_ntohs(IPH_LEN(fraghdr));
+  hlen = IPH_HL_BYTES(fraghdr);
+  if (hlen > len) {
+    /* invalid datagram */
+    goto nullreturn;
+  }
+  len = (u16_t)(len - hlen);
+
+  /* Check if we are allowed to enqueue more datagrams. */
+  clen = pbuf_clen(p);
+  if ((ip_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS) {
+#if IP_REASS_FREE_OLDEST
+    if (!ip_reass_remove_oldest_datagram(fraghdr, clen) ||
+        ((ip_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS))
+#endif /* IP_REASS_FREE_OLDEST */
+    {
+      /* No datagram could be freed and still too many pbufs enqueued */
+      LWIP_DEBUGF(IP_REASS_DEBUG, ("ip4_reass: Overflow condition: pbufct=%d, clen=%d, MAX=%d\n",
+                                   ip_reass_pbufcount, clen, IP_REASS_MAX_PBUFS));
+      IPFRAG_STATS_INC(ip_frag.memerr);
+      /* @todo: send ICMP time exceeded here? */
+      /* drop this pbuf */
+      goto nullreturn;
+    }
+  }
+
+  /* Look for the datagram the fragment belongs to in the current datagram queue,
+   * remembering the previous in the queue for later dequeueing. */
+  for (ipr = reassdatagrams; ipr != NULL; ipr = ipr->next) {
+    /* Check if the incoming fragment matches the one currently present
+       in the reassembly buffer. If so, we proceed with copying the
+       fragment into the buffer. */
+    if (IP_ADDRESSES_AND_ID_MATCH(&ipr->iphdr, fraghdr)) {
+      LWIP_DEBUGF(IP_REASS_DEBUG, ("ip4_reass: matching previous fragment ID=%"X16_F"\n",
+                                   lwip_ntohs(IPH_ID(fraghdr))));
+      IPFRAG_STATS_INC(ip_frag.cachehit);
+      break;
+    }
+  }
+
+  if (ipr == NULL) {
+    /* Enqueue a new datagram into the datagram queue */
+    ipr = ip_reass_enqueue_new_datagram(fraghdr, clen);
+    /* Bail if unable to enqueue */
+    if (ipr == NULL) {
+      goto nullreturn;
+    }
+  } else {
+    if (((lwip_ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) == 0) &&
+        ((lwip_ntohs(IPH_OFFSET(&ipr->iphdr)) & IP_OFFMASK) != 0)) {
+      /* ipr->iphdr is not the header from the first fragment, but fraghdr is
+       * -> copy fraghdr into ipr->iphdr since we want to have the header
+       * of the first fragment (for ICMP time exceeded and later, for copying
+       * all options, if supported)*/
+      SMEMCPY(&ipr->iphdr, fraghdr, IP_HLEN);
+    }
+  }
+
+  /* At this point, we have either created a new entry or pointing
+   * to an existing one */
+
+  /* check for 'no more fragments', and update queue entry*/
+  is_last = (IPH_OFFSET(fraghdr) & PP_NTOHS(IP_MF)) == 0;
+  if (is_last) {
+    u16_t datagram_len = (u16_t)(offset + len);
+    if ((datagram_len < offset) || (datagram_len > (0xFFFF - IP_HLEN))) {
+      /* u16_t overflow, cannot handle this */
+      goto nullreturn_ipr;
+    }
+  }
+  /* find the right place to insert this pbuf */
+  /* @todo: trim pbufs if fragments are overlapping */
+  valid = ip_reass_chain_frag_into_datagram_and_validate(ipr, p, is_last);
+  if (valid == IP_REASS_VALIDATE_PBUF_DROPPED) {
+    goto nullreturn_ipr;
+  }
+  /* if we come here, the pbuf has been enqueued */
+
+  /* Track the current number of pbufs current 'in-flight', in order to limit
+     the number of fragments that may be enqueued at any one time
+     (overflow checked by testing against IP_REASS_MAX_PBUFS) */
+  ip_reass_pbufcount = (u16_t)(ip_reass_pbufcount + clen);
+  if (is_last) {
+    u16_t datagram_len = (u16_t)(offset + len);
+    ipr->datagram_len = datagram_len;
+    ipr->flags |= IP_REASS_FLAG_LASTFRAG;
+    LWIP_DEBUGF(IP_REASS_DEBUG,
+                ("ip4_reass: last fragment seen, total len %"S16_F"\n",
+                 ipr->datagram_len));
+  }
+
+  if (valid == IP_REASS_VALIDATE_TELEGRAM_FINISHED) {
+    struct ip_reassdata *ipr_prev;
+    /* the totally last fragment (flag more fragments = 0) was received at least
+     * once AND all fragments are received */
+    u16_t datagram_len = (u16_t)(ipr->datagram_len + IP_HLEN);
+
+    /* save the second pbuf before copying the header over the pointer */
+    r = ((struct ip_reass_helper *)ipr->p->payload)->next_pbuf;
+
+    /* copy the original ip header back to the first pbuf */
+    fraghdr = (struct ip_hdr *)(ipr->p->payload);
+    SMEMCPY(fraghdr, &ipr->iphdr, IP_HLEN);
+    IPH_LEN_SET(fraghdr, lwip_htons(datagram_len));
+    IPH_OFFSET_SET(fraghdr, 0);
+    IPH_CHKSUM_SET(fraghdr, 0);
+    /* @todo: do we need to set/calculate the correct checksum? */
+#if CHECKSUM_GEN_IP
+    IF__NETIF_CHECKSUM_ENABLED(ip_current_input_netif(), NETIF_CHECKSUM_GEN_IP) {
+      IPH_CHKSUM_SET(fraghdr, inet_chksum(fraghdr, IP_HLEN));
+    }
+#endif /* CHECKSUM_GEN_IP */
+
+    p = ipr->p;
+
+    /* chain together the pbufs contained within the reass_data list. */
+    while (r != NULL) {
+      iprh = (struct ip_reass_helper *)r->payload;
+
+      /* hide the ip header for every succeeding fragment */
+      pbuf_remove_header(r, IP_HLEN);
+      pbuf_cat(p, r);
+      r = iprh->next_pbuf;
+    }
+
+    /* find the previous entry in the linked list */
+    if (ipr == reassdatagrams) {
+      ipr_prev = NULL;
+    } else {
+      for (ipr_prev = reassdatagrams; ipr_prev != NULL; ipr_prev = ipr_prev->next) {
+        if (ipr_prev->next == ipr) {
+          break;
+        }
+      }
+    }
+
+    /* release the sources allocate for the fragment queue entry */
+    ip_reass_dequeue_datagram(ipr, ipr_prev);
+
+    /* and adjust the number of pbufs currently queued for reassembly. */
+    clen = pbuf_clen(p);
+    LWIP_ASSERT("ip_reass_pbufcount >= clen", ip_reass_pbufcount >= clen);
+    ip_reass_pbufcount = (u16_t)(ip_reass_pbufcount - clen);
+
+    MIB2_STATS_INC(mib2.ipreasmoks);
+
+    /* Return the pbuf chain */
+    return p;
+  }
+  /* the datagram is not (yet?) reassembled completely */
+  LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass_pbufcount: %d out\n", ip_reass_pbufcount));
+  return NULL;
+
+nullreturn_ipr:
+  LWIP_ASSERT("ipr != NULL", ipr != NULL);
+  if (ipr->p == NULL) {
+    /* dropped pbuf after creating a new datagram entry: remove the entry, too */
+    LWIP_ASSERT("not firstalthough just enqueued", ipr == reassdatagrams);
+    ip_reass_dequeue_datagram(ipr, NULL);
+  }
+
+nullreturn:
+  LWIP_DEBUGF(IP_REASS_DEBUG, ("ip4_reass: nullreturn\n"));
+  IPFRAG_STATS_INC(ip_frag.drop);
+  pbuf_free(p);
+  return NULL;
+}
+#endif /* IP_REASSEMBLY */
+
+#if IP_FRAG
+#if !LWIP_NETIF_TX_SINGLE_PBUF
+/** Allocate a new struct pbuf_custom_ref */
+static struct pbuf_custom_ref *
+ip_frag_alloc_pbuf_custom_ref(void)
+{
+  return (struct pbuf_custom_ref *)memp_malloc(MEMP_FRAG_PBUF);
+}
+
+/** Free a struct pbuf_custom_ref */
+static void
+ip_frag_free_pbuf_custom_ref(struct pbuf_custom_ref *p)
+{
+  LWIP_ASSERT("p != NULL", p != NULL);
+  memp_free(MEMP_FRAG_PBUF, p);
+}
+
+/** Free-callback function to free a 'struct pbuf_custom_ref', called by
+ * pbuf_free. */
+static void
+ipfrag_free_pbuf_custom(struct pbuf *p)
+{
+  struct pbuf_custom_ref *pcr = (struct pbuf_custom_ref *)p;
+  LWIP_ASSERT("pcr != NULL", pcr != NULL);
+  LWIP_ASSERT("pcr == p", (void *)pcr == (void *)p);
+  if (pcr->original != NULL) {
+    pbuf_free(pcr->original);
+  }
+  ip_frag_free_pbuf_custom_ref(pcr);
+}
+#endif /* !LWIP_NETIF_TX_SINGLE_PBUF */
+
+/**
+ * Fragment an IP datagram if too large for the netif.
+ *
+ * Chop the datagram in MTU sized chunks and send them in order
+ * by pointing PBUF_REFs into p.
+ *
+ * @param p ip packet to send
+ * @param netif the netif on which to send
+ * @param dest destination ip address to which to send
+ *
+ * @return ERR_OK if sent successfully, err_t otherwise
+ */
+err_t
+ip4_frag(struct pbuf *p, struct netif *netif, const ip4_addr_t *dest)
+{
+  struct pbuf *rambuf;
+#if !LWIP_NETIF_TX_SINGLE_PBUF
+  struct pbuf *newpbuf;
+  u16_t newpbuflen = 0;
+  u16_t left_to_copy;
+#endif
+  struct ip_hdr *original_iphdr;
+  struct ip_hdr *iphdr;
+  const u16_t nfb = (u16_t)((netif->mtu - IP_HLEN) / 8);
+  u16_t left, fragsize;
+  u16_t ofo;
+  int last;
+  u16_t poff = IP_HLEN;
+  u16_t tmp;
+  int mf_set;
+
+  original_iphdr = (struct ip_hdr *)p->payload;
+  iphdr = original_iphdr;
+  if (IPH_HL_BYTES(iphdr) != IP_HLEN) {
+    /* ip4_frag() does not support IP options */
+    return ERR_VAL;
+  }
+  LWIP_ERROR("ip4_frag(): pbuf too short", p->len >= IP_HLEN, return ERR_VAL);
+
+  /* Save original offset */
+  tmp = lwip_ntohs(IPH_OFFSET(iphdr));
+  ofo = tmp & IP_OFFMASK;
+  /* already fragmented? if so, the last fragment we create must have MF, too */
+  mf_set = tmp & IP_MF;
+
+  left = (u16_t)(p->tot_len - IP_HLEN);
+
+  while (left) {
+    /* Fill this fragment */
+    fragsize = LWIP_MIN(left, (u16_t)(nfb * 8));
+
+#if LWIP_NETIF_TX_SINGLE_PBUF
+    rambuf = pbuf_alloc(PBUF_IP, fragsize, PBUF_RAM);
+    if (rambuf == NULL) {
+      goto memerr;
+    }
+    LWIP_ASSERT("this needs a pbuf in one piece!",
+                (rambuf->len == rambuf->tot_len) && (rambuf->next == NULL));
+    poff += pbuf_copy_partial(p, rambuf->payload, fragsize, poff);
+    /* make room for the IP header */
+    if (pbuf_add_header(rambuf, IP_HLEN)) {
+      pbuf_free(rambuf);
+      goto memerr;
+    }
+    /* fill in the IP header */
+    SMEMCPY(rambuf->payload, original_iphdr, IP_HLEN);
+    iphdr = (struct ip_hdr *)rambuf->payload;
+#else /* LWIP_NETIF_TX_SINGLE_PBUF */
+    /* When not using a static buffer, create a chain of pbufs.
+     * The first will be a PBUF_RAM holding the link and IP header.
+     * The rest will be PBUF_REFs mirroring the pbuf chain to be fragged,
+     * but limited to the size of an mtu.
+     */
+    rambuf = pbuf_alloc(PBUF_LINK, IP_HLEN, PBUF_RAM);
+    if (rambuf == NULL) {
+      goto memerr;
+    }
+    LWIP_ASSERT("this needs a pbuf in one piece!",
+                (rambuf->len >= (IP_HLEN)));
+    SMEMCPY(rambuf->payload, original_iphdr, IP_HLEN);
+    iphdr = (struct ip_hdr *)rambuf->payload;
+
+    left_to_copy = fragsize;
+    while (left_to_copy) {
+      struct pbuf_custom_ref *pcr;
+      u16_t plen = (u16_t)(p->len - poff);
+      LWIP_ASSERT("p->len >= poff", p->len >= poff);
+      newpbuflen = LWIP_MIN(left_to_copy, plen);
+      /* Is this pbuf already empty? */
+      if (!newpbuflen) {
+        poff = 0;
+        p = p->next;
+        continue;
+      }
+      pcr = ip_frag_alloc_pbuf_custom_ref();
+      if (pcr == NULL) {
+        pbuf_free(rambuf);
+        goto memerr;
+      }
+      /* Mirror this pbuf, although we might not need all of it. */
+      newpbuf = pbuf_alloced_custom(PBUF_RAW, newpbuflen, PBUF_REF, &pcr->pc,
+                                    (u8_t *)p->payload + poff, newpbuflen);
+      if (newpbuf == NULL) {
+        ip_frag_free_pbuf_custom_ref(pcr);
+        pbuf_free(rambuf);
+        goto memerr;
+      }
+      pbuf_ref(p);
+      pcr->original = p;
+      pcr->pc.custom_free_function = ipfrag_free_pbuf_custom;
+
+      /* Add it to end of rambuf's chain, but using pbuf_cat, not pbuf_chain
+       * so that it is removed when pbuf_dechain is later called on rambuf.
+       */
+      pbuf_cat(rambuf, newpbuf);
+      left_to_copy = (u16_t)(left_to_copy - newpbuflen);
+      if (left_to_copy) {
+        poff = 0;
+        p = p->next;
+      }
+    }
+    poff = (u16_t)(poff + newpbuflen);
+#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
+
+    /* Correct header */
+    last = (left <= netif->mtu - IP_HLEN);
+
+    /* Set new offset and MF flag */
+    tmp = (IP_OFFMASK & (ofo));
+    if (!last || mf_set) {
+      /* the last fragment has MF set if the input frame had it */
+      tmp = tmp | IP_MF;
+    }
+    IPH_OFFSET_SET(iphdr, lwip_htons(tmp));
+    IPH_LEN_SET(iphdr, lwip_htons((u16_t)(fragsize + IP_HLEN)));
+    IPH_CHKSUM_SET(iphdr, 0);
+#if CHECKSUM_GEN_IP
+    IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_IP) {
+      IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN));
+    }
+#endif /* CHECKSUM_GEN_IP */
+
+    /* No need for separate header pbuf - we allowed room for it in rambuf
+     * when allocated.
+     */
+    netif->output(netif, rambuf, dest);
+    IPFRAG_STATS_INC(ip_frag.xmit);
+
+    /* Unfortunately we can't reuse rambuf - the hardware may still be
+     * using the buffer. Instead we free it (and the ensuing chain) and
+     * recreate it next time round the loop. If we're lucky the hardware
+     * will have already sent the packet, the free will really free, and
+     * there will be zero memory penalty.
+     */
+
+    pbuf_free(rambuf);
+    left = (u16_t)(left - fragsize);
+    ofo = (u16_t)(ofo + nfb);
+  }
+  MIB2_STATS_INC(mib2.ipfragoks);
+  return ERR_OK;
+memerr:
+  MIB2_STATS_INC(mib2.ipfragfails);
+  return ERR_MEM;
+}
+#endif /* IP_FRAG */
+
+#endif /* LWIP_IPV4 */
diff --git a/lib/lwip/core/ipv6/dhcp6.c b/lib/lwip/core/ipv6/dhcp6.c
new file mode 100644
index 0000000000..e6a7e64da9
--- /dev/null
+++ b/lib/lwip/core/ipv6/dhcp6.c
@@ -0,0 +1,821 @@
+/**
+ * @file
+ *
+ * @defgroup dhcp6 DHCPv6
+ * @ingroup ip6
+ * DHCPv6 client: IPv6 address autoconfiguration as per
+ * RFC 3315 (stateful DHCPv6) and
+ * RFC 3736 (stateless DHCPv6).
+ *
+ * For now, only stateless DHCPv6 is implemented!
+ *
+ * TODO:
+ * - enable/disable API to not always start when RA is received
+ * - stateful DHCPv6 (for now, only stateless DHCPv6 for DNS and NTP servers works)
+ * - create Client Identifier?
+ * - only start requests if a valid local address is available on the netif
+ * - only start information requests if required (not for every RA)
+ *
+ * dhcp6_enable_stateful() enables stateful DHCPv6 for a netif (stateless disabled)<br>
+ * dhcp6_enable_stateless() enables stateless DHCPv6 for a netif (stateful disabled)<br>
+ * dhcp6_disable() disable DHCPv6 for a netif
+ *
+ * When enabled, requests are only issued after receipt of RA with the
+ * corresponding bits set.
+ */
+
+/*
+ * Copyright (c) 2018 Simon Goldschmidt
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt <goldsimon@gmx.de>
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV6 && LWIP_IPV6_DHCP6 /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/dhcp6.h"
+#include "lwip/prot/dhcp6.h"
+#include "lwip/def.h"
+#include "lwip/udp.h"
+#include "lwip/dns.h"
+
+#include <string.h>
+
+#ifdef LWIP_HOOK_FILENAME
+#include LWIP_HOOK_FILENAME
+#endif
+#ifndef LWIP_HOOK_DHCP6_APPEND_OPTIONS
+#define LWIP_HOOK_DHCP6_APPEND_OPTIONS(netif, dhcp6, state, msg, msg_type, options_len_ptr, max_len)
+#endif
+#ifndef LWIP_HOOK_DHCP6_PARSE_OPTION
+#define LWIP_HOOK_DHCP6_PARSE_OPTION(netif, dhcp6, state, msg, msg_type, option, len, pbuf, offset) do { LWIP_UNUSED_ARG(msg); } while(0)
+#endif
+
+#if LWIP_DNS && LWIP_DHCP6_MAX_DNS_SERVERS
+#if DNS_MAX_SERVERS > LWIP_DHCP6_MAX_DNS_SERVERS
+#define LWIP_DHCP6_PROVIDE_DNS_SERVERS LWIP_DHCP6_MAX_DNS_SERVERS
+#else
+#define LWIP_DHCP6_PROVIDE_DNS_SERVERS DNS_MAX_SERVERS
+#endif
+#else
+#define LWIP_DHCP6_PROVIDE_DNS_SERVERS 0
+#endif
+
+
+/** Option handling: options are parsed in dhcp6_parse_reply
+ * and saved in an array where other functions can load them from.
+ * This might be moved into the struct dhcp6 (not necessarily since
+ * lwIP is single-threaded and the array is only used while in recv
+ * callback). */
+enum dhcp6_option_idx {
+  DHCP6_OPTION_IDX_CLI_ID = 0,
+  DHCP6_OPTION_IDX_SERVER_ID,
+#if LWIP_DHCP6_PROVIDE_DNS_SERVERS
+  DHCP6_OPTION_IDX_DNS_SERVER,
+  DHCP6_OPTION_IDX_DOMAIN_LIST,
+#endif /* LWIP_DHCP_PROVIDE_DNS_SERVERS */
+#if LWIP_DHCP6_GET_NTP_SRV
+  DHCP6_OPTION_IDX_NTP_SERVER,
+#endif /* LWIP_DHCP_GET_NTP_SRV */
+  DHCP6_OPTION_IDX_MAX
+};
+
+struct dhcp6_option_info {
+  u8_t option_given;
+  u16_t val_start;
+  u16_t val_length;
+};
+
+/** Holds the decoded option info, only valid while in dhcp6_recv. */
+struct dhcp6_option_info dhcp6_rx_options[DHCP6_OPTION_IDX_MAX];
+
+#define dhcp6_option_given(dhcp6, idx)           (dhcp6_rx_options[idx].option_given != 0)
+#define dhcp6_got_option(dhcp6, idx)             (dhcp6_rx_options[idx].option_given = 1)
+#define dhcp6_clear_option(dhcp6, idx)           (dhcp6_rx_options[idx].option_given = 0)
+#define dhcp6_clear_all_options(dhcp6)           (memset(dhcp6_rx_options, 0, sizeof(dhcp6_rx_options)))
+#define dhcp6_get_option_start(dhcp6, idx)       (dhcp6_rx_options[idx].val_start)
+#define dhcp6_get_option_length(dhcp6, idx)      (dhcp6_rx_options[idx].val_length)
+#define dhcp6_set_option(dhcp6, idx, start, len) do { dhcp6_rx_options[idx].val_start = (start); dhcp6_rx_options[idx].val_length = (len); }while(0)
+
+
+const ip_addr_t dhcp6_All_DHCP6_Relay_Agents_and_Servers = IPADDR6_INIT_HOST(0xFF020000, 0, 0, 0x00010002);
+const ip_addr_t dhcp6_All_DHCP6_Servers = IPADDR6_INIT_HOST(0xFF020000, 0, 0, 0x00010003);
+
+static struct udp_pcb *dhcp6_pcb;
+static u8_t dhcp6_pcb_refcount;
+
+
+/* receive, unfold, parse and free incoming messages */
+static void dhcp6_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port);
+
+/** Ensure DHCP PCB is allocated and bound */
+static err_t
+dhcp6_inc_pcb_refcount(void)
+{
+  if (dhcp6_pcb_refcount == 0) {
+    LWIP_ASSERT("dhcp6_inc_pcb_refcount(): memory leak", dhcp6_pcb == NULL);
+
+    /* allocate UDP PCB */
+    dhcp6_pcb = udp_new_ip6();
+
+    if (dhcp6_pcb == NULL) {
+      return ERR_MEM;
+    }
+
+    ip_set_option(dhcp6_pcb, SOF_BROADCAST);
+
+    /* set up local and remote port for the pcb -> listen on all interfaces on all src/dest IPs */
+    udp_bind(dhcp6_pcb, IP6_ADDR_ANY, DHCP6_CLIENT_PORT);
+    udp_recv(dhcp6_pcb, dhcp6_recv, NULL);
+  }
+
+  dhcp6_pcb_refcount++;
+
+  return ERR_OK;
+}
+
+/** Free DHCP PCB if the last netif stops using it */
+static void
+dhcp6_dec_pcb_refcount(void)
+{
+  LWIP_ASSERT("dhcp6_pcb_refcount(): refcount error", (dhcp6_pcb_refcount > 0));
+  dhcp6_pcb_refcount--;
+
+  if (dhcp6_pcb_refcount == 0) {
+    udp_remove(dhcp6_pcb);
+    dhcp6_pcb = NULL;
+  }
+}
+
+/**
+ * @ingroup dhcp6
+ * Set a statically allocated struct dhcp6 to work with.
+ * Using this prevents dhcp6_start to allocate it using mem_malloc.
+ *
+ * @param netif the netif for which to set the struct dhcp
+ * @param dhcp6 (uninitialised) dhcp6 struct allocated by the application
+ */
+void
+dhcp6_set_struct(struct netif *netif, struct dhcp6 *dhcp6)
+{
+  LWIP_ASSERT("netif != NULL", netif != NULL);
+  LWIP_ASSERT("dhcp6 != NULL", dhcp6 != NULL);
+  LWIP_ASSERT("netif already has a struct dhcp6 set", netif_dhcp6_data(netif) == NULL);
+
+  /* clear data structure */
+  memset(dhcp6, 0, sizeof(struct dhcp6));
+  /* dhcp6_set_state(&dhcp, DHCP6_STATE_OFF); */
+  netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP6, dhcp6);
+}
+
+/**
+ * @ingroup dhcp6
+ * Removes a struct dhcp6 from a netif.
+ *
+ * ATTENTION: Only use this when not using dhcp6_set_struct() to allocate the
+ *            struct dhcp6 since the memory is passed back to the heap.
+ *
+ * @param netif the netif from which to remove the struct dhcp
+ */
+void dhcp6_cleanup(struct netif *netif)
+{
+  LWIP_ASSERT("netif != NULL", netif != NULL);
+
+  if (netif_dhcp6_data(netif) != NULL) {
+    mem_free(netif_dhcp6_data(netif));
+    netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP6, NULL);
+  }
+}
+
+static struct dhcp6*
+dhcp6_get_struct(struct netif *netif, const char *dbg_requester)
+{
+  struct dhcp6 *dhcp6 = netif_dhcp6_data(netif);
+  if (dhcp6 == NULL) {
+    LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("%s: mallocing new DHCPv6 client\n", dbg_requester));
+    dhcp6 = (struct dhcp6 *)mem_malloc(sizeof(struct dhcp6));
+    if (dhcp6 == NULL) {
+      LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("%s: could not allocate dhcp6\n", dbg_requester));
+      return NULL;
+    }
+
+    /* clear data structure, this implies DHCP6_STATE_OFF */
+    memset(dhcp6, 0, sizeof(struct dhcp6));
+    /* store this dhcp6 client in the netif */
+    netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP6, dhcp6);
+  } else {
+    /* already has DHCP6 client attached */
+    LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("%s: using existing DHCPv6 client\n", dbg_requester));
+  }
+
+  if (!dhcp6->pcb_allocated) {
+    if (dhcp6_inc_pcb_refcount() != ERR_OK) { /* ensure DHCP6 PCB is allocated */
+      mem_free(dhcp6);
+      netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP6, NULL);
+      return NULL;
+    }
+    LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("%s: allocated dhcp6\n", dbg_requester));
+    dhcp6->pcb_allocated = 1;
+  }
+  return dhcp6;
+}
+
+/*
+ * Set the DHCPv6 state
+ * If the state changed, reset the number of tries.
+ */
+static void
+dhcp6_set_state(struct dhcp6 *dhcp6, u8_t new_state, const char *dbg_caller)
+{
+  LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("DHCPv6 state: %d -> %d (%s)\n",
+    dhcp6->state, new_state, dbg_caller));
+  if (new_state != dhcp6->state) {
+    dhcp6->state = new_state;
+    dhcp6->tries = 0;
+    dhcp6->request_timeout = 0;
+  }
+}
+
+static int
+dhcp6_stateless_enabled(struct dhcp6 *dhcp6)
+{
+  if ((dhcp6->state == DHCP6_STATE_STATELESS_IDLE) ||
+      (dhcp6->state == DHCP6_STATE_REQUESTING_CONFIG)) {
+    return 1;
+  }
+  return 0;
+}
+
+/*static int
+dhcp6_stateful_enabled(struct dhcp6 *dhcp6)
+{
+  if (dhcp6->state == DHCP6_STATE_OFF) {
+    return 0;
+  }
+  if (dhcp6_stateless_enabled(dhcp6)) {
+    return 0;
+  }
+  return 1;
+}*/
+
+/**
+ * @ingroup dhcp6
+ * Enable stateful DHCPv6 on this netif
+ * Requests are sent on receipt of an RA message with the
+ * ND6_RA_FLAG_MANAGED_ADDR_CONFIG flag set.
+ *
+ * A struct dhcp6 will be allocated for this netif if not
+ * set via @ref dhcp6_set_struct before.
+ *
+ * @todo: stateful DHCPv6 not supported, yet
+ */
+err_t
+dhcp6_enable_stateful(struct netif *netif)
+{
+  LWIP_UNUSED_ARG(netif);
+  LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("stateful dhcp6 not implemented yet\n"));
+  return ERR_VAL;
+}
+
+/**
+ * @ingroup dhcp6
+ * Enable stateless DHCPv6 on this netif
+ * Requests are sent on receipt of an RA message with the
+ * ND6_RA_FLAG_OTHER_CONFIG flag set.
+ *
+ * A struct dhcp6 will be allocated for this netif if not
+ * set via @ref dhcp6_set_struct before.
+ */
+err_t
+dhcp6_enable_stateless(struct netif *netif)
+{
+  struct dhcp6 *dhcp6;
+
+  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp6_enable_stateless(netif=%p) %c%c%"U16_F"\n", (void *)netif, netif->name[0], netif->name[1], (u16_t)netif->num));
+
+  dhcp6 = dhcp6_get_struct(netif, "dhcp6_enable_stateless()");
+  if (dhcp6 == NULL) {
+    return ERR_MEM;
+  }
+  if (dhcp6_stateless_enabled(dhcp6)) {
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp6_enable_stateless(): stateless DHCPv6 already enabled\n"));
+    return ERR_OK;
+  } else if (dhcp6->state != DHCP6_STATE_OFF) {
+    /* stateful running */
+    /* @todo: stop stateful once it is implemented */
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp6_enable_stateless(): switching from stateful to stateless DHCPv6\n"));
+  }
+  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp6_enable_stateless(): stateless DHCPv6 enabled\n"));
+  dhcp6_set_state(dhcp6, DHCP6_STATE_STATELESS_IDLE, "dhcp6_enable_stateless");
+  return ERR_OK;
+}
+
+/**
+ * @ingroup dhcp6
+ * Disable stateful or stateless DHCPv6 on this netif
+ * Requests are sent on receipt of an RA message with the
+ * ND6_RA_FLAG_OTHER_CONFIG flag set.
+ */
+void
+dhcp6_disable(struct netif *netif)
+{
+  struct dhcp6 *dhcp6;
+
+  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp6_disable(netif=%p) %c%c%"U16_F"\n", (void *)netif, netif->name[0], netif->name[1], (u16_t)netif->num));
+
+  dhcp6 = netif_dhcp6_data(netif);
+  if (dhcp6 != NULL) {
+    if (dhcp6->state != DHCP6_STATE_OFF) {
+      LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("dhcp6_disable(): DHCPv6 disabled (old state: %s)\n",
+        (dhcp6_stateless_enabled(dhcp6) ? "stateless" : "stateful")));
+      dhcp6_set_state(dhcp6, DHCP6_STATE_OFF, "dhcp6_disable");
+      if (dhcp6->pcb_allocated != 0) {
+        dhcp6_dec_pcb_refcount(); /* free DHCPv6 PCB if not needed any more */
+        dhcp6->pcb_allocated = 0;
+      }
+    }
+  }
+}
+
+/**
+ * Create a DHCPv6 request, fill in common headers
+ *
+ * @param netif the netif under DHCPv6 control
+ * @param dhcp6 dhcp6 control struct
+ * @param message_type message type of the request
+ * @param opt_len_alloc option length to allocate
+ * @param options_out_len option length on exit
+ * @return a pbuf for the message
+ */
+static struct pbuf *
+dhcp6_create_msg(struct netif *netif, struct dhcp6 *dhcp6, u8_t message_type,
+                 u16_t opt_len_alloc, u16_t *options_out_len)
+{
+  struct pbuf *p_out;
+  struct dhcp6_msg *msg_out;
+
+  LWIP_ERROR("dhcp6_create_msg: netif != NULL", (netif != NULL), return NULL;);
+  LWIP_ERROR("dhcp6_create_msg: dhcp6 != NULL", (dhcp6 != NULL), return NULL;);
+  p_out = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct dhcp6_msg) + opt_len_alloc, PBUF_RAM);
+  if (p_out == NULL) {
+    LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
+                ("dhcp6_create_msg(): could not allocate pbuf\n"));
+    return NULL;
+  }
+  LWIP_ASSERT("dhcp6_create_msg: check that first pbuf can hold struct dhcp6_msg",
+              (p_out->len >= sizeof(struct dhcp6_msg) + opt_len_alloc));
+
+  /* @todo: limit new xid for certain message types? */
+  /* reuse transaction identifier in retransmissions */
+  if (dhcp6->tries == 0) {
+    dhcp6->xid = LWIP_RAND() & 0xFFFFFF;
+  }
+
+  LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE,
+              ("transaction id xid(%"X32_F")\n", dhcp6->xid));
+
+  msg_out = (struct dhcp6_msg *)p_out->payload;
+  memset(msg_out, 0, sizeof(struct dhcp6_msg) + opt_len_alloc);
+
+  msg_out->msgtype = message_type;
+  msg_out->transaction_id[0] = (u8_t)(dhcp6->xid >> 16);
+  msg_out->transaction_id[1] = (u8_t)(dhcp6->xid >> 8);
+  msg_out->transaction_id[2] = (u8_t)dhcp6->xid;
+  *options_out_len = 0;
+  return p_out;
+}
+
+static u16_t
+dhcp6_option_short(u16_t options_out_len, u8_t *options, u16_t value)
+{
+  options[options_out_len++] = (u8_t)((value & 0xff00U) >> 8);
+  options[options_out_len++] = (u8_t) (value & 0x00ffU);
+  return options_out_len;
+}
+
+static u16_t
+dhcp6_option_optionrequest(u16_t options_out_len, u8_t *options, const u16_t *req_options,
+                           u16_t num_req_options, u16_t max_len)
+{
+  size_t i;
+  u16_t ret;
+
+  LWIP_ASSERT("dhcp6_option_optionrequest: options_out_len + sizeof(struct dhcp6_msg) + addlen <= max_len",
+    sizeof(struct dhcp6_msg) + options_out_len + 4U + (2U * num_req_options) <= max_len);
+  LWIP_UNUSED_ARG(max_len);
+
+  ret = dhcp6_option_short(options_out_len, options, DHCP6_OPTION_ORO);
+  ret = dhcp6_option_short(ret, options, 2 * num_req_options);
+  for (i = 0; i < num_req_options; i++) {
+    ret = dhcp6_option_short(ret, options, req_options[i]);
+  }
+  return ret;
+}
+
+/* All options are added, shrink the pbuf to the required size */
+static void
+dhcp6_msg_finalize(u16_t options_out_len, struct pbuf *p_out)
+{
+  /* shrink the pbuf to the actual content length */
+  pbuf_realloc(p_out, (u16_t)(sizeof(struct dhcp6_msg) + options_out_len));
+}
+
+
+#if LWIP_IPV6_DHCP6_STATELESS
+static void
+dhcp6_information_request(struct netif *netif, struct dhcp6 *dhcp6)
+{
+  const u16_t requested_options[] = {
+#if LWIP_DHCP6_PROVIDE_DNS_SERVERS
+    DHCP6_OPTION_DNS_SERVERS,
+    DHCP6_OPTION_DOMAIN_LIST
+#endif
+#if LWIP_DHCP6_GET_NTP_SRV
+    , DHCP6_OPTION_SNTP_SERVERS
+#endif
+  };
+
+  u16_t msecs;
+  struct pbuf *p_out;
+  u16_t options_out_len;
+  LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("dhcp6_information_request()\n"));
+  /* create and initialize the DHCP message header */
+  p_out = dhcp6_create_msg(netif, dhcp6, DHCP6_INFOREQUEST, 4 + sizeof(requested_options), &options_out_len);
+  if (p_out != NULL) {
+    err_t err;
+    struct dhcp6_msg *msg_out = (struct dhcp6_msg *)p_out->payload;
+    u8_t *options = (u8_t *)(msg_out + 1);
+    LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("dhcp6_information_request: making request\n"));
+
+    options_out_len = dhcp6_option_optionrequest(options_out_len, options, requested_options,
+      LWIP_ARRAYSIZE(requested_options), p_out->len);
+    LWIP_HOOK_DHCP6_APPEND_OPTIONS(netif, dhcp6, DHCP6_STATE_REQUESTING_CONFIG, msg_out,
+      DHCP6_INFOREQUEST, options_out_len, p_out->len);
+    dhcp6_msg_finalize(options_out_len, p_out);
+
+    err = udp_sendto_if(dhcp6_pcb, p_out, &dhcp6_All_DHCP6_Relay_Agents_and_Servers, DHCP6_SERVER_PORT, netif);
+    pbuf_free(p_out);
+    LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp6_information_request: INFOREQUESTING -> %d\n", (int)err));
+    LWIP_UNUSED_ARG(err);
+  } else {
+    LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp6_information_request: could not allocate DHCP6 request\n"));
+  }
+  dhcp6_set_state(dhcp6, DHCP6_STATE_REQUESTING_CONFIG, "dhcp6_information_request");
+  if (dhcp6->tries < 255) {
+    dhcp6->tries++;
+  }
+  msecs = (u16_t)((dhcp6->tries < 6 ? 1 << dhcp6->tries : 60) * 1000);
+  dhcp6->request_timeout = (u16_t)((msecs + DHCP6_TIMER_MSECS - 1) / DHCP6_TIMER_MSECS);
+  LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp6_information_request(): set request timeout %"U16_F" msecs\n", msecs));
+}
+
+static err_t
+dhcp6_request_config(struct netif *netif, struct dhcp6 *dhcp6)
+{
+  /* stateless mode enabled and no request running? */
+  if (dhcp6->state == DHCP6_STATE_STATELESS_IDLE) {
+    /* send Information-request and wait for answer; setup receive timeout */
+    dhcp6_information_request(netif, dhcp6);
+  }
+
+  return ERR_OK;
+}
+
+static void
+dhcp6_abort_config_request(struct dhcp6 *dhcp6)
+{
+  if (dhcp6->state == DHCP6_STATE_REQUESTING_CONFIG) {
+    /* abort running request */
+    dhcp6_set_state(dhcp6, DHCP6_STATE_STATELESS_IDLE, "dhcp6_abort_config_request");
+  }
+}
+
+/* Handle a REPLY to INFOREQUEST
+ * This parses DNS and NTP server addresses from the reply.
+ */
+static void
+dhcp6_handle_config_reply(struct netif *netif, struct pbuf *p_msg_in)
+{
+  struct dhcp6 *dhcp6 = netif_dhcp6_data(netif);
+
+  LWIP_UNUSED_ARG(dhcp6);
+  LWIP_UNUSED_ARG(p_msg_in);
+
+#if LWIP_DHCP6_PROVIDE_DNS_SERVERS
+  if (dhcp6_option_given(dhcp6, DHCP6_OPTION_IDX_DNS_SERVER)) {
+    ip_addr_t dns_addr;
+    ip6_addr_t *dns_addr6;
+    u16_t op_start = dhcp6_get_option_start(dhcp6, DHCP6_OPTION_IDX_DNS_SERVER);
+    u16_t op_len = dhcp6_get_option_length(dhcp6, DHCP6_OPTION_IDX_DNS_SERVER);
+    u16_t idx;
+    u8_t n;
+
+    ip_addr_set_zero_ip6(&dns_addr);
+    dns_addr6 = ip_2_ip6(&dns_addr);
+    for (n = 0, idx = op_start; (idx < op_start + op_len) && (n < LWIP_DHCP6_PROVIDE_DNS_SERVERS);
+         n++, idx += sizeof(struct ip6_addr_packed)) {
+      u16_t copied = pbuf_copy_partial(p_msg_in, dns_addr6, sizeof(struct ip6_addr_packed), idx);
+      if (copied != sizeof(struct ip6_addr_packed)) {
+        /* pbuf length mismatch */
+        return;
+      }
+      ip6_addr_assign_zone(dns_addr6, IP6_UNKNOWN, netif);
+      /* @todo: do we need a different offset than DHCP(v4)? */
+      dns_setserver(n, &dns_addr);
+    }
+  }
+  /* @ todo: parse and set Domain Search List */
+#endif /* LWIP_DHCP6_PROVIDE_DNS_SERVERS */
+
+#if LWIP_DHCP6_GET_NTP_SRV
+  if (dhcp6_option_given(dhcp6, DHCP6_OPTION_IDX_NTP_SERVER)) {
+    ip_addr_t ntp_server_addrs[LWIP_DHCP6_MAX_NTP_SERVERS];
+    u16_t op_start = dhcp6_get_option_start(dhcp6, DHCP6_OPTION_IDX_NTP_SERVER);
+    u16_t op_len = dhcp6_get_option_length(dhcp6, DHCP6_OPTION_IDX_NTP_SERVER);
+    u16_t idx;
+    u8_t n;
+
+    for (n = 0, idx = op_start; (idx < op_start + op_len) && (n < LWIP_DHCP6_MAX_NTP_SERVERS);
+         n++, idx += sizeof(struct ip6_addr_packed)) {
+      u16_t copied;
+      ip6_addr_t *ntp_addr6 = ip_2_ip6(&ntp_server_addrs[n]);
+      ip_addr_set_zero_ip6(&ntp_server_addrs[n]);
+      copied = pbuf_copy_partial(p_msg_in, ntp_addr6, sizeof(struct ip6_addr_packed), idx);
+      if (copied != sizeof(struct ip6_addr_packed)) {
+        /* pbuf length mismatch */
+        return;
+      }
+      ip6_addr_assign_zone(ntp_addr6, IP6_UNKNOWN, netif);
+    }
+    dhcp6_set_ntp_servers(n, ntp_server_addrs);
+  }
+#endif /* LWIP_DHCP6_GET_NTP_SRV */
+}
+#endif /* LWIP_IPV6_DHCP6_STATELESS */
+
+/** This function is called from nd6 module when an RA message is received
+ * It triggers DHCPv6 requests (if enabled).
+ */
+void
+dhcp6_nd6_ra_trigger(struct netif *netif, u8_t managed_addr_config, u8_t other_config)
+{
+  struct dhcp6 *dhcp6;
+
+  LWIP_ASSERT("netif != NULL", netif != NULL);
+  dhcp6 = netif_dhcp6_data(netif);
+
+  LWIP_UNUSED_ARG(managed_addr_config);
+  LWIP_UNUSED_ARG(other_config);
+  LWIP_UNUSED_ARG(dhcp6);
+
+#if LWIP_IPV6_DHCP6_STATELESS
+  if (dhcp6 != NULL) {
+    if (dhcp6_stateless_enabled(dhcp6)) {
+      if (other_config) {
+        dhcp6_request_config(netif, dhcp6);
+      } else {
+        dhcp6_abort_config_request(dhcp6);
+      }
+    }
+  }
+#endif /* LWIP_IPV6_DHCP6_STATELESS */
+}
+
+/**
+ * Parse the DHCPv6 message and extract the DHCPv6 options.
+ *
+ * Extract the DHCPv6 options (offset + length) so that we can later easily
+ * check for them or extract the contents.
+ */
+static err_t
+dhcp6_parse_reply(struct pbuf *p, struct dhcp6 *dhcp6)
+{
+  u16_t offset;
+  u16_t offset_max;
+  u16_t options_idx;
+  struct dhcp6_msg *msg_in;
+
+  LWIP_UNUSED_ARG(dhcp6);
+
+  /* clear received options */
+  dhcp6_clear_all_options(dhcp6);
+  msg_in = (struct dhcp6_msg *)p->payload;
+
+  /* parse options */
+
+  options_idx = sizeof(struct dhcp6_msg);
+  /* parse options to the end of the received packet */
+  offset_max = p->tot_len;
+
+  offset = options_idx;
+  /* at least 4 byte to read? */
+  while ((offset + 4 <= offset_max)) {
+    u8_t op_len_buf[4];
+    u8_t *op_len;
+    u16_t op;
+    u16_t len;
+    u16_t val_offset = (u16_t)(offset + 4);
+    if (val_offset < offset) {
+      /* overflow */
+      return ERR_BUF;
+    }
+    /* copy option + length, might be split across pbufs */
+    op_len = (u8_t *)pbuf_get_contiguous(p, op_len_buf, 4, 4, offset);
+    if (op_len == NULL) {
+      /* failed to get option and length */
+      return ERR_VAL;
+    }
+    op = (op_len[0] << 8) | op_len[1];
+    len = (op_len[2] << 8) | op_len[3];
+    offset = val_offset + len;
+    if (offset < val_offset) {
+      /* overflow */
+      return ERR_BUF;
+    }
+
+    switch (op) {
+      case (DHCP6_OPTION_CLIENTID):
+        dhcp6_got_option(dhcp6, DHCP6_OPTION_IDX_CLI_ID);
+        dhcp6_set_option(dhcp6, DHCP6_OPTION_IDX_CLI_ID, val_offset, len);
+        break;
+      case (DHCP6_OPTION_SERVERID):
+        dhcp6_got_option(dhcp6, DHCP6_OPTION_IDX_SERVER_ID);
+        dhcp6_set_option(dhcp6, DHCP6_OPTION_IDX_SERVER_ID, val_offset, len);
+        break;
+#if LWIP_DHCP6_PROVIDE_DNS_SERVERS
+      case (DHCP6_OPTION_DNS_SERVERS):
+        dhcp6_got_option(dhcp6, DHCP6_OPTION_IDX_DNS_SERVER);
+        dhcp6_set_option(dhcp6, DHCP6_OPTION_IDX_DNS_SERVER, val_offset, len);
+        break;
+      case (DHCP6_OPTION_DOMAIN_LIST):
+        dhcp6_got_option(dhcp6, DHCP6_OPTION_IDX_DOMAIN_LIST);
+        dhcp6_set_option(dhcp6, DHCP6_OPTION_IDX_DOMAIN_LIST, val_offset, len);
+        break;
+#endif /* LWIP_DHCP6_PROVIDE_DNS_SERVERS */
+#if LWIP_DHCP6_GET_NTP_SRV
+      case (DHCP6_OPTION_SNTP_SERVERS):
+        dhcp6_got_option(dhcp6, DHCP6_OPTION_IDX_NTP_SERVER);
+        dhcp6_set_option(dhcp6, DHCP6_OPTION_IDX_NTP_SERVER, val_offset, len);
+        break;
+#endif /* LWIP_DHCP6_GET_NTP_SRV*/
+      default:
+        LWIP_DEBUGF(DHCP6_DEBUG, ("skipping option %"U16_F" in options\n", op));
+        LWIP_HOOK_DHCP6_PARSE_OPTION(ip_current_netif(), dhcp6, dhcp6->state, msg_in,
+          msg_in->msgtype, op, len, q, val_offset);
+        break;
+    }
+  }
+  return ERR_OK;
+}
+
+static void
+dhcp6_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
+{
+  struct netif *netif = ip_current_input_netif();
+  struct dhcp6 *dhcp6 = netif_dhcp6_data(netif);
+  struct dhcp6_msg *reply_msg = (struct dhcp6_msg *)p->payload;
+  u8_t msg_type;
+  u32_t xid;
+
+  LWIP_UNUSED_ARG(arg);
+
+  /* Caught DHCPv6 message from netif that does not have DHCPv6 enabled? -> not interested */
+  if ((dhcp6 == NULL) || (dhcp6->pcb_allocated == 0)) {
+    goto free_pbuf_and_return;
+  }
+
+  LWIP_ERROR("invalid server address type", IP_IS_V6(addr), goto free_pbuf_and_return;);
+
+  LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("dhcp6_recv(pbuf = %p) from DHCPv6 server %s port %"U16_F"\n", (void *)p,
+    ipaddr_ntoa(addr), port));
+  LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("pbuf->len = %"U16_F"\n", p->len));
+  LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("pbuf->tot_len = %"U16_F"\n", p->tot_len));
+  /* prevent warnings about unused arguments */
+  LWIP_UNUSED_ARG(pcb);
+  LWIP_UNUSED_ARG(addr);
+  LWIP_UNUSED_ARG(port);
+
+  if (p->len < sizeof(struct dhcp6_msg)) {
+    LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("DHCPv6 reply message or pbuf too short\n"));
+    goto free_pbuf_and_return;
+  }
+
+  /* match transaction ID against what we expected */
+  xid = reply_msg->transaction_id[0] << 16;
+  xid |= reply_msg->transaction_id[1] << 8;
+  xid |= reply_msg->transaction_id[2];
+  if (xid != dhcp6->xid) {
+    LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
+                ("transaction id mismatch reply_msg->xid(%"X32_F")!= dhcp6->xid(%"X32_F")\n", xid, dhcp6->xid));
+    goto free_pbuf_and_return;
+  }
+  /* option fields could be unfold? */
+  if (dhcp6_parse_reply(p, dhcp6) != ERR_OK) {
+    LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
+                ("problem unfolding DHCPv6 message - too short on memory?\n"));
+    goto free_pbuf_and_return;
+  }
+
+  /* read DHCP message type */
+  msg_type = reply_msg->msgtype;
+  /* message type is DHCP6 REPLY? */
+  if (msg_type == DHCP6_REPLY) {
+    LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("DHCP6_REPLY received\n"));
+#if LWIP_IPV6_DHCP6_STATELESS
+    /* in info-requesting state? */
+    if (dhcp6->state == DHCP6_STATE_REQUESTING_CONFIG) {
+      dhcp6_set_state(dhcp6, DHCP6_STATE_STATELESS_IDLE, "dhcp6_recv");
+      dhcp6_handle_config_reply(netif, p);
+    } else
+#endif /* LWIP_IPV6_DHCP6_STATELESS */
+    {
+      /* @todo: handle reply in other states? */
+    }
+  } else {
+    /* @todo: handle other message types */
+  }
+
+free_pbuf_and_return:
+  pbuf_free(p);
+}
+
+/**
+ * A DHCPv6 request has timed out.
+ *
+ * The timer that was started with the DHCPv6 request has
+ * timed out, indicating no response was received in time.
+ */
+static void
+dhcp6_timeout(struct netif *netif, struct dhcp6 *dhcp6)
+{
+  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp6_timeout()\n"));
+
+  LWIP_UNUSED_ARG(netif);
+  LWIP_UNUSED_ARG(dhcp6);
+
+#if LWIP_IPV6_DHCP6_STATELESS
+  /* back-off period has passed, or server selection timed out */
+  if (dhcp6->state == DHCP6_STATE_REQUESTING_CONFIG) {
+    LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("dhcp6_timeout(): retrying information request\n"));
+    dhcp6_information_request(netif, dhcp6);
+  }
+#endif /* LWIP_IPV6_DHCP6_STATELESS */
+}
+
+/**
+ * DHCPv6 timeout handling (this function must be called every 500ms,
+ * see @ref DHCP6_TIMER_MSECS).
+ *
+ * A DHCPv6 server is expected to respond within a short period of time.
+ * This timer checks whether an outstanding DHCPv6 request is timed out.
+ */
+void
+dhcp6_tmr(void)
+{
+  struct netif *netif;
+  /* loop through netif's */
+  NETIF_FOREACH(netif) {
+    struct dhcp6 *dhcp6 = netif_dhcp6_data(netif);
+    /* only act on DHCPv6 configured interfaces */
+    if (dhcp6 != NULL) {
+      /* timer is active (non zero), and is about to trigger now */
+      if (dhcp6->request_timeout > 1) {
+        dhcp6->request_timeout--;
+      } else if (dhcp6->request_timeout == 1) {
+        dhcp6->request_timeout--;
+        /* { dhcp6->request_timeout == 0 } */
+        LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp6_tmr(): request timeout\n"));
+        /* this client's request timeout triggered */
+        dhcp6_timeout(netif, dhcp6);
+      }
+    }
+  }
+}
+
+#endif /* LWIP_IPV6 && LWIP_IPV6_DHCP6 */
diff --git a/lib/lwip/core/ipv6/ethip6.c b/lib/lwip/core/ipv6/ethip6.c
new file mode 100644
index 0000000000..fec8b28a9d
--- /dev/null
+++ b/lib/lwip/core/ipv6/ethip6.c
@@ -0,0 +1,123 @@
+/**
+ * @file
+ *
+ * Ethernet output for IPv6. Uses ND tables for link-layer addressing.
+ */
+
+/*
+ * Copyright (c) 2010 Inico Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Ivan Delamer <delamer@inicotech.com>
+ *
+ *
+ * Please coordinate changes and requests with Ivan Delamer
+ * <delamer@inicotech.com>
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV6 && LWIP_ETHERNET
+
+#include "lwip/ethip6.h"
+#include "lwip/nd6.h"
+#include "lwip/pbuf.h"
+#include "lwip/ip6.h"
+#include "lwip/ip6_addr.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/netif.h"
+#include "lwip/icmp6.h"
+#include "lwip/prot/ethernet.h"
+#include "netif/ethernet.h"
+
+#include <string.h>
+
+/**
+ * Resolve and fill-in Ethernet address header for outgoing IPv6 packet.
+ *
+ * For IPv6 multicast, corresponding Ethernet addresses
+ * are selected and the packet is transmitted on the link.
+ *
+ * For unicast addresses, ask the ND6 module what to do. It will either let us
+ * send the the packet right away, or queue the packet for later itself, unless
+ * an error occurs.
+ *
+ * @todo anycast addresses
+ *
+ * @param netif The lwIP network interface which the IP packet will be sent on.
+ * @param q The pbuf(s) containing the IP packet to be sent.
+ * @param ip6addr The IP address of the packet destination.
+ *
+ * @return
+ * - ERR_OK or the return value of @ref nd6_get_next_hop_addr_or_queue.
+ */
+err_t
+ethip6_output(struct netif *netif, struct pbuf *q, const ip6_addr_t *ip6addr)
+{
+  struct eth_addr dest;
+  const u8_t *hwaddr;
+  err_t result;
+
+  LWIP_ASSERT_CORE_LOCKED();
+
+  /* The destination IP address must be properly zoned from here on down. */
+  IP6_ADDR_ZONECHECK_NETIF(ip6addr, netif);
+
+  /* multicast destination IP address? */
+  if (ip6_addr_ismulticast(ip6addr)) {
+    /* Hash IP multicast address to MAC address.*/
+    dest.addr[0] = 0x33;
+    dest.addr[1] = 0x33;
+    dest.addr[2] = ((const u8_t *)(&(ip6addr->addr[3])))[0];
+    dest.addr[3] = ((const u8_t *)(&(ip6addr->addr[3])))[1];
+    dest.addr[4] = ((const u8_t *)(&(ip6addr->addr[3])))[2];
+    dest.addr[5] = ((const u8_t *)(&(ip6addr->addr[3])))[3];
+
+    /* Send out. */
+    return ethernet_output(netif, q, (const struct eth_addr*)(netif->hwaddr), &dest, ETHTYPE_IPV6);
+  }
+
+  /* We have a unicast destination IP address */
+  /* @todo anycast? */
+
+  /* Ask ND6 what to do with the packet. */
+  result = nd6_get_next_hop_addr_or_queue(netif, q, ip6addr, &hwaddr);
+  if (result != ERR_OK) {
+    return result;
+  }
+
+  /* If no hardware address is returned, nd6 has queued the packet for later. */
+  if (hwaddr == NULL) {
+    return ERR_OK;
+  }
+
+  /* Send out the packet using the returned hardware address. */
+  SMEMCPY(dest.addr, hwaddr, 6);
+  return ethernet_output(netif, q, (const struct eth_addr*)(netif->hwaddr), &dest, ETHTYPE_IPV6);
+}
+
+#endif /* LWIP_IPV6 && LWIP_ETHERNET */
diff --git a/lib/lwip/core/ipv6/icmp6.c b/lib/lwip/core/ipv6/icmp6.c
new file mode 100644
index 0000000000..ed0bd7b748
--- /dev/null
+++ b/lib/lwip/core/ipv6/icmp6.c
@@ -0,0 +1,425 @@
+/**
+ * @file
+ *
+ * IPv6 version of ICMP, as per RFC 4443.
+ */
+
+/*
+ * Copyright (c) 2010 Inico Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Ivan Delamer <delamer@inicotech.com>
+ *
+ *
+ * Please coordinate changes and requests with Ivan Delamer
+ * <delamer@inicotech.com>
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_ICMP6 && LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/icmp6.h"
+#include "lwip/prot/icmp6.h"
+#include "lwip/ip6.h"
+#include "lwip/ip6_addr.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/pbuf.h"
+#include "lwip/netif.h"
+#include "lwip/nd6.h"
+#include "lwip/mld6.h"
+#include "lwip/ip.h"
+#include "lwip/stats.h"
+
+#include <string.h>
+
+#if !LWIP_ICMP6_DATASIZE || (LWIP_ICMP6_DATASIZE > (IP6_MIN_MTU_LENGTH - IP6_HLEN - ICMP6_HLEN))
+#undef LWIP_ICMP6_DATASIZE
+#define LWIP_ICMP6_DATASIZE   (IP6_MIN_MTU_LENGTH - IP6_HLEN - ICMP6_HLEN)
+#endif
+
+/* Forward declarations */
+static void icmp6_send_response(struct pbuf *p, u8_t code, u32_t data, u8_t type);
+static void icmp6_send_response_with_addrs(struct pbuf *p, u8_t code, u32_t data,
+    u8_t type, const ip6_addr_t *src_addr, const ip6_addr_t *dest_addr);
+static void icmp6_send_response_with_addrs_and_netif(struct pbuf *p, u8_t code, u32_t data,
+    u8_t type, const ip6_addr_t *src_addr, const ip6_addr_t *dest_addr, struct netif *netif);
+
+
+/**
+ * Process an input ICMPv6 message. Called by ip6_input.
+ *
+ * Will generate a reply for echo requests. Other messages are forwarded
+ * to nd6_input, or mld6_input.
+ *
+ * @param p the mld packet, p->payload pointing to the icmpv6 header
+ * @param inp the netif on which this packet was received
+ */
+void
+icmp6_input(struct pbuf *p, struct netif *inp)
+{
+  struct icmp6_hdr *icmp6hdr;
+  struct pbuf *r;
+  const ip6_addr_t *reply_src;
+
+  ICMP6_STATS_INC(icmp6.recv);
+
+  /* Check that ICMPv6 header fits in payload */
+  if (p->len < sizeof(struct icmp6_hdr)) {
+    /* drop short packets */
+    pbuf_free(p);
+    ICMP6_STATS_INC(icmp6.lenerr);
+    ICMP6_STATS_INC(icmp6.drop);
+    return;
+  }
+
+  icmp6hdr = (struct icmp6_hdr *)p->payload;
+
+#if CHECKSUM_CHECK_ICMP6
+  IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_CHECK_ICMP6) {
+    if (ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->tot_len, ip6_current_src_addr(),
+                          ip6_current_dest_addr()) != 0) {
+      /* Checksum failed */
+      pbuf_free(p);
+      ICMP6_STATS_INC(icmp6.chkerr);
+      ICMP6_STATS_INC(icmp6.drop);
+      return;
+    }
+  }
+#endif /* CHECKSUM_CHECK_ICMP6 */
+
+  switch (icmp6hdr->type) {
+  case ICMP6_TYPE_NA: /* Neighbor advertisement */
+  case ICMP6_TYPE_NS: /* Neighbor solicitation */
+  case ICMP6_TYPE_RA: /* Router advertisement */
+  case ICMP6_TYPE_RD: /* Redirect */
+  case ICMP6_TYPE_PTB: /* Packet too big */
+    nd6_input(p, inp);
+    return;
+  case ICMP6_TYPE_RS:
+#if LWIP_IPV6_FORWARD
+    /* @todo implement router functionality */
+#endif
+    break;
+#if LWIP_IPV6_MLD
+  case ICMP6_TYPE_MLQ:
+  case ICMP6_TYPE_MLR:
+  case ICMP6_TYPE_MLD:
+    mld6_input(p, inp);
+    return;
+#endif
+  case ICMP6_TYPE_EREQ:
+#if !LWIP_MULTICAST_PING
+    /* multicast destination address? */
+    if (ip6_addr_ismulticast(ip6_current_dest_addr())) {
+      /* drop */
+      pbuf_free(p);
+      ICMP6_STATS_INC(icmp6.drop);
+      return;
+    }
+#endif /* LWIP_MULTICAST_PING */
+
+    /* Allocate reply. */
+    r = pbuf_alloc(PBUF_IP, p->tot_len, PBUF_RAM);
+    if (r == NULL) {
+      /* drop */
+      pbuf_free(p);
+      ICMP6_STATS_INC(icmp6.memerr);
+      return;
+    }
+
+    /* Copy echo request. */
+    if (pbuf_copy(r, p) != ERR_OK) {
+      /* drop */
+      pbuf_free(p);
+      pbuf_free(r);
+      ICMP6_STATS_INC(icmp6.err);
+      return;
+    }
+
+    /* Determine reply source IPv6 address. */
+#if LWIP_MULTICAST_PING
+    if (ip6_addr_ismulticast(ip6_current_dest_addr())) {
+      reply_src = ip_2_ip6(ip6_select_source_address(inp, ip6_current_src_addr()));
+      if (reply_src == NULL) {
+        /* drop */
+        pbuf_free(p);
+        pbuf_free(r);
+        ICMP6_STATS_INC(icmp6.rterr);
+        return;
+      }
+    }
+    else
+#endif /* LWIP_MULTICAST_PING */
+    {
+      reply_src = ip6_current_dest_addr();
+    }
+
+    /* Set fields in reply. */
+    ((struct icmp6_echo_hdr *)(r->payload))->type = ICMP6_TYPE_EREP;
+    ((struct icmp6_echo_hdr *)(r->payload))->chksum = 0;
+#if CHECKSUM_GEN_ICMP6
+    IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_GEN_ICMP6) {
+      ((struct icmp6_echo_hdr *)(r->payload))->chksum = ip6_chksum_pseudo(r,
+          IP6_NEXTH_ICMP6, r->tot_len, reply_src, ip6_current_src_addr());
+    }
+#endif /* CHECKSUM_GEN_ICMP6 */
+
+    /* Send reply. */
+    ICMP6_STATS_INC(icmp6.xmit);
+    ip6_output_if(r, reply_src, ip6_current_src_addr(),
+        LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, inp);
+    pbuf_free(r);
+
+    break;
+  default:
+    ICMP6_STATS_INC(icmp6.proterr);
+    ICMP6_STATS_INC(icmp6.drop);
+    break;
+  }
+
+  pbuf_free(p);
+}
+
+
+/**
+ * Send an icmpv6 'destination unreachable' packet.
+ *
+ * This function must be used only in direct response to a packet that is being
+ * received right now. Otherwise, address zones would be lost.
+ *
+ * @param p the input packet for which the 'unreachable' should be sent,
+ *          p->payload pointing to the IPv6 header
+ * @param c ICMPv6 code for the unreachable type
+ */
+void
+icmp6_dest_unreach(struct pbuf *p, enum icmp6_dur_code c)
+{
+  icmp6_send_response(p, c, 0, ICMP6_TYPE_DUR);
+}
+
+/**
+ * Send an icmpv6 'packet too big' packet.
+ *
+ * This function must be used only in direct response to a packet that is being
+ * received right now. Otherwise, address zones would be lost.
+ *
+ * @param p the input packet for which the 'packet too big' should be sent,
+ *          p->payload pointing to the IPv6 header
+ * @param mtu the maximum mtu that we can accept
+ */
+void
+icmp6_packet_too_big(struct pbuf *p, u32_t mtu)
+{
+  icmp6_send_response(p, 0, mtu, ICMP6_TYPE_PTB);
+}
+
+/**
+ * Send an icmpv6 'time exceeded' packet.
+ *
+ * This function must be used only in direct response to a packet that is being
+ * received right now. Otherwise, address zones would be lost.
+ *
+ * @param p the input packet for which the 'time exceeded' should be sent,
+ *          p->payload pointing to the IPv6 header
+ * @param c ICMPv6 code for the time exceeded type
+ */
+void
+icmp6_time_exceeded(struct pbuf *p, enum icmp6_te_code c)
+{
+  icmp6_send_response(p, c, 0, ICMP6_TYPE_TE);
+}
+
+/**
+ * Send an icmpv6 'time exceeded' packet, with explicit source and destination
+ * addresses.
+ *
+ * This function may be used to send a response sometime after receiving the
+ * packet for which this response is meant. The provided source and destination
+ * addresses are used primarily to retain their zone information.
+ *
+ * @param p the input packet for which the 'time exceeded' should be sent,
+ *          p->payload pointing to the IPv6 header
+ * @param c ICMPv6 code for the time exceeded type
+ * @param src_addr source address of the original packet, with zone information
+ * @param dest_addr destination address of the original packet, with zone
+ *                  information
+ */
+void
+icmp6_time_exceeded_with_addrs(struct pbuf *p, enum icmp6_te_code c,
+    const ip6_addr_t *src_addr, const ip6_addr_t *dest_addr)
+{
+  icmp6_send_response_with_addrs(p, c, 0, ICMP6_TYPE_TE, src_addr, dest_addr);
+}
+
+/**
+ * Send an icmpv6 'parameter problem' packet.
+ *
+ * This function must be used only in direct response to a packet that is being
+ * received right now. Otherwise, address zones would be lost and the calculated
+ * offset would be wrong (calculated against ip6_current_header()).
+ *
+ * @param p the input packet for which the 'param problem' should be sent,
+ *          p->payload pointing to the IP header
+ * @param c ICMPv6 code for the param problem type
+ * @param pointer the pointer to the byte where the parameter is found
+ */
+void
+icmp6_param_problem(struct pbuf *p, enum icmp6_pp_code c, const void *pointer)
+{
+  u32_t pointer_u32 = (u32_t)((const u8_t *)pointer - (const u8_t *)ip6_current_header());
+  icmp6_send_response(p, c, pointer_u32, ICMP6_TYPE_PP);
+}
+
+/**
+ * Send an ICMPv6 packet in response to an incoming packet.
+ * The packet is sent *to* ip_current_src_addr() on ip_current_netif().
+ *
+ * @param p the input packet for which the response should be sent,
+ *          p->payload pointing to the IPv6 header
+ * @param code Code of the ICMPv6 header
+ * @param data Additional 32-bit parameter in the ICMPv6 header
+ * @param type Type of the ICMPv6 header
+ */
+static void
+icmp6_send_response(struct pbuf *p, u8_t code, u32_t data, u8_t type)
+{
+  const struct ip6_addr *reply_src, *reply_dest;
+  struct netif *netif = ip_current_netif();
+
+  LWIP_ASSERT("icmpv6 packet not a direct response", netif != NULL);
+  reply_dest = ip6_current_src_addr();
+
+  /* Select an address to use as source. */
+  reply_src = ip_2_ip6(ip6_select_source_address(netif, reply_dest));
+  if (reply_src == NULL) {
+    ICMP6_STATS_INC(icmp6.rterr);
+    return;
+  }
+  icmp6_send_response_with_addrs_and_netif(p, code, data, type, reply_src, reply_dest, netif);
+}
+
+/**
+ * Send an ICMPv6 packet in response to an incoming packet.
+ *
+ * Call this function if the packet is NOT sent as a direct response to an
+ * incoming packet, but rather sometime later (e.g. for a fragment reassembly
+ * timeout). The caller must provide the zoned source and destination addresses
+ * from the original packet with the src_addr and dest_addr parameters. The
+ * reason for this approach is that while the addresses themselves are part of
+ * the original packet, their zone information is not, thus possibly resulting
+ * in a link-local response being sent over the wrong link.
+ *
+ * @param p the input packet for which the response should be sent,
+ *          p->payload pointing to the IPv6 header
+ * @param code Code of the ICMPv6 header
+ * @param data Additional 32-bit parameter in the ICMPv6 header
+ * @param type Type of the ICMPv6 header
+ * @param src_addr original source address
+ * @param dest_addr original destination address
+ */
+static void
+icmp6_send_response_with_addrs(struct pbuf *p, u8_t code, u32_t data, u8_t type,
+    const ip6_addr_t *src_addr, const ip6_addr_t *dest_addr)
+{
+  const struct ip6_addr *reply_src, *reply_dest;
+  struct netif *netif;
+
+  /* Get the destination address and netif for this ICMP message. */
+  LWIP_ASSERT("must provide both source and destination", src_addr != NULL);
+  LWIP_ASSERT("must provide both source and destination", dest_addr != NULL);
+
+  /* Special case, as ip6_current_xxx is either NULL, or points
+     to a different packet than the one that expired. */
+  IP6_ADDR_ZONECHECK(src_addr);
+  IP6_ADDR_ZONECHECK(dest_addr);
+  /* Swap source and destination for the reply. */
+  reply_dest = src_addr;
+  reply_src = dest_addr;
+  netif = ip6_route(reply_src, reply_dest);
+  if (netif == NULL) {
+    ICMP6_STATS_INC(icmp6.rterr);
+    return;
+  }
+  icmp6_send_response_with_addrs_and_netif(p, code, data, type, reply_src,
+    reply_dest, netif);
+}
+
+/**
+ * Send an ICMPv6 packet (with srd/dst address and netif given).
+ *
+ * @param p the input packet for which the response should be sent,
+ *          p->payload pointing to the IPv6 header
+ * @param code Code of the ICMPv6 header
+ * @param data Additional 32-bit parameter in the ICMPv6 header
+ * @param type Type of the ICMPv6 header
+ * @param reply_src source address of the packet to send
+ * @param reply_dest destination address of the packet to send
+ * @param netif netif to send the packet
+ */
+static void
+icmp6_send_response_with_addrs_and_netif(struct pbuf *p, u8_t code, u32_t data, u8_t type,
+    const ip6_addr_t *reply_src, const ip6_addr_t *reply_dest, struct netif *netif)
+{
+  struct pbuf *q;
+  struct icmp6_hdr *icmp6hdr;
+  u16_t datalen = LWIP_MIN(p->tot_len, LWIP_ICMP6_DATASIZE);
+
+  /* ICMPv6 header + datalen (as much of the offending packet as possible) */
+  q = pbuf_alloc(PBUF_IP, sizeof(struct icmp6_hdr) + datalen,
+                 PBUF_RAM);
+  if (q == NULL) {
+    LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded: failed to allocate pbuf for ICMPv6 packet.\n"));
+    ICMP6_STATS_INC(icmp6.memerr);
+    return;
+  }
+  LWIP_ASSERT("check that first pbuf can hold icmp6 header",
+             (q->len >= (sizeof(struct icmp6_hdr))));
+
+  icmp6hdr = (struct icmp6_hdr *)q->payload;
+  icmp6hdr->type = type;
+  icmp6hdr->code = code;
+  icmp6hdr->data = lwip_htonl(data);
+
+  /* copy fields from original packet */
+  pbuf_copy_partial_pbuf(q, p, datalen, sizeof(struct icmp6_hdr));
+
+  /* calculate checksum */
+  icmp6hdr->chksum = 0;
+#if CHECKSUM_GEN_ICMP6
+  IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_ICMP6) {
+    icmp6hdr->chksum = ip6_chksum_pseudo(q, IP6_NEXTH_ICMP6, q->tot_len,
+      reply_src, reply_dest);
+  }
+#endif /* CHECKSUM_GEN_ICMP6 */
+
+  ICMP6_STATS_INC(icmp6.xmit);
+  ip6_output_if(q, reply_src, reply_dest, LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, netif);
+  pbuf_free(q);
+}
+
+#endif /* LWIP_ICMP6 && LWIP_IPV6 */
diff --git a/lib/lwip/core/ipv6/inet6.c b/lib/lwip/core/ipv6/inet6.c
new file mode 100644
index 0000000000..d9a992c22a
--- /dev/null
+++ b/lib/lwip/core/ipv6/inet6.c
@@ -0,0 +1,53 @@
+/**
+ * @file
+ *
+ * INET v6 addresses.
+ */
+
+/*
+ * Copyright (c) 2010 Inico Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Ivan Delamer <delamer@inicotech.com>
+ *
+ *
+ * Please coordinate changes and requests with Ivan Delamer
+ * <delamer@inicotech.com>
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV6 && LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/def.h"
+#include "lwip/inet.h"
+
+/** This variable is initialized by the system to contain the wildcard IPv6 address.
+ */
+const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT;
+
+#endif /* LWIP_IPV6 */
diff --git a/lib/lwip/core/ipv6/ip6.c b/lib/lwip/core/ipv6/ip6.c
new file mode 100644
index 0000000000..90e90dda45
--- /dev/null
+++ b/lib/lwip/core/ipv6/ip6.c
@@ -0,0 +1,1494 @@
+/**
+ * @file
+ *
+ * IPv6 layer.
+ */
+
+/*
+ * Copyright (c) 2010 Inico Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Ivan Delamer <delamer@inicotech.com>
+ *
+ *
+ * Please coordinate changes and requests with Ivan Delamer
+ * <delamer@inicotech.com>
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV6  /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/def.h"
+#include "lwip/mem.h"
+#include "lwip/netif.h"
+#include "lwip/ip.h"
+#include "lwip/ip6.h"
+#include "lwip/ip6_addr.h"
+#include "lwip/ip6_frag.h"
+#include "lwip/icmp6.h"
+#include "lwip/priv/raw_priv.h"
+#include "lwip/udp.h"
+#include "lwip/priv/tcp_priv.h"
+#include "lwip/dhcp6.h"
+#include "lwip/nd6.h"
+#include "lwip/mld6.h"
+#include "lwip/debug.h"
+#include "lwip/stats.h"
+
+#ifdef LWIP_HOOK_FILENAME
+#include LWIP_HOOK_FILENAME
+#endif
+
+/**
+ * Finds the appropriate network interface for a given IPv6 address. It tries to select
+ * a netif following a sequence of heuristics:
+ * 1) if there is only 1 netif, return it
+ * 2) if the destination is a zoned address, match its zone to a netif
+ * 3) if the either the source or destination address is a scoped address,
+ *    match the source address's zone (if set) or address (if not) to a netif
+ * 4) tries to match the destination subnet to a configured address
+ * 5) tries to find a router-announced route
+ * 6) tries to match the (unscoped) source address to the netif
+ * 7) returns the default netif, if configured
+ *
+ * Note that each of the two given addresses may or may not be properly zoned.
+ *
+ * @param src the source IPv6 address, if known
+ * @param dest the destination IPv6 address for which to find the route
+ * @return the netif on which to send to reach dest
+ */
+struct netif *
+ip6_route(const ip6_addr_t *src, const ip6_addr_t *dest)
+{
+#if LWIP_SINGLE_NETIF
+  LWIP_UNUSED_ARG(src);
+  LWIP_UNUSED_ARG(dest);
+#else /* LWIP_SINGLE_NETIF */
+  struct netif *netif;
+  s8_t i;
+
+  LWIP_ASSERT_CORE_LOCKED();
+
+  /* If single netif configuration, fast return. */
+  if ((netif_list != NULL) && (netif_list->next == NULL)) {
+    if (!netif_is_up(netif_list) || !netif_is_link_up(netif_list) ||
+        (ip6_addr_has_zone(dest) && !ip6_addr_test_zone(dest, netif_list))) {
+      return NULL;
+    }
+    return netif_list;
+  }
+
+#if LWIP_IPV6_SCOPES
+  /* Special processing for zoned destination addresses. This includes link-
+   * local unicast addresses and interface/link-local multicast addresses. Use
+   * the zone to find a matching netif. If the address is not zoned, then there
+   * is technically no "wrong" netif to choose, and we leave routing to other
+   * rules; in most cases this should be the scoped-source rule below. */
+  if (ip6_addr_has_zone(dest)) {
+    IP6_ADDR_ZONECHECK(dest);
+    /* Find a netif based on the zone. For custom mappings, one zone may map
+     * to multiple netifs, so find one that can actually send a packet. */
+    NETIF_FOREACH(netif) {
+      if (ip6_addr_test_zone(dest, netif) &&
+          netif_is_up(netif) && netif_is_link_up(netif)) {
+        return netif;
+      }
+    }
+    /* No matching netif found. Do no try to route to a different netif,
+     * as that would be a zone violation, resulting in any packets sent to
+     * that netif being dropped on output. */
+    return NULL;
+  }
+#endif /* LWIP_IPV6_SCOPES */
+
+  /* Special processing for scoped source and destination addresses. If we get
+   * here, the destination address does not have a zone, so either way we need
+   * to look at the source address, which may or may not have a zone. If it
+   * does, the zone is restrictive: there is (typically) only one matching
+   * netif for it, and we should avoid routing to any other netif as that would
+   * result in guaranteed zone violations. For scoped source addresses that do
+   * not have a zone, use (only) a netif that has that source address locally
+   * assigned. This case also applies to the loopback source address, which has
+   * an implied link-local scope. If only the destination address is scoped
+   * (but, again, not zoned), we still want to use only the source address to
+   * determine its zone because that's most likely what the user/application
+   * wants, regardless of whether the source address is scoped. Finally, some
+   * of this story also applies if scoping is disabled altogether. */
+#if LWIP_IPV6_SCOPES
+  if (ip6_addr_has_scope(dest, IP6_UNKNOWN) ||
+      ip6_addr_has_scope(src, IP6_UNICAST) ||
+#else /* LWIP_IPV6_SCOPES */
+  if (ip6_addr_islinklocal(dest) || ip6_addr_ismulticast_iflocal(dest) ||
+      ip6_addr_ismulticast_linklocal(dest) || ip6_addr_islinklocal(src) ||
+#endif /* LWIP_IPV6_SCOPES */
+      ip6_addr_isloopback(src)) {
+#if LWIP_IPV6_SCOPES
+    if (ip6_addr_has_zone(src)) {
+      /* Find a netif matching the source zone (relatively cheap). */
+      NETIF_FOREACH(netif) {
+        if (netif_is_up(netif) && netif_is_link_up(netif) &&
+            ip6_addr_test_zone(src, netif)) {
+          return netif;
+        }
+      }
+    } else
+#endif /* LWIP_IPV6_SCOPES */
+    {
+      /* Find a netif matching the source address (relatively expensive). */
+      NETIF_FOREACH(netif) {
+        if (!netif_is_up(netif) || !netif_is_link_up(netif)) {
+          continue;
+        }
+        for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+          if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) &&
+              ip6_addr_zoneless_eq(src, netif_ip6_addr(netif, i))) {
+            return netif;
+          }
+        }
+      }
+    }
+    /* Again, do not use any other netif in this case, as that could result in
+     * zone boundary violations. */
+    return NULL;
+  }
+
+  /* We come here only if neither source nor destination is scoped. */
+  IP6_ADDR_ZONECHECK(src);
+
+#ifdef LWIP_HOOK_IP6_ROUTE
+  netif = LWIP_HOOK_IP6_ROUTE(src, dest);
+  if (netif != NULL) {
+    return netif;
+  }
+#endif
+
+  /* See if the destination subnet matches a configured address. In accordance
+   * with RFC 5942, dynamically configured addresses do not have an implied
+   * local subnet, and thus should be considered /128 assignments. However, as
+   * such, the destination address may still match a local address, and so we
+   * still need to check for exact matches here. By (lwIP) policy, statically
+   * configured addresses do always have an implied local /64 subnet. */
+  NETIF_FOREACH(netif) {
+    if (!netif_is_up(netif) || !netif_is_link_up(netif)) {
+      continue;
+    }
+    for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+      if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) &&
+          ip6_addr_net_eq(dest, netif_ip6_addr(netif, i)) &&
+          (netif_ip6_addr_isstatic(netif, i) ||
+          ip6_addr_nethost_eq(dest, netif_ip6_addr(netif, i)))) {
+        return netif;
+      }
+    }
+  }
+
+  /* Get the netif for a suitable router-announced route. */
+  netif = nd6_find_route(dest);
+  if (netif != NULL) {
+    return netif;
+  }
+
+  /* Try with the netif that matches the source address. Given the earlier rule
+   * for scoped source addresses, this applies to unscoped addresses only. */
+  if (!ip6_addr_isany(src)) {
+    NETIF_FOREACH(netif) {
+      if (!netif_is_up(netif) || !netif_is_link_up(netif)) {
+        continue;
+      }
+      for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+        if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) &&
+            ip6_addr_eq(src, netif_ip6_addr(netif, i))) {
+          return netif;
+        }
+      }
+    }
+  }
+
+#if LWIP_NETIF_LOOPBACK && !LWIP_HAVE_LOOPIF
+  /* loopif is disabled, loopback traffic is passed through any netif */
+  if (ip6_addr_isloopback(dest)) {
+    /* don't check for link on loopback traffic */
+    if (netif_default != NULL && netif_is_up(netif_default)) {
+      return netif_default;
+    }
+    /* default netif is not up, just use any netif for loopback traffic */
+    NETIF_FOREACH(netif) {
+      if (netif_is_up(netif)) {
+        return netif;
+      }
+    }
+    return NULL;
+  }
+#endif /* LWIP_NETIF_LOOPBACK && !LWIP_HAVE_LOOPIF */
+#endif /* !LWIP_SINGLE_NETIF */
+
+  /* no matching netif found, use default netif, if up */
+  if ((netif_default == NULL) || !netif_is_up(netif_default) || !netif_is_link_up(netif_default)) {
+    return NULL;
+  }
+  return netif_default;
+}
+
+/**
+ * @ingroup ip6
+ * Select the best IPv6 source address for a given destination IPv6 address.
+ *
+ * This implementation follows RFC 6724 Sec. 5 to the following extent:
+ * - Rules 1, 2, 3: fully implemented
+ * - Rules 4, 5, 5.5: not applicable
+ * - Rule 6: not implemented
+ * - Rule 7: not applicable
+ * - Rule 8: limited to "prefer /64 subnet match over non-match"
+ *
+ * For Rule 2, we deliberately deviate from RFC 6724 Sec. 3.1 by considering
+ * ULAs to be of smaller scope than global addresses, to avoid that a preferred
+ * ULA is picked over a deprecated global address when given a global address
+ * as destination, as that would likely result in broken two-way communication.
+ *
+ * As long as temporary addresses are not supported (as used in Rule 7), a
+ * proper implementation of Rule 8 would obviate the need to implement Rule 6.
+ *
+ * @param netif the netif on which to send a packet
+ * @param dest the destination we are trying to reach (possibly not properly
+ *             zoned)
+ * @return the most suitable source address to use, or NULL if no suitable
+ *         source address is found
+ */
+const ip_addr_t *
+ip6_select_source_address(struct netif *netif, const ip6_addr_t *dest)
+{
+  const ip_addr_t *best_addr;
+  const ip6_addr_t *cand_addr;
+  s8_t dest_scope, cand_scope;
+  s8_t best_scope = IP6_MULTICAST_SCOPE_RESERVED;
+  u8_t i, cand_pref, cand_bits;
+  u8_t best_pref = 0;
+  u8_t best_bits = 0;
+
+  /* Start by determining the scope of the given destination address. These
+   * tests are hopefully (roughly) in order of likeliness to match. */
+  if (ip6_addr_isglobal(dest)) {
+    dest_scope = IP6_MULTICAST_SCOPE_GLOBAL;
+  } else if (ip6_addr_islinklocal(dest) || ip6_addr_isloopback(dest)) {
+    dest_scope = IP6_MULTICAST_SCOPE_LINK_LOCAL;
+  } else if (ip6_addr_isuniquelocal(dest)) {
+    dest_scope = IP6_MULTICAST_SCOPE_ORGANIZATION_LOCAL;
+  } else if (ip6_addr_ismulticast(dest)) {
+    dest_scope = ip6_addr_multicast_scope(dest);
+  } else if (ip6_addr_issitelocal(dest)) {
+    dest_scope = IP6_MULTICAST_SCOPE_SITE_LOCAL;
+  } else {
+    /* no match, consider scope global */
+    dest_scope = IP6_MULTICAST_SCOPE_GLOBAL;
+  }
+
+  best_addr = NULL;
+
+  for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+    /* Consider only valid (= preferred and deprecated) addresses. */
+    if (!ip6_addr_isvalid(netif_ip6_addr_state(netif, i))) {
+      continue;
+    }
+    /* Determine the scope of this candidate address. Same ordering idea. */
+    cand_addr = netif_ip6_addr(netif, i);
+    if (ip6_addr_isglobal(cand_addr)) {
+      cand_scope = IP6_MULTICAST_SCOPE_GLOBAL;
+    } else if (ip6_addr_islinklocal(cand_addr)) {
+      cand_scope = IP6_MULTICAST_SCOPE_LINK_LOCAL;
+    } else if (ip6_addr_isuniquelocal(cand_addr)) {
+      cand_scope = IP6_MULTICAST_SCOPE_ORGANIZATION_LOCAL;
+    } else if (ip6_addr_issitelocal(cand_addr)) {
+      cand_scope = IP6_MULTICAST_SCOPE_SITE_LOCAL;
+    } else {
+      /* no match, treat as low-priority global scope */
+      cand_scope = IP6_MULTICAST_SCOPE_RESERVEDF;
+    }
+    cand_pref = ip6_addr_ispreferred(netif_ip6_addr_state(netif, i));
+    /* @todo compute the actual common bits, for longest matching prefix. */
+    /* We cannot count on the destination address having a proper zone
+     * assignment, so do not compare zones in this case. */
+    cand_bits = ip6_addr_net_zoneless_eq(cand_addr, dest); /* just 1 or 0 for now */
+    if (cand_bits && ip6_addr_nethost_eq(cand_addr, dest)) {
+      return netif_ip_addr6(netif, i); /* Rule 1 */
+    }
+    if ((best_addr == NULL) || /* no alternative yet */
+        ((cand_scope < best_scope) && (cand_scope >= dest_scope)) ||
+        ((cand_scope > best_scope) && (best_scope < dest_scope)) || /* Rule 2 */
+        ((cand_scope == best_scope) && ((cand_pref > best_pref) || /* Rule 3 */
+        ((cand_pref == best_pref) && (cand_bits > best_bits))))) { /* Rule 8 */
+      /* We found a new "winning" candidate. */
+      best_addr = netif_ip_addr6(netif, i);
+      best_scope = cand_scope;
+      best_pref = cand_pref;
+      best_bits = cand_bits;
+    }
+  }
+
+  return best_addr; /* may be NULL */
+}
+
+#if LWIP_IPV6_FORWARD
+/**
+ * Forwards an IPv6 packet. It finds an appropriate route for the
+ * packet, decrements the HL value of the packet, and outputs
+ * the packet on the appropriate interface.
+ *
+ * @param p the packet to forward (p->payload points to IP header)
+ * @param iphdr the IPv6 header of the input packet
+ * @param inp the netif on which this packet was received
+ */
+static void
+ip6_forward(struct pbuf *p, struct ip6_hdr *iphdr, struct netif *inp)
+{
+  struct netif *netif;
+
+  /* do not forward link-local or loopback addresses */
+  if (ip6_addr_islinklocal(ip6_current_dest_addr()) ||
+      ip6_addr_isloopback(ip6_current_dest_addr())) {
+    LWIP_DEBUGF(IP6_DEBUG, ("ip6_forward: not forwarding link-local address.\n"));
+    IP6_STATS_INC(ip6.rterr);
+    IP6_STATS_INC(ip6.drop);
+    return;
+  }
+
+  /* Find network interface where to forward this IP packet to. */
+  netif = ip6_route(IP6_ADDR_ANY6, ip6_current_dest_addr());
+  if (netif == NULL) {
+    LWIP_DEBUGF(IP6_DEBUG, ("ip6_forward: no route for %"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F"\n",
+        IP6_ADDR_BLOCK1(ip6_current_dest_addr()),
+        IP6_ADDR_BLOCK2(ip6_current_dest_addr()),
+        IP6_ADDR_BLOCK3(ip6_current_dest_addr()),
+        IP6_ADDR_BLOCK4(ip6_current_dest_addr()),
+        IP6_ADDR_BLOCK5(ip6_current_dest_addr()),
+        IP6_ADDR_BLOCK6(ip6_current_dest_addr()),
+        IP6_ADDR_BLOCK7(ip6_current_dest_addr()),
+        IP6_ADDR_BLOCK8(ip6_current_dest_addr())));
+#if LWIP_ICMP6
+    /* Don't send ICMP messages in response to ICMP messages */
+    if (IP6H_NEXTH(iphdr) != IP6_NEXTH_ICMP6) {
+      icmp6_dest_unreach(p, ICMP6_DUR_NO_ROUTE);
+    }
+#endif /* LWIP_ICMP6 */
+    IP6_STATS_INC(ip6.rterr);
+    IP6_STATS_INC(ip6.drop);
+    return;
+  }
+#if LWIP_IPV6_SCOPES
+  /* Do not forward packets with a zoned (e.g., link-local) source address
+   * outside of their zone. We determined the zone a bit earlier, so we know
+   * that the address is properly zoned here, so we can safely use has_zone.
+   * Also skip packets with a loopback source address (link-local implied). */
+  if ((ip6_addr_has_zone(ip6_current_src_addr()) &&
+      !ip6_addr_test_zone(ip6_current_src_addr(), netif)) ||
+      ip6_addr_isloopback(ip6_current_src_addr())) {
+    LWIP_DEBUGF(IP6_DEBUG, ("ip6_forward: not forwarding packet beyond its source address zone.\n"));
+    IP6_STATS_INC(ip6.rterr);
+    IP6_STATS_INC(ip6.drop);
+    return;
+  }
+#endif /* LWIP_IPV6_SCOPES */
+  /* Do not forward packets onto the same network interface on which
+   * they arrived. */
+  if (netif == inp) {
+    LWIP_DEBUGF(IP6_DEBUG, ("ip6_forward: not bouncing packets back on incoming interface.\n"));
+    IP6_STATS_INC(ip6.rterr);
+    IP6_STATS_INC(ip6.drop);
+    return;
+  }
+
+  /* decrement HL */
+  IP6H_HOPLIM_SET(iphdr, IP6H_HOPLIM(iphdr) - 1);
+  /* send ICMP6 if HL == 0 */
+  if (IP6H_HOPLIM(iphdr) == 0) {
+#if LWIP_ICMP6
+    /* Don't send ICMP messages in response to ICMP messages */
+    if (IP6H_NEXTH(iphdr) != IP6_NEXTH_ICMP6) {
+      icmp6_time_exceeded(p, ICMP6_TE_HL);
+    }
+#endif /* LWIP_ICMP6 */
+    IP6_STATS_INC(ip6.drop);
+    return;
+  }
+
+  if (netif->mtu && (p->tot_len > netif->mtu)) {
+#if LWIP_ICMP6
+    /* Don't send ICMP messages in response to ICMP messages */
+    if (IP6H_NEXTH(iphdr) != IP6_NEXTH_ICMP6) {
+      icmp6_packet_too_big(p, netif->mtu);
+    }
+#endif /* LWIP_ICMP6 */
+    IP6_STATS_INC(ip6.drop);
+    return;
+  }
+
+  LWIP_DEBUGF(IP6_DEBUG, ("ip6_forward: forwarding packet to %"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F"\n",
+      IP6_ADDR_BLOCK1(ip6_current_dest_addr()),
+      IP6_ADDR_BLOCK2(ip6_current_dest_addr()),
+      IP6_ADDR_BLOCK3(ip6_current_dest_addr()),
+      IP6_ADDR_BLOCK4(ip6_current_dest_addr()),
+      IP6_ADDR_BLOCK5(ip6_current_dest_addr()),
+      IP6_ADDR_BLOCK6(ip6_current_dest_addr()),
+      IP6_ADDR_BLOCK7(ip6_current_dest_addr()),
+      IP6_ADDR_BLOCK8(ip6_current_dest_addr())));
+
+  /* transmit pbuf on chosen interface */
+  netif->output_ip6(netif, p, ip6_current_dest_addr());
+  IP6_STATS_INC(ip6.fw);
+  IP6_STATS_INC(ip6.xmit);
+  return;
+}
+#endif /* LWIP_IPV6_FORWARD */
+
+/** Return true if the current input packet should be accepted on this netif */
+static int
+ip6_input_accept(struct netif *netif)
+{
+  /* interface is up? */
+  if (netif_is_up(netif)) {
+    u8_t i;
+    /* unicast to this interface address? address configured? */
+    /* If custom scopes are used, the destination zone will be tested as
+      * part of the local-address comparison, but we need to test the source
+      * scope as well (e.g., is this interface on the same link?). */
+    for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+      if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) &&
+          ip6_addr_eq(ip6_current_dest_addr(), netif_ip6_addr(netif, i))
+#if IPV6_CUSTOM_SCOPES
+          && (!ip6_addr_has_zone(ip6_current_src_addr()) ||
+              ip6_addr_test_zone(ip6_current_src_addr(), netif))
+#endif /* IPV6_CUSTOM_SCOPES */
+      ) {
+        /* accept on this netif */
+        return 1;
+      }
+    }
+  }
+  return 0;
+}
+
+/**
+ * This function is called by the network interface device driver when
+ * an IPv6 packet is received. The function does the basic checks of the
+ * IP header such as packet size being at least larger than the header
+ * size etc. If the packet was not destined for us, the packet is
+ * forwarded (using ip6_forward).
+ *
+ * Finally, the packet is sent to the upper layer protocol input function.
+ *
+ * @param p the received IPv6 packet (p->payload points to IPv6 header)
+ * @param inp the netif on which this packet was received
+ * @return ERR_OK if the packet was processed (could return ERR_* if it wasn't
+ *         processed, but currently always returns ERR_OK)
+ */
+err_t
+ip6_input(struct pbuf *p, struct netif *inp)
+{
+  struct ip6_hdr *ip6hdr;
+  struct netif *netif;
+  const u8_t *nexth;
+  u16_t hlen, hlen_tot; /* the current header length */
+#if 0 /*IP_ACCEPT_LINK_LAYER_ADDRESSING*/
+  @todo
+  int check_ip_src=1;
+#endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING */
+#if LWIP_RAW
+  raw_input_state_t raw_status;
+#endif /* LWIP_RAW */
+
+  LWIP_ASSERT_CORE_LOCKED();
+
+  IP6_STATS_INC(ip6.recv);
+
+  /* identify the IP header */
+  ip6hdr = (struct ip6_hdr *)p->payload;
+  if (IP6H_V(ip6hdr) != 6) {
+    LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_WARNING, ("IPv6 packet dropped due to bad version number %"U32_F"\n",
+        IP6H_V(ip6hdr)));
+    pbuf_free(p);
+    IP6_STATS_INC(ip6.err);
+    IP6_STATS_INC(ip6.drop);
+    return ERR_OK;
+  }
+
+#ifdef LWIP_HOOK_IP6_INPUT
+  if (LWIP_HOOK_IP6_INPUT(p, inp)) {
+    /* the packet has been eaten */
+    return ERR_OK;
+  }
+#endif
+
+  /* header length exceeds first pbuf length, or ip length exceeds total pbuf length? */
+  if ((IP6_HLEN > p->len) || (IP6H_PLEN(ip6hdr) > (p->tot_len - IP6_HLEN))) {
+    if (IP6_HLEN > p->len) {
+      LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+        ("IPv6 header (len %"U16_F") does not fit in first pbuf (len %"U16_F"), IP packet dropped.\n",
+            (u16_t)IP6_HLEN, p->len));
+    }
+    if ((IP6H_PLEN(ip6hdr) + IP6_HLEN) > p->tot_len) {
+      LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+        ("IPv6 (plen %"U16_F") is longer than pbuf (len %"U16_F"), IP packet dropped.\n",
+            (u16_t)(IP6H_PLEN(ip6hdr) + IP6_HLEN), p->tot_len));
+    }
+    /* free (drop) packet pbufs */
+    pbuf_free(p);
+    IP6_STATS_INC(ip6.lenerr);
+    IP6_STATS_INC(ip6.drop);
+    return ERR_OK;
+  }
+
+  /* Trim pbuf. This should have been done at the netif layer,
+   * but we'll do it anyway just to be sure that its done. */
+  pbuf_realloc(p, (u16_t)(IP6_HLEN + IP6H_PLEN(ip6hdr)));
+
+  /* copy IP addresses to aligned ip6_addr_t */
+  ip_addr_copy_from_ip6_packed(ip_data.current_iphdr_dest, ip6hdr->dest);
+  ip_addr_copy_from_ip6_packed(ip_data.current_iphdr_src, ip6hdr->src);
+
+  /* Don't accept virtual IPv4 mapped IPv6 addresses.
+   * Don't accept multicast source addresses. */
+  if (ip6_addr_isipv4mappedipv6(ip_2_ip6(&ip_data.current_iphdr_dest)) ||
+     ip6_addr_isipv4mappedipv6(ip_2_ip6(&ip_data.current_iphdr_src)) ||
+     ip6_addr_ismulticast(ip_2_ip6(&ip_data.current_iphdr_src))) {
+    /* free (drop) packet pbufs */
+    pbuf_free(p);
+    IP6_STATS_INC(ip6.err);
+    IP6_STATS_INC(ip6.drop);
+    return ERR_OK;
+  }
+
+  /* Set the appropriate zone identifier on the addresses. */
+  ip6_addr_assign_zone(ip_2_ip6(&ip_data.current_iphdr_dest), IP6_UNKNOWN, inp);
+  ip6_addr_assign_zone(ip_2_ip6(&ip_data.current_iphdr_src), IP6_UNICAST, inp);
+
+  /* current header pointer. */
+  ip_data.current_ip6_header = ip6hdr;
+
+  /* In netif, used in case we need to send ICMPv6 packets back. */
+  ip_data.current_netif = inp;
+  ip_data.current_input_netif = inp;
+
+  /* match packet against an interface, i.e. is this packet for us? */
+  if (ip6_addr_ismulticast(ip6_current_dest_addr())) {
+    /* Always joined to multicast if-local and link-local all-nodes group. */
+    if (ip6_addr_isallnodes_iflocal(ip6_current_dest_addr()) ||
+        ip6_addr_isallnodes_linklocal(ip6_current_dest_addr())) {
+      netif = inp;
+    }
+#if LWIP_IPV6_MLD
+    else if (mld6_lookfor_group(inp, ip6_current_dest_addr())) {
+      netif = inp;
+    }
+#else /* LWIP_IPV6_MLD */
+    else if (ip6_addr_issolicitednode(ip6_current_dest_addr())) {
+      u8_t i;
+      /* Filter solicited node packets when MLD is not enabled
+       * (for Neighbor discovery). */
+      netif = NULL;
+      for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+        if (ip6_addr_isvalid(netif_ip6_addr_state(inp, i)) &&
+            ip6_addr_solicitednode_eq(ip6_current_dest_addr(), netif_ip6_addr(inp, i))) {
+          netif = inp;
+          LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: solicited node packet accepted on interface %c%c\n",
+              netif->name[0], netif->name[1]));
+          break;
+        }
+      }
+    }
+#endif /* LWIP_IPV6_MLD */
+    else {
+      netif = NULL;
+    }
+  } else {
+    /* start trying with inp. if that's not acceptable, start walking the
+       list of configured netifs. */
+    if (ip6_input_accept(inp)) {
+      netif = inp;
+    } else {
+      netif = NULL;
+#if !IPV6_CUSTOM_SCOPES
+      /* Shortcut: stop looking for other interfaces if either the source or
+        * the destination has a scope constrained to this interface. Custom
+        * scopes may break the 1:1 link/interface mapping, however. */
+      if (ip6_addr_islinklocal(ip6_current_dest_addr()) ||
+          ip6_addr_islinklocal(ip6_current_src_addr())) {
+        goto netif_found;
+      }
+#endif /* !IPV6_CUSTOM_SCOPES */
+#if !LWIP_NETIF_LOOPBACK || LWIP_HAVE_LOOPIF
+      /* The loopback address is to be considered link-local. Packets to it
+        * should be dropped on other interfaces, as per RFC 4291 Sec. 2.5.3.
+        * Its implied scope means packets *from* the loopback address should
+        * not be accepted on other interfaces, either. These requirements
+        * cannot be implemented in the case that loopback traffic is sent
+        * across a non-loopback interface, however. */
+      if (ip6_addr_isloopback(ip6_current_dest_addr()) ||
+          ip6_addr_isloopback(ip6_current_src_addr())) {
+        goto netif_found;
+      }
+#endif /* !LWIP_NETIF_LOOPBACK || LWIP_HAVE_LOOPIF */
+#if !LWIP_SINGLE_NETIF
+      NETIF_FOREACH(netif) {
+        if (netif == inp) {
+          /* we checked that before already */
+          continue;
+        }
+        if (ip6_input_accept(netif)) {
+          break;
+        }
+      }
+#endif /* !LWIP_SINGLE_NETIF */
+    }
+netif_found:
+    LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet accepted on interface %c%c\n",
+        netif ? netif->name[0] : 'X', netif? netif->name[1] : 'X'));
+  }
+
+  /* "::" packet source address? (used in duplicate address detection) */
+  if (ip6_addr_isany(ip6_current_src_addr()) &&
+      (!ip6_addr_issolicitednode(ip6_current_dest_addr()))) {
+    /* packet source is not valid */
+    /* free (drop) packet pbufs */
+    LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with src ANY_ADDRESS dropped\n"));
+    pbuf_free(p);
+    IP6_STATS_INC(ip6.drop);
+    goto ip6_input_cleanup;
+  }
+
+  /* packet not for us? */
+  if (netif == NULL) {
+    /* packet not for us, route or discard */
+    LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_TRACE, ("ip6_input: packet not for us.\n"));
+#if LWIP_IPV6_FORWARD
+    /* non-multicast packet? */
+    if (!ip6_addr_ismulticast(ip6_current_dest_addr())) {
+      /* try to forward IP packet on (other) interfaces */
+      ip6_forward(p, ip6hdr, inp);
+    }
+#endif /* LWIP_IPV6_FORWARD */
+    pbuf_free(p);
+    goto ip6_input_cleanup;
+  }
+
+  /* current netif pointer. */
+  ip_data.current_netif = netif;
+
+  /* Save next header type. */
+  nexth = &IP6H_NEXTH(ip6hdr);
+
+  /* Init header length. */
+  hlen = hlen_tot = IP6_HLEN;
+
+  LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: \n"));
+  ip6_debug_print(p);
+  LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: p->len %"U16_F" p->tot_len %"U16_F"\n", p->len, p->tot_len));
+
+  /* Move to payload. */
+  pbuf_remove_header(p, IP6_HLEN);
+
+  /* Process known option extension headers, if present. */
+  while (*nexth != IP6_NEXTH_NONE)
+  {
+    switch (*nexth) {
+    case IP6_NEXTH_HOPBYHOP:
+    {
+      s32_t opt_offset;
+      struct ip6_hbh_hdr *hbh_hdr;
+      struct ip6_opt_hdr *opt_hdr;
+      LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Hop-by-Hop options header\n"));
+
+      /* Get and check the header length, while staying in packet bounds. */
+      hbh_hdr = (struct ip6_hbh_hdr *)p->payload;
+
+      /* Get next header type. */
+      nexth = &IP6_HBH_NEXTH(hbh_hdr);
+
+      /* Get the header length. */
+      hlen = (u16_t)(8 * (1 + hbh_hdr->_hlen));
+
+      if ((p->len < 8) || (hlen > p->len)) {
+        LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+          ("IPv6 options header (hlen %"U16_F") does not fit in first pbuf (len %"U16_F"), IPv6 packet dropped.\n",
+              hlen, p->len));
+        /* free (drop) packet pbufs */
+        pbuf_free(p);
+        IP6_STATS_INC(ip6.lenerr);
+        IP6_STATS_INC(ip6.drop);
+        goto ip6_input_cleanup;
+      }
+
+      hlen_tot = (u16_t)(hlen_tot + hlen);
+
+      /* The extended option header starts right after Hop-by-Hop header. */
+      opt_offset = IP6_HBH_HLEN;
+      while (opt_offset < hlen)
+      {
+        s32_t opt_dlen = 0;
+
+        opt_hdr = (struct ip6_opt_hdr *)((u8_t *)hbh_hdr + opt_offset);
+
+        switch (IP6_OPT_TYPE(opt_hdr)) {
+        /* @todo: process IPV6 Hop-by-Hop option data */
+        case IP6_PAD1_OPTION:
+          /* PAD1 option doesn't have length and value field */
+          opt_dlen = -1;
+          break;
+        case IP6_PADN_OPTION:
+          opt_dlen = IP6_OPT_DLEN(opt_hdr);
+          break;
+        case IP6_ROUTER_ALERT_OPTION:
+          opt_dlen = IP6_OPT_DLEN(opt_hdr);
+          break;
+        case IP6_JUMBO_OPTION:
+          opt_dlen = IP6_OPT_DLEN(opt_hdr);
+          break;
+        default:
+          /* Check 2 MSB of Hop-by-Hop header type. */
+          switch (IP6_OPT_TYPE_ACTION(opt_hdr)) {
+          case 1:
+            /* Discard the packet. */
+            LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with invalid Hop-by-Hop option type dropped.\n"));
+            pbuf_free(p);
+            IP6_STATS_INC(ip6.drop);
+            goto ip6_input_cleanup;
+          case 2:
+            /* Send ICMP Parameter Problem */
+            icmp6_param_problem(p, ICMP6_PP_OPTION, opt_hdr);
+            LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with invalid Hop-by-Hop option type dropped.\n"));
+            pbuf_free(p);
+            IP6_STATS_INC(ip6.drop);
+            goto ip6_input_cleanup;
+          case 3:
+            /* Send ICMP Parameter Problem if destination address is not a multicast address */
+            if (!ip6_addr_ismulticast(ip6_current_dest_addr())) {
+              icmp6_param_problem(p, ICMP6_PP_OPTION, opt_hdr);
+            }
+            LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with invalid Hop-by-Hop option type dropped.\n"));
+            pbuf_free(p);
+            IP6_STATS_INC(ip6.drop);
+            goto ip6_input_cleanup;
+          default:
+            /* Skip over this option. */
+            opt_dlen = IP6_OPT_DLEN(opt_hdr);
+            break;
+          }
+          break;
+        }
+
+        /* Adjust the offset to move to the next extended option header */
+        opt_offset = opt_offset + IP6_OPT_HLEN + opt_dlen;
+      }
+      pbuf_remove_header(p, hlen);
+      break;
+    }
+    case IP6_NEXTH_DESTOPTS:
+    {
+      s32_t opt_offset;
+      struct ip6_dest_hdr *dest_hdr;
+      struct ip6_opt_hdr *opt_hdr;
+      LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Destination options header\n"));
+
+      dest_hdr = (struct ip6_dest_hdr *)p->payload;
+
+      /* Get next header type. */
+      nexth = &IP6_DEST_NEXTH(dest_hdr);
+
+      /* Get the header length. */
+      hlen = 8 * (1 + dest_hdr->_hlen);
+      if ((p->len < 8) || (hlen > p->len)) {
+        LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+          ("IPv6 options header (hlen %"U16_F") does not fit in first pbuf (len %"U16_F"), IPv6 packet dropped.\n",
+              hlen, p->len));
+        /* free (drop) packet pbufs */
+        pbuf_free(p);
+        IP6_STATS_INC(ip6.lenerr);
+        IP6_STATS_INC(ip6.drop);
+        goto ip6_input_cleanup;
+      }
+
+      hlen_tot = (u16_t)(hlen_tot + hlen);
+
+      /* The extended option header starts right after Destination header. */
+      opt_offset = IP6_DEST_HLEN;
+      while (opt_offset < hlen)
+      {
+        s32_t opt_dlen = 0;
+
+        opt_hdr = (struct ip6_opt_hdr *)((u8_t *)dest_hdr + opt_offset);
+
+        switch (IP6_OPT_TYPE(opt_hdr))
+        {
+        /* @todo: process IPV6 Destination option data */
+        case IP6_PAD1_OPTION:
+          /* PAD1 option deosn't have length and value field */
+          opt_dlen = -1;
+          break;
+        case IP6_PADN_OPTION:
+          opt_dlen = IP6_OPT_DLEN(opt_hdr);
+          break;
+        case IP6_ROUTER_ALERT_OPTION:
+          opt_dlen = IP6_OPT_DLEN(opt_hdr);
+          break;
+        case IP6_JUMBO_OPTION:
+          opt_dlen = IP6_OPT_DLEN(opt_hdr);
+          break;
+        case IP6_HOME_ADDRESS_OPTION:
+          opt_dlen = IP6_OPT_DLEN(opt_hdr);
+          break;
+        default:
+          /* Check 2 MSB of Destination header type. */
+          switch (IP6_OPT_TYPE_ACTION(opt_hdr))
+          {
+          case 1:
+            /* Discard the packet. */
+            LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with invalid destination option type dropped.\n"));
+            pbuf_free(p);
+            IP6_STATS_INC(ip6.drop);
+            goto ip6_input_cleanup;
+          case 2:
+            /* Send ICMP Parameter Problem */
+            icmp6_param_problem(p, ICMP6_PP_OPTION, opt_hdr);
+            LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with invalid destination option type dropped.\n"));
+            pbuf_free(p);
+            IP6_STATS_INC(ip6.drop);
+            goto ip6_input_cleanup;
+          case 3:
+            /* Send ICMP Parameter Problem if destination address is not a multicast address */
+            if (!ip6_addr_ismulticast(ip6_current_dest_addr())) {
+              icmp6_param_problem(p, ICMP6_PP_OPTION, opt_hdr);
+            }
+            LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with invalid destination option type dropped.\n"));
+            pbuf_free(p);
+            IP6_STATS_INC(ip6.drop);
+            goto ip6_input_cleanup;
+          default:
+            /* Skip over this option. */
+            opt_dlen = IP6_OPT_DLEN(opt_hdr);
+            break;
+          }
+          break;
+        }
+
+        /* Adjust the offset to move to the next extended option header */
+        opt_offset = opt_offset + IP6_OPT_HLEN + opt_dlen;
+      }
+
+      pbuf_remove_header(p, hlen);
+      break;
+    }
+    case IP6_NEXTH_ROUTING:
+    {
+      struct ip6_rout_hdr *rout_hdr;
+      LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Routing header\n"));
+
+      rout_hdr = (struct ip6_rout_hdr *)p->payload;
+
+      /* Get next header type. */
+      nexth = &IP6_ROUT_NEXTH(rout_hdr);
+
+      /* Get the header length. */
+      hlen = 8 * (1 + rout_hdr->_hlen);
+
+      if ((p->len < 8) || (hlen > p->len)) {
+        LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+          ("IPv6 options header (hlen %"U16_F") does not fit in first pbuf (len %"U16_F"), IPv6 packet dropped.\n",
+              hlen, p->len));
+        /* free (drop) packet pbufs */
+        pbuf_free(p);
+        IP6_STATS_INC(ip6.lenerr);
+        IP6_STATS_INC(ip6.drop);
+        goto ip6_input_cleanup;
+      }
+
+      /* Skip over this header. */
+      hlen_tot = (u16_t)(hlen_tot + hlen);
+
+      /* if segment left value is 0 in routing header, ignore the option */
+      if (IP6_ROUT_SEG_LEFT(rout_hdr)) {
+        /* The length field of routing option header must be even */
+        if (rout_hdr->_hlen & 0x1) {
+          /* Discard and send parameter field error */
+          icmp6_param_problem(p, ICMP6_PP_FIELD, &rout_hdr->_hlen);
+          LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with invalid routing type dropped\n"));
+          pbuf_free(p);
+          IP6_STATS_INC(ip6.drop);
+          goto ip6_input_cleanup;
+        }
+
+        switch (IP6_ROUT_TYPE(rout_hdr))
+        {
+        /* TODO: process routing by the type */
+        case IP6_ROUT_TYPE2:
+          break;
+        case IP6_ROUT_RPL:
+          break;
+        default:
+          /* Discard unrecognized routing type and send parameter field error */
+          icmp6_param_problem(p, ICMP6_PP_FIELD, &IP6_ROUT_TYPE(rout_hdr));
+          LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with invalid routing type dropped\n"));
+          pbuf_free(p);
+          IP6_STATS_INC(ip6.drop);
+          goto ip6_input_cleanup;
+        }
+      }
+
+      pbuf_remove_header(p, hlen);
+      break;
+    }
+    case IP6_NEXTH_FRAGMENT:
+    {
+      struct ip6_frag_hdr *frag_hdr;
+      LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Fragment header\n"));
+
+      frag_hdr = (struct ip6_frag_hdr *)p->payload;
+
+      /* Get next header type. */
+      nexth = &IP6_FRAG_NEXTH(frag_hdr);
+
+      /* Fragment Header length. */
+      hlen = 8;
+
+      /* Make sure this header fits in current pbuf. */
+      if (hlen > p->len) {
+        LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+          ("IPv6 options header (hlen %"U16_F") does not fit in first pbuf (len %"U16_F"), IPv6 packet dropped.\n",
+              hlen, p->len));
+        /* free (drop) packet pbufs */
+        pbuf_free(p);
+        IP6_FRAG_STATS_INC(ip6_frag.lenerr);
+        IP6_FRAG_STATS_INC(ip6_frag.drop);
+        goto ip6_input_cleanup;
+      }
+
+      hlen_tot = (u16_t)(hlen_tot + hlen);
+
+      /* check payload length is multiple of 8 octets when mbit is set */
+      if (IP6_FRAG_MBIT(frag_hdr) && (IP6H_PLEN(ip6hdr) & 0x7)) {
+        /* ipv6 payload length is not multiple of 8 octets */
+        icmp6_param_problem(p, ICMP6_PP_FIELD, LWIP_PACKED_CAST(const void *, &ip6hdr->_plen));
+        LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with invalid payload length dropped\n"));
+        pbuf_free(p);
+        IP6_STATS_INC(ip6.drop);
+        goto ip6_input_cleanup;
+      }
+
+      /* Offset == 0 and more_fragments == 0? */
+      if ((frag_hdr->_fragment_offset &
+           PP_HTONS(IP6_FRAG_OFFSET_MASK | IP6_FRAG_MORE_FLAG)) == 0) {
+        /* This is a 1-fragment packet. Skip this header and continue. */
+        pbuf_remove_header(p, hlen);
+      } else {
+#if LWIP_IPV6_REASS
+        /* reassemble the packet */
+        ip_data.current_ip_header_tot_len = hlen_tot;
+        p = ip6_reass(p);
+        /* packet not fully reassembled yet? */
+        if (p == NULL) {
+          goto ip6_input_cleanup;
+        }
+
+        /* Returned p point to IPv6 header.
+         * Update all our variables and pointers and continue. */
+        ip6hdr = (struct ip6_hdr *)p->payload;
+        nexth = &IP6H_NEXTH(ip6hdr);
+        hlen = hlen_tot = IP6_HLEN;
+        pbuf_remove_header(p, IP6_HLEN);
+
+#else /* LWIP_IPV6_REASS */
+        /* free (drop) packet pbufs */
+        LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Fragment header dropped (with LWIP_IPV6_REASS==0)\n"));
+        pbuf_free(p);
+        IP6_STATS_INC(ip6.opterr);
+        IP6_STATS_INC(ip6.drop);
+        goto ip6_input_cleanup;
+#endif /* LWIP_IPV6_REASS */
+      }
+      break;
+    }
+    default:
+      goto options_done;
+    }
+
+    if (*nexth == IP6_NEXTH_HOPBYHOP) {
+      /* Hop-by-Hop header comes only as a first option */
+      icmp6_param_problem(p, ICMP6_PP_HEADER, nexth);
+      LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Hop-by-Hop options header dropped (only valid as a first option)\n"));
+      pbuf_free(p);
+      IP6_STATS_INC(ip6.drop);
+      goto ip6_input_cleanup;
+    }
+  }
+
+options_done:
+
+  /* send to upper layers */
+  ip_data.current_ip_header_tot_len = hlen_tot;
+
+#if LWIP_RAW
+  /* p points to IPv6 header again for raw_input. */
+  pbuf_add_header_force(p, hlen_tot);
+  /* raw input did not eat the packet? */
+  raw_status = raw_input(p, inp);
+  if (raw_status != RAW_INPUT_EATEN)
+  {
+    /* Point to payload. */
+    pbuf_remove_header(p, hlen_tot);
+#else /* LWIP_RAW */
+  {
+#endif /* LWIP_RAW */
+    switch (*nexth) {
+    case IP6_NEXTH_NONE:
+      pbuf_free(p);
+      break;
+#if LWIP_UDP
+    case IP6_NEXTH_UDP:
+#if LWIP_UDPLITE
+    case IP6_NEXTH_UDPLITE:
+#endif /* LWIP_UDPLITE */
+      udp_input(p, inp);
+      break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+    case IP6_NEXTH_TCP:
+      tcp_input(p, inp);
+      break;
+#endif /* LWIP_TCP */
+#if LWIP_ICMP6
+    case IP6_NEXTH_ICMP6:
+      icmp6_input(p, inp);
+      break;
+#endif /* LWIP_ICMP */
+    default:
+#if LWIP_RAW
+        if (raw_status == RAW_INPUT_DELIVERED) {
+          /* @todo: ipv6 mib in-delivers? */
+        } else
+#endif /* LWIP_RAW */
+        {
+#if LWIP_ICMP6
+        /* p points to IPv6 header again for raw_input. */
+        pbuf_add_header_force(p, hlen_tot);
+        /* send ICMP parameter problem unless it was a multicast or ICMPv6 */
+        if ((!ip6_addr_ismulticast(ip6_current_dest_addr())) &&
+            (IP6H_NEXTH(ip6hdr) != IP6_NEXTH_ICMP6)) {
+          icmp6_param_problem(p, ICMP6_PP_HEADER, nexth);
+        }
+#endif /* LWIP_ICMP */
+        LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip6_input: Unsupported transport protocol %"U16_F"\n", (u16_t)IP6H_NEXTH(ip6hdr)));
+        IP6_STATS_INC(ip6.proterr);
+        IP6_STATS_INC(ip6.drop);
+      }
+      pbuf_free(p);
+      break;
+    }
+  }
+
+ip6_input_cleanup:
+  ip_data.current_netif = NULL;
+  ip_data.current_input_netif = NULL;
+  ip_data.current_ip6_header = NULL;
+  ip_data.current_ip_header_tot_len = 0;
+  ip6_addr_set_zero(ip6_current_src_addr());
+  ip6_addr_set_zero(ip6_current_dest_addr());
+
+  return ERR_OK;
+}
+
+
+/**
+ * Sends an IPv6 packet on a network interface. This function constructs
+ * the IPv6 header. If the source IPv6 address is NULL, the IPv6 "ANY" address is
+ * used as source (usually during network startup). If the source IPv6 address it
+ * IP6_ADDR_ANY, the most appropriate IPv6 address of the outgoing network
+ * interface is filled in as source address. If the destination IPv6 address is
+ * LWIP_IP_HDRINCL, p is assumed to already include an IPv6 header and
+ * p->payload points to it instead of the data.
+ *
+ * @param p the packet to send (p->payload points to the data, e.g. next
+            protocol header; if dest == LWIP_IP_HDRINCL, p already includes an
+            IPv6 header and p->payload points to that IPv6 header)
+ * @param src the source IPv6 address to send from (if src == IP6_ADDR_ANY, an
+ *         IP address of the netif is selected and used as source address.
+ *         if src == NULL, IP6_ADDR_ANY is used as source) (src is possibly not
+ *         properly zoned)
+ * @param dest the destination IPv6 address to send the packet to (possibly not
+ *             properly zoned)
+ * @param hl the Hop Limit value to be set in the IPv6 header
+ * @param tc the Traffic Class value to be set in the IPv6 header
+ * @param nexth the Next Header to be set in the IPv6 header
+ * @param netif the netif on which to send this packet
+ * @return ERR_OK if the packet was sent OK
+ *         ERR_BUF if p doesn't have enough space for IPv6/LINK headers
+ *         returns errors returned by netif->output_ip6
+ */
+err_t
+ip6_output_if(struct pbuf *p, const ip6_addr_t *src, const ip6_addr_t *dest,
+             u8_t hl, u8_t tc,
+             u8_t nexth, struct netif *netif)
+{
+  const ip6_addr_t *src_used = src;
+  if (dest != LWIP_IP_HDRINCL) {
+    if (src != NULL && ip6_addr_isany(src)) {
+      src_used = ip_2_ip6(ip6_select_source_address(netif, dest));
+      if ((src_used == NULL) || ip6_addr_isany(src_used)) {
+        /* No appropriate source address was found for this packet. */
+        LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip6_output: No suitable source address for packet.\n"));
+        IP6_STATS_INC(ip6.rterr);
+        return ERR_RTE;
+      }
+    }
+  }
+  return ip6_output_if_src(p, src_used, dest, hl, tc, nexth, netif);
+}
+
+/**
+ * Same as ip6_output_if() but 'src' address is not replaced by netif address
+ * when it is 'any'.
+ */
+err_t
+ip6_output_if_src(struct pbuf *p, const ip6_addr_t *src, const ip6_addr_t *dest,
+             u8_t hl, u8_t tc,
+             u8_t nexth, struct netif *netif)
+{
+  struct ip6_hdr *ip6hdr;
+  ip6_addr_t dest_addr;
+
+  LWIP_ASSERT_CORE_LOCKED();
+  LWIP_IP_CHECK_PBUF_REF_COUNT_FOR_TX(p);
+
+  /* Should the IPv6 header be generated or is it already included in p? */
+  if (dest != LWIP_IP_HDRINCL) {
+#if LWIP_IPV6_SCOPES
+    /* If the destination address is scoped but lacks a zone, add a zone now,
+     * based on the outgoing interface. The lower layers (e.g., nd6) absolutely
+     * require addresses to be properly zoned for correctness. In some cases,
+     * earlier attempts will have been made to add a zone to the destination,
+     * but this function is the only one that is called in all (other) cases,
+     * so we must do this here. */
+    if (ip6_addr_lacks_zone(dest, IP6_UNKNOWN)) {
+      ip6_addr_copy(dest_addr, *dest);
+      ip6_addr_assign_zone(&dest_addr, IP6_UNKNOWN, netif);
+      dest = &dest_addr;
+    }
+#endif /* LWIP_IPV6_SCOPES */
+
+    /* generate IPv6 header */
+    if (pbuf_add_header(p, IP6_HLEN)) {
+      LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip6_output: not enough room for IPv6 header in pbuf\n"));
+      IP6_STATS_INC(ip6.err);
+      return ERR_BUF;
+    }
+
+    ip6hdr = (struct ip6_hdr *)p->payload;
+    LWIP_ASSERT("check that first pbuf can hold struct ip6_hdr",
+               (p->len >= sizeof(struct ip6_hdr)));
+
+    IP6H_HOPLIM_SET(ip6hdr, hl);
+    IP6H_NEXTH_SET(ip6hdr, nexth);
+
+    /* dest cannot be NULL here */
+    ip6_addr_copy_to_packed(ip6hdr->dest, *dest);
+
+    IP6H_VTCFL_SET(ip6hdr, 6, tc, 0);
+    IP6H_PLEN_SET(ip6hdr, (u16_t)(p->tot_len - IP6_HLEN));
+
+    if (src == NULL) {
+      src = IP6_ADDR_ANY6;
+    }
+    /* src cannot be NULL here */
+    ip6_addr_copy_to_packed(ip6hdr->src, *src);
+
+  } else {
+    /* IP header already included in p */
+    ip6hdr = (struct ip6_hdr *)p->payload;
+    ip6_addr_copy_from_packed(dest_addr, ip6hdr->dest);
+    ip6_addr_assign_zone(&dest_addr, IP6_UNKNOWN, netif);
+    dest = &dest_addr;
+  }
+
+  IP6_STATS_INC(ip6.xmit);
+
+  LWIP_DEBUGF(IP6_DEBUG, ("ip6_output_if: %c%c%"U16_F"\n", netif->name[0], netif->name[1], (u16_t)netif->num));
+  ip6_debug_print(p);
+
+#if ENABLE_LOOPBACK
+  {
+    int i;
+#if !LWIP_HAVE_LOOPIF
+    if (ip6_addr_isloopback(dest)) {
+      return netif_loop_output(netif, p);
+    }
+#endif /* !LWIP_HAVE_LOOPIF */
+    for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+      if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) &&
+          ip6_addr_eq(dest, netif_ip6_addr(netif, i))) {
+        /* Packet to self, enqueue it for loopback */
+        LWIP_DEBUGF(IP6_DEBUG, ("netif_loop_output()\n"));
+        return netif_loop_output(netif, p);
+      }
+    }
+  }
+#if LWIP_MULTICAST_TX_OPTIONS
+  if ((p->flags & PBUF_FLAG_MCASTLOOP) != 0) {
+    netif_loop_output(netif, p);
+  }
+#endif /* LWIP_MULTICAST_TX_OPTIONS */
+#endif /* ENABLE_LOOPBACK */
+#if LWIP_IPV6_FRAG
+  /* don't fragment if interface has mtu set to 0 [loopif] */
+  if (netif_mtu6(netif) && (p->tot_len > nd6_get_destination_mtu(dest, netif))) {
+    return ip6_frag(p, netif, dest);
+  }
+#endif /* LWIP_IPV6_FRAG */
+
+  LWIP_DEBUGF(IP6_DEBUG, ("netif->output_ip6()\n"));
+  return netif->output_ip6(netif, p, dest);
+}
+
+/**
+ * Simple interface to ip6_output_if. It finds the outgoing network
+ * interface and calls upon ip6_output_if to do the actual work.
+ *
+ * @param p the packet to send (p->payload points to the data, e.g. next
+            protocol header; if dest == LWIP_IP_HDRINCL, p already includes an
+            IPv6 header and p->payload points to that IPv6 header)
+ * @param src the source IPv6 address to send from (if src == IP6_ADDR_ANY, an
+ *         IP address of the netif is selected and used as source address.
+ *         if src == NULL, IP6_ADDR_ANY is used as source)
+ * @param dest the destination IPv6 address to send the packet to
+ * @param hl the Hop Limit value to be set in the IPv6 header
+ * @param tc the Traffic Class value to be set in the IPv6 header
+ * @param nexth the Next Header to be set in the IPv6 header
+ *
+ * @return ERR_RTE if no route is found
+ *         see ip_output_if() for more return values
+ */
+err_t
+ip6_output(struct pbuf *p, const ip6_addr_t *src, const ip6_addr_t *dest,
+          u8_t hl, u8_t tc, u8_t nexth)
+{
+  struct netif *netif;
+  struct ip6_hdr *ip6hdr;
+  ip6_addr_t src_addr, dest_addr;
+
+  LWIP_IP_CHECK_PBUF_REF_COUNT_FOR_TX(p);
+
+  if (dest != LWIP_IP_HDRINCL) {
+    netif = ip6_route(src, dest);
+  } else {
+    /* IP header included in p, read addresses. */
+    ip6hdr = (struct ip6_hdr *)p->payload;
+    ip6_addr_copy_from_packed(src_addr, ip6hdr->src);
+    ip6_addr_copy_from_packed(dest_addr, ip6hdr->dest);
+    netif = ip6_route(&src_addr, &dest_addr);
+    dest = &dest_addr;
+  }
+
+  if (netif == NULL) {
+    LWIP_DEBUGF(IP6_DEBUG, ("ip6_output: no route for %"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F"\n",
+        IP6_ADDR_BLOCK1(dest),
+        IP6_ADDR_BLOCK2(dest),
+        IP6_ADDR_BLOCK3(dest),
+        IP6_ADDR_BLOCK4(dest),
+        IP6_ADDR_BLOCK5(dest),
+        IP6_ADDR_BLOCK6(dest),
+        IP6_ADDR_BLOCK7(dest),
+        IP6_ADDR_BLOCK8(dest)));
+    IP6_STATS_INC(ip6.rterr);
+    return ERR_RTE;
+  }
+
+  return ip6_output_if(p, src, dest, hl, tc, nexth, netif);
+}
+
+
+#if LWIP_NETIF_USE_HINTS
+/** Like ip6_output, but takes and addr_hint pointer that is passed on to netif->addr_hint
+ *  before calling ip6_output_if.
+ *
+ * @param p the packet to send (p->payload points to the data, e.g. next
+            protocol header; if dest == LWIP_IP_HDRINCL, p already includes an
+            IPv6 header and p->payload points to that IPv6 header)
+ * @param src the source IPv6 address to send from (if src == IP6_ADDR_ANY, an
+ *         IP address of the netif is selected and used as source address.
+ *         if src == NULL, IP6_ADDR_ANY is used as source)
+ * @param dest the destination IPv6 address to send the packet to
+ * @param hl the Hop Limit value to be set in the IPv6 header
+ * @param tc the Traffic Class value to be set in the IPv6 header
+ * @param nexth the Next Header to be set in the IPv6 header
+ * @param netif_hint netif output hint pointer set to netif->hint before
+ *        calling ip_output_if()
+ *
+ * @return ERR_RTE if no route is found
+ *         see ip_output_if() for more return values
+ */
+err_t
+ip6_output_hinted(struct pbuf *p, const ip6_addr_t *src, const ip6_addr_t *dest,
+          u8_t hl, u8_t tc, u8_t nexth, struct netif_hint *netif_hint)
+{
+  struct netif *netif;
+  struct ip6_hdr *ip6hdr;
+  ip6_addr_t src_addr, dest_addr;
+  err_t err;
+
+  LWIP_IP_CHECK_PBUF_REF_COUNT_FOR_TX(p);
+
+  if (dest != LWIP_IP_HDRINCL) {
+    netif = ip6_route(src, dest);
+  } else {
+    /* IP header included in p, read addresses. */
+    ip6hdr = (struct ip6_hdr *)p->payload;
+    ip6_addr_copy_from_packed(src_addr, ip6hdr->src);
+    ip6_addr_copy_from_packed(dest_addr, ip6hdr->dest);
+    netif = ip6_route(&src_addr, &dest_addr);
+    dest = &dest_addr;
+  }
+
+  if (netif == NULL) {
+    LWIP_DEBUGF(IP6_DEBUG, ("ip6_output: no route for %"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F"\n",
+        IP6_ADDR_BLOCK1(dest),
+        IP6_ADDR_BLOCK2(dest),
+        IP6_ADDR_BLOCK3(dest),
+        IP6_ADDR_BLOCK4(dest),
+        IP6_ADDR_BLOCK5(dest),
+        IP6_ADDR_BLOCK6(dest),
+        IP6_ADDR_BLOCK7(dest),
+        IP6_ADDR_BLOCK8(dest)));
+    IP6_STATS_INC(ip6.rterr);
+    return ERR_RTE;
+  }
+
+  NETIF_SET_HINTS(netif, netif_hint);
+  err = ip6_output_if(p, src, dest, hl, tc, nexth, netif);
+  NETIF_RESET_HINTS(netif);
+
+  return err;
+}
+#endif /* LWIP_NETIF_USE_HINTS*/
+
+#if LWIP_IPV6_MLD
+/**
+ * Add a hop-by-hop options header with a router alert option and padding.
+ *
+ * Used by MLD when sending a Multicast listener report/done message.
+ *
+ * @param p the packet to which we will prepend the options header
+ * @param nexth the next header protocol number (e.g. IP6_NEXTH_ICMP6)
+ * @param value the value of the router alert option data (e.g. IP6_ROUTER_ALERT_VALUE_MLD)
+ * @return ERR_OK if hop-by-hop header was added, ERR_* otherwise
+ */
+err_t
+ip6_options_add_hbh_ra(struct pbuf *p, u8_t nexth, u8_t value)
+{
+  u8_t *opt_data;
+  u32_t offset = 0;
+  struct ip6_hbh_hdr *hbh_hdr;
+  struct ip6_opt_hdr *opt_hdr;
+
+  /* fixed 4 bytes for router alert option and 2 bytes padding */
+  const u8_t hlen = (sizeof(struct ip6_opt_hdr) * 2) + IP6_ROUTER_ALERT_DLEN;
+  /* Move pointer to make room for hop-by-hop options header. */
+  if (pbuf_add_header(p, sizeof(struct ip6_hbh_hdr) + hlen)) {
+    LWIP_DEBUGF(IP6_DEBUG, ("ip6_options: no space for options header\n"));
+    IP6_STATS_INC(ip6.err);
+    return ERR_BUF;
+  }
+
+  /* Set fields of Hop-by-Hop header */
+  hbh_hdr = (struct ip6_hbh_hdr *)p->payload;
+  IP6_HBH_NEXTH(hbh_hdr) = nexth;
+  hbh_hdr->_hlen = 0;
+  offset = IP6_HBH_HLEN;
+
+  /* Set router alert options to Hop-by-Hop extended option header */
+  opt_hdr = (struct ip6_opt_hdr *)((u8_t *)hbh_hdr + offset);
+  IP6_OPT_TYPE(opt_hdr) = IP6_ROUTER_ALERT_OPTION;
+  IP6_OPT_DLEN(opt_hdr) = IP6_ROUTER_ALERT_DLEN;
+  offset += IP6_OPT_HLEN;
+
+  /* Set router alert option data */
+  opt_data = (u8_t *)hbh_hdr + offset;
+  opt_data[0] = value;
+  opt_data[1] = 0;
+  offset += IP6_OPT_DLEN(opt_hdr);
+
+  /* add 2 bytes padding to make 8 bytes Hop-by-Hop header length */
+  opt_hdr = (struct ip6_opt_hdr *)((u8_t *)hbh_hdr + offset);
+  IP6_OPT_TYPE(opt_hdr) = IP6_PADN_OPTION;
+  IP6_OPT_DLEN(opt_hdr) = 0;
+
+  return ERR_OK;
+}
+#endif /* LWIP_IPV6_MLD */
+
+#if IP6_DEBUG
+/* Print an IPv6 header by using LWIP_DEBUGF
+ * @param p an IPv6 packet, p->payload pointing to the IPv6 header
+ */
+void
+ip6_debug_print(struct pbuf *p)
+{
+  struct ip6_hdr *ip6hdr = (struct ip6_hdr *)p->payload;
+
+  LWIP_DEBUGF(IP6_DEBUG, ("IPv6 header:\n"));
+  LWIP_DEBUGF(IP6_DEBUG, ("+-------------------------------+\n"));
+  LWIP_DEBUGF(IP6_DEBUG, ("| %2"U16_F" |  %3"U16_F"  |      %7"U32_F"     | (ver, class, flow)\n",
+                    IP6H_V(ip6hdr),
+                    IP6H_TC(ip6hdr),
+                    IP6H_FL(ip6hdr)));
+  LWIP_DEBUGF(IP6_DEBUG, ("+-------------------------------+\n"));
+  LWIP_DEBUGF(IP6_DEBUG, ("|     %5"U16_F"     |  %3"U16_F"  |  %3"U16_F"  | (plen, nexth, hopl)\n",
+                    IP6H_PLEN(ip6hdr),
+                    IP6H_NEXTH(ip6hdr),
+                    IP6H_HOPLIM(ip6hdr)));
+  LWIP_DEBUGF(IP6_DEBUG, ("+-------------------------------+\n"));
+  LWIP_DEBUGF(IP6_DEBUG, ("|  %4"X32_F" |  %4"X32_F" |  %4"X32_F" |  %4"X32_F" | (src)\n",
+                    IP6_ADDR_BLOCK1(&(ip6hdr->src)),
+                    IP6_ADDR_BLOCK2(&(ip6hdr->src)),
+                    IP6_ADDR_BLOCK3(&(ip6hdr->src)),
+                    IP6_ADDR_BLOCK4(&(ip6hdr->src))));
+  LWIP_DEBUGF(IP6_DEBUG, ("|  %4"X32_F" |  %4"X32_F" |  %4"X32_F" |  %4"X32_F" |\n",
+                    IP6_ADDR_BLOCK5(&(ip6hdr->src)),
+                    IP6_ADDR_BLOCK6(&(ip6hdr->src)),
+                    IP6_ADDR_BLOCK7(&(ip6hdr->src)),
+                    IP6_ADDR_BLOCK8(&(ip6hdr->src))));
+  LWIP_DEBUGF(IP6_DEBUG, ("+-------------------------------+\n"));
+  LWIP_DEBUGF(IP6_DEBUG, ("|  %4"X32_F" |  %4"X32_F" |  %4"X32_F" |  %4"X32_F" | (dest)\n",
+                    IP6_ADDR_BLOCK1(&(ip6hdr->dest)),
+                    IP6_ADDR_BLOCK2(&(ip6hdr->dest)),
+                    IP6_ADDR_BLOCK3(&(ip6hdr->dest)),
+                    IP6_ADDR_BLOCK4(&(ip6hdr->dest))));
+  LWIP_DEBUGF(IP6_DEBUG, ("|  %4"X32_F" |  %4"X32_F" |  %4"X32_F" |  %4"X32_F" |\n",
+                    IP6_ADDR_BLOCK5(&(ip6hdr->dest)),
+                    IP6_ADDR_BLOCK6(&(ip6hdr->dest)),
+                    IP6_ADDR_BLOCK7(&(ip6hdr->dest)),
+                    IP6_ADDR_BLOCK8(&(ip6hdr->dest))));
+  LWIP_DEBUGF(IP6_DEBUG, ("+-------------------------------+\n"));
+}
+#endif /* IP6_DEBUG */
+
+#endif /* LWIP_IPV6 */
diff --git a/lib/lwip/core/ipv6/ip6_addr.c b/lib/lwip/core/ipv6/ip6_addr.c
new file mode 100644
index 0000000000..6e0ac86b05
--- /dev/null
+++ b/lib/lwip/core/ipv6/ip6_addr.c
@@ -0,0 +1,355 @@
+/**
+ * @file
+ *
+ * IPv6 addresses.
+ */
+
+/*
+ * Copyright (c) 2010 Inico Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Ivan Delamer <delamer@inicotech.com>
+ *
+ * Functions for handling IPv6 addresses.
+ *
+ * Please coordinate changes and requests with Ivan Delamer
+ * <delamer@inicotech.com>
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV6  /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/ip_addr.h"
+#include "lwip/def.h"
+#include "lwip/netif.h"
+
+#include <string.h>
+
+#if LWIP_IPV4
+#include "lwip/ip4_addr.h" /* for ip6addr_aton to handle IPv4-mapped addresses */
+#endif /* LWIP_IPV4 */
+
+/* used by IP6_ADDR_ANY(6) in ip6_addr.h */
+const ip_addr_t ip6_addr_any = IPADDR6_INIT(0ul, 0ul, 0ul, 0ul);
+
+#define lwip_xchar(i)        ((char)((i) < 10 ? '0' + (i) : 'A' + (i) - 10))
+
+/**
+ * Check whether "cp" is a valid ascii representation
+ * of an IPv6 address and convert to a binary address.
+ * Returns 1 if the address is valid, 0 if not.
+ *
+ * @param cp IPv6 address in ascii representation (e.g. "FF01::1")
+ * @param addr pointer to which to save the ip address in network order
+ * @return 1 if cp could be converted to addr, 0 on failure
+ */
+int
+ip6addr_aton(const char *cp, ip6_addr_t *addr)
+{
+  u32_t addr_index, zero_blocks, current_block_index, current_block_value;
+  const char *s;
+#if LWIP_IPV4
+  int check_ipv4_mapped = 0;
+#endif /* LWIP_IPV4 */
+
+  /* Count the number of colons, to count the number of blocks in a "::" sequence
+     zero_blocks may be 1 even if there are no :: sequences */
+  zero_blocks = 8;
+  for (s = cp; *s != 0; s++) {
+    if (*s == ':') {
+      zero_blocks--;
+#if LWIP_IPV4
+    } else if (*s == '.') {
+      if ((zero_blocks == 5) ||(zero_blocks == 2)) {
+        check_ipv4_mapped = 1;
+        /* last block could be the start of an IPv4 address */
+        zero_blocks--;
+      } else {
+        /* invalid format */
+        return 0;
+      }
+      break;
+#endif /* LWIP_IPV4 */
+    } else if (!lwip_isxdigit(*s)) {
+      break;
+    }
+  }
+
+  /* parse each block */
+  addr_index = 0;
+  current_block_index = 0;
+  current_block_value = 0;
+  for (s = cp; *s != 0; s++) {
+    if (*s == ':') {
+      if (addr) {
+        if (current_block_index & 0x1) {
+          addr->addr[addr_index++] |= current_block_value;
+        }
+        else {
+          addr->addr[addr_index] = current_block_value << 16;
+        }
+      }
+      current_block_index++;
+#if LWIP_IPV4
+      if (check_ipv4_mapped) {
+        if (current_block_index == 6) {
+          ip4_addr_t ip4;
+          int ret = ip4addr_aton(s + 1, &ip4);
+          if (ret) {
+            if (addr) {
+              addr->addr[3] = lwip_htonl(ip4.addr);
+              current_block_index++;
+              goto fix_byte_order_and_return;
+            }
+            return 1;
+          }
+        }
+      }
+#endif /* LWIP_IPV4 */
+      current_block_value = 0;
+      if (current_block_index > 7) {
+        /* address too long! */
+        return 0;
+      }
+      if (s[1] == ':') {
+        if (s[2] == ':') {
+          /* invalid format: three successive colons */
+          return 0;
+        }
+        s++;
+        /* "::" found, set zeros */
+        while (zero_blocks > 0) {
+          zero_blocks--;
+          if (current_block_index & 0x1) {
+            addr_index++;
+          } else {
+            if (addr) {
+              addr->addr[addr_index] = 0;
+            }
+          }
+          current_block_index++;
+          if (current_block_index > 7) {
+            /* address too long! */
+            return 0;
+          }
+        }
+      }
+    } else if (lwip_isxdigit(*s)) {
+      /* add current digit */
+      current_block_value = (current_block_value << 4) +
+          (lwip_isdigit(*s) ? (u32_t)(*s - '0') :
+          (u32_t)(10 + (lwip_islower(*s) ? *s - 'a' : *s - 'A')));
+    } else {
+      /* unexpected digit, space? CRLF? */
+      break;
+    }
+  }
+
+  if (addr) {
+    if (current_block_index & 0x1) {
+      addr->addr[addr_index++] |= current_block_value;
+    }
+    else {
+      addr->addr[addr_index] = current_block_value << 16;
+    }
+#if LWIP_IPV4
+fix_byte_order_and_return:
+#endif
+    /* convert to network byte order. */
+    for (addr_index = 0; addr_index < 4; addr_index++) {
+      addr->addr[addr_index] = lwip_htonl(addr->addr[addr_index]);
+    }
+
+    ip6_addr_clear_zone(addr);
+#if LWIP_IPV6_SCOPES
+    if (*s == '%') {
+      const char *scopestr = s + 1;
+      if (*scopestr) {
+        struct netif *netif = netif_find(scopestr);
+        if (netif) {
+          ip6_addr_assign_zone(addr, IP6_UNKNOWN, netif);
+        }
+      }
+    }
+#endif
+  }
+
+  if (current_block_index != 7) {
+    return 0;
+  }
+
+  return 1;
+}
+
+/**
+ * Convert numeric IPv6 address into ASCII representation.
+ * returns ptr to static buffer; not reentrant!
+ *
+ * @param addr ip6 address in network order to convert
+ * @return pointer to a global static (!) buffer that holds the ASCII
+ *         representation of addr
+ */
+char *
+ip6addr_ntoa(const ip6_addr_t *addr)
+{
+  static char str[40];
+  return ip6addr_ntoa_r(addr, str, 40);
+}
+
+/**
+ * Same as ipaddr_ntoa, but reentrant since a user-supplied buffer is used.
+ *
+ * @param addr ip6 address in network order to convert
+ * @param buf target buffer where the string is stored
+ * @param buflen length of buf
+ * @return either pointer to buf which now holds the ASCII
+ *         representation of addr or NULL if buf was too small
+ */
+char *
+ip6addr_ntoa_r(const ip6_addr_t *addr, char *buf, int buflen)
+{
+  u32_t current_block_index, current_block_value, next_block_value;
+  s32_t i;
+  u8_t zero_flag, empty_block_flag;
+
+#if LWIP_IPV4
+  if (ip6_addr_isipv4mappedipv6(addr)) {
+    /* This is an IPv4 mapped address */
+    ip4_addr_t addr4;
+    char *ret;
+#define IP4MAPPED_HEADER "::FFFF:"
+    char *buf_ip4 = buf + sizeof(IP4MAPPED_HEADER) - 1;
+    int buflen_ip4 = buflen - sizeof(IP4MAPPED_HEADER) + 1;
+    if (buflen < (int)sizeof(IP4MAPPED_HEADER)) {
+      return NULL;
+    }
+    memcpy(buf, IP4MAPPED_HEADER, sizeof(IP4MAPPED_HEADER));
+    addr4.addr = addr->addr[3];
+    ret = ip4addr_ntoa_r(&addr4, buf_ip4, buflen_ip4);
+    if (ret != buf_ip4) {
+      return NULL;
+    }
+    return buf;
+  }
+#endif /* LWIP_IPV4 */
+  i = 0;
+  empty_block_flag = 0; /* used to indicate a zero chain for "::' */
+
+  for (current_block_index = 0; current_block_index < 8; current_block_index++) {
+    /* get the current 16-bit block */
+    current_block_value = lwip_htonl(addr->addr[current_block_index >> 1]);
+    if ((current_block_index & 0x1) == 0) {
+      current_block_value = current_block_value >> 16;
+    }
+    current_block_value &= 0xffff;
+
+    /* Check for empty block. */
+    if (current_block_value == 0) {
+      if (current_block_index == 7 && empty_block_flag == 1) {
+        /* special case, we must render a ':' for the last block. */
+        buf[i++] = ':';
+        if (i >= buflen) {
+          return NULL;
+        }
+        break;
+      }
+      if (empty_block_flag == 0) {
+        /* generate empty block "::", but only if more than one contiguous zero block,
+         * according to current formatting suggestions RFC 5952. */
+        next_block_value = lwip_htonl(addr->addr[(current_block_index + 1) >> 1]);
+        if ((current_block_index & 0x1) == 0x01) {
+            next_block_value = next_block_value >> 16;
+        }
+        next_block_value &= 0xffff;
+        if (next_block_value == 0) {
+          empty_block_flag = 1;
+          buf[i++] = ':';
+          if (i >= buflen) {
+            return NULL;
+          }
+          continue; /* move on to next block. */
+        }
+      } else if (empty_block_flag == 1) {
+        /* move on to next block. */
+        continue;
+      }
+    } else if (empty_block_flag == 1) {
+      /* Set this flag value so we don't produce multiple empty blocks. */
+      empty_block_flag = 2;
+    }
+
+    if (current_block_index > 0) {
+      buf[i++] = ':';
+      if (i >= buflen) {
+        return NULL;
+      }
+    }
+
+    if ((current_block_value & 0xf000) == 0) {
+      zero_flag = 1;
+    } else {
+      buf[i++] = lwip_xchar(((current_block_value & 0xf000) >> 12));
+      zero_flag = 0;
+      if (i >= buflen) {
+        return NULL;
+      }
+    }
+
+    if (((current_block_value & 0xf00) == 0) && (zero_flag)) {
+      /* do nothing */
+    } else {
+      buf[i++] = lwip_xchar(((current_block_value & 0xf00) >> 8));
+      zero_flag = 0;
+      if (i >= buflen) {
+        return NULL;
+      }
+    }
+
+    if (((current_block_value & 0xf0) == 0) && (zero_flag)) {
+      /* do nothing */
+    }
+    else {
+      buf[i++] = lwip_xchar(((current_block_value & 0xf0) >> 4));
+      zero_flag = 0;
+      if (i >= buflen) {
+        return NULL;
+      }
+    }
+
+    buf[i++] = lwip_xchar((current_block_value & 0xf));
+    if (i >= buflen) {
+      return NULL;
+    }
+  }
+
+  buf[i] = 0;
+
+  return buf;
+}
+
+#endif /* LWIP_IPV6 */
diff --git a/lib/lwip/core/ipv6/ip6_frag.c b/lib/lwip/core/ipv6/ip6_frag.c
new file mode 100644
index 0000000000..16bcf95087
--- /dev/null
+++ b/lib/lwip/core/ipv6/ip6_frag.c
@@ -0,0 +1,862 @@
+/**
+ * @file
+ *
+ * IPv6 fragmentation and reassembly.
+ */
+
+/*
+ * Copyright (c) 2010 Inico Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Ivan Delamer <delamer@inicotech.com>
+ *
+ *
+ * Please coordinate changes and requests with Ivan Delamer
+ * <delamer@inicotech.com>
+ */
+
+#include "lwip/opt.h"
+#include "lwip/ip6_frag.h"
+#include "lwip/ip6.h"
+#include "lwip/icmp6.h"
+#include "lwip/nd6.h"
+#include "lwip/ip.h"
+
+#include "lwip/pbuf.h"
+#include "lwip/memp.h"
+#include "lwip/stats.h"
+
+#include <string.h>
+
+#if LWIP_IPV6 && LWIP_IPV6_REASS  /* don't build if not configured for use in lwipopts.h */
+
+
+/** Setting this to 0, you can turn off checking the fragments for overlapping
+ * regions. The code gets a little smaller. Only use this if you know that
+ * overlapping won't occur on your network! */
+#ifndef IP_REASS_CHECK_OVERLAP
+#define IP_REASS_CHECK_OVERLAP 1
+#endif /* IP_REASS_CHECK_OVERLAP */
+
+/** Set to 0 to prevent freeing the oldest datagram when the reassembly buffer is
+ * full (IP_REASS_MAX_PBUFS pbufs are enqueued). The code gets a little smaller.
+ * Datagrams will be freed by timeout only. Especially useful when MEMP_NUM_REASSDATA
+ * is set to 1, so one datagram can be reassembled at a time, only. */
+#ifndef IP_REASS_FREE_OLDEST
+#define IP_REASS_FREE_OLDEST 1
+#endif /* IP_REASS_FREE_OLDEST */
+
+#if IPV6_FRAG_COPYHEADER
+/* The number of bytes we need to "borrow" from (i.e., overwrite in) the header
+ * that precedes the fragment header for reassembly pruposes. */
+#define IPV6_FRAG_REQROOM ((s16_t)(sizeof(struct ip6_reass_helper) - IP6_FRAG_HLEN))
+#endif
+
+#define IP_REASS_FLAG_LASTFRAG 0x01
+
+/** This is a helper struct which holds the starting
+ * offset and the ending offset of this fragment to
+ * easily chain the fragments.
+ * It has the same packing requirements as the IPv6 header, since it replaces
+ * the Fragment Header in memory in incoming fragments to keep
+ * track of the various fragments.
+ */
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct ip6_reass_helper {
+  PACK_STRUCT_FIELD(struct pbuf *next_pbuf);
+  PACK_STRUCT_FIELD(u16_t start);
+  PACK_STRUCT_FIELD(u16_t end);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
+
+/* static variables */
+static struct ip6_reassdata *reassdatagrams;
+static u16_t ip6_reass_pbufcount;
+
+/* Forward declarations. */
+static void ip6_reass_free_complete_datagram(struct ip6_reassdata *ipr);
+#if IP_REASS_FREE_OLDEST
+static void ip6_reass_remove_oldest_datagram(struct ip6_reassdata *ipr, int pbufs_needed);
+#endif /* IP_REASS_FREE_OLDEST */
+
+void
+ip6_reass_tmr(void)
+{
+  struct ip6_reassdata *r, *tmp;
+
+#if !IPV6_FRAG_COPYHEADER
+  LWIP_ASSERT("sizeof(struct ip6_reass_helper) <= IP6_FRAG_HLEN, set IPV6_FRAG_COPYHEADER to 1",
+    sizeof(struct ip6_reass_helper) <= IP6_FRAG_HLEN);
+#endif /* !IPV6_FRAG_COPYHEADER */
+
+  r = reassdatagrams;
+  while (r != NULL) {
+    /* Decrement the timer. Once it reaches 0,
+     * clean up the incomplete fragment assembly */
+    if (r->timer > 0) {
+      r->timer--;
+      r = r->next;
+    } else {
+      /* reassembly timed out */
+      tmp = r;
+      /* get the next pointer before freeing */
+      r = r->next;
+      /* free the helper struct and all enqueued pbufs */
+      ip6_reass_free_complete_datagram(tmp);
+     }
+   }
+}
+
+/**
+ * Free a datagram (struct ip6_reassdata) and all its pbufs.
+ * Updates the total count of enqueued pbufs (ip6_reass_pbufcount),
+ * sends an ICMP time exceeded packet.
+ *
+ * @param ipr datagram to free
+ */
+static void
+ip6_reass_free_complete_datagram(struct ip6_reassdata *ipr)
+{
+  struct ip6_reassdata *prev;
+  u16_t pbufs_freed = 0;
+  u16_t clen;
+  struct pbuf *p;
+  struct ip6_reass_helper *iprh;
+
+#if LWIP_ICMP6
+  iprh = (struct ip6_reass_helper *)ipr->p->payload;
+  if (iprh->start == 0) {
+    /* The first fragment was received, send ICMP time exceeded. */
+    /* First, de-queue the first pbuf from r->p. */
+    p = ipr->p;
+    ipr->p = iprh->next_pbuf;
+    /* Restore the part that we've overwritten with our helper structure, or we
+     * might send garbage (and disclose a pointer) in the ICMPv6 reply. */
+    MEMCPY(p->payload, ipr->orig_hdr, sizeof(iprh));
+    /* Then, move back to the original ipv6 header (we are now pointing to Fragment header).
+       This cannot fail since we already checked when receiving this fragment. */
+    if (pbuf_header_force(p, (s16_t)((u8_t*)p->payload - (u8_t*)ipr->iphdr))) {
+      LWIP_ASSERT("ip6_reass_free: moving p->payload to ip6 header failed", 0);
+    }
+    else {
+      /* Reconstruct the zoned source and destination addresses, so that we do
+       * not end up sending the ICMP response over the wrong link. */
+      ip6_addr_t src_addr, dest_addr;
+      ip6_addr_copy_from_packed(src_addr, IPV6_FRAG_SRC(ipr));
+      ip6_addr_set_zone(&src_addr, ipr->src_zone);
+      ip6_addr_copy_from_packed(dest_addr, IPV6_FRAG_DEST(ipr));
+      ip6_addr_set_zone(&dest_addr, ipr->dest_zone);
+      /* Send the actual ICMP response. */
+      icmp6_time_exceeded_with_addrs(p, ICMP6_TE_FRAG, &src_addr, &dest_addr);
+    }
+    clen = pbuf_clen(p);
+    LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff);
+    pbufs_freed = (u16_t)(pbufs_freed + clen);
+    pbuf_free(p);
+  }
+#endif /* LWIP_ICMP6 */
+
+  /* First, free all received pbufs.  The individual pbufs need to be released
+     separately as they have not yet been chained */
+  p = ipr->p;
+  while (p != NULL) {
+    struct pbuf *pcur;
+    iprh = (struct ip6_reass_helper *)p->payload;
+    pcur = p;
+    /* get the next pointer before freeing */
+    p = iprh->next_pbuf;
+    clen = pbuf_clen(pcur);
+    LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff);
+    pbufs_freed = (u16_t)(pbufs_freed + clen);
+    pbuf_free(pcur);
+  }
+
+  /* Then, unchain the struct ip6_reassdata from the list and free it. */
+  if (ipr == reassdatagrams) {
+    reassdatagrams = ipr->next;
+  } else {
+    prev = reassdatagrams;
+    while (prev != NULL) {
+      if (prev->next == ipr) {
+        break;
+      }
+      prev = prev->next;
+    }
+    if (prev != NULL) {
+      prev->next = ipr->next;
+    }
+  }
+  memp_free(MEMP_IP6_REASSDATA, ipr);
+
+  /* Finally, update number of pbufs in reassembly queue */
+  LWIP_ASSERT("ip_reass_pbufcount >= clen", ip6_reass_pbufcount >= pbufs_freed);
+  ip6_reass_pbufcount = (u16_t)(ip6_reass_pbufcount - pbufs_freed);
+}
+
+#if IP_REASS_FREE_OLDEST
+/**
+ * Free the oldest datagram to make room for enqueueing new fragments.
+ * The datagram ipr is not freed!
+ *
+ * @param ipr ip6_reassdata for the current fragment
+ * @param pbufs_needed number of pbufs needed to enqueue
+ *        (used for freeing other datagrams if not enough space)
+ */
+static void
+ip6_reass_remove_oldest_datagram(struct ip6_reassdata *ipr, int pbufs_needed)
+{
+  struct ip6_reassdata *r, *oldest;
+
+  /* Free datagrams until being allowed to enqueue 'pbufs_needed' pbufs,
+   * but don't free the current datagram! */
+  do {
+    r = oldest = reassdatagrams;
+    while (r != NULL) {
+      if (r != ipr) {
+        if (r->timer <= oldest->timer) {
+          /* older than the previous oldest */
+          oldest = r;
+        }
+      }
+      r = r->next;
+    }
+    if (oldest == ipr) {
+      /* nothing to free, ipr is the only element on the list */
+      return;
+    }
+    if (oldest != NULL) {
+      ip6_reass_free_complete_datagram(oldest);
+    }
+  } while (((ip6_reass_pbufcount + pbufs_needed) > IP_REASS_MAX_PBUFS) && (reassdatagrams != NULL));
+}
+#endif /* IP_REASS_FREE_OLDEST */
+
+/**
+ * Reassembles incoming IPv6 fragments into an IPv6 datagram.
+ *
+ * @param p points to the IPv6 Fragment Header
+ * @return NULL if reassembly is incomplete, pbuf pointing to
+ *         IPv6 Header if reassembly is complete
+ */
+struct pbuf *
+ip6_reass(struct pbuf *p)
+{
+  struct ip6_reassdata *ipr, *ipr_prev;
+  struct ip6_reass_helper *iprh, *iprh_tmp, *iprh_prev=NULL;
+  struct ip6_frag_hdr *frag_hdr;
+  u16_t offset, len, start, end;
+  ptrdiff_t hdrdiff;
+  u16_t clen;
+  u8_t valid = 1;
+  struct pbuf *q, *next_pbuf;
+
+  IP6_FRAG_STATS_INC(ip6_frag.recv);
+
+  /* ip6_frag_hdr must be in the first pbuf, not chained. Checked by caller. */
+  LWIP_ASSERT("IPv6 fragment header does not fit in first pbuf",
+    p->len >= sizeof(struct ip6_frag_hdr));
+
+  frag_hdr = (struct ip6_frag_hdr *) p->payload;
+
+  clen = pbuf_clen(p);
+
+  offset = lwip_ntohs(frag_hdr->_fragment_offset);
+
+  /* Calculate fragment length from IPv6 payload length.
+   * Adjust for headers before Fragment Header.
+   * And finally adjust by Fragment Header length. */
+  len = lwip_ntohs(ip6_current_header()->_plen);
+  hdrdiff = (u8_t*)p->payload - (const u8_t*)ip6_current_header();
+  LWIP_ASSERT("not a valid pbuf (ip6_input check missing?)", hdrdiff <= 0xFFFF);
+  LWIP_ASSERT("not a valid pbuf (ip6_input check missing?)", hdrdiff >= IP6_HLEN);
+  hdrdiff -= IP6_HLEN;
+  hdrdiff += IP6_FRAG_HLEN;
+  if (hdrdiff > len) {
+    IP6_FRAG_STATS_INC(ip6_frag.proterr);
+    goto nullreturn;
+  }
+  len = (u16_t)(len - hdrdiff);
+  start = (offset & IP6_FRAG_OFFSET_MASK);
+  if (start > (0xFFFF - len)) {
+    /* u16_t overflow, cannot handle this */
+    IP6_FRAG_STATS_INC(ip6_frag.proterr);
+    goto nullreturn;
+  }
+
+  /* Look for the datagram the fragment belongs to in the current datagram queue,
+   * remembering the previous in the queue for later dequeueing. */
+  for (ipr = reassdatagrams, ipr_prev = NULL; ipr != NULL; ipr = ipr->next) {
+    /* Check if the incoming fragment matches the one currently present
+       in the reassembly buffer. If so, we proceed with copying the
+       fragment into the buffer. */
+    if ((frag_hdr->_identification == ipr->identification) &&
+        ip6_addr_packed_eq(ip6_current_src_addr(), &(IPV6_FRAG_SRC(ipr)), ipr->src_zone) &&
+        ip6_addr_packed_eq(ip6_current_dest_addr(), &(IPV6_FRAG_DEST(ipr)), ipr->dest_zone)) {
+      IP6_FRAG_STATS_INC(ip6_frag.cachehit);
+      break;
+    }
+    ipr_prev = ipr;
+  }
+
+  if (ipr == NULL) {
+  /* Enqueue a new datagram into the datagram queue */
+    ipr = (struct ip6_reassdata *)memp_malloc(MEMP_IP6_REASSDATA);
+    if (ipr == NULL) {
+#if IP_REASS_FREE_OLDEST
+      /* Make room and try again. */
+      ip6_reass_remove_oldest_datagram(ipr, clen);
+      ipr = (struct ip6_reassdata *)memp_malloc(MEMP_IP6_REASSDATA);
+      if (ipr != NULL) {
+        /* re-search ipr_prev since it might have been removed */
+        for (ipr_prev = reassdatagrams; ipr_prev != NULL; ipr_prev = ipr_prev->next) {
+          if (ipr_prev->next == ipr) {
+            break;
+          }
+        }
+      } else
+#endif /* IP_REASS_FREE_OLDEST */
+      {
+        IP6_FRAG_STATS_INC(ip6_frag.memerr);
+        goto nullreturn;
+      }
+    }
+
+    memset(ipr, 0, sizeof(struct ip6_reassdata));
+    ipr->timer = IPV6_REASS_MAXAGE;
+
+    /* enqueue the new structure to the front of the list */
+    ipr->next = reassdatagrams;
+    reassdatagrams = ipr;
+
+    /* Use the current IPv6 header for src/dest address reference.
+     * Eventually, we will replace it when we get the first fragment
+     * (it might be this one, in any case, it is done later). */
+    /* need to use the none-const pointer here: */
+    ipr->iphdr = ip_data.current_ip6_header;
+#if IPV6_FRAG_COPYHEADER
+    MEMCPY(&ipr->src, &ip6_current_header()->src, sizeof(ipr->src));
+    MEMCPY(&ipr->dest, &ip6_current_header()->dest, sizeof(ipr->dest));
+#endif /* IPV6_FRAG_COPYHEADER */
+#if LWIP_IPV6_SCOPES
+    /* Also store the address zone information.
+     * @todo It is possible that due to netif destruction and recreation, the
+     * stored zones end up resolving to a different interface. In that case, we
+     * risk sending a "time exceeded" ICMP response over the wrong link.
+     * Ideally, netif destruction would clean up matching pending reassembly
+     * structures, but custom zone mappings would make that non-trivial. */
+    ipr->src_zone = ip6_addr_zone(ip6_current_src_addr());
+    ipr->dest_zone = ip6_addr_zone(ip6_current_dest_addr());
+#endif /* LWIP_IPV6_SCOPES */
+    /* copy the fragmented packet id. */
+    ipr->identification = frag_hdr->_identification;
+
+    /* copy the nexth field */
+    ipr->nexth = frag_hdr->_nexth;
+  }
+
+  /* Check if we are allowed to enqueue more datagrams. */
+  if ((ip6_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS) {
+#if IP_REASS_FREE_OLDEST
+    ip6_reass_remove_oldest_datagram(ipr, clen);
+    if ((ip6_reass_pbufcount + clen) <= IP_REASS_MAX_PBUFS) {
+      /* re-search ipr_prev since it might have been removed */
+      for (ipr_prev = reassdatagrams; ipr_prev != NULL; ipr_prev = ipr_prev->next) {
+        if (ipr_prev->next == ipr) {
+          break;
+        }
+      }
+    } else
+#endif /* IP_REASS_FREE_OLDEST */
+    {
+      /* @todo: send ICMPv6 time exceeded here? */
+      /* drop this pbuf */
+      IP6_FRAG_STATS_INC(ip6_frag.memerr);
+      goto nullreturn;
+    }
+  }
+
+  /* Overwrite Fragment Header with our own helper struct. */
+#if IPV6_FRAG_COPYHEADER
+  if (IPV6_FRAG_REQROOM > 0) {
+    /* Make room for struct ip6_reass_helper (only required if sizeof(void*) > 4).
+       This cannot fail since we already checked when receiving this fragment. */
+    u8_t hdrerr = pbuf_header_force(p, IPV6_FRAG_REQROOM);
+    LWIP_UNUSED_ARG(hdrerr); /* in case of LWIP_NOASSERT */
+    LWIP_ASSERT("no room for struct ip6_reass_helper", hdrerr == 0);
+  }
+#else /* IPV6_FRAG_COPYHEADER */
+  LWIP_ASSERT("sizeof(struct ip6_reass_helper) <= IP6_FRAG_HLEN, set IPV6_FRAG_COPYHEADER to 1",
+    sizeof(struct ip6_reass_helper) <= IP6_FRAG_HLEN);
+#endif /* IPV6_FRAG_COPYHEADER */
+
+  /* Prepare the pointer to the helper structure, and its initial values.
+   * Do not yet write to the structure itself, as we still have to make a
+   * backup of the original data, and we should not do that until we know for
+   * sure that we are going to add this packet to the list. */
+  iprh = (struct ip6_reass_helper *)p->payload;
+  next_pbuf = NULL;
+  end = (u16_t)(start + len);
+
+  /* find the right place to insert this pbuf */
+  /* Iterate through until we either get to the end of the list (append),
+   * or we find on with a larger offset (insert). */
+  for (q = ipr->p; q != NULL;) {
+    iprh_tmp = (struct ip6_reass_helper*)q->payload;
+    if (start < iprh_tmp->start) {
+#if IP_REASS_CHECK_OVERLAP
+      if (end > iprh_tmp->start) {
+        /* fragment overlaps with following, throw away */
+        IP6_FRAG_STATS_INC(ip6_frag.proterr);
+        goto nullreturn;
+      }
+      if (iprh_prev != NULL) {
+        if (start < iprh_prev->end) {
+          /* fragment overlaps with previous, throw away */
+          IP6_FRAG_STATS_INC(ip6_frag.proterr);
+          goto nullreturn;
+        }
+      }
+#endif /* IP_REASS_CHECK_OVERLAP */
+      /* the new pbuf should be inserted before this */
+      next_pbuf = q;
+      if (iprh_prev != NULL) {
+        /* not the fragment with the lowest offset */
+        iprh_prev->next_pbuf = p;
+      } else {
+        /* fragment with the lowest offset */
+        ipr->p = p;
+      }
+      break;
+    } else if (start == iprh_tmp->start) {
+      /* received the same datagram twice: no need to keep the datagram */
+      goto nullreturn;
+#if IP_REASS_CHECK_OVERLAP
+    } else if (start < iprh_tmp->end) {
+      /* overlap: no need to keep the new datagram */
+      IP6_FRAG_STATS_INC(ip6_frag.proterr);
+      goto nullreturn;
+#endif /* IP_REASS_CHECK_OVERLAP */
+    } else {
+      /* Check if the fragments received so far have no gaps. */
+      if (iprh_prev != NULL) {
+        if (iprh_prev->end != iprh_tmp->start) {
+          /* There is a fragment missing between the current
+           * and the previous fragment */
+          valid = 0;
+        }
+      }
+    }
+    q = iprh_tmp->next_pbuf;
+    iprh_prev = iprh_tmp;
+  }
+
+  /* If q is NULL, then we made it to the end of the list. Determine what to do now */
+  if (q == NULL) {
+    if (iprh_prev != NULL) {
+      /* this is (for now), the fragment with the highest offset:
+       * chain it to the last fragment */
+#if IP_REASS_CHECK_OVERLAP
+      LWIP_ASSERT("check fragments don't overlap", iprh_prev->end <= start);
+#endif /* IP_REASS_CHECK_OVERLAP */
+      iprh_prev->next_pbuf = p;
+      if (iprh_prev->end != start) {
+        valid = 0;
+      }
+    } else {
+#if IP_REASS_CHECK_OVERLAP
+      LWIP_ASSERT("no previous fragment, this must be the first fragment!",
+        ipr->p == NULL);
+#endif /* IP_REASS_CHECK_OVERLAP */
+      /* this is the first fragment we ever received for this ip datagram */
+      ipr->p = p;
+    }
+  }
+
+  /* Track the current number of pbufs current 'in-flight', in order to limit
+  the number of fragments that may be enqueued at any one time */
+  ip6_reass_pbufcount = (u16_t)(ip6_reass_pbufcount + clen);
+
+  /* Remember IPv6 header if this is the first fragment. */
+  if (start == 0) {
+    /* need to use the none-const pointer here: */
+    ipr->iphdr = ip_data.current_ip6_header;
+    /* Make a backup of the part of the packet data that we are about to
+     * overwrite, so that we can restore the original later. */
+    MEMCPY(ipr->orig_hdr, p->payload, sizeof(*iprh));
+    /* For IPV6_FRAG_COPYHEADER there is no need to copy src/dst again, as they
+     * will be the same as they were. With LWIP_IPV6_SCOPES, the same applies
+     * to the source/destination zones. */
+  }
+  /* Only after the backup do we get to fill in the actual helper structure. */
+  iprh->next_pbuf = next_pbuf;
+  iprh->start = start;
+  iprh->end = end;
+
+  /* If this is the last fragment, calculate total packet length. */
+  if ((offset & IP6_FRAG_MORE_FLAG) == 0) {
+    ipr->datagram_len = iprh->end;
+  }
+
+  /* Additional validity tests: we have received first and last fragment. */
+  iprh_tmp = (struct ip6_reass_helper*)ipr->p->payload;
+  if (iprh_tmp->start != 0) {
+    valid = 0;
+  }
+  if (ipr->datagram_len == 0) {
+    valid = 0;
+  }
+
+  /* Final validity test: no gaps between current and last fragment. */
+  iprh_prev = iprh;
+  q = iprh->next_pbuf;
+  while ((q != NULL) && valid) {
+    iprh = (struct ip6_reass_helper*)q->payload;
+    if (iprh_prev->end != iprh->start) {
+      valid = 0;
+      break;
+    }
+    iprh_prev = iprh;
+    q = iprh->next_pbuf;
+  }
+
+  if (valid) {
+    /* All fragments have been received */
+    struct ip6_hdr* iphdr_ptr;
+
+    /* chain together the pbufs contained within the ip6_reassdata list. */
+    iprh = (struct ip6_reass_helper*) ipr->p->payload;
+    while (iprh != NULL) {
+      next_pbuf = iprh->next_pbuf;
+      if (next_pbuf != NULL) {
+        /* Save next helper struct (will be hidden in next step). */
+        iprh_tmp = (struct ip6_reass_helper*)next_pbuf->payload;
+
+        /* hide the fragment header for every succeeding fragment */
+        pbuf_remove_header(next_pbuf, IP6_FRAG_HLEN);
+#if IPV6_FRAG_COPYHEADER
+        if (IPV6_FRAG_REQROOM > 0) {
+          /* hide the extra bytes borrowed from ip6_hdr for struct ip6_reass_helper */
+          u8_t hdrerr = pbuf_remove_header(next_pbuf, IPV6_FRAG_REQROOM);
+          LWIP_UNUSED_ARG(hdrerr); /* in case of LWIP_NOASSERT */
+          LWIP_ASSERT("no room for struct ip6_reass_helper", hdrerr == 0);
+        }
+#endif
+        pbuf_cat(ipr->p, next_pbuf);
+      }
+      else {
+        iprh_tmp = NULL;
+      }
+
+      iprh = iprh_tmp;
+    }
+
+    /* Get the first pbuf. */
+    p = ipr->p;
+
+#if IPV6_FRAG_COPYHEADER
+    if (IPV6_FRAG_REQROOM > 0) {
+      u8_t hdrerr;
+      /* Restore (only) the bytes that we overwrote beyond the fragment header.
+       * Those bytes may belong to either the IPv6 header or an extension
+       * header placed before the fragment header. */
+      MEMCPY(p->payload, ipr->orig_hdr, IPV6_FRAG_REQROOM);
+      /* get back room for struct ip6_reass_helper (only required if sizeof(void*) > 4) */
+      hdrerr = pbuf_remove_header(p, IPV6_FRAG_REQROOM);
+      LWIP_UNUSED_ARG(hdrerr); /* in case of LWIP_NOASSERT */
+      LWIP_ASSERT("no room for struct ip6_reass_helper", hdrerr == 0);
+    }
+#endif
+
+    /* We need to get rid of the fragment header itself, which is somewhere in
+     * the middle of the packet (but still in the first pbuf of the chain).
+     * Getting rid of the header is required by RFC 2460 Sec. 4.5 and necessary
+     * in order to be able to reassemble packets that are close to full size
+     * (i.e., around 65535 bytes). We simply move up all the headers before the
+     * fragment header, including the IPv6 header, and adjust the payload start
+     * accordingly. This works because all these headers are in the first pbuf
+     * of the chain, and because the caller adjusts all its pointers on
+     * successful reassembly. */
+    MEMMOVE((u8_t*)ipr->iphdr + sizeof(struct ip6_frag_hdr), ipr->iphdr,
+      (size_t)((u8_t*)p->payload - (u8_t*)ipr->iphdr));
+
+    /* This is where the IPv6 header is now. */
+    iphdr_ptr = (struct ip6_hdr*)((u8_t*)ipr->iphdr +
+      sizeof(struct ip6_frag_hdr));
+
+    /* Adjust datagram length by adding header lengths. */
+    ipr->datagram_len = (u16_t)(ipr->datagram_len + ((u8_t*)p->payload - (u8_t*)iphdr_ptr)
+                         - IP6_HLEN);
+
+    /* Set payload length in ip header. */
+    iphdr_ptr->_plen = lwip_htons(ipr->datagram_len);
+
+    /* With the fragment header gone, we now need to adjust the next-header
+     * field of whatever header was originally before it. Since the packet made
+     * it through the original header processing routines at least up to the
+     * fragment header, we do not need any further sanity checks here. */
+    if (IP6H_NEXTH(iphdr_ptr) == IP6_NEXTH_FRAGMENT) {
+      iphdr_ptr->_nexth = ipr->nexth;
+    } else {
+      u8_t *ptr = (u8_t *)iphdr_ptr + IP6_HLEN;
+      while (*ptr != IP6_NEXTH_FRAGMENT) {
+        ptr += 8 * (1 + ptr[1]);
+      }
+      *ptr = ipr->nexth;
+    }
+
+    /* release the resources allocated for the fragment queue entry */
+    if (reassdatagrams == ipr) {
+      /* it was the first in the list */
+      reassdatagrams = ipr->next;
+    } else {
+      /* it wasn't the first, so it must have a valid 'prev' */
+      LWIP_ASSERT("sanity check linked list", ipr_prev != NULL);
+      ipr_prev->next = ipr->next;
+    }
+    memp_free(MEMP_IP6_REASSDATA, ipr);
+
+    /* adjust the number of pbufs currently queued for reassembly. */
+    clen = pbuf_clen(p);
+    LWIP_ASSERT("ip6_reass_pbufcount >= clen", ip6_reass_pbufcount >= clen);
+    ip6_reass_pbufcount = (u16_t)(ip6_reass_pbufcount - clen);
+
+    /* Move pbuf back to IPv6 header. This should never fail. */
+    if (pbuf_header_force(p, (s16_t)((u8_t*)p->payload - (u8_t*)iphdr_ptr))) {
+      LWIP_ASSERT("ip6_reass: moving p->payload to ip6 header failed", 0);
+      pbuf_free(p);
+      return NULL;
+    }
+
+    /* Return the pbuf chain */
+    return p;
+  }
+  /* the datagram is not (yet?) reassembled completely */
+  return NULL;
+
+nullreturn:
+  IP6_FRAG_STATS_INC(ip6_frag.drop);
+  pbuf_free(p);
+  return NULL;
+}
+
+#endif /* LWIP_IPV6 && LWIP_IPV6_REASS */
+
+#if LWIP_IPV6 && LWIP_IPV6_FRAG
+
+#if !LWIP_NETIF_TX_SINGLE_PBUF
+/** Allocate a new struct pbuf_custom_ref */
+static struct pbuf_custom_ref*
+ip6_frag_alloc_pbuf_custom_ref(void)
+{
+  return (struct pbuf_custom_ref*)memp_malloc(MEMP_FRAG_PBUF);
+}
+
+/** Free a struct pbuf_custom_ref */
+static void
+ip6_frag_free_pbuf_custom_ref(struct pbuf_custom_ref* p)
+{
+  LWIP_ASSERT("p != NULL", p != NULL);
+  memp_free(MEMP_FRAG_PBUF, p);
+}
+
+/** Free-callback function to free a 'struct pbuf_custom_ref', called by
+ * pbuf_free. */
+static void
+ip6_frag_free_pbuf_custom(struct pbuf *p)
+{
+  struct pbuf_custom_ref *pcr = (struct pbuf_custom_ref*)p;
+  LWIP_ASSERT("pcr != NULL", pcr != NULL);
+  LWIP_ASSERT("pcr == p", (void*)pcr == (void*)p);
+  if (pcr->original != NULL) {
+    pbuf_free(pcr->original);
+  }
+  ip6_frag_free_pbuf_custom_ref(pcr);
+}
+#endif /* !LWIP_NETIF_TX_SINGLE_PBUF */
+
+/**
+ * Fragment an IPv6 datagram if too large for the netif or path MTU.
+ *
+ * Chop the datagram in MTU sized chunks and send them in order
+ * by pointing PBUF_REFs into p
+ *
+ * @param p ipv6 packet to send
+ * @param netif the netif on which to send
+ * @param dest destination ipv6 address to which to send
+ *
+ * @return ERR_OK if sent successfully, err_t otherwise
+ */
+err_t
+ip6_frag(struct pbuf *p, struct netif *netif, const ip6_addr_t *dest)
+{
+  struct ip6_hdr *original_ip6hdr;
+  struct ip6_hdr *ip6hdr;
+  struct ip6_frag_hdr *frag_hdr;
+  struct pbuf *rambuf;
+#if !LWIP_NETIF_TX_SINGLE_PBUF
+  struct pbuf *newpbuf;
+  u16_t newpbuflen = 0;
+  u16_t left_to_copy;
+#endif
+  static u32_t identification;
+  u16_t left, cop;
+  const u16_t mtu = nd6_get_destination_mtu(dest, netif);
+  const u16_t nfb = (u16_t)((mtu - (IP6_HLEN + IP6_FRAG_HLEN)) & IP6_FRAG_OFFSET_MASK);
+  u16_t fragment_offset = 0;
+  u16_t last;
+  u16_t poff = IP6_HLEN;
+
+  identification++;
+
+  original_ip6hdr = (struct ip6_hdr *)p->payload;
+
+  /* @todo we assume there are no options in the unfragmentable part (IPv6 header). */
+  LWIP_ASSERT("p->tot_len >= IP6_HLEN", p->tot_len >= IP6_HLEN);
+  left = (u16_t)(p->tot_len - IP6_HLEN);
+
+  while (left) {
+    last = (left <= nfb);
+
+    /* Fill this fragment */
+    cop = last ? left : nfb;
+
+#if LWIP_NETIF_TX_SINGLE_PBUF
+    rambuf = pbuf_alloc(PBUF_IP, cop + IP6_FRAG_HLEN, PBUF_RAM);
+    if (rambuf == NULL) {
+      IP6_FRAG_STATS_INC(ip6_frag.memerr);
+      return ERR_MEM;
+    }
+    LWIP_ASSERT("this needs a pbuf in one piece!",
+      (rambuf->len == rambuf->tot_len) && (rambuf->next == NULL));
+    poff += pbuf_copy_partial(p, (u8_t*)rambuf->payload + IP6_FRAG_HLEN, cop, poff);
+    /* make room for the IP header */
+    if (pbuf_add_header(rambuf, IP6_HLEN)) {
+      pbuf_free(rambuf);
+      IP6_FRAG_STATS_INC(ip6_frag.memerr);
+      return ERR_MEM;
+    }
+    /* fill in the IP header */
+    SMEMCPY(rambuf->payload, original_ip6hdr, IP6_HLEN);
+    ip6hdr = (struct ip6_hdr *)rambuf->payload;
+    frag_hdr = (struct ip6_frag_hdr *)((u8_t*)rambuf->payload + IP6_HLEN);
+#else
+    /* When not using a static buffer, create a chain of pbufs.
+     * The first will be a PBUF_RAM holding the link, IPv6, and Fragment header.
+     * The rest will be PBUF_REFs mirroring the pbuf chain to be fragged,
+     * but limited to the size of an mtu.
+     */
+    rambuf = pbuf_alloc(PBUF_LINK, IP6_HLEN + IP6_FRAG_HLEN, PBUF_RAM);
+    if (rambuf == NULL) {
+      IP6_FRAG_STATS_INC(ip6_frag.memerr);
+      return ERR_MEM;
+    }
+    LWIP_ASSERT("this needs a pbuf in one piece!",
+                (rambuf->len >= (IP6_HLEN)));
+    SMEMCPY(rambuf->payload, original_ip6hdr, IP6_HLEN);
+    ip6hdr = (struct ip6_hdr *)rambuf->payload;
+    frag_hdr = (struct ip6_frag_hdr *)((u8_t*)rambuf->payload + IP6_HLEN);
+
+    /* Can just adjust p directly for needed offset. */
+    p->payload = (u8_t *)p->payload + poff;
+    p->len = (u16_t)(p->len - poff);
+    p->tot_len = (u16_t)(p->tot_len - poff);
+
+    left_to_copy = cop;
+    while (left_to_copy) {
+      struct pbuf_custom_ref *pcr;
+      newpbuflen = (left_to_copy < p->len) ? left_to_copy : p->len;
+      /* Is this pbuf already empty? */
+      if (!newpbuflen) {
+        p = p->next;
+        continue;
+      }
+      pcr = ip6_frag_alloc_pbuf_custom_ref();
+      if (pcr == NULL) {
+        pbuf_free(rambuf);
+        IP6_FRAG_STATS_INC(ip6_frag.memerr);
+        return ERR_MEM;
+      }
+      /* Mirror this pbuf, although we might not need all of it. */
+      newpbuf = pbuf_alloced_custom(PBUF_RAW, newpbuflen, PBUF_REF, &pcr->pc, p->payload, newpbuflen);
+      if (newpbuf == NULL) {
+        ip6_frag_free_pbuf_custom_ref(pcr);
+        pbuf_free(rambuf);
+        IP6_FRAG_STATS_INC(ip6_frag.memerr);
+        return ERR_MEM;
+      }
+      pbuf_ref(p);
+      pcr->original = p;
+      pcr->pc.custom_free_function = ip6_frag_free_pbuf_custom;
+
+      /* Add it to end of rambuf's chain, but using pbuf_cat, not pbuf_chain
+       * so that it is removed when pbuf_dechain is later called on rambuf.
+       */
+      pbuf_cat(rambuf, newpbuf);
+      left_to_copy = (u16_t)(left_to_copy - newpbuflen);
+      if (left_to_copy) {
+        p = p->next;
+      }
+    }
+    poff = newpbuflen;
+#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
+
+    /* Set headers */
+    frag_hdr->_nexth = original_ip6hdr->_nexth;
+    frag_hdr->reserved = 0;
+    frag_hdr->_fragment_offset = lwip_htons((u16_t)((fragment_offset & IP6_FRAG_OFFSET_MASK) | (last ? 0 : IP6_FRAG_MORE_FLAG)));
+    frag_hdr->_identification = lwip_htonl(identification);
+
+    IP6H_NEXTH_SET(ip6hdr, IP6_NEXTH_FRAGMENT);
+    IP6H_PLEN_SET(ip6hdr, (u16_t)(cop + IP6_FRAG_HLEN));
+
+    /* No need for separate header pbuf - we allowed room for it in rambuf
+     * when allocated.
+     */
+    IP6_FRAG_STATS_INC(ip6_frag.xmit);
+    netif->output_ip6(netif, rambuf, dest);
+
+    /* Unfortunately we can't reuse rambuf - the hardware may still be
+     * using the buffer. Instead we free it (and the ensuing chain) and
+     * recreate it next time round the loop. If we're lucky the hardware
+     * will have already sent the packet, the free will really free, and
+     * there will be zero memory penalty.
+     */
+
+    pbuf_free(rambuf);
+    left = (u16_t)(left - cop);
+    fragment_offset = (u16_t)(fragment_offset + cop);
+  }
+  return ERR_OK;
+}
+
+#endif /* LWIP_IPV6 && LWIP_IPV6_FRAG */
diff --git a/lib/lwip/core/ipv6/mld6.c b/lib/lwip/core/ipv6/mld6.c
new file mode 100644
index 0000000000..ac4fb01249
--- /dev/null
+++ b/lib/lwip/core/ipv6/mld6.c
@@ -0,0 +1,626 @@
+/**
+ * @file
+ * Multicast listener discovery
+ *
+ * @defgroup mld6 MLD6
+ * @ingroup ip6
+ * Multicast listener discovery for IPv6. Aims to be compliant with RFC 2710.
+ * No support for MLDv2.<br>
+ * Note: The allnodes (ff01::1, ff02::1) group is assumed be received by your
+ * netif since it must always be received for correct IPv6 operation (e.g. SLAAC).
+ * Ensure the netif filters are configured accordingly!<br>
+ * The netif flags also need NETIF_FLAG_MLD6 flag set to enable MLD6 on a
+ * netif ("netif->flags |= NETIF_FLAG_MLD6;").<br>
+ * To be called from TCPIP thread.
+ */
+
+/*
+ * Copyright (c) 2010 Inico Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Ivan Delamer <delamer@inicotech.com>
+ *
+ *
+ * Please coordinate changes and requests with Ivan Delamer
+ * <delamer@inicotech.com>
+ */
+
+/* Based on igmp.c implementation of igmp v2 protocol */
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV6 && LWIP_IPV6_MLD  /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/mld6.h"
+#include "lwip/prot/mld6.h"
+#include "lwip/icmp6.h"
+#include "lwip/ip6.h"
+#include "lwip/ip6_addr.h"
+#include "lwip/ip.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/pbuf.h"
+#include "lwip/netif.h"
+#include "lwip/memp.h"
+#include "lwip/stats.h"
+
+#include <string.h>
+
+
+/*
+ * MLD constants
+ */
+#define MLD6_HL                           1
+#define MLD6_JOIN_DELAYING_MEMBER_TMR_MS  (500)
+
+#define MLD6_GROUP_NON_MEMBER             0
+#define MLD6_GROUP_DELAYING_MEMBER        1
+#define MLD6_GROUP_IDLE_MEMBER            2
+
+/* Forward declarations. */
+static struct mld_group *mld6_new_group(struct netif *ifp, const ip6_addr_t *addr);
+static err_t mld6_remove_group(struct netif *netif, struct mld_group *group);
+static void mld6_delayed_report(struct mld_group *group, u16_t maxresp);
+static void mld6_send(struct netif *netif, struct mld_group *group, u8_t type);
+
+
+/**
+ * Stop MLD processing on interface
+ *
+ * @param netif network interface on which stop MLD processing
+ */
+err_t
+mld6_stop(struct netif *netif)
+{
+  struct mld_group *group = netif_mld6_data(netif);
+
+  netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_MLD6, NULL);
+
+  while (group != NULL) {
+    struct mld_group *next = group->next; /* avoid use-after-free below */
+
+    /* disable the group at the MAC level */
+    if (netif->mld_mac_filter != NULL) {
+      netif->mld_mac_filter(netif, &(group->group_address), NETIF_DEL_MAC_FILTER);
+    }
+
+    /* free group */
+    memp_free(MEMP_MLD6_GROUP, group);
+
+    /* move to "next" */
+    group = next;
+  }
+  return ERR_OK;
+}
+
+/**
+ * Report MLD memberships for this interface
+ *
+ * @param netif network interface on which report MLD memberships
+ */
+void
+mld6_report_groups(struct netif *netif)
+{
+  struct mld_group *group = netif_mld6_data(netif);
+
+  while (group != NULL) {
+    mld6_delayed_report(group, MLD6_JOIN_DELAYING_MEMBER_TMR_MS);
+    group = group->next;
+  }
+}
+
+/**
+ * Search for a group that is joined on a netif
+ *
+ * @param ifp the network interface for which to look
+ * @param addr the group ipv6 address to search for
+ * @return a struct mld_group* if the group has been found,
+ *         NULL if the group wasn't found.
+ */
+struct mld_group *
+mld6_lookfor_group(struct netif *ifp, const ip6_addr_t *addr)
+{
+  struct mld_group *group = netif_mld6_data(ifp);
+
+  while (group != NULL) {
+    if (ip6_addr_eq(&(group->group_address), addr)) {
+      return group;
+    }
+    group = group->next;
+  }
+
+  return NULL;
+}
+
+
+/**
+ * create a new group
+ *
+ * @param ifp the network interface for which to create
+ * @param addr the new group ipv6
+ * @return a struct mld_group*,
+ *         NULL on memory error.
+ */
+static struct mld_group *
+mld6_new_group(struct netif *ifp, const ip6_addr_t *addr)
+{
+  struct mld_group *group;
+
+  group = (struct mld_group *)memp_malloc(MEMP_MLD6_GROUP);
+  if (group != NULL) {
+    ip6_addr_set(&(group->group_address), addr);
+    group->timer              = 0; /* Not running */
+    group->group_state        = MLD6_GROUP_IDLE_MEMBER;
+    group->last_reporter_flag = 0;
+    group->use                = 0;
+    group->next               = netif_mld6_data(ifp);
+
+    netif_set_client_data(ifp, LWIP_NETIF_CLIENT_DATA_INDEX_MLD6, group);
+  }
+
+  return group;
+}
+
+/**
+ * Remove a group from the mld_group_list, but do not free it yet
+ *
+ * @param group the group to remove
+ * @return ERR_OK if group was removed from the list, an err_t otherwise
+ */
+static err_t
+mld6_remove_group(struct netif *netif, struct mld_group *group)
+{
+  err_t err = ERR_OK;
+
+  /* Is it the first group? */
+  if (netif_mld6_data(netif) == group) {
+    netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_MLD6, group->next);
+  } else {
+    /* look for group further down the list */
+    struct mld_group *tmpGroup;
+    for (tmpGroup = netif_mld6_data(netif); tmpGroup != NULL; tmpGroup = tmpGroup->next) {
+      if (tmpGroup->next == group) {
+        tmpGroup->next = group->next;
+        break;
+      }
+    }
+    /* Group not find group */
+    if (tmpGroup == NULL) {
+      err = ERR_ARG;
+    }
+  }
+
+  return err;
+}
+
+
+/**
+ * Process an input MLD message. Called by icmp6_input.
+ *
+ * @param p the mld packet, p->payload pointing to the icmpv6 header
+ * @param inp the netif on which this packet was received
+ */
+void
+mld6_input(struct pbuf *p, struct netif *inp)
+{
+  struct mld_header *mld_hdr;
+  struct mld_group *group;
+
+  MLD6_STATS_INC(mld6.recv);
+
+  /* Check that mld header fits in packet. */
+  if (p->len < sizeof(struct mld_header)) {
+    /* @todo debug message */
+    pbuf_free(p);
+    MLD6_STATS_INC(mld6.lenerr);
+    MLD6_STATS_INC(mld6.drop);
+    return;
+  }
+
+  mld_hdr = (struct mld_header *)p->payload;
+
+  switch (mld_hdr->type) {
+  case ICMP6_TYPE_MLQ: /* Multicast listener query. */
+    /* Is it a general query? */
+    if (ip6_addr_isallnodes_linklocal(ip6_current_dest_addr()) &&
+        ip6_addr_isany(&(mld_hdr->multicast_address))) {
+      MLD6_STATS_INC(mld6.rx_general);
+      /* Report all groups, except all nodes group, and if-local groups. */
+      group = netif_mld6_data(inp);
+      while (group != NULL) {
+        if ((!(ip6_addr_ismulticast_iflocal(&(group->group_address)))) &&
+            (!(ip6_addr_isallnodes_linklocal(&(group->group_address))))) {
+          mld6_delayed_report(group, lwip_ntohs(mld_hdr->max_resp_delay));
+        }
+        group = group->next;
+      }
+    } else {
+      /* Have we joined this group?
+       * We use IP6 destination address to have a memory aligned copy.
+       * mld_hdr->multicast_address should be the same. */
+      MLD6_STATS_INC(mld6.rx_group);
+      group = mld6_lookfor_group(inp, ip6_current_dest_addr());
+      if (group != NULL) {
+        /* Schedule a report. */
+        mld6_delayed_report(group, lwip_ntohs(mld_hdr->max_resp_delay));
+      }
+    }
+    break; /* ICMP6_TYPE_MLQ */
+  case ICMP6_TYPE_MLR: /* Multicast listener report. */
+    /* Have we joined this group?
+     * We use IP6 destination address to have a memory aligned copy.
+     * mld_hdr->multicast_address should be the same. */
+    MLD6_STATS_INC(mld6.rx_report);
+    group = mld6_lookfor_group(inp, ip6_current_dest_addr());
+    if (group != NULL) {
+      /* If we are waiting to report, cancel it. */
+      if (group->group_state == MLD6_GROUP_DELAYING_MEMBER) {
+        group->timer = 0; /* stopped */
+        group->group_state = MLD6_GROUP_IDLE_MEMBER;
+        group->last_reporter_flag = 0;
+      }
+    }
+    break; /* ICMP6_TYPE_MLR */
+  case ICMP6_TYPE_MLD: /* Multicast listener done. */
+    /* Do nothing, router will query us. */
+    break; /* ICMP6_TYPE_MLD */
+  default:
+    MLD6_STATS_INC(mld6.proterr);
+    MLD6_STATS_INC(mld6.drop);
+    break;
+  }
+
+  pbuf_free(p);
+}
+
+/**
+ * @ingroup mld6
+ * Join a group on one or all network interfaces.
+ *
+ * If the group is to be joined on all interfaces, the given group address must
+ * not have a zone set (i.e., it must have its zone index set to IP6_NO_ZONE).
+ * If the group is to be joined on one particular interface, the given group
+ * address may or may not have a zone set.
+ *
+ * @param srcaddr ipv6 address (zoned) of the network interface which should
+ *                join a new group. If IP6_ADDR_ANY6, join on all netifs
+ * @param groupaddr the ipv6 address of the group to join (possibly but not
+ *                  necessarily zoned)
+ * @return ERR_OK if group was joined on the netif(s), an err_t otherwise
+ */
+err_t
+mld6_joingroup(const ip6_addr_t *srcaddr, const ip6_addr_t *groupaddr)
+{
+  err_t         err = ERR_VAL; /* no matching interface */
+  struct netif *netif;
+
+  LWIP_ASSERT_CORE_LOCKED();
+
+  /* loop through netif's */
+  NETIF_FOREACH(netif) {
+    /* Should we join this interface ? */
+    if (ip6_addr_isany(srcaddr) ||
+        netif_get_ip6_addr_match(netif, srcaddr) >= 0) {
+      err = mld6_joingroup_netif(netif, groupaddr);
+      if (err != ERR_OK) {
+        return err;
+      }
+    }
+  }
+
+  return err;
+}
+
+/**
+ * @ingroup mld6
+ * Join a group on a network interface.
+ *
+ * @param netif the network interface which should join a new group.
+ * @param groupaddr the ipv6 address of the group to join (possibly but not
+ *                  necessarily zoned)
+ * @return ERR_OK if group was joined on the netif, an err_t otherwise
+ */
+err_t
+mld6_joingroup_netif(struct netif *netif, const ip6_addr_t *groupaddr)
+{
+  struct mld_group *group;
+#if LWIP_IPV6_SCOPES
+  ip6_addr_t ip6addr;
+
+  /* If the address has a particular scope but no zone set, use the netif to
+   * set one now. Within the mld6 module, all addresses are properly zoned. */
+  if (ip6_addr_lacks_zone(groupaddr, IP6_MULTICAST)) {
+    ip6_addr_set(&ip6addr, groupaddr);
+    ip6_addr_assign_zone(&ip6addr, IP6_MULTICAST, netif);
+    groupaddr = &ip6addr;
+  }
+  IP6_ADDR_ZONECHECK_NETIF(groupaddr, netif);
+#endif /* LWIP_IPV6_SCOPES */
+
+  LWIP_ASSERT_CORE_LOCKED();
+
+  /* find group or create a new one if not found */
+  group = mld6_lookfor_group(netif, groupaddr);
+
+  if (group == NULL) {
+    /* Joining a new group. Create a new group entry. */
+    group = mld6_new_group(netif, groupaddr);
+    if (group == NULL) {
+      return ERR_MEM;
+    }
+
+    /* Activate this address on the MAC layer. */
+    if (netif->mld_mac_filter != NULL) {
+      netif->mld_mac_filter(netif, groupaddr, NETIF_ADD_MAC_FILTER);
+    }
+
+    /* Report our membership. */
+    MLD6_STATS_INC(mld6.tx_report);
+    mld6_send(netif, group, ICMP6_TYPE_MLR);
+    mld6_delayed_report(group, MLD6_JOIN_DELAYING_MEMBER_TMR_MS);
+  }
+
+  /* Increment group use */
+  group->use++;
+  return ERR_OK;
+}
+
+/**
+ * @ingroup mld6
+ * Leave a group on a network interface.
+ *
+ * Zoning of address follows the same rules as @ref mld6_joingroup.
+ *
+ * @param srcaddr ipv6 address (zoned) of the network interface which should
+ *                leave the group. If IP6_ADDR_ANY6, leave on all netifs
+ * @param groupaddr the ipv6 address of the group to leave (possibly, but not
+ *                  necessarily zoned)
+ * @return ERR_OK if group was left on the netif(s), an err_t otherwise
+ */
+err_t
+mld6_leavegroup(const ip6_addr_t *srcaddr, const ip6_addr_t *groupaddr)
+{
+  err_t         err = ERR_VAL; /* no matching interface */
+  struct netif *netif;
+
+  LWIP_ASSERT_CORE_LOCKED();
+
+  /* loop through netif's */
+  NETIF_FOREACH(netif) {
+    /* Should we leave this interface ? */
+    if (ip6_addr_isany(srcaddr) ||
+        netif_get_ip6_addr_match(netif, srcaddr) >= 0) {
+      err_t res = mld6_leavegroup_netif(netif, groupaddr);
+      if (err != ERR_OK) {
+        /* Store this result if we have not yet gotten a success */
+        err = res;
+      }
+    }
+  }
+
+  return err;
+}
+
+/**
+ * @ingroup mld6
+ * Leave a group on a network interface.
+ *
+ * @param netif the network interface which should leave the group.
+ * @param groupaddr the ipv6 address of the group to leave (possibly, but not
+ *                  necessarily zoned)
+ * @return ERR_OK if group was left on the netif, an err_t otherwise
+ */
+err_t
+mld6_leavegroup_netif(struct netif *netif, const ip6_addr_t *groupaddr)
+{
+  struct mld_group *group;
+#if LWIP_IPV6_SCOPES
+  ip6_addr_t ip6addr;
+
+  if (ip6_addr_lacks_zone(groupaddr, IP6_MULTICAST)) {
+    ip6_addr_set(&ip6addr, groupaddr);
+    ip6_addr_assign_zone(&ip6addr, IP6_MULTICAST, netif);
+    groupaddr = &ip6addr;
+  }
+  IP6_ADDR_ZONECHECK_NETIF(groupaddr, netif);
+#endif /* LWIP_IPV6_SCOPES */
+
+  LWIP_ASSERT_CORE_LOCKED();
+
+  /* find group */
+  group = mld6_lookfor_group(netif, groupaddr);
+
+  if (group != NULL) {
+    /* Leave if there is no other use of the group */
+    if (group->use <= 1) {
+      /* Remove the group from the list */
+      mld6_remove_group(netif, group);
+
+      /* If we are the last reporter for this group */
+      if (group->last_reporter_flag) {
+        MLD6_STATS_INC(mld6.tx_leave);
+        mld6_send(netif, group, ICMP6_TYPE_MLD);
+      }
+
+      /* Disable the group at the MAC level */
+      if (netif->mld_mac_filter != NULL) {
+        netif->mld_mac_filter(netif, groupaddr, NETIF_DEL_MAC_FILTER);
+      }
+
+      /* free group struct */
+      memp_free(MEMP_MLD6_GROUP, group);
+    } else {
+      /* Decrement group use */
+      group->use--;
+    }
+
+    /* Left group */
+    return ERR_OK;
+  }
+
+  /* Group not found */
+  return ERR_VAL;
+}
+
+
+/**
+ * Periodic timer for mld processing. Must be called every
+ * MLD6_TMR_INTERVAL milliseconds (100).
+ *
+ * When a delaying member expires, a membership report is sent.
+ */
+void
+mld6_tmr(void)
+{
+  struct netif *netif;
+
+  NETIF_FOREACH(netif) {
+    struct mld_group *group = netif_mld6_data(netif);
+
+    while (group != NULL) {
+      if (group->timer > 0) {
+        group->timer--;
+        if (group->timer == 0) {
+          /* If the state is MLD6_GROUP_DELAYING_MEMBER then we send a report for this group */
+          if (group->group_state == MLD6_GROUP_DELAYING_MEMBER) {
+            MLD6_STATS_INC(mld6.tx_report);
+            mld6_send(netif, group, ICMP6_TYPE_MLR);
+            group->group_state = MLD6_GROUP_IDLE_MEMBER;
+          }
+        }
+      }
+      group = group->next;
+    }
+  }
+}
+
+/**
+ * Schedule a delayed membership report for a group
+ *
+ * @param group the mld_group for which "delaying" membership report
+ *              should be sent
+ * @param maxresp_in the max resp delay provided in the query
+ */
+static void
+mld6_delayed_report(struct mld_group *group, u16_t maxresp_in)
+{
+  /* Convert maxresp from milliseconds to tmr ticks */
+  u16_t maxresp = maxresp_in / MLD6_TMR_INTERVAL;
+  if (maxresp == 0) {
+    maxresp = 1;
+  }
+
+#ifdef LWIP_RAND
+  /* Randomize maxresp. (if LWIP_RAND is supported) */
+  maxresp = (u16_t)(LWIP_RAND() % maxresp);
+  if (maxresp == 0) {
+    maxresp = 1;
+  }
+#endif /* LWIP_RAND */
+
+  /* Apply timer value if no report has been scheduled already. */
+  if ((group->group_state == MLD6_GROUP_IDLE_MEMBER) ||
+     ((group->group_state == MLD6_GROUP_DELAYING_MEMBER) &&
+      ((group->timer == 0) || (maxresp < group->timer)))) {
+    group->timer = maxresp;
+    group->group_state = MLD6_GROUP_DELAYING_MEMBER;
+  }
+}
+
+/**
+ * Send a MLD message (report or done).
+ *
+ * An IPv6 hop-by-hop options header with a router alert option
+ * is prepended.
+ *
+ * @param group the group to report or quit
+ * @param type ICMP6_TYPE_MLR (report) or ICMP6_TYPE_MLD (done)
+ */
+static void
+mld6_send(struct netif *netif, struct mld_group *group, u8_t type)
+{
+  struct mld_header *mld_hdr;
+  struct pbuf *p;
+  const ip6_addr_t *src_addr;
+
+  /* Allocate a packet. Size is MLD header + IPv6 Hop-by-hop options header. */
+  p = pbuf_alloc(PBUF_IP, sizeof(struct mld_header) + MLD6_HBH_HLEN, PBUF_RAM);
+  if (p == NULL) {
+    MLD6_STATS_INC(mld6.memerr);
+    return;
+  }
+
+  /* Move to make room for Hop-by-hop options header. */
+  if (pbuf_remove_header(p, MLD6_HBH_HLEN)) {
+    pbuf_free(p);
+    MLD6_STATS_INC(mld6.lenerr);
+    return;
+  }
+
+  /* Select our source address. */
+  if (!ip6_addr_isvalid(netif_ip6_addr_state(netif, 0))) {
+    /* This is a special case, when we are performing duplicate address detection.
+     * We must join the multicast group, but we don't have a valid address yet. */
+    src_addr = IP6_ADDR_ANY6;
+  } else {
+    /* Use link-local address as source address. */
+    src_addr = netif_ip6_addr(netif, 0);
+  }
+
+  /* MLD message header pointer. */
+  mld_hdr = (struct mld_header *)p->payload;
+
+  /* Set fields. */
+  mld_hdr->type = type;
+  mld_hdr->code = 0;
+  mld_hdr->chksum = 0;
+  mld_hdr->max_resp_delay = 0;
+  mld_hdr->reserved = 0;
+  ip6_addr_copy_to_packed(mld_hdr->multicast_address, group->group_address);
+
+#if CHECKSUM_GEN_ICMP6
+  IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_ICMP6) {
+    mld_hdr->chksum = ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->len,
+      src_addr, &(group->group_address));
+  }
+#endif /* CHECKSUM_GEN_ICMP6 */
+
+  /* Add hop-by-hop headers options: router alert with MLD value. */
+  ip6_options_add_hbh_ra(p, IP6_NEXTH_ICMP6, IP6_ROUTER_ALERT_VALUE_MLD);
+
+  if (type == ICMP6_TYPE_MLR) {
+    /* Remember we were the last to report */
+    group->last_reporter_flag = 1;
+  }
+
+  /* Send the packet out. */
+  MLD6_STATS_INC(mld6.xmit);
+  ip6_output_if(p, (ip6_addr_isany(src_addr)) ? NULL : src_addr, &(group->group_address),
+      MLD6_HL, 0, IP6_NEXTH_HOPBYHOP, netif);
+  pbuf_free(p);
+}
+
+#endif /* LWIP_IPV6 */
diff --git a/lib/lwip/core/ipv6/nd6.c b/lib/lwip/core/ipv6/nd6.c
new file mode 100644
index 0000000000..3b13c21ec6
--- /dev/null
+++ b/lib/lwip/core/ipv6/nd6.c
@@ -0,0 +1,2474 @@
+/**
+ * @file
+ *
+ * Neighbor discovery and stateless address autoconfiguration for IPv6.
+ * Aims to be compliant with RFC 4861 (Neighbor discovery) and RFC 4862
+ * (Address autoconfiguration).
+ */
+
+/*
+ * Copyright (c) 2010 Inico Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Ivan Delamer <delamer@inicotech.com>
+ *
+ *
+ * Please coordinate changes and requests with Ivan Delamer
+ * <delamer@inicotech.com>
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV6  /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/nd6.h"
+#include "lwip/priv/nd6_priv.h"
+#include "lwip/prot/nd6.h"
+#include "lwip/prot/icmp6.h"
+#include "lwip/pbuf.h"
+#include "lwip/mem.h"
+#include "lwip/memp.h"
+#include "lwip/ip6.h"
+#include "lwip/ip6_addr.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/netif.h"
+#include "lwip/icmp6.h"
+#include "lwip/mld6.h"
+#include "lwip/dhcp6.h"
+#include "lwip/ip.h"
+#include "lwip/stats.h"
+#include "lwip/dns.h"
+
+#include <string.h>
+
+#ifdef LWIP_HOOK_FILENAME
+#include LWIP_HOOK_FILENAME
+#endif
+
+#if LWIP_IPV6_DUP_DETECT_ATTEMPTS > IP6_ADDR_TENTATIVE_COUNT_MASK
+#error LWIP_IPV6_DUP_DETECT_ATTEMPTS > IP6_ADDR_TENTATIVE_COUNT_MASK
+#endif
+#if LWIP_ND6_NUM_NEIGHBORS > 127
+#error LWIP_ND6_NUM_NEIGHBORS must fit into an s8_t (max value: 127)
+#endif
+#if LWIP_ND6_NUM_DESTINATIONS > 32767
+#error LWIP_ND6_NUM_DESTINATIONS must fit into an s16_t (max value: 32767)
+#endif
+#if LWIP_ND6_NUM_PREFIXES > 127
+#error LWIP_ND6_NUM_PREFIXES must fit into an s8_t (max value: 127)
+#endif
+#if LWIP_ND6_NUM_ROUTERS > 127
+#error LWIP_ND6_NUM_ROUTERS must fit into an s8_t (max value: 127)
+#endif
+
+/* Router tables. */
+struct nd6_neighbor_cache_entry neighbor_cache[LWIP_ND6_NUM_NEIGHBORS];
+struct nd6_destination_cache_entry destination_cache[LWIP_ND6_NUM_DESTINATIONS];
+struct nd6_prefix_list_entry prefix_list[LWIP_ND6_NUM_PREFIXES];
+struct nd6_router_list_entry default_router_list[LWIP_ND6_NUM_ROUTERS];
+
+/* Default values, can be updated by a RA message. */
+u32_t reachable_time = LWIP_ND6_REACHABLE_TIME;
+u32_t retrans_timer = LWIP_ND6_RETRANS_TIMER; /* @todo implement this value in timer */
+
+#if LWIP_ND6_QUEUEING
+static u8_t nd6_queue_size = 0;
+#endif
+
+/* Index for cache entries. */
+static netif_addr_idx_t nd6_cached_destination_index;
+
+/* Multicast address holder. */
+static ip6_addr_t multicast_address;
+
+static u8_t nd6_tmr_rs_reduction;
+
+/* Static buffer to parse RA packet options */
+union ra_options {
+  struct lladdr_option  lladdr;
+  struct mtu_option     mtu;
+  struct prefix_option  prefix;
+#if LWIP_ND6_RDNSS_MAX_DNS_SERVERS
+  struct rdnss_option   rdnss;
+#endif
+};
+static union ra_options nd6_ra_buffer;
+
+/* Forward declarations. */
+static s8_t nd6_find_neighbor_cache_entry(const ip6_addr_t *ip6addr);
+static s8_t nd6_new_neighbor_cache_entry(void);
+static void nd6_free_neighbor_cache_entry(s8_t i);
+static s16_t nd6_find_destination_cache_entry(const ip6_addr_t *ip6addr);
+static s16_t nd6_new_destination_cache_entry(void);
+static int nd6_is_prefix_in_netif(const ip6_addr_t *ip6addr, struct netif *netif);
+static s8_t nd6_select_router(const ip6_addr_t *ip6addr, struct netif *netif);
+static s8_t nd6_get_router(const ip6_addr_t *router_addr, struct netif *netif);
+static s8_t nd6_new_router(const ip6_addr_t *router_addr, struct netif *netif);
+static s8_t nd6_get_onlink_prefix(const ip6_addr_t *prefix, struct netif *netif);
+static s8_t nd6_new_onlink_prefix(const ip6_addr_t *prefix, struct netif *netif);
+static s8_t nd6_get_next_hop_entry(const ip6_addr_t *ip6addr, struct netif *netif);
+static err_t nd6_queue_packet(s8_t neighbor_index, struct pbuf *q);
+
+#define ND6_SEND_FLAG_MULTICAST_DEST 0x01
+#define ND6_SEND_FLAG_ALLNODES_DEST 0x02
+#define ND6_SEND_FLAG_ANY_SRC 0x04
+static void nd6_send_ns(struct netif *netif, const ip6_addr_t *target_addr, u8_t flags);
+static void nd6_send_na(struct netif *netif, const ip6_addr_t *target_addr, u8_t flags);
+static void nd6_send_neighbor_cache_probe(struct nd6_neighbor_cache_entry *entry, u8_t flags);
+#if LWIP_IPV6_SEND_ROUTER_SOLICIT
+static err_t nd6_send_rs(struct netif *netif);
+#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */
+
+#if LWIP_ND6_QUEUEING
+static void nd6_free_q(struct nd6_q_entry *q);
+#else /* LWIP_ND6_QUEUEING */
+#define nd6_free_q(q) pbuf_free(q)
+#endif /* LWIP_ND6_QUEUEING */
+static void nd6_send_q(s8_t i);
+
+
+/**
+ * A local address has been determined to be a duplicate. Take the appropriate
+ * action(s) on the address and the interface as a whole.
+ *
+ * @param netif the netif that owns the address
+ * @param addr_idx the index of the address detected to be a duplicate
+ */
+static void
+nd6_duplicate_addr_detected(struct netif *netif, s8_t addr_idx)
+{
+
+  /* Mark the address as duplicate, but leave its lifetimes alone. If this was
+   * a manually assigned address, it will remain in existence as duplicate, and
+   * as such be unusable for any practical purposes until manual intervention.
+   * If this was an autogenerated address, the address will follow normal
+   * expiration rules, and thus disappear once its valid lifetime expires. */
+  netif_ip6_addr_set_state(netif, addr_idx, IP6_ADDR_DUPLICATED);
+
+#if LWIP_IPV6_AUTOCONFIG
+  /* If the affected address was the link-local address that we use to generate
+   * all other addresses, then we should not continue to use those derived
+   * addresses either, so mark them as duplicate as well. For autoconfig-only
+   * setups, this will make the interface effectively unusable, approaching the
+   * intention of RFC 4862 Sec. 5.4.5. @todo implement the full requirements */
+  if (addr_idx == 0) {
+    s8_t i;
+    for (i = 1; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+      if (!ip6_addr_isinvalid(netif_ip6_addr_state(netif, i)) &&
+          !netif_ip6_addr_isstatic(netif, i)) {
+        netif_ip6_addr_set_state(netif, i, IP6_ADDR_DUPLICATED);
+      }
+    }
+  }
+#endif /* LWIP_IPV6_AUTOCONFIG */
+}
+
+#if LWIP_IPV6_AUTOCONFIG
+/**
+ * We received a router advertisement that contains a prefix with the
+ * autoconfiguration flag set. Add or update an associated autogenerated
+ * address.
+ *
+ * @param netif the netif on which the router advertisement arrived
+ * @param prefix_opt a pointer to the prefix option data
+ * @param prefix_addr an aligned copy of the prefix address
+ */
+static void
+nd6_process_autoconfig_prefix(struct netif *netif,
+  struct prefix_option *prefix_opt, const ip6_addr_t *prefix_addr)
+{
+  ip6_addr_t ip6addr;
+  u32_t valid_life, pref_life;
+  u8_t addr_state;
+  s8_t i, free_idx;
+
+  /* The caller already checks RFC 4862 Sec. 5.5.3 points (a) and (b). We do
+   * the rest, starting with checks for (c) and (d) here. */
+  valid_life = lwip_htonl(prefix_opt->valid_lifetime);
+  pref_life = lwip_htonl(prefix_opt->preferred_lifetime);
+  if (pref_life > valid_life || prefix_opt->prefix_length != 64) {
+    return; /* silently ignore this prefix for autoconfiguration purposes */
+  }
+
+  /* If an autogenerated address already exists for this prefix, update its
+   * lifetimes. An address is considered autogenerated if 1) it is not static
+   * (i.e., manually assigned), and 2) there is an advertised autoconfiguration
+   * prefix for it (the one we are processing here). This does not necessarily
+   * exclude the possibility that the address was actually assigned by, say,
+   * DHCPv6. If that distinction becomes important in the future, more state
+   * must be kept. As explained elsewhere we also update lifetimes of tentative
+   * and duplicate addresses. Skip address slot 0 (the link-local address). */
+  for (i = 1; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+    addr_state = netif_ip6_addr_state(netif, i);
+    if (!ip6_addr_isinvalid(addr_state) && !netif_ip6_addr_isstatic(netif, i) &&
+        ip6_addr_net_eq(prefix_addr, netif_ip6_addr(netif, i))) {
+      /* Update the valid lifetime, as per RFC 4862 Sec. 5.5.3 point (e).
+       * The valid lifetime will never drop to zero as a result of this. */
+      u32_t remaining_life = netif_ip6_addr_valid_life(netif, i);
+      if (valid_life > ND6_2HRS || valid_life > remaining_life) {
+        netif_ip6_addr_set_valid_life(netif, i, valid_life);
+      } else if (remaining_life > ND6_2HRS) {
+        netif_ip6_addr_set_valid_life(netif, i, ND6_2HRS);
+      }
+      LWIP_ASSERT("bad valid lifetime", !netif_ip6_addr_isstatic(netif, i));
+      /* Update the preferred lifetime. No bounds checks are needed here. In
+       * rare cases the advertisement may un-deprecate the address, though.
+       * Deprecation is left to the timer code where it is handled anyway. */
+      if (pref_life > 0 && addr_state == IP6_ADDR_DEPRECATED) {
+        netif_ip6_addr_set_state(netif, i, IP6_ADDR_PREFERRED);
+      }
+      netif_ip6_addr_set_pref_life(netif, i, pref_life);
+      return; /* there should be at most one matching address */
+    }
+  }
+
+  /* No autogenerated address exists for this prefix yet. See if we can add a
+   * new one. However, if IPv6 autoconfiguration is administratively disabled,
+   * do not generate new addresses, but do keep updating lifetimes for existing
+   * addresses. Also, when adding new addresses, we must protect explicitly
+   * against a valid lifetime of zero, because again, we use that as a special
+   * value. The generated address would otherwise expire immediately anyway.
+   * Finally, the original link-local address must be usable at all. We start
+   * creating addresses even if the link-local address is still in tentative
+   * state though, and deal with the fallout of that upon DAD collision. */
+  addr_state = netif_ip6_addr_state(netif, 0);
+  if (!netif->ip6_autoconfig_enabled || valid_life == IP6_ADDR_LIFE_STATIC ||
+      ip6_addr_isinvalid(addr_state) || ip6_addr_isduplicated(addr_state)) {
+    return;
+  }
+
+  /* Construct the new address that we intend to use, and then see if that
+   * address really does not exist. It might have been added manually, after
+   * all. As a side effect, find a free slot. Note that we cannot use
+   * netif_add_ip6_address() here, as it would return ERR_OK if the address
+   * already did exist, resulting in that address being given lifetimes. */
+  IP6_ADDR(&ip6addr, prefix_addr->addr[0], prefix_addr->addr[1],
+    netif_ip6_addr(netif, 0)->addr[2], netif_ip6_addr(netif, 0)->addr[3]);
+  ip6_addr_assign_zone(&ip6addr, IP6_UNICAST, netif);
+
+  free_idx = 0;
+  for (i = 1; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+    if (!ip6_addr_isinvalid(netif_ip6_addr_state(netif, i))) {
+      if (ip6_addr_eq(&ip6addr, netif_ip6_addr(netif, i))) {
+        return; /* formed address already exists */
+      }
+    } else if (free_idx == 0) {
+      free_idx = i;
+    }
+  }
+  if (free_idx == 0) {
+    return; /* no address slots available, try again on next advertisement */
+  }
+
+  /* Assign the new address to the interface. */
+  ip_addr_copy_from_ip6(netif->ip6_addr[free_idx], ip6addr);
+  netif_ip6_addr_set_valid_life(netif, free_idx, valid_life);
+  netif_ip6_addr_set_pref_life(netif, free_idx, pref_life);
+  netif_ip6_addr_set_state(netif, free_idx, IP6_ADDR_TENTATIVE);
+}
+#endif /* LWIP_IPV6_AUTOCONFIG */
+
+/**
+ * Process an incoming neighbor discovery message
+ *
+ * @param p the nd packet, p->payload pointing to the icmpv6 header
+ * @param inp the netif on which this packet was received
+ */
+void
+nd6_input(struct pbuf *p, struct netif *inp)
+{
+  u8_t msg_type;
+  s8_t i;
+  s16_t dest_idx;
+
+  ND6_STATS_INC(nd6.recv);
+
+  msg_type = *((u8_t *)p->payload);
+  switch (msg_type) {
+  case ICMP6_TYPE_NA: /* Neighbor Advertisement. */
+  {
+    struct na_header *na_hdr;
+    struct lladdr_option *lladdr_opt;
+    ip6_addr_t target_address;
+
+    /* Check that na header fits in packet. */
+    if (p->len < (sizeof(struct na_header))) {
+      /* @todo debug message */
+      pbuf_free(p);
+      ND6_STATS_INC(nd6.lenerr);
+      ND6_STATS_INC(nd6.drop);
+      return;
+    }
+
+    na_hdr = (struct na_header *)p->payload;
+
+    /* Create an aligned, zoned copy of the target address. */
+    ip6_addr_copy_from_packed(target_address, na_hdr->target_address);
+    ip6_addr_assign_zone(&target_address, IP6_UNICAST, inp);
+
+    /* Check a subset of the other RFC 4861 Sec. 7.1.2 requirements. */
+    if (IP6H_HOPLIM(ip6_current_header()) != ND6_HOPLIM || na_hdr->code != 0 ||
+        ip6_addr_ismulticast(&target_address)) {
+      pbuf_free(p);
+      ND6_STATS_INC(nd6.proterr);
+      ND6_STATS_INC(nd6.drop);
+      return;
+    }
+
+    /* @todo RFC MUST: if IP destination is multicast, Solicited flag is zero */
+    /* @todo RFC MUST: all included options have a length greater than zero */
+
+    /* Unsolicited NA?*/
+    if (ip6_addr_ismulticast(ip6_current_dest_addr())) {
+      /* This is an unsolicited NA.
+       * link-layer changed?
+       * part of DAD mechanism? */
+
+#if LWIP_IPV6_DUP_DETECT_ATTEMPTS
+      /* If the target address matches this netif, it is a DAD response. */
+      for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+        if (!ip6_addr_isinvalid(netif_ip6_addr_state(inp, i)) &&
+            !ip6_addr_isduplicated(netif_ip6_addr_state(inp, i)) &&
+            ip6_addr_eq(&target_address, netif_ip6_addr(inp, i))) {
+          /* We are using a duplicate address. */
+          nd6_duplicate_addr_detected(inp, i);
+
+          pbuf_free(p);
+          return;
+        }
+      }
+#endif /* LWIP_IPV6_DUP_DETECT_ATTEMPTS */
+
+      /* Check that link-layer address option also fits in packet. */
+      if (p->len < (sizeof(struct na_header) + 2)) {
+        /* @todo debug message */
+        pbuf_free(p);
+        ND6_STATS_INC(nd6.lenerr);
+        ND6_STATS_INC(nd6.drop);
+        return;
+      }
+
+      lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct na_header));
+
+      if (p->len < (sizeof(struct na_header) + (lladdr_opt->length << 3))) {
+        /* @todo debug message */
+        pbuf_free(p);
+        ND6_STATS_INC(nd6.lenerr);
+        ND6_STATS_INC(nd6.drop);
+        return;
+      }
+
+      /* This is an unsolicited NA, most likely there was a LLADDR change. */
+      i = nd6_find_neighbor_cache_entry(&target_address);
+      if (i >= 0) {
+        if (na_hdr->flags & ND6_FLAG_OVERRIDE) {
+          MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len);
+        }
+      }
+    } else {
+      /* This is a solicited NA.
+       * neighbor address resolution response?
+       * neighbor unreachability detection response? */
+
+      /* Find the cache entry corresponding to this na. */
+      i = nd6_find_neighbor_cache_entry(&target_address);
+      if (i < 0) {
+        /* We no longer care about this target address. drop it. */
+        pbuf_free(p);
+        return;
+      }
+
+      /* Update cache entry. */
+      if ((na_hdr->flags & ND6_FLAG_OVERRIDE) ||
+          (neighbor_cache[i].state == ND6_INCOMPLETE)) {
+        /* Check that link-layer address option also fits in packet. */
+        if (p->len < (sizeof(struct na_header) + 2)) {
+          /* @todo debug message */
+          pbuf_free(p);
+          ND6_STATS_INC(nd6.lenerr);
+          ND6_STATS_INC(nd6.drop);
+          return;
+        }
+
+        lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct na_header));
+
+        if (p->len < (sizeof(struct na_header) + (lladdr_opt->length << 3))) {
+          /* @todo debug message */
+          pbuf_free(p);
+          ND6_STATS_INC(nd6.lenerr);
+          ND6_STATS_INC(nd6.drop);
+          return;
+        }
+
+        MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len);
+      }
+
+      neighbor_cache[i].netif = inp;
+      neighbor_cache[i].state = ND6_REACHABLE;
+      neighbor_cache[i].counter.reachable_time = reachable_time;
+
+      /* Send queued packets, if any. */
+      if (neighbor_cache[i].q != NULL) {
+        nd6_send_q(i);
+      }
+    }
+
+    break; /* ICMP6_TYPE_NA */
+  }
+  case ICMP6_TYPE_NS: /* Neighbor solicitation. */
+  {
+    struct ns_header *ns_hdr;
+    struct lladdr_option *lladdr_opt;
+    ip6_addr_t target_address;
+    u8_t accepted;
+
+    /* Check that ns header fits in packet. */
+    if (p->len < sizeof(struct ns_header)) {
+      /* @todo debug message */
+      pbuf_free(p);
+      ND6_STATS_INC(nd6.lenerr);
+      ND6_STATS_INC(nd6.drop);
+      return;
+    }
+
+    ns_hdr = (struct ns_header *)p->payload;
+
+    /* Create an aligned, zoned copy of the target address. */
+    ip6_addr_copy_from_packed(target_address, ns_hdr->target_address);
+    ip6_addr_assign_zone(&target_address, IP6_UNICAST, inp);
+
+    /* Check a subset of the other RFC 4861 Sec. 7.1.1 requirements. */
+    if (IP6H_HOPLIM(ip6_current_header()) != ND6_HOPLIM || ns_hdr->code != 0 ||
+       ip6_addr_ismulticast(&target_address)) {
+      pbuf_free(p);
+      ND6_STATS_INC(nd6.proterr);
+      ND6_STATS_INC(nd6.drop);
+      return;
+    }
+
+    /* @todo RFC MUST: all included options have a length greater than zero */
+    /* @todo RFC MUST: if IP source is 'any', destination is solicited-node multicast address */
+    /* @todo RFC MUST: if IP source is 'any', there is no source LL address option */
+
+    /* Check if there is a link-layer address provided. Only point to it if in this buffer. */
+    if (p->len >= (sizeof(struct ns_header) + 2)) {
+      lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct ns_header));
+      if (p->len < (sizeof(struct ns_header) + (lladdr_opt->length << 3))) {
+        lladdr_opt = NULL;
+      }
+    } else {
+      lladdr_opt = NULL;
+    }
+
+    /* Check if the target address is configured on the receiving netif. */
+    accepted = 0;
+    for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; ++i) {
+      if ((ip6_addr_isvalid(netif_ip6_addr_state(inp, i)) ||
+           (ip6_addr_istentative(netif_ip6_addr_state(inp, i)) &&
+            ip6_addr_isany(ip6_current_src_addr()))) &&
+          ip6_addr_eq(&target_address, netif_ip6_addr(inp, i))) {
+        accepted = 1;
+        break;
+      }
+    }
+
+    /* NS not for us? */
+    if (!accepted) {
+      pbuf_free(p);
+      return;
+    }
+
+    /* Check for ANY address in src (DAD algorithm). */
+    if (ip6_addr_isany(ip6_current_src_addr())) {
+      /* Sender is validating this address. */
+      for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; ++i) {
+        if (!ip6_addr_isinvalid(netif_ip6_addr_state(inp, i)) &&
+            ip6_addr_eq(&target_address, netif_ip6_addr(inp, i))) {
+          /* Send a NA back so that the sender does not use this address. */
+          nd6_send_na(inp, netif_ip6_addr(inp, i), ND6_FLAG_OVERRIDE | ND6_SEND_FLAG_ALLNODES_DEST);
+          if (ip6_addr_istentative(netif_ip6_addr_state(inp, i))) {
+            /* We shouldn't use this address either. */
+            nd6_duplicate_addr_detected(inp, i);
+          }
+        }
+      }
+    } else {
+      /* Sender is trying to resolve our address. */
+      /* Verify that they included their own link-layer address. */
+      if (lladdr_opt == NULL) {
+        /* Not a valid message. */
+        pbuf_free(p);
+        ND6_STATS_INC(nd6.proterr);
+        ND6_STATS_INC(nd6.drop);
+        return;
+      }
+
+      i = nd6_find_neighbor_cache_entry(ip6_current_src_addr());
+      if (i>= 0) {
+        /* We already have a record for the solicitor. */
+        if (neighbor_cache[i].state == ND6_INCOMPLETE) {
+          neighbor_cache[i].netif = inp;
+          MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len);
+
+          /* Delay probe in case we get confirmation of reachability from upper layer (TCP). */
+          neighbor_cache[i].state = ND6_DELAY;
+          neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME / ND6_TMR_INTERVAL;
+        }
+      } else {
+        /* Add their IPv6 address and link-layer address to neighbor cache.
+         * We will need it at least to send a unicast NA message, but most
+         * likely we will also be communicating with this node soon. */
+        i = nd6_new_neighbor_cache_entry();
+        if (i < 0) {
+          /* We couldn't assign a cache entry for this neighbor.
+           * we won't be able to reply. drop it. */
+          pbuf_free(p);
+          ND6_STATS_INC(nd6.memerr);
+          return;
+        }
+        neighbor_cache[i].netif = inp;
+        MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len);
+        ip6_addr_set(&(neighbor_cache[i].next_hop_address), ip6_current_src_addr());
+
+        /* Receiving a message does not prove reachability: only in one direction.
+         * Delay probe in case we get confirmation of reachability from upper layer (TCP). */
+        neighbor_cache[i].state = ND6_DELAY;
+        neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME / ND6_TMR_INTERVAL;
+      }
+
+      /* Send back a NA for us. Allocate the reply pbuf. */
+      nd6_send_na(inp, &target_address, ND6_FLAG_SOLICITED | ND6_FLAG_OVERRIDE);
+    }
+
+    break; /* ICMP6_TYPE_NS */
+  }
+  case ICMP6_TYPE_RA: /* Router Advertisement. */
+  {
+    struct ra_header *ra_hdr;
+    u8_t *buffer; /* Used to copy options. */
+    u16_t offset;
+#if LWIP_ND6_RDNSS_MAX_DNS_SERVERS
+    /* There can be multiple RDNSS options per RA */
+    u8_t rdnss_server_idx = 0;
+#endif /* LWIP_ND6_RDNSS_MAX_DNS_SERVERS */
+
+    /* Check that RA header fits in packet. */
+    if (p->len < sizeof(struct ra_header)) {
+      /* @todo debug message */
+      pbuf_free(p);
+      ND6_STATS_INC(nd6.lenerr);
+      ND6_STATS_INC(nd6.drop);
+      return;
+    }
+
+    ra_hdr = (struct ra_header *)p->payload;
+
+    /* Check a subset of the other RFC 4861 Sec. 6.1.2 requirements. */
+    if (!ip6_addr_islinklocal(ip6_current_src_addr()) ||
+        IP6H_HOPLIM(ip6_current_header()) != ND6_HOPLIM || ra_hdr->code != 0) {
+      pbuf_free(p);
+      ND6_STATS_INC(nd6.proterr);
+      ND6_STATS_INC(nd6.drop);
+      return;
+    }
+
+    /* @todo RFC MUST: all included options have a length greater than zero */
+
+    /* If we are sending RS messages, stop. */
+#if LWIP_IPV6_SEND_ROUTER_SOLICIT
+    /* ensure at least one solicitation is sent (see RFC 4861, ch. 6.3.7) */
+    if ((inp->rs_count < LWIP_ND6_MAX_MULTICAST_SOLICIT) ||
+        (nd6_send_rs(inp) == ERR_OK)) {
+      inp->rs_count = 0;
+    } else {
+      inp->rs_count = 1;
+    }
+#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */
+
+    /* Get the matching default router entry. */
+    i = nd6_get_router(ip6_current_src_addr(), inp);
+    if (i < 0) {
+      /* Create a new router entry. */
+      i = nd6_new_router(ip6_current_src_addr(), inp);
+    }
+
+    if (i < 0) {
+      /* Could not create a new router entry. */
+      pbuf_free(p);
+      ND6_STATS_INC(nd6.memerr);
+      return;
+    }
+
+    /* Re-set invalidation timer. */
+    default_router_list[i].invalidation_timer = lwip_htons(ra_hdr->router_lifetime);
+
+    /* Re-set default timer values. */
+#if LWIP_ND6_ALLOW_RA_UPDATES
+    if (ra_hdr->retrans_timer > 0) {
+      retrans_timer = lwip_htonl(ra_hdr->retrans_timer);
+    }
+    if (ra_hdr->reachable_time > 0) {
+      reachable_time = lwip_htonl(ra_hdr->reachable_time);
+    }
+#endif /* LWIP_ND6_ALLOW_RA_UPDATES */
+
+    /* @todo set default hop limit... */
+    /* ra_hdr->current_hop_limit;*/
+
+    /* Update flags in local entry (incl. preference). */
+    default_router_list[i].flags = ra_hdr->flags;
+
+#if LWIP_IPV6_DHCP6
+    /* Trigger DHCPv6 if enabled */
+    dhcp6_nd6_ra_trigger(inp, ra_hdr->flags & ND6_RA_FLAG_MANAGED_ADDR_CONFIG,
+      ra_hdr->flags & ND6_RA_FLAG_OTHER_CONFIG);
+#endif
+
+    /* Offset to options. */
+    offset = sizeof(struct ra_header);
+
+    /* Process each option. */
+    while ((p->tot_len - offset) >= 2) {
+      u8_t option_type;
+      u16_t option_len;
+      int option_len8 = pbuf_try_get_at(p, offset + 1);
+      if (option_len8 <= 0) {
+        /* read beyond end or zero length */
+        goto lenerr_drop_free_return;
+      }
+      option_len = ((u8_t)option_len8) << 3;
+      if (option_len > p->tot_len - offset) {
+        /* short packet (option does not fit in) */
+        goto lenerr_drop_free_return;
+      }
+      if (p->len == p->tot_len) {
+        /* no need to copy from contiguous pbuf */
+        buffer = &((u8_t*)p->payload)[offset];
+      } else {
+        /* check if this option fits into our buffer */
+        if (option_len > sizeof(nd6_ra_buffer)) {
+          option_type = pbuf_get_at(p, offset);
+          /* invalid option length */
+          if (option_type != ND6_OPTION_TYPE_RDNSS) {
+            goto lenerr_drop_free_return;
+          }
+          /* we allow RDNSS option to be longer - we'll just drop some servers */
+          option_len = sizeof(nd6_ra_buffer);
+        }
+        buffer = (u8_t*)&nd6_ra_buffer;
+        option_len = pbuf_copy_partial(p, &nd6_ra_buffer, option_len, offset);
+      }
+      option_type = buffer[0];
+      switch (option_type) {
+      case ND6_OPTION_TYPE_SOURCE_LLADDR:
+      {
+        struct lladdr_option *lladdr_opt;
+        if (option_len < sizeof(struct lladdr_option)) {
+          goto lenerr_drop_free_return;
+        }
+        lladdr_opt = (struct lladdr_option *)buffer;
+        if ((default_router_list[i].neighbor_entry != NULL) &&
+            (default_router_list[i].neighbor_entry->state == ND6_INCOMPLETE)) {
+          SMEMCPY(default_router_list[i].neighbor_entry->lladdr, lladdr_opt->addr, inp->hwaddr_len);
+          default_router_list[i].neighbor_entry->state = ND6_REACHABLE;
+          default_router_list[i].neighbor_entry->counter.reachable_time = reachable_time;
+        }
+        break;
+      }
+      case ND6_OPTION_TYPE_MTU:
+      {
+        struct mtu_option *mtu_opt;
+        u32_t mtu32;
+        if (option_len < sizeof(struct mtu_option)) {
+          goto lenerr_drop_free_return;
+        }
+        mtu_opt = (struct mtu_option *)buffer;
+        mtu32 = lwip_htonl(mtu_opt->mtu);
+        if ((mtu32 >= IP6_MIN_MTU_LENGTH) && (mtu32 <= 0xffff)) {
+#if LWIP_ND6_ALLOW_RA_UPDATES
+          if (inp->mtu) {
+            /* don't set the mtu for IPv6 higher than the netif driver supports */
+            inp->mtu6 = LWIP_MIN(LWIP_MIN(inp->mtu, inp->mtu6), (u16_t)mtu32);
+          } else {
+            inp->mtu6 = (u16_t)mtu32;
+          }
+#endif /* LWIP_ND6_ALLOW_RA_UPDATES */
+        }
+        break;
+      }
+      case ND6_OPTION_TYPE_PREFIX_INFO:
+      {
+        struct prefix_option *prefix_opt;
+        ip6_addr_t prefix_addr;
+        if (option_len < sizeof(struct prefix_option)) {
+          goto lenerr_drop_free_return;
+        }
+
+        prefix_opt = (struct prefix_option *)buffer;
+
+        /* Get a memory-aligned copy of the prefix. */
+        ip6_addr_copy_from_packed(prefix_addr, prefix_opt->prefix);
+        ip6_addr_assign_zone(&prefix_addr, IP6_UNICAST, inp);
+
+        if (!ip6_addr_islinklocal(&prefix_addr)) {
+          if ((prefix_opt->flags & ND6_PREFIX_FLAG_ON_LINK) &&
+              (prefix_opt->prefix_length == 64)) {
+            /* Add to on-link prefix list. */
+            u32_t valid_life;
+            s8_t prefix;
+
+            valid_life = lwip_htonl(prefix_opt->valid_lifetime);
+
+            /* find cache entry for this prefix. */
+            prefix = nd6_get_onlink_prefix(&prefix_addr, inp);
+            if (prefix < 0 && valid_life > 0) {
+              /* Create a new cache entry. */
+              prefix = nd6_new_onlink_prefix(&prefix_addr, inp);
+            }
+            if (prefix >= 0) {
+              prefix_list[prefix].invalidation_timer = valid_life;
+            }
+          }
+#if LWIP_IPV6_AUTOCONFIG
+          if (prefix_opt->flags & ND6_PREFIX_FLAG_AUTONOMOUS) {
+            /* Perform processing for autoconfiguration. */
+            nd6_process_autoconfig_prefix(inp, prefix_opt, &prefix_addr);
+          }
+#endif /* LWIP_IPV6_AUTOCONFIG */
+        }
+
+        break;
+      }
+      case ND6_OPTION_TYPE_ROUTE_INFO:
+        /* @todo implement preferred routes.
+        struct route_option * route_opt;
+        route_opt = (struct route_option *)buffer;*/
+
+        break;
+#if LWIP_ND6_RDNSS_MAX_DNS_SERVERS
+      case ND6_OPTION_TYPE_RDNSS:
+      {
+        u8_t num, n;
+        u16_t copy_offset = offset + SIZEOF_RDNSS_OPTION_BASE;
+        struct rdnss_option * rdnss_opt;
+        if (option_len < SIZEOF_RDNSS_OPTION_BASE) {
+          goto lenerr_drop_free_return;
+        }
+
+        rdnss_opt = (struct rdnss_option *)buffer;
+        num = (rdnss_opt->length - 1) / 2;
+        for (n = 0; (rdnss_server_idx < DNS_MAX_SERVERS) && (n < num); n++, copy_offset += sizeof(ip6_addr_p_t)) {
+          ip_addr_t rdnss_address;
+
+          /* Copy directly from pbuf to get an aligned, zoned copy of the prefix. */
+          if (pbuf_copy_partial(p, &rdnss_address, sizeof(ip6_addr_p_t), copy_offset) == sizeof(ip6_addr_p_t)) {
+            IP_SET_TYPE_VAL(rdnss_address, IPADDR_TYPE_V6);
+            ip6_addr_assign_zone(ip_2_ip6(&rdnss_address), IP6_UNKNOWN, inp);
+
+            if (htonl(rdnss_opt->lifetime) > 0) {
+              /* TODO implement Lifetime > 0 */
+              dns_setserver(rdnss_server_idx++, &rdnss_address);
+            } else {
+              /* TODO implement DNS removal in dns.c */
+              u8_t s;
+              for (s = 0; s < DNS_MAX_SERVERS; s++) {
+                const ip_addr_t *addr = dns_getserver(s);
+                if(ip_addr_eq(addr, &rdnss_address)) {
+                  dns_setserver(s, NULL);
+                }
+              }
+            }
+          }
+        }
+        break;
+      }
+#endif /* LWIP_ND6_RDNSS_MAX_DNS_SERVERS */
+      default:
+        /* Unrecognized option, abort. */
+        ND6_STATS_INC(nd6.proterr);
+        break;
+      }
+      /* option length is checked earlier to be non-zero to make sure loop ends */
+      offset += 8 * (u8_t)option_len8;
+    }
+
+    break; /* ICMP6_TYPE_RA */
+  }
+  case ICMP6_TYPE_RD: /* Redirect */
+  {
+    struct redirect_header *redir_hdr;
+    struct lladdr_option *lladdr_opt;
+    ip6_addr_t destination_address, target_address;
+
+    /* Check that Redir header fits in packet. */
+    if (p->len < sizeof(struct redirect_header)) {
+      /* @todo debug message */
+      pbuf_free(p);
+      ND6_STATS_INC(nd6.lenerr);
+      ND6_STATS_INC(nd6.drop);
+      return;
+    }
+
+    redir_hdr = (struct redirect_header *)p->payload;
+
+    /* Create an aligned, zoned copy of the destination address. */
+    ip6_addr_copy_from_packed(destination_address, redir_hdr->destination_address);
+    ip6_addr_assign_zone(&destination_address, IP6_UNICAST, inp);
+
+    /* Check a subset of the other RFC 4861 Sec. 8.1 requirements. */
+    if (!ip6_addr_islinklocal(ip6_current_src_addr()) ||
+        IP6H_HOPLIM(ip6_current_header()) != ND6_HOPLIM ||
+        redir_hdr->code != 0 || ip6_addr_ismulticast(&destination_address)) {
+      pbuf_free(p);
+      ND6_STATS_INC(nd6.proterr);
+      ND6_STATS_INC(nd6.drop);
+      return;
+    }
+
+    /* @todo RFC MUST: IP source address equals first-hop router for destination_address */
+    /* @todo RFC MUST: ICMP target address is either link-local address or same as destination_address */
+    /* @todo RFC MUST: all included options have a length greater than zero */
+
+    if (p->len >= (sizeof(struct redirect_header) + 2)) {
+      lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct redirect_header));
+      if (p->len < (sizeof(struct redirect_header) + (lladdr_opt->length << 3))) {
+        lladdr_opt = NULL;
+      }
+    } else {
+      lladdr_opt = NULL;
+    }
+
+    /* Find dest address in cache */
+    dest_idx = nd6_find_destination_cache_entry(&destination_address);
+    if (dest_idx < 0) {
+      /* Destination not in cache, drop packet. */
+      pbuf_free(p);
+      return;
+    }
+
+    /* Create an aligned, zoned copy of the target address. */
+    ip6_addr_copy_from_packed(target_address, redir_hdr->target_address);
+    ip6_addr_assign_zone(&target_address, IP6_UNICAST, inp);
+
+    /* Set the new target address. */
+    ip6_addr_copy(destination_cache[dest_idx].next_hop_addr, target_address);
+
+    /* If Link-layer address of other router is given, try to add to neighbor cache. */
+    if (lladdr_opt != NULL) {
+      if (lladdr_opt->type == ND6_OPTION_TYPE_TARGET_LLADDR) {
+        i = nd6_find_neighbor_cache_entry(&target_address);
+        if (i < 0) {
+          i = nd6_new_neighbor_cache_entry();
+          if (i >= 0) {
+            neighbor_cache[i].netif = inp;
+            MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len);
+            ip6_addr_copy(neighbor_cache[i].next_hop_address, target_address);
+
+            /* Receiving a message does not prove reachability: only in one direction.
+             * Delay probe in case we get confirmation of reachability from upper layer (TCP). */
+            neighbor_cache[i].state = ND6_DELAY;
+            neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME / ND6_TMR_INTERVAL;
+          }
+        }
+        if (i >= 0) {
+          if (neighbor_cache[i].state == ND6_INCOMPLETE) {
+            MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len);
+            /* Receiving a message does not prove reachability: only in one direction.
+             * Delay probe in case we get confirmation of reachability from upper layer (TCP). */
+            neighbor_cache[i].state = ND6_DELAY;
+            neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME / ND6_TMR_INTERVAL;
+          }
+        }
+      }
+    }
+    break; /* ICMP6_TYPE_RD */
+  }
+  case ICMP6_TYPE_PTB: /* Packet too big */
+  {
+    struct icmp6_hdr *icmp6hdr; /* Packet too big message */
+    struct ip6_hdr *ip6hdr; /* IPv6 header of the packet which caused the error */
+    u32_t pmtu;
+    ip6_addr_t destination_address;
+
+    /* Check that ICMPv6 header + IPv6 header fit in payload */
+    if (p->len < (sizeof(struct icmp6_hdr) + IP6_HLEN)) {
+      /* drop short packets */
+      pbuf_free(p);
+      ND6_STATS_INC(nd6.lenerr);
+      ND6_STATS_INC(nd6.drop);
+      return;
+    }
+
+    icmp6hdr = (struct icmp6_hdr *)p->payload;
+    ip6hdr = (struct ip6_hdr *)((u8_t*)p->payload + sizeof(struct icmp6_hdr));
+
+    /* Create an aligned, zoned copy of the destination address. */
+    ip6_addr_copy_from_packed(destination_address, ip6hdr->dest);
+    ip6_addr_assign_zone(&destination_address, IP6_UNKNOWN, inp);
+
+    /* Look for entry in destination cache. */
+    dest_idx = nd6_find_destination_cache_entry(&destination_address);
+    if (dest_idx < 0) {
+      /* Destination not in cache, drop packet. */
+      pbuf_free(p);
+      return;
+    }
+
+    /* Change the Path MTU. */
+    pmtu = lwip_htonl(icmp6hdr->data);
+    destination_cache[dest_idx].pmtu = (u16_t)LWIP_MIN(pmtu, 0xFFFF);
+
+    break; /* ICMP6_TYPE_PTB */
+  }
+
+  default:
+    ND6_STATS_INC(nd6.proterr);
+    ND6_STATS_INC(nd6.drop);
+    break; /* default */
+  }
+
+  pbuf_free(p);
+  return;
+lenerr_drop_free_return:
+  ND6_STATS_INC(nd6.lenerr);
+  ND6_STATS_INC(nd6.drop);
+  pbuf_free(p);
+}
+
+
+/**
+ * Periodic timer for Neighbor discovery functions:
+ *
+ * - Update neighbor reachability states
+ * - Update destination cache entries age
+ * - Update invalidation timers of default routers and on-link prefixes
+ * - Update lifetimes of our addresses
+ * - Perform duplicate address detection (DAD) for our addresses
+ * - Send router solicitations
+ */
+void
+nd6_tmr(void)
+{
+  s8_t i;
+  struct netif *netif;
+
+  /* Process neighbor entries. */
+  for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) {
+    switch (neighbor_cache[i].state) {
+    case ND6_INCOMPLETE:
+      if ((neighbor_cache[i].counter.probes_sent >= LWIP_ND6_MAX_MULTICAST_SOLICIT) &&
+          (!neighbor_cache[i].isrouter)) {
+        /* Retries exceeded. */
+        nd6_free_neighbor_cache_entry(i);
+      } else {
+        /* Send a NS for this entry. */
+        neighbor_cache[i].counter.probes_sent++;
+        nd6_send_neighbor_cache_probe(&neighbor_cache[i], ND6_SEND_FLAG_MULTICAST_DEST);
+      }
+      break;
+    case ND6_REACHABLE:
+      /* Send queued packets, if any are left. Should have been sent already. */
+      if (neighbor_cache[i].q != NULL) {
+        nd6_send_q(i);
+      }
+      if (neighbor_cache[i].counter.reachable_time <= ND6_TMR_INTERVAL) {
+        /* Change to stale state. */
+        neighbor_cache[i].state = ND6_STALE;
+        neighbor_cache[i].counter.stale_time = 0;
+      } else {
+        neighbor_cache[i].counter.reachable_time -= ND6_TMR_INTERVAL;
+      }
+      break;
+    case ND6_STALE:
+      neighbor_cache[i].counter.stale_time++;
+      break;
+    case ND6_DELAY:
+      if (neighbor_cache[i].counter.delay_time <= 1) {
+        /* Change to PROBE state. */
+        neighbor_cache[i].state = ND6_PROBE;
+        neighbor_cache[i].counter.probes_sent = 0;
+      } else {
+        neighbor_cache[i].counter.delay_time--;
+      }
+      break;
+    case ND6_PROBE:
+      if ((neighbor_cache[i].counter.probes_sent >= LWIP_ND6_MAX_MULTICAST_SOLICIT) &&
+          (!neighbor_cache[i].isrouter)) {
+        /* Retries exceeded. */
+        nd6_free_neighbor_cache_entry(i);
+      } else {
+        /* Send a NS for this entry. */
+        neighbor_cache[i].counter.probes_sent++;
+        nd6_send_neighbor_cache_probe(&neighbor_cache[i], 0);
+      }
+      break;
+    case ND6_NO_ENTRY:
+    default:
+      /* Do nothing. */
+      break;
+    }
+  }
+
+  /* Process destination entries. */
+  for (i = 0; i < LWIP_ND6_NUM_DESTINATIONS; i++) {
+    destination_cache[i].age++;
+  }
+
+  /* Process router entries. */
+  for (i = 0; i < LWIP_ND6_NUM_ROUTERS; i++) {
+    if (default_router_list[i].neighbor_entry != NULL) {
+      /* Active entry. */
+      if (default_router_list[i].invalidation_timer <= ND6_TMR_INTERVAL / 1000) {
+        /* No more than 1 second remaining. Clear this entry. Also clear any of
+         * its destination cache entries, as per RFC 4861 Sec. 5.3 and 6.3.5. */
+        s8_t j;
+        for (j = 0; j < LWIP_ND6_NUM_DESTINATIONS; j++) {
+          if (ip6_addr_eq(&destination_cache[j].next_hop_addr,
+               &default_router_list[i].neighbor_entry->next_hop_address)) {
+             ip6_addr_set_any(&destination_cache[j].destination_addr);
+          }
+        }
+        default_router_list[i].neighbor_entry->isrouter = 0;
+        default_router_list[i].neighbor_entry = NULL;
+        default_router_list[i].invalidation_timer = 0;
+        default_router_list[i].flags = 0;
+      } else {
+        default_router_list[i].invalidation_timer -= ND6_TMR_INTERVAL / 1000;
+      }
+    }
+  }
+
+  /* Process prefix entries. */
+  for (i = 0; i < LWIP_ND6_NUM_PREFIXES; i++) {
+    if (prefix_list[i].netif != NULL) {
+      if (prefix_list[i].invalidation_timer <= ND6_TMR_INTERVAL / 1000) {
+        /* Entry timed out, remove it */
+        prefix_list[i].invalidation_timer = 0;
+        prefix_list[i].netif = NULL;
+      } else {
+        prefix_list[i].invalidation_timer -= ND6_TMR_INTERVAL / 1000;
+      }
+    }
+  }
+
+  /* Process our own addresses, updating address lifetimes and/or DAD state. */
+  NETIF_FOREACH(netif) {
+    for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; ++i) {
+      u8_t addr_state;
+#if LWIP_IPV6_ADDRESS_LIFETIMES
+      /* Step 1: update address lifetimes (valid and preferred). */
+      addr_state = netif_ip6_addr_state(netif, i);
+      /* RFC 4862 is not entirely clear as to whether address lifetimes affect
+       * tentative addresses, and is even less clear as to what should happen
+       * with duplicate addresses. We choose to track and update lifetimes for
+       * both those types, although for different reasons:
+       * - for tentative addresses, the line of thought of Sec. 5.7 combined
+       *   with the potentially long period that an address may be in tentative
+       *   state (due to the interface being down) suggests that lifetimes
+       *   should be independent of external factors which would include DAD;
+       * - for duplicate addresses, retiring them early could result in a new
+       *   but unwanted attempt at marking them as valid, while retiring them
+       *   late/never could clog up address slots on the netif.
+       * As a result, we may end up expiring addresses of either type here.
+       */
+      if (!ip6_addr_isinvalid(addr_state) &&
+          !netif_ip6_addr_isstatic(netif, i)) {
+        u32_t life = netif_ip6_addr_valid_life(netif, i);
+        if (life <= ND6_TMR_INTERVAL / 1000) {
+          /* The address has expired. */
+          netif_ip6_addr_set_valid_life(netif, i, 0);
+          netif_ip6_addr_set_pref_life(netif, i, 0);
+          netif_ip6_addr_set_state(netif, i, IP6_ADDR_INVALID);
+        } else {
+          if (!ip6_addr_life_isinfinite(life)) {
+            life -= ND6_TMR_INTERVAL / 1000;
+            LWIP_ASSERT("bad valid lifetime", life != IP6_ADDR_LIFE_STATIC);
+            netif_ip6_addr_set_valid_life(netif, i, life);
+          }
+          /* The address is still here. Update the preferred lifetime too. */
+          life = netif_ip6_addr_pref_life(netif, i);
+          if (life <= ND6_TMR_INTERVAL / 1000) {
+            /* This case must also trigger if 'life' was already zero, so as to
+             * deal correctly with advertised preferred-lifetime reductions. */
+            netif_ip6_addr_set_pref_life(netif, i, 0);
+            if (addr_state == IP6_ADDR_PREFERRED)
+              netif_ip6_addr_set_state(netif, i, IP6_ADDR_DEPRECATED);
+          } else if (!ip6_addr_life_isinfinite(life)) {
+            life -= ND6_TMR_INTERVAL / 1000;
+            netif_ip6_addr_set_pref_life(netif, i, life);
+          }
+        }
+      }
+      /* The address state may now have changed, so reobtain it next. */
+#endif /* LWIP_IPV6_ADDRESS_LIFETIMES */
+      /* Step 2: update DAD state. */
+      addr_state = netif_ip6_addr_state(netif, i);
+      if (ip6_addr_istentative(addr_state)) {
+        if ((addr_state & IP6_ADDR_TENTATIVE_COUNT_MASK) >= LWIP_IPV6_DUP_DETECT_ATTEMPTS) {
+          /* No NA received in response. Mark address as valid. For dynamic
+           * addresses with an expired preferred lifetime, the state is set to
+           * deprecated right away. That should almost never happen, though. */
+          addr_state = IP6_ADDR_PREFERRED;
+#if LWIP_IPV6_ADDRESS_LIFETIMES
+          if (!netif_ip6_addr_isstatic(netif, i) &&
+              netif_ip6_addr_pref_life(netif, i) == 0) {
+            addr_state = IP6_ADDR_DEPRECATED;
+          }
+#endif /* LWIP_IPV6_ADDRESS_LIFETIMES */
+          netif_ip6_addr_set_state(netif, i, addr_state);
+        } else if (netif_is_up(netif) && netif_is_link_up(netif)) {
+          /* tentative: set next state by increasing by one */
+          netif_ip6_addr_set_state(netif, i, addr_state + 1);
+          /* Send a NS for this address. Use the unspecified address as source
+           * address in all cases (RFC 4862 Sec. 5.4.2), not in the least
+           * because as it is, we only consider multicast replies for DAD. */
+          nd6_send_ns(netif, netif_ip6_addr(netif, i),
+            ND6_SEND_FLAG_MULTICAST_DEST | ND6_SEND_FLAG_ANY_SRC);
+        }
+      }
+    }
+  }
+
+#if LWIP_IPV6_SEND_ROUTER_SOLICIT
+  /* Send router solicitation messages, if necessary. */
+  if (!nd6_tmr_rs_reduction) {
+    nd6_tmr_rs_reduction = (ND6_RTR_SOLICITATION_INTERVAL / ND6_TMR_INTERVAL) - 1;
+    NETIF_FOREACH(netif) {
+      if ((netif->rs_count > 0) && netif_is_up(netif) &&
+          netif_is_link_up(netif) &&
+          !ip6_addr_isinvalid(netif_ip6_addr_state(netif, 0)) &&
+          !ip6_addr_isduplicated(netif_ip6_addr_state(netif, 0))) {
+        if (nd6_send_rs(netif) == ERR_OK) {
+          netif->rs_count--;
+        }
+      }
+    }
+  } else {
+    nd6_tmr_rs_reduction--;
+  }
+#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */
+
+}
+
+/** Send a neighbor solicitation message for a specific neighbor cache entry
+ *
+ * @param entry the neightbor cache entry for which to send the message
+ * @param flags one of ND6_SEND_FLAG_*
+ */
+static void
+nd6_send_neighbor_cache_probe(struct nd6_neighbor_cache_entry *entry, u8_t flags)
+{
+  nd6_send_ns(entry->netif, &entry->next_hop_address, flags);
+}
+
+/**
+ * Send a neighbor solicitation message
+ *
+ * @param netif the netif on which to send the message
+ * @param target_addr the IPv6 target address for the ND message
+ * @param flags one of ND6_SEND_FLAG_*
+ */
+static void
+nd6_send_ns(struct netif *netif, const ip6_addr_t *target_addr, u8_t flags)
+{
+  struct ns_header *ns_hdr;
+  struct pbuf *p;
+  const ip6_addr_t *src_addr = NULL;
+  u16_t lladdr_opt_len;
+
+  LWIP_ASSERT("target address is required", target_addr != NULL);
+
+  if (!(flags & ND6_SEND_FLAG_ANY_SRC)) {
+    int i;
+    for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+      if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) &&
+            ip6_addr_net_eq(target_addr, netif_ip6_addr(netif, i))) {
+        src_addr = netif_ip6_addr(netif, i);
+        break;
+      }
+    }
+
+    if (i == LWIP_IPV6_NUM_ADDRESSES) {
+      LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_WARNING, ("ICMPv6 NS: no available src address\n"));
+      ND6_STATS_INC(nd6.err);
+      return;
+    }
+
+    /* calculate option length (in 8-byte-blocks) */
+    lladdr_opt_len = ((netif->hwaddr_len + 2) + 7) >> 3;
+  } else {
+    src_addr = IP6_ADDR_ANY6;
+    /* Option "MUST NOT be included when the source IP address is the unspecified address." */
+    lladdr_opt_len = 0;
+  }
+
+  /* Allocate a packet. */
+  p = pbuf_alloc(PBUF_IP, sizeof(struct ns_header) + (lladdr_opt_len << 3), PBUF_RAM);
+  if (p == NULL) {
+    ND6_STATS_INC(nd6.memerr);
+    return;
+  }
+
+  /* Set fields. */
+  ns_hdr = (struct ns_header *)p->payload;
+
+  ns_hdr->type = ICMP6_TYPE_NS;
+  ns_hdr->code = 0;
+  ns_hdr->chksum = 0;
+  ns_hdr->reserved = 0;
+  ip6_addr_copy_to_packed(ns_hdr->target_address, *target_addr);
+
+  if (lladdr_opt_len != 0) {
+    struct lladdr_option *lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct ns_header));
+    lladdr_opt->type = ND6_OPTION_TYPE_SOURCE_LLADDR;
+    lladdr_opt->length = (u8_t)lladdr_opt_len;
+    SMEMCPY(lladdr_opt->addr, netif->hwaddr, netif->hwaddr_len);
+  }
+
+  /* Generate the solicited node address for the target address. */
+  if (flags & ND6_SEND_FLAG_MULTICAST_DEST) {
+    ip6_addr_set_solicitednode(&multicast_address, target_addr->addr[3]);
+    ip6_addr_assign_zone(&multicast_address, IP6_MULTICAST, netif);
+    target_addr = &multicast_address;
+  }
+
+#if CHECKSUM_GEN_ICMP6
+  IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_ICMP6) {
+    ns_hdr->chksum = ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->len, src_addr,
+      target_addr);
+  }
+#endif /* CHECKSUM_GEN_ICMP6 */
+
+  /* Send the packet out. */
+  ND6_STATS_INC(nd6.xmit);
+  ip6_output_if(p, (src_addr == IP6_ADDR_ANY6) ? NULL : src_addr, target_addr,
+      ND6_HOPLIM, 0, IP6_NEXTH_ICMP6, netif);
+  pbuf_free(p);
+}
+
+/**
+ * Send a neighbor advertisement message
+ *
+ * @param netif the netif on which to send the message
+ * @param target_addr the IPv6 target address for the ND message
+ * @param flags one of ND6_SEND_FLAG_*
+ */
+static void
+nd6_send_na(struct netif *netif, const ip6_addr_t *target_addr, u8_t flags)
+{
+  struct na_header *na_hdr;
+  struct lladdr_option *lladdr_opt;
+  struct pbuf *p;
+  const ip6_addr_t *src_addr;
+  const ip6_addr_t *dest_addr;
+  u16_t lladdr_opt_len;
+
+  LWIP_ASSERT("target address is required", target_addr != NULL);
+
+  /* Use link-local address as source address. */
+  /* src_addr = netif_ip6_addr(netif, 0); */
+  /* Use target address as source address. */
+  src_addr = target_addr;
+
+  /* Allocate a packet. */
+  lladdr_opt_len = ((netif->hwaddr_len + 2) >> 3) + (((netif->hwaddr_len + 2) & 0x07) ? 1 : 0);
+  p = pbuf_alloc(PBUF_IP, sizeof(struct na_header) + (lladdr_opt_len << 3), PBUF_RAM);
+  if (p == NULL) {
+    ND6_STATS_INC(nd6.memerr);
+    return;
+  }
+
+  /* Set fields. */
+  na_hdr = (struct na_header *)p->payload;
+  lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct na_header));
+
+  na_hdr->type = ICMP6_TYPE_NA;
+  na_hdr->code = 0;
+  na_hdr->chksum = 0;
+  na_hdr->flags = flags & 0xf0;
+  na_hdr->reserved[0] = 0;
+  na_hdr->reserved[1] = 0;
+  na_hdr->reserved[2] = 0;
+  ip6_addr_copy_to_packed(na_hdr->target_address, *target_addr);
+
+  lladdr_opt->type = ND6_OPTION_TYPE_TARGET_LLADDR;
+  lladdr_opt->length = (u8_t)lladdr_opt_len;
+  SMEMCPY(lladdr_opt->addr, netif->hwaddr, netif->hwaddr_len);
+
+  /* Generate the solicited node address for the target address. */
+  if (flags & ND6_SEND_FLAG_MULTICAST_DEST) {
+    ip6_addr_set_solicitednode(&multicast_address, target_addr->addr[3]);
+    ip6_addr_assign_zone(&multicast_address, IP6_MULTICAST, netif);
+    dest_addr = &multicast_address;
+  } else if (flags & ND6_SEND_FLAG_ALLNODES_DEST) {
+    ip6_addr_set_allnodes_linklocal(&multicast_address);
+    ip6_addr_assign_zone(&multicast_address, IP6_MULTICAST, netif);
+    dest_addr = &multicast_address;
+  } else {
+    dest_addr = ip6_current_src_addr();
+  }
+
+#if CHECKSUM_GEN_ICMP6
+  IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_ICMP6) {
+    na_hdr->chksum = ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->len, src_addr,
+      dest_addr);
+  }
+#endif /* CHECKSUM_GEN_ICMP6 */
+
+  /* Send the packet out. */
+  ND6_STATS_INC(nd6.xmit);
+  ip6_output_if(p, src_addr, dest_addr,
+      ND6_HOPLIM, 0, IP6_NEXTH_ICMP6, netif);
+  pbuf_free(p);
+}
+
+#if LWIP_IPV6_SEND_ROUTER_SOLICIT
+/**
+ * Send a router solicitation message
+ *
+ * @param netif the netif on which to send the message
+ */
+static err_t
+nd6_send_rs(struct netif *netif)
+{
+  struct rs_header *rs_hdr;
+  struct lladdr_option *lladdr_opt;
+  struct pbuf *p;
+  const ip6_addr_t *src_addr;
+  err_t err;
+  u16_t lladdr_opt_len = 0;
+
+  /* Link-local source address, or unspecified address? */
+  if (ip6_addr_isvalid(netif_ip6_addr_state(netif, 0))) {
+    src_addr = netif_ip6_addr(netif, 0);
+  } else {
+    src_addr = IP6_ADDR_ANY6;
+  }
+
+  /* Generate the all routers target address. */
+  ip6_addr_set_allrouters_linklocal(&multicast_address);
+  ip6_addr_assign_zone(&multicast_address, IP6_MULTICAST, netif);
+
+  /* Allocate a packet. */
+  if (src_addr != IP6_ADDR_ANY6) {
+    lladdr_opt_len = ((netif->hwaddr_len + 2) >> 3) + (((netif->hwaddr_len + 2) & 0x07) ? 1 : 0);
+  }
+  p = pbuf_alloc(PBUF_IP, sizeof(struct rs_header) + (lladdr_opt_len << 3), PBUF_RAM);
+  if (p == NULL) {
+    ND6_STATS_INC(nd6.memerr);
+    return ERR_BUF;
+  }
+
+  /* Set fields. */
+  rs_hdr = (struct rs_header *)p->payload;
+
+  rs_hdr->type = ICMP6_TYPE_RS;
+  rs_hdr->code = 0;
+  rs_hdr->chksum = 0;
+  rs_hdr->reserved = 0;
+
+  if (src_addr != IP6_ADDR_ANY6) {
+    /* Include our hw address. */
+    lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct rs_header));
+    lladdr_opt->type = ND6_OPTION_TYPE_SOURCE_LLADDR;
+    lladdr_opt->length = (u8_t)lladdr_opt_len;
+    SMEMCPY(lladdr_opt->addr, netif->hwaddr, netif->hwaddr_len);
+  }
+
+#if CHECKSUM_GEN_ICMP6
+  IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_ICMP6) {
+    rs_hdr->chksum = ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->len, src_addr,
+      &multicast_address);
+  }
+#endif /* CHECKSUM_GEN_ICMP6 */
+
+  /* Send the packet out. */
+  ND6_STATS_INC(nd6.xmit);
+
+  err = ip6_output_if(p, (src_addr == IP6_ADDR_ANY6) ? NULL : src_addr, &multicast_address,
+      ND6_HOPLIM, 0, IP6_NEXTH_ICMP6, netif);
+  pbuf_free(p);
+
+  return err;
+}
+#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */
+
+/**
+ * Search for a neighbor cache entry
+ *
+ * @param ip6addr the IPv6 address of the neighbor
+ * @return The neighbor cache entry index that matched, -1 if no
+ * entry is found
+ */
+static s8_t
+nd6_find_neighbor_cache_entry(const ip6_addr_t *ip6addr)
+{
+  s8_t i;
+  for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) {
+    if (ip6_addr_eq(ip6addr, &(neighbor_cache[i].next_hop_address))) {
+      return i;
+    }
+  }
+  return -1;
+}
+
+/**
+ * Create a new neighbor cache entry.
+ *
+ * If no unused entry is found, will try to recycle an old entry
+ * according to ad-hoc "age" heuristic.
+ *
+ * @return The neighbor cache entry index that was created, -1 if no
+ * entry could be created
+ */
+static s8_t
+nd6_new_neighbor_cache_entry(void)
+{
+  s8_t i;
+  s8_t j;
+  u32_t time;
+
+
+  /* First, try to find an empty entry. */
+  for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) {
+    if (neighbor_cache[i].state == ND6_NO_ENTRY) {
+      return i;
+    }
+  }
+
+  /* We need to recycle an entry. in general, do not recycle if it is a router. */
+
+  /* Next, try to find a Stale entry. */
+  for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) {
+    if ((neighbor_cache[i].state == ND6_STALE) &&
+        (!neighbor_cache[i].isrouter)) {
+      nd6_free_neighbor_cache_entry(i);
+      return i;
+    }
+  }
+
+  /* Next, try to find a Probe entry. */
+  for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) {
+    if ((neighbor_cache[i].state == ND6_PROBE) &&
+        (!neighbor_cache[i].isrouter)) {
+      nd6_free_neighbor_cache_entry(i);
+      return i;
+    }
+  }
+
+  /* Next, try to find a Delayed entry. */
+  for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) {
+    if ((neighbor_cache[i].state == ND6_DELAY) &&
+        (!neighbor_cache[i].isrouter)) {
+      nd6_free_neighbor_cache_entry(i);
+      return i;
+    }
+  }
+
+  /* Next, try to find the oldest reachable entry. */
+  time = 0xfffffffful;
+  j = -1;
+  for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) {
+    if ((neighbor_cache[i].state == ND6_REACHABLE) &&
+        (!neighbor_cache[i].isrouter)) {
+      if (neighbor_cache[i].counter.reachable_time < time) {
+        j = i;
+        time = neighbor_cache[i].counter.reachable_time;
+      }
+    }
+  }
+  if (j >= 0) {
+    nd6_free_neighbor_cache_entry(j);
+    return j;
+  }
+
+  /* Next, find oldest incomplete entry without queued packets. */
+  time = 0;
+  j = -1;
+  for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) {
+    if (
+        (neighbor_cache[i].q == NULL) &&
+        (neighbor_cache[i].state == ND6_INCOMPLETE) &&
+        (!neighbor_cache[i].isrouter)) {
+      if (neighbor_cache[i].counter.probes_sent >= time) {
+        j = i;
+        time = neighbor_cache[i].counter.probes_sent;
+      }
+    }
+  }
+  if (j >= 0) {
+    nd6_free_neighbor_cache_entry(j);
+    return j;
+  }
+
+  /* Next, find oldest incomplete entry with queued packets. */
+  time = 0;
+  j = -1;
+  for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) {
+    if ((neighbor_cache[i].state == ND6_INCOMPLETE) &&
+        (!neighbor_cache[i].isrouter)) {
+      if (neighbor_cache[i].counter.probes_sent >= time) {
+        j = i;
+        time = neighbor_cache[i].counter.probes_sent;
+      }
+    }
+  }
+  if (j >= 0) {
+    nd6_free_neighbor_cache_entry(j);
+    return j;
+  }
+
+  /* No more entries to try. */
+  return -1;
+}
+
+/**
+ * Will free any resources associated with a neighbor cache
+ * entry, and will mark it as unused.
+ *
+ * @param i the neighbor cache entry index to free
+ */
+static void
+nd6_free_neighbor_cache_entry(s8_t i)
+{
+  if ((i < 0) || (i >= LWIP_ND6_NUM_NEIGHBORS)) {
+    return;
+  }
+  if (neighbor_cache[i].isrouter) {
+    /* isrouter needs to be cleared before deleting a neighbor cache entry */
+    return;
+  }
+
+  /* Free any queued packets. */
+  if (neighbor_cache[i].q != NULL) {
+    nd6_free_q(neighbor_cache[i].q);
+    neighbor_cache[i].q = NULL;
+  }
+
+  neighbor_cache[i].state = ND6_NO_ENTRY;
+  neighbor_cache[i].isrouter = 0;
+  neighbor_cache[i].netif = NULL;
+  neighbor_cache[i].counter.reachable_time = 0;
+  ip6_addr_set_zero(&(neighbor_cache[i].next_hop_address));
+}
+
+/**
+ * Search for a destination cache entry
+ *
+ * @param ip6addr the IPv6 address of the destination
+ * @return The destination cache entry index that matched, -1 if no
+ * entry is found
+ */
+static s16_t
+nd6_find_destination_cache_entry(const ip6_addr_t *ip6addr)
+{
+  s16_t i;
+
+  IP6_ADDR_ZONECHECK(ip6addr);
+
+  for (i = 0; i < LWIP_ND6_NUM_DESTINATIONS; i++) {
+    if (ip6_addr_eq(ip6addr, &(destination_cache[i].destination_addr))) {
+      return i;
+    }
+  }
+  return -1;
+}
+
+/**
+ * Create a new destination cache entry. If no unused entry is found,
+ * will recycle oldest entry.
+ *
+ * @return The destination cache entry index that was created, -1 if no
+ * entry was created
+ */
+static s16_t
+nd6_new_destination_cache_entry(void)
+{
+  s16_t i, j;
+  u32_t age;
+
+  /* Find an empty entry. */
+  for (i = 0; i < LWIP_ND6_NUM_DESTINATIONS; i++) {
+    if (ip6_addr_isany(&(destination_cache[i].destination_addr))) {
+      return i;
+    }
+  }
+
+  /* Find oldest entry. */
+  age = 0;
+  j = LWIP_ND6_NUM_DESTINATIONS - 1;
+  for (i = 0; i < LWIP_ND6_NUM_DESTINATIONS; i++) {
+    if (destination_cache[i].age > age) {
+      j = i;
+    }
+  }
+
+  return j;
+}
+
+/**
+ * Clear the destination cache.
+ *
+ * This operation may be necessary for consistency in the light of changing
+ * local addresses and/or use of the gateway hook.
+ */
+void
+nd6_clear_destination_cache(void)
+{
+  int i;
+
+  for (i = 0; i < LWIP_ND6_NUM_DESTINATIONS; i++) {
+    ip6_addr_set_any(&destination_cache[i].destination_addr);
+  }
+}
+
+/**
+ * Determine whether an address matches an on-link prefix or the subnet of a
+ * statically assigned address.
+ *
+ * @param ip6addr the IPv6 address to match
+ * @return 1 if the address is on-link, 0 otherwise
+ */
+static int
+nd6_is_prefix_in_netif(const ip6_addr_t *ip6addr, struct netif *netif)
+{
+  s8_t i;
+
+  /* Check to see if the address matches an on-link prefix. */
+  for (i = 0; i < LWIP_ND6_NUM_PREFIXES; i++) {
+    if ((prefix_list[i].netif == netif) &&
+        (prefix_list[i].invalidation_timer > 0) &&
+        ip6_addr_net_eq(ip6addr, &(prefix_list[i].prefix))) {
+      return 1;
+    }
+  }
+  /* Check to see if address prefix matches a manually configured (= static)
+   * address. Static addresses have an implied /64 subnet assignment. Dynamic
+   * addresses (from autoconfiguration) have no implied subnet assignment, and
+   * are thus effectively /128 assignments. See RFC 5942 for more on this. */
+  for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+    if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) &&
+        netif_ip6_addr_isstatic(netif, i) &&
+        ip6_addr_net_eq(ip6addr, netif_ip6_addr(netif, i))) {
+      return 1;
+    }
+  }
+  return 0;
+}
+
+/**
+ * Select a default router for a destination.
+ *
+ * This function is used both for routing and for finding a next-hop target for
+ * a packet. In the former case, the given netif is NULL, and the returned
+ * router entry must be for a netif suitable for sending packets (up, link up).
+ * In the latter case, the given netif is not NULL and restricts router choice.
+ *
+ * @param ip6addr the destination address
+ * @param netif the netif for the outgoing packet, if known
+ * @return the default router entry index, or -1 if no suitable
+ *         router is found
+ */
+static s8_t
+nd6_select_router(const ip6_addr_t *ip6addr, struct netif *netif)
+{
+  struct netif *router_netif;
+  s8_t i, j, valid_router;
+  static s8_t last_router;
+
+  LWIP_UNUSED_ARG(ip6addr); /* @todo match preferred routes!! (must implement ND6_OPTION_TYPE_ROUTE_INFO) */
+
+  /* @todo: implement default router preference */
+
+  /* Look for valid routers. A reachable router is preferred. */
+  valid_router = -1;
+  for (i = 0; i < LWIP_ND6_NUM_ROUTERS; i++) {
+    /* Is the router netif both set and apppropriate? */
+    if (default_router_list[i].neighbor_entry != NULL) {
+      router_netif = default_router_list[i].neighbor_entry->netif;
+      if ((router_netif != NULL) && (netif != NULL ? netif == router_netif :
+          (netif_is_up(router_netif) && netif_is_link_up(router_netif)))) {
+        /* Is the router valid, i.e., reachable or probably reachable as per
+         * RFC 4861 Sec. 6.3.6? Note that we will never return a router that
+         * has no neighbor cache entry, due to the netif association tests. */
+        if (default_router_list[i].neighbor_entry->state != ND6_INCOMPLETE) {
+          /* Is the router known to be reachable? */
+          if (default_router_list[i].neighbor_entry->state == ND6_REACHABLE) {
+            return i; /* valid and reachable - done! */
+          } else if (valid_router < 0) {
+            valid_router = i; /* valid but not known to be reachable */
+          }
+        }
+      }
+    }
+  }
+  if (valid_router >= 0) {
+    return valid_router;
+  }
+
+  /* Look for any router for which we have any information at all. */
+  /* last_router is used for round-robin selection of incomplete routers, as
+   * recommended in RFC 4861 Sec. 6.3.6 point (2). Advance only when picking a
+   * route, to select the same router as next-hop target in the common case. */
+  if ((netif == NULL) && (++last_router >= LWIP_ND6_NUM_ROUTERS)) {
+    last_router = 0;
+  }
+  i = last_router;
+  for (j = 0; j < LWIP_ND6_NUM_ROUTERS; j++) {
+    if (default_router_list[i].neighbor_entry != NULL) {
+      router_netif = default_router_list[i].neighbor_entry->netif;
+      if ((router_netif != NULL) && (netif != NULL ? netif == router_netif :
+          (netif_is_up(router_netif) && netif_is_link_up(router_netif)))) {
+        return i;
+      }
+    }
+    if (++i >= LWIP_ND6_NUM_ROUTERS) {
+      i = 0;
+    }
+  }
+
+  /* no suitable router found. */
+  return -1;
+}
+
+/**
+ * Find a router-announced route to the given destination. This route may be
+ * based on an on-link prefix or a default router.
+ *
+ * If a suitable route is found, the returned netif is guaranteed to be in a
+ * suitable state (up, link up) to be used for packet transmission.
+ *
+ * @param ip6addr the destination IPv6 address
+ * @return the netif to use for the destination, or NULL if none found
+ */
+struct netif *
+nd6_find_route(const ip6_addr_t *ip6addr)
+{
+  struct netif *netif;
+  s8_t i;
+
+  /* @todo decide if it makes sense to check the destination cache first */
+
+  /* Check if there is a matching on-link prefix. There may be multiple
+   * matches. Pick the first one that is associated with a suitable netif. */
+  for (i = 0; i < LWIP_ND6_NUM_PREFIXES; ++i) {
+    netif = prefix_list[i].netif;
+    if ((netif != NULL) && ip6_addr_net_eq(&prefix_list[i].prefix, ip6addr) &&
+        netif_is_up(netif) && netif_is_link_up(netif)) {
+      return netif;
+    }
+  }
+
+  /* No on-link prefix match. Find a router that can forward the packet. */
+  i = nd6_select_router(ip6addr, NULL);
+  if (i >= 0) {
+    LWIP_ASSERT("selected router must have a neighbor entry",
+      default_router_list[i].neighbor_entry != NULL);
+    return default_router_list[i].neighbor_entry->netif;
+  }
+
+  return NULL;
+}
+
+/**
+ * Find an entry for a default router.
+ *
+ * @param router_addr the IPv6 address of the router
+ * @param netif the netif on which the router is found, if known
+ * @return the index of the router entry, or -1 if not found
+ */
+static s8_t
+nd6_get_router(const ip6_addr_t *router_addr, struct netif *netif)
+{
+  s8_t i;
+
+  IP6_ADDR_ZONECHECK_NETIF(router_addr, netif);
+
+  /* Look for router. */
+  for (i = 0; i < LWIP_ND6_NUM_ROUTERS; i++) {
+    if ((default_router_list[i].neighbor_entry != NULL) &&
+        ((netif != NULL) ? netif == default_router_list[i].neighbor_entry->netif : 1) &&
+        ip6_addr_eq(router_addr, &(default_router_list[i].neighbor_entry->next_hop_address))) {
+      return i;
+    }
+  }
+
+  /* router not found. */
+  return -1;
+}
+
+/**
+ * Create a new entry for a default router.
+ *
+ * @param router_addr the IPv6 address of the router
+ * @param netif the netif on which the router is connected, if known
+ * @return the index on the router table, or -1 if could not be created
+ */
+static s8_t
+nd6_new_router(const ip6_addr_t *router_addr, struct netif *netif)
+{
+  s8_t router_index;
+  s8_t free_router_index;
+  s8_t neighbor_index;
+
+  IP6_ADDR_ZONECHECK_NETIF(router_addr, netif);
+
+  /* Do we have a neighbor entry for this router? */
+  neighbor_index = nd6_find_neighbor_cache_entry(router_addr);
+  if (neighbor_index < 0) {
+    /* Create a neighbor entry for this router. */
+    neighbor_index = nd6_new_neighbor_cache_entry();
+    if (neighbor_index < 0) {
+      /* Could not create neighbor entry for this router. */
+      return -1;
+    }
+    ip6_addr_set(&(neighbor_cache[neighbor_index].next_hop_address), router_addr);
+    neighbor_cache[neighbor_index].netif = netif;
+    neighbor_cache[neighbor_index].q = NULL;
+    neighbor_cache[neighbor_index].state = ND6_INCOMPLETE;
+    neighbor_cache[neighbor_index].counter.probes_sent = 1;
+    nd6_send_neighbor_cache_probe(&neighbor_cache[neighbor_index], ND6_SEND_FLAG_MULTICAST_DEST);
+  }
+
+  /* Mark neighbor as router. */
+  neighbor_cache[neighbor_index].isrouter = 1;
+
+  /* Look for empty entry. */
+  free_router_index = LWIP_ND6_NUM_ROUTERS;
+  for (router_index = LWIP_ND6_NUM_ROUTERS - 1; router_index >= 0; router_index--) {
+    /* check if router already exists (this is a special case for 2 netifs on the same subnet
+       - e.g. wifi and cable) */
+    if(default_router_list[router_index].neighbor_entry == &(neighbor_cache[neighbor_index])){
+      return router_index;
+    }
+    if (default_router_list[router_index].neighbor_entry == NULL) {
+      /* remember lowest free index to create a new entry */
+      free_router_index = router_index;
+    }
+  }
+  if (free_router_index < LWIP_ND6_NUM_ROUTERS) {
+    default_router_list[free_router_index].neighbor_entry = &(neighbor_cache[neighbor_index]);
+    return free_router_index;
+  }
+
+  /* Could not create a router entry. */
+
+  /* Mark neighbor entry as not-router. Entry might be useful as neighbor still. */
+  neighbor_cache[neighbor_index].isrouter = 0;
+
+  /* router not found. */
+  return -1;
+}
+
+/**
+ * Find the cached entry for an on-link prefix.
+ *
+ * @param prefix the IPv6 prefix that is on-link
+ * @param netif the netif on which the prefix is on-link
+ * @return the index on the prefix table, or -1 if not found
+ */
+static s8_t
+nd6_get_onlink_prefix(const ip6_addr_t *prefix, struct netif *netif)
+{
+  s8_t i;
+
+  /* Look for prefix in list. */
+  for (i = 0; i < LWIP_ND6_NUM_PREFIXES; ++i) {
+    if ((ip6_addr_net_eq(&(prefix_list[i].prefix), prefix)) &&
+        (prefix_list[i].netif == netif)) {
+      return i;
+    }
+  }
+
+  /* Entry not available. */
+  return -1;
+}
+
+/**
+ * Creates a new entry for an on-link prefix.
+ *
+ * @param prefix the IPv6 prefix that is on-link
+ * @param netif the netif on which the prefix is on-link
+ * @return the index on the prefix table, or -1 if not created
+ */
+static s8_t
+nd6_new_onlink_prefix(const ip6_addr_t *prefix, struct netif *netif)
+{
+  s8_t i;
+
+  /* Create new entry. */
+  for (i = 0; i < LWIP_ND6_NUM_PREFIXES; ++i) {
+    if ((prefix_list[i].netif == NULL) ||
+        (prefix_list[i].invalidation_timer == 0)) {
+      /* Found empty prefix entry. */
+      prefix_list[i].netif = netif;
+      ip6_addr_set(&(prefix_list[i].prefix), prefix);
+      return i;
+    }
+  }
+
+  /* Entry not available. */
+  return -1;
+}
+
+/**
+ * Determine the next hop for a destination. Will determine if the
+ * destination is on-link, else a suitable on-link router is selected.
+ *
+ * The last entry index is cached for fast entry search.
+ *
+ * @param ip6addr the destination address
+ * @param netif the netif on which the packet will be sent
+ * @return the neighbor cache entry for the next hop, ERR_RTE if no
+ *         suitable next hop was found, ERR_MEM if no cache entry
+ *         could be created
+ */
+static s8_t
+nd6_get_next_hop_entry(const ip6_addr_t *ip6addr, struct netif *netif)
+{
+#ifdef LWIP_HOOK_ND6_GET_GW
+  const ip6_addr_t *next_hop_addr;
+#endif /* LWIP_HOOK_ND6_GET_GW */
+  s8_t i;
+  s16_t dst_idx;
+  struct nd6_destination_cache_entry *dest;
+
+  IP6_ADDR_ZONECHECK_NETIF(ip6addr, netif);
+
+#if LWIP_NETIF_HWADDRHINT
+  if (netif->hints != NULL) {
+    /* per-pcb cached entry was given */
+    netif_addr_idx_t addr_hint = netif->hints->addr_hint;
+    if (addr_hint < LWIP_ND6_NUM_DESTINATIONS) {
+      nd6_cached_destination_index = addr_hint;
+    }
+  }
+#endif /* LWIP_NETIF_HWADDRHINT */
+
+  LWIP_ASSERT("sane cache index", nd6_cached_destination_index < LWIP_ND6_NUM_DESTINATIONS);
+
+  /* Look for ip6addr in destination cache. */
+  dest = &destination_cache[nd6_cached_destination_index];
+  if (ip6_addr_eq(ip6addr, &dest->destination_addr)) {
+    /* the cached entry index is the right one! */
+    /* do nothing. */
+    ND6_STATS_INC(nd6.cachehit);
+  } else {
+    /* Search destination cache. */
+    dst_idx = nd6_find_destination_cache_entry(ip6addr);
+    if (dst_idx >= 0) {
+      /* found destination entry. make it our new cached index. */
+      LWIP_ASSERT("type overflow", (size_t)dst_idx < NETIF_ADDR_IDX_MAX);
+      nd6_cached_destination_index = (netif_addr_idx_t)dst_idx;
+      dest = &destination_cache[dst_idx];
+    } else {
+      /* Not found. Create a new destination entry. */
+      dst_idx = nd6_new_destination_cache_entry();
+      if (dst_idx >= 0) {
+        /* got new destination entry. make it our new cached index. */
+        LWIP_ASSERT("type overflow", (size_t)dst_idx < NETIF_ADDR_IDX_MAX);
+        nd6_cached_destination_index = (netif_addr_idx_t)dst_idx;
+        dest = &destination_cache[dst_idx];
+      } else {
+        /* Could not create a destination cache entry. */
+        return ERR_MEM;
+      }
+
+      /* Copy dest address to destination cache. */
+      ip6_addr_set(&dest->destination_addr, ip6addr);
+
+      /* Now find the next hop. is it a neighbor? */
+      if (ip6_addr_islinklocal(ip6addr) ||
+          nd6_is_prefix_in_netif(ip6addr, netif)) {
+        /* Destination in local link. */
+        dest->pmtu = netif_mtu6(netif);
+        ip6_addr_copy(dest->next_hop_addr, dest->destination_addr);
+#ifdef LWIP_HOOK_ND6_GET_GW
+      } else if ((next_hop_addr = LWIP_HOOK_ND6_GET_GW(netif, ip6addr)) != NULL) {
+        /* Next hop for destination provided by hook function. */
+        dest->pmtu = netif->mtu;
+        ip6_addr_set(&dest->next_hop_addr, next_hop_addr);
+#endif /* LWIP_HOOK_ND6_GET_GW */
+      } else {
+        /* We need to select a router. */
+        i = nd6_select_router(ip6addr, netif);
+        if (i < 0) {
+          /* No router found. */
+          ip6_addr_set_any(&dest->destination_addr);
+          return ERR_RTE;
+        }
+        dest->pmtu = netif_mtu6(netif); /* Start with netif mtu, correct through ICMPv6 if necessary */
+        ip6_addr_copy(dest->next_hop_addr, default_router_list[i].neighbor_entry->next_hop_address);
+      }
+    }
+#if LWIP_NETIF_HWADDRHINT
+    if (netif->hints != NULL) {
+      /* per-pcb cached entry was given */
+      netif->hints->addr_hint = nd6_cached_destination_index;
+    }
+#endif /* LWIP_NETIF_HWADDRHINT */
+  }
+
+  /* Look in neighbor cache for the next-hop address. */
+  if (ip6_addr_eq(&dest->next_hop_addr,
+                   &(neighbor_cache[dest->cached_neighbor_idx].next_hop_address))) {
+    /* Cache hit. */
+    /* Do nothing. */
+    ND6_STATS_INC(nd6.cachehit);
+  } else {
+    i = nd6_find_neighbor_cache_entry(&dest->next_hop_addr);
+    if (i >= 0) {
+      /* Found a matching record, make it new cached entry. */
+      dest->cached_neighbor_idx = i;
+    } else {
+      /* Neighbor not in cache. Make a new entry. */
+      i = nd6_new_neighbor_cache_entry();
+      if (i >= 0) {
+        /* got new neighbor entry. make it our new cached index. */
+        dest->cached_neighbor_idx = i;
+      } else {
+        /* Could not create a neighbor cache entry. */
+        return ERR_MEM;
+      }
+
+      /* Initialize fields. */
+      ip6_addr_copy(neighbor_cache[i].next_hop_address, dest->next_hop_addr);
+      neighbor_cache[i].isrouter = 0;
+      neighbor_cache[i].netif = netif;
+      neighbor_cache[i].state = ND6_INCOMPLETE;
+      neighbor_cache[i].counter.probes_sent = 1;
+      nd6_send_neighbor_cache_probe(&neighbor_cache[i], ND6_SEND_FLAG_MULTICAST_DEST);
+    }
+  }
+
+  /* Reset this destination's age. */
+  dest->age = 0;
+
+  return dest->cached_neighbor_idx;
+}
+
+/**
+ * Queue a packet for a neighbor.
+ *
+ * @param neighbor_index the index in the neighbor cache table
+ * @param q packet to be queued
+ * @return ERR_OK if succeeded, ERR_MEM if out of memory
+ */
+static err_t
+nd6_queue_packet(s8_t neighbor_index, struct pbuf *q)
+{
+  err_t result = ERR_MEM;
+  struct pbuf *p;
+  int copy_needed = 0;
+#if LWIP_ND6_QUEUEING
+  struct nd6_q_entry *new_entry, *r;
+#endif /* LWIP_ND6_QUEUEING */
+
+  if ((neighbor_index < 0) || (neighbor_index >= LWIP_ND6_NUM_NEIGHBORS)) {
+    return ERR_ARG;
+  }
+
+  /* IF q includes a pbuf that must be copied, we have to copy the whole chain
+   * into a new PBUF_RAM. See the definition of PBUF_NEEDS_COPY for details. */
+  p = q;
+  while (p) {
+    if (PBUF_NEEDS_COPY(p)) {
+      copy_needed = 1;
+      break;
+    }
+    p = p->next;
+  }
+  if (copy_needed) {
+    /* copy the whole packet into new pbufs */
+    p = pbuf_clone(PBUF_LINK, PBUF_RAM, q);
+    while ((p == NULL) && (neighbor_cache[neighbor_index].q != NULL)) {
+      /* Free oldest packet (as per RFC recommendation) */
+#if LWIP_ND6_QUEUEING
+      r = neighbor_cache[neighbor_index].q;
+      neighbor_cache[neighbor_index].q = r->next;
+      r->next = NULL;
+      nd6_free_q(r);
+#else /* LWIP_ND6_QUEUEING */
+      pbuf_free(neighbor_cache[neighbor_index].q);
+      neighbor_cache[neighbor_index].q = NULL;
+#endif /* LWIP_ND6_QUEUEING */
+      p = pbuf_clone(PBUF_LINK, PBUF_RAM, q);
+    }
+  } else {
+    /* referencing the old pbuf is enough */
+    p = q;
+    pbuf_ref(p);
+  }
+  /* packet was copied/ref'd? */
+  if (p != NULL) {
+    /* queue packet ... */
+#if LWIP_ND6_QUEUEING
+    /* allocate a new nd6 queue entry */
+    new_entry = NULL;
+    if (nd6_queue_size < MEMP_NUM_ND6_QUEUE) {
+      new_entry = (struct nd6_q_entry *)memp_malloc(MEMP_ND6_QUEUE);
+      nd6_queue_size++;
+    }
+    if ((new_entry == NULL) && (neighbor_cache[neighbor_index].q != NULL)) {
+      /* Free oldest packet (as per RFC recommendation) */
+      r = neighbor_cache[neighbor_index].q;
+      neighbor_cache[neighbor_index].q = r->next;
+      r->next = NULL;
+      nd6_free_q(r);
+      new_entry = (struct nd6_q_entry *)memp_malloc(MEMP_ND6_QUEUE);
+      nd6_queue_size++;
+    }
+    if (new_entry != NULL) {
+      new_entry->next = NULL;
+      new_entry->p = p;
+      if (neighbor_cache[neighbor_index].q != NULL) {
+        /* queue was already existent, append the new entry to the end */
+        r = neighbor_cache[neighbor_index].q;
+        while (r->next != NULL) {
+          r = r->next;
+        }
+        r->next = new_entry;
+      } else {
+        /* queue did not exist, first item in queue */
+        neighbor_cache[neighbor_index].q = new_entry;
+      }
+      LWIP_DEBUGF(LWIP_DBG_TRACE, ("ipv6: queued packet %p on neighbor entry %"S16_F"\n", (void *)p, (s16_t)neighbor_index));
+      result = ERR_OK;
+    } else {
+      /* the pool MEMP_ND6_QUEUE is empty */
+      pbuf_free(p);
+      LWIP_DEBUGF(LWIP_DBG_TRACE, ("ipv6: could not queue a copy of packet %p (out of memory)\n", (void *)p));
+      /* { result == ERR_MEM } through initialization */
+    }
+#else /* LWIP_ND6_QUEUEING */
+    /* Queue a single packet. If an older packet is already queued, free it as per RFC. */
+    if (neighbor_cache[neighbor_index].q != NULL) {
+      pbuf_free(neighbor_cache[neighbor_index].q);
+    }
+    neighbor_cache[neighbor_index].q = p;
+    LWIP_DEBUGF(LWIP_DBG_TRACE, ("ipv6: queued packet %p on neighbor entry %"S16_F"\n", (void *)p, (s16_t)neighbor_index));
+    result = ERR_OK;
+#endif /* LWIP_ND6_QUEUEING */
+  } else {
+    LWIP_DEBUGF(LWIP_DBG_TRACE, ("ipv6: could not queue a copy of packet %p (out of memory)\n", (void *)q));
+    /* { result == ERR_MEM } through initialization */
+  }
+
+  return result;
+}
+
+#if LWIP_ND6_QUEUEING
+/**
+ * Free a complete queue of nd6 q entries
+ *
+ * @param q a queue of nd6_q_entry to free
+ */
+static void
+nd6_free_q(struct nd6_q_entry *q)
+{
+  struct nd6_q_entry *r;
+  LWIP_ASSERT("q != NULL", q != NULL);
+  LWIP_ASSERT("q->p != NULL", q->p != NULL);
+  while (q) {
+    r = q;
+    q = q->next;
+    LWIP_ASSERT("r->p != NULL", (r->p != NULL));
+    pbuf_free(r->p);
+    memp_free(MEMP_ND6_QUEUE, r);
+    nd6_queue_size--;
+  }
+}
+#endif /* LWIP_ND6_QUEUEING */
+
+/**
+ * Send queued packets for a neighbor
+ *
+ * @param i the neighbor to send packets to
+ */
+static void
+nd6_send_q(s8_t i)
+{
+  struct ip6_hdr *ip6hdr;
+  ip6_addr_t dest;
+#if LWIP_ND6_QUEUEING
+  struct nd6_q_entry *q;
+#endif /* LWIP_ND6_QUEUEING */
+
+  if ((i < 0) || (i >= LWIP_ND6_NUM_NEIGHBORS)) {
+    return;
+  }
+
+#if LWIP_ND6_QUEUEING
+  while (neighbor_cache[i].q != NULL) {
+    /* remember first in queue */
+    q = neighbor_cache[i].q;
+    /* pop first item off the queue */
+    neighbor_cache[i].q = q->next;
+    /* Get ipv6 header. */
+    ip6hdr = (struct ip6_hdr *)(q->p->payload);
+    /* Create an aligned copy. */
+    ip6_addr_copy_from_packed(dest, ip6hdr->dest);
+    /* Restore the zone, if applicable. */
+    ip6_addr_assign_zone(&dest, IP6_UNKNOWN, neighbor_cache[i].netif);
+    /* send the queued IPv6 packet */
+    (neighbor_cache[i].netif)->output_ip6(neighbor_cache[i].netif, q->p, &dest);
+    /* free the queued IP packet */
+    pbuf_free(q->p);
+    /* now queue entry can be freed */
+    memp_free(MEMP_ND6_QUEUE, q);
+    nd6_queue_size--;
+  }
+#else /* LWIP_ND6_QUEUEING */
+  if (neighbor_cache[i].q != NULL) {
+    /* Get ipv6 header. */
+    ip6hdr = (struct ip6_hdr *)(neighbor_cache[i].q->payload);
+    /* Create an aligned copy. */
+    ip6_addr_copy_from_packed(dest, ip6hdr->dest);
+    /* Restore the zone, if applicable. */
+    ip6_addr_assign_zone(&dest, IP6_UNKNOWN, neighbor_cache[i].netif);
+    /* send the queued IPv6 packet */
+    (neighbor_cache[i].netif)->output_ip6(neighbor_cache[i].netif, neighbor_cache[i].q, &dest);
+    /* free the queued IP packet */
+    pbuf_free(neighbor_cache[i].q);
+    neighbor_cache[i].q = NULL;
+  }
+#endif /* LWIP_ND6_QUEUEING */
+}
+
+/**
+ * A packet is to be transmitted to a specific IPv6 destination on a specific
+ * interface. Check if we can find the hardware address of the next hop to use
+ * for the packet. If so, give the hardware address to the caller, which should
+ * use it to send the packet right away. Otherwise, enqueue the packet for
+ * later transmission while looking up the hardware address, if possible.
+ *
+ * As such, this function returns one of three different possible results:
+ *
+ * - ERR_OK with a non-NULL 'hwaddrp': the caller should send the packet now.
+ * - ERR_OK with a NULL 'hwaddrp': the packet has been enqueued for later.
+ * - not ERR_OK: something went wrong; forward the error upward in the stack.
+ *
+ * @param netif The lwIP network interface on which the IP packet will be sent.
+ * @param q The pbuf(s) containing the IP packet to be sent.
+ * @param ip6addr The destination IPv6 address of the packet.
+ * @param hwaddrp On success, filled with a pointer to a HW address or NULL (meaning
+ *        the packet has been queued).
+ * @return
+ * - ERR_OK on success, ERR_RTE if no route was found for the packet,
+ * or ERR_MEM if low memory conditions prohibit sending the packet at all.
+ */
+err_t
+nd6_get_next_hop_addr_or_queue(struct netif *netif, struct pbuf *q, const ip6_addr_t *ip6addr, const u8_t **hwaddrp)
+{
+  s8_t i;
+
+  /* Get next hop record. */
+  i = nd6_get_next_hop_entry(ip6addr, netif);
+  if (i < 0) {
+    /* failed to get a next hop neighbor record. */
+    return i;
+  }
+
+  /* Now that we have a destination record, send or queue the packet. */
+  if (neighbor_cache[i].state == ND6_STALE) {
+    /* Switch to delay state. */
+    neighbor_cache[i].state = ND6_DELAY;
+    neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME / ND6_TMR_INTERVAL;
+  }
+  /* @todo should we send or queue if PROBE? send for now, to let unicast NS pass. */
+  if ((neighbor_cache[i].state == ND6_REACHABLE) ||
+      (neighbor_cache[i].state == ND6_DELAY) ||
+      (neighbor_cache[i].state == ND6_PROBE)) {
+
+    /* Tell the caller to send out the packet now. */
+    *hwaddrp = neighbor_cache[i].lladdr;
+    return ERR_OK;
+  }
+
+  /* We should queue packet on this interface. */
+  *hwaddrp = NULL;
+  return nd6_queue_packet(i, q);
+}
+
+
+/**
+ * Get the Path MTU for a destination.
+ *
+ * @param ip6addr the destination address
+ * @param netif the netif on which the packet will be sent
+ * @return the Path MTU, if known, or the netif default MTU
+ */
+u16_t
+nd6_get_destination_mtu(const ip6_addr_t *ip6addr, struct netif *netif)
+{
+  s16_t i;
+
+  i = nd6_find_destination_cache_entry(ip6addr);
+  if (i >= 0) {
+    if (destination_cache[i].pmtu > 0) {
+      return destination_cache[i].pmtu;
+    }
+  }
+
+  if (netif != NULL) {
+    return netif_mtu6(netif);
+  }
+
+  return IP6_MIN_MTU_LENGTH; /* Minimum MTU */
+}
+
+
+#if LWIP_ND6_TCP_REACHABILITY_HINTS
+/**
+ * Provide the Neighbor discovery process with a hint that a
+ * destination is reachable. Called by tcp_receive when ACKs are
+ * received or sent (as per RFC). This is useful to avoid sending
+ * NS messages every 30 seconds.
+ *
+ * @param ip6addr the destination address which is know to be reachable
+ *                by an upper layer protocol (TCP)
+ */
+void
+nd6_reachability_hint(const ip6_addr_t *ip6addr)
+{
+  s8_t i;
+  s16_t dst_idx;
+  struct nd6_destination_cache_entry *dest;
+
+  /* Find destination in cache. */
+  if (ip6_addr_eq(ip6addr, &(destination_cache[nd6_cached_destination_index].destination_addr))) {
+    dst_idx = nd6_cached_destination_index;
+    ND6_STATS_INC(nd6.cachehit);
+  } else {
+    dst_idx = nd6_find_destination_cache_entry(ip6addr);
+  }
+  if (dst_idx < 0) {
+    return;
+  }
+
+  /* Find next hop neighbor in cache. */
+  dest = &destination_cache[dst_idx];
+  if (ip6_addr_eq(&dest->next_hop_addr, &(neighbor_cache[dest->cached_neighbor_idx].next_hop_address))) {
+    i = dest->cached_neighbor_idx;
+    ND6_STATS_INC(nd6.cachehit);
+  } else {
+    i = nd6_find_neighbor_cache_entry(&dest->next_hop_addr);
+  }
+  if (i < 0) {
+    return;
+  }
+
+  /* For safety: don't set as reachable if we don't have a LL address yet. Misuse protection. */
+  if (neighbor_cache[i].state == ND6_INCOMPLETE || neighbor_cache[i].state == ND6_NO_ENTRY) {
+    return;
+  }
+
+  /* Set reachability state. */
+  neighbor_cache[i].state = ND6_REACHABLE;
+  neighbor_cache[i].counter.reachable_time = reachable_time;
+}
+#endif /* LWIP_ND6_TCP_REACHABILITY_HINTS */
+
+/**
+ * Remove all prefix, neighbor_cache and router entries of the specified netif.
+ *
+ * @param netif points to a network interface
+ */
+void
+nd6_cleanup_netif(struct netif *netif)
+{
+  u8_t i;
+  s8_t router_index;
+  for (i = 0; i < LWIP_ND6_NUM_PREFIXES; i++) {
+    if (prefix_list[i].netif == netif) {
+      prefix_list[i].netif = NULL;
+    }
+  }
+  for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) {
+    if (neighbor_cache[i].netif == netif) {
+      for (router_index = 0; router_index < LWIP_ND6_NUM_ROUTERS; router_index++) {
+        if (default_router_list[router_index].neighbor_entry == &neighbor_cache[i]) {
+          default_router_list[router_index].neighbor_entry = NULL;
+          default_router_list[router_index].flags = 0;
+        }
+      }
+      neighbor_cache[i].isrouter = 0;
+      nd6_free_neighbor_cache_entry(i);
+    }
+  }
+  /* Clear the destination cache, since many entries may now have become
+   * invalid for one of several reasons. As destination cache entries have no
+   * netif association, use a sledgehammer approach (this can be improved). */
+  nd6_clear_destination_cache();
+}
+
+#if LWIP_IPV6_MLD
+/**
+ * The state of a local IPv6 address entry is about to change. If needed, join
+ * or leave the solicited-node multicast group for the address.
+ *
+ * @param netif The netif that owns the address.
+ * @param addr_idx The index of the address.
+ * @param new_state The new (IP6_ADDR_) state for the address.
+ */
+void
+nd6_adjust_mld_membership(struct netif *netif, s8_t addr_idx, u8_t new_state)
+{
+  u8_t old_state, old_member, new_member;
+
+  old_state = netif_ip6_addr_state(netif, addr_idx);
+
+  /* Determine whether we were, and should be, a member of the solicited-node
+   * multicast group for this address. For tentative addresses, the group is
+   * not joined until the address enters the TENTATIVE_1 (or VALID) state. */
+  old_member = (old_state != IP6_ADDR_INVALID && old_state != IP6_ADDR_DUPLICATED && old_state != IP6_ADDR_TENTATIVE);
+  new_member = (new_state != IP6_ADDR_INVALID && new_state != IP6_ADDR_DUPLICATED && new_state != IP6_ADDR_TENTATIVE);
+
+  if (old_member != new_member) {
+    ip6_addr_set_solicitednode(&multicast_address, netif_ip6_addr(netif, addr_idx)->addr[3]);
+    ip6_addr_assign_zone(&multicast_address, IP6_MULTICAST, netif);
+
+    if (new_member) {
+      mld6_joingroup_netif(netif, &multicast_address);
+    } else {
+      mld6_leavegroup_netif(netif, &multicast_address);
+    }
+  }
+}
+#endif /* LWIP_IPV6_MLD */
+
+/** Netif was added, set up, or reconnected (link up) */
+void
+nd6_restart_netif(struct netif *netif)
+{
+#if LWIP_IPV6_SEND_ROUTER_SOLICIT
+  /* Send Router Solicitation messages (see RFC 4861, ch. 6.3.7). */
+  netif->rs_count = LWIP_ND6_MAX_MULTICAST_SOLICIT;
+#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */
+}
+
+#endif /* LWIP_IPV6 */
diff --git a/lib/lwip/core/mem.c b/lib/lwip/core/mem.c
new file mode 100644
index 0000000000..8566edce55
--- /dev/null
+++ b/lib/lwip/core/mem.c
@@ -0,0 +1,1017 @@
+/**
+ * @file
+ * Dynamic memory manager
+ *
+ * This is a lightweight replacement for the standard C library malloc().
+ *
+ * If you want to use the standard C library malloc() instead, define
+ * MEM_LIBC_MALLOC to 1 in your lwipopts.h
+ *
+ * To let mem_malloc() use pools (prevents fragmentation and is much faster than
+ * a heap but might waste some memory), define MEM_USE_POOLS to 1, define
+ * MEMP_USE_CUSTOM_POOLS to 1 and create a file "lwippools.h" that includes a list
+ * of pools like this (more pools can be added between _START and _END):
+ *
+ * Define three pools with sizes 256, 512, and 1512 bytes
+ * LWIP_MALLOC_MEMPOOL_START
+ * LWIP_MALLOC_MEMPOOL(20, 256)
+ * LWIP_MALLOC_MEMPOOL(10, 512)
+ * LWIP_MALLOC_MEMPOOL(5, 1512)
+ * LWIP_MALLOC_MEMPOOL_END
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *         Simon Goldschmidt
+ *
+ */
+
+#include "lwip/opt.h"
+#include "lwip/mem.h"
+#include "lwip/def.h"
+#include "lwip/sys.h"
+#include "lwip/stats.h"
+#include "lwip/err.h"
+
+#include <string.h>
+
+#if MEM_LIBC_MALLOC
+#include <stdlib.h> /* for malloc()/free() */
+#endif
+
+/* This is overridable for tests only... */
+#ifndef LWIP_MEM_ILLEGAL_FREE
+#define LWIP_MEM_ILLEGAL_FREE(msg)         LWIP_ASSERT(msg, 0)
+#endif
+
+#define MEM_STATS_INC_LOCKED(x)         SYS_ARCH_LOCKED(MEM_STATS_INC(x))
+#define MEM_STATS_INC_USED_LOCKED(x, y) SYS_ARCH_LOCKED(MEM_STATS_INC_USED(x, y))
+#define MEM_STATS_DEC_USED_LOCKED(x, y) SYS_ARCH_LOCKED(MEM_STATS_DEC_USED(x, y))
+
+#if MEM_OVERFLOW_CHECK
+#define MEM_SANITY_OFFSET   MEM_SANITY_REGION_BEFORE_ALIGNED
+#define MEM_SANITY_OVERHEAD (MEM_SANITY_REGION_BEFORE_ALIGNED + MEM_SANITY_REGION_AFTER_ALIGNED)
+#else
+#define MEM_SANITY_OFFSET   0
+#define MEM_SANITY_OVERHEAD 0
+#endif
+
+#if MEM_OVERFLOW_CHECK || MEMP_OVERFLOW_CHECK
+/**
+ * Check if a mep element was victim of an overflow or underflow
+ * (e.g. the restricted area after/before it has been altered)
+ *
+ * @param p the mem element to check
+ * @param size allocated size of the element
+ * @param descr1 description of the element source shown on error
+ * @param descr2 description of the element source shown on error
+ */
+void
+mem_overflow_check_raw(void *p, size_t size, const char *descr1, const char *descr2)
+{
+#if MEM_SANITY_REGION_AFTER_ALIGNED || MEM_SANITY_REGION_BEFORE_ALIGNED
+  u16_t k;
+  u8_t *m;
+
+#if MEM_SANITY_REGION_AFTER_ALIGNED > 0
+  m = (u8_t *)p + size;
+  for (k = 0; k < MEM_SANITY_REGION_AFTER_ALIGNED; k++) {
+    if (m[k] != 0xcd) {
+      char errstr[128];
+      snprintf(errstr, sizeof(errstr), "detected mem overflow in %s%s", descr1, descr2);
+      LWIP_ASSERT(errstr, 0);
+    }
+  }
+#endif /* MEM_SANITY_REGION_AFTER_ALIGNED > 0 */
+
+#if MEM_SANITY_REGION_BEFORE_ALIGNED > 0
+  m = (u8_t *)p - MEM_SANITY_REGION_BEFORE_ALIGNED;
+  for (k = 0; k < MEM_SANITY_REGION_BEFORE_ALIGNED; k++) {
+    if (m[k] != 0xcd) {
+      char errstr[128];
+      snprintf(errstr, sizeof(errstr), "detected mem underflow in %s%s", descr1, descr2);
+      LWIP_ASSERT(errstr, 0);
+    }
+  }
+#endif /* MEM_SANITY_REGION_BEFORE_ALIGNED > 0 */
+#else
+  LWIP_UNUSED_ARG(p);
+  LWIP_UNUSED_ARG(desc);
+  LWIP_UNUSED_ARG(descr);
+#endif
+}
+
+/**
+ * Initialize the restricted area of a mem element.
+ */
+void
+mem_overflow_init_raw(void *p, size_t size)
+{
+#if MEM_SANITY_REGION_BEFORE_ALIGNED > 0 || MEM_SANITY_REGION_AFTER_ALIGNED > 0
+  u8_t *m;
+#if MEM_SANITY_REGION_BEFORE_ALIGNED > 0
+  m = (u8_t *)p - MEM_SANITY_REGION_BEFORE_ALIGNED;
+  memset(m, 0xcd, MEM_SANITY_REGION_BEFORE_ALIGNED);
+#endif
+#if MEM_SANITY_REGION_AFTER_ALIGNED > 0
+  m = (u8_t *)p + size;
+  memset(m, 0xcd, MEM_SANITY_REGION_AFTER_ALIGNED);
+#endif
+#else /* MEM_SANITY_REGION_BEFORE_ALIGNED > 0 || MEM_SANITY_REGION_AFTER_ALIGNED > 0 */
+  LWIP_UNUSED_ARG(p);
+  LWIP_UNUSED_ARG(desc);
+#endif /* MEM_SANITY_REGION_BEFORE_ALIGNED > 0 || MEM_SANITY_REGION_AFTER_ALIGNED > 0 */
+}
+#endif /* MEM_OVERFLOW_CHECK || MEMP_OVERFLOW_CHECK */
+
+#if MEM_LIBC_MALLOC || MEM_USE_POOLS
+
+/** mem_init is not used when using pools instead of a heap or using
+ * C library malloc().
+ */
+void
+mem_init(void)
+{
+}
+
+/** mem_trim is not used when using pools instead of a heap or using
+ * C library malloc(): we can't free part of a pool element and the stack
+ * support mem_trim() to return a different pointer
+ */
+void *
+mem_trim(void *mem, mem_size_t size)
+{
+  LWIP_UNUSED_ARG(size);
+  return mem;
+}
+#endif /* MEM_LIBC_MALLOC || MEM_USE_POOLS */
+
+#if MEM_LIBC_MALLOC
+/* lwIP heap implemented using C library malloc() */
+
+/* in case C library malloc() needs extra protection,
+ * allow these defines to be overridden.
+ */
+#ifndef mem_clib_free
+#define mem_clib_free free
+#endif
+#ifndef mem_clib_malloc
+#define mem_clib_malloc malloc
+#endif
+#ifndef mem_clib_calloc
+#define mem_clib_calloc calloc
+#endif
+
+#if LWIP_STATS && MEM_STATS
+#define MEM_LIBC_STATSHELPER_SIZE LWIP_MEM_ALIGN_SIZE(sizeof(mem_size_t))
+#else
+#define MEM_LIBC_STATSHELPER_SIZE 0
+#endif
+
+/**
+ * Allocate a block of memory with a minimum of 'size' bytes.
+ *
+ * @param size is the minimum size of the requested block in bytes.
+ * @return pointer to allocated memory or NULL if no free memory was found.
+ *
+ * Note that the returned value must always be aligned (as defined by MEM_ALIGNMENT).
+ */
+void *
+mem_malloc(mem_size_t size)
+{
+  void *ret = mem_clib_malloc(size + MEM_LIBC_STATSHELPER_SIZE);
+  if (ret == NULL) {
+    MEM_STATS_INC_LOCKED(err);
+  } else {
+    LWIP_ASSERT("malloc() must return aligned memory", LWIP_MEM_ALIGN(ret) == ret);
+#if LWIP_STATS && MEM_STATS
+    *(mem_size_t *)ret = size;
+    ret = (u8_t *)ret + MEM_LIBC_STATSHELPER_SIZE;
+    MEM_STATS_INC_USED_LOCKED(used, size);
+#endif
+  }
+  return ret;
+}
+
+/** Put memory back on the heap
+ *
+ * @param rmem is the pointer as returned by a previous call to mem_malloc()
+ */
+void
+mem_free(void *rmem)
+{
+  LWIP_ASSERT("rmem != NULL", (rmem != NULL));
+  LWIP_ASSERT("rmem == MEM_ALIGN(rmem)", (rmem == LWIP_MEM_ALIGN(rmem)));
+#if LWIP_STATS && MEM_STATS
+  rmem = (u8_t *)rmem - MEM_LIBC_STATSHELPER_SIZE;
+  MEM_STATS_DEC_USED_LOCKED(used, *(mem_size_t *)rmem);
+#endif
+  mem_clib_free(rmem);
+}
+
+#elif MEM_USE_POOLS
+
+/* lwIP heap implemented with different sized pools */
+
+/**
+ * Allocate memory: determine the smallest pool that is big enough
+ * to contain an element of 'size' and get an element from that pool.
+ *
+ * @param size the size in bytes of the memory needed
+ * @return a pointer to the allocated memory or NULL if the pool is empty
+ */
+void *
+mem_malloc(mem_size_t size)
+{
+  void *ret;
+  struct memp_malloc_helper *element = NULL;
+  memp_t poolnr;
+  mem_size_t required_size = size + LWIP_MEM_ALIGN_SIZE(sizeof(struct memp_malloc_helper));
+
+  for (poolnr = MEMP_POOL_FIRST; poolnr <= MEMP_POOL_LAST; poolnr = (memp_t)(poolnr + 1)) {
+    /* is this pool big enough to hold an element of the required size
+       plus a struct memp_malloc_helper that saves the pool this element came from? */
+    if (required_size <= memp_pools[poolnr]->size) {
+      element = (struct memp_malloc_helper *)memp_malloc(poolnr);
+      if (element == NULL) {
+        /* No need to DEBUGF or ASSERT: This error is already taken care of in memp.c */
+#if MEM_USE_POOLS_TRY_BIGGER_POOL
+        /** Try a bigger pool if this one is empty! */
+        if (poolnr < MEMP_POOL_LAST) {
+          continue;
+        }
+#endif /* MEM_USE_POOLS_TRY_BIGGER_POOL */
+        MEM_STATS_INC_LOCKED(err);
+        return NULL;
+      }
+      break;
+    }
+  }
+  if (poolnr > MEMP_POOL_LAST) {
+    LWIP_ASSERT("mem_malloc(): no pool is that big!", 0);
+    MEM_STATS_INC_LOCKED(err);
+    return NULL;
+  }
+
+  /* save the pool number this element came from */
+  element->poolnr = poolnr;
+  /* and return a pointer to the memory directly after the struct memp_malloc_helper */
+  ret = (u8_t *)element + LWIP_MEM_ALIGN_SIZE(sizeof(struct memp_malloc_helper));
+
+#if MEMP_OVERFLOW_CHECK || (LWIP_STATS && MEM_STATS)
+  /* truncating to u16_t is safe because struct memp_desc::size is u16_t */
+  element->size = (u16_t)size;
+  MEM_STATS_INC_USED_LOCKED(used, element->size);
+#endif /* MEMP_OVERFLOW_CHECK || (LWIP_STATS && MEM_STATS) */
+#if MEMP_OVERFLOW_CHECK
+  /* initialize unused memory (diff between requested size and selected pool's size) */
+  memset((u8_t *)ret + size, 0xcd, memp_pools[poolnr]->size - size);
+#endif /* MEMP_OVERFLOW_CHECK */
+  return ret;
+}
+
+/**
+ * Free memory previously allocated by mem_malloc. Loads the pool number
+ * and calls memp_free with that pool number to put the element back into
+ * its pool
+ *
+ * @param rmem the memory element to free
+ */
+void
+mem_free(void *rmem)
+{
+  struct memp_malloc_helper *hmem;
+
+  LWIP_ASSERT("rmem != NULL", (rmem != NULL));
+  LWIP_ASSERT("rmem == MEM_ALIGN(rmem)", (rmem == LWIP_MEM_ALIGN(rmem)));
+
+  /* get the original struct memp_malloc_helper */
+  /* cast through void* to get rid of alignment warnings */
+  hmem = (struct memp_malloc_helper *)(void *)((u8_t *)rmem - LWIP_MEM_ALIGN_SIZE(sizeof(struct memp_malloc_helper)));
+
+  LWIP_ASSERT("hmem != NULL", (hmem != NULL));
+  LWIP_ASSERT("hmem == MEM_ALIGN(hmem)", (hmem == LWIP_MEM_ALIGN(hmem)));
+  LWIP_ASSERT("hmem->poolnr < MEMP_MAX", (hmem->poolnr < MEMP_MAX));
+
+  MEM_STATS_DEC_USED_LOCKED(used, hmem->size);
+#if MEMP_OVERFLOW_CHECK
+  {
+    u16_t i;
+    LWIP_ASSERT("MEM_USE_POOLS: invalid chunk size",
+                hmem->size <= memp_pools[hmem->poolnr]->size);
+    /* check that unused memory remained untouched (diff between requested size and selected pool's size) */
+    for (i = hmem->size; i < memp_pools[hmem->poolnr]->size; i++) {
+      u8_t data = *((u8_t *)rmem + i);
+      LWIP_ASSERT("MEM_USE_POOLS: mem overflow detected", data == 0xcd);
+    }
+  }
+#endif /* MEMP_OVERFLOW_CHECK */
+
+  /* and put it in the pool we saved earlier */
+  memp_free(hmem->poolnr, hmem);
+}
+
+#else /* MEM_USE_POOLS */
+/* lwIP replacement for your libc malloc() */
+
+/**
+ * The heap is made up as a list of structs of this type.
+ * This does not have to be aligned since for getting its size,
+ * we only use the macro SIZEOF_STRUCT_MEM, which automatically aligns.
+ */
+struct mem {
+  /** index (-> ram[next]) of the next struct */
+  mem_size_t next;
+  /** index (-> ram[prev]) of the previous struct */
+  mem_size_t prev;
+  /** 1: this area is used; 0: this area is unused */
+  u8_t used;
+#if MEM_OVERFLOW_CHECK
+  /** this keeps track of the user allocation size for guard checks */
+  mem_size_t user_size;
+#endif
+};
+
+/** All allocated blocks will be MIN_SIZE bytes big, at least!
+ * MIN_SIZE can be overridden to suit your needs. Smaller values save space,
+ * larger values could prevent too small blocks to fragment the RAM too much. */
+#ifndef MIN_SIZE
+#define MIN_SIZE             12
+#endif /* MIN_SIZE */
+/* some alignment macros: we define them here for better source code layout */
+#define MIN_SIZE_ALIGNED     LWIP_MEM_ALIGN_SIZE(MIN_SIZE)
+#define SIZEOF_STRUCT_MEM    LWIP_MEM_ALIGN_SIZE(sizeof(struct mem))
+#define MEM_SIZE_ALIGNED     LWIP_MEM_ALIGN_SIZE(MEM_SIZE)
+
+/** If you want to relocate the heap to external memory, simply define
+ * LWIP_RAM_HEAP_POINTER as a void-pointer to that location.
+ * If so, make sure the memory at that location is big enough (see below on
+ * how that space is calculated). */
+#ifndef LWIP_RAM_HEAP_POINTER
+/** the heap. we need one struct mem at the end and some room for alignment */
+LWIP_DECLARE_MEMORY_ALIGNED(ram_heap, MEM_SIZE_ALIGNED + (2U * SIZEOF_STRUCT_MEM));
+#define LWIP_RAM_HEAP_POINTER ram_heap
+#endif /* LWIP_RAM_HEAP_POINTER */
+
+/** pointer to the heap (ram_heap): for alignment, ram is now a pointer instead of an array */
+static u8_t *ram;
+/** the last entry, always unused! */
+static struct mem *ram_end;
+
+/** concurrent access protection */
+#if !NO_SYS
+static sys_mutex_t mem_mutex;
+#endif
+
+#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
+
+static volatile u8_t mem_free_count;
+
+/* Allow mem_free from other (e.g. interrupt) context */
+#define LWIP_MEM_FREE_DECL_PROTECT()  SYS_ARCH_DECL_PROTECT(lev_free)
+#define LWIP_MEM_FREE_PROTECT()       SYS_ARCH_PROTECT(lev_free)
+#define LWIP_MEM_FREE_UNPROTECT()     SYS_ARCH_UNPROTECT(lev_free)
+#define LWIP_MEM_ALLOC_DECL_PROTECT() SYS_ARCH_DECL_PROTECT(lev_alloc)
+#define LWIP_MEM_ALLOC_PROTECT()      SYS_ARCH_PROTECT(lev_alloc)
+#define LWIP_MEM_ALLOC_UNPROTECT()    SYS_ARCH_UNPROTECT(lev_alloc)
+#define LWIP_MEM_LFREE_VOLATILE       volatile
+
+#else /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
+
+/* Protect the heap only by using a mutex */
+#define LWIP_MEM_FREE_DECL_PROTECT()
+#define LWIP_MEM_FREE_PROTECT()    sys_mutex_lock(&mem_mutex)
+#define LWIP_MEM_FREE_UNPROTECT()  sys_mutex_unlock(&mem_mutex)
+/* mem_malloc is protected using mutex AND LWIP_MEM_ALLOC_PROTECT */
+#define LWIP_MEM_ALLOC_DECL_PROTECT()
+#define LWIP_MEM_ALLOC_PROTECT()
+#define LWIP_MEM_ALLOC_UNPROTECT()
+#define LWIP_MEM_LFREE_VOLATILE
+
+#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
+
+/** pointer to the lowest free block, this is used for faster search */
+static struct mem * LWIP_MEM_LFREE_VOLATILE lfree;
+
+#if MEM_SANITY_CHECK
+static void mem_sanity(void);
+#define MEM_SANITY() mem_sanity()
+#else
+#define MEM_SANITY()
+#endif
+
+#if MEM_OVERFLOW_CHECK
+static void
+mem_overflow_init_element(struct mem *mem, mem_size_t user_size)
+{
+  void *p = (u8_t *)mem + SIZEOF_STRUCT_MEM + MEM_SANITY_OFFSET;
+  mem->user_size = user_size;
+  mem_overflow_init_raw(p, user_size);
+}
+
+static void
+mem_overflow_check_element(struct mem *mem)
+{
+  void *p = (u8_t *)mem + SIZEOF_STRUCT_MEM + MEM_SANITY_OFFSET;
+  mem_overflow_check_raw(p, mem->user_size, "heap", "");
+}
+#else /* MEM_OVERFLOW_CHECK */
+#define mem_overflow_init_element(mem, size)
+#define mem_overflow_check_element(mem)
+#endif /* MEM_OVERFLOW_CHECK */
+
+static struct mem *
+ptr_to_mem(mem_size_t ptr)
+{
+  return (struct mem *)(void *)&ram[ptr];
+}
+
+static mem_size_t
+mem_to_ptr(void *mem)
+{
+  return (mem_size_t)((u8_t *)mem - ram);
+}
+
+/**
+ * "Plug holes" by combining adjacent empty struct mems.
+ * After this function is through, there should not exist
+ * one empty struct mem pointing to another empty struct mem.
+ *
+ * @param mem this points to a struct mem which just has been freed
+ * @internal this function is only called by mem_free() and mem_trim()
+ *
+ * This assumes access to the heap is protected by the calling function
+ * already.
+ */
+static void
+plug_holes(struct mem *mem)
+{
+  struct mem *nmem;
+  struct mem *pmem;
+
+  LWIP_ASSERT("plug_holes: mem >= ram", (u8_t *)mem >= ram);
+  LWIP_ASSERT("plug_holes: mem < ram_end", (u8_t *)mem < (u8_t *)ram_end);
+  LWIP_ASSERT("plug_holes: mem->used == 0", mem->used == 0);
+
+  /* plug hole forward */
+  LWIP_ASSERT("plug_holes: mem->next <= MEM_SIZE_ALIGNED", mem->next <= MEM_SIZE_ALIGNED);
+
+  nmem = ptr_to_mem(mem->next);
+  if (mem != nmem && nmem->used == 0 && (u8_t *)nmem != (u8_t *)ram_end) {
+    /* if mem->next is unused and not end of ram, combine mem and mem->next */
+    if (lfree == nmem) {
+      lfree = mem;
+    }
+    mem->next = nmem->next;
+    if (nmem->next != MEM_SIZE_ALIGNED) {
+      ptr_to_mem(nmem->next)->prev = mem_to_ptr(mem);
+    }
+  }
+
+  /* plug hole backward */
+  pmem = ptr_to_mem(mem->prev);
+  if (pmem != mem && pmem->used == 0) {
+    /* if mem->prev is unused, combine mem and mem->prev */
+    if (lfree == mem) {
+      lfree = pmem;
+    }
+    pmem->next = mem->next;
+    if (mem->next != MEM_SIZE_ALIGNED) {
+      ptr_to_mem(mem->next)->prev = mem_to_ptr(pmem);
+    }
+  }
+}
+
+/**
+ * Zero the heap and initialize start, end and lowest-free
+ */
+void
+mem_init(void)
+{
+  struct mem *mem;
+
+  LWIP_ASSERT("Sanity check alignment",
+              (SIZEOF_STRUCT_MEM & (MEM_ALIGNMENT - 1)) == 0);
+
+  /* align the heap */
+  ram = (u8_t *)LWIP_MEM_ALIGN(LWIP_RAM_HEAP_POINTER);
+  /* initialize the start of the heap */
+  mem = (struct mem *)(void *)ram;
+  mem->next = MEM_SIZE_ALIGNED;
+  mem->prev = 0;
+  mem->used = 0;
+  /* initialize the end of the heap */
+  ram_end = ptr_to_mem(MEM_SIZE_ALIGNED);
+  ram_end->used = 1;
+  ram_end->next = MEM_SIZE_ALIGNED;
+  ram_end->prev = MEM_SIZE_ALIGNED;
+  MEM_SANITY();
+
+  /* initialize the lowest-free pointer to the start of the heap */
+  lfree = (struct mem *)(void *)ram;
+
+  MEM_STATS_AVAIL(avail, MEM_SIZE_ALIGNED);
+
+  if (sys_mutex_new(&mem_mutex) != ERR_OK) {
+    LWIP_ASSERT("failed to create mem_mutex", 0);
+  }
+}
+
+/* Check if a struct mem is correctly linked.
+ * If not, double-free is a possible reason.
+ */
+static int
+mem_link_valid(struct mem *mem)
+{
+  struct mem *nmem, *pmem;
+  mem_size_t rmem_idx;
+  rmem_idx = mem_to_ptr(mem);
+  nmem = ptr_to_mem(mem->next);
+  pmem = ptr_to_mem(mem->prev);
+  if ((mem->next > MEM_SIZE_ALIGNED) || (mem->prev > MEM_SIZE_ALIGNED) ||
+      ((mem->prev != rmem_idx) && (pmem->next != rmem_idx)) ||
+      ((nmem != ram_end) && (nmem->prev != rmem_idx))) {
+    return 0;
+  }
+  return 1;
+}
+
+#if MEM_SANITY_CHECK
+static void
+mem_sanity(void)
+{
+  struct mem *mem;
+  u8_t last_used;
+
+  /* begin with first element here */
+  mem = (struct mem *)ram;
+  LWIP_ASSERT("heap element used valid", (mem->used == 0) || (mem->used == 1));
+  last_used = mem->used;
+  LWIP_ASSERT("heap element prev ptr valid", mem->prev == 0);
+  LWIP_ASSERT("heap element next ptr valid", mem->next <= MEM_SIZE_ALIGNED);
+  LWIP_ASSERT("heap element next ptr aligned", LWIP_MEM_ALIGN(ptr_to_mem(mem->next) == ptr_to_mem(mem->next)));
+
+  /* check all elements before the end of the heap */
+  for (mem = ptr_to_mem(mem->next);
+       ((u8_t *)mem > ram) && (mem < ram_end);
+       mem = ptr_to_mem(mem->next)) {
+    LWIP_ASSERT("heap element aligned", LWIP_MEM_ALIGN(mem) == mem);
+    LWIP_ASSERT("heap element prev ptr valid", mem->prev <= MEM_SIZE_ALIGNED);
+    LWIP_ASSERT("heap element next ptr valid", mem->next <= MEM_SIZE_ALIGNED);
+    LWIP_ASSERT("heap element prev ptr aligned", LWIP_MEM_ALIGN(ptr_to_mem(mem->prev) == ptr_to_mem(mem->prev)));
+    LWIP_ASSERT("heap element next ptr aligned", LWIP_MEM_ALIGN(ptr_to_mem(mem->next) == ptr_to_mem(mem->next)));
+
+    if (last_used == 0) {
+      /* 2 unused elements in a row? */
+      LWIP_ASSERT("heap element unused?", mem->used == 1);
+    } else {
+      LWIP_ASSERT("heap element unused member", (mem->used == 0) || (mem->used == 1));
+    }
+
+    LWIP_ASSERT("heap element link valid", mem_link_valid(mem));
+
+    /* used/unused altering */
+    last_used = mem->used;
+  }
+  LWIP_ASSERT("heap end ptr sanity", mem == ptr_to_mem(MEM_SIZE_ALIGNED));
+  LWIP_ASSERT("heap element used valid", mem->used == 1);
+  LWIP_ASSERT("heap element prev ptr valid", mem->prev == MEM_SIZE_ALIGNED);
+  LWIP_ASSERT("heap element next ptr valid", mem->next == MEM_SIZE_ALIGNED);
+}
+#endif /* MEM_SANITY_CHECK */
+
+/**
+ * Put a struct mem back on the heap
+ *
+ * @param rmem is the data portion of a struct mem as returned by a previous
+ *             call to mem_malloc()
+ */
+void
+mem_free(void *rmem)
+{
+  struct mem *mem;
+  LWIP_MEM_FREE_DECL_PROTECT();
+
+  if (rmem == NULL) {
+    LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("mem_free(p == NULL) was called.\n"));
+    return;
+  }
+  if ((((mem_ptr_t)rmem) & (MEM_ALIGNMENT - 1)) != 0) {
+    LWIP_MEM_ILLEGAL_FREE("mem_free: sanity check alignment");
+    LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SEVERE, ("mem_free: sanity check alignment\n"));
+    /* protect mem stats from concurrent access */
+    MEM_STATS_INC_LOCKED(illegal);
+    return;
+  }
+
+  /* Get the corresponding struct mem: */
+  /* cast through void* to get rid of alignment warnings */
+  mem = (struct mem *)(void *)((u8_t *)rmem - (SIZEOF_STRUCT_MEM + MEM_SANITY_OFFSET));
+
+  if ((u8_t *)mem < ram || (u8_t *)rmem + MIN_SIZE_ALIGNED > (u8_t *)ram_end) {
+    LWIP_MEM_ILLEGAL_FREE("mem_free: illegal memory");
+    LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SEVERE, ("mem_free: illegal memory\n"));
+    /* protect mem stats from concurrent access */
+    MEM_STATS_INC_LOCKED(illegal);
+    return;
+  }
+#if MEM_OVERFLOW_CHECK
+  mem_overflow_check_element(mem);
+#endif
+  /* protect the heap from concurrent access */
+  LWIP_MEM_FREE_PROTECT();
+  /* mem has to be in a used state */
+  if (!mem->used) {
+    LWIP_MEM_ILLEGAL_FREE("mem_free: illegal memory: double free");
+    LWIP_MEM_FREE_UNPROTECT();
+    LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SEVERE, ("mem_free: illegal memory: double free?\n"));
+    /* protect mem stats from concurrent access */
+    MEM_STATS_INC_LOCKED(illegal);
+    return;
+  }
+
+  if (!mem_link_valid(mem)) {
+    LWIP_MEM_ILLEGAL_FREE("mem_free: illegal memory: non-linked: double free");
+    LWIP_MEM_FREE_UNPROTECT();
+    LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SEVERE, ("mem_free: illegal memory: non-linked: double free?\n"));
+    /* protect mem stats from concurrent access */
+    MEM_STATS_INC_LOCKED(illegal);
+    return;
+  }
+
+  /* mem is now unused. */
+  mem->used = 0;
+
+  if (mem < lfree) {
+    /* the newly freed struct is now the lowest */
+    lfree = mem;
+  }
+
+  MEM_STATS_DEC_USED(used, mem->next - (mem_size_t)(((u8_t *)mem - ram)));
+
+  /* finally, see if prev or next are free also */
+  plug_holes(mem);
+  MEM_SANITY();
+#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
+  mem_free_count = 1;
+#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
+  LWIP_MEM_FREE_UNPROTECT();
+}
+
+/**
+ * Shrink memory returned by mem_malloc().
+ *
+ * @param rmem pointer to memory allocated by mem_malloc the is to be shrunk
+ * @param new_size required size after shrinking (needs to be smaller than or
+ *                equal to the previous size)
+ * @return for compatibility reasons: is always == rmem, at the moment
+ *         or NULL if newsize is > old size, in which case rmem is NOT touched
+ *         or freed!
+ */
+void *
+mem_trim(void *rmem, mem_size_t new_size)
+{
+  mem_size_t size, newsize;
+  mem_size_t ptr, ptr2;
+  struct mem *mem, *mem2;
+  /* use the FREE_PROTECT here: it protects with sem OR SYS_ARCH_PROTECT */
+  LWIP_MEM_FREE_DECL_PROTECT();
+
+  /* Expand the size of the allocated memory region so that we can
+     adjust for alignment. */
+  newsize = (mem_size_t)LWIP_MEM_ALIGN_SIZE(new_size);
+  if (newsize < MIN_SIZE_ALIGNED) {
+    /* every data block must be at least MIN_SIZE_ALIGNED long */
+    newsize = MIN_SIZE_ALIGNED;
+  }
+#if MEM_OVERFLOW_CHECK
+  newsize += MEM_SANITY_REGION_BEFORE_ALIGNED + MEM_SANITY_REGION_AFTER_ALIGNED;
+#endif
+  if ((newsize > MEM_SIZE_ALIGNED) || (newsize < new_size)) {
+    return NULL;
+  }
+
+  LWIP_ASSERT("mem_trim: legal memory", (u8_t *)rmem >= (u8_t *)ram &&
+              (u8_t *)rmem < (u8_t *)ram_end);
+
+  if ((u8_t *)rmem < (u8_t *)ram || (u8_t *)rmem >= (u8_t *)ram_end) {
+    LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SEVERE, ("mem_trim: illegal memory\n"));
+    /* protect mem stats from concurrent access */
+    MEM_STATS_INC_LOCKED(illegal);
+    return rmem;
+  }
+  /* Get the corresponding struct mem ... */
+  /* cast through void* to get rid of alignment warnings */
+  mem = (struct mem *)(void *)((u8_t *)rmem - (SIZEOF_STRUCT_MEM + MEM_SANITY_OFFSET));
+#if MEM_OVERFLOW_CHECK
+  mem_overflow_check_element(mem);
+#endif
+  /* ... and its offset pointer */
+  ptr = mem_to_ptr(mem);
+
+  size = (mem_size_t)((mem_size_t)(mem->next - ptr) - (SIZEOF_STRUCT_MEM + MEM_SANITY_OVERHEAD));
+  LWIP_ASSERT("mem_trim can only shrink memory", newsize <= size);
+  if (newsize > size) {
+    /* not supported */
+    return NULL;
+  }
+  if (newsize == size) {
+    /* No change in size, simply return */
+    return rmem;
+  }
+
+  /* protect the heap from concurrent access */
+  LWIP_MEM_FREE_PROTECT();
+
+  mem2 = ptr_to_mem(mem->next);
+  if (mem2->used == 0) {
+    /* The next struct is unused, we can simply move it at little */
+    mem_size_t next;
+    LWIP_ASSERT("invalid next ptr", mem->next != MEM_SIZE_ALIGNED);
+    /* remember the old next pointer */
+    next = mem2->next;
+    /* create new struct mem which is moved directly after the shrunk mem */
+    ptr2 = (mem_size_t)(ptr + SIZEOF_STRUCT_MEM + newsize);
+    if (lfree == mem2) {
+      lfree = ptr_to_mem(ptr2);
+    }
+    mem2 = ptr_to_mem(ptr2);
+    mem2->used = 0;
+    /* restore the next pointer */
+    mem2->next = next;
+    /* link it back to mem */
+    mem2->prev = ptr;
+    /* link mem to it */
+    mem->next = ptr2;
+    /* last thing to restore linked list: as we have moved mem2,
+     * let 'mem2->next->prev' point to mem2 again. but only if mem2->next is not
+     * the end of the heap */
+    if (mem2->next != MEM_SIZE_ALIGNED) {
+      ptr_to_mem(mem2->next)->prev = ptr2;
+    }
+    MEM_STATS_DEC_USED(used, (size - newsize));
+    /* no need to plug holes, we've already done that */
+  } else if (newsize + SIZEOF_STRUCT_MEM + MIN_SIZE_ALIGNED <= size) {
+    /* Next struct is used but there's room for another struct mem with
+     * at least MIN_SIZE_ALIGNED of data.
+     * Old size ('size') must be big enough to contain at least 'newsize' plus a struct mem
+     * ('SIZEOF_STRUCT_MEM') with some data ('MIN_SIZE_ALIGNED').
+     * @todo we could leave out MIN_SIZE_ALIGNED. We would create an empty
+     *       region that couldn't hold data, but when mem->next gets freed,
+     *       the 2 regions would be combined, resulting in more free memory */
+    ptr2 = (mem_size_t)(ptr + SIZEOF_STRUCT_MEM + newsize);
+    LWIP_ASSERT("invalid next ptr", mem->next != MEM_SIZE_ALIGNED);
+    mem2 = ptr_to_mem(ptr2);
+    if (mem2 < lfree) {
+      lfree = mem2;
+    }
+    mem2->used = 0;
+    mem2->next = mem->next;
+    mem2->prev = ptr;
+    mem->next = ptr2;
+    if (mem2->next != MEM_SIZE_ALIGNED) {
+      ptr_to_mem(mem2->next)->prev = ptr2;
+    }
+    MEM_STATS_DEC_USED(used, (size - newsize));
+    /* the original mem->next is used, so no need to plug holes! */
+  }
+  /* else {
+    next struct mem is used but size between mem and mem2 is not big enough
+    to create another struct mem
+    -> don't do anyhting.
+    -> the remaining space stays unused since it is too small
+  } */
+#if MEM_OVERFLOW_CHECK
+  mem_overflow_init_element(mem, new_size);
+#endif
+  MEM_SANITY();
+#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
+  mem_free_count = 1;
+#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
+  LWIP_MEM_FREE_UNPROTECT();
+  return rmem;
+}
+
+/**
+ * Allocate a block of memory with a minimum of 'size' bytes.
+ *
+ * @param size_in is the minimum size of the requested block in bytes.
+ * @return pointer to allocated memory or NULL if no free memory was found.
+ *
+ * Note that the returned value will always be aligned (as defined by MEM_ALIGNMENT).
+ */
+void *
+mem_malloc(mem_size_t size_in)
+{
+  mem_size_t ptr, ptr2, size;
+  struct mem *mem, *mem2;
+#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
+  u8_t local_mem_free_count = 0;
+#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
+  LWIP_MEM_ALLOC_DECL_PROTECT();
+
+  if (size_in == 0) {
+    return NULL;
+  }
+
+  /* Expand the size of the allocated memory region so that we can
+     adjust for alignment. */
+  size = (mem_size_t)LWIP_MEM_ALIGN_SIZE(size_in);
+  if (size < MIN_SIZE_ALIGNED) {
+    /* every data block must be at least MIN_SIZE_ALIGNED long */
+    size = MIN_SIZE_ALIGNED;
+  }
+#if MEM_OVERFLOW_CHECK
+  size += MEM_SANITY_REGION_BEFORE_ALIGNED + MEM_SANITY_REGION_AFTER_ALIGNED;
+#endif
+  if ((size > MEM_SIZE_ALIGNED) || (size < size_in)) {
+    return NULL;
+  }
+
+  /* protect the heap from concurrent access */
+  sys_mutex_lock(&mem_mutex);
+  LWIP_MEM_ALLOC_PROTECT();
+#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
+  /* run as long as a mem_free disturbed mem_malloc or mem_trim */
+  do {
+    local_mem_free_count = 0;
+#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
+
+    /* Scan through the heap searching for a free block that is big enough,
+     * beginning with the lowest free block.
+     */
+    for (ptr = mem_to_ptr(lfree); ptr < MEM_SIZE_ALIGNED - size;
+         ptr = ptr_to_mem(ptr)->next) {
+      mem = ptr_to_mem(ptr);
+#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
+      mem_free_count = 0;
+      LWIP_MEM_ALLOC_UNPROTECT();
+      /* allow mem_free or mem_trim to run */
+      LWIP_MEM_ALLOC_PROTECT();
+      if (mem_free_count != 0) {
+        /* If mem_free or mem_trim have run, we have to restart since they
+           could have altered our current struct mem. */
+        local_mem_free_count = 1;
+        break;
+      }
+#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
+
+      if ((!mem->used) &&
+          (mem->next - (ptr + SIZEOF_STRUCT_MEM)) >= size) {
+        /* mem is not used and at least perfect fit is possible:
+         * mem->next - (ptr + SIZEOF_STRUCT_MEM) gives us the 'user data size' of mem */
+
+        if (mem->next - (ptr + SIZEOF_STRUCT_MEM) >= (size + SIZEOF_STRUCT_MEM + MIN_SIZE_ALIGNED)) {
+          /* (in addition to the above, we test if another struct mem (SIZEOF_STRUCT_MEM) containing
+           * at least MIN_SIZE_ALIGNED of data also fits in the 'user data space' of 'mem')
+           * -> split large block, create empty remainder,
+           * remainder must be large enough to contain MIN_SIZE_ALIGNED data: if
+           * mem->next - (ptr + (2*SIZEOF_STRUCT_MEM)) == size,
+           * struct mem would fit in but no data between mem2 and mem2->next
+           * @todo we could leave out MIN_SIZE_ALIGNED. We would create an empty
+           *       region that couldn't hold data, but when mem->next gets freed,
+           *       the 2 regions would be combined, resulting in more free memory
+           */
+          ptr2 = (mem_size_t)(ptr + SIZEOF_STRUCT_MEM + size);
+          LWIP_ASSERT("invalid next ptr",ptr2 != MEM_SIZE_ALIGNED);
+          /* create mem2 struct */
+          mem2 = ptr_to_mem(ptr2);
+          mem2->used = 0;
+          mem2->next = mem->next;
+          mem2->prev = ptr;
+          /* and insert it between mem and mem->next */
+          mem->next = ptr2;
+          mem->used = 1;
+
+          if (mem2->next != MEM_SIZE_ALIGNED) {
+            ptr_to_mem(mem2->next)->prev = ptr2;
+          }
+          MEM_STATS_INC_USED(used, (size + SIZEOF_STRUCT_MEM));
+        } else {
+          /* (a mem2 struct does no fit into the user data space of mem and mem->next will always
+           * be used at this point: if not we have 2 unused structs in a row, plug_holes should have
+           * take care of this).
+           * -> near fit or exact fit: do not split, no mem2 creation
+           * also can't move mem->next directly behind mem, since mem->next
+           * will always be used at this point!
+           */
+          mem->used = 1;
+          MEM_STATS_INC_USED(used, mem->next - mem_to_ptr(mem));
+        }
+#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
+mem_malloc_adjust_lfree:
+#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
+        if (mem == lfree) {
+          struct mem *cur = lfree;
+          /* Find next free block after mem and update lowest free pointer */
+          while (cur->used && cur != ram_end) {
+#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
+            mem_free_count = 0;
+            LWIP_MEM_ALLOC_UNPROTECT();
+            /* prevent high interrupt latency... */
+            LWIP_MEM_ALLOC_PROTECT();
+            if (mem_free_count != 0) {
+              /* If mem_free or mem_trim have run, we have to restart since they
+                 could have altered our current struct mem or lfree. */
+              goto mem_malloc_adjust_lfree;
+            }
+#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
+            cur = ptr_to_mem(cur->next);
+          }
+          lfree = cur;
+          LWIP_ASSERT("mem_malloc: !lfree->used", ((lfree == ram_end) || (!lfree->used)));
+        }
+        LWIP_MEM_ALLOC_UNPROTECT();
+        sys_mutex_unlock(&mem_mutex);
+        LWIP_ASSERT("mem_malloc: allocated memory not above ram_end.",
+                    (mem_ptr_t)mem + SIZEOF_STRUCT_MEM + size <= (mem_ptr_t)ram_end);
+        LWIP_ASSERT("mem_malloc: allocated memory properly aligned.",
+                    ((mem_ptr_t)mem + SIZEOF_STRUCT_MEM) % MEM_ALIGNMENT == 0);
+        LWIP_ASSERT("mem_malloc: sanity check alignment",
+                    (((mem_ptr_t)mem) & (MEM_ALIGNMENT - 1)) == 0);
+
+#if MEM_OVERFLOW_CHECK
+        mem_overflow_init_element(mem, size_in);
+#endif
+        MEM_SANITY();
+        return (u8_t *)mem + SIZEOF_STRUCT_MEM + MEM_SANITY_OFFSET;
+      }
+    }
+#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
+    /* if we got interrupted by a mem_free, try again */
+  } while (local_mem_free_count != 0);
+#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
+  MEM_STATS_INC(err);
+  LWIP_MEM_ALLOC_UNPROTECT();
+  sys_mutex_unlock(&mem_mutex);
+  LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("mem_malloc: could not allocate %"S16_F" bytes\n", (s16_t)size));
+  return NULL;
+}
+
+#endif /* MEM_USE_POOLS */
+
+#if MEM_LIBC_MALLOC && (!LWIP_STATS || !MEM_STATS)
+void *
+mem_calloc(mem_size_t count, mem_size_t size)
+{
+  return mem_clib_calloc(count, size);
+}
+
+#else /* MEM_LIBC_MALLOC && (!LWIP_STATS || !MEM_STATS) */
+/**
+ * Contiguously allocates enough space for count objects that are size bytes
+ * of memory each and returns a pointer to the allocated memory.
+ *
+ * The allocated memory is filled with bytes of value zero.
+ *
+ * @param count number of objects to allocate
+ * @param size size of the objects to allocate
+ * @return pointer to allocated memory / NULL pointer if there is an error
+ */
+void *
+mem_calloc(mem_size_t count, mem_size_t size)
+{
+  void *p;
+  size_t alloc_size = (size_t)count * (size_t)size;
+
+  if ((size_t)(mem_size_t)alloc_size != alloc_size) {
+    LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("mem_calloc: could not allocate %"SZT_F" bytes\n", alloc_size));
+    return NULL;
+  }
+
+  /* allocate 'count' objects of size 'size' */
+  p = mem_malloc((mem_size_t)alloc_size);
+  if (p) {
+    /* zero the memory */
+    memset(p, 0, alloc_size);
+  }
+  return p;
+}
+#endif /* MEM_LIBC_MALLOC && (!LWIP_STATS || !MEM_STATS) */
diff --git a/lib/lwip/core/memp.c b/lib/lwip/core/memp.c
new file mode 100644
index 0000000000..352ce5a551
--- /dev/null
+++ b/lib/lwip/core/memp.c
@@ -0,0 +1,447 @@
+/**
+ * @file
+ * Dynamic pool memory manager
+ *
+ * lwIP has dedicated pools for many structures (netconn, protocol control blocks,
+ * packet buffers, ...). All these pools are managed here.
+ *
+ * @defgroup mempool Memory pools
+ * @ingroup infrastructure
+ * Custom memory pools
+
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#include "lwip/memp.h"
+#include "lwip/sys.h"
+#include "lwip/stats.h"
+
+#include <string.h>
+
+/* Make sure we include everything we need for size calculation required by memp_std.h */
+#include "lwip/pbuf.h"
+#include "lwip/raw.h"
+#include "lwip/udp.h"
+#include "lwip/tcp.h"
+#include "lwip/priv/tcp_priv.h"
+#include "lwip/altcp.h"
+#include "lwip/ip4_frag.h"
+#include "lwip/netbuf.h"
+#include "lwip/api.h"
+#include "lwip/priv/tcpip_priv.h"
+#include "lwip/priv/api_msg.h"
+#include "lwip/priv/sockets_priv.h"
+#include "lwip/etharp.h"
+#include "lwip/igmp.h"
+#include "lwip/timeouts.h"
+/* needed by default MEMP_NUM_SYS_TIMEOUT */
+#include "netif/ppp/ppp_opts.h"
+#include "lwip/netdb.h"
+#include "lwip/dns.h"
+#include "lwip/priv/nd6_priv.h"
+#include "lwip/ip6_frag.h"
+#include "lwip/mld6.h"
+
+#define LWIP_MEMPOOL(name,num,size,desc) LWIP_MEMPOOL_DECLARE(name,num,size,desc)
+#include "lwip/priv/memp_std.h"
+
+const struct memp_desc *const memp_pools[MEMP_MAX] = {
+#define LWIP_MEMPOOL(name,num,size,desc) &memp_ ## name,
+#include "lwip/priv/memp_std.h"
+};
+
+#ifdef LWIP_HOOK_FILENAME
+#include LWIP_HOOK_FILENAME
+#endif
+
+#if MEMP_MEM_MALLOC && MEMP_OVERFLOW_CHECK >= 2
+#undef MEMP_OVERFLOW_CHECK
+/* MEMP_OVERFLOW_CHECK >= 2 does not work with MEMP_MEM_MALLOC, use 1 instead */
+#define MEMP_OVERFLOW_CHECK 1
+#endif
+
+#if MEMP_SANITY_CHECK && !MEMP_MEM_MALLOC
+/**
+ * Check that memp-lists don't form a circle, using "Floyd's cycle-finding algorithm".
+ */
+static int
+memp_sanity(const struct memp_desc *desc)
+{
+  struct memp *t, *h;
+
+  t = *desc->tab;
+  if (t != NULL) {
+    for (h = t->next; (t != NULL) && (h != NULL); t = t->next,
+         h = ((h->next != NULL) ? h->next->next : NULL)) {
+      if (t == h) {
+        return 0;
+      }
+    }
+  }
+
+  return 1;
+}
+#endif /* MEMP_SANITY_CHECK && !MEMP_MEM_MALLOC */
+
+#if MEMP_OVERFLOW_CHECK
+/**
+ * Check if a memp element was victim of an overflow or underflow
+ * (e.g. the restricted area after/before it has been altered)
+ *
+ * @param p the memp element to check
+ * @param desc the pool p comes from
+ */
+static void
+memp_overflow_check_element(struct memp *p, const struct memp_desc *desc)
+{
+  mem_overflow_check_raw((u8_t *)p + MEMP_SIZE, desc->size, "pool ", desc->desc);
+}
+
+/**
+ * Initialize the restricted area of on memp element.
+ */
+static void
+memp_overflow_init_element(struct memp *p, const struct memp_desc *desc)
+{
+  mem_overflow_init_raw((u8_t *)p + MEMP_SIZE, desc->size);
+}
+
+#if MEMP_OVERFLOW_CHECK >= 2
+/**
+ * Do an overflow check for all elements in every pool.
+ *
+ * @see memp_overflow_check_element for a description of the check
+ */
+static void
+memp_overflow_check_all(void)
+{
+  u16_t i, j;
+  struct memp *p;
+  SYS_ARCH_DECL_PROTECT(old_level);
+  SYS_ARCH_PROTECT(old_level);
+
+  for (i = 0; i < MEMP_MAX; ++i) {
+    p = (struct memp *)LWIP_MEM_ALIGN(memp_pools[i]->base);
+    for (j = 0; j < memp_pools[i]->num; ++j) {
+      memp_overflow_check_element(p, memp_pools[i]);
+      p = LWIP_ALIGNMENT_CAST(struct memp *, ((u8_t *)p + MEMP_SIZE + memp_pools[i]->size + MEM_SANITY_REGION_AFTER_ALIGNED));
+    }
+  }
+  SYS_ARCH_UNPROTECT(old_level);
+}
+#endif /* MEMP_OVERFLOW_CHECK >= 2 */
+#endif /* MEMP_OVERFLOW_CHECK */
+
+/**
+ * Initialize custom memory pool.
+ * Related functions: memp_malloc_pool, memp_free_pool
+ *
+ * @param desc pool to initialize
+ */
+void
+memp_init_pool(const struct memp_desc *desc)
+{
+#if MEMP_MEM_MALLOC
+  LWIP_UNUSED_ARG(desc);
+#else
+  int i;
+  struct memp *memp;
+
+  *desc->tab = NULL;
+  memp = (struct memp *)LWIP_MEM_ALIGN(desc->base);
+#if MEMP_MEM_INIT
+  /* force memset on pool memory */
+  memset(memp, 0, (size_t)desc->num * (MEMP_SIZE + desc->size
+#if MEMP_OVERFLOW_CHECK
+                                       + MEM_SANITY_REGION_AFTER_ALIGNED
+#endif
+                                      ));
+#endif
+  /* create a linked list of memp elements */
+  for (i = 0; i < desc->num; ++i) {
+    memp->next = *desc->tab;
+    *desc->tab = memp;
+#if MEMP_OVERFLOW_CHECK
+    memp_overflow_init_element(memp, desc);
+#endif /* MEMP_OVERFLOW_CHECK */
+    /* cast through void* to get rid of alignment warnings */
+    memp = (struct memp *)(void *)((u8_t *)memp + MEMP_SIZE + desc->size
+#if MEMP_OVERFLOW_CHECK
+                                   + MEM_SANITY_REGION_AFTER_ALIGNED
+#endif
+                                  );
+  }
+#if MEMP_STATS
+  desc->stats->avail = desc->num;
+#endif /* MEMP_STATS */
+#endif /* !MEMP_MEM_MALLOC */
+
+#if MEMP_STATS && (defined(LWIP_DEBUG) || LWIP_STATS_DISPLAY)
+  desc->stats->name  = desc->desc;
+#endif /* MEMP_STATS && (defined(LWIP_DEBUG) || LWIP_STATS_DISPLAY) */
+}
+
+/**
+ * Initializes lwIP built-in pools.
+ * Related functions: memp_malloc, memp_free
+ *
+ * Carves out memp_memory into linked lists for each pool-type.
+ */
+void
+memp_init(void)
+{
+  u16_t i;
+
+  /* for every pool: */
+  for (i = 0; i < LWIP_ARRAYSIZE(memp_pools); i++) {
+    memp_init_pool(memp_pools[i]);
+
+#if LWIP_STATS && MEMP_STATS
+    lwip_stats.memp[i] = memp_pools[i]->stats;
+#endif
+  }
+
+#if MEMP_OVERFLOW_CHECK >= 2
+  /* check everything a first time to see if it worked */
+  memp_overflow_check_all();
+#endif /* MEMP_OVERFLOW_CHECK >= 2 */
+}
+
+static void *
+#if !MEMP_OVERFLOW_CHECK
+do_memp_malloc_pool(const struct memp_desc *desc)
+#else
+do_memp_malloc_pool_fn(const struct memp_desc *desc, const char *file, const int line)
+#endif
+{
+  struct memp *memp;
+  SYS_ARCH_DECL_PROTECT(old_level);
+
+#if MEMP_MEM_MALLOC
+  memp = (struct memp *)mem_malloc(MEMP_SIZE + MEMP_ALIGN_SIZE(desc->size));
+  SYS_ARCH_PROTECT(old_level);
+#else /* MEMP_MEM_MALLOC */
+  SYS_ARCH_PROTECT(old_level);
+
+  memp = *desc->tab;
+#endif /* MEMP_MEM_MALLOC */
+
+  if (memp != NULL) {
+#if !MEMP_MEM_MALLOC
+#if MEMP_OVERFLOW_CHECK == 1
+    memp_overflow_check_element(memp, desc);
+#endif /* MEMP_OVERFLOW_CHECK */
+
+    *desc->tab = memp->next;
+#if MEMP_OVERFLOW_CHECK
+    memp->next = NULL;
+#endif /* MEMP_OVERFLOW_CHECK */
+#endif /* !MEMP_MEM_MALLOC */
+#if MEMP_OVERFLOW_CHECK
+    memp->file = file;
+    memp->line = line;
+#if MEMP_MEM_MALLOC
+    memp_overflow_init_element(memp, desc);
+#endif /* MEMP_MEM_MALLOC */
+#endif /* MEMP_OVERFLOW_CHECK */
+    LWIP_ASSERT("memp_malloc: memp properly aligned",
+                ((mem_ptr_t)memp % MEM_ALIGNMENT) == 0);
+#if MEMP_STATS
+    desc->stats->used++;
+    if (desc->stats->used > desc->stats->max) {
+      desc->stats->max = desc->stats->used;
+    }
+#endif
+    SYS_ARCH_UNPROTECT(old_level);
+    /* cast through u8_t* to get rid of alignment warnings */
+    return ((u8_t *)memp + MEMP_SIZE);
+  } else {
+#if MEMP_STATS
+    desc->stats->err++;
+#endif
+    SYS_ARCH_UNPROTECT(old_level);
+    LWIP_DEBUGF(MEMP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("memp_malloc: out of memory in pool %s\n", desc->desc));
+  }
+
+  return NULL;
+}
+
+/**
+ * Get an element from a custom pool.
+ *
+ * @param desc the pool to get an element from
+ *
+ * @return a pointer to the allocated memory or a NULL pointer on error
+ */
+void *
+#if !MEMP_OVERFLOW_CHECK
+memp_malloc_pool(const struct memp_desc *desc)
+#else
+memp_malloc_pool_fn(const struct memp_desc *desc, const char *file, const int line)
+#endif
+{
+  LWIP_ASSERT("invalid pool desc", desc != NULL);
+  if (desc == NULL) {
+    return NULL;
+  }
+
+#if !MEMP_OVERFLOW_CHECK
+  return do_memp_malloc_pool(desc);
+#else
+  return do_memp_malloc_pool_fn(desc, file, line);
+#endif
+}
+
+/**
+ * Get an element from a specific pool.
+ *
+ * @param type the pool to get an element from
+ *
+ * @return a pointer to the allocated memory or a NULL pointer on error
+ */
+void *
+#if !MEMP_OVERFLOW_CHECK
+memp_malloc(memp_t type)
+#else
+memp_malloc_fn(memp_t type, const char *file, const int line)
+#endif
+{
+  void *memp;
+  LWIP_ERROR("memp_malloc: type < MEMP_MAX", (type < MEMP_MAX), return NULL;);
+
+#if MEMP_OVERFLOW_CHECK >= 2
+  memp_overflow_check_all();
+#endif /* MEMP_OVERFLOW_CHECK >= 2 */
+
+#if !MEMP_OVERFLOW_CHECK
+  memp = do_memp_malloc_pool(memp_pools[type]);
+#else
+  memp = do_memp_malloc_pool_fn(memp_pools[type], file, line);
+#endif
+
+  return memp;
+}
+
+static void
+do_memp_free_pool(const struct memp_desc *desc, void *mem)
+{
+  struct memp *memp;
+  SYS_ARCH_DECL_PROTECT(old_level);
+
+  LWIP_ASSERT("memp_free: mem properly aligned",
+              ((mem_ptr_t)mem % MEM_ALIGNMENT) == 0);
+
+  /* cast through void* to get rid of alignment warnings */
+  memp = (struct memp *)(void *)((u8_t *)mem - MEMP_SIZE);
+
+  SYS_ARCH_PROTECT(old_level);
+
+#if MEMP_OVERFLOW_CHECK == 1
+  memp_overflow_check_element(memp, desc);
+#endif /* MEMP_OVERFLOW_CHECK */
+
+#if MEMP_STATS
+  desc->stats->used--;
+#endif
+
+#if MEMP_MEM_MALLOC
+  LWIP_UNUSED_ARG(desc);
+  SYS_ARCH_UNPROTECT(old_level);
+  mem_free(memp);
+#else /* MEMP_MEM_MALLOC */
+  memp->next = *desc->tab;
+  *desc->tab = memp;
+
+#if MEMP_SANITY_CHECK
+  LWIP_ASSERT("memp sanity", memp_sanity(desc));
+#endif /* MEMP_SANITY_CHECK */
+
+  SYS_ARCH_UNPROTECT(old_level);
+#endif /* !MEMP_MEM_MALLOC */
+}
+
+/**
+ * Put a custom pool element back into its pool.
+ *
+ * @param desc the pool where to put mem
+ * @param mem the memp element to free
+ */
+void
+memp_free_pool(const struct memp_desc *desc, void *mem)
+{
+  LWIP_ASSERT("invalid pool desc", desc != NULL);
+  if ((desc == NULL) || (mem == NULL)) {
+    return;
+  }
+
+  do_memp_free_pool(desc, mem);
+}
+
+/**
+ * Put an element back into its pool.
+ *
+ * @param type the pool where to put mem
+ * @param mem the memp element to free
+ */
+void
+memp_free(memp_t type, void *mem)
+{
+#ifdef LWIP_HOOK_MEMP_AVAILABLE
+  struct memp *old_first;
+#endif
+
+  LWIP_ERROR("memp_free: type < MEMP_MAX", (type < MEMP_MAX), return;);
+
+  if (mem == NULL) {
+    return;
+  }
+
+#if MEMP_OVERFLOW_CHECK >= 2
+  memp_overflow_check_all();
+#endif /* MEMP_OVERFLOW_CHECK >= 2 */
+
+#ifdef LWIP_HOOK_MEMP_AVAILABLE
+  old_first = *memp_pools[type]->tab;
+#endif
+
+  do_memp_free_pool(memp_pools[type], mem);
+
+#ifdef LWIP_HOOK_MEMP_AVAILABLE
+  if (old_first == NULL) {
+    LWIP_HOOK_MEMP_AVAILABLE(type);
+  }
+#endif
+}
diff --git a/lib/lwip/core/netif.c b/lib/lwip/core/netif.c
new file mode 100644
index 0000000000..d3a0677474
--- /dev/null
+++ b/lib/lwip/core/netif.c
@@ -0,0 +1,1855 @@
+/**
+ * @file
+ * lwIP network interface abstraction
+ *
+ * @defgroup netif Network interface (NETIF)
+ * @ingroup callbackstyle_api
+ *
+ * @defgroup netif_ip4 IPv4 address handling
+ * @ingroup netif
+ *
+ * @defgroup netif_ip6 IPv6 address handling
+ * @ingroup netif
+ *
+ * @defgroup netif_cd Client data handling
+ * Store data (void*) on a netif for application usage.
+ * @see @ref LWIP_NUM_NETIF_CLIENT_DATA
+ * @ingroup netif
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ */
+
+#include "lwip/opt.h"
+
+#include <string.h> /* memset */
+#include <stdlib.h> /* atoi */
+
+#include "lwip/def.h"
+#include "lwip/ip_addr.h"
+#include "lwip/ip6_addr.h"
+#include "lwip/netif.h"
+#include "lwip/priv/tcp_priv.h"
+#include "lwip/udp.h"
+#include "lwip/priv/raw_priv.h"
+#include "lwip/snmp.h"
+#include "lwip/igmp.h"
+#include "lwip/etharp.h"
+#include "lwip/stats.h"
+#include "lwip/sys.h"
+#include "lwip/ip.h"
+#if ENABLE_LOOPBACK
+#if LWIP_NETIF_LOOPBACK_MULTITHREADING
+#include "lwip/tcpip.h"
+#endif /* LWIP_NETIF_LOOPBACK_MULTITHREADING */
+#endif /* ENABLE_LOOPBACK */
+
+#include "netif/ethernet.h"
+
+#if LWIP_AUTOIP
+#include "lwip/autoip.h"
+#endif /* LWIP_AUTOIP */
+#if LWIP_DHCP
+#include "lwip/dhcp.h"
+#endif /* LWIP_DHCP */
+#if LWIP_ACD
+#include "lwip/acd.h"
+#endif /* LWIP_ACD */
+#if LWIP_IPV6_DHCP6
+#include "lwip/dhcp6.h"
+#endif /* LWIP_IPV6_DHCP6 */
+#if LWIP_IPV6_MLD
+#include "lwip/mld6.h"
+#endif /* LWIP_IPV6_MLD */
+#if LWIP_IPV6
+#include "lwip/nd6.h"
+#endif
+
+#if LWIP_NETIF_STATUS_CALLBACK
+#define NETIF_STATUS_CALLBACK(n) do{ if (n->status_callback) { (n->status_callback)(n); }}while(0)
+#else
+#define NETIF_STATUS_CALLBACK(n)
+#endif /* LWIP_NETIF_STATUS_CALLBACK */
+
+#if LWIP_NETIF_LINK_CALLBACK
+#define NETIF_LINK_CALLBACK(n) do{ if (n->link_callback) { (n->link_callback)(n); }}while(0)
+#else
+#define NETIF_LINK_CALLBACK(n)
+#endif /* LWIP_NETIF_LINK_CALLBACK */
+
+#if LWIP_NETIF_EXT_STATUS_CALLBACK
+static netif_ext_callback_t *ext_callback;
+#endif
+
+#if !LWIP_SINGLE_NETIF
+struct netif *netif_list;
+#endif /* !LWIP_SINGLE_NETIF */
+struct netif *netif_default;
+
+#define netif_index_to_num(index)   ((index) - 1)
+static u8_t netif_num;
+
+#if LWIP_NUM_NETIF_CLIENT_DATA > 0
+static u8_t netif_client_id;
+#endif
+
+#define NETIF_REPORT_TYPE_IPV4  0x01
+#define NETIF_REPORT_TYPE_IPV6  0x02
+static void netif_issue_reports(struct netif *netif, u8_t report_type);
+
+#if LWIP_IPV6
+static err_t netif_null_output_ip6(struct netif *netif, struct pbuf *p, const ip6_addr_t *ipaddr);
+#endif /* LWIP_IPV6 */
+#if LWIP_IPV4
+static err_t netif_null_output_ip4(struct netif *netif, struct pbuf *p, const ip4_addr_t *ipaddr);
+#endif /* LWIP_IPV4 */
+
+#if LWIP_HAVE_LOOPIF
+#if LWIP_IPV4
+static err_t netif_loop_output_ipv4(struct netif *netif, struct pbuf *p, const ip4_addr_t *addr);
+#endif
+#if LWIP_IPV6
+static err_t netif_loop_output_ipv6(struct netif *netif, struct pbuf *p, const ip6_addr_t *addr);
+#endif
+
+
+static struct netif loop_netif;
+
+#if LWIP_TESTMODE
+struct netif* netif_get_loopif(void)
+{
+  return &loop_netif;
+}
+#endif
+
+
+/**
+ * Initialize a lwip network interface structure for a loopback interface
+ *
+ * @param netif the lwip network interface structure for this loopif
+ * @return ERR_OK if the loopif is initialized
+ *         ERR_MEM if private data couldn't be allocated
+ */
+static err_t
+netif_loopif_init(struct netif *netif)
+{
+  LWIP_ASSERT("netif_loopif_init: invalid netif", netif != NULL);
+
+  /* initialize the snmp variables and counters inside the struct netif
+   * ifSpeed: no assumption can be made!
+   */
+  MIB2_INIT_NETIF(netif, snmp_ifType_softwareLoopback, 0);
+
+  netif->name[0] = 'l';
+  netif->name[1] = 'o';
+#if LWIP_IPV4
+  netif->output = netif_loop_output_ipv4;
+#endif
+#if LWIP_IPV6
+  netif->output_ip6 = netif_loop_output_ipv6;
+#endif
+#if LWIP_LOOPIF_MULTICAST
+  netif_set_flags(netif, NETIF_FLAG_IGMP);
+#endif
+  NETIF_SET_CHECKSUM_CTRL(netif, NETIF_CHECKSUM_DISABLE_ALL);
+  return ERR_OK;
+}
+#endif /* LWIP_HAVE_LOOPIF */
+
+void
+netif_init(void)
+{
+#if LWIP_HAVE_LOOPIF
+#if LWIP_IPV4
+#define LOOPIF_ADDRINIT &loop_ipaddr, &loop_netmask, &loop_gw,
+  ip4_addr_t loop_ipaddr, loop_netmask, loop_gw;
+  IP4_ADDR(&loop_gw, 127, 0, 0, 1);
+  IP4_ADDR(&loop_ipaddr, 127, 0, 0, 1);
+  IP4_ADDR(&loop_netmask, 255, 0, 0, 0);
+#else /* LWIP_IPV4 */
+#define LOOPIF_ADDRINIT
+#endif /* LWIP_IPV4 */
+
+#if NO_SYS
+  netif_add(&loop_netif, LOOPIF_ADDRINIT NULL, netif_loopif_init, ip_input);
+#else  /* NO_SYS */
+  netif_add(&loop_netif, LOOPIF_ADDRINIT NULL, netif_loopif_init, tcpip_input);
+#endif /* NO_SYS */
+
+#if LWIP_IPV6
+  IP_ADDR6_HOST(loop_netif.ip6_addr, 0, 0, 0, 0x00000001UL);
+  loop_netif.ip6_addr_state[0] = IP6_ADDR_VALID;
+#endif /* LWIP_IPV6 */
+
+  netif_set_link_up(&loop_netif);
+  netif_set_up(&loop_netif);
+
+#endif /* LWIP_HAVE_LOOPIF */
+}
+
+/**
+ * @ingroup lwip_nosys
+ * Forwards a received packet for input processing with
+ * ethernet_input() or ip_input() depending on netif flags.
+ * Don't call directly, pass to netif_add() and call
+ * netif->input().
+ * Only works if the netif driver correctly sets
+ * NETIF_FLAG_ETHARP and/or NETIF_FLAG_ETHERNET flag!
+ */
+err_t
+netif_input(struct pbuf *p, struct netif *inp)
+{
+  LWIP_ASSERT_CORE_LOCKED();
+
+  LWIP_ASSERT("netif_input: invalid pbuf", p != NULL);
+  LWIP_ASSERT("netif_input: invalid netif", inp != NULL);
+
+#if LWIP_ETHERNET
+  if (inp->flags & (NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET)) {
+    return ethernet_input(p, inp);
+  } else
+#endif /* LWIP_ETHERNET */
+    return ip_input(p, inp);
+}
+
+/**
+ * @ingroup netif
+ * Add a network interface to the list of lwIP netifs.
+ *
+ * Same as @ref netif_add but without IPv4 addresses
+ */
+struct netif *
+netif_add_noaddr(struct netif *netif, void *state, netif_init_fn init, netif_input_fn input)
+{
+  return netif_add(netif,
+#if LWIP_IPV4
+                   NULL, NULL, NULL,
+#endif /* LWIP_IPV4*/
+                   state, init, input);
+}
+
+/**
+ * @ingroup netif
+ * Add a network interface to the list of lwIP netifs.
+ *
+ * @param netif a pre-allocated netif structure
+ * @param ipaddr IP address for the new netif
+ * @param netmask network mask for the new netif
+ * @param gw default gateway IP address for the new netif
+ * @param state opaque data passed to the new netif
+ * @param init callback function that initializes the interface
+ * @param input callback function that is called to pass
+ * ingress packets up in the protocol layer stack.<br>
+ * It is recommended to use a function that passes the input directly
+ * to the stack (netif_input(), NO_SYS=1 mode) or via sending a
+ * message to TCPIP thread (tcpip_input(), NO_SYS=0 mode).<br>
+ * These functions use netif flags NETIF_FLAG_ETHARP and NETIF_FLAG_ETHERNET
+ * to decide whether to forward to ethernet_input() or ip_input().
+ * In other words, the functions only work when the netif
+ * driver is implemented correctly!<br>
+ * Most members of struct netif should be be initialized by the
+ * netif init function = netif driver (init parameter of this function).<br>
+ * IPv6: Don't forget to call netif_create_ip6_linklocal_address() after
+ * setting the MAC address in struct netif.hwaddr
+ * (IPv6 requires a link-local address).
+ *
+ * @return netif, or NULL if failed.
+ */
+struct netif *
+netif_add(struct netif *netif,
+#if LWIP_IPV4
+          const ip4_addr_t *ipaddr, const ip4_addr_t *netmask, const ip4_addr_t *gw,
+#endif /* LWIP_IPV4 */
+          void *state, netif_init_fn init, netif_input_fn input)
+{
+#if LWIP_IPV6
+  s8_t i;
+#endif
+
+  LWIP_ASSERT_CORE_LOCKED();
+
+#if LWIP_SINGLE_NETIF
+  if (netif_default != NULL) {
+    LWIP_ASSERT("single netif already set", 0);
+    return NULL;
+  }
+#endif
+
+  LWIP_ERROR("netif_add: invalid netif", netif != NULL, return NULL);
+  LWIP_ERROR("netif_add: No init function given", init != NULL, return NULL);
+
+#if LWIP_IPV4
+  if (ipaddr == NULL) {
+    ipaddr = ip_2_ip4(IP4_ADDR_ANY);
+  }
+  if (netmask == NULL) {
+    netmask = ip_2_ip4(IP4_ADDR_ANY);
+  }
+  if (gw == NULL) {
+    gw = ip_2_ip4(IP4_ADDR_ANY);
+  }
+
+  /* reset new interface configuration state */
+  ip_addr_set_zero_ip4(&netif->ip_addr);
+  ip_addr_set_zero_ip4(&netif->netmask);
+  ip_addr_set_zero_ip4(&netif->gw);
+  netif->output = netif_null_output_ip4;
+#endif /* LWIP_IPV4 */
+#if LWIP_IPV6
+  for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+    ip_addr_set_zero_ip6(&netif->ip6_addr[i]);
+    netif->ip6_addr_state[i] = IP6_ADDR_INVALID;
+#if LWIP_IPV6_ADDRESS_LIFETIMES
+    netif->ip6_addr_valid_life[i] = IP6_ADDR_LIFE_STATIC;
+    netif->ip6_addr_pref_life[i] = IP6_ADDR_LIFE_STATIC;
+#endif /* LWIP_IPV6_ADDRESS_LIFETIMES */
+  }
+  netif->output_ip6 = netif_null_output_ip6;
+#endif /* LWIP_IPV6 */
+  NETIF_SET_CHECKSUM_CTRL(netif, NETIF_CHECKSUM_ENABLE_ALL);
+  netif->mtu = 0;
+  netif->flags = 0;
+#ifdef netif_get_client_data
+  memset(netif->client_data, 0, sizeof(netif->client_data));
+#endif /* LWIP_NUM_NETIF_CLIENT_DATA */
+#if LWIP_IPV6
+#if LWIP_IPV6_AUTOCONFIG
+  /* IPv6 address autoconfiguration should be enabled by default */
+  netif->ip6_autoconfig_enabled = 1;
+#endif /* LWIP_IPV6_AUTOCONFIG */
+  nd6_restart_netif(netif);
+#endif /* LWIP_IPV6 */
+#if LWIP_NETIF_STATUS_CALLBACK
+  netif->status_callback = NULL;
+#endif /* LWIP_NETIF_STATUS_CALLBACK */
+#if LWIP_NETIF_LINK_CALLBACK
+  netif->link_callback = NULL;
+#endif /* LWIP_NETIF_LINK_CALLBACK */
+#if LWIP_IGMP
+  netif->igmp_mac_filter = NULL;
+#endif /* LWIP_IGMP */
+#if LWIP_IPV6 && LWIP_IPV6_MLD
+  netif->mld_mac_filter = NULL;
+#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
+
+  /* remember netif specific state information data */
+  netif->state = state;
+  netif->num = netif_num;
+  netif->input = input;
+
+#if LWIP_ACD
+  netif->acd_list = NULL;
+#endif /* LWIP_ACD */
+  NETIF_RESET_HINTS(netif);
+#if ENABLE_LOOPBACK
+  netif->loop_first = NULL;
+  netif->loop_last = NULL;
+#if LWIP_LOOPBACK_MAX_PBUFS
+  netif->loop_cnt_current = 0;
+#endif /* LWIP_LOOPBACK_MAX_PBUFS */
+#if LWIP_NETIF_LOOPBACK_MULTITHREADING
+  netif->reschedule_poll = 0;
+#endif /* LWIP_NETIF_LOOPBACK_MULTITHREADING */
+#endif /* ENABLE_LOOPBACK */
+
+#if LWIP_IPV4
+  netif_set_addr(netif, ipaddr, netmask, gw);
+#endif /* LWIP_IPV4 */
+
+  /* call user specified initialization function for netif */
+  if (init(netif) != ERR_OK) {
+    return NULL;
+  }
+#if LWIP_IPV6 && LWIP_ND6_ALLOW_RA_UPDATES
+  /* Initialize the MTU for IPv6 to the one set by the netif driver.
+     This can be updated later by RA. */
+  netif->mtu6 = netif->mtu;
+#endif /* LWIP_IPV6 && LWIP_ND6_ALLOW_RA_UPDATES */
+
+#if !LWIP_SINGLE_NETIF
+  /* Assign a unique netif number in the range [0..254], so that (num+1) can
+     serve as an interface index that fits in a u8_t.
+     We assume that the new netif has not yet been added to the list here.
+     This algorithm is O(n^2), but that should be OK for lwIP.
+     */
+  {
+    struct netif *netif2;
+    int num_netifs;
+    do {
+      if (netif->num == 255) {
+        netif->num = 0;
+      }
+      num_netifs = 0;
+      for (netif2 = netif_list; netif2 != NULL; netif2 = netif2->next) {
+        LWIP_ASSERT("netif already added", netif2 != netif);
+        num_netifs++;
+        LWIP_ASSERT("too many netifs, max. supported number is 255", num_netifs <= 255);
+        if (netif2->num == netif->num) {
+          netif->num++;
+          break;
+        }
+      }
+    } while (netif2 != NULL);
+  }
+  if (netif->num == 254) {
+    netif_num = 0;
+  } else {
+    netif_num = (u8_t)(netif->num + 1);
+  }
+
+  /* add this netif to the list */
+  netif->next = netif_list;
+  netif_list = netif;
+#endif /* "LWIP_SINGLE_NETIF */
+  mib2_netif_added(netif);
+
+#if LWIP_IGMP
+  /* start IGMP processing */
+  if (netif->flags & NETIF_FLAG_IGMP) {
+    igmp_start(netif);
+  }
+#endif /* LWIP_IGMP */
+
+  LWIP_DEBUGF(NETIF_DEBUG, ("netif: added interface %c%c IP",
+                            netif->name[0], netif->name[1]));
+#if LWIP_IPV4
+  LWIP_DEBUGF(NETIF_DEBUG, (" addr "));
+  ip4_addr_debug_print(NETIF_DEBUG, ipaddr);
+  LWIP_DEBUGF(NETIF_DEBUG, (" netmask "));
+  ip4_addr_debug_print(NETIF_DEBUG, netmask);
+  LWIP_DEBUGF(NETIF_DEBUG, (" gw "));
+  ip4_addr_debug_print(NETIF_DEBUG, gw);
+#endif /* LWIP_IPV4 */
+  LWIP_DEBUGF(NETIF_DEBUG, ("\n"));
+
+  netif_invoke_ext_callback(netif, LWIP_NSC_NETIF_ADDED, NULL);
+
+  return netif;
+}
+
+static void
+netif_do_ip_addr_changed(const ip_addr_t *old_addr, const ip_addr_t *new_addr)
+{
+#if LWIP_TCP
+  tcp_netif_ip_addr_changed(old_addr, new_addr);
+#endif /* LWIP_TCP */
+#if LWIP_UDP
+  udp_netif_ip_addr_changed(old_addr, new_addr);
+#endif /* LWIP_UDP */
+#if LWIP_RAW
+  raw_netif_ip_addr_changed(old_addr, new_addr);
+#endif /* LWIP_RAW */
+}
+
+#if LWIP_IPV4
+static int
+netif_do_set_ipaddr(struct netif *netif, const ip4_addr_t *ipaddr, ip_addr_t *old_addr)
+{
+  LWIP_ASSERT("invalid pointer", ipaddr != NULL);
+  LWIP_ASSERT("invalid pointer", old_addr != NULL);
+
+  /* address is actually being changed? */
+  if (ip4_addr_eq(ipaddr, netif_ip4_addr(netif)) == 0) {
+    ip_addr_t new_addr;
+    *ip_2_ip4(&new_addr) = *ipaddr;
+    IP_SET_TYPE_VAL(new_addr, IPADDR_TYPE_V4);
+
+    ip_addr_copy(*old_addr, *netif_ip_addr4(netif));
+
+    LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_STATE, ("netif_set_ipaddr: netif address being changed\n"));
+    netif_do_ip_addr_changed(old_addr, &new_addr);
+
+#if LWIP_ACD
+    acd_netif_ip_addr_changed(netif, old_addr, &new_addr);
+#endif /* LWIP_ACD */
+
+    mib2_remove_ip4(netif);
+    mib2_remove_route_ip4(0, netif);
+    /* set new IP address to netif */
+    ip4_addr_set(ip_2_ip4(&netif->ip_addr), ipaddr);
+    IP_SET_TYPE_VAL(netif->ip_addr, IPADDR_TYPE_V4);
+    mib2_add_ip4(netif);
+    mib2_add_route_ip4(0, netif);
+
+    netif_issue_reports(netif, NETIF_REPORT_TYPE_IPV4);
+
+    NETIF_STATUS_CALLBACK(netif);
+    return 1; /* address changed */
+  }
+  return 0; /* address unchanged */
+}
+
+/**
+ * @ingroup netif_ip4
+ * Change the IP address of a network interface
+ *
+ * @param netif the network interface to change
+ * @param ipaddr the new IP address
+ *
+ * @note call netif_set_addr() if you also want to change netmask and
+ * default gateway
+ */
+void
+netif_set_ipaddr(struct netif *netif, const ip4_addr_t *ipaddr)
+{
+  ip_addr_t old_addr;
+
+  LWIP_ERROR("netif_set_ipaddr: invalid netif", netif != NULL, return);
+
+  /* Don't propagate NULL pointer (IPv4 ANY) to subsequent functions */
+  if (ipaddr == NULL) {
+    ipaddr = IP4_ADDR_ANY4;
+  }
+
+  LWIP_ASSERT_CORE_LOCKED();
+
+  if (netif_do_set_ipaddr(netif, ipaddr, &old_addr)) {
+#if LWIP_NETIF_EXT_STATUS_CALLBACK
+    netif_ext_callback_args_t args;
+    args.ipv4_changed.old_address = &old_addr;
+    netif_invoke_ext_callback(netif, LWIP_NSC_IPV4_ADDRESS_CHANGED, &args);
+#endif
+  }
+}
+
+static int
+netif_do_set_netmask(struct netif *netif, const ip4_addr_t *netmask, ip_addr_t *old_nm)
+{
+  /* address is actually being changed? */
+  if (ip4_addr_eq(netmask, netif_ip4_netmask(netif)) == 0) {
+#if LWIP_NETIF_EXT_STATUS_CALLBACK
+    LWIP_ASSERT("invalid pointer", old_nm != NULL);
+    ip_addr_copy(*old_nm, *netif_ip_netmask4(netif));
+#else
+    LWIP_UNUSED_ARG(old_nm);
+#endif
+    mib2_remove_route_ip4(0, netif);
+    /* set new netmask to netif */
+    ip4_addr_set(ip_2_ip4(&netif->netmask), netmask);
+    IP_SET_TYPE_VAL(netif->netmask, IPADDR_TYPE_V4);
+    mib2_add_route_ip4(0, netif);
+    LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("netif: netmask of interface %c%c set to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+                netif->name[0], netif->name[1],
+                ip4_addr1_16(netif_ip4_netmask(netif)),
+                ip4_addr2_16(netif_ip4_netmask(netif)),
+                ip4_addr3_16(netif_ip4_netmask(netif)),
+                ip4_addr4_16(netif_ip4_netmask(netif))));
+    return 1; /* netmask changed */
+  }
+  return 0; /* netmask unchanged */
+}
+
+/**
+ * @ingroup netif_ip4
+ * Change the netmask of a network interface
+ *
+ * @param netif the network interface to change
+ * @param netmask the new netmask
+ *
+ * @note call netif_set_addr() if you also want to change ip address and
+ * default gateway
+ */
+void
+netif_set_netmask(struct netif *netif, const ip4_addr_t *netmask)
+{
+#if LWIP_NETIF_EXT_STATUS_CALLBACK
+  ip_addr_t old_nm_val;
+  ip_addr_t *old_nm = &old_nm_val;
+#else
+  ip_addr_t *old_nm = NULL;
+#endif
+  LWIP_ASSERT_CORE_LOCKED();
+
+  LWIP_ERROR("netif_set_netmask: invalid netif", netif != NULL, return);
+
+  /* Don't propagate NULL pointer (IPv4 ANY) to subsequent functions */
+  if (netmask == NULL) {
+    netmask = IP4_ADDR_ANY4;
+  }
+
+  if (netif_do_set_netmask(netif, netmask, old_nm)) {
+#if LWIP_NETIF_EXT_STATUS_CALLBACK
+    netif_ext_callback_args_t args;
+    args.ipv4_changed.old_netmask = old_nm;
+    netif_invoke_ext_callback(netif, LWIP_NSC_IPV4_NETMASK_CHANGED, &args);
+#endif
+  }
+}
+
+static int
+netif_do_set_gw(struct netif *netif, const ip4_addr_t *gw, ip_addr_t *old_gw)
+{
+  /* address is actually being changed? */
+  if (ip4_addr_eq(gw, netif_ip4_gw(netif)) == 0) {
+#if LWIP_NETIF_EXT_STATUS_CALLBACK
+    LWIP_ASSERT("invalid pointer", old_gw != NULL);
+    ip_addr_copy(*old_gw, *netif_ip_gw4(netif));
+#else
+    LWIP_UNUSED_ARG(old_gw);
+#endif
+
+    ip4_addr_set(ip_2_ip4(&netif->gw), gw);
+    IP_SET_TYPE_VAL(netif->gw, IPADDR_TYPE_V4);
+    LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("netif: GW address of interface %c%c set to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+                netif->name[0], netif->name[1],
+                ip4_addr1_16(netif_ip4_gw(netif)),
+                ip4_addr2_16(netif_ip4_gw(netif)),
+                ip4_addr3_16(netif_ip4_gw(netif)),
+                ip4_addr4_16(netif_ip4_gw(netif))));
+    return 1; /* gateway changed */
+  }
+  return 0; /* gateway unchanged */
+}
+
+/**
+ * @ingroup netif_ip4
+ * Change the default gateway for a network interface
+ *
+ * @param netif the network interface to change
+ * @param gw the new default gateway
+ *
+ * @note call netif_set_addr() if you also want to change ip address and netmask
+ */
+void
+netif_set_gw(struct netif *netif, const ip4_addr_t *gw)
+{
+#if LWIP_NETIF_EXT_STATUS_CALLBACK
+  ip_addr_t old_gw_val;
+  ip_addr_t *old_gw = &old_gw_val;
+#else
+  ip_addr_t *old_gw = NULL;
+#endif
+  LWIP_ASSERT_CORE_LOCKED();
+
+  LWIP_ERROR("netif_set_gw: invalid netif", netif != NULL, return);
+
+  /* Don't propagate NULL pointer (IPv4 ANY) to subsequent functions */
+  if (gw == NULL) {
+    gw = IP4_ADDR_ANY4;
+  }
+
+  if (netif_do_set_gw(netif, gw, old_gw)) {
+#if LWIP_NETIF_EXT_STATUS_CALLBACK
+    netif_ext_callback_args_t args;
+    args.ipv4_changed.old_gw = old_gw;
+    netif_invoke_ext_callback(netif, LWIP_NSC_IPV4_GATEWAY_CHANGED, &args);
+#endif
+  }
+}
+
+/**
+ * @ingroup netif_ip4
+ * Change IP address configuration for a network interface (including netmask
+ * and default gateway).
+ *
+ * @param netif the network interface to change
+ * @param ipaddr the new IP address
+ * @param netmask the new netmask
+ * @param gw the new default gateway
+ */
+void
+netif_set_addr(struct netif *netif, const ip4_addr_t *ipaddr, const ip4_addr_t *netmask,
+               const ip4_addr_t *gw)
+{
+#if LWIP_NETIF_EXT_STATUS_CALLBACK
+  netif_nsc_reason_t change_reason = LWIP_NSC_NONE;
+  netif_ext_callback_args_t cb_args;
+  ip_addr_t old_nm_val;
+  ip_addr_t old_gw_val;
+  ip_addr_t *old_nm = &old_nm_val;
+  ip_addr_t *old_gw = &old_gw_val;
+#else
+  ip_addr_t *old_nm = NULL;
+  ip_addr_t *old_gw = NULL;
+#endif
+  ip_addr_t old_addr;
+  int remove;
+
+  LWIP_ASSERT_CORE_LOCKED();
+
+  /* Don't propagate NULL pointer (IPv4 ANY) to subsequent functions */
+  if (ipaddr == NULL) {
+    ipaddr = IP4_ADDR_ANY4;
+  }
+  if (netmask == NULL) {
+    netmask = IP4_ADDR_ANY4;
+  }
+  if (gw == NULL) {
+    gw = IP4_ADDR_ANY4;
+  }
+
+  remove = ip4_addr_isany(ipaddr);
+  if (remove) {
+    /* when removing an address, we have to remove it *before* changing netmask/gw
+       to ensure that tcp RST segment can be sent correctly */
+    if (netif_do_set_ipaddr(netif, ipaddr, &old_addr)) {
+#if LWIP_NETIF_EXT_STATUS_CALLBACK
+      change_reason |= LWIP_NSC_IPV4_ADDRESS_CHANGED;
+      cb_args.ipv4_changed.old_address = &old_addr;
+#endif
+    }
+  }
+  if (netif_do_set_netmask(netif, netmask, old_nm)) {
+#if LWIP_NETIF_EXT_STATUS_CALLBACK
+    change_reason |= LWIP_NSC_IPV4_NETMASK_CHANGED;
+    cb_args.ipv4_changed.old_netmask = old_nm;
+#endif
+  }
+  if (netif_do_set_gw(netif, gw, old_gw)) {
+#if LWIP_NETIF_EXT_STATUS_CALLBACK
+    change_reason |= LWIP_NSC_IPV4_GATEWAY_CHANGED;
+    cb_args.ipv4_changed.old_gw = old_gw;
+#endif
+  }
+  if (!remove) {
+    /* set ipaddr last to ensure netmask/gw have been set when status callback is called */
+    if (netif_do_set_ipaddr(netif, ipaddr, &old_addr)) {
+#if LWIP_NETIF_EXT_STATUS_CALLBACK
+      change_reason |= LWIP_NSC_IPV4_ADDRESS_CHANGED;
+      cb_args.ipv4_changed.old_address = &old_addr;
+#endif
+    }
+  }
+
+#if LWIP_NETIF_EXT_STATUS_CALLBACK
+  if (change_reason != LWIP_NSC_NONE) {
+    change_reason |= LWIP_NSC_IPV4_SETTINGS_CHANGED;
+  }
+  if (!remove) {
+    /* Issue a callback even if the address hasn't changed, eg. DHCP reboot */
+    change_reason |= LWIP_NSC_IPV4_ADDR_VALID;
+  }
+  if (change_reason != LWIP_NSC_NONE) {
+    netif_invoke_ext_callback(netif, change_reason, &cb_args);
+  }
+#endif
+}
+#endif /* LWIP_IPV4*/
+
+/**
+ * @ingroup netif
+ * Remove a network interface from the list of lwIP netifs.
+ *
+ * @param netif the network interface to remove
+ */
+void
+netif_remove(struct netif *netif)
+{
+#if LWIP_IPV6
+  int i;
+#endif
+
+  LWIP_ASSERT_CORE_LOCKED();
+
+  if (netif == NULL) {
+    return;
+  }
+
+  netif_invoke_ext_callback(netif, LWIP_NSC_NETIF_REMOVED, NULL);
+
+#if LWIP_IPV4
+  if (!ip4_addr_isany_val(*netif_ip4_addr(netif))) {
+    netif_do_ip_addr_changed(netif_ip_addr4(netif), NULL);
+  }
+
+#if LWIP_IGMP
+  /* stop IGMP processing */
+  if (netif->flags & NETIF_FLAG_IGMP) {
+    igmp_stop(netif);
+  }
+#endif /* LWIP_IGMP */
+#endif /* LWIP_IPV4*/
+
+#if LWIP_IPV6
+  for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+    if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i))) {
+      netif_do_ip_addr_changed(netif_ip_addr6(netif, i), NULL);
+    }
+  }
+#if LWIP_IPV6_MLD
+  /* stop MLD processing */
+  mld6_stop(netif);
+#endif /* LWIP_IPV6_MLD */
+#endif /* LWIP_IPV6 */
+  if (netif_is_up(netif)) {
+    /* set netif down before removing (call callback function) */
+    netif_set_down(netif);
+  }
+
+  mib2_remove_ip4(netif);
+
+  /* this netif is default? */
+  if (netif_default == netif) {
+    /* reset default netif */
+    netif_set_default(NULL);
+  }
+#if !LWIP_SINGLE_NETIF
+  /*  is it the first netif? */
+  if (netif_list == netif) {
+    netif_list = netif->next;
+  } else {
+    /*  look for netif further down the list */
+    struct netif *tmp_netif;
+    NETIF_FOREACH(tmp_netif) {
+      if (tmp_netif->next == netif) {
+        tmp_netif->next = netif->next;
+        break;
+      }
+    }
+    if (tmp_netif == NULL) {
+      return; /* netif is not on the list */
+    }
+  }
+#endif /* !LWIP_SINGLE_NETIF */
+  mib2_netif_removed(netif);
+#if LWIP_NETIF_REMOVE_CALLBACK
+  if (netif->remove_callback) {
+    netif->remove_callback(netif);
+  }
+#endif /* LWIP_NETIF_REMOVE_CALLBACK */
+  LWIP_DEBUGF( NETIF_DEBUG, ("netif_remove: removed netif\n") );
+}
+
+/**
+ * @ingroup netif
+ * Set a network interface as the default network interface
+ * (used to output all packets for which no specific route is found)
+ *
+ * @param netif the default network interface
+ */
+void
+netif_set_default(struct netif *netif)
+{
+  LWIP_ASSERT_CORE_LOCKED();
+
+  if (netif == NULL) {
+    /* remove default route */
+    mib2_remove_route_ip4(1, netif);
+  } else {
+    /* install default route */
+    mib2_add_route_ip4(1, netif);
+  }
+  netif_default = netif;
+  LWIP_DEBUGF(NETIF_DEBUG, ("netif: setting default interface %c%c\n",
+                            netif ? netif->name[0] : '\'', netif ? netif->name[1] : '\''));
+}
+
+/**
+ * @ingroup netif
+ * Bring an interface up, available for processing
+ * traffic.
+ */
+void
+netif_set_up(struct netif *netif)
+{
+  LWIP_ASSERT_CORE_LOCKED();
+
+  LWIP_ERROR("netif_set_up: invalid netif", netif != NULL, return);
+
+  if (!(netif->flags & NETIF_FLAG_UP)) {
+    netif_set_flags(netif, NETIF_FLAG_UP);
+
+    MIB2_COPY_SYSUPTIME_TO(&netif->ts);
+
+    NETIF_STATUS_CALLBACK(netif);
+
+#if LWIP_NETIF_EXT_STATUS_CALLBACK
+    {
+      netif_ext_callback_args_t args;
+      args.status_changed.state = 1;
+      netif_invoke_ext_callback(netif, LWIP_NSC_STATUS_CHANGED, &args);
+    }
+#endif
+
+    netif_issue_reports(netif, NETIF_REPORT_TYPE_IPV4 | NETIF_REPORT_TYPE_IPV6);
+#if LWIP_IPV6
+    nd6_restart_netif(netif);
+#endif /* LWIP_IPV6 */
+  }
+}
+
+/** Send ARP/IGMP/MLD/RS events, e.g. on link-up/netif-up or addr-change
+ */
+static void
+netif_issue_reports(struct netif *netif, u8_t report_type)
+{
+  LWIP_ASSERT("netif_issue_reports: invalid netif", netif != NULL);
+
+  /* Only send reports when both link and admin states are up */
+  if (!(netif->flags & NETIF_FLAG_LINK_UP) ||
+      !(netif->flags & NETIF_FLAG_UP)) {
+    return;
+  }
+
+#if LWIP_IPV4
+  if ((report_type & NETIF_REPORT_TYPE_IPV4) &&
+      !ip4_addr_isany_val(*netif_ip4_addr(netif))) {
+#if LWIP_ARP && !LWIP_ACD
+    /* For Ethernet network interfaces:
+     * we would like to send a "gratuitous ARP".
+     * Only needs to be done here if ACD isn't configured.
+     */
+    if (netif->flags & (NETIF_FLAG_ETHARP)) {
+      etharp_gratuitous(netif);
+    }
+#endif /* LWIP_ARP */
+
+#if LWIP_IGMP
+    /* resend IGMP memberships */
+    if (netif->flags & NETIF_FLAG_IGMP) {
+      igmp_report_groups(netif);
+    }
+#endif /* LWIP_IGMP */
+  }
+#endif /* LWIP_IPV4 */
+
+#if LWIP_IPV6
+  if (report_type & NETIF_REPORT_TYPE_IPV6) {
+#if LWIP_IPV6_MLD
+    /* send mld memberships */
+    mld6_report_groups(netif);
+#endif /* LWIP_IPV6_MLD */
+  }
+#endif /* LWIP_IPV6 */
+}
+
+/**
+ * @ingroup netif
+ * Bring an interface down, disabling any traffic processing.
+ */
+void
+netif_set_down(struct netif *netif)
+{
+  LWIP_ASSERT_CORE_LOCKED();
+
+  LWIP_ERROR("netif_set_down: invalid netif", netif != NULL, return);
+
+  if (netif->flags & NETIF_FLAG_UP) {
+#if LWIP_NETIF_EXT_STATUS_CALLBACK
+    {
+      netif_ext_callback_args_t args;
+      args.status_changed.state = 0;
+      netif_invoke_ext_callback(netif, LWIP_NSC_STATUS_CHANGED, &args);
+    }
+#endif
+
+    netif_clear_flags(netif, NETIF_FLAG_UP);
+    MIB2_COPY_SYSUPTIME_TO(&netif->ts);
+
+#if LWIP_IPV4 && LWIP_ARP
+    if (netif->flags & NETIF_FLAG_ETHARP) {
+      etharp_cleanup_netif(netif);
+    }
+#endif /* LWIP_IPV4 && LWIP_ARP */
+
+#if LWIP_IPV6
+    nd6_cleanup_netif(netif);
+#endif /* LWIP_IPV6 */
+
+    NETIF_STATUS_CALLBACK(netif);
+  }
+}
+
+#if LWIP_NETIF_STATUS_CALLBACK
+/**
+ * @ingroup netif
+ * Set callback to be called when interface is brought up/down or address is changed while up
+ */
+void
+netif_set_status_callback(struct netif *netif, netif_status_callback_fn status_callback)
+{
+  LWIP_ASSERT_CORE_LOCKED();
+
+  if (netif) {
+    netif->status_callback = status_callback;
+  }
+}
+#endif /* LWIP_NETIF_STATUS_CALLBACK */
+
+#if LWIP_NETIF_REMOVE_CALLBACK
+/**
+ * @ingroup netif
+ * Set callback to be called when the interface has been removed
+ */
+void
+netif_set_remove_callback(struct netif *netif, netif_status_callback_fn remove_callback)
+{
+  LWIP_ASSERT_CORE_LOCKED();
+
+  if (netif) {
+    netif->remove_callback = remove_callback;
+  }
+}
+#endif /* LWIP_NETIF_REMOVE_CALLBACK */
+
+/**
+ * @ingroup netif
+ * Called by a driver when its link goes up
+ */
+void
+netif_set_link_up(struct netif *netif)
+{
+  LWIP_ASSERT_CORE_LOCKED();
+
+  LWIP_ERROR("netif_set_link_up: invalid netif", netif != NULL, return);
+
+  if (!(netif->flags & NETIF_FLAG_LINK_UP)) {
+    netif_set_flags(netif, NETIF_FLAG_LINK_UP);
+
+#if LWIP_DHCP
+    dhcp_network_changed_link_up(netif);
+#endif /* LWIP_DHCP */
+
+#if LWIP_AUTOIP
+    autoip_network_changed_link_up(netif);
+#endif /* LWIP_AUTOIP */
+
+    netif_issue_reports(netif, NETIF_REPORT_TYPE_IPV4 | NETIF_REPORT_TYPE_IPV6);
+#if LWIP_IPV6
+    nd6_restart_netif(netif);
+#endif /* LWIP_IPV6 */
+
+    NETIF_LINK_CALLBACK(netif);
+#if LWIP_NETIF_EXT_STATUS_CALLBACK
+    {
+      netif_ext_callback_args_t args;
+      args.link_changed.state = 1;
+      netif_invoke_ext_callback(netif, LWIP_NSC_LINK_CHANGED, &args);
+    }
+#endif
+  }
+}
+
+/**
+ * @ingroup netif
+ * Called by a driver when its link goes down
+ */
+void
+netif_set_link_down(struct netif *netif)
+{
+  LWIP_ASSERT_CORE_LOCKED();
+
+  LWIP_ERROR("netif_set_link_down: invalid netif", netif != NULL, return);
+
+  if (netif->flags & NETIF_FLAG_LINK_UP) {
+    netif_clear_flags(netif, NETIF_FLAG_LINK_UP);
+
+#if LWIP_AUTOIP
+    autoip_network_changed_link_down(netif);
+#endif /* LWIP_AUTOIP */
+
+#if LWIP_ACD
+    acd_network_changed_link_down(netif);
+#endif /* LWIP_ACD */
+
+#if LWIP_IPV6 && LWIP_ND6_ALLOW_RA_UPDATES
+    netif->mtu6 = netif->mtu;
+#endif
+
+    NETIF_LINK_CALLBACK(netif);
+#if LWIP_NETIF_EXT_STATUS_CALLBACK
+    {
+      netif_ext_callback_args_t args;
+      args.link_changed.state = 0;
+      netif_invoke_ext_callback(netif, LWIP_NSC_LINK_CHANGED, &args);
+    }
+#endif
+  }
+}
+
+#if LWIP_NETIF_LINK_CALLBACK
+/**
+ * @ingroup netif
+ * Set callback to be called when link is brought up/down
+ */
+void
+netif_set_link_callback(struct netif *netif, netif_status_callback_fn link_callback)
+{
+  LWIP_ASSERT_CORE_LOCKED();
+
+  if (netif) {
+    netif->link_callback = link_callback;
+  }
+}
+#endif /* LWIP_NETIF_LINK_CALLBACK */
+
+#if ENABLE_LOOPBACK
+/**
+ * @ingroup netif
+ * Send an IP packet to be received on the same netif (loopif-like).
+ * The pbuf is copied and added to an internal queue which is fed to 
+ * netif->input by netif_poll().
+ * In multithreaded mode, the call to netif_poll() is queued to be done on the
+ * TCP/IP thread.
+ * In callback mode, the user has the responsibility to call netif_poll() in 
+ * the main loop of their application.
+ *
+ * @param netif the lwip network interface structure
+ * @param p the (IP) packet to 'send'
+ * @return ERR_OK if the packet has been sent
+ *         ERR_MEM if the pbuf used to copy the packet couldn't be allocated
+ */
+err_t
+netif_loop_output(struct netif *netif, struct pbuf *p)
+{
+  struct pbuf *r;
+  err_t err;
+  struct pbuf *last;
+#if LWIP_LOOPBACK_MAX_PBUFS
+  u16_t clen = 0;
+#endif /* LWIP_LOOPBACK_MAX_PBUFS */
+  /* If we have a loopif, SNMP counters are adjusted for it,
+   * if not they are adjusted for 'netif'. */
+#if MIB2_STATS
+#if LWIP_HAVE_LOOPIF
+  struct netif *stats_if = &loop_netif;
+#else /* LWIP_HAVE_LOOPIF */
+  struct netif *stats_if = netif;
+#endif /* LWIP_HAVE_LOOPIF */
+#endif /* MIB2_STATS */
+#if LWIP_NETIF_LOOPBACK_MULTITHREADING
+  u8_t schedule_poll = 0;
+#endif /* LWIP_NETIF_LOOPBACK_MULTITHREADING */
+  SYS_ARCH_DECL_PROTECT(lev);
+
+  LWIP_ASSERT("netif_loop_output: invalid netif", netif != NULL);
+  LWIP_ASSERT("netif_loop_output: invalid pbuf", p != NULL);
+
+  /* Allocate a new pbuf */
+  r = pbuf_alloc(PBUF_LINK, p->tot_len, PBUF_RAM);
+  if (r == NULL) {
+    LINK_STATS_INC(link.memerr);
+    LINK_STATS_INC(link.drop);
+    MIB2_STATS_NETIF_INC(stats_if, ifoutdiscards);
+    return ERR_MEM;
+  }
+#if LWIP_LOOPBACK_MAX_PBUFS
+  clen = pbuf_clen(r);
+  /* check for overflow or too many pbuf on queue */
+  if (((netif->loop_cnt_current + clen) < netif->loop_cnt_current) ||
+      ((netif->loop_cnt_current + clen) > LWIP_MIN(LWIP_LOOPBACK_MAX_PBUFS, 0xFFFF))) {
+    pbuf_free(r);
+    LINK_STATS_INC(link.memerr);
+    LINK_STATS_INC(link.drop);
+    MIB2_STATS_NETIF_INC(stats_if, ifoutdiscards);
+    return ERR_MEM;
+  }
+  netif->loop_cnt_current = (u16_t)(netif->loop_cnt_current + clen);
+#endif /* LWIP_LOOPBACK_MAX_PBUFS */
+
+  /* Copy the whole pbuf queue p into the single pbuf r */
+  if ((err = pbuf_copy(r, p)) != ERR_OK) {
+    pbuf_free(r);
+    LINK_STATS_INC(link.memerr);
+    LINK_STATS_INC(link.drop);
+    MIB2_STATS_NETIF_INC(stats_if, ifoutdiscards);
+    return err;
+  }
+
+  /* Put the packet on a linked list which gets emptied through calling
+     netif_poll(). */
+
+  /* let last point to the last pbuf in chain r */
+  for (last = r; last->next != NULL; last = last->next) {
+    /* nothing to do here, just get to the last pbuf */
+  }
+
+  SYS_ARCH_PROTECT(lev);
+  if (netif->loop_first != NULL) {
+    LWIP_ASSERT("if first != NULL, last must also be != NULL", netif->loop_last != NULL);
+    netif->loop_last->next = r;
+    netif->loop_last = last;
+#if LWIP_NETIF_LOOPBACK_MULTITHREADING
+    if (netif->reschedule_poll) {
+      schedule_poll = 1;
+      netif->reschedule_poll = 0;
+    }
+#endif /* LWIP_NETIF_LOOPBACK_MULTITHREADING */
+  } else {
+    netif->loop_first = r;
+    netif->loop_last = last;
+#if LWIP_NETIF_LOOPBACK_MULTITHREADING
+    /* No existing packets queued, schedule poll */
+    schedule_poll = 1;
+#endif /* LWIP_NETIF_LOOPBACK_MULTITHREADING */
+  }
+  SYS_ARCH_UNPROTECT(lev);
+
+  LINK_STATS_INC(link.xmit);
+  MIB2_STATS_NETIF_ADD(stats_if, ifoutoctets, p->tot_len);
+  MIB2_STATS_NETIF_INC(stats_if, ifoutucastpkts);
+
+#if LWIP_NETIF_LOOPBACK_MULTITHREADING
+  /* For multithreading environment, schedule a call to netif_poll */
+  if (schedule_poll) {
+    if (tcpip_try_callback((tcpip_callback_fn)netif_poll, netif) != ERR_OK) {
+      SYS_ARCH_PROTECT(lev);
+      netif->reschedule_poll = 1;
+      SYS_ARCH_UNPROTECT(lev);
+    }
+  }
+#endif /* LWIP_NETIF_LOOPBACK_MULTITHREADING */
+
+  return ERR_OK;
+}
+
+#if LWIP_HAVE_LOOPIF
+#if LWIP_IPV4
+static err_t
+netif_loop_output_ipv4(struct netif *netif, struct pbuf *p, const ip4_addr_t *addr)
+{
+  LWIP_UNUSED_ARG(addr);
+  return netif_loop_output(netif, p);
+}
+#endif /* LWIP_IPV4 */
+
+#if LWIP_IPV6
+static err_t
+netif_loop_output_ipv6(struct netif *netif, struct pbuf *p, const ip6_addr_t *addr)
+{
+  LWIP_UNUSED_ARG(addr);
+  return netif_loop_output(netif, p);
+}
+#endif /* LWIP_IPV6 */
+#endif /* LWIP_HAVE_LOOPIF */
+
+
+/**
+ * Call netif_poll() in the main loop of your application. This is to prevent
+ * reentering non-reentrant functions like tcp_input(). Packets passed to
+ * netif_loop_output() are put on a list that is passed to netif->input() by
+ * netif_poll().
+ */
+void
+netif_poll(struct netif *netif)
+{
+  /* If we have a loopif, SNMP counters are adjusted for it,
+   * if not they are adjusted for 'netif'. */
+#if MIB2_STATS
+#if LWIP_HAVE_LOOPIF
+  struct netif *stats_if = &loop_netif;
+#else /* LWIP_HAVE_LOOPIF */
+  struct netif *stats_if = netif;
+#endif /* LWIP_HAVE_LOOPIF */
+#endif /* MIB2_STATS */
+  SYS_ARCH_DECL_PROTECT(lev);
+
+  LWIP_ASSERT("netif_poll: invalid netif", netif != NULL);
+
+  /* Get a packet from the list. With SYS_LIGHTWEIGHT_PROT=1, this is protected */
+  SYS_ARCH_PROTECT(lev);
+  while (netif->loop_first != NULL) {
+    struct pbuf *in, *in_end;
+#if LWIP_LOOPBACK_MAX_PBUFS
+    u8_t clen = 1;
+#endif /* LWIP_LOOPBACK_MAX_PBUFS */
+
+    in = in_end = netif->loop_first;
+    while (in_end->len != in_end->tot_len) {
+      LWIP_ASSERT("bogus pbuf: len != tot_len but next == NULL!", in_end->next != NULL);
+      in_end = in_end->next;
+#if LWIP_LOOPBACK_MAX_PBUFS
+      clen++;
+#endif /* LWIP_LOOPBACK_MAX_PBUFS */
+    }
+#if LWIP_LOOPBACK_MAX_PBUFS
+    /* adjust the number of pbufs on queue */
+    LWIP_ASSERT("netif->loop_cnt_current underflow",
+                ((netif->loop_cnt_current - clen) < netif->loop_cnt_current));
+    netif->loop_cnt_current = (u16_t)(netif->loop_cnt_current - clen);
+#endif /* LWIP_LOOPBACK_MAX_PBUFS */
+
+    /* 'in_end' now points to the last pbuf from 'in' */
+    if (in_end == netif->loop_last) {
+      /* this was the last pbuf in the list */
+      netif->loop_first = netif->loop_last = NULL;
+    } else {
+      /* pop the pbuf off the list */
+      netif->loop_first = in_end->next;
+      LWIP_ASSERT("should not be null since first != last!", netif->loop_first != NULL);
+    }
+    /* De-queue the pbuf from its successors on the 'loop_' list. */
+    in_end->next = NULL;
+    SYS_ARCH_UNPROTECT(lev);
+
+    in->if_idx = netif_get_index(netif);
+
+    LINK_STATS_INC(link.recv);
+    MIB2_STATS_NETIF_ADD(stats_if, ifinoctets, in->tot_len);
+    MIB2_STATS_NETIF_INC(stats_if, ifinucastpkts);
+    /* loopback packets are always IP packets! */
+    if (ip_input(in, netif) != ERR_OK) {
+      pbuf_free(in);
+    }
+    SYS_ARCH_PROTECT(lev);
+  }
+  SYS_ARCH_UNPROTECT(lev);
+}
+
+#if !LWIP_NETIF_LOOPBACK_MULTITHREADING
+/**
+ * Calls netif_poll() for every netif on the netif_list.
+ */
+void
+netif_poll_all(void)
+{
+  struct netif *netif;
+  /* loop through netifs */
+  NETIF_FOREACH(netif) {
+    netif_poll(netif);
+  }
+}
+#endif /* !LWIP_NETIF_LOOPBACK_MULTITHREADING */
+#endif /* ENABLE_LOOPBACK */
+
+#if LWIP_NUM_NETIF_CLIENT_DATA > 0
+/**
+ * @ingroup netif_cd
+ * Allocate an index to store data in client_data member of struct netif.
+ * Returned value is an index in mentioned array.
+ * @see LWIP_NUM_NETIF_CLIENT_DATA
+ */
+u8_t
+netif_alloc_client_data_id(void)
+{
+  u8_t result = netif_client_id;
+  netif_client_id++;
+
+  LWIP_ASSERT_CORE_LOCKED();
+
+#if LWIP_NUM_NETIF_CLIENT_DATA > 256
+#error LWIP_NUM_NETIF_CLIENT_DATA must be <= 256
+#endif
+  LWIP_ASSERT("Increase LWIP_NUM_NETIF_CLIENT_DATA in lwipopts.h", result < LWIP_NUM_NETIF_CLIENT_DATA);
+  return (u8_t)(result + LWIP_NETIF_CLIENT_DATA_INDEX_MAX);
+}
+#endif
+
+#if LWIP_IPV6
+/**
+ * @ingroup netif_ip6
+ * Change an IPv6 address of a network interface
+ *
+ * @param netif the network interface to change
+ * @param addr_idx index of the IPv6 address
+ * @param addr6 the new IPv6 address
+ *
+ * @note call netif_ip6_addr_set_state() to set the address valid/temptative
+ */
+void
+netif_ip6_addr_set(struct netif *netif, s8_t addr_idx, const ip6_addr_t *addr6)
+{
+  LWIP_ASSERT_CORE_LOCKED();
+
+  LWIP_ASSERT("netif_ip6_addr_set: invalid netif", netif != NULL);
+  LWIP_ASSERT("netif_ip6_addr_set: invalid addr6", addr6 != NULL);
+
+  netif_ip6_addr_set_parts(netif, addr_idx, addr6->addr[0], addr6->addr[1],
+                           addr6->addr[2], addr6->addr[3]);
+}
+
+/*
+ * Change an IPv6 address of a network interface (internal version taking 4 * u32_t)
+ *
+ * @param netif the network interface to change
+ * @param addr_idx index of the IPv6 address
+ * @param i0 word0 of the new IPv6 address
+ * @param i1 word1 of the new IPv6 address
+ * @param i2 word2 of the new IPv6 address
+ * @param i3 word3 of the new IPv6 address
+ */
+void
+netif_ip6_addr_set_parts(struct netif *netif, s8_t addr_idx, u32_t i0, u32_t i1, u32_t i2, u32_t i3)
+{
+  ip_addr_t old_addr;
+  ip_addr_t new_ipaddr;
+  LWIP_ASSERT_CORE_LOCKED();
+  LWIP_ASSERT("netif != NULL", netif != NULL);
+  LWIP_ASSERT("invalid index", addr_idx < LWIP_IPV6_NUM_ADDRESSES);
+
+  ip6_addr_copy(*ip_2_ip6(&old_addr), *netif_ip6_addr(netif, addr_idx));
+  IP_SET_TYPE_VAL(old_addr, IPADDR_TYPE_V6);
+
+  /* address is actually being changed? */
+  if ((ip_2_ip6(&old_addr)->addr[0] != i0) || (ip_2_ip6(&old_addr)->addr[1] != i1) ||
+      (ip_2_ip6(&old_addr)->addr[2] != i2) || (ip_2_ip6(&old_addr)->addr[3] != i3)) {
+    LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_STATE, ("netif_ip6_addr_set: netif address being changed\n"));
+
+    IP_ADDR6(&new_ipaddr, i0, i1, i2, i3);
+    ip6_addr_assign_zone(ip_2_ip6(&new_ipaddr), IP6_UNICAST, netif);
+
+    if (ip6_addr_isvalid(netif_ip6_addr_state(netif, addr_idx))) {
+      netif_do_ip_addr_changed(netif_ip_addr6(netif, addr_idx), &new_ipaddr);
+    }
+    /* @todo: remove/readd mib2 ip6 entries? */
+
+    ip_addr_copy(netif->ip6_addr[addr_idx], new_ipaddr);
+
+    if (ip6_addr_isvalid(netif_ip6_addr_state(netif, addr_idx))) {
+      netif_issue_reports(netif, NETIF_REPORT_TYPE_IPV6);
+      NETIF_STATUS_CALLBACK(netif);
+    }
+
+#if LWIP_NETIF_EXT_STATUS_CALLBACK
+    {
+      netif_ext_callback_args_t args;
+      args.ipv6_set.addr_index  = addr_idx;
+      args.ipv6_set.old_address = &old_addr;
+      netif_invoke_ext_callback(netif, LWIP_NSC_IPV6_SET, &args);
+    }
+#endif
+  }
+
+  LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("netif: IPv6 address %d of interface %c%c set to %s/0x%"X8_F"\n",
+              addr_idx, netif->name[0], netif->name[1], ip6addr_ntoa(netif_ip6_addr(netif, addr_idx)),
+              netif_ip6_addr_state(netif, addr_idx)));
+}
+
+/**
+ * @ingroup netif_ip6
+ * Change the state of an IPv6 address of a network interface
+ * (INVALID, TEMPTATIVE, PREFERRED, DEPRECATED, where TEMPTATIVE
+ * includes the number of checks done, see ip6_addr.h)
+ *
+ * @param netif the network interface to change
+ * @param addr_idx index of the IPv6 address
+ * @param state the new IPv6 address state
+ */
+void
+netif_ip6_addr_set_state(struct netif *netif, s8_t addr_idx, u8_t state)
+{
+  u8_t old_state;
+  LWIP_ASSERT_CORE_LOCKED();
+  LWIP_ASSERT("netif != NULL", netif != NULL);
+  LWIP_ASSERT("invalid index", addr_idx < LWIP_IPV6_NUM_ADDRESSES);
+
+  old_state = netif_ip6_addr_state(netif, addr_idx);
+  /* state is actually being changed? */
+  if (old_state != state) {
+    u8_t old_valid = old_state & IP6_ADDR_VALID;
+    u8_t new_valid = state & IP6_ADDR_VALID;
+    LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_STATE, ("netif_ip6_addr_set_state: netif address state being changed\n"));
+
+#if LWIP_IPV6_MLD
+    /* Reevaluate solicited-node multicast group membership. */
+    if (netif->flags & NETIF_FLAG_MLD6) {
+      nd6_adjust_mld_membership(netif, addr_idx, state);
+    }
+#endif /* LWIP_IPV6_MLD */
+
+    if (old_valid && !new_valid) {
+      /* address about to be removed by setting invalid */
+      netif_do_ip_addr_changed(netif_ip_addr6(netif, addr_idx), NULL);
+      /* @todo: remove mib2 ip6 entries? */
+    }
+    netif->ip6_addr_state[addr_idx] = state;
+
+    if (!old_valid && new_valid) {
+      /* address added by setting valid */
+      /* This is a good moment to check that the address is properly zoned. */
+      IP6_ADDR_ZONECHECK_NETIF(netif_ip6_addr(netif, addr_idx), netif);
+      /* @todo: add mib2 ip6 entries? */
+      netif_issue_reports(netif, NETIF_REPORT_TYPE_IPV6);
+    }
+    if ((old_state & ~IP6_ADDR_TENTATIVE_COUNT_MASK) !=
+        (state     & ~IP6_ADDR_TENTATIVE_COUNT_MASK)) {
+      /* address state has changed -> call the callback function */
+      NETIF_STATUS_CALLBACK(netif);
+    }
+
+#if LWIP_NETIF_EXT_STATUS_CALLBACK
+    {
+      netif_ext_callback_args_t args;
+      args.ipv6_addr_state_changed.addr_index = addr_idx;
+      args.ipv6_addr_state_changed.old_state  = old_state;
+      args.ipv6_addr_state_changed.address    = netif_ip_addr6(netif, addr_idx);
+      netif_invoke_ext_callback(netif, LWIP_NSC_IPV6_ADDR_STATE_CHANGED, &args);
+    }
+#endif
+  }
+  LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("netif: IPv6 address %d of interface %c%c set to %s/0x%"X8_F"\n",
+              addr_idx, netif->name[0], netif->name[1], ip6addr_ntoa(netif_ip6_addr(netif, addr_idx)),
+              netif_ip6_addr_state(netif, addr_idx)));
+}
+
+/**
+ * Checks if a specific local address is present on the netif and returns its
+ * index. Depending on its state, it may or may not be assigned to the
+ * interface (as per RFC terminology).
+ *
+ * The given address may or may not be zoned (i.e., have a zone index other
+ * than IP6_NO_ZONE). If the address is zoned, it must have the correct zone
+ * for the given netif, or no match will be found.
+ *
+ * @param netif the netif to check
+ * @param ip6addr the IPv6 address to find
+ * @return >= 0: address found, this is its index
+ *         -1: address not found on this netif
+ */
+s8_t
+netif_get_ip6_addr_match(struct netif *netif, const ip6_addr_t *ip6addr)
+{
+  s8_t i;
+
+  LWIP_ASSERT_CORE_LOCKED();
+
+  LWIP_ASSERT("netif_get_ip6_addr_match: invalid netif", netif != NULL);
+  LWIP_ASSERT("netif_get_ip6_addr_match: invalid ip6addr", ip6addr != NULL);
+
+#if LWIP_IPV6_SCOPES
+  if (ip6_addr_has_zone(ip6addr) && !ip6_addr_test_zone(ip6addr, netif)) {
+    return -1; /* wrong zone, no match */
+  }
+#endif /* LWIP_IPV6_SCOPES */
+
+  for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+    if (!ip6_addr_isinvalid(netif_ip6_addr_state(netif, i)) &&
+        ip6_addr_zoneless_eq(netif_ip6_addr(netif, i), ip6addr)) {
+      return i;
+    }
+  }
+  return -1;
+}
+
+/**
+ * @ingroup netif_ip6
+ * Create a link-local IPv6 address on a netif (stored in slot 0)
+ *
+ * @param netif the netif to create the address on
+ * @param from_mac_48bit if != 0, assume hwadr is a 48-bit MAC address (std conversion)
+ *                       if == 0, use hwaddr directly as interface ID
+ */
+void
+netif_create_ip6_linklocal_address(struct netif *netif, u8_t from_mac_48bit)
+{
+  u8_t i, addr_index;
+
+  LWIP_ASSERT_CORE_LOCKED();
+
+  LWIP_ASSERT("netif_create_ip6_linklocal_address: invalid netif", netif != NULL);
+
+  /* Link-local prefix. */
+  ip_2_ip6(&netif->ip6_addr[0])->addr[0] = PP_HTONL(0xfe800000ul);
+  ip_2_ip6(&netif->ip6_addr[0])->addr[1] = 0;
+
+  /* Generate interface ID. */
+  if (from_mac_48bit) {
+    /* Assume hwaddr is a 48-bit IEEE 802 MAC. Convert to EUI-64 address. Complement Group bit. */
+    ip_2_ip6(&netif->ip6_addr[0])->addr[2] = lwip_htonl((((u32_t)(netif->hwaddr[0] ^ 0x02)) << 24) |
+        ((u32_t)(netif->hwaddr[1]) << 16) |
+        ((u32_t)(netif->hwaddr[2]) << 8) |
+        (0xff));
+    ip_2_ip6(&netif->ip6_addr[0])->addr[3] = lwip_htonl((u32_t)(0xfeul << 24) |
+        ((u32_t)(netif->hwaddr[3]) << 16) |
+        ((u32_t)(netif->hwaddr[4]) << 8) |
+        (netif->hwaddr[5]));
+  } else {
+    /* Use hwaddr directly as interface ID. */
+    ip_2_ip6(&netif->ip6_addr[0])->addr[2] = 0;
+    ip_2_ip6(&netif->ip6_addr[0])->addr[3] = 0;
+
+    addr_index = 3;
+    for (i = 0; (i < 8) && (i < netif->hwaddr_len); i++) {
+      if (i == 4) {
+        addr_index--;
+      }
+      ip_2_ip6(&netif->ip6_addr[0])->addr[addr_index] |= lwip_htonl(((u32_t)(netif->hwaddr[netif->hwaddr_len - i - 1])) << (8 * (i & 0x03)));
+    }
+  }
+
+  /* Set a link-local zone. Even though the zone is implied by the owning
+   * netif, setting the zone anyway has two important conceptual advantages:
+   * 1) it avoids the need for a ton of exceptions in internal code, allowing
+   *    e.g. ip6_addr_eq() to be used on local addresses;
+   * 2) the properly zoned address is visible externally, e.g. when any outside
+   *    code enumerates available addresses or uses one to bind a socket.
+   * Any external code unaware of address scoping is likely to just ignore the
+   * zone field, so this should not create any compatibility problems. */
+  ip6_addr_assign_zone(ip_2_ip6(&netif->ip6_addr[0]), IP6_UNICAST, netif);
+
+  /* Set address state. */
+#if LWIP_IPV6_DUP_DETECT_ATTEMPTS
+  /* Will perform duplicate address detection (DAD). */
+  netif_ip6_addr_set_state(netif, 0, IP6_ADDR_TENTATIVE);
+#else
+  /* Consider address valid. */
+  netif_ip6_addr_set_state(netif, 0, IP6_ADDR_PREFERRED);
+#endif /* LWIP_IPV6_AUTOCONFIG */
+}
+
+/**
+ * @ingroup netif_ip6
+ * This function allows for the easy addition of a new IPv6 address to an interface.
+ * It takes care of finding an empty slot and then sets the address tentative
+ * (to make sure that all the subsequent processing happens).
+ *
+ * @param netif netif to add the address on
+ * @param ip6addr address to add
+ * @param chosen_idx if != NULL, the chosen IPv6 address index will be stored here
+ */
+err_t
+netif_add_ip6_address(struct netif *netif, const ip6_addr_t *ip6addr, s8_t *chosen_idx)
+{
+  s8_t i;
+
+  LWIP_ASSERT_CORE_LOCKED();
+
+  LWIP_ASSERT("netif_add_ip6_address: invalid netif", netif != NULL);
+  LWIP_ASSERT("netif_add_ip6_address: invalid ip6addr", ip6addr != NULL);
+
+  i = netif_get_ip6_addr_match(netif, ip6addr);
+  if (i >= 0) {
+    /* Address already added */
+    if (chosen_idx != NULL) {
+      *chosen_idx = i;
+    }
+    return ERR_OK;
+  }
+
+  /* Find a free slot. The first one is reserved for link-local addresses. */
+  for (i = ip6_addr_islinklocal(ip6addr) ? 0 : 1; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+    if (ip6_addr_isinvalid(netif_ip6_addr_state(netif, i))) {
+      ip_addr_copy_from_ip6(netif->ip6_addr[i], *ip6addr);
+      ip6_addr_assign_zone(ip_2_ip6(&netif->ip6_addr[i]), IP6_UNICAST, netif);
+      netif_ip6_addr_set_state(netif, i, IP6_ADDR_TENTATIVE);
+      if (chosen_idx != NULL) {
+        *chosen_idx = i;
+      }
+      return ERR_OK;
+    }
+  }
+
+  if (chosen_idx != NULL) {
+    *chosen_idx = -1;
+  }
+  return ERR_VAL;
+}
+
+/** Dummy IPv6 output function for netifs not supporting IPv6
+ */
+static err_t
+netif_null_output_ip6(struct netif *netif, struct pbuf *p, const ip6_addr_t *ipaddr)
+{
+  LWIP_UNUSED_ARG(netif);
+  LWIP_UNUSED_ARG(p);
+  LWIP_UNUSED_ARG(ipaddr);
+
+  return ERR_IF;
+}
+#endif /* LWIP_IPV6 */
+
+#if LWIP_IPV4
+/** Dummy IPv4 output function for netifs not supporting IPv4
+ */
+static err_t
+netif_null_output_ip4(struct netif *netif, struct pbuf *p, const ip4_addr_t *ipaddr)
+{
+  LWIP_UNUSED_ARG(netif);
+  LWIP_UNUSED_ARG(p);
+  LWIP_UNUSED_ARG(ipaddr);
+
+  return ERR_IF;
+}
+#endif /* LWIP_IPV4 */
+
+/**
+* @ingroup netif
+* Return the interface index for the netif with name
+* or NETIF_NO_INDEX if not found/on error
+*
+* @param name the name of the netif
+*/
+u8_t
+netif_name_to_index(const char *name)
+{
+  struct netif *netif = netif_find(name);
+  if (netif != NULL) {
+    return netif_get_index(netif);
+  }
+  /* No name found, return invalid index */
+  return NETIF_NO_INDEX;
+}
+
+/**
+* @ingroup netif
+* Return the interface name for the netif matching index
+* or NULL if not found/on error
+*
+* @param idx the interface index of the netif
+* @param name char buffer of at least NETIF_NAMESIZE bytes
+*/
+char *
+netif_index_to_name(u8_t idx, char *name)
+{
+  struct netif *netif = netif_get_by_index(idx);
+
+  if (netif != NULL) {
+    name[0] = netif->name[0];
+    name[1] = netif->name[1];
+    lwip_itoa(&name[2], NETIF_NAMESIZE - 2, netif_index_to_num(idx));
+    return name;
+  }
+  return NULL;
+}
+
+/**
+* @ingroup netif
+* Return the interface for the netif index
+*
+* @param idx index of netif to find
+*/
+struct netif *
+netif_get_by_index(u8_t idx)
+{
+  struct netif *netif;
+
+  LWIP_ASSERT_CORE_LOCKED();
+
+  if (idx != NETIF_NO_INDEX) {
+    NETIF_FOREACH(netif) {
+      if (idx == netif_get_index(netif)) {
+        return netif; /* found! */
+      }
+    }
+  }
+
+  return NULL;
+}
+
+/**
+ * @ingroup netif
+ * Find a network interface by searching for its name
+ *
+ * @param name the name of the netif (like netif->name) plus concatenated number
+ * in ascii representation (e.g. 'en0')
+ */
+struct netif *
+netif_find(const char *name)
+{
+  struct netif *netif;
+  u8_t num;
+
+  LWIP_ASSERT_CORE_LOCKED();
+
+  if (name == NULL) {
+    return NULL;
+  }
+
+  num = (u8_t)atoi(&name[2]);
+  if (!num && (name[2] != '0')) {
+    /* this means atoi has failed */
+    return NULL;
+  }
+
+  NETIF_FOREACH(netif) {
+    if (num == netif->num &&
+        name[0] == netif->name[0] &&
+        name[1] == netif->name[1]) {
+      LWIP_DEBUGF(NETIF_DEBUG, ("netif_find: found %c%c\n", name[0], name[1]));
+      return netif;
+    }
+  }
+  LWIP_DEBUGF(NETIF_DEBUG, ("netif_find: didn't find %c%c\n", name[0], name[1]));
+  return NULL;
+}
+
+#if LWIP_NETIF_EXT_STATUS_CALLBACK
+/**
+ * @ingroup netif
+ * Add extended netif events listener
+ * @param callback pointer to listener structure
+ * @param fn callback function
+ */
+void
+netif_add_ext_callback(netif_ext_callback_t *callback, netif_ext_callback_fn fn)
+{
+  LWIP_ASSERT_CORE_LOCKED();
+  LWIP_ASSERT("callback must be != NULL", callback != NULL);
+  LWIP_ASSERT("fn must be != NULL", fn != NULL);
+
+  callback->callback_fn = fn;
+  callback->next        = ext_callback;
+  ext_callback          = callback;
+}
+
+/**
+ * @ingroup netif
+ * Remove extended netif events listener
+ * @param callback pointer to listener structure
+ */
+void
+netif_remove_ext_callback(netif_ext_callback_t* callback)
+{
+  netif_ext_callback_t *last, *iter;
+
+  LWIP_ASSERT_CORE_LOCKED();
+  LWIP_ASSERT("callback must be != NULL", callback != NULL);
+
+  if (ext_callback == NULL) {
+    return;
+  }
+
+  if (callback == ext_callback) {
+    ext_callback = ext_callback->next;
+  } else {
+    last = ext_callback;
+    for (iter = ext_callback->next; iter != NULL; last = iter, iter = iter->next) {
+      if (iter == callback) {
+        LWIP_ASSERT("last != NULL", last != NULL);
+        last->next = callback->next;
+        break;
+      }
+    }
+  }
+  callback->next = NULL;
+}
+
+/**
+ * Invoke extended netif status event
+ * @param netif netif that is affected by change
+ * @param reason change reason
+ * @param args depends on reason, see reason description
+ */
+void
+netif_invoke_ext_callback(struct netif *netif, netif_nsc_reason_t reason, const netif_ext_callback_args_t *args)
+{
+  netif_ext_callback_t *callback = ext_callback;
+
+  LWIP_ASSERT("netif must be != NULL", netif != NULL);
+
+  while (callback != NULL) {
+    /* cache next pointer: the callback might unregister itself */
+    netif_ext_callback_t *next = callback->next;
+    callback->callback_fn(netif, reason, args);
+    callback = next;
+  }
+}
+#endif /* LWIP_NETIF_EXT_STATUS_CALLBACK */
diff --git a/lib/lwip/core/pbuf.c b/lib/lwip/core/pbuf.c
new file mode 100644
index 0000000000..1fb64d4115
--- /dev/null
+++ b/lib/lwip/core/pbuf.c
@@ -0,0 +1,1545 @@
+/**
+ * @file
+ * Packet buffer management
+ */
+
+/**
+ * @defgroup pbuf Packet buffers (PBUF)
+ * @ingroup infrastructure
+ *
+ * Packets are built from the pbuf data structure. It supports dynamic
+ * memory allocation for packet contents or can reference externally
+ * managed packet contents both in RAM and ROM. Quick allocation for
+ * incoming packets is provided through pools with fixed sized pbufs.
+ *
+ * A packet may span over multiple pbufs, chained as a singly linked
+ * list. This is called a "pbuf chain".
+ *
+ * Multiple packets may be queued, also using this singly linked list.
+ * This is called a "packet queue".
+ *
+ * So, a packet queue consists of one or more pbuf chains, each of
+ * which consist of one or more pbufs. CURRENTLY, PACKET QUEUES ARE
+ * NOT SUPPORTED!!! Use helper structs to queue multiple packets.
+ *
+ * The differences between a pbuf chain and a packet queue are very
+ * precise but subtle.
+ *
+ * The last pbuf of a packet has a ->tot_len field that equals the
+ * ->len field. It can be found by traversing the list. If the last
+ * pbuf of a packet has a ->next field other than NULL, more packets
+ * are on the queue.
+ *
+ * Therefore, looping through a pbuf of a single packet, has an
+ * loop end condition (tot_len == p->len), NOT (next == NULL).
+ *
+ * Example of custom pbuf usage: @ref zerocopyrx
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#include "lwip/pbuf.h"
+#include "lwip/stats.h"
+#include "lwip/def.h"
+#include "lwip/mem.h"
+#include "lwip/memp.h"
+#include "lwip/sys.h"
+#include "lwip/netif.h"
+#if LWIP_TCP && TCP_QUEUE_OOSEQ
+#include "lwip/priv/tcp_priv.h"
+#endif
+#if LWIP_CHECKSUM_ON_COPY
+#include "lwip/inet_chksum.h"
+#endif
+
+#include <string.h>
+
+#define SIZEOF_STRUCT_PBUF        LWIP_MEM_ALIGN_SIZE(sizeof(struct pbuf))
+/* Since the pool is created in memp, PBUF_POOL_BUFSIZE will be automatically
+   aligned there. Therefore, PBUF_POOL_BUFSIZE_ALIGNED can be used here. */
+#define PBUF_POOL_BUFSIZE_ALIGNED LWIP_MEM_ALIGN_SIZE(PBUF_POOL_BUFSIZE)
+
+static const struct pbuf *
+pbuf_skip_const(const struct pbuf *in, u16_t in_offset, u16_t *out_offset);
+
+#if !LWIP_TCP || !TCP_QUEUE_OOSEQ || !PBUF_POOL_FREE_OOSEQ
+#define PBUF_POOL_IS_EMPTY()
+#else /* !LWIP_TCP || !TCP_QUEUE_OOSEQ || !PBUF_POOL_FREE_OOSEQ */
+
+#if !NO_SYS
+#ifndef PBUF_POOL_FREE_OOSEQ_QUEUE_CALL
+#include "lwip/tcpip.h"
+#define PBUF_POOL_FREE_OOSEQ_QUEUE_CALL()  do { \
+  if (tcpip_try_callback(pbuf_free_ooseq_callback, NULL) != ERR_OK) { \
+      SYS_ARCH_PROTECT(old_level); \
+      pbuf_free_ooseq_pending = 0; \
+      SYS_ARCH_UNPROTECT(old_level); \
+  } } while(0)
+#endif /* PBUF_POOL_FREE_OOSEQ_QUEUE_CALL */
+#endif /* !NO_SYS */
+
+volatile u8_t pbuf_free_ooseq_pending;
+#define PBUF_POOL_IS_EMPTY() pbuf_pool_is_empty()
+
+/**
+ * Attempt to reclaim some memory from queued out-of-sequence TCP segments
+ * if we run out of pool pbufs. It's better to give priority to new packets
+ * if we're running out.
+ *
+ * This must be done in the correct thread context therefore this function
+ * can only be used with NO_SYS=0 and through tcpip_callback.
+ */
+#if !NO_SYS
+static
+#endif /* !NO_SYS */
+void
+pbuf_free_ooseq(void)
+{
+  struct tcp_pcb *pcb;
+  SYS_ARCH_SET(pbuf_free_ooseq_pending, 0);
+
+  for (pcb = tcp_active_pcbs; NULL != pcb; pcb = pcb->next) {
+    if (pcb->ooseq != NULL) {
+      /** Free the ooseq pbufs of one PCB only */
+      LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free_ooseq: freeing out-of-sequence pbufs\n"));
+      tcp_free_ooseq(pcb);
+      return;
+    }
+  }
+}
+
+#if !NO_SYS
+/**
+ * Just a callback function for tcpip_callback() that calls pbuf_free_ooseq().
+ */
+static void
+pbuf_free_ooseq_callback(void *arg)
+{
+  LWIP_UNUSED_ARG(arg);
+  pbuf_free_ooseq();
+}
+#endif /* !NO_SYS */
+
+/** Queue a call to pbuf_free_ooseq if not already queued. */
+static void
+pbuf_pool_is_empty(void)
+{
+#ifndef PBUF_POOL_FREE_OOSEQ_QUEUE_CALL
+  SYS_ARCH_SET(pbuf_free_ooseq_pending, 1);
+#else /* PBUF_POOL_FREE_OOSEQ_QUEUE_CALL */
+  u8_t queued;
+  SYS_ARCH_DECL_PROTECT(old_level);
+  SYS_ARCH_PROTECT(old_level);
+  queued = pbuf_free_ooseq_pending;
+  pbuf_free_ooseq_pending = 1;
+  SYS_ARCH_UNPROTECT(old_level);
+
+  if (!queued) {
+    /* queue a call to pbuf_free_ooseq if not already queued */
+    PBUF_POOL_FREE_OOSEQ_QUEUE_CALL();
+  }
+#endif /* PBUF_POOL_FREE_OOSEQ_QUEUE_CALL */
+}
+#endif /* !LWIP_TCP || !TCP_QUEUE_OOSEQ || !PBUF_POOL_FREE_OOSEQ */
+
+/* Initialize members of struct pbuf after allocation */
+static void
+pbuf_init_alloced_pbuf(struct pbuf *p, void *payload, u16_t tot_len, u16_t len, pbuf_type type, u8_t flags)
+{
+  p->next = NULL;
+  p->payload = payload;
+  p->tot_len = tot_len;
+  p->len = len;
+  p->type_internal = (u8_t)type;
+  p->flags = flags;
+  p->ref = 1;
+  p->if_idx = NETIF_NO_INDEX;
+}
+
+/**
+ * @ingroup pbuf
+ * Allocates a pbuf of the given type (possibly a chain for PBUF_POOL type).
+ *
+ * The actual memory allocated for the pbuf is determined by the
+ * layer at which the pbuf is allocated and the requested size
+ * (from the size parameter).
+ *
+ * @param layer header size
+ * @param length size of the pbuf's payload
+ * @param type this parameter decides how and where the pbuf
+ * should be allocated as follows:
+ *
+ * - PBUF_RAM: buffer memory for pbuf is allocated as one large
+ *             chunk. This includes protocol headers as well.
+ * - PBUF_ROM: no buffer memory is allocated for the pbuf, even for
+ *             protocol headers. Additional headers must be prepended
+ *             by allocating another pbuf and chain in to the front of
+ *             the ROM pbuf. It is assumed that the memory used is really
+ *             similar to ROM in that it is immutable and will not be
+ *             changed. Memory which is dynamic should generally not
+ *             be attached to PBUF_ROM pbufs. Use PBUF_REF instead.
+ * - PBUF_REF: no buffer memory is allocated for the pbuf, even for
+ *             protocol headers. It is assumed that the pbuf is only
+ *             being used in a single thread. If the pbuf gets queued,
+ *             then pbuf_take should be called to copy the buffer.
+ * - PBUF_POOL: the pbuf is allocated as a pbuf chain, with pbufs from
+ *              the pbuf pool that is allocated during pbuf_init().
+ *
+ * @return the allocated pbuf. If multiple pbufs where allocated, this
+ * is the first pbuf of a pbuf chain.
+ */
+struct pbuf *
+pbuf_alloc(pbuf_layer layer, u16_t length, pbuf_type type)
+{
+  struct pbuf *p;
+  u16_t offset = (u16_t)layer;
+  LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc(length=%"U16_F")\n", length));
+
+  switch (type) {
+    case PBUF_REF: /* fall through */
+    case PBUF_ROM:
+      p = pbuf_alloc_reference(NULL, length, type);
+      break;
+    case PBUF_POOL: {
+      struct pbuf *q, *last;
+      u16_t rem_len; /* remaining length */
+      p = NULL;
+      last = NULL;
+      rem_len = length;
+      do {
+        u16_t qlen;
+        q = (struct pbuf *)memp_malloc(MEMP_PBUF_POOL);
+        if (q == NULL) {
+          PBUF_POOL_IS_EMPTY();
+          /* free chain so far allocated */
+          if (p) {
+            pbuf_free(p);
+          }
+          /* bail out unsuccessfully */
+          return NULL;
+        }
+        qlen = LWIP_MIN(rem_len, (u16_t)(PBUF_POOL_BUFSIZE_ALIGNED - LWIP_MEM_ALIGN_SIZE(offset)));
+        pbuf_init_alloced_pbuf(q, LWIP_MEM_ALIGN((void *)((u8_t *)q + SIZEOF_STRUCT_PBUF + offset)),
+                               rem_len, qlen, type, 0);
+        LWIP_ASSERT("pbuf_alloc: pbuf q->payload properly aligned",
+                    ((mem_ptr_t)q->payload % MEM_ALIGNMENT) == 0);
+        LWIP_ASSERT("PBUF_POOL_BUFSIZE must be bigger than MEM_ALIGNMENT",
+                    (PBUF_POOL_BUFSIZE_ALIGNED - LWIP_MEM_ALIGN_SIZE(offset)) > 0 );
+        if (p == NULL) {
+          /* allocated head of pbuf chain (into p) */
+          p = q;
+        } else {
+          /* make previous pbuf point to this pbuf */
+          last->next = q;
+        }
+        last = q;
+        rem_len = (u16_t)(rem_len - qlen);
+        offset = 0;
+      } while (rem_len > 0);
+      break;
+    }
+    case PBUF_RAM: {
+      mem_size_t payload_len = (mem_size_t)(LWIP_MEM_ALIGN_SIZE(offset) + LWIP_MEM_ALIGN_SIZE(length));
+      mem_size_t alloc_len = (mem_size_t)(LWIP_MEM_ALIGN_SIZE(SIZEOF_STRUCT_PBUF) + payload_len);
+
+      /* bug #50040: Check for integer overflow when calculating alloc_len */
+      if ((payload_len < LWIP_MEM_ALIGN_SIZE(length)) ||
+          (alloc_len < LWIP_MEM_ALIGN_SIZE(length))) {
+        return NULL;
+      }
+
+      /* If pbuf is to be allocated in RAM, allocate memory for it. */
+      p = (struct pbuf *)mem_malloc(alloc_len);
+      if (p == NULL) {
+        return NULL;
+      }
+      pbuf_init_alloced_pbuf(p, LWIP_MEM_ALIGN((void *)((u8_t *)p + SIZEOF_STRUCT_PBUF + offset)),
+                             length, length, type, 0);
+      LWIP_ASSERT("pbuf_alloc: pbuf->payload properly aligned",
+                  ((mem_ptr_t)p->payload % MEM_ALIGNMENT) == 0);
+      break;
+    }
+    default:
+      LWIP_ASSERT("pbuf_alloc: erroneous type", 0);
+      return NULL;
+  }
+  LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc(length=%"U16_F") == %p\n", length, (void *)p));
+  return p;
+}
+
+/**
+ * @ingroup pbuf
+ * Allocates a pbuf for referenced data.
+ * Referenced data can be volatile (PBUF_REF) or long-lived (PBUF_ROM).
+ *
+ * The actual memory allocated for the pbuf is determined by the
+ * layer at which the pbuf is allocated and the requested size
+ * (from the size parameter).
+ *
+ * @param payload referenced payload
+ * @param length size of the pbuf's payload
+ * @param type this parameter decides how and where the pbuf
+ * should be allocated as follows:
+ *
+ * - PBUF_ROM: It is assumed that the memory used is really
+ *             similar to ROM in that it is immutable and will not be
+ *             changed. Memory which is dynamic should generally not
+ *             be attached to PBUF_ROM pbufs. Use PBUF_REF instead.
+ * - PBUF_REF: It is assumed that the pbuf is only
+ *             being used in a single thread. If the pbuf gets queued,
+ *             then pbuf_take should be called to copy the buffer.
+ *
+ * @return the allocated pbuf.
+ */
+struct pbuf *
+pbuf_alloc_reference(void *payload, u16_t length, pbuf_type type)
+{
+  struct pbuf *p;
+  LWIP_ASSERT("invalid pbuf_type", (type == PBUF_REF) || (type == PBUF_ROM));
+  /* only allocate memory for the pbuf structure */
+  p = (struct pbuf *)memp_malloc(MEMP_PBUF);
+  if (p == NULL) {
+    LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+                ("pbuf_alloc_reference: Could not allocate MEMP_PBUF for PBUF_%s.\n",
+                 (type == PBUF_ROM) ? "ROM" : "REF"));
+    return NULL;
+  }
+  pbuf_init_alloced_pbuf(p, payload, length, length, type, 0);
+  return p;
+}
+
+
+#if LWIP_SUPPORT_CUSTOM_PBUF
+/**
+ * @ingroup pbuf
+ * Initialize a custom pbuf (already allocated).
+ * Example of custom pbuf usage: @ref zerocopyrx
+ *
+ * @param l header size
+ * @param length size of the pbuf's payload
+ * @param type type of the pbuf (only used to treat the pbuf accordingly, as
+ *        this function allocates no memory)
+ * @param p pointer to the custom pbuf to initialize (already allocated)
+ * @param payload_mem pointer to the buffer that is used for payload and headers,
+ *        must be at least big enough to hold 'length' plus the header size,
+ *        may be NULL if set later.
+ *        ATTENTION: The caller is responsible for correct alignment of this buffer!!
+ * @param payload_mem_len the size of the 'payload_mem' buffer, must be at least
+ *        big enough to hold 'length' plus the header size
+ */
+struct pbuf *
+pbuf_alloced_custom(pbuf_layer l, u16_t length, pbuf_type type, struct pbuf_custom *p,
+                    void *payload_mem, u16_t payload_mem_len)
+{
+  u16_t offset = (u16_t)l;
+  void *payload;
+  LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloced_custom(length=%"U16_F")\n", length));
+
+  if (LWIP_MEM_ALIGN_SIZE(offset) + length > payload_mem_len) {
+    LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_LEVEL_WARNING, ("pbuf_alloced_custom(length=%"U16_F") buffer too short\n", length));
+    return NULL;
+  }
+
+  if (payload_mem != NULL) {
+    payload = (u8_t *)payload_mem + LWIP_MEM_ALIGN_SIZE(offset);
+  } else {
+    payload = NULL;
+  }
+  pbuf_init_alloced_pbuf(&p->pbuf, payload, length, length, type, PBUF_FLAG_IS_CUSTOM);
+  return &p->pbuf;
+}
+#endif /* LWIP_SUPPORT_CUSTOM_PBUF */
+
+/**
+ * @ingroup pbuf
+ * Shrink a pbuf chain to a desired length.
+ *
+ * @param p pbuf to shrink.
+ * @param new_len desired new length of pbuf chain
+ *
+ * Depending on the desired length, the first few pbufs in a chain might
+ * be skipped and left unchanged. The new last pbuf in the chain will be
+ * resized, and any remaining pbufs will be freed.
+ *
+ * @note If the pbuf is ROM/REF, only the ->tot_len and ->len fields are adjusted.
+ * @note May not be called on a packet queue.
+ *
+ * @note Despite its name, pbuf_realloc cannot grow the size of a pbuf (chain).
+ */
+void
+pbuf_realloc(struct pbuf *p, u16_t new_len)
+{
+  struct pbuf *q;
+  u16_t rem_len; /* remaining length */
+  u16_t shrink;
+
+  LWIP_ASSERT("pbuf_realloc: p != NULL", p != NULL);
+
+  /* desired length larger than current length? */
+  if (new_len >= p->tot_len) {
+    /* enlarging not yet supported */
+    return;
+  }
+
+  /* the pbuf chain grows by (new_len - p->tot_len) bytes
+   * (which may be negative in case of shrinking) */
+  shrink = (u16_t)(p->tot_len - new_len);
+
+  /* first, step over any pbufs that should remain in the chain */
+  rem_len = new_len;
+  q = p;
+  /* should this pbuf be kept? */
+  while (rem_len > q->len) {
+    /* decrease remaining length by pbuf length */
+    rem_len = (u16_t)(rem_len - q->len);
+    /* decrease total length indicator */
+    q->tot_len = (u16_t)(q->tot_len - shrink);
+    /* proceed to next pbuf in chain */
+    q = q->next;
+    LWIP_ASSERT("pbuf_realloc: q != NULL", q != NULL);
+  }
+  /* we have now reached the new last pbuf (in q) */
+  /* rem_len == desired length for pbuf q */
+
+  /* shrink allocated memory for PBUF_RAM */
+  /* (other types merely adjust their length fields */
+  if (pbuf_match_allocsrc(q, PBUF_TYPE_ALLOC_SRC_MASK_STD_HEAP) && (rem_len != q->len)
+#if LWIP_SUPPORT_CUSTOM_PBUF
+      && ((q->flags & PBUF_FLAG_IS_CUSTOM) == 0)
+#endif /* LWIP_SUPPORT_CUSTOM_PBUF */
+     ) {
+    /* reallocate and adjust the length of the pbuf that will be split */
+    struct pbuf *r = (struct pbuf *)mem_trim(q, (mem_size_t)(((u8_t *)q->payload - (u8_t *)q) + rem_len));
+    LWIP_ASSERT("mem_trim returned r == NULL", r != NULL);
+    /* help to detect faulty overridden implementation of mem_trim */
+    LWIP_ASSERT("mem_trim returned r != q", r == q);
+    LWIP_UNUSED_ARG(r);
+  }
+  /* adjust length fields for new last pbuf */
+  q->len = rem_len;
+  q->tot_len = q->len;
+
+  /* any remaining pbufs in chain? */
+  if (q->next != NULL) {
+    /* free remaining pbufs in chain */
+    pbuf_free(q->next);
+  }
+  /* q is last packet in chain */
+  q->next = NULL;
+
+}
+
+/**
+ * Adjusts the payload pointer to reveal headers in the payload.
+ * @see pbuf_add_header.
+ *
+ * @param p pbuf to change the header size.
+ * @param header_size_increment Number of bytes to increment header size.
+ * @param force Allow 'header_size_increment > 0' for PBUF_REF/PBUF_ROM types
+ *
+ * @return non-zero on failure, zero on success.
+ *
+ */
+static u8_t
+pbuf_add_header_impl(struct pbuf *p, size_t header_size_increment, u8_t force)
+{
+  u16_t type_internal;
+  void *payload;
+  u16_t increment_magnitude;
+
+  LWIP_ASSERT("p != NULL", p != NULL);
+  if ((p == NULL) || (header_size_increment > 0xFFFF)) {
+    return 1;
+  }
+  if (header_size_increment == 0) {
+    return 0;
+  }
+
+  increment_magnitude = (u16_t)header_size_increment;
+  /* Do not allow tot_len to wrap as a result. */
+  if ((u16_t)(increment_magnitude + p->tot_len) < increment_magnitude) {
+    return 1;
+  }
+
+  type_internal = p->type_internal;
+
+  /* pbuf types containing payloads? */
+  if (type_internal & PBUF_TYPE_FLAG_STRUCT_DATA_CONTIGUOUS) {
+    /* set new payload pointer */
+    payload = (u8_t *)p->payload - header_size_increment;
+    /* boundary check fails? */
+    if ((u8_t *)payload < (u8_t *)p + SIZEOF_STRUCT_PBUF) {
+      LWIP_DEBUGF( PBUF_DEBUG | LWIP_DBG_TRACE,
+                   ("pbuf_add_header: failed as %p < %p (not enough space for new header size)\n",
+                    (void *)payload, (void *)((u8_t *)p + SIZEOF_STRUCT_PBUF)));
+      /* bail out unsuccessfully */
+      return 1;
+    }
+    /* pbuf types referring to external payloads? */
+  } else {
+    /* hide a header in the payload? */
+    if (force) {
+      payload = (u8_t *)p->payload - header_size_increment;
+    } else {
+      /* cannot expand payload to front (yet!)
+       * bail out unsuccessfully */
+      return 1;
+    }
+  }
+  LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_add_header: old %p new %p (%"U16_F")\n",
+              (void *)p->payload, (void *)payload, increment_magnitude));
+
+  /* modify pbuf fields */
+  p->payload = payload;
+  p->len = (u16_t)(p->len + increment_magnitude);
+  p->tot_len = (u16_t)(p->tot_len + increment_magnitude);
+
+
+  return 0;
+}
+
+/**
+ * Adjusts the payload pointer to reveal headers in the payload.
+ *
+ * Adjusts the ->payload pointer so that space for a header
+ * appears in the pbuf payload.
+ *
+ * The ->payload, ->tot_len and ->len fields are adjusted.
+ *
+ * @param p pbuf to change the header size.
+ * @param header_size_increment Number of bytes to increment header size which
+ *          increases the size of the pbuf. New space is on the front.
+ *          If header_size_increment is 0, this function does nothing and returns successful.
+ *
+ * PBUF_ROM and PBUF_REF type buffers cannot have their sizes increased, so
+ * the call will fail. A check is made that the increase in header size does
+ * not move the payload pointer in front of the start of the buffer.
+ *
+ * @return non-zero on failure, zero on success.
+ *
+ */
+u8_t
+pbuf_add_header(struct pbuf *p, size_t header_size_increment)
+{
+  return pbuf_add_header_impl(p, header_size_increment, 0);
+}
+
+/**
+ * Same as @ref pbuf_add_header but does not check if 'header_size > 0' is allowed.
+ * This is used internally only, to allow PBUF_REF for RX.
+ */
+u8_t
+pbuf_add_header_force(struct pbuf *p, size_t header_size_increment)
+{
+  return pbuf_add_header_impl(p, header_size_increment, 1);
+}
+
+/**
+ * Adjusts the payload pointer to hide headers in the payload.
+ *
+ * Adjusts the ->payload pointer so that space for a header
+ * disappears in the pbuf payload.
+ *
+ * The ->payload, ->tot_len and ->len fields are adjusted.
+ *
+ * @param p pbuf to change the header size.
+ * @param header_size_decrement Number of bytes to decrement header size which
+ *          decreases the size of the pbuf.
+ *          If header_size_decrement is 0, this function does nothing and returns successful.
+ * @return non-zero on failure, zero on success.
+ *
+ */
+u8_t
+pbuf_remove_header(struct pbuf *p, size_t header_size_decrement)
+{
+  void *payload;
+  u16_t increment_magnitude;
+
+  LWIP_ASSERT("p != NULL", p != NULL);
+  if ((p == NULL) || (header_size_decrement > 0xFFFF)) {
+    return 1;
+  }
+  if (header_size_decrement == 0) {
+    return 0;
+  }
+
+  increment_magnitude = (u16_t)header_size_decrement;
+  /* Check that we aren't going to move off the end of the pbuf */
+  LWIP_ERROR("increment_magnitude <= p->len", (increment_magnitude <= p->len), return 1;);
+
+  /* remember current payload pointer */
+  payload = p->payload;
+  LWIP_UNUSED_ARG(payload); /* only used in LWIP_DEBUGF below */
+
+  /* increase payload pointer (guarded by length check above) */
+  p->payload = (u8_t *)p->payload + header_size_decrement;
+  /* modify pbuf length fields */
+  p->len = (u16_t)(p->len - increment_magnitude);
+  p->tot_len = (u16_t)(p->tot_len - increment_magnitude);
+
+  LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_remove_header: old %p new %p (%"U16_F")\n",
+              (void *)payload, (void *)p->payload, increment_magnitude));
+
+  return 0;
+}
+
+static u8_t
+pbuf_header_impl(struct pbuf *p, s16_t header_size_increment, u8_t force)
+{
+  if (header_size_increment < 0) {
+    return pbuf_remove_header(p, (size_t) - header_size_increment);
+  } else {
+    return pbuf_add_header_impl(p, (size_t)header_size_increment, force);
+  }
+}
+
+/**
+ * Adjusts the payload pointer to hide or reveal headers in the payload.
+ *
+ * Adjusts the ->payload pointer so that space for a header
+ * (dis)appears in the pbuf payload.
+ *
+ * The ->payload, ->tot_len and ->len fields are adjusted.
+ *
+ * @param p pbuf to change the header size.
+ * @param header_size_increment Number of bytes to increment header size which
+ * increases the size of the pbuf. New space is on the front.
+ * (Using a negative value decreases the header size.)
+ * If header_size_increment is 0, this function does nothing and returns successful.
+ *
+ * PBUF_ROM and PBUF_REF type buffers cannot have their sizes increased, so
+ * the call will fail. A check is made that the increase in header size does
+ * not move the payload pointer in front of the start of the buffer.
+ * @return non-zero on failure, zero on success.
+ *
+ */
+u8_t
+pbuf_header(struct pbuf *p, s16_t header_size_increment)
+{
+  return pbuf_header_impl(p, header_size_increment, 0);
+}
+
+/**
+ * Same as pbuf_header but does not check if 'header_size > 0' is allowed.
+ * This is used internally only, to allow PBUF_REF for RX.
+ */
+u8_t
+pbuf_header_force(struct pbuf *p, s16_t header_size_increment)
+{
+  return pbuf_header_impl(p, header_size_increment, 1);
+}
+
+/** Similar to pbuf_header(-size) but de-refs header pbufs for (size >= p->len)
+ *
+ * @param q pbufs to operate on
+ * @param size The number of bytes to remove from the beginning of the pbuf list.
+ *             While size >= p->len, pbufs are freed.
+ *        ATTENTION: this is the opposite direction as @ref pbuf_header, but
+ *                   takes an u16_t not s16_t!
+ * @return the new head pbuf
+ */
+struct pbuf *
+pbuf_free_header(struct pbuf *q, u16_t size)
+{
+  struct pbuf *p = q;
+  u16_t free_left = size;
+  while (free_left && p) {
+    if (free_left >= p->len) {
+      struct pbuf *f = p;
+      free_left = (u16_t)(free_left - p->len);
+      p = p->next;
+      f->next = NULL;
+      pbuf_free(f);
+    } else {
+      pbuf_remove_header(p, free_left);
+      free_left = 0;
+    }
+  }
+  return p;
+}
+
+/**
+ * @ingroup pbuf
+ * Dereference a pbuf chain or queue and deallocate any no-longer-used
+ * pbufs at the head of this chain or queue.
+ *
+ * Decrements the pbuf reference count. If it reaches zero, the pbuf is
+ * deallocated.
+ *
+ * For a pbuf chain, this is repeated for each pbuf in the chain,
+ * up to the first pbuf which has a non-zero reference count after
+ * decrementing. So, when all reference counts are one, the whole
+ * chain is free'd.
+ *
+ * @param p The pbuf (chain) to be dereferenced.
+ *
+ * @return the number of pbufs that were de-allocated
+ * from the head of the chain.
+ *
+ * @note the reference counter of a pbuf equals the number of pointers
+ * that refer to the pbuf (or into the pbuf).
+ *
+ * @internal examples:
+ *
+ * Assuming existing chains a->b->c with the following reference
+ * counts, calling pbuf_free(a) results in:
+ *
+ * 1->2->3 becomes ...1->3
+ * 3->3->3 becomes 2->3->3
+ * 1->1->2 becomes ......1
+ * 2->1->1 becomes 1->1->1
+ * 1->1->1 becomes .......
+ *
+ */
+u8_t
+pbuf_free(struct pbuf *p)
+{
+  u8_t alloc_src;
+  struct pbuf *q;
+  u8_t count;
+
+  if (p == NULL) {
+    LWIP_ASSERT("p != NULL", p != NULL);
+    /* if assertions are disabled, proceed with debug output */
+    LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+                ("pbuf_free(p == NULL) was called.\n"));
+    return 0;
+  }
+  LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free(%p)\n", (void *)p));
+
+  PERF_START;
+
+  count = 0;
+  /* de-allocate all consecutive pbufs from the head of the chain that
+   * obtain a zero reference count after decrementing*/
+  while (p != NULL) {
+    LWIP_PBUF_REF_T ref;
+    SYS_ARCH_DECL_PROTECT(old_level);
+    /* Since decrementing ref cannot be guaranteed to be a single machine operation
+     * we must protect it. We put the new ref into a local variable to prevent
+     * further protection. */
+    SYS_ARCH_PROTECT(old_level);
+    /* all pbufs in a chain are referenced at least once */
+    LWIP_ASSERT("pbuf_free: p->ref > 0", p->ref > 0);
+    /* decrease reference count (number of pointers to pbuf) */
+    ref = --(p->ref);
+    SYS_ARCH_UNPROTECT(old_level);
+    /* this pbuf is no longer referenced to? */
+    if (ref == 0) {
+      /* remember next pbuf in chain for next iteration */
+      q = p->next;
+      LWIP_DEBUGF( PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free: deallocating %p\n", (void *)p));
+      alloc_src = pbuf_get_allocsrc(p);
+#if LWIP_SUPPORT_CUSTOM_PBUF
+      /* is this a custom pbuf? */
+      if ((p->flags & PBUF_FLAG_IS_CUSTOM) != 0) {
+        struct pbuf_custom *pc = (struct pbuf_custom *)p;
+        LWIP_ASSERT("pc->custom_free_function != NULL", pc->custom_free_function != NULL);
+        pc->custom_free_function(p);
+      } else
+#endif /* LWIP_SUPPORT_CUSTOM_PBUF */
+      {
+        /* is this a pbuf from the pool? */
+        if (alloc_src == PBUF_TYPE_ALLOC_SRC_MASK_STD_MEMP_PBUF_POOL) {
+          memp_free(MEMP_PBUF_POOL, p);
+          /* is this a ROM or RAM referencing pbuf? */
+        } else if (alloc_src == PBUF_TYPE_ALLOC_SRC_MASK_STD_MEMP_PBUF) {
+          memp_free(MEMP_PBUF, p);
+          /* type == PBUF_RAM */
+        } else if (alloc_src == PBUF_TYPE_ALLOC_SRC_MASK_STD_HEAP) {
+          mem_free(p);
+        } else {
+          /* @todo: support freeing other types */
+          LWIP_ASSERT("invalid pbuf type", 0);
+        }
+      }
+      count++;
+      /* proceed to next pbuf */
+      p = q;
+      /* p->ref > 0, this pbuf is still referenced to */
+      /* (and so the remaining pbufs in chain as well) */
+    } else {
+      LWIP_DEBUGF( PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free: %p has ref %"U16_F", ending here.\n", (void *)p, (u16_t)ref));
+      /* stop walking through the chain */
+      p = NULL;
+    }
+  }
+  PERF_STOP("pbuf_free");
+  /* return number of de-allocated pbufs */
+  return count;
+}
+
+/**
+ * Count number of pbufs in a chain
+ *
+ * @param p first pbuf of chain
+ * @return the number of pbufs in a chain
+ */
+u16_t
+pbuf_clen(const struct pbuf *p)
+{
+  u16_t len;
+
+  len = 0;
+  while (p != NULL) {
+    ++len;
+    p = p->next;
+  }
+  return len;
+}
+
+/**
+ * @ingroup pbuf
+ * Increment the reference count of the pbuf.
+ *
+ * @param p pbuf to increase reference counter of
+ *
+ */
+void
+pbuf_ref(struct pbuf *p)
+{
+  /* pbuf given? */
+  if (p != NULL) {
+    SYS_ARCH_SET(p->ref, (LWIP_PBUF_REF_T)(p->ref + 1));
+    LWIP_ASSERT("pbuf ref overflow", p->ref > 0);
+  }
+}
+
+/**
+ * @ingroup pbuf
+ * Concatenate two pbufs (each may be a pbuf chain) and take over
+ * the caller's reference of the tail pbuf.
+ *
+ * @note The caller MAY NOT reference the tail pbuf afterwards.
+ * Use pbuf_chain() for that purpose.
+ *
+ * This function explicitly does not check for tot_len overflow to prevent
+ * failing to queue too long pbufs. This can produce invalid pbufs, so
+ * handle with care!
+ *
+ * @see pbuf_chain()
+ */
+void
+pbuf_cat(struct pbuf *h, struct pbuf *t)
+{
+  struct pbuf *p;
+
+  LWIP_ERROR("(h != NULL) && (t != NULL) (programmer violates API)",
+             ((h != NULL) && (t != NULL)), return;);
+
+  /* proceed to last pbuf of chain */
+  for (p = h; p->next != NULL; p = p->next) {
+    /* add total length of second chain to all totals of first chain */
+    p->tot_len = (u16_t)(p->tot_len + t->tot_len);
+  }
+  /* { p is last pbuf of first h chain, p->next == NULL } */
+  LWIP_ASSERT("p->tot_len == p->len (of last pbuf in chain)", p->tot_len == p->len);
+  LWIP_ASSERT("p->next == NULL", p->next == NULL);
+  /* add total length of second chain to last pbuf total of first chain */
+  p->tot_len = (u16_t)(p->tot_len + t->tot_len);
+  /* chain last pbuf of head (p) with first of tail (t) */
+  p->next = t;
+  /* p->next now references t, but the caller will drop its reference to t,
+   * so netto there is no change to the reference count of t.
+   */
+}
+
+/**
+ * @ingroup pbuf
+ * Chain two pbufs (or pbuf chains) together.
+ *
+ * The caller MUST call pbuf_free(t) once it has stopped
+ * using it. Use pbuf_cat() instead if you no longer use t.
+ *
+ * @param h head pbuf (chain)
+ * @param t tail pbuf (chain)
+ * @note The pbufs MUST belong to the same packet.
+ * @note MAY NOT be called on a packet queue.
+ *
+ * The ->tot_len fields of all pbufs of the head chain are adjusted.
+ * The ->next field of the last pbuf of the head chain is adjusted.
+ * The ->ref field of the first pbuf of the tail chain is adjusted.
+ *
+ */
+void
+pbuf_chain(struct pbuf *h, struct pbuf *t)
+{
+  pbuf_cat(h, t);
+  /* t is now referenced by h */
+  pbuf_ref(t);
+  LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_chain: %p references %p\n", (void *)h, (void *)t));
+}
+
+/**
+ * Dechains the first pbuf from its succeeding pbufs in the chain.
+ *
+ * Makes p->tot_len field equal to p->len.
+ * @param p pbuf to dechain
+ * @return remainder of the pbuf chain, or NULL if it was de-allocated.
+ * @note May not be called on a packet queue.
+ */
+struct pbuf *
+pbuf_dechain(struct pbuf *p)
+{
+  struct pbuf *q;
+  u8_t tail_gone = 1;
+  /* tail */
+  q = p->next;
+  /* pbuf has successor in chain? */
+  if (q != NULL) {
+    /* assert tot_len invariant: (p->tot_len == p->len + (p->next? p->next->tot_len: 0) */
+    LWIP_ASSERT("p->tot_len == p->len + q->tot_len", q->tot_len == p->tot_len - p->len);
+    /* enforce invariant if assertion is disabled */
+    q->tot_len = (u16_t)(p->tot_len - p->len);
+    /* decouple pbuf from remainder */
+    p->next = NULL;
+    /* total length of pbuf p is its own length only */
+    p->tot_len = p->len;
+    /* q is no longer referenced by p, free it */
+    LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_dechain: unreferencing %p\n", (void *)q));
+    tail_gone = pbuf_free(q);
+    if (tail_gone > 0) {
+      LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE,
+                  ("pbuf_dechain: deallocated %p (as it is no longer referenced)\n", (void *)q));
+    }
+    /* return remaining tail or NULL if deallocated */
+  }
+  /* assert tot_len invariant: (p->tot_len == p->len + (p->next? p->next->tot_len: 0) */
+  LWIP_ASSERT("p->tot_len == p->len", p->tot_len == p->len);
+  return ((tail_gone > 0) ? NULL : q);
+}
+
+/**
+ * @ingroup pbuf
+ * Copy the contents of one packet buffer into another.
+ *
+ * @note Only one packet is copied, no packet queue!
+ *
+ * @param p_to pbuf destination of the copy
+ * @param p_from pbuf source of the copy
+ *
+ * @return ERR_OK if pbuf was copied
+ *         ERR_ARG if one of the pbufs is NULL or p_to is not big
+ *                 enough to hold p_from
+ *         ERR_VAL if any of the pbufs are part of a queue
+ */
+err_t
+pbuf_copy(struct pbuf *p_to, const struct pbuf *p_from)
+{
+  LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_copy(%p, %p)\n",
+              (const void *)p_to, (const void *)p_from));
+
+  LWIP_ERROR("pbuf_copy: invalid source", p_from != NULL, return ERR_ARG;);
+  return pbuf_copy_partial_pbuf(p_to, p_from, p_from->tot_len, 0);
+}
+
+/**
+ * @ingroup pbuf
+ * Copy part or all of one packet buffer into another, to a specified offset.
+ *
+ * @note Only data in one packet is copied, no packet queue!
+ * @note Argument order is shared with pbuf_copy, but different than pbuf_copy_partial.
+ *
+ * @param p_to pbuf destination of the copy
+ * @param p_from pbuf source of the copy
+ * @param copy_len number of bytes to copy
+ * @param offset offset in destination pbuf where to copy to
+ *
+ * @return ERR_OK if copy_len bytes were copied
+ *         ERR_ARG if one of the pbufs is NULL or p_from is shorter than copy_len
+ *                 or p_to is not big enough to hold copy_len at offset
+ *         ERR_VAL if any of the pbufs are part of a queue
+ */
+err_t
+pbuf_copy_partial_pbuf(struct pbuf *p_to, const struct pbuf *p_from, u16_t copy_len, u16_t offset)
+{
+  size_t offset_to = offset, offset_from = 0, len;
+
+  LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_copy_partial_pbuf(%p, %p, %"U16_F", %"U16_F")\n",
+              (const void *)p_to, (const void *)p_from, copy_len, offset));
+
+  /* is the copy_len in range? */
+  LWIP_ERROR("pbuf_copy_partial_pbuf: copy_len bigger than source", ((p_from != NULL) &&
+             (p_from->tot_len >= copy_len)), return ERR_ARG;);
+  /* is the target big enough to hold the source? */
+  LWIP_ERROR("pbuf_copy_partial_pbuf: target not big enough", ((p_to != NULL) &&
+             (p_to->tot_len >= (offset + copy_len))), return ERR_ARG;);
+
+  /* iterate through pbuf chain */
+  do {
+    /* copy one part of the original chain */
+    if ((p_to->len - offset_to) >= (p_from->len - offset_from)) {
+      /* complete current p_from fits into current p_to */
+      len = p_from->len - offset_from;
+    } else {
+      /* current p_from does not fit into current p_to */
+      len = p_to->len - offset_to;
+    }
+    len = LWIP_MIN(copy_len, len);
+    MEMCPY((u8_t *)p_to->payload + offset_to, (u8_t *)p_from->payload + offset_from, len);
+    offset_to += len;
+    offset_from += len;
+    copy_len = (u16_t)(copy_len - len);
+    LWIP_ASSERT("offset_to <= p_to->len", offset_to <= p_to->len);
+    LWIP_ASSERT("offset_from <= p_from->len", offset_from <= p_from->len);
+    if (offset_from >= p_from->len) {
+      /* on to next p_from (if any) */
+      offset_from = 0;
+      p_from = p_from->next;
+      LWIP_ERROR("p_from != NULL", (p_from != NULL) || (copy_len == 0), return ERR_ARG;);
+    }
+    if (offset_to == p_to->len) {
+      /* on to next p_to (if any) */
+      offset_to = 0;
+      p_to = p_to->next;
+      LWIP_ERROR("p_to != NULL", (p_to != NULL) || (copy_len == 0), return ERR_ARG;);
+    }
+
+    if ((p_from != NULL) && (p_from->len == p_from->tot_len)) {
+      /* don't copy more than one packet! */
+      LWIP_ERROR("pbuf_copy_partial_pbuf() does not allow packet queues!",
+                 (p_from->next == NULL), return ERR_VAL;);
+    }
+    if ((p_to != NULL) && (p_to->len == p_to->tot_len)) {
+      /* don't copy more than one packet! */
+      LWIP_ERROR("pbuf_copy_partial_pbuf() does not allow packet queues!",
+                 (p_to->next == NULL), return ERR_VAL;);
+    }
+  } while (copy_len);
+  LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_copy_partial_pbuf: copy complete.\n"));
+  return ERR_OK;
+}
+
+/**
+ * @ingroup pbuf
+ * Copy (part of) the contents of a packet buffer
+ * to an application supplied buffer.
+ *
+ * @param buf the pbuf from which to copy data
+ * @param dataptr the application supplied buffer
+ * @param len length of data to copy (dataptr must be big enough). No more
+ * than buf->tot_len will be copied, irrespective of len
+ * @param offset offset into the packet buffer from where to begin copying len bytes
+ * @return the number of bytes copied, or 0 on failure
+ */
+u16_t
+pbuf_copy_partial(const struct pbuf *buf, void *dataptr, u16_t len, u16_t offset)
+{
+  const struct pbuf *p;
+  u16_t left = 0;
+  u16_t buf_copy_len;
+  u16_t copied_total = 0;
+
+  LWIP_ERROR("pbuf_copy_partial: invalid buf", (buf != NULL), return 0;);
+  LWIP_ERROR("pbuf_copy_partial: invalid dataptr", (dataptr != NULL), return 0;);
+
+  /* Note some systems use byte copy if dataptr or one of the pbuf payload pointers are unaligned. */
+  for (p = buf; len != 0 && p != NULL; p = p->next) {
+    if ((offset != 0) && (offset >= p->len)) {
+      /* don't copy from this buffer -> on to the next */
+      offset = (u16_t)(offset - p->len);
+    } else {
+      /* copy from this buffer. maybe only partially. */
+      buf_copy_len = (u16_t)(p->len - offset);
+      if (buf_copy_len > len) {
+        buf_copy_len = len;
+      }
+      /* copy the necessary parts of the buffer */
+      MEMCPY(&((char *)dataptr)[left], &((char *)p->payload)[offset], buf_copy_len);
+      copied_total = (u16_t)(copied_total + buf_copy_len);
+      left = (u16_t)(left + buf_copy_len);
+      len = (u16_t)(len - buf_copy_len);
+      offset = 0;
+    }
+  }
+  return copied_total;
+}
+
+/**
+ * @ingroup pbuf
+ * Get part of a pbuf's payload as contiguous memory. The returned memory is
+ * either a pointer into the pbuf's payload or, if split over multiple pbufs,
+ * a copy into the user-supplied buffer.
+ *
+ * @param p the pbuf from which to copy data
+ * @param buffer the application supplied buffer
+ * @param bufsize size of the application supplied buffer
+ * @param len length of data to copy (dataptr must be big enough). No more
+ * than buf->tot_len will be copied, irrespective of len
+ * @param offset offset into the packet buffer from where to begin copying len bytes
+ * @return the number of bytes copied, or 0 on failure
+ */
+void *
+pbuf_get_contiguous(const struct pbuf *p, void *buffer, size_t bufsize, u16_t len, u16_t offset)
+{
+  const struct pbuf *q;
+  u16_t out_offset;
+
+  LWIP_ERROR("pbuf_get_contiguous: invalid buf", (p != NULL), return NULL;);
+  LWIP_ERROR("pbuf_get_contiguous: invalid dataptr", (buffer != NULL), return NULL;);
+  LWIP_ERROR("pbuf_get_contiguous: invalid dataptr", (bufsize >= len), return NULL;);
+
+  q = pbuf_skip_const(p, offset, &out_offset);
+  if (q != NULL) {
+    if (q->len >= (out_offset + len)) {
+      /* all data in this pbuf, return zero-copy */
+      return (u8_t *)q->payload + out_offset;
+    }
+    /* need to copy */
+    if (pbuf_copy_partial(q, buffer, len, out_offset) != len) {
+      /* copying failed: pbuf is too short */
+      return NULL;
+    }
+    return buffer;
+  }
+  /* pbuf is too short (offset does not fit in) */
+  return NULL;
+}
+
+#if LWIP_TCP && TCP_QUEUE_OOSEQ && LWIP_WND_SCALE
+/**
+ * This method modifies a 'pbuf chain', so that its total length is
+ * smaller than 64K. The remainder of the original pbuf chain is stored
+ * in *rest.
+ * This function never creates new pbufs, but splits an existing chain
+ * in two parts. The tot_len of the modified packet queue will likely be
+ * smaller than 64K.
+ * 'packet queues' are not supported by this function.
+ *
+ * @param p the pbuf queue to be split
+ * @param rest pointer to store the remainder (after the first 64K)
+ */
+void pbuf_split_64k(struct pbuf *p, struct pbuf **rest)
+{
+  *rest = NULL;
+  if ((p != NULL) && (p->next != NULL)) {
+    u16_t tot_len_front = p->len;
+    struct pbuf *i = p;
+    struct pbuf *r = p->next;
+
+    /* continue until the total length (summed up as u16_t) overflows */
+    while ((r != NULL) && ((u16_t)(tot_len_front + r->len) >= tot_len_front)) {
+      tot_len_front = (u16_t)(tot_len_front + r->len);
+      i = r;
+      r = r->next;
+    }
+    /* i now points to last packet of the first segment. Set next
+       pointer to NULL */
+    i->next = NULL;
+
+    if (r != NULL) {
+      /* Update the tot_len field in the first part */
+      for (i = p; i != NULL; i = i->next) {
+        i->tot_len = (u16_t)(i->tot_len - r->tot_len);
+        LWIP_ASSERT("tot_len/len mismatch in last pbuf",
+                    (i->next != NULL) || (i->tot_len == i->len));
+      }
+      if (p->flags & PBUF_FLAG_TCP_FIN) {
+        r->flags |= PBUF_FLAG_TCP_FIN;
+      }
+
+      /* tot_len field in rest does not need modifications */
+      /* reference counters do not need modifications */
+      *rest = r;
+    }
+  }
+}
+#endif /* LWIP_TCP && TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */
+
+/* Actual implementation of pbuf_skip() but returning const pointer... */
+static const struct pbuf *
+pbuf_skip_const(const struct pbuf *in, u16_t in_offset, u16_t *out_offset)
+{
+  u16_t offset_left = in_offset;
+  const struct pbuf *q = in;
+
+  /* get the correct pbuf */
+  while ((q != NULL) && (q->len <= offset_left)) {
+    offset_left = (u16_t)(offset_left - q->len);
+    q = q->next;
+  }
+  if (out_offset != NULL) {
+    *out_offset = offset_left;
+  }
+  return q;
+}
+
+/**
+ * @ingroup pbuf
+ * Skip a number of bytes at the start of a pbuf
+ *
+ * @param in input pbuf
+ * @param in_offset offset to skip
+ * @param out_offset resulting offset in the returned pbuf
+ * @return the pbuf in the queue where the offset is or NULL when the offset is too high
+ */
+struct pbuf *
+pbuf_skip(struct pbuf *in, u16_t in_offset, u16_t *out_offset)
+{
+  const struct pbuf *out = pbuf_skip_const(in, in_offset, out_offset);
+  return LWIP_CONST_CAST(struct pbuf *, out);
+}
+
+/**
+ * @ingroup pbuf
+ * Copy application supplied data into a pbuf.
+ * This function can only be used to copy the equivalent of buf->tot_len data.
+ *
+ * @param buf pbuf to fill with data
+ * @param dataptr application supplied data buffer
+ * @param len length of the application supplied data buffer
+ *
+ * @return ERR_OK if successful, ERR_MEM if the pbuf is not big enough
+ */
+err_t
+pbuf_take(struct pbuf *buf, const void *dataptr, u16_t len)
+{
+  struct pbuf *p;
+  size_t buf_copy_len;
+  size_t total_copy_len = len;
+  size_t copied_total = 0;
+
+  LWIP_ERROR("pbuf_take: invalid buf", (buf != NULL), return ERR_ARG;);
+  LWIP_ERROR("pbuf_take: invalid dataptr", (dataptr != NULL), return ERR_ARG;);
+  LWIP_ERROR("pbuf_take: buf not large enough", (buf->tot_len >= len), return ERR_MEM;);
+
+  if ((buf == NULL) || (dataptr == NULL) || (buf->tot_len < len)) {
+    return ERR_ARG;
+  }
+
+  /* Note some systems use byte copy if dataptr or one of the pbuf payload pointers are unaligned. */
+  for (p = buf; total_copy_len != 0; p = p->next) {
+    LWIP_ASSERT("pbuf_take: invalid pbuf", p != NULL);
+    buf_copy_len = total_copy_len;
+    if (buf_copy_len > p->len) {
+      /* this pbuf cannot hold all remaining data */
+      buf_copy_len = p->len;
+    }
+    /* copy the necessary parts of the buffer */
+    MEMCPY(p->payload, &((const char *)dataptr)[copied_total], buf_copy_len);
+    total_copy_len -= buf_copy_len;
+    copied_total += buf_copy_len;
+  }
+  LWIP_ASSERT("did not copy all data", total_copy_len == 0 && copied_total == len);
+  return ERR_OK;
+}
+
+/**
+ * @ingroup pbuf
+ * Same as pbuf_take() but puts data at an offset
+ *
+ * @param buf pbuf to fill with data
+ * @param dataptr application supplied data buffer
+ * @param len length of the application supplied data buffer
+ * @param offset offset in pbuf where to copy dataptr to
+ *
+ * @return ERR_OK if successful, ERR_MEM if the pbuf is not big enough
+ */
+err_t
+pbuf_take_at(struct pbuf *buf, const void *dataptr, u16_t len, u16_t offset)
+{
+  u16_t target_offset;
+  struct pbuf *q = pbuf_skip(buf, offset, &target_offset);
+
+  /* return requested data if pbuf is OK */
+  if ((q != NULL) && (q->tot_len >= target_offset + len)) {
+    u16_t remaining_len = len;
+    const u8_t *src_ptr = (const u8_t *)dataptr;
+    /* copy the part that goes into the first pbuf */
+    u16_t first_copy_len;
+    LWIP_ASSERT("check pbuf_skip result", target_offset < q->len);
+    first_copy_len = (u16_t)LWIP_MIN(q->len - target_offset, len);
+    MEMCPY(((u8_t *)q->payload) + target_offset, dataptr, first_copy_len);
+    remaining_len = (u16_t)(remaining_len - first_copy_len);
+    src_ptr += first_copy_len;
+    if (remaining_len > 0) {
+      return pbuf_take(q->next, src_ptr, remaining_len);
+    }
+    return ERR_OK;
+  }
+  return ERR_MEM;
+}
+
+/**
+ * @ingroup pbuf
+ * Creates a single pbuf out of a queue of pbufs.
+ *
+ * @remark: Either the source pbuf 'p' is freed by this function or the original
+ *          pbuf 'p' is returned, therefore the caller has to check the result!
+ *
+ * @param p the source pbuf
+ * @param layer pbuf_layer of the new pbuf
+ *
+ * @return a new, single pbuf (p->next is NULL)
+ *         or the old pbuf if allocation fails
+ */
+struct pbuf *
+pbuf_coalesce(struct pbuf *p, pbuf_layer layer)
+{
+  struct pbuf *q;
+  if (p->next == NULL) {
+    return p;
+  }
+  q = pbuf_clone(layer, PBUF_RAM, p);
+  if (q == NULL) {
+    /* @todo: what do we do now? */
+    return p;
+  }
+  pbuf_free(p);
+  return q;
+}
+
+/**
+ * @ingroup pbuf
+ * Allocates a new pbuf of same length (via pbuf_alloc()) and copies the source
+ * pbuf into this new pbuf (using pbuf_copy()).
+ *
+ * @param layer pbuf_layer of the new pbuf
+ * @param type this parameter decides how and where the pbuf should be allocated
+ *             (@see pbuf_alloc())
+ * @param p the source pbuf
+ *
+ * @return a new pbuf or NULL if allocation fails
+ */
+struct pbuf *
+pbuf_clone(pbuf_layer layer, pbuf_type type, struct pbuf *p)
+{
+  struct pbuf *q;
+  err_t err;
+  q = pbuf_alloc(layer, p->tot_len, type);
+  if (q == NULL) {
+    return NULL;
+  }
+  err = pbuf_copy(q, p);
+  LWIP_UNUSED_ARG(err); /* in case of LWIP_NOASSERT */
+  LWIP_ASSERT("pbuf_copy failed", err == ERR_OK);
+  return q;
+}
+
+#if LWIP_CHECKSUM_ON_COPY
+/**
+ * Copies data into a single pbuf (*not* into a pbuf queue!) and updates
+ * the checksum while copying
+ *
+ * @param p the pbuf to copy data into
+ * @param start_offset offset of p->payload where to copy the data to
+ * @param dataptr data to copy into the pbuf
+ * @param len length of data to copy into the pbuf
+ * @param chksum pointer to the checksum which is updated
+ * @return ERR_OK if successful, another error if the data does not fit
+ *         within the (first) pbuf (no pbuf queues!)
+ */
+err_t
+pbuf_fill_chksum(struct pbuf *p, u16_t start_offset, const void *dataptr,
+                 u16_t len, u16_t *chksum)
+{
+  u32_t acc;
+  u16_t copy_chksum;
+  char *dst_ptr;
+  LWIP_ASSERT("p != NULL", p != NULL);
+  LWIP_ASSERT("dataptr != NULL", dataptr != NULL);
+  LWIP_ASSERT("chksum != NULL", chksum != NULL);
+  LWIP_ASSERT("len != 0", len != 0);
+
+  if ((start_offset >= p->len) || (start_offset + len > p->len)) {
+    return ERR_ARG;
+  }
+
+  dst_ptr = ((char *)p->payload) + start_offset;
+  copy_chksum = LWIP_CHKSUM_COPY(dst_ptr, dataptr, len);
+  if ((start_offset & 1) != 0) {
+    copy_chksum = SWAP_BYTES_IN_WORD(copy_chksum);
+  }
+  acc = *chksum;
+  acc += copy_chksum;
+  *chksum = FOLD_U32T(acc);
+  return ERR_OK;
+}
+#endif /* LWIP_CHECKSUM_ON_COPY */
+
+/**
+ * @ingroup pbuf
+ * Get one byte from the specified position in a pbuf
+ * WARNING: returns zero for offset >= p->tot_len
+ *
+ * @param p pbuf to parse
+ * @param offset offset into p of the byte to return
+ * @return byte at an offset into p OR ZERO IF 'offset' >= p->tot_len
+ */
+u8_t
+pbuf_get_at(const struct pbuf *p, u16_t offset)
+{
+  int ret = pbuf_try_get_at(p, offset);
+  if (ret >= 0) {
+    return (u8_t)ret;
+  }
+  return 0;
+}
+
+/**
+ * @ingroup pbuf
+ * Get one byte from the specified position in a pbuf
+ *
+ * @param p pbuf to parse
+ * @param offset offset into p of the byte to return
+ * @return byte at an offset into p [0..0xFF] OR negative if 'offset' >= p->tot_len
+ */
+int
+pbuf_try_get_at(const struct pbuf *p, u16_t offset)
+{
+  u16_t q_idx;
+  const struct pbuf *q = pbuf_skip_const(p, offset, &q_idx);
+
+  /* return requested data if pbuf is OK */
+  if ((q != NULL) && (q->len > q_idx)) {
+    return ((u8_t *)q->payload)[q_idx];
+  }
+  return -1;
+}
+
+/**
+ * @ingroup pbuf
+ * Put one byte to the specified position in a pbuf
+ * WARNING: silently ignores offset >= p->tot_len
+ *
+ * @param p pbuf to fill
+ * @param offset offset into p of the byte to write
+ * @param data byte to write at an offset into p
+ */
+void
+pbuf_put_at(struct pbuf *p, u16_t offset, u8_t data)
+{
+  u16_t q_idx;
+  struct pbuf *q = pbuf_skip(p, offset, &q_idx);
+
+  /* write requested data if pbuf is OK */
+  if ((q != NULL) && (q->len > q_idx)) {
+    ((u8_t *)q->payload)[q_idx] = data;
+  }
+}
+
+/**
+ * @ingroup pbuf
+ * Compare pbuf contents at specified offset with memory s2, both of length n
+ *
+ * @param p pbuf to compare
+ * @param offset offset into p at which to start comparing
+ * @param s2 buffer to compare
+ * @param n length of buffer to compare
+ * @return zero if equal, nonzero otherwise
+ *         (0xffff if p is too short, diffoffset+1 otherwise)
+ */
+u16_t
+pbuf_memcmp(const struct pbuf *p, u16_t offset, const void *s2, u16_t n)
+{
+  u16_t start = offset;
+  const struct pbuf *q = p;
+  u16_t i;
+
+  /* pbuf long enough to perform check? */
+  if (p->tot_len < (offset + n)) {
+    return 0xffff;
+  }
+
+  /* get the correct pbuf from chain. We know it succeeds because of p->tot_len check above. */
+  while ((q != NULL) && (q->len <= start)) {
+    start = (u16_t)(start - q->len);
+    q = q->next;
+  }
+
+  /* return requested data if pbuf is OK */
+  for (i = 0; i < n; i++) {
+    /* We know pbuf_get_at() succeeds because of p->tot_len check above. */
+    u8_t a = pbuf_get_at(q, (u16_t)(start + i));
+    u8_t b = ((const u8_t *)s2)[i];
+    if (a != b) {
+      return (u16_t)LWIP_MIN(i + 1, 0xFFFF);
+    }
+  }
+  return 0;
+}
+
+/**
+ * @ingroup pbuf
+ * Find occurrence of mem (with length mem_len) in pbuf p, starting at offset
+ * start_offset.
+ *
+ * @param p pbuf to search, maximum length is 0xFFFE since 0xFFFF is used as
+ *        return value 'not found'
+ * @param mem search for the contents of this buffer
+ * @param mem_len length of 'mem'
+ * @param start_offset offset into p at which to start searching
+ * @return 0xFFFF if substr was not found in p or the index where it was found
+ */
+u16_t
+pbuf_memfind(const struct pbuf *p, const void *mem, u16_t mem_len, u16_t start_offset)
+{
+  u16_t i;
+  u16_t max_cmp_start = (u16_t)(p->tot_len - mem_len);
+  if (p->tot_len >= mem_len + start_offset) {
+    for (i = start_offset; i <= max_cmp_start; i++) {
+      u16_t plus = pbuf_memcmp(p, i, mem, mem_len);
+      if (plus == 0) {
+        return i;
+      }
+    }
+  }
+  return 0xFFFF;
+}
+
+/**
+ * Find occurrence of substr with length substr_len in pbuf p, start at offset
+ * start_offset
+ * WARNING: in contrast to strstr(), this one does not stop at the first \0 in
+ * the pbuf/source string!
+ *
+ * @param p pbuf to search, maximum length is 0xFFFE since 0xFFFF is used as
+ *        return value 'not found'
+ * @param substr string to search for in p, maximum length is 0xFFFE
+ * @return 0xFFFF if substr was not found in p or the index where it was found
+ */
+u16_t
+pbuf_strstr(const struct pbuf *p, const char *substr)
+{
+  size_t substr_len;
+  if ((substr == NULL) || (substr[0] == 0) || (p->tot_len == 0xFFFF)) {
+    return 0xFFFF;
+  }
+  substr_len = strlen(substr);
+  if (substr_len >= 0xFFFF) {
+    return 0xFFFF;
+  }
+  return pbuf_memfind(p, substr, (u16_t)substr_len, 0);
+}
diff --git a/lib/lwip/core/raw.c b/lib/lwip/core/raw.c
new file mode 100644
index 0000000000..d85aaec051
--- /dev/null
+++ b/lib/lwip/core/raw.c
@@ -0,0 +1,673 @@
+/**
+ * @file
+ * Implementation of raw protocol PCBs for low-level handling of
+ * different types of protocols besides (or overriding) those
+ * already available in lwIP.<br>
+ * See also @ref raw_raw
+ *
+ * @defgroup raw_raw RAW
+ * @ingroup callbackstyle_api
+ * Implementation of raw protocol PCBs for low-level handling of
+ * different types of protocols besides (or overriding) those
+ * already available in lwIP.<br>
+ * @see @ref api
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_RAW /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/def.h"
+#include "lwip/memp.h"
+#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+#include "lwip/raw.h"
+#include "lwip/priv/raw_priv.h"
+#include "lwip/stats.h"
+#include "lwip/ip6.h"
+#include "lwip/ip6_addr.h"
+#include "lwip/inet_chksum.h"
+
+#include <string.h>
+
+/** The list of RAW PCBs */
+static struct raw_pcb *raw_pcbs;
+
+static u8_t
+raw_input_local_match(struct raw_pcb *pcb, u8_t broadcast)
+{
+  LWIP_UNUSED_ARG(broadcast); /* in IPv6 only case */
+
+  /* check if PCB is bound to specific netif */
+  if ((pcb->netif_idx != NETIF_NO_INDEX) &&
+      (pcb->netif_idx != netif_get_index(ip_data.current_input_netif))) {
+    return 0;
+  }
+
+#if LWIP_IPV4 && LWIP_IPV6
+  /* Dual-stack: PCBs listening to any IP type also listen to any IP address */
+  if (IP_IS_ANY_TYPE_VAL(pcb->local_ip)) {
+#if IP_SOF_BROADCAST_RECV
+    if ((broadcast != 0) && !ip_get_option(pcb, SOF_BROADCAST)) {
+      return 0;
+    }
+#endif /* IP_SOF_BROADCAST_RECV */
+    return 1;
+  }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
+  /* Only need to check PCB if incoming IP version matches PCB IP version */
+  if (IP_ADDR_PCB_VERSION_MATCH_EXACT(pcb, ip_current_dest_addr())) {
+#if LWIP_IPV4
+    /* Special case: IPv4 broadcast: receive all broadcasts
+     * Note: broadcast variable can only be 1 if it is an IPv4 broadcast */
+    if (broadcast != 0) {
+#if IP_SOF_BROADCAST_RECV
+      if (ip_get_option(pcb, SOF_BROADCAST))
+#endif /* IP_SOF_BROADCAST_RECV */
+      {
+        if (ip4_addr_isany(ip_2_ip4(&pcb->local_ip))) {
+          return 1;
+        }
+      }
+    } else
+#endif /* LWIP_IPV4 */
+      /* Handle IPv4 and IPv6: catch all or exact match */
+      if (ip_addr_isany(&pcb->local_ip) ||
+          ip_addr_eq(&pcb->local_ip, ip_current_dest_addr())) {
+        return 1;
+      }
+  }
+
+  return 0;
+}
+
+/**
+ * Determine if in incoming IP packet is covered by a RAW PCB
+ * and if so, pass it to a user-provided receive callback function.
+ *
+ * Given an incoming IP datagram (as a chain of pbufs) this function
+ * finds a corresponding RAW PCB and calls the corresponding receive
+ * callback function.
+ *
+ * @param p pbuf to be demultiplexed to a RAW PCB.
+ * @param inp network interface on which the datagram was received.
+ * @return - 1 if the packet has been eaten by a RAW PCB receive
+ *           callback function. The caller MAY NOT not reference the
+ *           packet any longer, and MAY NOT call pbuf_free().
+ * @return - 0 if packet is not eaten (pbuf is still referenced by the
+ *           caller).
+ *
+ */
+raw_input_state_t
+raw_input(struct pbuf *p, struct netif *inp)
+{
+  struct raw_pcb *pcb, *prev;
+  s16_t proto;
+  raw_input_state_t ret = RAW_INPUT_NONE;
+  u8_t broadcast = ip_addr_isbroadcast(ip_current_dest_addr(), ip_current_netif());
+
+  LWIP_UNUSED_ARG(inp);
+
+#if LWIP_IPV6
+#if LWIP_IPV4
+  if (IP_HDR_GET_VERSION(p->payload) == 6)
+#endif /* LWIP_IPV4 */
+  {
+    struct ip6_hdr *ip6hdr = (struct ip6_hdr *)p->payload;
+    proto = IP6H_NEXTH(ip6hdr);
+  }
+#if LWIP_IPV4
+  else
+#endif /* LWIP_IPV4 */
+#endif /* LWIP_IPV6 */
+#if LWIP_IPV4
+  {
+    proto = IPH_PROTO((struct ip_hdr *)p->payload);
+  }
+#endif /* LWIP_IPV4 */
+
+  prev = NULL;
+  pcb = raw_pcbs;
+  /* loop through all raw pcbs until the packet is eaten by one */
+  /* this allows multiple pcbs to match against the packet by design */
+  while (pcb != NULL) {
+    if ((pcb->protocol == proto) && raw_input_local_match(pcb, broadcast) &&
+        (((pcb->flags & RAW_FLAGS_CONNECTED) == 0) ||
+         ip_addr_eq(&pcb->remote_ip, ip_current_src_addr()))) {
+      /* receive callback function available? */
+      if (pcb->recv != NULL) {
+        u8_t eaten;
+#ifndef LWIP_NOASSERT
+        void *old_payload = p->payload;
+#endif
+        ret = RAW_INPUT_DELIVERED;
+        /* the receive callback function did not eat the packet? */
+        eaten = pcb->recv(pcb->recv_arg, pcb, p, ip_current_src_addr());
+        if (eaten != 0) {
+          /* receive function ate the packet */
+          p = NULL;
+          if (prev != NULL) {
+            /* move the pcb to the front of raw_pcbs so that is
+               found faster next time */
+            prev->next = pcb->next;
+            pcb->next = raw_pcbs;
+            raw_pcbs = pcb;
+          }
+          return RAW_INPUT_EATEN;
+        } else {
+          /* sanity-check that the receive callback did not alter the pbuf */
+          LWIP_ASSERT("raw pcb recv callback altered pbuf payload pointer without eating packet",
+                      p->payload == old_payload);
+        }
+      }
+      /* no receive callback function was set for this raw PCB */
+    }
+    /* drop the packet */
+    prev = pcb;
+    pcb = pcb->next;
+  }
+  return ret;
+}
+
+/**
+ * @ingroup raw_raw
+ * Bind a RAW PCB.
+ *
+ * @param pcb RAW PCB to be bound with a local address ipaddr.
+ * @param ipaddr local IP address to bind with. Use IP4_ADDR_ANY to
+ * bind to all local interfaces.
+ *
+ * @return lwIP error code.
+ * - ERR_OK. Successful. No error occurred.
+ * - ERR_USE. The specified IP address is already bound to by
+ * another RAW PCB.
+ *
+ * @see raw_disconnect()
+ */
+err_t
+raw_bind(struct raw_pcb *pcb, const ip_addr_t *ipaddr)
+{
+  LWIP_ASSERT_CORE_LOCKED();
+  if ((pcb == NULL) || (ipaddr == NULL)) {
+    return ERR_VAL;
+  }
+  ip_addr_set_ipaddr(&pcb->local_ip, ipaddr);
+#if LWIP_IPV6 && LWIP_IPV6_SCOPES
+  /* If the given IP address should have a zone but doesn't, assign one now.
+   * This is legacy support: scope-aware callers should always provide properly
+   * zoned source addresses. */
+  if (IP_IS_V6(&pcb->local_ip) &&
+      ip6_addr_lacks_zone(ip_2_ip6(&pcb->local_ip), IP6_UNKNOWN)) {
+    ip6_addr_select_zone(ip_2_ip6(&pcb->local_ip), ip_2_ip6(&pcb->local_ip));
+  }
+#endif /* LWIP_IPV6 && LWIP_IPV6_SCOPES */
+  return ERR_OK;
+}
+
+/**
+ * @ingroup raw_raw
+ * Bind an RAW PCB to a specific netif.
+ * After calling this function, all packets received via this PCB
+ * are guaranteed to have come in via the specified netif, and all
+ * outgoing packets will go out via the specified netif.
+ *
+ * @param pcb RAW PCB to be bound with netif.
+ * @param netif netif to bind to. Can be NULL.
+ *
+ * @see raw_disconnect()
+ */
+void
+raw_bind_netif(struct raw_pcb *pcb, const struct netif *netif)
+{
+  LWIP_ASSERT_CORE_LOCKED();
+  if (netif != NULL) {
+    pcb->netif_idx = netif_get_index(netif);
+  } else {
+    pcb->netif_idx = NETIF_NO_INDEX;
+  }
+}
+
+/**
+ * @ingroup raw_raw
+ * Connect an RAW PCB. This function is required by upper layers
+ * of lwip. Using the raw api you could use raw_sendto() instead
+ *
+ * This will associate the RAW PCB with the remote address.
+ *
+ * @param pcb RAW PCB to be connected with remote address ipaddr and port.
+ * @param ipaddr remote IP address to connect with.
+ *
+ * @return lwIP error code
+ *
+ * @see raw_disconnect() and raw_sendto()
+ */
+err_t
+raw_connect(struct raw_pcb *pcb, const ip_addr_t *ipaddr)
+{
+  LWIP_ASSERT_CORE_LOCKED();
+  if ((pcb == NULL) || (ipaddr == NULL)) {
+    return ERR_VAL;
+  }
+  ip_addr_set_ipaddr(&pcb->remote_ip, ipaddr);
+#if LWIP_IPV6 && LWIP_IPV6_SCOPES
+  /* If the given IP address should have a zone but doesn't, assign one now,
+   * using the bound address to make a more informed decision when possible. */
+  if (IP_IS_V6(&pcb->remote_ip) &&
+      ip6_addr_lacks_zone(ip_2_ip6(&pcb->remote_ip), IP6_UNKNOWN)) {
+    ip6_addr_select_zone(ip_2_ip6(&pcb->remote_ip), ip_2_ip6(&pcb->local_ip));
+  }
+#endif /* LWIP_IPV6 && LWIP_IPV6_SCOPES */
+  raw_set_flags(pcb, RAW_FLAGS_CONNECTED);
+  return ERR_OK;
+}
+
+/**
+ * @ingroup raw_raw
+ * Disconnect a RAW PCB.
+ *
+ * @param pcb the raw pcb to disconnect.
+ */
+void
+raw_disconnect(struct raw_pcb *pcb)
+{
+  LWIP_ASSERT_CORE_LOCKED();
+  /* reset remote address association */
+#if LWIP_IPV4 && LWIP_IPV6
+  if (IP_IS_ANY_TYPE_VAL(pcb->local_ip)) {
+    ip_addr_copy(pcb->remote_ip, *IP_ANY_TYPE);
+  } else {
+#endif
+    ip_addr_set_any(IP_IS_V6_VAL(pcb->remote_ip), &pcb->remote_ip);
+#if LWIP_IPV4 && LWIP_IPV6
+  }
+#endif
+  pcb->netif_idx = NETIF_NO_INDEX;
+  /* mark PCB as unconnected */
+  raw_clear_flags(pcb, RAW_FLAGS_CONNECTED);
+}
+
+/**
+ * @ingroup raw_raw
+ * Set the callback function for received packets that match the
+ * raw PCB's protocol and binding.
+ *
+ * The callback function MUST either
+ * - eat the packet by calling pbuf_free() and returning non-zero. The
+ *   packet will not be passed to other raw PCBs or other protocol layers.
+ * - not free the packet, and return zero. The packet will be matched
+ *   against further PCBs and/or forwarded to another protocol layers.
+ */
+void
+raw_recv(struct raw_pcb *pcb, raw_recv_fn recv, void *recv_arg)
+{
+  LWIP_ASSERT_CORE_LOCKED();
+  /* remember recv() callback and user data */
+  pcb->recv = recv;
+  pcb->recv_arg = recv_arg;
+}
+
+/**
+ * @ingroup raw_raw
+ * Send the raw IP packet to the given address. An IP header will be prepended
+ * to the packet, unless the RAW_FLAGS_HDRINCL flag is set on the PCB. In that
+ * case, the packet must include an IP header, which will then be sent as is.
+ *
+ * @param pcb the raw pcb which to send
+ * @param p the IP payload to send
+ * @param ipaddr the destination address of the IP packet
+ *
+ */
+err_t
+raw_sendto(struct raw_pcb *pcb, struct pbuf *p, const ip_addr_t *ipaddr)
+{
+  struct netif *netif;
+  const ip_addr_t *src_ip;
+
+  if ((pcb == NULL) || (ipaddr == NULL) || !IP_ADDR_PCB_VERSION_MATCH(pcb, ipaddr)) {
+    return ERR_VAL;
+  }
+
+  LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_TRACE, ("raw_sendto\n"));
+
+  if (pcb->netif_idx != NETIF_NO_INDEX) {
+    netif = netif_get_by_index(pcb->netif_idx);
+  } else {
+#if LWIP_MULTICAST_TX_OPTIONS
+    netif = NULL;
+    if (ip_addr_ismulticast(ipaddr)) {
+      /* For multicast-destined packets, use the user-provided interface index to
+       * determine the outgoing interface, if an interface index is set and a
+       * matching netif can be found. Otherwise, fall back to regular routing. */
+      netif = netif_get_by_index(pcb->mcast_ifindex);
+    }
+
+    if (netif == NULL)
+#endif /* LWIP_MULTICAST_TX_OPTIONS */
+    {
+      netif = ip_route(&pcb->local_ip, ipaddr);
+    }
+  }
+
+  if (netif == NULL) {
+    LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, ("raw_sendto: No route to "));
+    ip_addr_debug_print(RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, ipaddr);
+    LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, ("\n"));
+    return ERR_RTE;
+  }
+
+  if (ip_addr_isany(&pcb->local_ip) || ip_addr_ismulticast(&pcb->local_ip)) {
+    /* use outgoing network interface IP address as source address */
+    src_ip = ip_netif_get_local_ip(netif, ipaddr);
+#if LWIP_IPV6
+    if (src_ip == NULL) {
+      return ERR_RTE;
+    }
+#endif /* LWIP_IPV6 */
+  } else {
+    /* use RAW PCB local IP address as source address */
+    src_ip = &pcb->local_ip;
+  }
+
+  return raw_sendto_if_src(pcb, p, ipaddr, netif, src_ip);
+}
+
+/**
+ * @ingroup raw_raw
+ * Send the raw IP packet to the given address, using a particular outgoing
+ * netif and source IP address. An IP header will be prepended to the packet,
+ * unless the RAW_FLAGS_HDRINCL flag is set on the PCB. In that case, the
+ * packet must include an IP header, which will then be sent as is.
+ *
+ * @param pcb RAW PCB used to send the data
+ * @param p chain of pbufs to be sent
+ * @param dst_ip destination IP address
+ * @param netif the netif used for sending
+ * @param src_ip source IP address
+ */
+err_t
+raw_sendto_if_src(struct raw_pcb *pcb, struct pbuf *p, const ip_addr_t *dst_ip,
+                  struct netif *netif, const ip_addr_t *src_ip)
+{
+  err_t err;
+  struct pbuf *q; /* q will be sent down the stack */
+  u16_t header_size;
+  u8_t ttl;
+
+  LWIP_ASSERT_CORE_LOCKED();
+
+  if ((pcb == NULL) || (dst_ip == NULL) || (netif == NULL) || (src_ip == NULL) ||
+      !IP_ADDR_PCB_VERSION_MATCH(pcb, src_ip) || !IP_ADDR_PCB_VERSION_MATCH(pcb, dst_ip)) {
+    return ERR_VAL;
+  }
+
+  header_size = (
+#if LWIP_IPV4 && LWIP_IPV6
+                  IP_IS_V6(dst_ip) ? IP6_HLEN : IP_HLEN);
+#elif LWIP_IPV4
+                  IP_HLEN);
+#else
+                  IP6_HLEN);
+#endif
+
+  /* Handle the HDRINCL option as an exception: none of the code below applies
+   * to this case, and sending the packet needs to be done differently too. */
+  if (pcb->flags & RAW_FLAGS_HDRINCL) {
+    /* A full header *must* be present in the first pbuf of the chain, as the
+     * output routines may access its fields directly. */
+    if (p->len < header_size) {
+      return ERR_VAL;
+    }
+    /* @todo multicast loop support, if at all desired for this scenario.. */
+    NETIF_SET_HINTS(netif, &pcb->netif_hints);
+    err = ip_output_if_hdrincl(p, src_ip, dst_ip, netif);
+    NETIF_RESET_HINTS(netif);
+    return err;
+  }
+
+  /* packet too large to add an IP header without causing an overflow? */
+  if ((u16_t)(p->tot_len + header_size) < p->tot_len) {
+    return ERR_MEM;
+  }
+  /* not enough space to add an IP header to first pbuf in given p chain? */
+  if (pbuf_add_header(p, header_size)) {
+    /* allocate header in new pbuf */
+    q = pbuf_alloc(PBUF_IP, 0, PBUF_RAM);
+    /* new header pbuf could not be allocated? */
+    if (q == NULL) {
+      LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("raw_sendto: could not allocate header\n"));
+      return ERR_MEM;
+    }
+    if (p->tot_len != 0) {
+      /* chain header q in front of given pbuf p */
+      pbuf_chain(q, p);
+    }
+    /* { first pbuf q points to header pbuf } */
+    LWIP_DEBUGF(RAW_DEBUG, ("raw_sendto: added header pbuf %p before given pbuf %p\n", (void *)q, (void *)p));
+  } else {
+    /* first pbuf q equals given pbuf */
+    q = p;
+    if (pbuf_remove_header(q, header_size)) {
+      LWIP_ASSERT("Can't restore header we just removed!", 0);
+      return ERR_MEM;
+    }
+  }
+
+#if IP_SOF_BROADCAST
+  if (IP_IS_V4(dst_ip)) {
+    /* broadcast filter? */
+    if (!ip_get_option(pcb, SOF_BROADCAST) && ip_addr_isbroadcast(dst_ip, netif)) {
+      LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, ("raw_sendto: SOF_BROADCAST not enabled on pcb %p\n", (void *)pcb));
+      /* free any temporary header pbuf allocated by pbuf_header() */
+      if (q != p) {
+        pbuf_free(q);
+      }
+      return ERR_VAL;
+    }
+  }
+#endif /* IP_SOF_BROADCAST */
+
+  /* Multicast Loop? */
+#if LWIP_MULTICAST_TX_OPTIONS
+  if (((pcb->flags & RAW_FLAGS_MULTICAST_LOOP) != 0) && ip_addr_ismulticast(dst_ip)) {
+    q->flags |= PBUF_FLAG_MCASTLOOP;
+  }
+#endif /* LWIP_MULTICAST_TX_OPTIONS */
+
+#if LWIP_IPV6
+  /* If requested, based on the IPV6_CHECKSUM socket option per RFC3542,
+     compute the checksum and update the checksum in the payload. */
+  if (IP_IS_V6(dst_ip) && pcb->chksum_reqd) {
+    u16_t chksum = ip6_chksum_pseudo(p, pcb->protocol, p->tot_len, ip_2_ip6(src_ip), ip_2_ip6(dst_ip));
+    LWIP_ASSERT("Checksum must fit into first pbuf", p->len >= (pcb->chksum_offset + 2));
+    SMEMCPY(((u8_t *)p->payload) + pcb->chksum_offset, &chksum, sizeof(u16_t));
+  }
+#endif
+
+  /* Determine TTL to use */
+#if LWIP_MULTICAST_TX_OPTIONS
+  ttl = (ip_addr_ismulticast(dst_ip) ? raw_get_multicast_ttl(pcb) : pcb->ttl);
+#else /* LWIP_MULTICAST_TX_OPTIONS */
+  ttl = pcb->ttl;
+#endif /* LWIP_MULTICAST_TX_OPTIONS */
+
+  NETIF_SET_HINTS(netif, &pcb->netif_hints);
+  err = ip_output_if(q, src_ip, dst_ip, ttl, pcb->tos, pcb->protocol, netif);
+  NETIF_RESET_HINTS(netif);
+
+  /* did we chain a header earlier? */
+  if (q != p) {
+    /* free the header */
+    pbuf_free(q);
+  }
+  return err;
+}
+
+/**
+ * @ingroup raw_raw
+ * Send the raw IP packet to the address given by raw_connect()
+ *
+ * @param pcb the raw pcb which to send
+ * @param p the IP payload to send
+ *
+ */
+err_t
+raw_send(struct raw_pcb *pcb, struct pbuf *p)
+{
+  return raw_sendto(pcb, p, &pcb->remote_ip);
+}
+
+/**
+ * @ingroup raw_raw
+ * Remove an RAW PCB.
+ *
+ * @param pcb RAW PCB to be removed. The PCB is removed from the list of
+ * RAW PCB's and the data structure is freed from memory.
+ *
+ * @see raw_new()
+ */
+void
+raw_remove(struct raw_pcb *pcb)
+{
+  struct raw_pcb *pcb2;
+  LWIP_ASSERT_CORE_LOCKED();
+  /* pcb to be removed is first in list? */
+  if (raw_pcbs == pcb) {
+    /* make list start at 2nd pcb */
+    raw_pcbs = raw_pcbs->next;
+    /* pcb not 1st in list */
+  } else {
+    for (pcb2 = raw_pcbs; pcb2 != NULL; pcb2 = pcb2->next) {
+      /* find pcb in raw_pcbs list */
+      if (pcb2->next != NULL && pcb2->next == pcb) {
+        /* remove pcb from list */
+        pcb2->next = pcb->next;
+        break;
+      }
+    }
+  }
+  memp_free(MEMP_RAW_PCB, pcb);
+}
+
+/**
+ * @ingroup raw_raw
+ * Create a RAW PCB.
+ *
+ * @return The RAW PCB which was created. NULL if the PCB data structure
+ * could not be allocated.
+ *
+ * @param proto the protocol number of the IPs payload (e.g. IP_PROTO_ICMP)
+ *
+ * @see raw_remove()
+ */
+struct raw_pcb *
+raw_new(u8_t proto)
+{
+  struct raw_pcb *pcb;
+
+  LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_TRACE, ("raw_new\n"));
+  LWIP_ASSERT_CORE_LOCKED();
+
+  pcb = (struct raw_pcb *)memp_malloc(MEMP_RAW_PCB);
+  /* could allocate RAW PCB? */
+  if (pcb != NULL) {
+    /* initialize PCB to all zeroes */
+    memset(pcb, 0, sizeof(struct raw_pcb));
+    pcb->protocol = proto;
+    pcb->ttl = RAW_TTL;
+#if LWIP_MULTICAST_TX_OPTIONS
+    raw_set_multicast_ttl(pcb, RAW_TTL);
+#endif /* LWIP_MULTICAST_TX_OPTIONS */
+    pcb_tci_init(pcb);
+    pcb->next = raw_pcbs;
+    raw_pcbs = pcb;
+  }
+  return pcb;
+}
+
+/**
+ * @ingroup raw_raw
+ * Create a RAW PCB for specific IP type.
+ *
+ * @return The RAW PCB which was created. NULL if the PCB data structure
+ * could not be allocated.
+ *
+ * @param type IP address type, see @ref lwip_ip_addr_type definitions.
+ * If you want to listen to IPv4 and IPv6 (dual-stack) packets,
+ * supply @ref IPADDR_TYPE_ANY as argument and bind to @ref IP_ANY_TYPE.
+ * @param proto the protocol number (next header) of the IPv6 packet payload
+ *              (e.g. IP6_NEXTH_ICMP6)
+ *
+ * @see raw_remove()
+ */
+struct raw_pcb *
+raw_new_ip_type(u8_t type, u8_t proto)
+{
+  struct raw_pcb *pcb;
+  LWIP_ASSERT_CORE_LOCKED();
+  pcb = raw_new(proto);
+#if LWIP_IPV4 && LWIP_IPV6
+  if (pcb != NULL) {
+    IP_SET_TYPE_VAL(pcb->local_ip,  type);
+    IP_SET_TYPE_VAL(pcb->remote_ip, type);
+  }
+#else /* LWIP_IPV4 && LWIP_IPV6 */
+  LWIP_UNUSED_ARG(type);
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+  return pcb;
+}
+
+/** This function is called from netif.c when address is changed
+ *
+ * @param old_addr IP address of the netif before change
+ * @param new_addr IP address of the netif after change
+ */
+void raw_netif_ip_addr_changed(const ip_addr_t *old_addr, const ip_addr_t *new_addr)
+{
+  struct raw_pcb *rpcb;
+
+  if (!ip_addr_isany(old_addr) && !ip_addr_isany(new_addr)) {
+    for (rpcb = raw_pcbs; rpcb != NULL; rpcb = rpcb->next) {
+      /* PCB bound to current local interface address? */
+      if (ip_addr_eq(&rpcb->local_ip, old_addr)) {
+        /* The PCB is bound to the old ipaddr and
+         * is set to bound to the new one instead */
+        ip_addr_copy(rpcb->local_ip, *new_addr);
+      }
+    }
+  }
+}
+
+#endif /* LWIP_RAW */
diff --git a/lib/lwip/core/stats.c b/lib/lwip/core/stats.c
new file mode 100644
index 0000000000..95445ec955
--- /dev/null
+++ b/lib/lwip/core/stats.c
@@ -0,0 +1,168 @@
+/**
+ * @file
+ * Statistics module
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_STATS /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/def.h"
+#include "lwip/stats.h"
+#include "lwip/mem.h"
+#include "lwip/debug.h"
+
+#include <string.h>
+
+struct stats_ lwip_stats;
+
+void
+stats_init(void)
+{
+#ifdef LWIP_DEBUG
+#if MEM_STATS
+  lwip_stats.mem.name = "MEM";
+#endif /* MEM_STATS */
+#endif /* LWIP_DEBUG */
+}
+
+#if LWIP_STATS_DISPLAY
+void
+stats_display_proto(struct stats_proto *proto, const char *name)
+{
+  LWIP_PLATFORM_DIAG(("\n%s\n\t", name));
+  LWIP_PLATFORM_DIAG(("xmit: %"STAT_COUNTER_F"\n\t", proto->xmit));
+  LWIP_PLATFORM_DIAG(("recv: %"STAT_COUNTER_F"\n\t", proto->recv));
+  LWIP_PLATFORM_DIAG(("fw: %"STAT_COUNTER_F"\n\t", proto->fw));
+  LWIP_PLATFORM_DIAG(("drop: %"STAT_COUNTER_F"\n\t", proto->drop));
+  LWIP_PLATFORM_DIAG(("chkerr: %"STAT_COUNTER_F"\n\t", proto->chkerr));
+  LWIP_PLATFORM_DIAG(("lenerr: %"STAT_COUNTER_F"\n\t", proto->lenerr));
+  LWIP_PLATFORM_DIAG(("memerr: %"STAT_COUNTER_F"\n\t", proto->memerr));
+  LWIP_PLATFORM_DIAG(("rterr: %"STAT_COUNTER_F"\n\t", proto->rterr));
+  LWIP_PLATFORM_DIAG(("proterr: %"STAT_COUNTER_F"\n\t", proto->proterr));
+  LWIP_PLATFORM_DIAG(("opterr: %"STAT_COUNTER_F"\n\t", proto->opterr));
+  LWIP_PLATFORM_DIAG(("err: %"STAT_COUNTER_F"\n\t", proto->err));
+  LWIP_PLATFORM_DIAG(("cachehit: %"STAT_COUNTER_F"\n", proto->cachehit));
+}
+
+#if IGMP_STATS || MLD6_STATS
+void
+stats_display_igmp(struct stats_igmp *igmp, const char *name)
+{
+  LWIP_PLATFORM_DIAG(("\n%s\n\t", name));
+  LWIP_PLATFORM_DIAG(("xmit: %"STAT_COUNTER_F"\n\t", igmp->xmit));
+  LWIP_PLATFORM_DIAG(("recv: %"STAT_COUNTER_F"\n\t", igmp->recv));
+  LWIP_PLATFORM_DIAG(("drop: %"STAT_COUNTER_F"\n\t", igmp->drop));
+  LWIP_PLATFORM_DIAG(("chkerr: %"STAT_COUNTER_F"\n\t", igmp->chkerr));
+  LWIP_PLATFORM_DIAG(("lenerr: %"STAT_COUNTER_F"\n\t", igmp->lenerr));
+  LWIP_PLATFORM_DIAG(("memerr: %"STAT_COUNTER_F"\n\t", igmp->memerr));
+  LWIP_PLATFORM_DIAG(("proterr: %"STAT_COUNTER_F"\n\t", igmp->proterr));
+  LWIP_PLATFORM_DIAG(("rx_v1: %"STAT_COUNTER_F"\n\t", igmp->rx_v1));
+  LWIP_PLATFORM_DIAG(("rx_group: %"STAT_COUNTER_F"\n\t", igmp->rx_group));
+  LWIP_PLATFORM_DIAG(("rx_general: %"STAT_COUNTER_F"\n\t", igmp->rx_general));
+  LWIP_PLATFORM_DIAG(("rx_report: %"STAT_COUNTER_F"\n\t", igmp->rx_report));
+  LWIP_PLATFORM_DIAG(("tx_join: %"STAT_COUNTER_F"\n\t", igmp->tx_join));
+  LWIP_PLATFORM_DIAG(("tx_leave: %"STAT_COUNTER_F"\n\t", igmp->tx_leave));
+  LWIP_PLATFORM_DIAG(("tx_report: %"STAT_COUNTER_F"\n", igmp->tx_report));
+}
+#endif /* IGMP_STATS || MLD6_STATS */
+
+#if MEM_STATS || MEMP_STATS
+void
+stats_display_mem(struct stats_mem *mem, const char *name)
+{
+  LWIP_PLATFORM_DIAG(("\nMEM %s\n\t", name));
+  LWIP_PLATFORM_DIAG(("avail: %"MEM_SIZE_F"\n\t", mem->avail));
+  LWIP_PLATFORM_DIAG(("used: %"MEM_SIZE_F"\n\t", mem->used));
+  LWIP_PLATFORM_DIAG(("max: %"MEM_SIZE_F"\n\t", mem->max));
+  LWIP_PLATFORM_DIAG(("err: %"STAT_COUNTER_F"\n", mem->err));
+}
+
+#if MEMP_STATS
+void
+stats_display_memp(struct stats_mem *mem, int idx)
+{
+  if (idx < MEMP_MAX) {
+    stats_display_mem(mem, mem->name);
+  }
+}
+#endif /* MEMP_STATS */
+#endif /* MEM_STATS || MEMP_STATS */
+
+#if SYS_STATS
+void
+stats_display_sys(struct stats_sys *sys)
+{
+  LWIP_PLATFORM_DIAG(("\nSYS\n\t"));
+  LWIP_PLATFORM_DIAG(("sem.used:  %"STAT_COUNTER_F"\n\t", sys->sem.used));
+  LWIP_PLATFORM_DIAG(("sem.max:   %"STAT_COUNTER_F"\n\t", sys->sem.max));
+  LWIP_PLATFORM_DIAG(("sem.err:   %"STAT_COUNTER_F"\n\t", sys->sem.err));
+  LWIP_PLATFORM_DIAG(("mutex.used: %"STAT_COUNTER_F"\n\t", sys->mutex.used));
+  LWIP_PLATFORM_DIAG(("mutex.max:  %"STAT_COUNTER_F"\n\t", sys->mutex.max));
+  LWIP_PLATFORM_DIAG(("mutex.err:  %"STAT_COUNTER_F"\n\t", sys->mutex.err));
+  LWIP_PLATFORM_DIAG(("mbox.used:  %"STAT_COUNTER_F"\n\t", sys->mbox.used));
+  LWIP_PLATFORM_DIAG(("mbox.max:   %"STAT_COUNTER_F"\n\t", sys->mbox.max));
+  LWIP_PLATFORM_DIAG(("mbox.err:   %"STAT_COUNTER_F"\n", sys->mbox.err));
+}
+#endif /* SYS_STATS */
+
+void
+stats_display(void)
+{
+  s16_t i;
+
+  LINK_STATS_DISPLAY();
+  ETHARP_STATS_DISPLAY();
+  IPFRAG_STATS_DISPLAY();
+  IP6_FRAG_STATS_DISPLAY();
+  IP_STATS_DISPLAY();
+  ND6_STATS_DISPLAY();
+  IP6_STATS_DISPLAY();
+  IGMP_STATS_DISPLAY();
+  MLD6_STATS_DISPLAY();
+  ICMP_STATS_DISPLAY();
+  ICMP6_STATS_DISPLAY();
+  UDP_STATS_DISPLAY();
+  TCP_STATS_DISPLAY();
+  MEM_STATS_DISPLAY();
+  for (i = 0; i < MEMP_MAX; i++) {
+    MEMP_STATS_DISPLAY(i);
+  }
+  SYS_STATS_DISPLAY();
+}
+#endif /* LWIP_STATS_DISPLAY */
+
+#endif /* LWIP_STATS */
diff --git a/lib/lwip/core/sys.c b/lib/lwip/core/sys.c
new file mode 100644
index 0000000000..69d7197a1f
--- /dev/null
+++ b/lib/lwip/core/sys.c
@@ -0,0 +1,148 @@
+/**
+ * @file
+ * lwIP Operating System abstraction
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+/**
+ * @defgroup sys_layer Porting (system abstraction layer)
+ * @ingroup lwip
+ *
+ * @defgroup sys_os OS abstraction layer
+ * @ingroup sys_layer
+ * No need to implement functions in this section in NO_SYS mode.
+ * The OS-specific code should be implemented in arch/sys_arch.h
+ * and sys_arch.c of your port.
+ *
+ * The operating system emulation layer provides a common interface
+ * between the lwIP code and the underlying operating system kernel. The
+ * general idea is that porting lwIP to new architectures requires only
+ * small changes to a few header files and a new sys_arch
+ * implementation. It is also possible to do a sys_arch implementation
+ * that does not rely on any underlying operating system.
+ *
+ * The sys_arch provides semaphores, mailboxes and mutexes to lwIP. For the full
+ * lwIP functionality, multiple threads support can be implemented in the
+ * sys_arch, but this is not required for the basic lwIP
+ * functionality. Timer scheduling is implemented in lwIP, but can be implemented
+ * by the sys_arch port (LWIP_TIMERS_CUSTOM==1).
+ *
+ * In addition to the source file providing the functionality of sys_arch,
+ * the OS emulation layer must provide several header files defining
+ * macros used throughout lwip.  The files required and the macros they
+ * must define are listed below the sys_arch description.
+ *
+ * Since lwIP 1.4.0, semaphore, mutexes and mailbox functions are prototyped in a way that
+ * allows both using pointers or actual OS structures to be used. This way, memory
+ * required for such types can be either allocated in place (globally or on the
+ * stack) or on the heap (allocated internally in the "*_new()" functions).
+ *
+ * Note:
+ * -----
+ * Be careful with using mem_malloc() in sys_arch. When malloc() refers to
+ * mem_malloc() you can run into a circular function call problem. In mem.c
+ * mem_init() tries to allocate a semaphore using mem_malloc, which of course
+ * can't be performed when sys_arch uses mem_malloc.
+ *
+ * @defgroup sys_sem Semaphores
+ * @ingroup sys_os
+ * Semaphores can be either counting or binary - lwIP works with both
+ * kinds.
+ * Semaphores are represented by the type "sys_sem_t" which is typedef'd
+ * in the sys_arch.h file. Mailboxes are equivalently represented by the
+ * type "sys_mbox_t". Mutexes are represented by the type "sys_mutex_t".
+ * lwIP does not place any restrictions on how these types are represented
+ * internally.
+ *
+ * @defgroup sys_mutex Mutexes
+ * @ingroup sys_os
+ * Mutexes are recommended to correctly handle priority inversion,
+ * especially if you use LWIP_CORE_LOCKING .
+ *
+ * @defgroup sys_mbox Mailboxes
+ * @ingroup sys_os
+ * Mailboxes should be implemented as a queue which allows multiple messages
+ * to be posted (implementing as a rendez-vous point where only one message can be
+ * posted at a time can have a highly negative impact on performance). A message
+ * in a mailbox is just a pointer, nothing more.
+ *
+ * @defgroup sys_time Time
+ * @ingroup sys_layer
+ *
+ * @defgroup sys_prot Critical sections
+ * @ingroup sys_layer
+ * Used to protect short regions of code against concurrent access.
+ * - Your system is a bare-metal system (probably with an RTOS)
+ *   and interrupts are under your control:
+ *   Implement this as LockInterrupts() / UnlockInterrupts()
+ * - Your system uses an RTOS with deferred interrupt handling from a
+ *   worker thread: Implement as a global mutex or lock/unlock scheduler
+ * - Your system uses a high-level OS with e.g. POSIX signals:
+ *   Implement as a global mutex
+ *
+ * @defgroup sys_misc Misc
+ * @ingroup sys_os
+ */
+
+#include "lwip/opt.h"
+
+#include "lwip/sys.h"
+
+/* Most of the functions defined in sys.h must be implemented in the
+ * architecture-dependent file sys_arch.c */
+
+#if !NO_SYS
+
+#ifndef sys_msleep
+/**
+ * Sleep for some ms. Timeouts are NOT processed while sleeping.
+ *
+ * @param ms number of milliseconds to sleep
+ */
+void
+sys_msleep(u32_t ms)
+{
+  if (ms > 0) {
+    sys_sem_t delaysem;
+    err_t err = sys_sem_new(&delaysem, 0);
+    if (err == ERR_OK) {
+      sys_arch_sem_wait(&delaysem, ms);
+      sys_sem_free(&delaysem);
+    }
+  }
+}
+#endif /* sys_msleep */
+
+#endif /* !NO_SYS */
diff --git a/lib/lwip/core/tcp.c b/lib/lwip/core/tcp.c
new file mode 100644
index 0000000000..ea95ffeef8
--- /dev/null
+++ b/lib/lwip/core/tcp.c
@@ -0,0 +1,2696 @@
+/**
+ * @file
+ * Transmission Control Protocol for IP
+ * See also @ref tcp_raw
+ *
+ * @defgroup tcp_raw TCP
+ * @ingroup callbackstyle_api
+ * Transmission Control Protocol for IP<br>
+ * @see @ref api
+ *
+ * Common functions for the TCP implementation, such as functions
+ * for manipulating the data structures and the TCP timer functions. TCP functions
+ * related to input and output is found in tcp_in.c and tcp_out.c respectively.<br>
+ *
+ * TCP connection setup
+ * --------------------
+ * The functions used for setting up connections is similar to that of
+ * the sequential API and of the BSD socket API. A new TCP connection
+ * identifier (i.e., a protocol control block - PCB) is created with the
+ * tcp_new() function. This PCB can then be either set to listen for new
+ * incoming connections or be explicitly connected to another host.
+ * - tcp_new()
+ * - tcp_bind()
+ * - tcp_listen() and tcp_listen_with_backlog()
+ * - tcp_accept()
+ * - tcp_connect()
+ *
+ * Sending TCP data
+ * ----------------
+ * TCP data is sent by enqueueing the data with a call to tcp_write() and
+ * triggering to send by calling tcp_output(). When the data is successfully
+ * transmitted to the remote host, the application will be notified with a
+ * call to a specified callback function.
+ * - tcp_write()
+ * - tcp_output()
+ * - tcp_sent()
+ *
+ * Receiving TCP data
+ * ------------------
+ * TCP data reception is callback based - an application specified
+ * callback function is called when new data arrives. When the
+ * application has taken the data, it has to call the tcp_recved()
+ * function to indicate that TCP can advertise increase the receive
+ * window.
+ * - tcp_recv()
+ * - tcp_recved()
+ *
+ * Application polling
+ * -------------------
+ * When a connection is idle (i.e., no data is either transmitted or
+ * received), lwIP will repeatedly poll the application by calling a
+ * specified callback function. This can be used either as a watchdog
+ * timer for killing connections that have stayed idle for too long, or
+ * as a method of waiting for memory to become available. For instance,
+ * if a call to tcp_write() has failed because memory wasn't available,
+ * the application may use the polling functionality to call tcp_write()
+ * again when the connection has been idle for a while.
+ * - tcp_poll()
+ *
+ * Closing and aborting connections
+ * --------------------------------
+ * - tcp_close()
+ * - tcp_abort()
+ * - tcp_err()
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/def.h"
+#include "lwip/mem.h"
+#include "lwip/memp.h"
+#include "lwip/tcp.h"
+#include "lwip/priv/tcp_priv.h"
+#include "lwip/debug.h"
+#include "lwip/stats.h"
+#include "lwip/ip6.h"
+#include "lwip/ip6_addr.h"
+#include "lwip/nd6.h"
+
+#include <string.h>
+
+#ifdef LWIP_HOOK_FILENAME
+#include LWIP_HOOK_FILENAME
+#endif
+
+#ifndef TCP_LOCAL_PORT_RANGE_START
+/* From http://www.iana.org/assignments/port-numbers:
+   "The Dynamic and/or Private Ports are those from 49152 through 65535" */
+#define TCP_LOCAL_PORT_RANGE_START        0xc000
+#define TCP_LOCAL_PORT_RANGE_END          0xffff
+#define TCP_ENSURE_LOCAL_PORT_RANGE(port) ((u16_t)(((port) & (u16_t)~TCP_LOCAL_PORT_RANGE_START) + TCP_LOCAL_PORT_RANGE_START))
+#endif
+
+#if LWIP_TCP_KEEPALIVE
+#define TCP_KEEP_DUR(pcb)   ((pcb)->keep_cnt * (pcb)->keep_intvl)
+#define TCP_KEEP_INTVL(pcb) ((pcb)->keep_intvl)
+#else /* LWIP_TCP_KEEPALIVE */
+#define TCP_KEEP_DUR(pcb)   TCP_MAXIDLE
+#define TCP_KEEP_INTVL(pcb) TCP_KEEPINTVL_DEFAULT
+#endif /* LWIP_TCP_KEEPALIVE */
+
+/* As initial send MSS, we use TCP_MSS but limit it to 536. */
+#if TCP_MSS > 536
+#define INITIAL_MSS 536
+#else
+#define INITIAL_MSS TCP_MSS
+#endif
+
+static const char *const tcp_state_str[] = {
+  "CLOSED",
+  "LISTEN",
+  "SYN_SENT",
+  "SYN_RCVD",
+  "ESTABLISHED",
+  "FIN_WAIT_1",
+  "FIN_WAIT_2",
+  "CLOSE_WAIT",
+  "CLOSING",
+  "LAST_ACK",
+  "TIME_WAIT"
+};
+
+/* last local TCP port */
+static u16_t tcp_port = TCP_LOCAL_PORT_RANGE_START;
+
+/* Incremented every coarse grained timer shot (typically every 500 ms). */
+u32_t tcp_ticks;
+static const u8_t tcp_backoff[13] =
+{ 1, 2, 3, 4, 5, 6, 7, 7, 7, 7, 7, 7, 7};
+/* Times per slowtmr hits */
+static const u8_t tcp_persist_backoff[7] = { 3, 6, 12, 24, 48, 96, 120 };
+
+/* The TCP PCB lists. */
+
+/** List of all TCP PCBs bound but not yet (connected || listening) */
+struct tcp_pcb *tcp_bound_pcbs;
+/** List of all TCP PCBs in LISTEN state */
+union tcp_listen_pcbs_t tcp_listen_pcbs;
+/** List of all TCP PCBs that are in a state in which
+ * they accept or send data. */
+struct tcp_pcb *tcp_active_pcbs;
+/** List of all TCP PCBs in TIME-WAIT state */
+struct tcp_pcb *tcp_tw_pcbs;
+
+/** An array with all (non-temporary) PCB lists, mainly used for smaller code size */
+struct tcp_pcb **const tcp_pcb_lists[] = {&tcp_listen_pcbs.pcbs, &tcp_bound_pcbs,
+         &tcp_active_pcbs, &tcp_tw_pcbs
+};
+
+u8_t tcp_active_pcbs_changed;
+
+/** Timer counter to handle calling slow-timer from tcp_tmr() */
+static u8_t tcp_timer;
+static u8_t tcp_timer_ctr;
+static u16_t tcp_new_port(void);
+
+static err_t tcp_close_shutdown_fin(struct tcp_pcb *pcb);
+#if LWIP_TCP_PCB_NUM_EXT_ARGS
+static void tcp_ext_arg_invoke_callbacks_destroyed(struct tcp_pcb_ext_args *ext_args);
+#endif
+
+/**
+ * Initialize this module.
+ */
+void
+tcp_init(void)
+{
+#ifdef LWIP_RAND
+  tcp_port = TCP_ENSURE_LOCAL_PORT_RANGE(LWIP_RAND());
+#endif /* LWIP_RAND */
+}
+
+/** Free a tcp pcb */
+void
+tcp_free(struct tcp_pcb *pcb)
+{
+  LWIP_ASSERT("tcp_free: LISTEN", pcb->state != LISTEN);
+#if LWIP_TCP_PCB_NUM_EXT_ARGS
+  tcp_ext_arg_invoke_callbacks_destroyed(pcb->ext_args);
+#endif
+  memp_free(MEMP_TCP_PCB, pcb);
+}
+
+/** Free a tcp listen pcb */
+static void
+tcp_free_listen(struct tcp_pcb *pcb)
+{
+  LWIP_ASSERT("tcp_free_listen: !LISTEN", pcb->state != LISTEN);
+#if LWIP_TCP_PCB_NUM_EXT_ARGS
+  tcp_ext_arg_invoke_callbacks_destroyed(pcb->ext_args);
+#endif
+  memp_free(MEMP_TCP_PCB_LISTEN, pcb);
+}
+
+/**
+ * Called periodically to dispatch TCP timers.
+ */
+void
+tcp_tmr(void)
+{
+  /* Call tcp_fasttmr() every 250 ms */
+  tcp_fasttmr();
+
+  if (++tcp_timer & 1) {
+    /* Call tcp_slowtmr() every 500 ms, i.e., every other timer
+       tcp_tmr() is called. */
+    tcp_slowtmr();
+  }
+}
+
+#if LWIP_CALLBACK_API || TCP_LISTEN_BACKLOG
+/** Called when a listen pcb is closed. Iterates one pcb list and removes the
+ * closed listener pcb from pcb->listener if matching.
+ */
+static void
+tcp_remove_listener(struct tcp_pcb *list, struct tcp_pcb_listen *lpcb)
+{
+  struct tcp_pcb *pcb;
+
+  LWIP_ASSERT("tcp_remove_listener: invalid listener", lpcb != NULL);
+
+  for (pcb = list; pcb != NULL; pcb = pcb->next) {
+    if (pcb->listener == lpcb) {
+      pcb->listener = NULL;
+    }
+  }
+}
+#endif
+
+/** Called when a listen pcb is closed. Iterates all pcb lists and removes the
+ * closed listener pcb from pcb->listener if matching.
+ */
+static void
+tcp_listen_closed(struct tcp_pcb *pcb)
+{
+#if LWIP_CALLBACK_API || TCP_LISTEN_BACKLOG
+  size_t i;
+  LWIP_ASSERT("pcb != NULL", pcb != NULL);
+  LWIP_ASSERT("pcb->state == LISTEN", pcb->state == LISTEN);
+  for (i = 1; i < LWIP_ARRAYSIZE(tcp_pcb_lists); i++) {
+    tcp_remove_listener(*tcp_pcb_lists[i], (struct tcp_pcb_listen *)pcb);
+  }
+#endif
+  LWIP_UNUSED_ARG(pcb);
+}
+
+#if TCP_LISTEN_BACKLOG
+/** @ingroup tcp_raw
+ * Delay accepting a connection in respect to the listen backlog:
+ * the number of outstanding connections is increased until
+ * tcp_backlog_accepted() is called.
+ *
+ * ATTENTION: the caller is responsible for calling tcp_backlog_accepted()
+ * or else the backlog feature will get out of sync!
+ *
+ * @param pcb the connection pcb which is not fully accepted yet
+ */
+void
+tcp_backlog_delayed(struct tcp_pcb *pcb)
+{
+  LWIP_ASSERT("pcb != NULL", pcb != NULL);
+  LWIP_ASSERT_CORE_LOCKED();
+  if ((pcb->flags & TF_BACKLOGPEND) == 0) {
+    if (pcb->listener != NULL) {
+      pcb->listener->accepts_pending++;
+      LWIP_ASSERT("accepts_pending != 0", pcb->listener->accepts_pending != 0);
+      tcp_set_flags(pcb, TF_BACKLOGPEND);
+    }
+  }
+}
+
+/** @ingroup tcp_raw
+ * A delayed-accept a connection is accepted (or closed/aborted): decreases
+ * the number of outstanding connections after calling tcp_backlog_delayed().
+ *
+ * ATTENTION: the caller is responsible for calling tcp_backlog_accepted()
+ * or else the backlog feature will get out of sync!
+ *
+ * @param pcb the connection pcb which is now fully accepted (or closed/aborted)
+ */
+void
+tcp_backlog_accepted(struct tcp_pcb *pcb)
+{
+  LWIP_ASSERT("pcb != NULL", pcb != NULL);
+  LWIP_ASSERT_CORE_LOCKED();
+  if ((pcb->flags & TF_BACKLOGPEND) != 0) {
+    if (pcb->listener != NULL) {
+      LWIP_ASSERT("accepts_pending != 0", pcb->listener->accepts_pending != 0);
+      pcb->listener->accepts_pending--;
+      tcp_clear_flags(pcb, TF_BACKLOGPEND);
+    }
+  }
+}
+#endif /* TCP_LISTEN_BACKLOG */
+
+/**
+ * Closes the TX side of a connection held by the PCB.
+ * For tcp_close(), a RST is sent if the application didn't receive all data
+ * (tcp_recved() not called for all data passed to recv callback).
+ *
+ * Listening pcbs are freed and may not be referenced any more.
+ * Connection pcbs are freed if not yet connected and may not be referenced
+ * any more. If a connection is established (at least SYN received or in
+ * a closing state), the connection is closed, and put in a closing state.
+ * The pcb is then automatically freed in tcp_slowtmr(). It is therefore
+ * unsafe to reference it.
+ *
+ * @param pcb the tcp_pcb to close
+ * @return ERR_OK if connection has been closed
+ *         another err_t if closing failed and pcb is not freed
+ */
+static err_t
+tcp_close_shutdown(struct tcp_pcb *pcb, u8_t rst_on_unacked_data)
+{
+  LWIP_ASSERT("tcp_close_shutdown: invalid pcb", pcb != NULL);
+
+  if (rst_on_unacked_data && ((pcb->state == ESTABLISHED) || (pcb->state == CLOSE_WAIT))) {
+    if ((pcb->refused_data != NULL) || (pcb->rcv_wnd != TCP_WND_MAX(pcb))) {
+      /* Not all data received by application, send RST to tell the remote
+         side about this. */
+      LWIP_ASSERT("pcb->flags & TF_RXCLOSED", pcb->flags & TF_RXCLOSED);
+
+      /* don't call tcp_abort here: we must not deallocate the pcb since
+         that might not be expected when calling tcp_close */
+      tcp_rst(pcb, pcb->snd_nxt, pcb->rcv_nxt, &pcb->local_ip, &pcb->remote_ip,
+              pcb->local_port, pcb->remote_port);
+
+      tcp_pcb_purge(pcb);
+      TCP_RMV_ACTIVE(pcb);
+      /* Deallocate the pcb since we already sent a RST for it */
+      if (tcp_input_pcb == pcb) {
+        /* prevent using a deallocated pcb: free it from tcp_input later */
+        tcp_trigger_input_pcb_close();
+      } else {
+        tcp_free(pcb);
+      }
+      return ERR_OK;
+    }
+  }
+
+  /* - states which free the pcb are handled here,
+     - states which send FIN and change state are handled in tcp_close_shutdown_fin() */
+  switch (pcb->state) {
+    case CLOSED:
+      /* Closing a pcb in the CLOSED state might seem erroneous,
+       * however, it is in this state once allocated and as yet unused
+       * and the user needs some way to free it should the need arise.
+       * Calling tcp_close() with a pcb that has already been closed, (i.e. twice)
+       * or for a pcb that has been used and then entered the CLOSED state
+       * is erroneous, but this should never happen as the pcb has in those cases
+       * been freed, and so any remaining handles are bogus. */
+      if (pcb->local_port != 0) {
+        TCP_RMV(&tcp_bound_pcbs, pcb);
+      }
+      tcp_free(pcb);
+      break;
+    case LISTEN:
+      tcp_listen_closed(pcb);
+      tcp_pcb_remove(&tcp_listen_pcbs.pcbs, pcb);
+      tcp_free_listen(pcb);
+      break;
+    case SYN_SENT:
+      TCP_PCB_REMOVE_ACTIVE(pcb);
+      tcp_free(pcb);
+      MIB2_STATS_INC(mib2.tcpattemptfails);
+      break;
+    default:
+      return tcp_close_shutdown_fin(pcb);
+  }
+  return ERR_OK;
+}
+
+static err_t
+tcp_close_shutdown_fin(struct tcp_pcb *pcb)
+{
+  err_t err;
+  LWIP_ASSERT("pcb != NULL", pcb != NULL);
+
+  switch (pcb->state) {
+    case SYN_RCVD:
+      err = tcp_send_fin(pcb);
+      if (err == ERR_OK) {
+        tcp_backlog_accepted(pcb);
+        MIB2_STATS_INC(mib2.tcpattemptfails);
+        pcb->state = FIN_WAIT_1;
+      }
+      break;
+    case ESTABLISHED:
+      err = tcp_send_fin(pcb);
+      if (err == ERR_OK) {
+        MIB2_STATS_INC(mib2.tcpestabresets);
+        pcb->state = FIN_WAIT_1;
+      }
+      break;
+    case CLOSE_WAIT:
+      err = tcp_send_fin(pcb);
+      if (err == ERR_OK) {
+        MIB2_STATS_INC(mib2.tcpestabresets);
+        pcb->state = LAST_ACK;
+      }
+      break;
+    default:
+      /* Has already been closed, do nothing. */
+      return ERR_OK;
+  }
+
+  if (err == ERR_OK) {
+    /* To ensure all data has been sent when tcp_close returns, we have
+       to make sure tcp_output doesn't fail.
+       Since we don't really have to ensure all data has been sent when tcp_close
+       returns (unsent data is sent from tcp timer functions, also), we don't care
+       for the return value of tcp_output for now. */
+    tcp_output(pcb);
+  } else if (err == ERR_MEM) {
+    /* Mark this pcb for closing. Closing is retried from tcp_tmr. */
+    tcp_set_flags(pcb, TF_CLOSEPEND);
+    /* We have to return ERR_OK from here to indicate to the callers that this
+       pcb should not be used any more as it will be freed soon via tcp_tmr.
+       This is OK here since sending FIN does not guarantee a time frime for
+       actually freeing the pcb, either (it is left in closure states for
+       remote ACK or timeout) */
+    return ERR_OK;
+  }
+  return err;
+}
+
+/**
+ * @ingroup tcp_raw
+ * Closes the connection held by the PCB.
+ *
+ * Listening pcbs are freed and may not be referenced any more.
+ * Connection pcbs are freed if not yet connected and may not be referenced
+ * any more. If a connection is established (at least SYN received or in
+ * a closing state), the connection is closed, and put in a closing state.
+ * The pcb is then automatically freed in tcp_slowtmr(). It is therefore
+ * unsafe to reference it (unless an error is returned).
+ *
+ * The function may return ERR_MEM if no memory
+ * was available for closing the connection. If so, the application
+ * should wait and try again either by using the acknowledgment
+ * callback or the polling functionality. If the close succeeds, the
+ * function returns ERR_OK.
+ *
+ * @param pcb the tcp_pcb to close
+ * @return ERR_OK if connection has been closed
+ *         another err_t if closing failed and pcb is not freed
+ */
+err_t
+tcp_close(struct tcp_pcb *pcb)
+{
+  LWIP_ASSERT_CORE_LOCKED();
+
+  LWIP_ERROR("tcp_close: invalid pcb", pcb != NULL, return ERR_ARG);
+  LWIP_DEBUGF(TCP_DEBUG, ("tcp_close: closing in "));
+
+  tcp_debug_print_state(pcb->state);
+
+  if (pcb->state != LISTEN) {
+    /* Set a flag not to receive any more data... */
+    tcp_set_flags(pcb, TF_RXCLOSED);
+  }
+  /* ... and close */
+  return tcp_close_shutdown(pcb, 1);
+}
+
+/**
+ * @ingroup tcp_raw
+ * Causes all or part of a full-duplex connection of this PCB to be shut down.
+ * This doesn't deallocate the PCB unless shutting down both sides!
+ * Shutting down both sides is the same as calling tcp_close, so if it succeeds
+ * (i.e. returns ER_OK), the PCB must not be referenced any more!
+ *
+ * @param pcb PCB to shutdown
+ * @param shut_rx shut down receive side if this is != 0
+ * @param shut_tx shut down send side if this is != 0
+ * @return ERR_OK if shutdown succeeded (or the PCB has already been shut down)
+ *         another err_t on error.
+ */
+err_t
+tcp_shutdown(struct tcp_pcb *pcb, int shut_rx, int shut_tx)
+{
+  LWIP_ASSERT_CORE_LOCKED();
+
+  LWIP_ERROR("tcp_shutdown: invalid pcb", pcb != NULL, return ERR_ARG);
+
+  if (pcb->state == LISTEN) {
+    return ERR_CONN;
+  }
+  if (shut_rx) {
+    /* shut down the receive side: set a flag not to receive any more data... */
+    tcp_set_flags(pcb, TF_RXCLOSED);
+    if (shut_tx) {
+      /* shutting down the tx AND rx side is the same as closing for the raw API */
+      return tcp_close_shutdown(pcb, 1);
+    }
+    /* ... and free buffered data */
+    if (pcb->refused_data != NULL) {
+      pbuf_free(pcb->refused_data);
+      pcb->refused_data = NULL;
+    }
+  }
+  if (shut_tx) {
+    /* This can't happen twice since if it succeeds, the pcb's state is changed.
+       Only close in these states as the others directly deallocate the PCB */
+    switch (pcb->state) {
+      case SYN_RCVD:
+      case ESTABLISHED:
+      case CLOSE_WAIT:
+        return tcp_close_shutdown(pcb, (u8_t)shut_rx);
+      default:
+        /* Not (yet?) connected, cannot shutdown the TX side as that would bring us
+          into CLOSED state, where the PCB is deallocated. */
+        return ERR_CONN;
+    }
+  }
+  return ERR_OK;
+}
+
+/**
+ * Abandons a connection and optionally sends a RST to the remote
+ * host.  Deletes the local protocol control block. This is done when
+ * a connection is killed because of shortage of memory.
+ *
+ * @param pcb the tcp_pcb to abort
+ * @param reset boolean to indicate whether a reset should be sent
+ */
+void
+tcp_abandon(struct tcp_pcb *pcb, int reset)
+{
+  u32_t seqno, ackno;
+#if LWIP_CALLBACK_API
+  tcp_err_fn errf;
+#endif /* LWIP_CALLBACK_API */
+  void *errf_arg;
+
+  LWIP_ASSERT_CORE_LOCKED();
+
+  LWIP_ERROR("tcp_abandon: invalid pcb", pcb != NULL, return);
+
+  /* pcb->state LISTEN not allowed here */
+  LWIP_ASSERT("don't call tcp_abort/tcp_abandon for listen-pcbs",
+              pcb->state != LISTEN);
+  /* Figure out on which TCP PCB list we are, and remove us. If we
+     are in an active state, call the receive function associated with
+     the PCB with a NULL argument, and send an RST to the remote end. */
+  if (pcb->state == TIME_WAIT) {
+    tcp_pcb_remove(&tcp_tw_pcbs, pcb);
+    tcp_free(pcb);
+  } else {
+    int send_rst = 0;
+    u16_t local_port = 0;
+    enum tcp_state last_state;
+    seqno = pcb->snd_nxt;
+    ackno = pcb->rcv_nxt;
+#if LWIP_CALLBACK_API
+    errf = pcb->errf;
+#endif /* LWIP_CALLBACK_API */
+    errf_arg = pcb->callback_arg;
+    if (pcb->state == CLOSED) {
+      if (pcb->local_port != 0) {
+        /* bound, not yet opened */
+        TCP_RMV(&tcp_bound_pcbs, pcb);
+      }
+    } else {
+      send_rst = reset;
+      local_port = pcb->local_port;
+      TCP_PCB_REMOVE_ACTIVE(pcb);
+    }
+    if (pcb->unacked != NULL) {
+      tcp_segs_free(pcb->unacked);
+    }
+    if (pcb->unsent != NULL) {
+      tcp_segs_free(pcb->unsent);
+    }
+#if TCP_QUEUE_OOSEQ
+    if (pcb->ooseq != NULL) {
+      tcp_segs_free(pcb->ooseq);
+    }
+#endif /* TCP_QUEUE_OOSEQ */
+    tcp_backlog_accepted(pcb);
+    if (send_rst) {
+      LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_abandon: sending RST\n"));
+      tcp_rst(pcb, seqno, ackno, &pcb->local_ip, &pcb->remote_ip, local_port, pcb->remote_port);
+    }
+    last_state = pcb->state;
+    tcp_free(pcb);
+    TCP_EVENT_ERR(last_state, errf, errf_arg, ERR_ABRT);
+  }
+}
+
+/**
+ * @ingroup tcp_raw
+ * Aborts the connection by sending a RST (reset) segment to the remote
+ * host. The pcb is deallocated. This function never fails.
+ *
+ * ATTENTION: When calling this from one of the TCP callbacks, make
+ * sure you always return ERR_ABRT (and never return ERR_ABRT otherwise
+ * or you will risk accessing deallocated memory or memory leaks!
+ *
+ * @param pcb the tcp pcb to abort
+ */
+void
+tcp_abort(struct tcp_pcb *pcb)
+{
+  tcp_abandon(pcb, 1);
+}
+
+/**
+ * @ingroup tcp_raw
+ * Binds the connection to a local port number and IP address. If the
+ * IP address is not given (i.e., ipaddr == IP_ANY_TYPE), the connection is
+ * bound to all local IP addresses.
+ * If another connection is bound to the same port, the function will
+ * return ERR_USE, otherwise ERR_OK is returned.
+ * @see MEMP_NUM_TCP_PCB_LISTEN and MEMP_NUM_TCP_PCB
+ *
+ * @param pcb the tcp_pcb to bind (no check is done whether this pcb is
+ *        already bound!)
+ * @param ipaddr the local ip address to bind to (use IPx_ADDR_ANY to bind
+ *        to any local address
+ * @param port the local port to bind to
+ * @return ERR_USE if the port is already in use
+ *         ERR_VAL if bind failed because the PCB is not in a valid state
+ *         ERR_OK if bound
+ */
+err_t
+tcp_bind(struct tcp_pcb *pcb, const ip_addr_t *ipaddr, u16_t port)
+{
+  int i;
+  int max_pcb_list = NUM_TCP_PCB_LISTS;
+  struct tcp_pcb *cpcb;
+#if LWIP_IPV6 && LWIP_IPV6_SCOPES
+  ip_addr_t zoned_ipaddr;
+#endif /* LWIP_IPV6 && LWIP_IPV6_SCOPES */
+
+  LWIP_ASSERT_CORE_LOCKED();
+
+#if LWIP_IPV4
+  /* Don't propagate NULL pointer (IPv4 ANY) to subsequent functions */
+  if (ipaddr == NULL) {
+    ipaddr = IP4_ADDR_ANY;
+  }
+#else /* LWIP_IPV4 */
+  LWIP_ERROR("tcp_bind: invalid ipaddr", ipaddr != NULL, return ERR_ARG);
+#endif /* LWIP_IPV4 */
+
+  LWIP_ERROR("tcp_bind: invalid pcb", pcb != NULL, return ERR_ARG);
+
+  LWIP_ERROR("tcp_bind: can only bind in state CLOSED", pcb->state == CLOSED, return ERR_VAL);
+
+#if SO_REUSE
+  /* Unless the REUSEADDR flag is set,
+     we have to check the pcbs in TIME-WAIT state, also.
+     We do not dump TIME_WAIT pcb's; they can still be matched by incoming
+     packets using both local and remote IP addresses and ports to distinguish.
+   */
+  if (ip_get_option(pcb, SOF_REUSEADDR)) {
+    max_pcb_list = NUM_TCP_PCB_LISTS_NO_TIME_WAIT;
+  }
+#endif /* SO_REUSE */
+
+#if LWIP_IPV6 && LWIP_IPV6_SCOPES
+  /* If the given IP address should have a zone but doesn't, assign one now.
+   * This is legacy support: scope-aware callers should always provide properly
+   * zoned source addresses. Do the zone selection before the address-in-use
+   * check below; as such we have to make a temporary copy of the address. */
+  if (IP_IS_V6(ipaddr) && ip6_addr_lacks_zone(ip_2_ip6(ipaddr), IP6_UNICAST)) {
+    ip_addr_copy(zoned_ipaddr, *ipaddr);
+    ip6_addr_select_zone(ip_2_ip6(&zoned_ipaddr), ip_2_ip6(&zoned_ipaddr));
+    ipaddr = &zoned_ipaddr;
+  }
+#endif /* LWIP_IPV6 && LWIP_IPV6_SCOPES */
+
+  if (port == 0) {
+    port = tcp_new_port();
+    if (port == 0) {
+      return ERR_BUF;
+    }
+  } else {
+    /* Check if the address already is in use (on all lists) */
+    for (i = 0; i < max_pcb_list; i++) {
+      for (cpcb = *tcp_pcb_lists[i]; cpcb != NULL; cpcb = cpcb->next) {
+        if (cpcb->local_port == port) {
+#if SO_REUSE
+          /* Omit checking for the same port if both pcbs have REUSEADDR set.
+             For SO_REUSEADDR, the duplicate-check for a 5-tuple is done in
+             tcp_connect. */
+          if (!ip_get_option(pcb, SOF_REUSEADDR) ||
+              !ip_get_option(cpcb, SOF_REUSEADDR))
+#endif /* SO_REUSE */
+          {
+            /* @todo: check accept_any_ip_version */
+            if ((IP_IS_V6(ipaddr) == IP_IS_V6_VAL(cpcb->local_ip)) &&
+                (ip_addr_isany(&cpcb->local_ip) ||
+                 ip_addr_isany(ipaddr) ||
+                 ip_addr_eq(&cpcb->local_ip, ipaddr))) {
+              return ERR_USE;
+            }
+          }
+        }
+      }
+    }
+  }
+
+  if (!ip_addr_isany(ipaddr)
+#if LWIP_IPV4 && LWIP_IPV6
+      || (IP_GET_TYPE(ipaddr) != IP_GET_TYPE(&pcb->local_ip))
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+     ) {
+    ip_addr_set(&pcb->local_ip, ipaddr);
+  }
+  pcb->local_port = port;
+  TCP_REG(&tcp_bound_pcbs, pcb);
+  LWIP_DEBUGF(TCP_DEBUG, ("tcp_bind: bind to port %"U16_F"\n", port));
+  return ERR_OK;
+}
+
+/**
+ * @ingroup tcp_raw
+ * Binds the connection to a netif and IP address.
+ * After calling this function, all packets received via this PCB
+ * are guaranteed to have come in via the specified netif, and all
+ * outgoing packets will go out via the specified netif.
+ *
+ * @param pcb the tcp_pcb to bind.
+ * @param netif the netif to bind to. Can be NULL.
+ */
+void
+tcp_bind_netif(struct tcp_pcb *pcb, const struct netif *netif)
+{
+  LWIP_ASSERT_CORE_LOCKED();
+  if (netif != NULL) {
+    pcb->netif_idx = netif_get_index(netif);
+  } else {
+    pcb->netif_idx = NETIF_NO_INDEX;
+  }
+}
+
+#if LWIP_CALLBACK_API
+/**
+ * Default accept callback if no accept callback is specified by the user.
+ */
+static err_t
+tcp_accept_null(void *arg, struct tcp_pcb *pcb, err_t err)
+{
+  LWIP_UNUSED_ARG(arg);
+  LWIP_UNUSED_ARG(err);
+
+  LWIP_ASSERT("tcp_accept_null: invalid pcb", pcb != NULL);
+
+  tcp_abort(pcb);
+
+  return ERR_ABRT;
+}
+#endif /* LWIP_CALLBACK_API */
+
+/**
+ * @ingroup tcp_raw
+ * Set the state of the connection to be LISTEN, which means that it
+ * is able to accept incoming connections. The protocol control block
+ * is reallocated in order to consume less memory. Setting the
+ * connection to LISTEN is an irreversible process.
+ * When an incoming connection is accepted, the function specified with
+ * the tcp_accept() function will be called. The pcb has to be bound
+ * to a local port with the tcp_bind() function.
+ *
+ * The tcp_listen() function returns a new connection identifier, and
+ * the one passed as an argument to the function will be
+ * deallocated. The reason for this behavior is that less memory is
+ * needed for a connection that is listening, so tcp_listen() will
+ * reclaim the memory needed for the original connection and allocate a
+ * new smaller memory block for the listening connection.
+ *
+ * tcp_listen() may return NULL if no memory was available for the
+ * listening connection. If so, the memory associated with the pcb
+ * passed as an argument to tcp_listen() will not be deallocated.
+ *
+ * The backlog limits the number of outstanding connections
+ * in the listen queue to the value specified by the backlog argument.
+ * To use it, your need to set TCP_LISTEN_BACKLOG=1 in your lwipopts.h.
+ *
+ * @param pcb the original tcp_pcb
+ * @param backlog the incoming connections queue limit
+ * @return tcp_pcb used for listening, consumes less memory.
+ *
+ * @note The original tcp_pcb is freed. This function therefore has to be
+ *       called like this:
+ *             tpcb = tcp_listen_with_backlog(tpcb, backlog);
+ */
+struct tcp_pcb *
+tcp_listen_with_backlog(struct tcp_pcb *pcb, u8_t backlog)
+{
+  LWIP_ASSERT_CORE_LOCKED();
+  return tcp_listen_with_backlog_and_err(pcb, backlog, NULL);
+}
+
+/**
+ * @ingroup tcp_raw
+ * Set the state of the connection to be LISTEN, which means that it
+ * is able to accept incoming connections. The protocol control block
+ * is reallocated in order to consume less memory. Setting the
+ * connection to LISTEN is an irreversible process.
+ *
+ * @param pcb the original tcp_pcb
+ * @param backlog the incoming connections queue limit
+ * @param err when NULL is returned, this contains the error reason
+ * @return tcp_pcb used for listening, consumes less memory.
+ *
+ * @note The original tcp_pcb is freed. This function therefore has to be
+ *       called like this:
+ *             tpcb = tcp_listen_with_backlog_and_err(tpcb, backlog, &err);
+ */
+struct tcp_pcb *
+tcp_listen_with_backlog_and_err(struct tcp_pcb *pcb, u8_t backlog, err_t *err)
+{
+  struct tcp_pcb_listen *lpcb = NULL;
+  err_t res;
+
+  LWIP_UNUSED_ARG(backlog);
+
+  LWIP_ASSERT_CORE_LOCKED();
+
+  LWIP_ERROR("tcp_listen_with_backlog_and_err: invalid pcb", pcb != NULL, res = ERR_ARG; goto done);
+  LWIP_ERROR("tcp_listen_with_backlog_and_err: pcb already connected", pcb->state == CLOSED, res = ERR_CLSD; goto done);
+
+  /* already listening? */
+  if (pcb->state == LISTEN) {
+    lpcb = (struct tcp_pcb_listen *)pcb;
+    res = ERR_ALREADY;
+    goto done;
+  }
+#if SO_REUSE
+  if (ip_get_option(pcb, SOF_REUSEADDR)) {
+    /* Since SOF_REUSEADDR allows reusing a local address before the pcb's usage
+       is declared (listen-/connection-pcb), we have to make sure now that
+       this port is only used once for every local IP. */
+    for (lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) {
+      if ((lpcb->local_port == pcb->local_port) &&
+          ip_addr_eq(&lpcb->local_ip, &pcb->local_ip)) {
+        /* this address/port is already used */
+        lpcb = NULL;
+        res = ERR_USE;
+        goto done;
+      }
+    }
+  }
+#endif /* SO_REUSE */
+  lpcb = (struct tcp_pcb_listen *)memp_malloc(MEMP_TCP_PCB_LISTEN);
+  if (lpcb == NULL) {
+    res = ERR_MEM;
+    goto done;
+  }
+  lpcb->callback_arg = pcb->callback_arg;
+  lpcb->local_port = pcb->local_port;
+  lpcb->state = LISTEN;
+  lpcb->prio = pcb->prio;
+  lpcb->so_options = pcb->so_options;
+  lpcb->netif_idx = pcb->netif_idx;
+  lpcb->ttl = pcb->ttl;
+  lpcb->tos = pcb->tos;
+#if LWIP_VLAN_PCP
+  lpcb->netif_hints.tci = pcb->netif_hints.tci;
+#endif /* LWIP_VLAN_PCP */
+#if LWIP_IPV4 && LWIP_IPV6
+  IP_SET_TYPE_VAL(lpcb->remote_ip, pcb->local_ip.type);
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+  ip_addr_copy(lpcb->local_ip, pcb->local_ip);
+  if (pcb->local_port != 0) {
+    TCP_RMV(&tcp_bound_pcbs, pcb);
+  }
+#if LWIP_TCP_PCB_NUM_EXT_ARGS
+  /* copy over ext_args to listening pcb  */
+  memcpy(&lpcb->ext_args, &pcb->ext_args, sizeof(pcb->ext_args));
+#endif
+  tcp_free(pcb);
+#if LWIP_CALLBACK_API
+  lpcb->accept = tcp_accept_null;
+#endif /* LWIP_CALLBACK_API */
+#if TCP_LISTEN_BACKLOG
+  lpcb->accepts_pending = 0;
+  tcp_backlog_set(lpcb, backlog);
+#endif /* TCP_LISTEN_BACKLOG */
+  TCP_REG(&tcp_listen_pcbs.pcbs, (struct tcp_pcb *)lpcb);
+  res = ERR_OK;
+done:
+  if (err != NULL) {
+    *err = res;
+  }
+  return (struct tcp_pcb *)lpcb;
+}
+
+/**
+ * Update the state that tracks the available window space to advertise.
+ *
+ * Returns how much extra window would be advertised if we sent an
+ * update now.
+ */
+u32_t
+tcp_update_rcv_ann_wnd(struct tcp_pcb *pcb)
+{
+  u32_t new_right_edge;
+
+  LWIP_ASSERT("tcp_update_rcv_ann_wnd: invalid pcb", pcb != NULL);
+  new_right_edge = pcb->rcv_nxt + pcb->rcv_wnd;
+
+  if (TCP_SEQ_GEQ(new_right_edge, pcb->rcv_ann_right_edge + LWIP_MIN((TCP_WND / 2), pcb->mss))) {
+    /* we can advertise more window */
+    pcb->rcv_ann_wnd = pcb->rcv_wnd;
+    return new_right_edge - pcb->rcv_ann_right_edge;
+  } else {
+    if (TCP_SEQ_GT(pcb->rcv_nxt, pcb->rcv_ann_right_edge)) {
+      /* Can happen due to other end sending out of advertised window,
+       * but within actual available (but not yet advertised) window */
+      pcb->rcv_ann_wnd = 0;
+    } else {
+      /* keep the right edge of window constant */
+      u32_t new_rcv_ann_wnd = pcb->rcv_ann_right_edge - pcb->rcv_nxt;
+#if !LWIP_WND_SCALE
+      LWIP_ASSERT("new_rcv_ann_wnd <= 0xffff", new_rcv_ann_wnd <= 0xffff);
+#endif
+      pcb->rcv_ann_wnd = (tcpwnd_size_t)new_rcv_ann_wnd;
+    }
+    return 0;
+  }
+}
+
+/**
+ * @ingroup tcp_raw
+ * This function should be called by the application when it has
+ * processed the data. The purpose is to advertise a larger window
+ * when the data has been processed.
+ *
+ * @param pcb the tcp_pcb for which data is read
+ * @param len the amount of bytes that have been read by the application
+ */
+void
+tcp_recved(struct tcp_pcb *pcb, u16_t len)
+{
+  u32_t wnd_inflation;
+  tcpwnd_size_t rcv_wnd;
+
+  LWIP_ASSERT_CORE_LOCKED();
+
+  LWIP_ERROR("tcp_recved: invalid pcb", pcb != NULL, return);
+
+  /* pcb->state LISTEN not allowed here */
+  LWIP_ASSERT("don't call tcp_recved for listen-pcbs",
+              pcb->state != LISTEN);
+
+  rcv_wnd = (tcpwnd_size_t)(pcb->rcv_wnd + len);
+  if ((rcv_wnd > TCP_WND_MAX(pcb)) || (rcv_wnd < pcb->rcv_wnd)) {
+    /* window got too big or tcpwnd_size_t overflow */
+    LWIP_DEBUGF(TCP_DEBUG, ("tcp_recved: window got too big or tcpwnd_size_t overflow\n"));
+    pcb->rcv_wnd = TCP_WND_MAX(pcb);
+  } else  {
+    pcb->rcv_wnd = rcv_wnd;
+  }
+
+  wnd_inflation = tcp_update_rcv_ann_wnd(pcb);
+
+  /* If the change in the right edge of window is significant (default
+   * watermark is TCP_WND/4), then send an explicit update now.
+   * Otherwise wait for a packet to be sent in the normal course of
+   * events (or more window to be available later) */
+  if (wnd_inflation >= TCP_WND_UPDATE_THRESHOLD) {
+    tcp_ack_now(pcb);
+    tcp_output(pcb);
+  }
+
+  LWIP_DEBUGF(TCP_DEBUG, ("tcp_recved: received %"U16_F" bytes, wnd %"TCPWNDSIZE_F" (%"TCPWNDSIZE_F").\n",
+                          len, pcb->rcv_wnd, (u16_t)(TCP_WND_MAX(pcb) - pcb->rcv_wnd)));
+}
+
+/**
+ * Allocate a new local TCP port.
+ *
+ * @return a new (free) local TCP port number
+ */
+static u16_t
+tcp_new_port(void)
+{
+  u8_t i;
+  u16_t n = 0;
+  struct tcp_pcb *pcb;
+
+again:
+  tcp_port++;
+  if (tcp_port == TCP_LOCAL_PORT_RANGE_END) {
+    tcp_port = TCP_LOCAL_PORT_RANGE_START;
+  }
+  /* Check all PCB lists. */
+  for (i = 0; i < NUM_TCP_PCB_LISTS; i++) {
+    for (pcb = *tcp_pcb_lists[i]; pcb != NULL; pcb = pcb->next) {
+      if (pcb->local_port == tcp_port) {
+        n++;
+        if (n > (TCP_LOCAL_PORT_RANGE_END - TCP_LOCAL_PORT_RANGE_START)) {
+          return 0;
+        }
+        goto again;
+      }
+    }
+  }
+  return tcp_port;
+}
+
+/**
+ * @ingroup tcp_raw
+ * Connects to another host. The function given as the "connected"
+ * argument will be called when the connection has been established.
+ *  Sets up the pcb to connect to the remote host and sends the
+ * initial SYN segment which opens the connection.
+ *
+ * The tcp_connect() function returns immediately; it does not wait for
+ * the connection to be properly setup. Instead, it will call the
+ * function specified as the fourth argument (the "connected" argument)
+ * when the connection is established. If the connection could not be
+ * properly established, either because the other host refused the
+ * connection or because the other host didn't answer, the "err"
+ * callback function of this pcb (registered with tcp_err, see below)
+ * will be called.
+ *
+ * The tcp_connect() function can return ERR_MEM if no memory is
+ * available for enqueueing the SYN segment. If the SYN indeed was
+ * enqueued successfully, the tcp_connect() function returns ERR_OK.
+ *
+ * @param pcb the tcp_pcb used to establish the connection
+ * @param ipaddr the remote ip address to connect to
+ * @param port the remote tcp port to connect to
+ * @param connected callback function to call when connected (on error,
+                    the err callback will be called)
+ * @return ERR_VAL if invalid arguments are given
+ *         ERR_OK if connect request has been sent
+ *         other err_t values if connect request couldn't be sent
+ */
+err_t
+tcp_connect(struct tcp_pcb *pcb, const ip_addr_t *ipaddr, u16_t port,
+            tcp_connected_fn connected)
+{
+  struct netif *netif = NULL;
+  err_t ret;
+  u32_t iss;
+  u16_t old_local_port;
+
+  LWIP_ASSERT_CORE_LOCKED();
+
+  LWIP_ERROR("tcp_connect: invalid pcb", pcb != NULL, return ERR_ARG);
+  LWIP_ERROR("tcp_connect: invalid ipaddr", ipaddr != NULL, return ERR_ARG);
+
+  LWIP_ERROR("tcp_connect: can only connect from state CLOSED", pcb->state == CLOSED, return ERR_ISCONN);
+
+  LWIP_DEBUGF(TCP_DEBUG, ("tcp_connect to port %"U16_F"\n", port));
+  ip_addr_set(&pcb->remote_ip, ipaddr);
+  pcb->remote_port = port;
+
+  if (pcb->netif_idx != NETIF_NO_INDEX) {
+    netif = netif_get_by_index(pcb->netif_idx);
+  } else {
+    /* check if we have a route to the remote host */
+    netif = ip_route(&pcb->local_ip, &pcb->remote_ip);
+  }
+  if (netif == NULL) {
+    /* Don't even try to send a SYN packet if we have no route since that will fail. */
+    return ERR_RTE;
+  }
+
+  /* check if local IP has been assigned to pcb, if not, get one */
+  if (ip_addr_isany(&pcb->local_ip)) {
+    const ip_addr_t *local_ip = ip_netif_get_local_ip(netif, ipaddr);
+    if (local_ip == NULL) {
+      return ERR_RTE;
+    }
+    ip_addr_copy(pcb->local_ip, *local_ip);
+  }
+
+#if LWIP_IPV6 && LWIP_IPV6_SCOPES
+  /* If the given IP address should have a zone but doesn't, assign one now.
+   * Given that we already have the target netif, this is easy and cheap. */
+  if (IP_IS_V6(&pcb->remote_ip) &&
+      ip6_addr_lacks_zone(ip_2_ip6(&pcb->remote_ip), IP6_UNICAST)) {
+    ip6_addr_assign_zone(ip_2_ip6(&pcb->remote_ip), IP6_UNICAST, netif);
+  }
+#endif /* LWIP_IPV6 && LWIP_IPV6_SCOPES */
+
+  old_local_port = pcb->local_port;
+  if (pcb->local_port == 0) {
+    pcb->local_port = tcp_new_port();
+    if (pcb->local_port == 0) {
+      return ERR_BUF;
+    }
+  } else {
+#if SO_REUSE
+    if (ip_get_option(pcb, SOF_REUSEADDR)) {
+      /* Since SOF_REUSEADDR allows reusing a local address, we have to make sure
+         now that the 5-tuple is unique. */
+      struct tcp_pcb *cpcb;
+      int i;
+      /* Don't check listen- and bound-PCBs, check active- and TIME-WAIT PCBs. */
+      for (i = 2; i < NUM_TCP_PCB_LISTS; i++) {
+        for (cpcb = *tcp_pcb_lists[i]; cpcb != NULL; cpcb = cpcb->next) {
+          if ((cpcb->local_port == pcb->local_port) &&
+              (cpcb->remote_port == port) &&
+              ip_addr_eq(&cpcb->local_ip, &pcb->local_ip) &&
+              ip_addr_eq(&cpcb->remote_ip, ipaddr)) {
+            /* linux returns EISCONN here, but ERR_USE should be OK for us */
+            return ERR_USE;
+          }
+        }
+      }
+    }
+#endif /* SO_REUSE */
+  }
+
+  iss = tcp_next_iss(pcb);
+  pcb->rcv_nxt = 0;
+  pcb->snd_nxt = iss;
+  pcb->lastack = iss - 1;
+  pcb->snd_wl2 = iss - 1;
+  pcb->snd_lbb = iss - 1;
+  /* Start with a window that does not need scaling. When window scaling is
+     enabled and used, the window is enlarged when both sides agree on scaling. */
+  pcb->rcv_wnd = pcb->rcv_ann_wnd = TCPWND_MIN16(TCP_WND);
+  pcb->rcv_ann_right_edge = pcb->rcv_nxt;
+  pcb->snd_wnd = TCP_WND;
+  /* As initial send MSS, we use TCP_MSS but limit it to 536.
+     The send MSS is updated when an MSS option is received. */
+  pcb->mss = INITIAL_MSS;
+#if TCP_CALCULATE_EFF_SEND_MSS
+  pcb->mss = tcp_eff_send_mss_netif(pcb->mss, netif, &pcb->remote_ip);
+#endif /* TCP_CALCULATE_EFF_SEND_MSS */
+  pcb->cwnd = 1;
+#if LWIP_CALLBACK_API
+  pcb->connected = connected;
+#else /* LWIP_CALLBACK_API */
+  LWIP_UNUSED_ARG(connected);
+#endif /* LWIP_CALLBACK_API */
+
+  /* Send a SYN together with the MSS option. */
+  ret = tcp_enqueue_flags(pcb, TCP_SYN);
+  if (ret == ERR_OK) {
+    /* SYN segment was enqueued, changed the pcbs state now */
+    pcb->state = SYN_SENT;
+    if (old_local_port != 0) {
+      TCP_RMV(&tcp_bound_pcbs, pcb);
+    }
+    TCP_REG_ACTIVE(pcb);
+    MIB2_STATS_INC(mib2.tcpactiveopens);
+
+    tcp_output(pcb);
+  }
+  return ret;
+}
+
+/**
+ * Called every 500 ms and implements the retransmission timer and the timer that
+ * removes PCBs that have been in TIME-WAIT for enough time. It also increments
+ * various timers such as the inactivity timer in each PCB.
+ *
+ * Automatically called from tcp_tmr().
+ */
+void
+tcp_slowtmr(void)
+{
+  struct tcp_pcb *pcb, *prev;
+  tcpwnd_size_t eff_wnd;
+  u8_t pcb_remove;      /* flag if a PCB should be removed */
+  u8_t pcb_reset;       /* flag if a RST should be sent when removing */
+  err_t err;
+
+  err = ERR_OK;
+
+  ++tcp_ticks;
+  ++tcp_timer_ctr;
+
+tcp_slowtmr_start:
+  /* Steps through all of the active PCBs. */
+  prev = NULL;
+  pcb = tcp_active_pcbs;
+  if (pcb == NULL) {
+    LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: no active pcbs\n"));
+  }
+  while (pcb != NULL) {
+    LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: processing active pcb\n"));
+    LWIP_ASSERT("tcp_slowtmr: active pcb->state != CLOSED", pcb->state != CLOSED);
+    LWIP_ASSERT("tcp_slowtmr: active pcb->state != LISTEN", pcb->state != LISTEN);
+    LWIP_ASSERT("tcp_slowtmr: active pcb->state != TIME-WAIT", pcb->state != TIME_WAIT);
+    if (pcb->last_timer == tcp_timer_ctr) {
+      /* skip this pcb, we have already processed it */
+      prev = pcb;
+      pcb = pcb->next;
+      continue;
+    }
+    pcb->last_timer = tcp_timer_ctr;
+
+    pcb_remove = 0;
+    pcb_reset = 0;
+
+    if (pcb->state == SYN_SENT && pcb->nrtx >= TCP_SYNMAXRTX) {
+      ++pcb_remove;
+      LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: max SYN retries reached\n"));
+    } else if (pcb->nrtx >= TCP_MAXRTX) {
+      ++pcb_remove;
+      LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: max DATA retries reached\n"));
+    } else {
+      if (pcb->persist_backoff > 0) {
+        LWIP_ASSERT("tcp_slowtimr: persist ticking with in-flight data", pcb->unacked == NULL);
+        LWIP_ASSERT("tcp_slowtimr: persist ticking with empty send buffer", pcb->unsent != NULL);
+        if (pcb->persist_probe >= TCP_MAXRTX) {
+          ++pcb_remove; /* max probes reached */
+        } else {
+          u8_t backoff_cnt = tcp_persist_backoff[pcb->persist_backoff - 1];
+          if (pcb->persist_cnt < backoff_cnt) {
+            pcb->persist_cnt++;
+          }
+          if (pcb->persist_cnt >= backoff_cnt) {
+            int next_slot = 1; /* increment timer to next slot */
+            /* If snd_wnd is zero, send 1 byte probes */
+            if (pcb->snd_wnd == 0) {
+              if (tcp_zero_window_probe(pcb) != ERR_OK) {
+                next_slot = 0; /* try probe again with current slot */
+              }
+              /* snd_wnd not fully closed, split unsent head and fill window */
+            } else {
+              if (tcp_split_unsent_seg(pcb, (u16_t)pcb->snd_wnd) == ERR_OK) {
+                if (tcp_output(pcb) == ERR_OK) {
+                  /* sending will cancel persist timer, else retry with current slot */
+                  next_slot = 0;
+                }
+              }
+            }
+            if (next_slot) {
+              pcb->persist_cnt = 0;
+              if (pcb->persist_backoff < sizeof(tcp_persist_backoff)) {
+                pcb->persist_backoff++;
+              }
+            }
+          }
+        }
+      } else {
+        /* Increase the retransmission timer if it is running */
+        if ((pcb->rtime >= 0) && (pcb->rtime < 0x7FFF)) {
+          ++pcb->rtime;
+        }
+
+        if (pcb->rtime >= pcb->rto) {
+          /* Time for a retransmission. */
+          LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_slowtmr: rtime %"S16_F
+                                      " pcb->rto %"S16_F"\n",
+                                      pcb->rtime, pcb->rto));
+          /* If prepare phase fails but we have unsent data but no unacked data,
+             still execute the backoff calculations below, as this means we somehow
+             failed to send segment. */
+          if ((tcp_rexmit_rto_prepare(pcb) == ERR_OK) || ((pcb->unacked == NULL) && (pcb->unsent != NULL))) {
+            /* Double retransmission time-out unless we are trying to
+             * connect to somebody (i.e., we are in SYN_SENT). */
+            if (pcb->state != SYN_SENT) {
+              u8_t backoff_idx = LWIP_MIN(pcb->nrtx, sizeof(tcp_backoff) - 1);
+              int calc_rto = ((pcb->sa >> 3) + pcb->sv) << tcp_backoff[backoff_idx];
+              pcb->rto = (s16_t)LWIP_MIN(calc_rto, 0x7FFF);
+            }
+
+            /* Reset the retransmission timer. */
+            pcb->rtime = 0;
+
+            /* Reduce congestion window and ssthresh. */
+            eff_wnd = LWIP_MIN(pcb->cwnd, pcb->snd_wnd);
+            pcb->ssthresh = eff_wnd >> 1;
+            if (pcb->ssthresh < (tcpwnd_size_t)(pcb->mss << 1)) {
+              pcb->ssthresh = (tcpwnd_size_t)(pcb->mss << 1);
+            }
+            pcb->cwnd = pcb->mss;
+            LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_slowtmr: cwnd %"TCPWNDSIZE_F
+                                         " ssthresh %"TCPWNDSIZE_F"\n",
+                                         pcb->cwnd, pcb->ssthresh));
+            pcb->bytes_acked = 0;
+
+            /* The following needs to be called AFTER cwnd is set to one
+               mss - STJ */
+            tcp_rexmit_rto_commit(pcb);
+          }
+        }
+      }
+    }
+    /* Check if this PCB has stayed too long in FIN-WAIT-2 */
+    if (pcb->state == FIN_WAIT_2) {
+      /* If this PCB is in FIN_WAIT_2 because of SHUT_WR don't let it time out. */
+      if (pcb->flags & TF_RXCLOSED) {
+        /* PCB was fully closed (either through close() or SHUT_RDWR):
+           normal FIN-WAIT timeout handling. */
+        if ((u32_t)(tcp_ticks - pcb->tmr) >
+            TCP_FIN_WAIT_TIMEOUT / TCP_SLOW_INTERVAL) {
+          ++pcb_remove;
+          LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: removing pcb stuck in FIN-WAIT-2\n"));
+        }
+      }
+    }
+
+    /* Check if KEEPALIVE should be sent */
+    if (ip_get_option(pcb, SOF_KEEPALIVE) &&
+        ((pcb->state == ESTABLISHED) ||
+         (pcb->state == CLOSE_WAIT))) {
+      if ((u32_t)(tcp_ticks - pcb->tmr) >
+          (pcb->keep_idle + TCP_KEEP_DUR(pcb)) / TCP_SLOW_INTERVAL) {
+        LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: KEEPALIVE timeout. Aborting connection to "));
+        ip_addr_debug_print_val(TCP_DEBUG, pcb->remote_ip);
+        LWIP_DEBUGF(TCP_DEBUG, ("\n"));
+
+        ++pcb_remove;
+        ++pcb_reset;
+      } else if ((u32_t)(tcp_ticks - pcb->tmr) >
+                 (pcb->keep_idle + pcb->keep_cnt_sent * TCP_KEEP_INTVL(pcb))
+                 / TCP_SLOW_INTERVAL) {
+        err = tcp_keepalive(pcb);
+        if (err == ERR_OK) {
+          pcb->keep_cnt_sent++;
+        }
+      }
+    }
+
+    /* If this PCB has queued out of sequence data, but has been
+       inactive for too long, will drop the data (it will eventually
+       be retransmitted). */
+#if TCP_QUEUE_OOSEQ
+    if (pcb->ooseq != NULL &&
+        (tcp_ticks - pcb->tmr >= (u32_t)pcb->rto * TCP_OOSEQ_TIMEOUT)) {
+      LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_slowtmr: dropping OOSEQ queued data\n"));
+      tcp_free_ooseq(pcb);
+    }
+#endif /* TCP_QUEUE_OOSEQ */
+
+    /* Check if this PCB has stayed too long in SYN-RCVD */
+    if (pcb->state == SYN_RCVD) {
+      if ((u32_t)(tcp_ticks - pcb->tmr) >
+          TCP_SYN_RCVD_TIMEOUT / TCP_SLOW_INTERVAL) {
+        ++pcb_remove;
+        LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: removing pcb stuck in SYN-RCVD\n"));
+      }
+    }
+
+    /* Check if this PCB has stayed too long in LAST-ACK */
+    if (pcb->state == LAST_ACK) {
+      if ((u32_t)(tcp_ticks - pcb->tmr) > 2 * TCP_MSL / TCP_SLOW_INTERVAL) {
+        ++pcb_remove;
+        LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: removing pcb stuck in LAST-ACK\n"));
+      }
+    }
+
+    /* If the PCB should be removed, do it. */
+    if (pcb_remove) {
+      struct tcp_pcb *pcb2;
+#if LWIP_CALLBACK_API
+      tcp_err_fn err_fn = pcb->errf;
+#endif /* LWIP_CALLBACK_API */
+      void *err_arg;
+      enum tcp_state last_state;
+      tcp_pcb_purge(pcb);
+      /* Remove PCB from tcp_active_pcbs list. */
+      if (prev != NULL) {
+        LWIP_ASSERT("tcp_slowtmr: middle tcp != tcp_active_pcbs", pcb != tcp_active_pcbs);
+        prev->next = pcb->next;
+      } else {
+        /* This PCB was the first. */
+        LWIP_ASSERT("tcp_slowtmr: first pcb == tcp_active_pcbs", tcp_active_pcbs == pcb);
+        tcp_active_pcbs = pcb->next;
+      }
+
+      if (pcb_reset) {
+        tcp_rst(pcb, pcb->snd_nxt, pcb->rcv_nxt, &pcb->local_ip, &pcb->remote_ip,
+                pcb->local_port, pcb->remote_port);
+      }
+
+      err_arg = pcb->callback_arg;
+      last_state = pcb->state;
+      pcb2 = pcb;
+      pcb = pcb->next;
+      tcp_free(pcb2);
+
+      tcp_active_pcbs_changed = 0;
+      TCP_EVENT_ERR(last_state, err_fn, err_arg, ERR_ABRT);
+      if (tcp_active_pcbs_changed) {
+        goto tcp_slowtmr_start;
+      }
+    } else {
+      /* get the 'next' element now and work with 'prev' below (in case of abort) */
+      prev = pcb;
+      pcb = pcb->next;
+
+      /* We check if we should poll the connection. */
+      ++prev->polltmr;
+      if (prev->polltmr >= prev->pollinterval) {
+        prev->polltmr = 0;
+        LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: polling application\n"));
+        tcp_active_pcbs_changed = 0;
+        TCP_EVENT_POLL(prev, err);
+        if (tcp_active_pcbs_changed) {
+          goto tcp_slowtmr_start;
+        }
+        /* if err == ERR_ABRT, 'prev' is already deallocated */
+        if (err == ERR_OK) {
+          tcp_output(prev);
+        }
+      }
+    }
+  }
+
+
+  /* Steps through all of the TIME-WAIT PCBs. */
+  prev = NULL;
+  pcb = tcp_tw_pcbs;
+  while (pcb != NULL) {
+    LWIP_ASSERT("tcp_slowtmr: TIME-WAIT pcb->state == TIME-WAIT", pcb->state == TIME_WAIT);
+    pcb_remove = 0;
+
+    /* Check if this PCB has stayed long enough in TIME-WAIT */
+    if ((u32_t)(tcp_ticks - pcb->tmr) > 2 * TCP_MSL / TCP_SLOW_INTERVAL) {
+      ++pcb_remove;
+    }
+
+    /* If the PCB should be removed, do it. */
+    if (pcb_remove) {
+      struct tcp_pcb *pcb2;
+      tcp_pcb_purge(pcb);
+      /* Remove PCB from tcp_tw_pcbs list. */
+      if (prev != NULL) {
+        LWIP_ASSERT("tcp_slowtmr: middle tcp != tcp_tw_pcbs", pcb != tcp_tw_pcbs);
+        prev->next = pcb->next;
+      } else {
+        /* This PCB was the first. */
+        LWIP_ASSERT("tcp_slowtmr: first pcb == tcp_tw_pcbs", tcp_tw_pcbs == pcb);
+        tcp_tw_pcbs = pcb->next;
+      }
+      pcb2 = pcb;
+      pcb = pcb->next;
+      tcp_free(pcb2);
+    } else {
+      prev = pcb;
+      pcb = pcb->next;
+    }
+  }
+}
+
+/**
+ * Is called every TCP_FAST_INTERVAL (250 ms) and process data previously
+ * "refused" by upper layer (application) and sends delayed ACKs or pending FINs.
+ *
+ * Automatically called from tcp_tmr().
+ */
+void
+tcp_fasttmr(void)
+{
+  struct tcp_pcb *pcb;
+
+  ++tcp_timer_ctr;
+
+tcp_fasttmr_start:
+  pcb = tcp_active_pcbs;
+
+  while (pcb != NULL) {
+    if (pcb->last_timer != tcp_timer_ctr) {
+      struct tcp_pcb *next;
+      pcb->last_timer = tcp_timer_ctr;
+      /* send delayed ACKs */
+      if (pcb->flags & TF_ACK_DELAY) {
+        LWIP_DEBUGF(TCP_DEBUG, ("tcp_fasttmr: delayed ACK\n"));
+        tcp_ack_now(pcb);
+        tcp_output(pcb);
+        tcp_clear_flags(pcb, TF_ACK_DELAY | TF_ACK_NOW);
+      }
+      /* send pending FIN */
+      if (pcb->flags & TF_CLOSEPEND) {
+        LWIP_DEBUGF(TCP_DEBUG, ("tcp_fasttmr: pending FIN\n"));
+        tcp_clear_flags(pcb, TF_CLOSEPEND);
+        tcp_close_shutdown_fin(pcb);
+      }
+
+      next = pcb->next;
+
+      /* If there is data which was previously "refused" by upper layer */
+      if (pcb->refused_data != NULL) {
+        tcp_active_pcbs_changed = 0;
+        tcp_process_refused_data(pcb);
+        if (tcp_active_pcbs_changed) {
+          /* application callback has changed the pcb list: restart the loop */
+          goto tcp_fasttmr_start;
+        }
+      }
+      pcb = next;
+    } else {
+      pcb = pcb->next;
+    }
+  }
+}
+
+/** Call tcp_output for all active pcbs that have TF_NAGLEMEMERR set */
+void
+tcp_txnow(void)
+{
+  struct tcp_pcb *pcb;
+
+  for (pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
+    if (pcb->flags & TF_NAGLEMEMERR) {
+      tcp_output(pcb);
+    }
+  }
+}
+
+/** Pass pcb->refused_data to the recv callback */
+err_t
+tcp_process_refused_data(struct tcp_pcb *pcb)
+{
+#if TCP_QUEUE_OOSEQ && LWIP_WND_SCALE
+  struct pbuf *rest;
+#endif /* TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */
+
+  LWIP_ERROR("tcp_process_refused_data: invalid pcb", pcb != NULL, return ERR_ARG);
+
+#if TCP_QUEUE_OOSEQ && LWIP_WND_SCALE
+  while (pcb->refused_data != NULL)
+#endif /* TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */
+  {
+    err_t err;
+    u8_t refused_flags = pcb->refused_data->flags;
+    /* set pcb->refused_data to NULL in case the callback frees it and then
+       closes the pcb */
+    struct pbuf *refused_data = pcb->refused_data;
+#if TCP_QUEUE_OOSEQ && LWIP_WND_SCALE
+    pbuf_split_64k(refused_data, &rest);
+    pcb->refused_data = rest;
+#else /* TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */
+    pcb->refused_data = NULL;
+#endif /* TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */
+    /* Notify again application with data previously received. */
+    LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: notify kept packet\n"));
+    TCP_EVENT_RECV(pcb, refused_data, ERR_OK, err);
+    if (err == ERR_OK) {
+      /* did refused_data include a FIN? */
+      if ((refused_flags & PBUF_FLAG_TCP_FIN)
+#if TCP_QUEUE_OOSEQ && LWIP_WND_SCALE
+          && (rest == NULL)
+#endif /* TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */
+         ) {
+        /* correct rcv_wnd as the application won't call tcp_recved()
+           for the FIN's seqno */
+        if (pcb->rcv_wnd != TCP_WND_MAX(pcb)) {
+          pcb->rcv_wnd++;
+        }
+        TCP_EVENT_CLOSED(pcb, err);
+        if (err == ERR_ABRT) {
+          return ERR_ABRT;
+        }
+      }
+    } else if (err == ERR_ABRT) {
+      /* if err == ERR_ABRT, 'pcb' is already deallocated */
+      /* Drop incoming packets because pcb is "full" (only if the incoming
+         segment contains data). */
+      LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: drop incoming packets, because pcb is \"full\"\n"));
+      return ERR_ABRT;
+    } else {
+      /* data is still refused, pbuf is still valid (go on for ACK-only packets) */
+#if TCP_QUEUE_OOSEQ && LWIP_WND_SCALE
+      if (rest != NULL) {
+        pbuf_cat(refused_data, rest);
+      }
+#endif /* TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */
+      pcb->refused_data = refused_data;
+      return ERR_INPROGRESS;
+    }
+  }
+  return ERR_OK;
+}
+
+/**
+ * Deallocates a list of TCP segments (tcp_seg structures).
+ *
+ * @param seg tcp_seg list of TCP segments to free
+ */
+void
+tcp_segs_free(struct tcp_seg *seg)
+{
+  while (seg != NULL) {
+    struct tcp_seg *next = seg->next;
+    tcp_seg_free(seg);
+    seg = next;
+  }
+}
+
+/**
+ * Frees a TCP segment (tcp_seg structure).
+ *
+ * @param seg single tcp_seg to free
+ */
+void
+tcp_seg_free(struct tcp_seg *seg)
+{
+  if (seg != NULL) {
+    if (seg->p != NULL) {
+      pbuf_free(seg->p);
+#if TCP_DEBUG
+      seg->p = NULL;
+#endif /* TCP_DEBUG */
+    }
+    memp_free(MEMP_TCP_SEG, seg);
+  }
+}
+
+/**
+ * @ingroup tcp
+ * Sets the priority of a connection.
+ *
+ * @param pcb the tcp_pcb to manipulate
+ * @param prio new priority
+ */
+void
+tcp_setprio(struct tcp_pcb *pcb, u8_t prio)
+{
+  LWIP_ASSERT_CORE_LOCKED();
+
+  LWIP_ERROR("tcp_setprio: invalid pcb", pcb != NULL, return);
+
+  pcb->prio = prio;
+}
+
+#if TCP_QUEUE_OOSEQ
+/**
+ * Returns a copy of the given TCP segment.
+ * The pbuf and data are not copied, only the pointers
+ *
+ * @param seg the old tcp_seg
+ * @return a copy of seg
+ */
+struct tcp_seg *
+tcp_seg_copy(struct tcp_seg *seg)
+{
+  struct tcp_seg *cseg;
+
+  LWIP_ASSERT("tcp_seg_copy: invalid seg", seg != NULL);
+
+  cseg = (struct tcp_seg *)memp_malloc(MEMP_TCP_SEG);
+  if (cseg == NULL) {
+    return NULL;
+  }
+  SMEMCPY((u8_t *)cseg, (const u8_t *)seg, sizeof(struct tcp_seg));
+  pbuf_ref(cseg->p);
+  return cseg;
+}
+#endif /* TCP_QUEUE_OOSEQ */
+
+#if LWIP_CALLBACK_API
+/**
+ * Default receive callback that is called if the user didn't register
+ * a recv callback for the pcb.
+ */
+err_t
+tcp_recv_null(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
+{
+  LWIP_UNUSED_ARG(arg);
+
+  LWIP_ERROR("tcp_recv_null: invalid pcb", pcb != NULL, return ERR_ARG);
+
+  if (p != NULL) {
+    tcp_recved(pcb, p->tot_len);
+    pbuf_free(p);
+  } else if (err == ERR_OK) {
+    return tcp_close(pcb);
+  }
+  return ERR_OK;
+}
+#endif /* LWIP_CALLBACK_API */
+
+/**
+ * Kills the oldest active connection that has a lower priority than 'prio'.
+ *
+ * @param prio minimum priority
+ */
+static void
+tcp_kill_prio(u8_t prio)
+{
+  struct tcp_pcb *pcb, *inactive;
+  u32_t inactivity;
+  u8_t mprio;
+
+  mprio = LWIP_MIN(TCP_PRIO_MAX, prio);
+
+  /* We want to kill connections with a lower prio, so bail out if
+   * supplied prio is 0 - there can never be a lower prio
+   */
+  if (mprio == 0) {
+    return;
+  }
+
+  /* We only want kill connections with a lower prio, so decrement prio by one
+   * and start searching for oldest connection with same or lower priority than mprio.
+   * We want to find the connections with the lowest possible prio, and among
+   * these the one with the longest inactivity time.
+   */
+  mprio--;
+
+  inactivity = 0;
+  inactive = NULL;
+  for (pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
+        /* lower prio is always a kill candidate */
+    if ((pcb->prio < mprio) ||
+        /* longer inactivity is also a kill candidate */
+        ((pcb->prio == mprio) && ((u32_t)(tcp_ticks - pcb->tmr) >= inactivity))) {
+      inactivity = tcp_ticks - pcb->tmr;
+      inactive   = pcb;
+      mprio      = pcb->prio;
+    }
+  }
+  if (inactive != NULL) {
+    LWIP_DEBUGF(TCP_DEBUG, ("tcp_kill_prio: killing oldest PCB %p (%"S32_F")\n",
+                            (void *)inactive, inactivity));
+    tcp_abort(inactive);
+  }
+}
+
+/**
+ * Kills the oldest connection that is in specific state.
+ * Called from tcp_alloc() for LAST_ACK and CLOSING if no more connections are available.
+ */
+static void
+tcp_kill_state(enum tcp_state state)
+{
+  struct tcp_pcb *pcb, *inactive;
+  u32_t inactivity;
+
+  LWIP_ASSERT("invalid state", (state == CLOSING) || (state == LAST_ACK));
+
+  inactivity = 0;
+  inactive = NULL;
+  /* Go through the list of active pcbs and get the oldest pcb that is in state
+     CLOSING/LAST_ACK. */
+  for (pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
+    if (pcb->state == state) {
+      if ((u32_t)(tcp_ticks - pcb->tmr) >= inactivity) {
+        inactivity = tcp_ticks - pcb->tmr;
+        inactive = pcb;
+      }
+    }
+  }
+  if (inactive != NULL) {
+    LWIP_DEBUGF(TCP_DEBUG, ("tcp_kill_closing: killing oldest %s PCB %p (%"S32_F")\n",
+                            tcp_state_str[state], (void *)inactive, inactivity));
+    /* Don't send a RST, since no data is lost. */
+    tcp_abandon(inactive, 0);
+  }
+}
+
+/**
+ * Kills the oldest connection that is in TIME_WAIT state.
+ * Called from tcp_alloc() if no more connections are available.
+ */
+static void
+tcp_kill_timewait(void)
+{
+  struct tcp_pcb *pcb, *inactive;
+  u32_t inactivity;
+
+  inactivity = 0;
+  inactive = NULL;
+  /* Go through the list of TIME_WAIT pcbs and get the oldest pcb. */
+  for (pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) {
+    if ((u32_t)(tcp_ticks - pcb->tmr) >= inactivity) {
+      inactivity = tcp_ticks - pcb->tmr;
+      inactive = pcb;
+    }
+  }
+  if (inactive != NULL) {
+    LWIP_DEBUGF(TCP_DEBUG, ("tcp_kill_timewait: killing oldest TIME-WAIT PCB %p (%"S32_F")\n",
+                            (void *)inactive, inactivity));
+    tcp_abort(inactive);
+  }
+}
+
+/* Called when allocating a pcb fails.
+ * In this case, we want to handle all pcbs that want to close first: if we can
+ * now send the FIN (which failed before), the pcb might be in a state that is
+ * OK for us to now free it.
+ */
+static void
+tcp_handle_closepend(void)
+{
+  struct tcp_pcb *pcb = tcp_active_pcbs;
+
+  while (pcb != NULL) {
+    struct tcp_pcb *next = pcb->next;
+    /* send pending FIN */
+    if (pcb->flags & TF_CLOSEPEND) {
+      LWIP_DEBUGF(TCP_DEBUG, ("tcp_handle_closepend: pending FIN\n"));
+      tcp_clear_flags(pcb, TF_CLOSEPEND);
+      tcp_close_shutdown_fin(pcb);
+    }
+    pcb = next;
+  }
+}
+
+/**
+ * Allocate a new tcp_pcb structure.
+ *
+ * @param prio priority for the new pcb
+ * @return a new tcp_pcb that initially is in state CLOSED
+ */
+struct tcp_pcb *
+tcp_alloc(u8_t prio)
+{
+  struct tcp_pcb *pcb;
+
+  LWIP_ASSERT_CORE_LOCKED();
+
+  pcb = (struct tcp_pcb *)memp_malloc(MEMP_TCP_PCB);
+  if (pcb == NULL) {
+    /* Try to send FIN for all pcbs stuck in TF_CLOSEPEND first */
+    tcp_handle_closepend();
+
+    /* Try killing oldest connection in TIME-WAIT. */
+    LWIP_DEBUGF(TCP_DEBUG, ("tcp_alloc: killing off oldest TIME-WAIT connection\n"));
+    tcp_kill_timewait();
+    /* Try to allocate a tcp_pcb again. */
+    pcb = (struct tcp_pcb *)memp_malloc(MEMP_TCP_PCB);
+    if (pcb == NULL) {
+      /* Try killing oldest connection in LAST-ACK (these wouldn't go to TIME-WAIT). */
+      LWIP_DEBUGF(TCP_DEBUG, ("tcp_alloc: killing off oldest LAST-ACK connection\n"));
+      tcp_kill_state(LAST_ACK);
+      /* Try to allocate a tcp_pcb again. */
+      pcb = (struct tcp_pcb *)memp_malloc(MEMP_TCP_PCB);
+      if (pcb == NULL) {
+        /* Try killing oldest connection in CLOSING. */
+        LWIP_DEBUGF(TCP_DEBUG, ("tcp_alloc: killing off oldest CLOSING connection\n"));
+        tcp_kill_state(CLOSING);
+        /* Try to allocate a tcp_pcb again. */
+        pcb = (struct tcp_pcb *)memp_malloc(MEMP_TCP_PCB);
+        if (pcb == NULL) {
+          /* Try killing oldest active connection with lower priority than the new one. */
+          LWIP_DEBUGF(TCP_DEBUG, ("tcp_alloc: killing oldest connection with prio lower than %d\n", prio));
+          tcp_kill_prio(prio);
+          /* Try to allocate a tcp_pcb again. */
+          pcb = (struct tcp_pcb *)memp_malloc(MEMP_TCP_PCB);
+          if (pcb != NULL) {
+            /* adjust err stats: memp_malloc failed multiple times before */
+            MEMP_STATS_DEC(err, MEMP_TCP_PCB);
+          }
+        }
+        if (pcb != NULL) {
+          /* adjust err stats: memp_malloc failed multiple times before */
+          MEMP_STATS_DEC(err, MEMP_TCP_PCB);
+        }
+      }
+      if (pcb != NULL) {
+        /* adjust err stats: memp_malloc failed multiple times before */
+        MEMP_STATS_DEC(err, MEMP_TCP_PCB);
+      }
+    }
+    if (pcb != NULL) {
+      /* adjust err stats: memp_malloc failed above */
+      MEMP_STATS_DEC(err, MEMP_TCP_PCB);
+    }
+  }
+  if (pcb != NULL) {
+    /* zero out the whole pcb, so there is no need to initialize members to zero */
+    memset(pcb, 0, sizeof(struct tcp_pcb));
+    pcb->prio = prio;
+    pcb->snd_buf = TCP_SND_BUF;
+    /* Start with a window that does not need scaling. When window scaling is
+       enabled and used, the window is enlarged when both sides agree on scaling. */
+    pcb->rcv_wnd = pcb->rcv_ann_wnd = TCPWND_MIN16(TCP_WND);
+    pcb->ttl = TCP_TTL;
+    /* As initial send MSS, we use TCP_MSS but limit it to 536.
+       The send MSS is updated when an MSS option is received. */
+    pcb->mss = INITIAL_MSS;
+    /* Set initial TCP's retransmission timeout to 3000 ms by default.
+       This value could be configured in lwipopts */
+    pcb->rto = LWIP_TCP_RTO_TIME / TCP_SLOW_INTERVAL;
+    pcb->sv = LWIP_TCP_RTO_TIME / TCP_SLOW_INTERVAL;
+    pcb->rtime = -1;
+    pcb->cwnd = 1;
+    pcb->tmr = tcp_ticks;
+    pcb->last_timer = tcp_timer_ctr;
+
+    /* RFC 5681 recommends setting ssthresh arbitrarily high and gives an example
+    of using the largest advertised receive window.  We've seen complications with
+    receiving TCPs that use window scaling and/or window auto-tuning where the
+    initial advertised window is very small and then grows rapidly once the
+    connection is established. To avoid these complications, we set ssthresh to the
+    largest effective cwnd (amount of in-flight data) that the sender can have. */
+    pcb->ssthresh = TCP_SND_BUF;
+
+#if LWIP_CALLBACK_API
+    pcb->recv = tcp_recv_null;
+#endif /* LWIP_CALLBACK_API */
+
+    /* Init KEEPALIVE timer */
+    pcb->keep_idle  = TCP_KEEPIDLE_DEFAULT;
+
+#if LWIP_TCP_KEEPALIVE
+    pcb->keep_intvl = TCP_KEEPINTVL_DEFAULT;
+    pcb->keep_cnt   = TCP_KEEPCNT_DEFAULT;
+#endif /* LWIP_TCP_KEEPALIVE */
+    pcb_tci_init(pcb);
+  }
+  return pcb;
+}
+
+/**
+ * @ingroup tcp_raw
+ * Creates a new TCP protocol control block but doesn't place it on
+ * any of the TCP PCB lists.
+ * The pcb is not put on any list until binding using tcp_bind().
+ * If memory is not available for creating the new pcb, NULL is returned.
+ * @see MEMP_NUM_TCP_PCB_LISTEN and MEMP_NUM_TCP_PCB
+ *
+ * @internal: Maybe there should be a idle TCP PCB list where these
+ * PCBs are put on. Port reservation using tcp_bind() is implemented but
+ * allocated pcbs that are not bound can't be killed automatically if wanting
+ * to allocate a pcb with higher prio (@see tcp_kill_prio())
+ *
+ * @return a new tcp_pcb that initially is in state CLOSED
+ */
+struct tcp_pcb *
+tcp_new(void)
+{
+  return tcp_alloc(TCP_PRIO_NORMAL);
+}
+
+/**
+ * @ingroup tcp_raw
+ * Creates a new TCP protocol control block but doesn't
+ * place it on any of the TCP PCB lists.
+ * The pcb is not put on any list until binding using tcp_bind().
+ * @see MEMP_NUM_TCP_PCB_LISTEN and MEMP_NUM_TCP_PCB
+ *
+ * @param type IP address type, see @ref lwip_ip_addr_type definitions.
+ * If you want to listen to IPv4 and IPv6 (dual-stack) connections,
+ * supply @ref IPADDR_TYPE_ANY as argument and bind to @ref IP_ANY_TYPE.
+ * @return a new tcp_pcb that initially is in state CLOSED
+ */
+struct tcp_pcb *
+tcp_new_ip_type(u8_t type)
+{
+  struct tcp_pcb *pcb;
+  pcb = tcp_alloc(TCP_PRIO_NORMAL);
+#if LWIP_IPV4 && LWIP_IPV6
+  if (pcb != NULL) {
+    IP_SET_TYPE_VAL(pcb->local_ip, type);
+    IP_SET_TYPE_VAL(pcb->remote_ip, type);
+  }
+#else
+  LWIP_UNUSED_ARG(type);
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+  return pcb;
+}
+
+/**
+ * @ingroup tcp_raw
+ * Specifies the program specific state that should be passed to all
+ * other callback functions. The "pcb" argument is the current TCP
+ * connection control block, and the "arg" argument is the argument
+ * that will be passed to the callbacks.
+ *
+ * @param pcb tcp_pcb to set the callback argument
+ * @param arg void pointer argument to pass to callback functions
+ */
+void
+tcp_arg(struct tcp_pcb *pcb, void *arg)
+{
+  LWIP_ASSERT_CORE_LOCKED();
+  /* This function is allowed to be called for both listen pcbs and
+     connection pcbs. */
+  if (pcb != NULL) {
+    pcb->callback_arg = arg;
+  }
+}
+#if LWIP_CALLBACK_API
+
+/**
+ * @ingroup tcp_raw
+ * Sets the callback function that will be called when new data
+ * arrives. The callback function will be passed a NULL pbuf to
+ * indicate that the remote host has closed the connection. If the
+ * callback function returns ERR_OK or ERR_ABRT it must have
+ * freed the pbuf, otherwise it must not have freed it.
+ *
+ * @param pcb tcp_pcb to set the recv callback
+ * @param recv callback function to call for this pcb when data is received
+ */
+void
+tcp_recv(struct tcp_pcb *pcb, tcp_recv_fn recv)
+{
+  LWIP_ASSERT_CORE_LOCKED();
+  if (pcb != NULL) {
+    LWIP_ASSERT("invalid socket state for recv callback", pcb->state != LISTEN);
+    pcb->recv = recv;
+  }
+}
+
+/**
+ * @ingroup tcp_raw
+ * Specifies the callback function that should be called when data has
+ * successfully been received (i.e., acknowledged) by the remote
+ * host. The len argument passed to the callback function gives the
+ * amount bytes that was acknowledged by the last acknowledgment.
+ *
+ * @param pcb tcp_pcb to set the sent callback
+ * @param sent callback function to call for this pcb when data is successfully sent
+ */
+void
+tcp_sent(struct tcp_pcb *pcb, tcp_sent_fn sent)
+{
+  LWIP_ASSERT_CORE_LOCKED();
+  if (pcb != NULL) {
+    LWIP_ASSERT("invalid socket state for sent callback", pcb->state != LISTEN);
+    pcb->sent = sent;
+  }
+}
+
+/**
+ * @ingroup tcp_raw
+ * Used to specify the function that should be called when a fatal error
+ * has occurred on the connection.
+ *
+ * If a connection is aborted because of an error, the application is
+ * alerted of this event by the err callback. Errors that might abort a
+ * connection are when there is a shortage of memory. The callback
+ * function to be called is set using the tcp_err() function.
+ *
+ * @note The corresponding pcb is already freed when this callback is called!
+ *
+ * @param pcb tcp_pcb to set the err callback
+ * @param err callback function to call for this pcb when a fatal error
+ *        has occurred on the connection
+ */
+void
+tcp_err(struct tcp_pcb *pcb, tcp_err_fn err)
+{
+  LWIP_ASSERT_CORE_LOCKED();
+  if (pcb != NULL) {
+    LWIP_ASSERT("invalid socket state for err callback", pcb->state != LISTEN);
+    pcb->errf = err;
+  }
+}
+
+/**
+ * @ingroup tcp_raw
+ * Used for specifying the function that should be called when a
+ * LISTENing connection has been connected to another host.
+ * @see MEMP_NUM_TCP_PCB_LISTEN and MEMP_NUM_TCP_PCB
+ *
+ * @param pcb tcp_pcb to set the accept callback
+ * @param accept callback function to call for this pcb when LISTENing
+ *        connection has been connected to another host
+ */
+void
+tcp_accept(struct tcp_pcb *pcb, tcp_accept_fn accept)
+{
+  LWIP_ASSERT_CORE_LOCKED();
+  if ((pcb != NULL) && (pcb->state == LISTEN)) {
+    struct tcp_pcb_listen *lpcb = (struct tcp_pcb_listen *)pcb;
+    lpcb->accept = accept;
+  }
+}
+#endif /* LWIP_CALLBACK_API */
+
+
+/**
+ * @ingroup tcp_raw
+ * Specifies the polling interval and the callback function that should
+ * be called to poll the application. The interval is specified in
+ * number of TCP coarse grained timer shots, which typically occurs
+ * twice a second. An interval of 10 means that the application would
+ * be polled every 5 seconds.
+ *
+ * When a connection is idle (i.e., no data is either transmitted or
+ * received), lwIP will repeatedly poll the application by calling a
+ * specified callback function. This can be used either as a watchdog
+ * timer for killing connections that have stayed idle for too long, or
+ * as a method of waiting for memory to become available. For instance,
+ * if a call to tcp_write() has failed because memory wasn't available,
+ * the application may use the polling functionality to call tcp_write()
+ * again when the connection has been idle for a while.
+ */
+void
+tcp_poll(struct tcp_pcb *pcb, tcp_poll_fn poll, u8_t interval)
+{
+  LWIP_ASSERT_CORE_LOCKED();
+
+  LWIP_ERROR("tcp_poll: invalid pcb", pcb != NULL, return);
+  LWIP_ASSERT("invalid socket state for poll", pcb->state != LISTEN);
+
+#if LWIP_CALLBACK_API
+  pcb->poll = poll;
+#else /* LWIP_CALLBACK_API */
+  LWIP_UNUSED_ARG(poll);
+#endif /* LWIP_CALLBACK_API */
+  pcb->pollinterval = interval;
+}
+
+/**
+ * Purges a TCP PCB. Removes any buffered data and frees the buffer memory
+ * (pcb->ooseq, pcb->unsent and pcb->unacked are freed).
+ *
+ * @param pcb tcp_pcb to purge. The pcb itself is not deallocated!
+ */
+void
+tcp_pcb_purge(struct tcp_pcb *pcb)
+{
+  LWIP_ERROR("tcp_pcb_purge: invalid pcb", pcb != NULL, return);
+
+  if (pcb->state != CLOSED &&
+      pcb->state != TIME_WAIT &&
+      pcb->state != LISTEN) {
+
+    LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge\n"));
+
+    tcp_backlog_accepted(pcb);
+
+    if (pcb->refused_data != NULL) {
+      LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: data left on ->refused_data\n"));
+      pbuf_free(pcb->refused_data);
+      pcb->refused_data = NULL;
+    }
+    if (pcb->unsent != NULL) {
+      LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: not all data sent\n"));
+    }
+    if (pcb->unacked != NULL) {
+      LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: data left on ->unacked\n"));
+    }
+#if TCP_QUEUE_OOSEQ
+    if (pcb->ooseq != NULL) {
+      LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: data left on ->ooseq\n"));
+      tcp_free_ooseq(pcb);
+    }
+#endif /* TCP_QUEUE_OOSEQ */
+
+    /* Stop the retransmission timer as it will expect data on unacked
+       queue if it fires */
+    pcb->rtime = -1;
+
+    tcp_segs_free(pcb->unsent);
+    tcp_segs_free(pcb->unacked);
+    pcb->unacked = pcb->unsent = NULL;
+#if TCP_OVERSIZE
+    pcb->unsent_oversize = 0;
+#endif /* TCP_OVERSIZE */
+  }
+}
+
+/**
+ * Purges the PCB and removes it from a PCB list. Any delayed ACKs are sent first.
+ *
+ * @param pcblist PCB list to purge.
+ * @param pcb tcp_pcb to purge. The pcb itself is NOT deallocated!
+ */
+void
+tcp_pcb_remove(struct tcp_pcb **pcblist, struct tcp_pcb *pcb)
+{
+  LWIP_ASSERT("tcp_pcb_remove: invalid pcb", pcb != NULL);
+  LWIP_ASSERT("tcp_pcb_remove: invalid pcblist", pcblist != NULL);
+
+  TCP_RMV(pcblist, pcb);
+
+  tcp_pcb_purge(pcb);
+
+  /* if there is an outstanding delayed ACKs, send it */
+  if ((pcb->state != TIME_WAIT) &&
+      (pcb->state != LISTEN) &&
+      (pcb->flags & TF_ACK_DELAY)) {
+    tcp_ack_now(pcb);
+    tcp_output(pcb);
+  }
+
+  if (pcb->state != LISTEN) {
+    LWIP_ASSERT("unsent segments leaking", pcb->unsent == NULL);
+    LWIP_ASSERT("unacked segments leaking", pcb->unacked == NULL);
+#if TCP_QUEUE_OOSEQ
+    LWIP_ASSERT("ooseq segments leaking", pcb->ooseq == NULL);
+#endif /* TCP_QUEUE_OOSEQ */
+  }
+
+  pcb->state = CLOSED;
+  /* reset the local port to prevent the pcb from being 'bound' */
+  pcb->local_port = 0;
+
+  LWIP_ASSERT("tcp_pcb_remove: tcp_pcbs_sane()", tcp_pcbs_sane());
+}
+
+/**
+ * Calculates a new initial sequence number for new connections.
+ *
+ * @return u32_t pseudo random sequence number
+ */
+u32_t
+tcp_next_iss(struct tcp_pcb *pcb)
+{
+#ifdef LWIP_HOOK_TCP_ISN
+  LWIP_ASSERT("tcp_next_iss: invalid pcb", pcb != NULL);
+  return LWIP_HOOK_TCP_ISN(&pcb->local_ip, pcb->local_port, &pcb->remote_ip, pcb->remote_port);
+#else /* LWIP_HOOK_TCP_ISN */
+  static u32_t iss = 6510;
+
+  LWIP_ASSERT("tcp_next_iss: invalid pcb", pcb != NULL);
+  LWIP_UNUSED_ARG(pcb);
+
+  iss += tcp_ticks;       /* XXX */
+  return iss;
+#endif /* LWIP_HOOK_TCP_ISN */
+}
+
+#if TCP_CALCULATE_EFF_SEND_MSS
+/**
+ * Calculates the effective send mss that can be used for a specific IP address
+ * by calculating the minimum of TCP_MSS and the mtu (if set) of the target
+ * netif (if not NULL).
+ */
+u16_t
+tcp_eff_send_mss_netif(u16_t sendmss, struct netif *outif, const ip_addr_t *dest)
+{
+  u16_t mss_s;
+  u16_t mtu;
+
+  LWIP_UNUSED_ARG(dest); /* in case IPv6 is disabled */
+
+  LWIP_ASSERT("tcp_eff_send_mss_netif: invalid dst_ip", dest != NULL);
+
+#if LWIP_IPV6
+#if LWIP_IPV4
+  if (IP_IS_V6(dest))
+#endif /* LWIP_IPV4 */
+  {
+    /* First look in destination cache, to see if there is a Path MTU. */
+    mtu = nd6_get_destination_mtu(ip_2_ip6(dest), outif);
+  }
+#if LWIP_IPV4
+  else
+#endif /* LWIP_IPV4 */
+#endif /* LWIP_IPV6 */
+#if LWIP_IPV4
+  {
+    if (outif == NULL) {
+      return sendmss;
+    }
+    mtu = outif->mtu;
+  }
+#endif /* LWIP_IPV4 */
+
+  if (mtu != 0) {
+    u16_t offset;
+#if LWIP_IPV6
+#if LWIP_IPV4
+    if (IP_IS_V6(dest))
+#endif /* LWIP_IPV4 */
+    {
+      offset = IP6_HLEN + TCP_HLEN;
+    }
+#if LWIP_IPV4
+    else
+#endif /* LWIP_IPV4 */
+#endif /* LWIP_IPV6 */
+#if LWIP_IPV4
+    {
+      offset = IP_HLEN + TCP_HLEN;
+    }
+#endif /* LWIP_IPV4 */
+    mss_s = (mtu > offset) ? (u16_t)(mtu - offset) : 0;
+    /* RFC 1122, chap 4.2.2.6:
+     * Eff.snd.MSS = min(SendMSS+20, MMS_S) - TCPhdrsize - IPoptionsize
+     * We correct for TCP options in tcp_write(), and don't support IP options.
+     */
+    sendmss = LWIP_MIN(sendmss, mss_s);
+  }
+  return sendmss;
+}
+#endif /* TCP_CALCULATE_EFF_SEND_MSS */
+
+/** Helper function for tcp_netif_ip_addr_changed() that iterates a pcb list */
+static void
+tcp_netif_ip_addr_changed_pcblist(const ip_addr_t *old_addr, struct tcp_pcb *pcb_list)
+{
+  struct tcp_pcb *pcb;
+  pcb = pcb_list;
+
+  LWIP_ASSERT("tcp_netif_ip_addr_changed_pcblist: invalid old_addr", old_addr != NULL);
+
+  while (pcb != NULL) {
+    /* PCB bound to current local interface address? */
+    if (ip_addr_eq(&pcb->local_ip, old_addr)
+#if LWIP_AUTOIP
+        /* connections to link-local addresses must persist (RFC3927 ch. 1.9) */
+        && (!IP_IS_V4_VAL(pcb->local_ip) || !ip4_addr_islinklocal(ip_2_ip4(&pcb->local_ip)))
+#endif /* LWIP_AUTOIP */
+       ) {
+      /* this connection must be aborted */
+      struct tcp_pcb *next = pcb->next;
+      LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_STATE, ("netif_set_ipaddr: aborting TCP pcb %p\n", (void *)pcb));
+      tcp_abort(pcb);
+      pcb = next;
+    } else {
+      pcb = pcb->next;
+    }
+  }
+}
+
+/** This function is called from netif.c when address is changed or netif is removed
+ *
+ * @param old_addr IP address of the netif before change
+ * @param new_addr IP address of the netif after change or NULL if netif has been removed
+ */
+void
+tcp_netif_ip_addr_changed(const ip_addr_t *old_addr, const ip_addr_t *new_addr)
+{
+  struct tcp_pcb_listen *lpcb;
+
+  if (!ip_addr_isany(old_addr)) {
+    tcp_netif_ip_addr_changed_pcblist(old_addr, tcp_active_pcbs);
+    tcp_netif_ip_addr_changed_pcblist(old_addr, tcp_bound_pcbs);
+
+    if (!ip_addr_isany(new_addr)) {
+      /* PCB bound to current local interface address? */
+      for (lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) {
+        /* PCB bound to current local interface address? */
+        if (ip_addr_eq(&lpcb->local_ip, old_addr)) {
+          /* The PCB is listening to the old ipaddr and
+            * is set to listen to the new one instead */
+          ip_addr_copy(lpcb->local_ip, *new_addr);
+        }
+      }
+    }
+  }
+}
+
+const char *
+tcp_debug_state_str(enum tcp_state s)
+{
+  return tcp_state_str[s];
+}
+
+err_t
+tcp_tcp_get_tcp_addrinfo(struct tcp_pcb *pcb, int local, ip_addr_t *addr, u16_t *port)
+{
+  if (pcb) {
+    if (local) {
+      if (addr) {
+        *addr = pcb->local_ip;
+      }
+      if (port) {
+        *port = pcb->local_port;
+      }
+    } else {
+      if (addr) {
+        *addr = pcb->remote_ip;
+      }
+      if (port) {
+        *port = pcb->remote_port;
+      }
+    }
+    return ERR_OK;
+  }
+  return ERR_VAL;
+}
+
+#if TCP_QUEUE_OOSEQ
+/* Free all ooseq pbufs (and possibly reset SACK state) */
+void
+tcp_free_ooseq(struct tcp_pcb *pcb)
+{
+  if (pcb->ooseq) {
+    tcp_segs_free(pcb->ooseq);
+    pcb->ooseq = NULL;
+#if LWIP_TCP_SACK_OUT
+    memset(pcb->rcv_sacks, 0, sizeof(pcb->rcv_sacks));
+#endif /* LWIP_TCP_SACK_OUT */
+  }
+}
+#endif /* TCP_QUEUE_OOSEQ */
+
+#if TCP_DEBUG || TCP_INPUT_DEBUG || TCP_OUTPUT_DEBUG
+/**
+ * Print a tcp header for debugging purposes.
+ *
+ * @param tcphdr pointer to a struct tcp_hdr
+ */
+void
+tcp_debug_print(struct tcp_hdr *tcphdr)
+{
+  LWIP_DEBUGF(TCP_DEBUG, ("TCP header:\n"));
+  LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n"));
+  LWIP_DEBUGF(TCP_DEBUG, ("|    %5"U16_F"      |    %5"U16_F"      | (src port, dest port)\n",
+                          lwip_ntohs(tcphdr->src), lwip_ntohs(tcphdr->dest)));
+  LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n"));
+  LWIP_DEBUGF(TCP_DEBUG, ("|           %010"U32_F"          | (seq no)\n",
+                          lwip_ntohl(tcphdr->seqno)));
+  LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n"));
+  LWIP_DEBUGF(TCP_DEBUG, ("|           %010"U32_F"          | (ack no)\n",
+                          lwip_ntohl(tcphdr->ackno)));
+  LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n"));
+  LWIP_DEBUGF(TCP_DEBUG, ("| %2"U16_F" |   |%"U16_F"%"U16_F"%"U16_F"%"U16_F"%"U16_F"%"U16_F"|     %5"U16_F"     | (hdrlen, flags (",
+                          TCPH_HDRLEN(tcphdr),
+                          (u16_t)(TCPH_FLAGS(tcphdr) >> 5 & 1),
+                          (u16_t)(TCPH_FLAGS(tcphdr) >> 4 & 1),
+                          (u16_t)(TCPH_FLAGS(tcphdr) >> 3 & 1),
+                          (u16_t)(TCPH_FLAGS(tcphdr) >> 2 & 1),
+                          (u16_t)(TCPH_FLAGS(tcphdr) >> 1 & 1),
+                          (u16_t)(TCPH_FLAGS(tcphdr)      & 1),
+                          lwip_ntohs(tcphdr->wnd)));
+  tcp_debug_print_flags(TCPH_FLAGS(tcphdr));
+  LWIP_DEBUGF(TCP_DEBUG, ("), win)\n"));
+  LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n"));
+  LWIP_DEBUGF(TCP_DEBUG, ("|    0x%04"X16_F"     |     %5"U16_F"     | (chksum, urgp)\n",
+                          lwip_ntohs(tcphdr->chksum), lwip_ntohs(tcphdr->urgp)));
+  LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n"));
+}
+
+/**
+ * Print a tcp state for debugging purposes.
+ *
+ * @param s enum tcp_state to print
+ */
+void
+tcp_debug_print_state(enum tcp_state s)
+{
+  LWIP_DEBUGF(TCP_DEBUG, ("State: %s\n", tcp_state_str[s]));
+}
+
+/**
+ * Print tcp flags for debugging purposes.
+ *
+ * @param flags tcp flags, all active flags are printed
+ */
+void
+tcp_debug_print_flags(u8_t flags)
+{
+  if (flags & TCP_FIN) {
+    LWIP_DEBUGF(TCP_DEBUG, ("FIN "));
+  }
+  if (flags & TCP_SYN) {
+    LWIP_DEBUGF(TCP_DEBUG, ("SYN "));
+  }
+  if (flags & TCP_RST) {
+    LWIP_DEBUGF(TCP_DEBUG, ("RST "));
+  }
+  if (flags & TCP_PSH) {
+    LWIP_DEBUGF(TCP_DEBUG, ("PSH "));
+  }
+  if (flags & TCP_ACK) {
+    LWIP_DEBUGF(TCP_DEBUG, ("ACK "));
+  }
+  if (flags & TCP_URG) {
+    LWIP_DEBUGF(TCP_DEBUG, ("URG "));
+  }
+  if (flags & TCP_ECE) {
+    LWIP_DEBUGF(TCP_DEBUG, ("ECE "));
+  }
+  if (flags & TCP_CWR) {
+    LWIP_DEBUGF(TCP_DEBUG, ("CWR "));
+  }
+  LWIP_DEBUGF(TCP_DEBUG, ("\n"));
+}
+
+/**
+ * Print all tcp_pcbs in every list for debugging purposes.
+ */
+void
+tcp_debug_print_pcbs(void)
+{
+  struct tcp_pcb *pcb;
+  struct tcp_pcb_listen *pcbl;
+
+  LWIP_DEBUGF(TCP_DEBUG, ("Active PCB states:\n"));
+  for (pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
+    LWIP_DEBUGF(TCP_DEBUG, ("Local port %"U16_F", foreign port %"U16_F" snd_nxt %"U32_F" rcv_nxt %"U32_F" ",
+                            pcb->local_port, pcb->remote_port,
+                            pcb->snd_nxt, pcb->rcv_nxt));
+    tcp_debug_print_state(pcb->state);
+  }
+
+  LWIP_DEBUGF(TCP_DEBUG, ("Listen PCB states:\n"));
+  for (pcbl = tcp_listen_pcbs.listen_pcbs; pcbl != NULL; pcbl = pcbl->next) {
+    LWIP_DEBUGF(TCP_DEBUG, ("Local port %"U16_F" ", pcbl->local_port));
+    tcp_debug_print_state(pcbl->state);
+  }
+
+  LWIP_DEBUGF(TCP_DEBUG, ("TIME-WAIT PCB states:\n"));
+  for (pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) {
+    LWIP_DEBUGF(TCP_DEBUG, ("Local port %"U16_F", foreign port %"U16_F" snd_nxt %"U32_F" rcv_nxt %"U32_F" ",
+                            pcb->local_port, pcb->remote_port,
+                            pcb->snd_nxt, pcb->rcv_nxt));
+    tcp_debug_print_state(pcb->state);
+  }
+}
+
+/**
+ * Check state consistency of the tcp_pcb lists.
+ */
+s16_t
+tcp_pcbs_sane(void)
+{
+  struct tcp_pcb *pcb;
+  for (pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
+    LWIP_ASSERT("tcp_pcbs_sane: active pcb->state != CLOSED", pcb->state != CLOSED);
+    LWIP_ASSERT("tcp_pcbs_sane: active pcb->state != LISTEN", pcb->state != LISTEN);
+    LWIP_ASSERT("tcp_pcbs_sane: active pcb->state != TIME-WAIT", pcb->state != TIME_WAIT);
+  }
+  for (pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) {
+    LWIP_ASSERT("tcp_pcbs_sane: tw pcb->state == TIME-WAIT", pcb->state == TIME_WAIT);
+  }
+  return 1;
+}
+#endif /* TCP_DEBUG */
+
+#if LWIP_TCP_PCB_NUM_EXT_ARGS
+/**
+ * @defgroup tcp_raw_extargs ext arguments
+ * @ingroup tcp_raw
+ * Additional data storage per tcp pcb<br>
+ * @see @ref tcp_raw
+ *
+ * When LWIP_TCP_PCB_NUM_EXT_ARGS is > 0, every tcp pcb (including listen pcb)
+ * includes a number of additional argument entries in an array.
+ *
+ * To support memory management, in addition to a 'void *', callbacks can be
+ * provided to manage transition from listening pcbs to connections and to
+ * deallocate memory when a pcb is deallocated (see struct @ref tcp_ext_arg_callbacks).
+ *
+ * After allocating this index, use @ref tcp_ext_arg_set and @ref tcp_ext_arg_get
+ * to store and load arguments from this index for a given pcb.
+ */
+
+static u8_t tcp_ext_arg_id;
+
+/**
+ * @ingroup tcp_raw_extargs
+ * Allocate an index to store data in ext_args member of struct tcp_pcb.
+ * Returned value is an index in mentioned array.
+ * The index is *global* over all pcbs!
+ *
+ * When @ref LWIP_TCP_PCB_NUM_EXT_ARGS is > 0, every tcp pcb (including listen pcb)
+ * includes a number of additional argument entries in an array.
+ *
+ * To support memory management, in addition to a 'void *', callbacks can be
+ * provided to manage transition from listening pcbs to connections and to
+ * deallocate memory when a pcb is deallocated (see struct @ref tcp_ext_arg_callbacks).
+ *
+ * After allocating this index, use @ref tcp_ext_arg_set and @ref tcp_ext_arg_get
+ * to store and load arguments from this index for a given pcb.
+ *
+ * @return a unique index into struct tcp_pcb.ext_args
+ */
+u8_t
+tcp_ext_arg_alloc_id(void)
+{
+  u8_t result = tcp_ext_arg_id;
+  tcp_ext_arg_id++;
+
+  LWIP_ASSERT_CORE_LOCKED();
+
+#if LWIP_TCP_PCB_NUM_EXT_ARGS >= 255
+#error LWIP_TCP_PCB_NUM_EXT_ARGS
+#endif
+  LWIP_ASSERT("Increase LWIP_TCP_PCB_NUM_EXT_ARGS in lwipopts.h", result < LWIP_TCP_PCB_NUM_EXT_ARGS);
+  return result;
+}
+
+/**
+ * @ingroup tcp_raw_extargs
+ * Set callbacks for a given index of ext_args on the specified pcb.
+ *
+ * @param pcb tcp_pcb for which to set the callback
+ * @param id ext_args index to set (allocated via @ref tcp_ext_arg_alloc_id)
+ * @param callbacks callback table (const since it is referenced, not copied!)
+ */
+void
+tcp_ext_arg_set_callbacks(struct tcp_pcb *pcb, u8_t id, const struct tcp_ext_arg_callbacks * const callbacks)
+{
+  LWIP_ASSERT("pcb != NULL", pcb != NULL);
+  LWIP_ASSERT("id < LWIP_TCP_PCB_NUM_EXT_ARGS", id < LWIP_TCP_PCB_NUM_EXT_ARGS);
+  LWIP_ASSERT("callbacks != NULL", callbacks != NULL);
+
+  LWIP_ASSERT_CORE_LOCKED();
+
+  pcb->ext_args[id].callbacks = callbacks;
+}
+
+/**
+ * @ingroup tcp_raw_extargs
+ * Set data for a given index of ext_args on the specified pcb.
+ *
+ * @param pcb tcp_pcb for which to set the data
+ * @param id ext_args index to set (allocated via @ref tcp_ext_arg_alloc_id)
+ * @param arg data pointer to set
+ */
+void tcp_ext_arg_set(struct tcp_pcb *pcb, u8_t id, void *arg)
+{
+  LWIP_ASSERT("pcb != NULL", pcb != NULL);
+  LWIP_ASSERT("id < LWIP_TCP_PCB_NUM_EXT_ARGS", id < LWIP_TCP_PCB_NUM_EXT_ARGS);
+
+  LWIP_ASSERT_CORE_LOCKED();
+
+  pcb->ext_args[id].data = arg;
+}
+
+/**
+ * @ingroup tcp_raw_extargs
+ * Set data for a given index of ext_args on the specified pcb.
+ *
+ * @param pcb tcp_pcb for which to set the data
+ * @param id ext_args index to set (allocated via @ref tcp_ext_arg_alloc_id)
+ * @return data pointer at the given index
+ */
+void *tcp_ext_arg_get(const struct tcp_pcb *pcb, u8_t id)
+{
+  LWIP_ASSERT("pcb != NULL", pcb != NULL);
+  LWIP_ASSERT("id < LWIP_TCP_PCB_NUM_EXT_ARGS", id < LWIP_TCP_PCB_NUM_EXT_ARGS);
+
+  LWIP_ASSERT_CORE_LOCKED();
+
+  return pcb->ext_args[id].data;
+}
+
+/** This function calls the "destroy" callback for all ext_args once a pcb is
+ * freed.
+ */
+static void
+tcp_ext_arg_invoke_callbacks_destroyed(struct tcp_pcb_ext_args *ext_args)
+{
+  int i;
+  LWIP_ASSERT("ext_args != NULL", ext_args != NULL);
+
+  for (i = 0; i < LWIP_TCP_PCB_NUM_EXT_ARGS; i++) {
+    if (ext_args[i].callbacks != NULL) {
+      if (ext_args[i].callbacks->destroy != NULL) {
+        ext_args[i].callbacks->destroy((u8_t)i, ext_args[i].data);
+      }
+    }
+  }
+}
+
+/** This function calls the "passive_open" callback for all ext_args if a connection
+ * is in the process of being accepted. This is called just after the SYN is
+ * received and before a SYN/ACK is sent, to allow to modify the very first
+ * segment sent even on passive open. Naturally, the "accepted" callback of the
+ * pcb has not been called yet!
+ */
+err_t
+tcp_ext_arg_invoke_callbacks_passive_open(struct tcp_pcb_listen *lpcb, struct tcp_pcb *cpcb)
+{
+  int i;
+  LWIP_ASSERT("lpcb != NULL", lpcb != NULL);
+  LWIP_ASSERT("cpcb != NULL", cpcb != NULL);
+
+  for (i = 0; i < LWIP_TCP_PCB_NUM_EXT_ARGS; i++) {
+    if (lpcb->ext_args[i].callbacks != NULL) {
+      if (lpcb->ext_args[i].callbacks->passive_open != NULL) {
+        err_t err = lpcb->ext_args[i].callbacks->passive_open((u8_t)i, lpcb, cpcb);
+        if (err != ERR_OK) {
+          return err;
+        }
+      }
+    }
+  }
+  return ERR_OK;
+}
+#endif /* LWIP_TCP_PCB_NUM_EXT_ARGS */
+
+#endif /* LWIP_TCP */
diff --git a/lib/lwip/core/tcp_in.c b/lib/lwip/core/tcp_in.c
new file mode 100644
index 0000000000..c7a1f7b732
--- /dev/null
+++ b/lib/lwip/core/tcp_in.c
@@ -0,0 +1,2184 @@
+/**
+ * @file
+ * Transmission Control Protocol, incoming traffic
+ *
+ * The input processing functions of the TCP layer.
+ *
+ * These functions are generally called in the order (ip_input() ->)
+ * tcp_input() -> * tcp_process() -> tcp_receive() (-> application).
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/priv/tcp_priv.h"
+#include "lwip/def.h"
+#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+#include "lwip/mem.h"
+#include "lwip/memp.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/stats.h"
+#include "lwip/ip6.h"
+#include "lwip/ip6_addr.h"
+#if LWIP_ND6_TCP_REACHABILITY_HINTS
+#include "lwip/nd6.h"
+#endif /* LWIP_ND6_TCP_REACHABILITY_HINTS */
+
+#include <string.h>
+
+#ifdef LWIP_HOOK_FILENAME
+#include LWIP_HOOK_FILENAME
+#endif
+
+/** Initial CWND calculation as defined RFC 2581 */
+#define LWIP_TCP_CALC_INITIAL_CWND(mss) ((tcpwnd_size_t)LWIP_MIN((4U * (mss)), LWIP_MAX((2U * (mss)), 4380U)))
+
+/* These variables are global to all functions involved in the input
+   processing of TCP segments. They are set by the tcp_input()
+   function. */
+static struct tcp_seg inseg;
+static struct tcp_hdr *tcphdr;
+static u16_t tcphdr_optlen;
+static u16_t tcphdr_opt1len;
+static u8_t *tcphdr_opt2;
+static u16_t tcp_optidx;
+static u32_t seqno, ackno;
+static tcpwnd_size_t recv_acked;
+static u16_t tcplen;
+static u8_t flags;
+
+static u8_t recv_flags;
+static struct pbuf *recv_data;
+
+struct tcp_pcb *tcp_input_pcb;
+
+/* Forward declarations. */
+static err_t tcp_process(struct tcp_pcb *pcb);
+static void tcp_receive(struct tcp_pcb *pcb);
+static void tcp_parseopt(struct tcp_pcb *pcb);
+
+static void tcp_listen_input(struct tcp_pcb_listen *pcb);
+static void tcp_timewait_input(struct tcp_pcb *pcb);
+
+static int tcp_input_delayed_close(struct tcp_pcb *pcb);
+
+#if LWIP_TCP_SACK_OUT
+static void tcp_add_sack(struct tcp_pcb *pcb, u32_t left, u32_t right);
+static void tcp_remove_sacks_lt(struct tcp_pcb *pcb, u32_t seq);
+#if defined(TCP_OOSEQ_BYTES_LIMIT) || defined(TCP_OOSEQ_PBUFS_LIMIT)
+static void tcp_remove_sacks_gt(struct tcp_pcb *pcb, u32_t seq);
+#endif /* TCP_OOSEQ_BYTES_LIMIT || TCP_OOSEQ_PBUFS_LIMIT */
+#endif /* LWIP_TCP_SACK_OUT */
+
+/**
+ * The initial input processing of TCP. It verifies the TCP header, demultiplexes
+ * the segment between the PCBs and passes it on to tcp_process(), which implements
+ * the TCP finite state machine. This function is called by the IP layer (in
+ * ip_input()).
+ *
+ * @param p received TCP segment to process (p->payload pointing to the TCP header)
+ * @param inp network interface on which this segment was received
+ */
+void
+tcp_input(struct pbuf *p, struct netif *inp)
+{
+  struct tcp_pcb *pcb, *prev;
+  struct tcp_pcb_listen *lpcb;
+#if SO_REUSE
+  struct tcp_pcb *lpcb_prev = NULL;
+  struct tcp_pcb_listen *lpcb_any = NULL;
+#endif /* SO_REUSE */
+  u8_t hdrlen_bytes;
+  err_t err;
+
+  LWIP_UNUSED_ARG(inp);
+  LWIP_ASSERT_CORE_LOCKED();
+  LWIP_ASSERT("tcp_input: invalid pbuf", p != NULL);
+
+  PERF_START;
+
+  TCP_STATS_INC(tcp.recv);
+  MIB2_STATS_INC(mib2.tcpinsegs);
+
+  tcphdr = (struct tcp_hdr *)p->payload;
+
+#if TCP_INPUT_DEBUG
+  tcp_debug_print(tcphdr);
+#endif
+
+  /* Check that TCP header fits in payload */
+  if (p->len < TCP_HLEN) {
+    /* drop short packets */
+    LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: short packet (%"U16_F" bytes) discarded\n", p->tot_len));
+    TCP_STATS_INC(tcp.lenerr);
+    goto dropped;
+  }
+
+  /* Don't even process incoming broadcasts/multicasts. */
+  if (ip_addr_isbroadcast(ip_current_dest_addr(), ip_current_netif()) ||
+      ip_addr_ismulticast(ip_current_dest_addr())) {
+    TCP_STATS_INC(tcp.proterr);
+    goto dropped;
+  }
+
+#if CHECKSUM_CHECK_TCP
+  IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_CHECK_TCP) {
+    /* Verify TCP checksum. */
+    u16_t chksum = ip_chksum_pseudo(p, IP_PROTO_TCP, p->tot_len,
+                                    ip_current_src_addr(), ip_current_dest_addr());
+    if (chksum != 0) {
+      LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packet discarded due to failing checksum 0x%04"X16_F"\n",
+                                    chksum));
+      tcp_debug_print(tcphdr);
+      TCP_STATS_INC(tcp.chkerr);
+      goto dropped;
+    }
+  }
+#endif /* CHECKSUM_CHECK_TCP */
+
+  /* sanity-check header length */
+  hdrlen_bytes = TCPH_HDRLEN_BYTES(tcphdr);
+  if ((hdrlen_bytes < TCP_HLEN) || (hdrlen_bytes > p->tot_len)) {
+    LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: invalid header length (%"U16_F")\n", (u16_t)hdrlen_bytes));
+    TCP_STATS_INC(tcp.lenerr);
+    goto dropped;
+  }
+
+  /* Move the payload pointer in the pbuf so that it points to the
+     TCP data instead of the TCP header. */
+  tcphdr_optlen = (u16_t)(hdrlen_bytes - TCP_HLEN);
+  tcphdr_opt2 = NULL;
+  if (p->len >= hdrlen_bytes) {
+    /* all options are in the first pbuf */
+    tcphdr_opt1len = tcphdr_optlen;
+    pbuf_remove_header(p, hdrlen_bytes); /* cannot fail */
+  } else {
+    u16_t opt2len;
+    /* TCP header fits into first pbuf, options don't - data is in the next pbuf */
+    /* there must be a next pbuf, due to hdrlen_bytes sanity check above */
+    LWIP_ASSERT("p->next != NULL", p->next != NULL);
+
+    /* advance over the TCP header (cannot fail) */
+    pbuf_remove_header(p, TCP_HLEN);
+
+    /* determine how long the first and second parts of the options are */
+    tcphdr_opt1len = p->len;
+    opt2len = (u16_t)(tcphdr_optlen - tcphdr_opt1len);
+
+    /* options continue in the next pbuf: set p to zero length and hide the
+        options in the next pbuf (adjusting p->tot_len) */
+    pbuf_remove_header(p, tcphdr_opt1len);
+
+    /* check that the options fit in the second pbuf */
+    if (opt2len > p->next->len) {
+      /* drop short packets */
+      LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: options overflow second pbuf (%"U16_F" bytes)\n", p->next->len));
+      TCP_STATS_INC(tcp.lenerr);
+      goto dropped;
+    }
+
+    /* remember the pointer to the second part of the options */
+    tcphdr_opt2 = (u8_t *)p->next->payload;
+
+    /* advance p->next to point after the options, and manually
+        adjust p->tot_len to keep it consistent with the changed p->next */
+    pbuf_remove_header(p->next, opt2len);
+    p->tot_len = (u16_t)(p->tot_len - opt2len);
+
+    LWIP_ASSERT("p->len == 0", p->len == 0);
+    LWIP_ASSERT("p->tot_len == p->next->tot_len", p->tot_len == p->next->tot_len);
+  }
+
+  /* Convert fields in TCP header to host byte order. */
+  tcphdr->src = lwip_ntohs(tcphdr->src);
+  tcphdr->dest = lwip_ntohs(tcphdr->dest);
+  seqno = tcphdr->seqno = lwip_ntohl(tcphdr->seqno);
+  ackno = tcphdr->ackno = lwip_ntohl(tcphdr->ackno);
+  tcphdr->wnd = lwip_ntohs(tcphdr->wnd);
+
+  flags = TCPH_FLAGS(tcphdr);
+  tcplen = p->tot_len;
+  if (flags & (TCP_FIN | TCP_SYN)) {
+    tcplen++;
+    if (tcplen < p->tot_len) {
+      /* u16_t overflow, cannot handle this */
+      LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: length u16_t overflow, cannot handle this\n"));
+      TCP_STATS_INC(tcp.lenerr);
+      goto dropped;
+    }
+  }
+
+  /* Demultiplex an incoming segment. First, we check if it is destined
+     for an active connection. */
+  prev = NULL;
+
+  for (pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
+    LWIP_ASSERT("tcp_input: active pcb->state != CLOSED", pcb->state != CLOSED);
+    LWIP_ASSERT("tcp_input: active pcb->state != TIME-WAIT", pcb->state != TIME_WAIT);
+    LWIP_ASSERT("tcp_input: active pcb->state != LISTEN", pcb->state != LISTEN);
+
+    /* check if PCB is bound to specific netif */
+    if ((pcb->netif_idx != NETIF_NO_INDEX) &&
+        (pcb->netif_idx != netif_get_index(ip_data.current_input_netif))) {
+      prev = pcb;
+      continue;
+    }
+
+    if (pcb->remote_port == tcphdr->src &&
+        pcb->local_port == tcphdr->dest &&
+        ip_addr_eq(&pcb->remote_ip, ip_current_src_addr()) &&
+        ip_addr_eq(&pcb->local_ip, ip_current_dest_addr())) {
+      /* Move this PCB to the front of the list so that subsequent
+         lookups will be faster (we exploit locality in TCP segment
+         arrivals). */
+      LWIP_ASSERT("tcp_input: pcb->next != pcb (before cache)", pcb->next != pcb);
+      if (prev != NULL) {
+        prev->next = pcb->next;
+        pcb->next = tcp_active_pcbs;
+        tcp_active_pcbs = pcb;
+      } else {
+        TCP_STATS_INC(tcp.cachehit);
+      }
+      LWIP_ASSERT("tcp_input: pcb->next != pcb (after cache)", pcb->next != pcb);
+      break;
+    }
+    prev = pcb;
+  }
+
+  if (pcb == NULL) {
+    /* If it did not go to an active connection, we check the connections
+       in the TIME-WAIT state. */
+    for (pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) {
+      LWIP_ASSERT("tcp_input: TIME-WAIT pcb->state == TIME-WAIT", pcb->state == TIME_WAIT);
+
+      /* check if PCB is bound to specific netif */
+      if ((pcb->netif_idx != NETIF_NO_INDEX) &&
+          (pcb->netif_idx != netif_get_index(ip_data.current_input_netif))) {
+        continue;
+      }
+
+      if (pcb->remote_port == tcphdr->src &&
+          pcb->local_port == tcphdr->dest &&
+          ip_addr_eq(&pcb->remote_ip, ip_current_src_addr()) &&
+          ip_addr_eq(&pcb->local_ip, ip_current_dest_addr())) {
+        /* We don't really care enough to move this PCB to the front
+           of the list since we are not very likely to receive that
+           many segments for connections in TIME-WAIT. */
+        LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packed for TIME_WAITing connection.\n"));
+#ifdef LWIP_HOOK_TCP_INPACKET_PCB
+        if (LWIP_HOOK_TCP_INPACKET_PCB(pcb, tcphdr, tcphdr_optlen, tcphdr_opt1len,
+                                       tcphdr_opt2, p) == ERR_OK)
+#endif
+        {
+          tcp_timewait_input(pcb);
+        }
+        pbuf_free(p);
+        return;
+      }
+    }
+
+    /* Finally, if we still did not get a match, we check all PCBs that
+       are LISTENing for incoming connections. */
+    prev = NULL;
+    for (lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) {
+      /* check if PCB is bound to specific netif */
+      if ((lpcb->netif_idx != NETIF_NO_INDEX) &&
+          (lpcb->netif_idx != netif_get_index(ip_data.current_input_netif))) {
+        prev = (struct tcp_pcb *)lpcb;
+        continue;
+      }
+
+      if (lpcb->local_port == tcphdr->dest) {
+        if (IP_IS_ANY_TYPE_VAL(lpcb->local_ip)) {
+          /* found an ANY TYPE (IPv4/IPv6) match */
+#if SO_REUSE
+          lpcb_any = lpcb;
+          lpcb_prev = prev;
+#else /* SO_REUSE */
+          break;
+#endif /* SO_REUSE */
+        } else if (IP_ADDR_PCB_VERSION_MATCH_EXACT(lpcb, ip_current_dest_addr())) {
+          if (ip_addr_eq(&lpcb->local_ip, ip_current_dest_addr())) {
+            /* found an exact match */
+            break;
+          } else if (ip_addr_isany(&lpcb->local_ip)) {
+            /* found an ANY-match */
+#if SO_REUSE
+            lpcb_any = lpcb;
+            lpcb_prev = prev;
+#else /* SO_REUSE */
+            break;
+#endif /* SO_REUSE */
+          }
+        }
+      }
+      prev = (struct tcp_pcb *)lpcb;
+    }
+#if SO_REUSE
+    /* first try specific local IP */
+    if (lpcb == NULL) {
+      /* only pass to ANY if no specific local IP has been found */
+      lpcb = lpcb_any;
+      prev = lpcb_prev;
+    }
+#endif /* SO_REUSE */
+    if (lpcb != NULL) {
+      /* Move this PCB to the front of the list so that subsequent
+         lookups will be faster (we exploit locality in TCP segment
+         arrivals). */
+      if (prev != NULL) {
+        ((struct tcp_pcb_listen *)prev)->next = lpcb->next;
+        /* our successor is the remainder of the listening list */
+        lpcb->next = tcp_listen_pcbs.listen_pcbs;
+        /* put this listening pcb at the head of the listening list */
+        tcp_listen_pcbs.listen_pcbs = lpcb;
+      } else {
+        TCP_STATS_INC(tcp.cachehit);
+      }
+
+      LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packed for LISTENing connection.\n"));
+#ifdef LWIP_HOOK_TCP_INPACKET_PCB
+      if (LWIP_HOOK_TCP_INPACKET_PCB((struct tcp_pcb *)lpcb, tcphdr, tcphdr_optlen,
+                                     tcphdr_opt1len, tcphdr_opt2, p) == ERR_OK)
+#endif
+      {
+        tcp_listen_input(lpcb);
+      }
+      pbuf_free(p);
+      return;
+    }
+  }
+
+#if TCP_INPUT_DEBUG
+  LWIP_DEBUGF(TCP_INPUT_DEBUG, ("+-+-+-+-+-+-+-+-+-+-+-+-+-+- tcp_input: flags "));
+  tcp_debug_print_flags(TCPH_FLAGS(tcphdr));
+  LWIP_DEBUGF(TCP_INPUT_DEBUG, ("-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n"));
+#endif /* TCP_INPUT_DEBUG */
+
+
+#ifdef LWIP_HOOK_TCP_INPACKET_PCB
+  if ((pcb != NULL) && LWIP_HOOK_TCP_INPACKET_PCB(pcb, tcphdr, tcphdr_optlen,
+      tcphdr_opt1len, tcphdr_opt2, p) != ERR_OK) {
+    pbuf_free(p);
+    return;
+  }
+#endif
+  if (pcb != NULL) {
+    /* The incoming segment belongs to a connection. */
+#if TCP_INPUT_DEBUG
+    tcp_debug_print_state(pcb->state);
+#endif /* TCP_INPUT_DEBUG */
+
+    /* Set up a tcp_seg structure. */
+    inseg.next = NULL;
+    inseg.len = p->tot_len;
+    inseg.p = p;
+    inseg.tcphdr = tcphdr;
+
+    recv_data = NULL;
+    recv_flags = 0;
+    recv_acked = 0;
+
+    if (flags & TCP_PSH) {
+      p->flags |= PBUF_FLAG_PUSH;
+    }
+
+    /* If there is data which was previously "refused" by upper layer */
+    if (pcb->refused_data != NULL) {
+      if ((tcp_process_refused_data(pcb) == ERR_ABRT) ||
+          ((pcb->refused_data != NULL) && (tcplen > 0))) {
+        /* pcb has been aborted or refused data is still refused and the new
+           segment contains data */
+        if (pcb->rcv_ann_wnd == 0) {
+          /* this is a zero-window probe, we respond to it with current RCV.NXT
+          and drop the data segment */
+          tcp_send_empty_ack(pcb);
+        }
+        TCP_STATS_INC(tcp.drop);
+        MIB2_STATS_INC(mib2.tcpinerrs);
+        goto aborted;
+      }
+    }
+    tcp_input_pcb = pcb;
+    err = tcp_process(pcb);
+    /* A return value of ERR_ABRT means that tcp_abort() was called
+       and that the pcb has been freed. If so, we don't do anything. */
+    if (err != ERR_ABRT) {
+      if (recv_flags & TF_RESET) {
+        /* TF_RESET means that the connection was reset by the other
+           end. We then call the error callback to inform the
+           application that the connection is dead before we
+           deallocate the PCB. */
+        TCP_EVENT_ERR(pcb->state, pcb->errf, pcb->callback_arg, ERR_RST);
+        tcp_pcb_remove(&tcp_active_pcbs, pcb);
+        tcp_free(pcb);
+      } else {
+        err = ERR_OK;
+        /* If the application has registered a "sent" function to be
+           called when new send buffer space is available, we call it
+           now. */
+        if (recv_acked > 0) {
+          u16_t acked16;
+#if LWIP_WND_SCALE
+          /* recv_acked is u32_t but the sent callback only takes a u16_t,
+             so we might have to call it multiple times. */
+          u32_t acked = recv_acked;
+          while (acked > 0) {
+            acked16 = (u16_t)LWIP_MIN(acked, 0xffffu);
+            acked -= acked16;
+#else
+          {
+            acked16 = recv_acked;
+#endif
+            TCP_EVENT_SENT(pcb, (u16_t)acked16, err);
+            if (err == ERR_ABRT) {
+              goto aborted;
+            }
+          }
+          recv_acked = 0;
+        }
+        if (tcp_input_delayed_close(pcb)) {
+          goto aborted;
+        }
+#if TCP_QUEUE_OOSEQ && LWIP_WND_SCALE
+        while (recv_data != NULL) {
+          struct pbuf *rest = NULL;
+          pbuf_split_64k(recv_data, &rest);
+#else /* TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */
+        if (recv_data != NULL) {
+#endif /* TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */
+
+          LWIP_ASSERT("pcb->refused_data == NULL", pcb->refused_data == NULL);
+          if (pcb->flags & TF_RXCLOSED) {
+            /* received data although already closed -> abort (send RST) to
+               notify the remote host that not all data has been processed */
+            pbuf_free(recv_data);
+#if TCP_QUEUE_OOSEQ && LWIP_WND_SCALE
+            if (rest != NULL) {
+              pbuf_free(rest);
+            }
+#endif /* TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */
+            tcp_abort(pcb);
+            goto aborted;
+          }
+
+          /* Notify application that data has been received. */
+          TCP_EVENT_RECV(pcb, recv_data, ERR_OK, err);
+          if (err == ERR_ABRT) {
+#if TCP_QUEUE_OOSEQ && LWIP_WND_SCALE
+            if (rest != NULL) {
+              pbuf_free(rest);
+            }
+#endif /* TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */
+            goto aborted;
+          }
+
+          /* If the upper layer can't receive this data, store it */
+          if (err != ERR_OK) {
+#if TCP_QUEUE_OOSEQ && LWIP_WND_SCALE
+            if (rest != NULL) {
+              pbuf_cat(recv_data, rest);
+            }
+#endif /* TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */
+            pcb->refused_data = recv_data;
+            LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: keep incoming packet, because pcb is \"full\"\n"));
+#if TCP_QUEUE_OOSEQ && LWIP_WND_SCALE
+            break;
+          } else {
+            /* Upper layer received the data, go on with the rest if > 64K */
+            recv_data = rest;
+#endif /* TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */
+          }
+        }
+
+        /* If a FIN segment was received, we call the callback
+           function with a NULL buffer to indicate EOF. */
+        if (recv_flags & TF_GOT_FIN) {
+          if (pcb->refused_data != NULL) {
+            /* Delay this if we have refused data. */
+            pcb->refused_data->flags |= PBUF_FLAG_TCP_FIN;
+          } else {
+            /* correct rcv_wnd as the application won't call tcp_recved()
+               for the FIN's seqno */
+            if (pcb->rcv_wnd != TCP_WND_MAX(pcb)) {
+              pcb->rcv_wnd++;
+            }
+            TCP_EVENT_CLOSED(pcb, err);
+            if (err == ERR_ABRT) {
+              goto aborted;
+            }
+          }
+        }
+
+        tcp_input_pcb = NULL;
+        if (tcp_input_delayed_close(pcb)) {
+          goto aborted;
+        }
+        /* Try to send something out. */
+        tcp_output(pcb);
+#if TCP_INPUT_DEBUG
+#if TCP_DEBUG
+        tcp_debug_print_state(pcb->state);
+#endif /* TCP_DEBUG */
+#endif /* TCP_INPUT_DEBUG */
+      }
+    }
+    /* Jump target if pcb has been aborted in a callback (by calling tcp_abort()).
+       Below this line, 'pcb' may not be dereferenced! */
+aborted:
+    tcp_input_pcb = NULL;
+    recv_data = NULL;
+
+    /* give up our reference to inseg.p */
+    if (inseg.p != NULL) {
+      pbuf_free(inseg.p);
+      inseg.p = NULL;
+    }
+  } else {
+    /* If no matching PCB was found, send a TCP RST (reset) to the
+       sender. */
+    LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_input: no PCB match found, resetting.\n"));
+    if (!(TCPH_FLAGS(tcphdr) & TCP_RST)) {
+      TCP_STATS_INC(tcp.proterr);
+      TCP_STATS_INC(tcp.drop);
+      tcp_rst_netif(ip_data.current_input_netif, ackno, seqno + tcplen, ip_current_dest_addr(),
+              ip_current_src_addr(), tcphdr->dest, tcphdr->src);
+    }
+    pbuf_free(p);
+  }
+
+  LWIP_ASSERT("tcp_input: tcp_pcbs_sane()", tcp_pcbs_sane());
+  PERF_STOP("tcp_input");
+  return;
+dropped:
+  TCP_STATS_INC(tcp.drop);
+  MIB2_STATS_INC(mib2.tcpinerrs);
+  pbuf_free(p);
+}
+
+/** Called from tcp_input to check for TF_CLOSED flag. This results in closing
+ * and deallocating a pcb at the correct place to ensure no one references it
+ * any more.
+ * @returns 1 if the pcb has been closed and deallocated, 0 otherwise
+ */
+static int
+tcp_input_delayed_close(struct tcp_pcb *pcb)
+{
+  LWIP_ASSERT("tcp_input_delayed_close: invalid pcb", pcb != NULL);
+
+  if (recv_flags & TF_CLOSED) {
+    /* The connection has been closed and we will deallocate the
+        PCB. */
+    if (!(pcb->flags & TF_RXCLOSED)) {
+      /* Connection closed although the application has only shut down the
+          tx side: call the PCB's err callback and indicate the closure to
+          ensure the application doesn't continue using the PCB. */
+      TCP_EVENT_ERR(pcb->state, pcb->errf, pcb->callback_arg, ERR_CLSD);
+    }
+    tcp_pcb_remove(&tcp_active_pcbs, pcb);
+    tcp_free(pcb);
+    return 1;
+  }
+  return 0;
+}
+
+/**
+ * Called by tcp_input() when a segment arrives for a listening
+ * connection (from tcp_input()).
+ *
+ * @param pcb the tcp_pcb_listen for which a segment arrived
+ *
+ * @note the segment which arrived is saved in global variables, therefore only the pcb
+ *       involved is passed as a parameter to this function
+ */
+static void
+tcp_listen_input(struct tcp_pcb_listen *pcb)
+{
+  struct tcp_pcb *npcb;
+  u32_t iss;
+  err_t rc;
+
+  if (flags & TCP_RST) {
+    /* An incoming RST should be ignored. Return. */
+    return;
+  }
+
+  LWIP_ASSERT("tcp_listen_input: invalid pcb", pcb != NULL);
+
+  /* In the LISTEN state, we check for incoming SYN segments,
+     creates a new PCB, and responds with a SYN|ACK. */
+  if (flags & TCP_ACK) {
+    /* For incoming segments with the ACK flag set, respond with a
+       RST. */
+    LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_listen_input: ACK in LISTEN, sending reset\n"));
+    tcp_rst_netif(ip_data.current_input_netif, ackno, seqno + tcplen, ip_current_dest_addr(),
+            ip_current_src_addr(), tcphdr->dest, tcphdr->src);
+  } else if (flags & TCP_SYN) {
+    LWIP_DEBUGF(TCP_DEBUG, ("TCP connection request %"U16_F" -> %"U16_F".\n", tcphdr->src, tcphdr->dest));
+#if TCP_LISTEN_BACKLOG
+    if (pcb->accepts_pending >= pcb->backlog) {
+      LWIP_DEBUGF(TCP_DEBUG, ("tcp_listen_input: listen backlog exceeded for port %"U16_F"\n", tcphdr->dest));
+      return;
+    }
+#endif /* TCP_LISTEN_BACKLOG */
+    npcb = tcp_alloc(pcb->prio);
+    /* If a new PCB could not be created (probably due to lack of memory),
+       we don't do anything, but rely on the sender will retransmit the
+       SYN at a time when we have more memory available. */
+    if (npcb == NULL) {
+      err_t err;
+      LWIP_DEBUGF(TCP_DEBUG, ("tcp_listen_input: could not allocate PCB\n"));
+      TCP_STATS_INC(tcp.memerr);
+      TCP_EVENT_ACCEPT(pcb, NULL, pcb->callback_arg, ERR_MEM, err);
+      LWIP_UNUSED_ARG(err); /* err not useful here */
+      return;
+    }
+#if TCP_LISTEN_BACKLOG
+    pcb->accepts_pending++;
+    tcp_set_flags(npcb, TF_BACKLOGPEND);
+#endif /* TCP_LISTEN_BACKLOG */
+    /* Set up the new PCB. */
+    ip_addr_copy(npcb->local_ip, *ip_current_dest_addr());
+    ip_addr_copy(npcb->remote_ip, *ip_current_src_addr());
+    npcb->local_port = pcb->local_port;
+    npcb->remote_port = tcphdr->src;
+    npcb->state = SYN_RCVD;
+    npcb->rcv_nxt = seqno + 1;
+    npcb->rcv_ann_right_edge = npcb->rcv_nxt;
+    iss = tcp_next_iss(npcb);
+    npcb->snd_wl2 = iss;
+    npcb->snd_nxt = iss;
+    npcb->lastack = iss;
+    npcb->snd_lbb = iss;
+    npcb->snd_wl1 = seqno - 1;/* initialise to seqno-1 to force window update */
+    npcb->callback_arg = pcb->callback_arg;
+#if LWIP_CALLBACK_API || TCP_LISTEN_BACKLOG
+    npcb->listener = pcb;
+#endif /* LWIP_CALLBACK_API || TCP_LISTEN_BACKLOG */
+#if LWIP_VLAN_PCP
+    npcb->netif_hints.tci = pcb->netif_hints.tci;
+#endif /* LWIP_VLAN_PCP */
+    /* inherit socket options */
+    npcb->so_options = pcb->so_options & SOF_INHERITED;
+    npcb->netif_idx = pcb->netif_idx;
+    /* Register the new PCB so that we can begin receiving segments
+       for it. */
+    TCP_REG_ACTIVE(npcb);
+
+    /* Parse any options in the SYN. */
+    tcp_parseopt(npcb);
+    npcb->snd_wnd = tcphdr->wnd;
+    npcb->snd_wnd_max = npcb->snd_wnd;
+
+#if TCP_CALCULATE_EFF_SEND_MSS
+    npcb->mss = tcp_eff_send_mss(npcb->mss, &npcb->local_ip, &npcb->remote_ip);
+#endif /* TCP_CALCULATE_EFF_SEND_MSS */
+
+    MIB2_STATS_INC(mib2.tcppassiveopens);
+
+#if LWIP_TCP_PCB_NUM_EXT_ARGS
+    if (tcp_ext_arg_invoke_callbacks_passive_open(pcb, npcb) != ERR_OK) {
+      tcp_abandon(npcb, 0);
+      return;
+    }
+#endif
+
+    /* Send a SYN|ACK together with the MSS option. */
+    rc = tcp_enqueue_flags(npcb, TCP_SYN | TCP_ACK);
+    if (rc != ERR_OK) {
+      tcp_abandon(npcb, 0);
+      return;
+    }
+    tcp_output(npcb);
+  }
+  return;
+}
+
+/**
+ * Called by tcp_input() when a segment arrives for a connection in
+ * TIME_WAIT.
+ *
+ * @param pcb the tcp_pcb for which a segment arrived
+ *
+ * @note the segment which arrived is saved in global variables, therefore only the pcb
+ *       involved is passed as a parameter to this function
+ */
+static void
+tcp_timewait_input(struct tcp_pcb *pcb)
+{
+  /* RFC 1337: in TIME_WAIT, ignore RST and ACK FINs + any 'acceptable' segments */
+  /* RFC 793 3.9 Event Processing - Segment Arrives:
+   * - first check sequence number - we skip that one in TIME_WAIT (always
+   *   acceptable since we only send ACKs)
+   * - second check the RST bit (... return) */
+  if (flags & TCP_RST) {
+    return;
+  }
+
+  LWIP_ASSERT("tcp_timewait_input: invalid pcb", pcb != NULL);
+
+  /* - fourth, check the SYN bit, */
+  if (flags & TCP_SYN) {
+    /* If an incoming segment is not acceptable, an acknowledgment
+       should be sent in reply */
+    if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, pcb->rcv_nxt + pcb->rcv_wnd)) {
+      /* If the SYN is in the window it is an error, send a reset */
+      tcp_rst(pcb, ackno, seqno + tcplen, ip_current_dest_addr(),
+              ip_current_src_addr(), tcphdr->dest, tcphdr->src);
+      return;
+    }
+  } else if (flags & TCP_FIN) {
+    /* - eighth, check the FIN bit: Remain in the TIME-WAIT state.
+         Restart the 2 MSL time-wait timeout.*/
+    pcb->tmr = tcp_ticks;
+  }
+
+  if ((tcplen > 0)) {
+    /* Acknowledge data, FIN or out-of-window SYN */
+    tcp_ack_now(pcb);
+    tcp_output(pcb);
+  }
+  return;
+}
+
+/**
+ * Implements the TCP state machine. Called by tcp_input. In some
+ * states tcp_receive() is called to receive data. The tcp_seg
+ * argument will be freed by the caller (tcp_input()) unless the
+ * recv_data pointer in the pcb is set.
+ *
+ * @param pcb the tcp_pcb for which a segment arrived
+ *
+ * @note the segment which arrived is saved in global variables, therefore only the pcb
+ *       involved is passed as a parameter to this function
+ */
+static err_t
+tcp_process(struct tcp_pcb *pcb)
+{
+  struct tcp_seg *rseg;
+  u8_t acceptable = 0;
+  err_t err;
+
+  err = ERR_OK;
+
+  LWIP_ASSERT("tcp_process: invalid pcb", pcb != NULL);
+
+  /* Process incoming RST segments. */
+  if (flags & TCP_RST) {
+    /* First, determine if the reset is acceptable. */
+    if (pcb->state == SYN_SENT) {
+      /* "In the SYN-SENT state (a RST received in response to an initial SYN),
+          the RST is acceptable if the ACK field acknowledges the SYN." */
+      if (ackno == pcb->snd_nxt) {
+        acceptable = 1;
+      }
+    } else {
+      /* "In all states except SYN-SENT, all reset (RST) segments are validated
+          by checking their SEQ-fields." */
+      if (seqno == pcb->rcv_nxt) {
+        acceptable = 1;
+      } else  if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt,
+                                  pcb->rcv_nxt + pcb->rcv_wnd)) {
+        /* If the sequence number is inside the window, we send a challenge ACK
+           and wait for a re-send with matching sequence number.
+           This follows RFC 5961 section 3.2 and addresses CVE-2004-0230
+           (RST spoofing attack), which is present in RFC 793 RST handling. */
+        tcp_ack_now(pcb);
+      }
+    }
+
+    if (acceptable) {
+      LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_process: Connection RESET\n"));
+      LWIP_ASSERT("tcp_input: pcb->state != CLOSED", pcb->state != CLOSED);
+      recv_flags |= TF_RESET;
+      tcp_clear_flags(pcb, TF_ACK_DELAY);
+      return ERR_RST;
+    } else {
+      LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_process: unacceptable reset seqno %"U32_F" rcv_nxt %"U32_F"\n",
+                                    seqno, pcb->rcv_nxt));
+      LWIP_DEBUGF(TCP_DEBUG, ("tcp_process: unacceptable reset seqno %"U32_F" rcv_nxt %"U32_F"\n",
+                              seqno, pcb->rcv_nxt));
+      return ERR_OK;
+    }
+  }
+
+  if ((flags & TCP_SYN) && (pcb->state != SYN_SENT && pcb->state != SYN_RCVD)) {
+    /* Cope with new connection attempt after remote end crashed */
+    tcp_ack_now(pcb);
+    return ERR_OK;
+  }
+
+  if ((pcb->flags & TF_RXCLOSED) == 0) {
+    /* Update the PCB (in)activity timer unless rx is closed (see tcp_shutdown) */
+    pcb->tmr = tcp_ticks;
+  }
+  pcb->keep_cnt_sent = 0;
+  pcb->persist_probe = 0;
+
+  tcp_parseopt(pcb);
+
+  if (flags & TCP_SYN) {
+    /* accept SYN only in 2 states: */
+    if ((pcb->state != SYN_SENT) && (pcb->state != SYN_RCVD)) {
+      return ERR_OK;
+    }
+  }
+
+  /* Do different things depending on the TCP state. */
+  switch (pcb->state) {
+    case SYN_SENT:
+      LWIP_DEBUGF(TCP_INPUT_DEBUG, ("SYN-SENT: ackno %"U32_F" pcb->snd_nxt %"U32_F" unacked %s %"U32_F"\n",
+                                    ackno, pcb->snd_nxt, pcb->unacked ? "" : " empty:",
+                                    pcb->unacked ? lwip_ntohl(pcb->unacked->tcphdr->seqno) : 0));
+      /* received SYN ACK with expected sequence number? */
+      if ((flags & TCP_ACK) && (flags & TCP_SYN)
+          && (ackno == pcb->lastack + 1)) {
+        pcb->rcv_nxt = seqno + 1;
+        pcb->rcv_ann_right_edge = pcb->rcv_nxt;
+        pcb->lastack = ackno;
+        pcb->snd_wnd = tcphdr->wnd;
+        pcb->snd_wnd_max = pcb->snd_wnd;
+        pcb->snd_wl1 = seqno - 1; /* initialise to seqno - 1 to force window update */
+        pcb->state = ESTABLISHED;
+
+#if TCP_CALCULATE_EFF_SEND_MSS
+        pcb->mss = tcp_eff_send_mss(pcb->mss, &pcb->local_ip, &pcb->remote_ip);
+#endif /* TCP_CALCULATE_EFF_SEND_MSS */
+
+        pcb->cwnd = LWIP_TCP_CALC_INITIAL_CWND(pcb->mss);
+        LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_process (SENT): cwnd %"TCPWNDSIZE_F
+                                     " ssthresh %"TCPWNDSIZE_F"\n",
+                                     pcb->cwnd, pcb->ssthresh));
+        LWIP_ASSERT("pcb->snd_queuelen > 0", (pcb->snd_queuelen > 0));
+        --pcb->snd_queuelen;
+        LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_process: SYN-SENT --queuelen %"TCPWNDSIZE_F"\n", (tcpwnd_size_t)pcb->snd_queuelen));
+        rseg = pcb->unacked;
+        if (rseg == NULL) {
+          /* might happen if tcp_output fails in tcp_rexmit_rto()
+             in which case the segment is on the unsent list */
+          rseg = pcb->unsent;
+          LWIP_ASSERT("no segment to free", rseg != NULL);
+          pcb->unsent = rseg->next;
+        } else {
+          pcb->unacked = rseg->next;
+        }
+        tcp_seg_free(rseg);
+
+        /* If there's nothing left to acknowledge, stop the retransmit
+           timer, otherwise reset it to start again */
+        if (pcb->unacked == NULL) {
+          pcb->rtime = -1;
+        } else {
+          pcb->rtime = 0;
+          pcb->nrtx = 0;
+        }
+
+        /* Call the user specified function to call when successfully
+         * connected. */
+        TCP_EVENT_CONNECTED(pcb, ERR_OK, err);
+        if (err == ERR_ABRT) {
+          return ERR_ABRT;
+        }
+        tcp_ack_now(pcb);
+      }
+      /* received ACK? possibly a half-open connection */
+      else if (flags & TCP_ACK) {
+        /* send a RST to bring the other side in a non-synchronized state. */
+        tcp_rst(pcb, ackno, seqno + tcplen, ip_current_dest_addr(),
+                ip_current_src_addr(), tcphdr->dest, tcphdr->src);
+        /* Resend SYN immediately (don't wait for rto timeout) to establish
+          connection faster, but do not send more SYNs than we otherwise would
+          have, or we might get caught in a loop on loopback interfaces. */
+        if (pcb->nrtx < TCP_SYNMAXRTX) {
+          pcb->rtime = 0;
+          tcp_rexmit_rto(pcb);
+        }
+      }
+      break;
+    case SYN_RCVD:
+      if (flags & TCP_SYN) {
+        if (seqno == pcb->rcv_nxt - 1) {
+          /* Looks like another copy of the SYN - retransmit our SYN-ACK */
+          tcp_rexmit(pcb);
+        }
+      } else if (flags & TCP_ACK) {
+        /* expected ACK number? */
+        if (TCP_SEQ_BETWEEN(ackno, pcb->lastack + 1, pcb->snd_nxt)) {
+          pcb->state = ESTABLISHED;
+          LWIP_DEBUGF(TCP_DEBUG, ("TCP connection established %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
+#if LWIP_CALLBACK_API || TCP_LISTEN_BACKLOG
+          if (pcb->listener == NULL) {
+            /* listen pcb might be closed by now */
+            err = ERR_VAL;
+          } else
+#endif /* LWIP_CALLBACK_API || TCP_LISTEN_BACKLOG */
+          {
+#if LWIP_CALLBACK_API
+            LWIP_ASSERT("pcb->listener->accept != NULL", pcb->listener->accept != NULL);
+#endif
+            tcp_backlog_accepted(pcb);
+            /* Call the accept function. */
+            TCP_EVENT_ACCEPT(pcb->listener, pcb, pcb->callback_arg, ERR_OK, err);
+          }
+          if (err != ERR_OK) {
+            /* If the accept function returns with an error, we abort
+             * the connection. */
+            /* Already aborted? */
+            if (err != ERR_ABRT) {
+              tcp_abort(pcb);
+            }
+            return ERR_ABRT;
+          }
+          /* If there was any data contained within this ACK,
+           * we'd better pass it on to the application as well. */
+          tcp_receive(pcb);
+
+          /* Prevent ACK for SYN to generate a sent event */
+          if (recv_acked != 0) {
+            recv_acked--;
+          }
+
+          pcb->cwnd = LWIP_TCP_CALC_INITIAL_CWND(pcb->mss);
+          LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_process (SYN_RCVD): cwnd %"TCPWNDSIZE_F
+                                       " ssthresh %"TCPWNDSIZE_F"\n",
+                                       pcb->cwnd, pcb->ssthresh));
+
+          if (recv_flags & TF_GOT_FIN) {
+            tcp_ack_now(pcb);
+            pcb->state = CLOSE_WAIT;
+          }
+        } else {
+          /* incorrect ACK number, send RST */
+          tcp_rst(pcb, ackno, seqno + tcplen, ip_current_dest_addr(),
+                  ip_current_src_addr(), tcphdr->dest, tcphdr->src);
+        }
+      }
+      break;
+    case CLOSE_WAIT:
+    /* FALLTHROUGH */
+    case ESTABLISHED:
+      tcp_receive(pcb);
+      if (recv_flags & TF_GOT_FIN) { /* passive close */
+        tcp_ack_now(pcb);
+        pcb->state = CLOSE_WAIT;
+      }
+      break;
+    case FIN_WAIT_1:
+      tcp_receive(pcb);
+      if (recv_flags & TF_GOT_FIN) {
+        if ((flags & TCP_ACK) && (ackno == pcb->snd_nxt) &&
+            pcb->unsent == NULL) {
+          LWIP_DEBUGF(TCP_DEBUG,
+                      ("TCP connection closed: FIN_WAIT_1 %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
+          tcp_ack_now(pcb);
+          tcp_pcb_purge(pcb);
+          TCP_RMV_ACTIVE(pcb);
+          pcb->state = TIME_WAIT;
+          TCP_REG(&tcp_tw_pcbs, pcb);
+        } else {
+          tcp_ack_now(pcb);
+          pcb->state = CLOSING;
+        }
+      } else if ((flags & TCP_ACK) && (ackno == pcb->snd_nxt) &&
+                 pcb->unsent == NULL) {
+        pcb->state = FIN_WAIT_2;
+      }
+      break;
+    case FIN_WAIT_2:
+      tcp_receive(pcb);
+      if (recv_flags & TF_GOT_FIN) {
+        LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: FIN_WAIT_2 %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
+        tcp_ack_now(pcb);
+        tcp_pcb_purge(pcb);
+        TCP_RMV_ACTIVE(pcb);
+        pcb->state = TIME_WAIT;
+        TCP_REG(&tcp_tw_pcbs, pcb);
+      }
+      break;
+    case CLOSING:
+      tcp_receive(pcb);
+      if ((flags & TCP_ACK) && ackno == pcb->snd_nxt && pcb->unsent == NULL) {
+        LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: CLOSING %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
+        tcp_pcb_purge(pcb);
+        TCP_RMV_ACTIVE(pcb);
+        pcb->state = TIME_WAIT;
+        TCP_REG(&tcp_tw_pcbs, pcb);
+      }
+      break;
+    case LAST_ACK:
+      tcp_receive(pcb);
+      if ((flags & TCP_ACK) && ackno == pcb->snd_nxt && pcb->unsent == NULL) {
+        LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: LAST_ACK %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
+        /* bugfix #21699: don't set pcb->state to CLOSED here or we risk leaking segments */
+        recv_flags |= TF_CLOSED;
+      }
+      break;
+    default:
+      break;
+  }
+  return ERR_OK;
+}
+
+#if TCP_QUEUE_OOSEQ
+/**
+ * Insert segment into the list (segments covered with new one will be deleted)
+ *
+ * Called from tcp_receive()
+ */
+static void
+tcp_oos_insert_segment(struct tcp_seg *cseg, struct tcp_seg *next)
+{
+  struct tcp_seg *old_seg;
+
+  LWIP_ASSERT("tcp_oos_insert_segment: invalid cseg", cseg != NULL);
+
+  if (TCPH_FLAGS(cseg->tcphdr) & TCP_FIN) {
+    /* received segment overlaps all following segments */
+    tcp_segs_free(next);
+    next = NULL;
+  } else {
+    /* delete some following segments
+       oos queue may have segments with FIN flag */
+    while (next &&
+           TCP_SEQ_GEQ((seqno + cseg->len),
+                       (next->tcphdr->seqno + next->len))) {
+      /* cseg with FIN already processed */
+      if (TCPH_FLAGS(next->tcphdr) & TCP_FIN) {
+        TCPH_SET_FLAG(cseg->tcphdr, TCP_FIN);
+      }
+      old_seg = next;
+      next = next->next;
+      tcp_seg_free(old_seg);
+    }
+    if (next &&
+        TCP_SEQ_GT(seqno + cseg->len, next->tcphdr->seqno)) {
+      /* We need to trim the incoming segment. */
+      cseg->len = (u16_t)(next->tcphdr->seqno - seqno);
+      pbuf_realloc(cseg->p, cseg->len);
+    }
+  }
+  cseg->next = next;
+}
+#endif /* TCP_QUEUE_OOSEQ */
+
+/** Remove segments from a list if the incoming ACK acknowledges them */
+static struct tcp_seg *
+tcp_free_acked_segments(struct tcp_pcb *pcb, struct tcp_seg *seg_list, const char *dbg_list_name,
+                        struct tcp_seg *dbg_other_seg_list)
+{
+  struct tcp_seg *next;
+  u16_t clen;
+
+  LWIP_UNUSED_ARG(dbg_list_name);
+  LWIP_UNUSED_ARG(dbg_other_seg_list);
+
+  while (seg_list != NULL &&
+         TCP_SEQ_LEQ(lwip_ntohl(seg_list->tcphdr->seqno) +
+                     TCP_TCPLEN(seg_list), ackno)) {
+    LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: removing %"U32_F":%"U32_F" from pcb->%s\n",
+                                  lwip_ntohl(seg_list->tcphdr->seqno),
+                                  lwip_ntohl(seg_list->tcphdr->seqno) + TCP_TCPLEN(seg_list),
+                                  dbg_list_name));
+
+    next = seg_list;
+    seg_list = seg_list->next;
+
+    clen = pbuf_clen(next->p);
+    LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_receive: queuelen %"TCPWNDSIZE_F" ... ",
+                                 (tcpwnd_size_t)pcb->snd_queuelen));
+    LWIP_ASSERT("pcb->snd_queuelen >= pbuf_clen(next->p)", (pcb->snd_queuelen >= clen));
+
+    pcb->snd_queuelen = (u16_t)(pcb->snd_queuelen - clen);
+    recv_acked = (tcpwnd_size_t)(recv_acked + next->len);
+    tcp_seg_free(next);
+
+    LWIP_DEBUGF(TCP_QLEN_DEBUG, ("%"TCPWNDSIZE_F" (after freeing %s)\n",
+                                 (tcpwnd_size_t)pcb->snd_queuelen,
+                                 dbg_list_name));
+    if (pcb->snd_queuelen != 0) {
+      LWIP_ASSERT("tcp_receive: valid queue length",
+                  seg_list != NULL || dbg_other_seg_list != NULL);
+    }
+  }
+  return seg_list;
+}
+
+/**
+ * Called by tcp_process. Checks if the given segment is an ACK for outstanding
+ * data, and if so frees the memory of the buffered data. Next, it places the
+ * segment on any of the receive queues (pcb->recved or pcb->ooseq). If the segment
+ * is buffered, the pbuf is referenced by pbuf_ref so that it will not be freed until
+ * it has been removed from the buffer.
+ *
+ * If the incoming segment constitutes an ACK for a segment that was used for RTT
+ * estimation, the RTT is estimated here as well.
+ *
+ * Called from tcp_process().
+ */
+static void
+tcp_receive(struct tcp_pcb *pcb)
+{
+  s16_t m;
+  u32_t right_wnd_edge;
+
+  LWIP_ASSERT("tcp_receive: invalid pcb", pcb != NULL);
+  LWIP_ASSERT("tcp_receive: wrong state", pcb->state >= ESTABLISHED);
+
+  if (flags & TCP_ACK) {
+    right_wnd_edge = pcb->snd_wnd + pcb->snd_wl2;
+
+    /* Update window. */
+    if (TCP_SEQ_LT(pcb->snd_wl1, seqno) ||
+        (pcb->snd_wl1 == seqno && TCP_SEQ_LT(pcb->snd_wl2, ackno)) ||
+        (pcb->snd_wl2 == ackno && (u32_t)SND_WND_SCALE(pcb, tcphdr->wnd) > pcb->snd_wnd)) {
+      pcb->snd_wnd = SND_WND_SCALE(pcb, tcphdr->wnd);
+      /* keep track of the biggest window announced by the remote host to calculate
+         the maximum segment size */
+      if (pcb->snd_wnd_max < pcb->snd_wnd) {
+        pcb->snd_wnd_max = pcb->snd_wnd;
+      }
+      pcb->snd_wl1 = seqno;
+      pcb->snd_wl2 = ackno;
+      LWIP_DEBUGF(TCP_WND_DEBUG, ("tcp_receive: window update %"TCPWNDSIZE_F"\n", pcb->snd_wnd));
+#if TCP_WND_DEBUG
+    } else {
+      if (pcb->snd_wnd != (tcpwnd_size_t)SND_WND_SCALE(pcb, tcphdr->wnd)) {
+        LWIP_DEBUGF(TCP_WND_DEBUG,
+                    ("tcp_receive: no window update lastack %"U32_F" ackno %"
+                     U32_F" wl1 %"U32_F" seqno %"U32_F" wl2 %"U32_F"\n",
+                     pcb->lastack, ackno, pcb->snd_wl1, seqno, pcb->snd_wl2));
+      }
+#endif /* TCP_WND_DEBUG */
+    }
+
+    /* (From Stevens TCP/IP Illustrated Vol II, p970.) Its only a
+     * duplicate ack if:
+     * 1) It doesn't ACK new data
+     * 2) length of received packet is zero (i.e. no payload)
+     * 3) the advertised window hasn't changed
+     * 4) There is outstanding unacknowledged data (retransmission timer running)
+     * 5) The ACK is == biggest ACK sequence number so far seen (snd_una)
+     *
+     * If it passes all five, should process as a dupack:
+     * a) dupacks < 3: do nothing
+     * b) dupacks == 3: fast retransmit
+     * c) dupacks > 3: increase cwnd
+     *
+     * If it only passes 1-3, should reset dupack counter (and add to
+     * stats, which we don't do in lwIP)
+     *
+     * If it only passes 1, should reset dupack counter
+     *
+     */
+
+    /* Clause 1 */
+    if (TCP_SEQ_LEQ(ackno, pcb->lastack)) {
+      /* Clause 2 */
+      if (tcplen == 0) {
+        /* Clause 3 */
+        if (pcb->snd_wl2 + pcb->snd_wnd == right_wnd_edge) {
+          /* Clause 4 */
+          if (pcb->rtime >= 0) {
+            /* Clause 5 */
+            if (pcb->lastack == ackno) {
+              if ((u8_t)(pcb->dupacks + 1) > pcb->dupacks) {
+                ++pcb->dupacks;
+              }
+              if (pcb->dupacks > 3) {
+                /* Inflate the congestion window */
+                TCP_WND_INC(pcb->cwnd, pcb->mss);
+              }
+              if (pcb->dupacks >= 3) {
+                /* Do fast retransmit (checked via TF_INFR, not via dupacks count) */
+                tcp_rexmit_fast(pcb);
+              }
+            }
+          }
+        }
+      }
+    } else if (TCP_SEQ_BETWEEN(ackno, pcb->lastack + 1, pcb->snd_nxt)) {
+      /* We come here when the ACK acknowledges new data. */
+      tcpwnd_size_t acked;
+
+      /* Reset the "IN Fast Retransmit" flag, since we are no longer
+         in fast retransmit. Also reset the congestion window to the
+         slow start threshold. */
+      if (pcb->flags & TF_INFR) {
+        tcp_clear_flags(pcb, TF_INFR);
+        pcb->cwnd = pcb->ssthresh;
+        pcb->bytes_acked = 0;
+      }
+
+      /* Reset the number of retransmissions. */
+      pcb->nrtx = 0;
+
+      /* Reset the retransmission time-out. */
+      pcb->rto = (s16_t)((pcb->sa >> 3) + pcb->sv);
+
+      /* Record how much data this ACK acks */
+      acked = (tcpwnd_size_t)(ackno - pcb->lastack);
+
+      /* Reset the fast retransmit variables. */
+      pcb->dupacks = 0;
+      pcb->lastack = ackno;
+
+      /* Update the congestion control variables (cwnd and
+         ssthresh). */
+      if (pcb->state >= ESTABLISHED) {
+        if (pcb->cwnd < pcb->ssthresh) {
+          tcpwnd_size_t increase;
+          /* limit to 1 SMSS segment during period following RTO */
+          u8_t num_seg = (pcb->flags & TF_RTO) ? 1 : 2;
+          /* RFC 3465, section 2.2 Slow Start */
+          increase = LWIP_MIN(acked, (tcpwnd_size_t)(num_seg * pcb->mss));
+          TCP_WND_INC(pcb->cwnd, increase);
+          LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_receive: slow start cwnd %"TCPWNDSIZE_F"\n", pcb->cwnd));
+        } else {
+          /* RFC 3465, section 2.1 Congestion Avoidance */
+          TCP_WND_INC(pcb->bytes_acked, acked);
+          if (pcb->bytes_acked >= pcb->cwnd) {
+            pcb->bytes_acked = (tcpwnd_size_t)(pcb->bytes_acked - pcb->cwnd);
+            TCP_WND_INC(pcb->cwnd, pcb->mss);
+          }
+          LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_receive: congestion avoidance cwnd %"TCPWNDSIZE_F"\n", pcb->cwnd));
+        }
+      }
+      LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: ACK for %"U32_F", unacked->seqno %"U32_F":%"U32_F"\n",
+                                    ackno,
+                                    pcb->unacked != NULL ?
+                                    lwip_ntohl(pcb->unacked->tcphdr->seqno) : 0,
+                                    pcb->unacked != NULL ?
+                                    lwip_ntohl(pcb->unacked->tcphdr->seqno) + TCP_TCPLEN(pcb->unacked) : 0));
+
+      /* Remove segment from the unacknowledged list if the incoming
+         ACK acknowledges them. */
+      pcb->unacked = tcp_free_acked_segments(pcb, pcb->unacked, "unacked", pcb->unsent);
+      /* We go through the ->unsent list to see if any of the segments
+         on the list are acknowledged by the ACK. This may seem
+         strange since an "unsent" segment shouldn't be acked. The
+         rationale is that lwIP puts all outstanding segments on the
+         ->unsent list after a retransmission, so these segments may
+         in fact have been sent once. */
+      pcb->unsent = tcp_free_acked_segments(pcb, pcb->unsent, "unsent", pcb->unacked);
+
+      /* If there's nothing left to acknowledge, stop the retransmit
+         timer, otherwise reset it to start again */
+      if (pcb->unacked == NULL) {
+        pcb->rtime = -1;
+      } else {
+        pcb->rtime = 0;
+      }
+
+      pcb->polltmr = 0;
+
+#if TCP_OVERSIZE
+      if (pcb->unsent == NULL) {
+        pcb->unsent_oversize = 0;
+      }
+#endif /* TCP_OVERSIZE */
+
+#if LWIP_IPV6 && LWIP_ND6_TCP_REACHABILITY_HINTS
+      if (ip_current_is_v6()) {
+        /* Inform neighbor reachability of forward progress. */
+        nd6_reachability_hint(ip6_current_src_addr());
+      }
+#endif /* LWIP_IPV6 && LWIP_ND6_TCP_REACHABILITY_HINTS*/
+
+      pcb->snd_buf = (tcpwnd_size_t)(pcb->snd_buf + recv_acked);
+      /* check if this ACK ends our retransmission of in-flight data */
+      if (pcb->flags & TF_RTO) {
+        /* RTO is done if
+            1) both queues are empty or
+            2) unacked is empty and unsent head contains data not part of RTO or
+            3) unacked head contains data not part of RTO */
+        if (pcb->unacked == NULL) {
+          if ((pcb->unsent == NULL) ||
+              (TCP_SEQ_LEQ(pcb->rto_end, lwip_ntohl(pcb->unsent->tcphdr->seqno)))) {
+            tcp_clear_flags(pcb, TF_RTO);
+          }
+        } else if (TCP_SEQ_LEQ(pcb->rto_end, lwip_ntohl(pcb->unacked->tcphdr->seqno))) {
+          tcp_clear_flags(pcb, TF_RTO);
+        }
+      }
+      /* End of ACK for new data processing. */
+    } else {
+      /* Out of sequence ACK, didn't really ack anything */
+      tcp_send_empty_ack(pcb);
+    }
+
+    LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: pcb->rttest %"U32_F" rtseq %"U32_F" ackno %"U32_F"\n",
+                                pcb->rttest, pcb->rtseq, ackno));
+
+    /* RTT estimation calculations. This is done by checking if the
+       incoming segment acknowledges the segment we use to take a
+       round-trip time measurement. */
+    if (pcb->rttest && TCP_SEQ_LT(pcb->rtseq, ackno)) {
+      /* diff between this shouldn't exceed 32K since this are tcp timer ticks
+         and a round-trip shouldn't be that long... */
+      m = (s16_t)(tcp_ticks - pcb->rttest);
+
+      LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: experienced rtt %"U16_F" ticks (%"U16_F" msec).\n",
+                                  m, (u16_t)(m * TCP_SLOW_INTERVAL)));
+
+      /* This is taken directly from VJs original code in his paper */
+      m = (s16_t)(m - (pcb->sa >> 3));
+      pcb->sa = (s16_t)(pcb->sa + m);
+      if (m < 0) {
+        m = (s16_t) - m;
+      }
+      m = (s16_t)(m - (pcb->sv >> 2));
+      pcb->sv = (s16_t)(pcb->sv + m);
+      pcb->rto = (s16_t)((pcb->sa >> 3) + pcb->sv);
+
+      LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: RTO %"U16_F" (%"U16_F" milliseconds)\n",
+                                  pcb->rto, (u16_t)(pcb->rto * TCP_SLOW_INTERVAL)));
+
+      pcb->rttest = 0;
+    }
+  }
+
+  /* If the incoming segment contains data, we must process it
+     further unless the pcb already received a FIN.
+     (RFC 793, chapter 3.9, "SEGMENT ARRIVES" in states CLOSE-WAIT, CLOSING,
+     LAST-ACK and TIME-WAIT: "Ignore the segment text.") */
+  if ((tcplen > 0) && (pcb->state < CLOSE_WAIT)) {
+    /* This code basically does three things:
+
+    +) If the incoming segment contains data that is the next
+    in-sequence data, this data is passed to the application. This
+    might involve trimming the first edge of the data. The rcv_nxt
+    variable and the advertised window are adjusted.
+
+    +) If the incoming segment has data that is above the next
+    sequence number expected (->rcv_nxt), the segment is placed on
+    the ->ooseq queue. This is done by finding the appropriate
+    place in the ->ooseq queue (which is ordered by sequence
+    number) and trim the segment in both ends if needed. An
+    immediate ACK is sent to indicate that we received an
+    out-of-sequence segment.
+
+    +) Finally, we check if the first segment on the ->ooseq queue
+    now is in sequence (i.e., if rcv_nxt >= ooseq->seqno). If
+    rcv_nxt > ooseq->seqno, we must trim the first edge of the
+    segment on ->ooseq before we adjust rcv_nxt. The data in the
+    segments that are now on sequence are chained onto the
+    incoming segment so that we only need to call the application
+    once.
+    */
+
+    /* First, we check if we must trim the first edge. We have to do
+       this if the sequence number of the incoming segment is less
+       than rcv_nxt, and the sequence number plus the length of the
+       segment is larger than rcv_nxt. */
+    /*    if (TCP_SEQ_LT(seqno, pcb->rcv_nxt)) {
+          if (TCP_SEQ_LT(pcb->rcv_nxt, seqno + tcplen)) {*/
+    if (TCP_SEQ_BETWEEN(pcb->rcv_nxt, seqno + 1, seqno + tcplen - 1)) {
+      /* Trimming the first edge is done by pushing the payload
+         pointer in the pbuf downwards. This is somewhat tricky since
+         we do not want to discard the full contents of the pbuf up to
+         the new starting point of the data since we have to keep the
+         TCP header which is present in the first pbuf in the chain.
+
+         What is done is really quite a nasty hack: the first pbuf in
+         the pbuf chain is pointed to by inseg.p. Since we need to be
+         able to deallocate the whole pbuf, we cannot change this
+         inseg.p pointer to point to any of the later pbufs in the
+         chain. Instead, we point the ->payload pointer in the first
+         pbuf to data in one of the later pbufs. We also set the
+         inseg.data pointer to point to the right place. This way, the
+         ->p pointer will still point to the first pbuf, but the
+         ->p->payload pointer will point to data in another pbuf.
+
+         After we are done with adjusting the pbuf pointers we must
+         adjust the ->data pointer in the seg and the segment
+         length.*/
+
+      struct pbuf *p = inseg.p;
+      u32_t off32 = pcb->rcv_nxt - seqno;
+      u16_t new_tot_len, off;
+      LWIP_ASSERT("inseg.p != NULL", inseg.p);
+      LWIP_ASSERT("insane offset!", (off32 < 0xffff));
+      off = (u16_t)off32;
+      LWIP_ASSERT("pbuf too short!", (((s32_t)inseg.p->tot_len) >= off));
+      inseg.len -= off;
+      new_tot_len = (u16_t)(inseg.p->tot_len - off);
+      while (p->len < off) {
+        off -= p->len;
+        /* all pbufs up to and including this one have len==0, so tot_len is equal */
+        p->tot_len = new_tot_len;
+        p->len = 0;
+        p = p->next;
+      }
+      /* cannot fail... */
+      pbuf_remove_header(p, off);
+      inseg.tcphdr->seqno = seqno = pcb->rcv_nxt;
+    } else {
+      if (TCP_SEQ_LT(seqno, pcb->rcv_nxt)) {
+        /* the whole segment is < rcv_nxt */
+        /* must be a duplicate of a packet that has already been correctly handled */
+
+        LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: duplicate seqno %"U32_F"\n", seqno));
+        tcp_ack_now(pcb);
+      }
+    }
+
+    /* The sequence number must be within the window (above rcv_nxt
+       and below rcv_nxt + rcv_wnd) in order to be further
+       processed. */
+    if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt,
+                        pcb->rcv_nxt + pcb->rcv_wnd - 1)) {
+      if (pcb->rcv_nxt == seqno) {
+        /* The incoming segment is the next in sequence. We check if
+           we have to trim the end of the segment and update rcv_nxt
+           and pass the data to the application. */
+        tcplen = TCP_TCPLEN(&inseg);
+
+        if (tcplen > pcb->rcv_wnd) {
+          LWIP_DEBUGF(TCP_INPUT_DEBUG,
+                      ("tcp_receive: other end overran receive window"
+                       "seqno %"U32_F" len %"U16_F" right edge %"U32_F"\n",
+                       seqno, tcplen, pcb->rcv_nxt + pcb->rcv_wnd));
+          if (TCPH_FLAGS(inseg.tcphdr) & TCP_FIN) {
+            /* Must remove the FIN from the header as we're trimming
+             * that byte of sequence-space from the packet */
+            TCPH_FLAGS_SET(inseg.tcphdr, TCPH_FLAGS(inseg.tcphdr) & ~(unsigned int)TCP_FIN);
+          }
+          /* Adjust length of segment to fit in the window. */
+          TCPWND_CHECK16(pcb->rcv_wnd);
+          inseg.len = (u16_t)pcb->rcv_wnd;
+          if (TCPH_FLAGS(inseg.tcphdr) & TCP_SYN) {
+            inseg.len -= 1;
+          }
+          pbuf_realloc(inseg.p, inseg.len);
+          tcplen = TCP_TCPLEN(&inseg);
+          LWIP_ASSERT("tcp_receive: segment not trimmed correctly to rcv_wnd",
+                      (seqno + tcplen) == (pcb->rcv_nxt + pcb->rcv_wnd));
+        }
+#if TCP_QUEUE_OOSEQ
+        /* Received in-sequence data, adjust ooseq data if:
+           - FIN has been received or
+           - inseq overlaps with ooseq */
+        if (pcb->ooseq != NULL) {
+          if (TCPH_FLAGS(inseg.tcphdr) & TCP_FIN) {
+            LWIP_DEBUGF(TCP_INPUT_DEBUG,
+                        ("tcp_receive: received in-order FIN, binning ooseq queue\n"));
+            /* Received in-order FIN means anything that was received
+             * out of order must now have been received in-order, so
+             * bin the ooseq queue */
+            while (pcb->ooseq != NULL) {
+              struct tcp_seg *old_ooseq = pcb->ooseq;
+              pcb->ooseq = pcb->ooseq->next;
+              tcp_seg_free(old_ooseq);
+            }
+          } else {
+            struct tcp_seg *next = pcb->ooseq;
+            /* Remove all segments on ooseq that are covered by inseg already.
+             * FIN is copied from ooseq to inseg if present. */
+            while (next &&
+                   TCP_SEQ_GEQ(seqno + tcplen,
+                               next->tcphdr->seqno + next->len)) {
+              struct tcp_seg *tmp;
+              /* inseg cannot have FIN here (already processed above) */
+              if ((TCPH_FLAGS(next->tcphdr) & TCP_FIN) != 0 &&
+                  (TCPH_FLAGS(inseg.tcphdr) & TCP_SYN) == 0) {
+                TCPH_SET_FLAG(inseg.tcphdr, TCP_FIN);
+                tcplen = TCP_TCPLEN(&inseg);
+              }
+              tmp = next;
+              next = next->next;
+              tcp_seg_free(tmp);
+            }
+            /* Now trim right side of inseg if it overlaps with the first
+             * segment on ooseq */
+            if (next &&
+                TCP_SEQ_GT(seqno + tcplen,
+                           next->tcphdr->seqno)) {
+              /* inseg cannot have FIN here (already processed above) */
+              inseg.len = (u16_t)(next->tcphdr->seqno - seqno);
+              if (TCPH_FLAGS(inseg.tcphdr) & TCP_SYN) {
+                inseg.len -= 1;
+              }
+              pbuf_realloc(inseg.p, inseg.len);
+              tcplen = TCP_TCPLEN(&inseg);
+              LWIP_ASSERT("tcp_receive: segment not trimmed correctly to ooseq queue",
+                          (seqno + tcplen) == next->tcphdr->seqno);
+            }
+            pcb->ooseq = next;
+          }
+        }
+#endif /* TCP_QUEUE_OOSEQ */
+
+        pcb->rcv_nxt = seqno + tcplen;
+
+        /* Update the receiver's (our) window. */
+        LWIP_ASSERT("tcp_receive: tcplen > rcv_wnd", pcb->rcv_wnd >= tcplen);
+        pcb->rcv_wnd -= tcplen;
+
+        tcp_update_rcv_ann_wnd(pcb);
+
+        /* If there is data in the segment, we make preparations to
+           pass this up to the application. The ->recv_data variable
+           is used for holding the pbuf that goes to the
+           application. The code for reassembling out-of-sequence data
+           chains its data on this pbuf as well.
+
+           If the segment was a FIN, we set the TF_GOT_FIN flag that will
+           be used to indicate to the application that the remote side has
+           closed its end of the connection. */
+        if (inseg.p->tot_len > 0) {
+          recv_data = inseg.p;
+          /* Since this pbuf now is the responsibility of the
+             application, we delete our reference to it so that we won't
+             (mistakingly) deallocate it. */
+          inseg.p = NULL;
+        }
+        if (TCPH_FLAGS(inseg.tcphdr) & TCP_FIN) {
+          LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: received FIN.\n"));
+          recv_flags |= TF_GOT_FIN;
+        }
+
+#if TCP_QUEUE_OOSEQ
+        /* We now check if we have segments on the ->ooseq queue that
+           are now in sequence. */
+        while (pcb->ooseq != NULL &&
+               pcb->ooseq->tcphdr->seqno == pcb->rcv_nxt) {
+
+          struct tcp_seg *cseg = pcb->ooseq;
+          seqno = pcb->ooseq->tcphdr->seqno;
+
+          pcb->rcv_nxt += TCP_TCPLEN(cseg);
+          LWIP_ASSERT("tcp_receive: ooseq tcplen > rcv_wnd",
+                      pcb->rcv_wnd >= TCP_TCPLEN(cseg));
+          pcb->rcv_wnd -= TCP_TCPLEN(cseg);
+
+          tcp_update_rcv_ann_wnd(pcb);
+
+          if (cseg->p->tot_len > 0) {
+            /* Chain this pbuf onto the pbuf that we will pass to
+               the application. */
+            /* With window scaling, this can overflow recv_data->tot_len, but
+               that's not a problem since we explicitly fix that before passing
+               recv_data to the application. */
+            if (recv_data) {
+              pbuf_cat(recv_data, cseg->p);
+            } else {
+              recv_data = cseg->p;
+            }
+            cseg->p = NULL;
+          }
+          if (TCPH_FLAGS(cseg->tcphdr) & TCP_FIN) {
+            LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: dequeued FIN.\n"));
+            recv_flags |= TF_GOT_FIN;
+            if (pcb->state == ESTABLISHED) { /* force passive close or we can move to active close */
+              pcb->state = CLOSE_WAIT;
+            }
+          }
+
+          pcb->ooseq = cseg->next;
+          tcp_seg_free(cseg);
+        }
+#if LWIP_TCP_SACK_OUT
+        if (pcb->flags & TF_SACK) {
+          if (pcb->ooseq != NULL) {
+            /* Some segments may have been removed from ooseq, let's remove all SACKs that
+               describe anything before the new beginning of that list. */
+            tcp_remove_sacks_lt(pcb, pcb->ooseq->tcphdr->seqno);
+          } else if (LWIP_TCP_SACK_VALID(pcb, 0)) {
+            /* ooseq has been cleared. Nothing to SACK */
+            memset(pcb->rcv_sacks, 0, sizeof(pcb->rcv_sacks));
+          }
+        }
+#endif /* LWIP_TCP_SACK_OUT */
+#endif /* TCP_QUEUE_OOSEQ */
+
+
+        /* Acknowledge the segment(s). */
+        tcp_ack(pcb);
+
+#if LWIP_TCP_SACK_OUT
+        if (LWIP_TCP_SACK_VALID(pcb, 0)) {
+          /* Normally the ACK for the data received could be piggy-backed on a data packet,
+             but lwIP currently does not support including SACKs in data packets. So we force
+             it to respond with an empty ACK packet (only if there is at least one SACK to be sent).
+             NOTE: tcp_send_empty_ack() on success clears the ACK flags (set by tcp_ack()) */
+          tcp_send_empty_ack(pcb);
+        }
+#endif /* LWIP_TCP_SACK_OUT */
+
+#if LWIP_IPV6 && LWIP_ND6_TCP_REACHABILITY_HINTS
+        if (ip_current_is_v6()) {
+          /* Inform neighbor reachability of forward progress. */
+          nd6_reachability_hint(ip6_current_src_addr());
+        }
+#endif /* LWIP_IPV6 && LWIP_ND6_TCP_REACHABILITY_HINTS*/
+
+      } else {
+        /* We get here if the incoming segment is out-of-sequence. */
+
+#if TCP_QUEUE_OOSEQ
+        /* We queue the segment on the ->ooseq queue. */
+        if (pcb->ooseq == NULL) {
+          pcb->ooseq = tcp_seg_copy(&inseg);
+#if LWIP_TCP_SACK_OUT
+          if (pcb->flags & TF_SACK) {
+            /* All the SACKs should be invalid, so we can simply store the most recent one: */
+            pcb->rcv_sacks[0].left = seqno;
+            pcb->rcv_sacks[0].right = seqno + inseg.len;
+          }
+#endif /* LWIP_TCP_SACK_OUT */
+        } else {
+          /* If the queue is not empty, we walk through the queue and
+             try to find a place where the sequence number of the
+             incoming segment is between the sequence numbers of the
+             previous and the next segment on the ->ooseq queue. That is
+             the place where we put the incoming segment. If needed, we
+             trim the second edges of the previous and the incoming
+             segment so that it will fit into the sequence.
+
+             If the incoming segment has the same sequence number as a
+             segment on the ->ooseq queue, we discard the segment that
+             contains less data. */
+
+#if LWIP_TCP_SACK_OUT
+          /* This is the left edge of the lowest possible SACK range.
+             It may start before the newly received segment (possibly adjusted below). */
+          u32_t sackbeg = TCP_SEQ_LT(seqno, pcb->ooseq->tcphdr->seqno) ? seqno : pcb->ooseq->tcphdr->seqno;
+#endif /* LWIP_TCP_SACK_OUT */
+          struct tcp_seg *next, *prev = NULL;
+          for (next = pcb->ooseq; next != NULL; next = next->next) {
+            if (seqno == next->tcphdr->seqno) {
+              /* The sequence number of the incoming segment is the
+                 same as the sequence number of the segment on
+                 ->ooseq. We check the lengths to see which one to
+                 discard. */
+              if (inseg.len > next->len) {
+                /* The incoming segment is larger than the old
+                   segment. We replace some segments with the new
+                   one. */
+                struct tcp_seg *cseg = tcp_seg_copy(&inseg);
+                if (cseg != NULL) {
+                  if (prev != NULL) {
+                    prev->next = cseg;
+                  } else {
+                    pcb->ooseq = cseg;
+                  }
+                  tcp_oos_insert_segment(cseg, next);
+                }
+                break;
+              } else {
+                /* Either the lengths are the same or the incoming
+                   segment was smaller than the old one; in either
+                   case, we ditch the incoming segment. */
+                break;
+              }
+            } else {
+              if (prev == NULL) {
+                if (TCP_SEQ_LT(seqno, next->tcphdr->seqno)) {
+                  /* The sequence number of the incoming segment is lower
+                     than the sequence number of the first segment on the
+                     queue. We put the incoming segment first on the
+                     queue. */
+                  struct tcp_seg *cseg = tcp_seg_copy(&inseg);
+                  if (cseg != NULL) {
+                    pcb->ooseq = cseg;
+                    tcp_oos_insert_segment(cseg, next);
+                  }
+                  break;
+                }
+              } else {
+                /*if (TCP_SEQ_LT(prev->tcphdr->seqno, seqno) &&
+                  TCP_SEQ_LT(seqno, next->tcphdr->seqno)) {*/
+                if (TCP_SEQ_BETWEEN(seqno, prev->tcphdr->seqno + 1, next->tcphdr->seqno - 1)) {
+                  /* The sequence number of the incoming segment is in
+                     between the sequence numbers of the previous and
+                     the next segment on ->ooseq. We trim trim the previous
+                     segment, delete next segments that included in received segment
+                     and trim received, if needed. */
+                  struct tcp_seg *cseg = tcp_seg_copy(&inseg);
+                  if (cseg != NULL) {
+                    if (TCP_SEQ_GT(prev->tcphdr->seqno + prev->len, seqno)) {
+                      /* We need to trim the prev segment. */
+                      prev->len = (u16_t)(seqno - prev->tcphdr->seqno);
+                      pbuf_realloc(prev->p, prev->len);
+                    }
+                    prev->next = cseg;
+                    tcp_oos_insert_segment(cseg, next);
+                  }
+                  break;
+                }
+              }
+
+#if LWIP_TCP_SACK_OUT
+              /* The new segment goes after the 'next' one. If there is a "hole" in sequence numbers
+                 between 'prev' and the beginning of 'next', we want to move sackbeg. */
+              if (prev != NULL && prev->tcphdr->seqno + prev->len != next->tcphdr->seqno) {
+                sackbeg = next->tcphdr->seqno;
+              }
+#endif /* LWIP_TCP_SACK_OUT */
+
+              /* We don't use 'prev' below, so let's set it to current 'next'.
+                 This way even if we break the loop below, 'prev' will be pointing
+                 at the segment right in front of the newly added one. */
+              prev = next;
+
+              /* If the "next" segment is the last segment on the
+                 ooseq queue, we add the incoming segment to the end
+                 of the list. */
+              if (next->next == NULL &&
+                  TCP_SEQ_GT(seqno, next->tcphdr->seqno)) {
+                if (TCPH_FLAGS(next->tcphdr) & TCP_FIN) {
+                  /* segment "next" already contains all data */
+                  break;
+                }
+                next->next = tcp_seg_copy(&inseg);
+                if (next->next != NULL) {
+                  if (TCP_SEQ_GT(next->tcphdr->seqno + next->len, seqno)) {
+                    /* We need to trim the last segment. */
+                    next->len = (u16_t)(seqno - next->tcphdr->seqno);
+                    pbuf_realloc(next->p, next->len);
+                  }
+                  /* check if the remote side overruns our receive window */
+                  if (TCP_SEQ_GT((u32_t)tcplen + seqno, pcb->rcv_nxt + (u32_t)pcb->rcv_wnd)) {
+                    LWIP_DEBUGF(TCP_INPUT_DEBUG,
+                                ("tcp_receive: other end overran receive window"
+                                 "seqno %"U32_F" len %"U16_F" right edge %"U32_F"\n",
+                                 seqno, tcplen, pcb->rcv_nxt + pcb->rcv_wnd));
+                    if (TCPH_FLAGS(next->next->tcphdr) & TCP_FIN) {
+                      /* Must remove the FIN from the header as we're trimming
+                       * that byte of sequence-space from the packet */
+                      TCPH_FLAGS_SET(next->next->tcphdr, TCPH_FLAGS(next->next->tcphdr) & ~TCP_FIN);
+                    }
+                    /* Adjust length of segment to fit in the window. */
+                    next->next->len = (u16_t)(pcb->rcv_nxt + pcb->rcv_wnd - seqno);
+                    pbuf_realloc(next->next->p, next->next->len);
+                    tcplen = TCP_TCPLEN(next->next);
+                    LWIP_ASSERT("tcp_receive: segment not trimmed correctly to rcv_wnd",
+                                (seqno + tcplen) == (pcb->rcv_nxt + pcb->rcv_wnd));
+                  }
+                }
+                break;
+              }
+            }
+          }
+
+#if LWIP_TCP_SACK_OUT
+          if (pcb->flags & TF_SACK) {
+            if (prev == NULL) {
+              /* The new segment is at the beginning. sackbeg should already be set properly.
+                 We need to find the right edge. */
+              next = pcb->ooseq;
+            } else if (prev->next != NULL) {
+              /* The new segment was added after 'prev'. If there is a "hole" between 'prev' and 'prev->next',
+                 we need to move sackbeg. After that we should find the right edge. */
+              next = prev->next;
+              if (prev->tcphdr->seqno + prev->len != next->tcphdr->seqno) {
+                sackbeg = next->tcphdr->seqno;
+              }
+            } else {
+              next = NULL;
+            }
+            if (next != NULL) {
+              u32_t sackend = next->tcphdr->seqno;
+              for ( ; (next != NULL) && (sackend == next->tcphdr->seqno); next = next->next) {
+                sackend += next->len;
+              }
+              tcp_add_sack(pcb, sackbeg, sackend);
+            }
+          }
+#endif /* LWIP_TCP_SACK_OUT */
+        }
+#if defined(TCP_OOSEQ_BYTES_LIMIT) || defined(TCP_OOSEQ_PBUFS_LIMIT)
+        {
+          /* Check that the data on ooseq doesn't exceed one of the limits
+             and throw away everything above that limit. */
+#ifdef TCP_OOSEQ_BYTES_LIMIT
+          const u32_t ooseq_max_blen = TCP_OOSEQ_BYTES_LIMIT(pcb);
+          u32_t ooseq_blen = 0;
+#endif
+#ifdef TCP_OOSEQ_PBUFS_LIMIT
+          const u16_t ooseq_max_qlen = TCP_OOSEQ_PBUFS_LIMIT(pcb);
+          u16_t ooseq_qlen = 0;
+#endif
+          struct tcp_seg *next, *prev = NULL;
+          for (next = pcb->ooseq; next != NULL; prev = next, next = next->next) {
+            struct pbuf *p = next->p;
+            int stop_here = 0;
+#ifdef TCP_OOSEQ_BYTES_LIMIT
+            ooseq_blen += p->tot_len;
+            if (ooseq_blen > ooseq_max_blen) {
+              stop_here = 1;
+            }
+#endif
+#ifdef TCP_OOSEQ_PBUFS_LIMIT
+            ooseq_qlen += pbuf_clen(p);
+            if (ooseq_qlen > ooseq_max_qlen) {
+              stop_here = 1;
+            }
+#endif
+            if (stop_here) {
+#if LWIP_TCP_SACK_OUT
+              if (pcb->flags & TF_SACK) {
+                /* Let's remove all SACKs from next's seqno up. */
+                tcp_remove_sacks_gt(pcb, next->tcphdr->seqno);
+              }
+#endif /* LWIP_TCP_SACK_OUT */
+              /* too much ooseq data, dump this and everything after it */
+              tcp_segs_free(next);
+              if (prev == NULL) {
+                /* first ooseq segment is too much, dump the whole queue */
+                pcb->ooseq = NULL;
+              } else {
+                /* just dump 'next' and everything after it */
+                prev->next = NULL;
+              }
+              break;
+            }
+          }
+        }
+#endif /* TCP_OOSEQ_BYTES_LIMIT || TCP_OOSEQ_PBUFS_LIMIT */
+#endif /* TCP_QUEUE_OOSEQ */
+
+        /* We send the ACK packet after we've (potentially) dealt with SACKs,
+           so they can be included in the acknowledgment. */
+        tcp_send_empty_ack(pcb);
+      }
+    } else {
+      /* The incoming segment is not within the window. */
+      tcp_send_empty_ack(pcb);
+    }
+  } else {
+    /* Segments with length 0 is taken care of here. Segments that
+       fall out of the window are ACKed. */
+    if (!TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, pcb->rcv_nxt + pcb->rcv_wnd - 1)) {
+      tcp_ack_now(pcb);
+    }
+  }
+}
+
+static u8_t
+tcp_get_next_optbyte(void)
+{
+  u16_t optidx = tcp_optidx++;
+  if ((tcphdr_opt2 == NULL) || (optidx < tcphdr_opt1len)) {
+    u8_t *opts = (u8_t *)tcphdr + TCP_HLEN;
+    return opts[optidx];
+  } else {
+    u8_t idx = (u8_t)(optidx - tcphdr_opt1len);
+    return tcphdr_opt2[idx];
+  }
+}
+
+/**
+ * Parses the options contained in the incoming segment.
+ *
+ * Called from tcp_listen_input() and tcp_process().
+ * Currently, only the MSS option is supported!
+ *
+ * @param pcb the tcp_pcb for which a segment arrived
+ */
+static void
+tcp_parseopt(struct tcp_pcb *pcb)
+{
+  u8_t data;
+  u16_t mss;
+#if LWIP_TCP_TIMESTAMPS
+  u32_t tsval;
+#endif
+
+  LWIP_ASSERT("tcp_parseopt: invalid pcb", pcb != NULL);
+
+  /* Parse the TCP MSS option, if present. */
+  if (tcphdr_optlen != 0) {
+    for (tcp_optidx = 0; tcp_optidx < tcphdr_optlen; ) {
+      u8_t opt = tcp_get_next_optbyte();
+      switch (opt) {
+        case LWIP_TCP_OPT_EOL:
+          /* End of options. */
+          LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: EOL\n"));
+          return;
+        case LWIP_TCP_OPT_NOP:
+          /* NOP option. */
+          LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: NOP\n"));
+          break;
+        case LWIP_TCP_OPT_MSS:
+          LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: MSS\n"));
+          if (tcp_get_next_optbyte() != LWIP_TCP_OPT_LEN_MSS || (tcp_optidx - 2 + LWIP_TCP_OPT_LEN_MSS) > tcphdr_optlen) {
+            /* Bad length */
+            LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n"));
+            return;
+          }
+          /* An MSS option with the right option length. */
+          mss = (u16_t)(tcp_get_next_optbyte() << 8);
+          mss |= tcp_get_next_optbyte();
+          /* Limit the mss to the configured TCP_MSS and prevent division by zero */
+          pcb->mss = ((mss > TCP_MSS) || (mss == 0)) ? TCP_MSS : mss;
+          break;
+#if LWIP_WND_SCALE
+        case LWIP_TCP_OPT_WS:
+          LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: WND_SCALE\n"));
+          if (tcp_get_next_optbyte() != LWIP_TCP_OPT_LEN_WS || (tcp_optidx - 2 + LWIP_TCP_OPT_LEN_WS) > tcphdr_optlen) {
+            /* Bad length */
+            LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n"));
+            return;
+          }
+          /* An WND_SCALE option with the right option length. */
+          data = tcp_get_next_optbyte();
+          /* If syn was received with wnd scale option,
+             activate wnd scale opt, but only if this is not a retransmission */
+          if ((flags & TCP_SYN) && !(pcb->flags & TF_WND_SCALE)) {
+            pcb->snd_scale = data;
+            if (pcb->snd_scale > 14U) {
+              pcb->snd_scale = 14U;
+            }
+            pcb->rcv_scale = TCP_RCV_SCALE;
+            tcp_set_flags(pcb, TF_WND_SCALE);
+            /* window scaling is enabled, we can use the full receive window */
+            LWIP_ASSERT("window not at default value", pcb->rcv_wnd == TCPWND_MIN16(TCP_WND));
+            LWIP_ASSERT("window not at default value", pcb->rcv_ann_wnd == TCPWND_MIN16(TCP_WND));
+            pcb->rcv_wnd = pcb->rcv_ann_wnd = TCP_WND;
+          }
+          break;
+#endif /* LWIP_WND_SCALE */
+#if LWIP_TCP_TIMESTAMPS
+        case LWIP_TCP_OPT_TS:
+          LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: TS\n"));
+          if (tcp_get_next_optbyte() != LWIP_TCP_OPT_LEN_TS || (tcp_optidx - 2 + LWIP_TCP_OPT_LEN_TS) > tcphdr_optlen) {
+            /* Bad length */
+            LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n"));
+            return;
+          }
+          /* TCP timestamp option with valid length */
+          tsval = tcp_get_next_optbyte();
+          tsval |= (tcp_get_next_optbyte() << 8);
+          tsval |= (tcp_get_next_optbyte() << 16);
+          tsval |= (tcp_get_next_optbyte() << 24);
+          if (flags & TCP_SYN) {
+            pcb->ts_recent = lwip_ntohl(tsval);
+            /* Enable sending timestamps in every segment now that we know
+               the remote host supports it. */
+            tcp_set_flags(pcb, TF_TIMESTAMP);
+          } else if (TCP_SEQ_BETWEEN(pcb->ts_lastacksent, seqno, seqno + tcplen)) {
+            pcb->ts_recent = lwip_ntohl(tsval);
+          }
+          /* Advance to next option (6 bytes already read) */
+          tcp_optidx += LWIP_TCP_OPT_LEN_TS - 6;
+          break;
+#endif /* LWIP_TCP_TIMESTAMPS */
+#if LWIP_TCP_SACK_OUT
+        case LWIP_TCP_OPT_SACK_PERM:
+          LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: SACK_PERM\n"));
+          if (tcp_get_next_optbyte() != LWIP_TCP_OPT_LEN_SACK_PERM || (tcp_optidx - 2 + LWIP_TCP_OPT_LEN_SACK_PERM) > tcphdr_optlen) {
+            /* Bad length */
+            LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n"));
+            return;
+          }
+          /* TCP SACK_PERM option with valid length */
+          if (flags & TCP_SYN) {
+            /* We only set it if we receive it in a SYN (or SYN+ACK) packet */
+            tcp_set_flags(pcb, TF_SACK);
+          }
+          break;
+#endif /* LWIP_TCP_SACK_OUT */
+        default:
+          LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: other\n"));
+          data = tcp_get_next_optbyte();
+          if (data < 2) {
+            LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n"));
+            /* If the length field is zero, the options are malformed
+               and we don't process them further. */
+            return;
+          }
+          /* All other options have a length field, so that we easily
+             can skip past them. */
+          tcp_optidx += data - 2;
+      }
+    }
+  }
+}
+
+void
+tcp_trigger_input_pcb_close(void)
+{
+  recv_flags |= TF_CLOSED;
+}
+
+#if LWIP_TCP_SACK_OUT
+/**
+ * Called by tcp_receive() to add new SACK entry.
+ *
+ * The new SACK entry will be placed at the beginning of rcv_sacks[], as the newest one.
+ * Existing SACK entries will be "pushed back", to preserve their order.
+ * This is the behavior described in RFC 2018, section 4.
+ *
+ * @param pcb the tcp_pcb for which a segment arrived
+ * @param left the left side of the SACK (the first sequence number)
+ * @param right the right side of the SACK (the first sequence number past this SACK)
+ */
+static void
+tcp_add_sack(struct tcp_pcb *pcb, u32_t left, u32_t right)
+{
+  u8_t i;
+  u8_t unused_idx;
+
+  if ((pcb->flags & TF_SACK) == 0 || !TCP_SEQ_LT(left, right)) {
+    return;
+  }
+
+  /* First, let's remove all SACKs that are no longer needed (because they overlap with the newest one),
+     while moving all other SACKs forward.
+     We run this loop for all entries, until we find the first invalid one.
+     There is no point checking after that. */
+  for (i = unused_idx = 0; (i < LWIP_TCP_MAX_SACK_NUM) && LWIP_TCP_SACK_VALID(pcb, i); ++i) {
+    /* We only want to use SACK at [i] if it doesn't overlap with left:right range.
+       It does not overlap if its right side is before the newly added SACK,
+       or if its left side is after the newly added SACK.
+       NOTE: The equality should not really happen, but it doesn't hurt. */
+    if (TCP_SEQ_LEQ(pcb->rcv_sacks[i].right, left) || TCP_SEQ_LEQ(right, pcb->rcv_sacks[i].left)) {
+      if (unused_idx != i) {
+        /* We don't need to copy if it's already in the right spot */
+        pcb->rcv_sacks[unused_idx] = pcb->rcv_sacks[i];
+      }
+      ++unused_idx;
+    }
+  }
+
+  /* Now 'unused_idx' is the index of the first invalid SACK entry,
+     anywhere between 0 (no valid entries) and LWIP_TCP_MAX_SACK_NUM (all entries are valid).
+     We want to clear this and all following SACKs.
+     However, we will be adding another one in the front (and shifting everything else back).
+     So let's just iterate from the back, and set each entry to the one to the left if it's valid,
+     or to 0 if it is not. */
+  for (i = LWIP_TCP_MAX_SACK_NUM - 1; i > 0; --i) {
+    /* [i] is the index we are setting, and the value should be at index [i-1],
+       or 0 if that index is unused (>= unused_idx). */
+    if (i - 1 >= unused_idx) {
+      /* [i-1] is unused. Let's clear [i]. */
+      pcb->rcv_sacks[i].left = pcb->rcv_sacks[i].right = 0;
+    } else {
+      pcb->rcv_sacks[i] = pcb->rcv_sacks[i - 1];
+    }
+  }
+
+  /* And now we can store the newest SACK */
+  pcb->rcv_sacks[0].left = left;
+  pcb->rcv_sacks[0].right = right;
+}
+
+/**
+ * Called to remove a range of SACKs.
+ *
+ * SACK entries will be removed or adjusted to not acknowledge any sequence
+ * numbers that are less than 'seq' passed. It not only invalidates entries,
+ * but also moves all entries that are still valid to the beginning.
+ *
+ * @param pcb the tcp_pcb to modify
+ * @param seq the lowest sequence number to keep in SACK entries
+ */
+static void
+tcp_remove_sacks_lt(struct tcp_pcb *pcb, u32_t seq)
+{
+  u8_t i;
+  u8_t unused_idx;
+
+  /* We run this loop for all entries, until we find the first invalid one.
+     There is no point checking after that. */
+  for (i = unused_idx = 0; (i < LWIP_TCP_MAX_SACK_NUM) && LWIP_TCP_SACK_VALID(pcb, i); ++i) {
+    /* We only want to use SACK at index [i] if its right side is > 'seq'. */
+    if (TCP_SEQ_GT(pcb->rcv_sacks[i].right, seq)) {
+      if (unused_idx != i) {
+        /* We only copy it if it's not in the right spot already. */
+        pcb->rcv_sacks[unused_idx] = pcb->rcv_sacks[i];
+      }
+      /* NOTE: It is possible that its left side is < 'seq', in which case we should adjust it. */
+      if (TCP_SEQ_LT(pcb->rcv_sacks[unused_idx].left, seq)) {
+        pcb->rcv_sacks[unused_idx].left = seq;
+      }
+      ++unused_idx;
+    }
+  }
+
+  /* We also need to invalidate everything from 'unused_idx' till the end */
+  for (i = unused_idx; i < LWIP_TCP_MAX_SACK_NUM; ++i) {
+    pcb->rcv_sacks[i].left = pcb->rcv_sacks[i].right = 0;
+  }
+}
+
+#if defined(TCP_OOSEQ_BYTES_LIMIT) || defined(TCP_OOSEQ_PBUFS_LIMIT)
+/**
+ * Called to remove a range of SACKs.
+ *
+ * SACK entries will be removed or adjusted to not acknowledge any sequence
+ * numbers that are greater than (or equal to) 'seq' passed. It not only invalidates entries,
+ * but also moves all entries that are still valid to the beginning.
+ *
+ * @param pcb the tcp_pcb to modify
+ * @param seq the highest sequence number to keep in SACK entries
+ */
+static void
+tcp_remove_sacks_gt(struct tcp_pcb *pcb, u32_t seq)
+{
+  u8_t i;
+  u8_t unused_idx;
+
+  /* We run this loop for all entries, until we find the first invalid one.
+     There is no point checking after that. */
+  for (i = unused_idx = 0; (i < LWIP_TCP_MAX_SACK_NUM) && LWIP_TCP_SACK_VALID(pcb, i); ++i) {
+    /* We only want to use SACK at index [i] if its left side is < 'seq'. */
+    if (TCP_SEQ_LT(pcb->rcv_sacks[i].left, seq)) {
+      if (unused_idx != i) {
+        /* We only copy it if it's not in the right spot already. */
+        pcb->rcv_sacks[unused_idx] = pcb->rcv_sacks[i];
+      }
+      /* NOTE: It is possible that its right side is > 'seq', in which case we should adjust it. */
+      if (TCP_SEQ_GT(pcb->rcv_sacks[unused_idx].right, seq)) {
+        pcb->rcv_sacks[unused_idx].right = seq;
+      }
+      ++unused_idx;
+    }
+  }
+
+  /* We also need to invalidate everything from 'unused_idx' till the end */
+  for (i = unused_idx; i < LWIP_TCP_MAX_SACK_NUM; ++i) {
+    pcb->rcv_sacks[i].left = pcb->rcv_sacks[i].right = 0;
+  }
+}
+#endif /* TCP_OOSEQ_BYTES_LIMIT || TCP_OOSEQ_PBUFS_LIMIT */
+
+#endif /* LWIP_TCP_SACK_OUT */
+
+#endif /* LWIP_TCP */
diff --git a/lib/lwip/core/tcp_out.c b/lib/lwip/core/tcp_out.c
new file mode 100644
index 0000000000..64579ee5cb
--- /dev/null
+++ b/lib/lwip/core/tcp_out.c
@@ -0,0 +1,2257 @@
+/**
+ * @file
+ * Transmission Control Protocol, outgoing traffic
+ *
+ * The output functions of TCP.
+ *
+ * There are two distinct ways for TCP segments to get sent:
+ * - queued data: these are segments transferring data or segments containing
+ *   SYN or FIN (which both count as one sequence number). They are created as
+ *   struct @ref pbuf together with a struct tcp_seg and enqueue to the
+ *   unsent list of the pcb. They are sent by tcp_output:
+ *   - @ref tcp_write : creates data segments
+ *   - @ref tcp_split_unsent_seg : splits a data segment
+ *   - @ref tcp_enqueue_flags : creates SYN-only or FIN-only segments
+ *   - @ref tcp_output / tcp_output_segment : finalize the tcp header
+ *      (e.g. sequence numbers, options, checksum) and output to IP
+ *   - the various tcp_rexmit functions shuffle around segments between the
+ *     unsent an unacked lists to retransmit them
+ *   - tcp_create_segment and tcp_pbuf_prealloc allocate pbuf and
+ *     segment for these functions
+ * - direct send: these segments don't contain data but control the connection
+ *   behaviour. They are created as pbuf only and sent directly without
+ *   enqueueing them:
+ *   - @ref tcp_send_empty_ack sends an ACK-only segment
+ *   - @ref tcp_rst sends a RST segment
+ *   - @ref tcp_keepalive sends a keepalive segment
+ *   - @ref tcp_zero_window_probe sends a window probe segment
+ *   - tcp_output_alloc_header allocates a header-only pbuf for these functions
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/priv/tcp_priv.h"
+#include "lwip/def.h"
+#include "lwip/mem.h"
+#include "lwip/memp.h"
+#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/stats.h"
+#include "lwip/ip6.h"
+#include "lwip/ip6_addr.h"
+#if LWIP_TCP_TIMESTAMPS
+#include "lwip/sys.h"
+#endif
+
+#include <string.h>
+
+#ifdef LWIP_HOOK_FILENAME
+#include LWIP_HOOK_FILENAME
+#endif
+
+/* Allow to add custom TCP header options by defining this hook */
+#ifdef LWIP_HOOK_TCP_OUT_TCPOPT_LENGTH
+#define LWIP_TCP_OPT_LENGTH_SEGMENT(flags, pcb) LWIP_HOOK_TCP_OUT_TCPOPT_LENGTH(pcb, LWIP_TCP_OPT_LENGTH(flags))
+#else
+#define LWIP_TCP_OPT_LENGTH_SEGMENT(flags, pcb) LWIP_TCP_OPT_LENGTH(flags)
+#endif
+
+/* Define some copy-macros for checksum-on-copy so that the code looks
+   nicer by preventing too many ifdef's. */
+#if TCP_CHECKSUM_ON_COPY
+#define TCP_DATA_COPY(dst, src, len, seg) do { \
+  tcp_seg_add_chksum(LWIP_CHKSUM_COPY(dst, src, len), \
+                     len, &seg->chksum, &seg->chksum_swapped); \
+  seg->flags |= TF_SEG_DATA_CHECKSUMMED; } while(0)
+#define TCP_DATA_COPY2(dst, src, len, chksum, chksum_swapped)  \
+  tcp_seg_add_chksum(LWIP_CHKSUM_COPY(dst, src, len), len, chksum, chksum_swapped);
+#else /* TCP_CHECKSUM_ON_COPY*/
+#define TCP_DATA_COPY(dst, src, len, seg)                     MEMCPY(dst, src, len)
+#define TCP_DATA_COPY2(dst, src, len, chksum, chksum_swapped) MEMCPY(dst, src, len)
+#endif /* TCP_CHECKSUM_ON_COPY*/
+
+/** Define this to 1 for an extra check that the output checksum is valid
+ * (useful when the checksum is generated by the application, not the stack) */
+#ifndef TCP_CHECKSUM_ON_COPY_SANITY_CHECK
+#define TCP_CHECKSUM_ON_COPY_SANITY_CHECK   0
+#endif
+/* Allow to override the failure of sanity check from warning to e.g. hard failure */
+#if TCP_CHECKSUM_ON_COPY_SANITY_CHECK
+#ifndef TCP_CHECKSUM_ON_COPY_SANITY_CHECK_FAIL
+#define TCP_CHECKSUM_ON_COPY_SANITY_CHECK_FAIL(msg) LWIP_DEBUGF(TCP_DEBUG | LWIP_DBG_LEVEL_WARNING, msg)
+#endif
+#endif
+
+#if TCP_OVERSIZE
+/** The size of segment pbufs created when TCP_OVERSIZE is enabled */
+#ifndef TCP_OVERSIZE_CALC_LENGTH
+#define TCP_OVERSIZE_CALC_LENGTH(length) ((length) + TCP_OVERSIZE)
+#endif
+#endif
+
+/* Forward declarations.*/
+static err_t tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb, struct netif *netif);
+static err_t tcp_output_control_segment_netif(const struct tcp_pcb *pcb, struct pbuf *p,
+                                              const ip_addr_t *src, const ip_addr_t *dst,
+                                              struct netif *netif);
+
+/* tcp_route: common code that returns a fixed bound netif or calls ip_route */
+static struct netif *
+tcp_route(const struct tcp_pcb *pcb, const ip_addr_t *src, const ip_addr_t *dst)
+{
+  LWIP_UNUSED_ARG(src); /* in case IPv4-only and source-based routing is disabled */
+
+  if ((pcb != NULL) && (pcb->netif_idx != NETIF_NO_INDEX)) {
+    return netif_get_by_index(pcb->netif_idx);
+  } else {
+    return ip_route(src, dst);
+  }
+}
+
+/**
+ * Create a TCP segment with prefilled header.
+ *
+ * Called by @ref tcp_write, @ref tcp_enqueue_flags and @ref tcp_split_unsent_seg
+ *
+ * @param pcb Protocol control block for the TCP connection.
+ * @param p pbuf that is used to hold the TCP header.
+ * @param hdrflags TCP flags for header.
+ * @param seqno TCP sequence number of this packet
+ * @param optflags options to include in TCP header
+ * @return a new tcp_seg pointing to p, or NULL.
+ * The TCP header is filled in except ackno and wnd.
+ * p is freed on failure.
+ */
+static struct tcp_seg *
+tcp_create_segment(const struct tcp_pcb *pcb, struct pbuf *p, u8_t hdrflags, u32_t seqno, u8_t optflags)
+{
+  struct tcp_seg *seg;
+  u8_t optlen;
+
+  LWIP_ASSERT("tcp_create_segment: invalid pcb", pcb != NULL);
+  LWIP_ASSERT("tcp_create_segment: invalid pbuf", p != NULL);
+
+  optlen = LWIP_TCP_OPT_LENGTH_SEGMENT(optflags, pcb);
+
+  if ((seg = (struct tcp_seg *)memp_malloc(MEMP_TCP_SEG)) == NULL) {
+    LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("tcp_create_segment: no memory.\n"));
+    pbuf_free(p);
+    return NULL;
+  }
+  seg->flags = optflags;
+  seg->next = NULL;
+  seg->p = p;
+  LWIP_ASSERT("p->tot_len >= optlen", p->tot_len >= optlen);
+  seg->len = p->tot_len - optlen;
+#if TCP_OVERSIZE_DBGCHECK
+  seg->oversize_left = 0;
+#endif /* TCP_OVERSIZE_DBGCHECK */
+#if TCP_CHECKSUM_ON_COPY
+  seg->chksum = 0;
+  seg->chksum_swapped = 0;
+  /* check optflags */
+  LWIP_ASSERT("invalid optflags passed: TF_SEG_DATA_CHECKSUMMED",
+              (optflags & TF_SEG_DATA_CHECKSUMMED) == 0);
+#endif /* TCP_CHECKSUM_ON_COPY */
+
+  /* build TCP header */
+  if (pbuf_add_header(p, TCP_HLEN)) {
+    LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("tcp_create_segment: no room for TCP header in pbuf.\n"));
+    TCP_STATS_INC(tcp.err);
+    tcp_seg_free(seg);
+    return NULL;
+  }
+  seg->tcphdr = (struct tcp_hdr *)seg->p->payload;
+  seg->tcphdr->src = lwip_htons(pcb->local_port);
+  seg->tcphdr->dest = lwip_htons(pcb->remote_port);
+  seg->tcphdr->seqno = lwip_htonl(seqno);
+  /* ackno is set in tcp_output */
+  TCPH_HDRLEN_FLAGS_SET(seg->tcphdr, (5 + optlen / 4), hdrflags);
+  /* wnd and chksum are set in tcp_output */
+  seg->tcphdr->urgp = 0;
+  return seg;
+}
+
+/**
+ * Allocate a PBUF_RAM pbuf, perhaps with extra space at the end.
+ *
+ * This function is like pbuf_alloc(layer, length, PBUF_RAM) except
+ * there may be extra bytes available at the end.
+ *
+ * Called by @ref tcp_write
+ *
+ * @param layer flag to define header size.
+ * @param length size of the pbuf's payload.
+ * @param max_length maximum usable size of payload+oversize.
+ * @param oversize pointer to a u16_t that will receive the number of usable tail bytes.
+ * @param pcb The TCP connection that will enqueue the pbuf.
+ * @param apiflags API flags given to tcp_write.
+ * @param first_seg true when this pbuf will be used in the first enqueued segment.
+ */
+#if TCP_OVERSIZE
+static struct pbuf *
+tcp_pbuf_prealloc(pbuf_layer layer, u16_t length, u16_t max_length,
+                  u16_t *oversize, const struct tcp_pcb *pcb, u8_t apiflags,
+                  u8_t first_seg)
+{
+  struct pbuf *p;
+  u16_t alloc = length;
+
+  LWIP_ASSERT("tcp_pbuf_prealloc: invalid oversize", oversize != NULL);
+  LWIP_ASSERT("tcp_pbuf_prealloc: invalid pcb", pcb != NULL);
+
+#if LWIP_NETIF_TX_SINGLE_PBUF
+  LWIP_UNUSED_ARG(max_length);
+  LWIP_UNUSED_ARG(pcb);
+  LWIP_UNUSED_ARG(apiflags);
+  LWIP_UNUSED_ARG(first_seg);
+  alloc = max_length;
+#else /* LWIP_NETIF_TX_SINGLE_PBUF */
+  if (length < max_length) {
+    /* Should we allocate an oversized pbuf, or just the minimum
+     * length required? If tcp_write is going to be called again
+     * before this segment is transmitted, we want the oversized
+     * buffer. If the segment will be transmitted immediately, we can
+     * save memory by allocating only length. We use a simple
+     * heuristic based on the following information:
+     *
+     * Did the user set TCP_WRITE_FLAG_MORE?
+     *
+     * Will the Nagle algorithm defer transmission of this segment?
+     */
+    if ((apiflags & TCP_WRITE_FLAG_MORE) ||
+        (!(pcb->flags & TF_NODELAY) &&
+         (!first_seg ||
+          pcb->unsent != NULL ||
+          pcb->unacked != NULL))) {
+      alloc = LWIP_MIN(max_length, LWIP_MEM_ALIGN_SIZE(TCP_OVERSIZE_CALC_LENGTH(length)));
+    }
+  }
+#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
+  p = pbuf_alloc(layer, alloc, PBUF_RAM);
+  if (p == NULL) {
+    return NULL;
+  }
+  LWIP_ASSERT("need unchained pbuf", p->next == NULL);
+  *oversize = p->len - length;
+  /* trim p->len to the currently used size */
+  p->len = p->tot_len = length;
+  return p;
+}
+#else /* TCP_OVERSIZE */
+#define tcp_pbuf_prealloc(layer, length, mx, os, pcb, api, fst) pbuf_alloc((layer), (length), PBUF_RAM)
+#endif /* TCP_OVERSIZE */
+
+#if TCP_CHECKSUM_ON_COPY
+/** Add a checksum of newly added data to the segment.
+ *
+ * Called by tcp_write and tcp_split_unsent_seg.
+ */
+static void
+tcp_seg_add_chksum(u16_t chksum, u16_t len, u16_t *seg_chksum,
+                   u8_t *seg_chksum_swapped)
+{
+  u32_t helper;
+  /* add chksum to old chksum and fold to u16_t */
+  helper = chksum + *seg_chksum;
+  chksum = FOLD_U32T(helper);
+  if ((len & 1) != 0) {
+    *seg_chksum_swapped = 1 - *seg_chksum_swapped;
+    chksum = SWAP_BYTES_IN_WORD(chksum);
+  }
+  *seg_chksum = chksum;
+}
+#endif /* TCP_CHECKSUM_ON_COPY */
+
+/** Checks if tcp_write is allowed or not (checks state, snd_buf and snd_queuelen).
+ *
+ * @param pcb the tcp pcb to check for
+ * @param len length of data to send (checked agains snd_buf)
+ * @return ERR_OK if tcp_write is allowed to proceed, another err_t otherwise
+ */
+static err_t
+tcp_write_checks(struct tcp_pcb *pcb, u16_t len)
+{
+  LWIP_ASSERT("tcp_write_checks: invalid pcb", pcb != NULL);
+
+  /* connection is in invalid state for data transmission? */
+  if ((pcb->state != ESTABLISHED) &&
+      (pcb->state != CLOSE_WAIT) &&
+      (pcb->state != SYN_SENT) &&
+      (pcb->state != SYN_RCVD)) {
+    LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_STATE | LWIP_DBG_LEVEL_SEVERE, ("tcp_write() called in invalid state\n"));
+    return ERR_CONN;
+  } else if (len == 0) {
+    return ERR_OK;
+  }
+
+  /* fail on too much data */
+  if (len > pcb->snd_buf) {
+    LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SEVERE, ("tcp_write: too much data (len=%"U16_F" > snd_buf=%"TCPWNDSIZE_F")\n",
+                len, pcb->snd_buf));
+    tcp_set_flags(pcb, TF_NAGLEMEMERR);
+    return ERR_MEM;
+  }
+
+  LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_write: queuelen: %"TCPWNDSIZE_F"\n", (tcpwnd_size_t)pcb->snd_queuelen));
+
+  /* If total number of pbufs on the unsent/unacked queues exceeds the
+   * configured maximum, return an error */
+  /* check for configured max queuelen and possible overflow */
+  if (pcb->snd_queuelen >= LWIP_MIN(TCP_SND_QUEUELEN, (TCP_SNDQUEUELEN_OVERFLOW + 1))) {
+    LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SEVERE, ("tcp_write: too long queue %"U16_F" (max %"U16_F")\n",
+                pcb->snd_queuelen, (u16_t)TCP_SND_QUEUELEN));
+    TCP_STATS_INC(tcp.memerr);
+    tcp_set_flags(pcb, TF_NAGLEMEMERR);
+    return ERR_MEM;
+  }
+  if (pcb->snd_queuelen != 0) {
+    LWIP_ASSERT("tcp_write: pbufs on queue => at least one queue non-empty",
+                pcb->unacked != NULL || pcb->unsent != NULL);
+  } else {
+    LWIP_ASSERT("tcp_write: no pbufs on queue => both queues empty",
+                pcb->unacked == NULL && pcb->unsent == NULL);
+  }
+  return ERR_OK;
+}
+
+/**
+ * @ingroup tcp_raw
+ * Write data for sending (but does not send it immediately).
+ *
+ * It waits in the expectation of more data being sent soon (as
+ * it can send them more efficiently by combining them together).
+ * To prompt the system to send data now, call tcp_output() after
+ * calling tcp_write().
+ *
+ * This function enqueues the data pointed to by the argument dataptr. The length of
+ * the data is passed as the len parameter. The apiflags can be one or more of:
+ * - TCP_WRITE_FLAG_COPY: indicates whether the new memory should be allocated
+ *   for the data to be copied into. If this flag is not given, no new memory
+ *   should be allocated and the data should only be referenced by pointer. This
+ *   also means that the memory behind dataptr must not change until the data is
+ *   ACKed by the remote host
+ * - TCP_WRITE_FLAG_MORE: indicates that more data follows. If this is omitted,
+ *   the PSH flag is set in the last segment created by this call to tcp_write.
+ *   If this flag is given, the PSH flag is not set.
+ *
+ * The tcp_write() function will fail and return ERR_MEM if the length
+ * of the data exceeds the current send buffer size or if the length of
+ * the queue of outgoing segment is larger than the upper limit defined
+ * in lwipopts.h. The number of bytes available in the output queue can
+ * be retrieved with the tcp_sndbuf() function.
+ *
+ * The proper way to use this function is to call the function with at
+ * most tcp_sndbuf() bytes of data. If the function returns ERR_MEM,
+ * the application should wait until some of the currently enqueued
+ * data has been successfully received by the other host and try again.
+ *
+ * @param pcb Protocol control block for the TCP connection to enqueue data for.
+ * @param arg Pointer to the data to be enqueued for sending.
+ * @param len Data length in bytes
+ * @param apiflags combination of following flags :
+ * - TCP_WRITE_FLAG_COPY (0x01) data will be copied into memory belonging to the stack
+ * - TCP_WRITE_FLAG_MORE (0x02) for TCP connection, PSH flag will not be set on last segment sent,
+ * @return ERR_OK if enqueued, another err_t on error
+ */
+err_t
+tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags)
+{
+  struct pbuf *concat_p = NULL;
+  struct tcp_seg *last_unsent = NULL, *seg = NULL, *prev_seg = NULL, *queue = NULL;
+  u16_t pos = 0; /* position in 'arg' data */
+  u16_t queuelen;
+  u8_t optlen;
+  u8_t optflags = 0;
+#if TCP_OVERSIZE
+  u16_t oversize = 0;
+  u16_t oversize_used = 0;
+#if TCP_OVERSIZE_DBGCHECK
+  u16_t oversize_add = 0;
+#endif /* TCP_OVERSIZE_DBGCHECK*/
+#endif /* TCP_OVERSIZE */
+  u16_t extendlen = 0;
+#if TCP_CHECKSUM_ON_COPY
+  u16_t concat_chksum = 0;
+  u8_t concat_chksum_swapped = 0;
+  u16_t concat_chksummed = 0;
+#endif /* TCP_CHECKSUM_ON_COPY */
+  err_t err;
+  u16_t mss_local;
+
+  LWIP_ERROR("tcp_write: invalid pcb", pcb != NULL, return ERR_ARG);
+
+  /* don't allocate segments bigger than half the maximum window we ever received */
+  mss_local = LWIP_MIN(pcb->mss, TCPWND_MIN16(pcb->snd_wnd_max / 2));
+  mss_local = mss_local ? mss_local : pcb->mss;
+
+  LWIP_ASSERT_CORE_LOCKED();
+
+#if LWIP_NETIF_TX_SINGLE_PBUF
+  /* Always copy to try to create single pbufs for TX */
+  apiflags |= TCP_WRITE_FLAG_COPY;
+#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
+
+  LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_write(pcb=%p, data=%p, len=%"U16_F", apiflags=%"U16_F")\n",
+                                 (void *)pcb, arg, len, (u16_t)apiflags));
+  LWIP_ERROR("tcp_write: arg == NULL (programmer violates API)",
+             arg != NULL, return ERR_ARG;);
+
+  err = tcp_write_checks(pcb, len);
+  if (err != ERR_OK) {
+    return err;
+  }
+  queuelen = pcb->snd_queuelen;
+
+#if LWIP_TCP_TIMESTAMPS
+  if ((pcb->flags & TF_TIMESTAMP)) {
+    /* Make sure the timestamp option is only included in data segments if we
+       agreed about it with the remote host. */
+    optflags = TF_SEG_OPTS_TS;
+    optlen = LWIP_TCP_OPT_LENGTH_SEGMENT(TF_SEG_OPTS_TS, pcb);
+    /* ensure that segments can hold at least one data byte... */
+    mss_local = LWIP_MAX(mss_local, LWIP_TCP_OPT_LEN_TS + 1);
+  } else
+#endif /* LWIP_TCP_TIMESTAMPS */
+  {
+    optlen = LWIP_TCP_OPT_LENGTH_SEGMENT(0, pcb);
+  }
+
+
+  /*
+   * TCP segmentation is done in three phases with increasing complexity:
+   *
+   * 1. Copy data directly into an oversized pbuf.
+   * 2. Chain a new pbuf to the end of pcb->unsent.
+   * 3. Create new segments.
+   *
+   * We may run out of memory at any point. In that case we must
+   * return ERR_MEM and not change anything in pcb. Therefore, all
+   * changes are recorded in local variables and committed at the end
+   * of the function. Some pcb fields are maintained in local copies:
+   *
+   * queuelen = pcb->snd_queuelen
+   * oversize = pcb->unsent_oversize
+   *
+   * These variables are set consistently by the phases:
+   *
+   * seg points to the last segment tampered with.
+   *
+   * pos records progress as data is segmented.
+   */
+
+  /* Find the tail of the unsent queue. */
+  if (pcb->unsent != NULL) {
+    u16_t space;
+    u16_t unsent_optlen;
+
+    /* @todo: this could be sped up by keeping last_unsent in the pcb */
+    for (last_unsent = pcb->unsent; last_unsent->next != NULL;
+         last_unsent = last_unsent->next);
+
+    /* Usable space at the end of the last unsent segment */
+    unsent_optlen = LWIP_TCP_OPT_LENGTH_SEGMENT(last_unsent->flags, pcb);
+    LWIP_ASSERT("mss_local is too small", mss_local >= last_unsent->len + unsent_optlen);
+    space = mss_local - (last_unsent->len + unsent_optlen);
+
+    /*
+     * Phase 1: Copy data directly into an oversized pbuf.
+     *
+     * The number of bytes copied is recorded in the oversize_used
+     * variable. The actual copying is done at the bottom of the
+     * function.
+     */
+#if TCP_OVERSIZE
+#if TCP_OVERSIZE_DBGCHECK
+    /* check that pcb->unsent_oversize matches last_unsent->oversize_left */
+    LWIP_ASSERT("unsent_oversize mismatch (pcb vs. last_unsent)",
+                pcb->unsent_oversize == last_unsent->oversize_left);
+#endif /* TCP_OVERSIZE_DBGCHECK */
+    oversize = pcb->unsent_oversize;
+    if (oversize > 0) {
+      LWIP_ASSERT("inconsistent oversize vs. space", oversize <= space);
+      seg = last_unsent;
+      oversize_used = LWIP_MIN(space, LWIP_MIN(oversize, len));
+      pos += oversize_used;
+      oversize -= oversize_used;
+      space -= oversize_used;
+    }
+    /* now we are either finished or oversize is zero */
+    LWIP_ASSERT("inconsistent oversize vs. len", (oversize == 0) || (pos == len));
+#endif /* TCP_OVERSIZE */
+
+#if !LWIP_NETIF_TX_SINGLE_PBUF
+    /*
+     * Phase 2: Chain a new pbuf to the end of pcb->unsent.
+     *
+     * As an exception when NOT copying the data, if the given data buffer
+     * directly follows the last unsent data buffer in memory, extend the last
+     * ROM pbuf reference to the buffer, thus saving a ROM pbuf allocation.
+     *
+     * We don't extend segments containing SYN/FIN flags or options
+     * (len==0). The new pbuf is kept in concat_p and pbuf_cat'ed at
+     * the end.
+     *
+     * This phase is skipped for LWIP_NETIF_TX_SINGLE_PBUF as we could only execute
+     * it after rexmit puts a segment from unacked to unsent and at this point,
+     * oversize info is lost.
+     */
+    if ((pos < len) && (space > 0) && (last_unsent->len > 0)) {
+      u16_t seglen = LWIP_MIN(space, len - pos);
+      seg = last_unsent;
+
+      /* Create a pbuf with a copy or reference to seglen bytes. We
+       * can use PBUF_RAW here since the data appears in the middle of
+       * a segment. A header will never be prepended. */
+      if (apiflags & TCP_WRITE_FLAG_COPY) {
+        /* Data is copied */
+        if ((concat_p = tcp_pbuf_prealloc(PBUF_RAW, seglen, space, &oversize, pcb, apiflags, 1)) == NULL) {
+          LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+                      ("tcp_write : could not allocate memory for pbuf copy size %"U16_F"\n",
+                       seglen));
+          goto memerr;
+        }
+#if TCP_OVERSIZE_DBGCHECK
+        oversize_add = oversize;
+#endif /* TCP_OVERSIZE_DBGCHECK */
+        TCP_DATA_COPY2(concat_p->payload, (const u8_t *)arg + pos, seglen, &concat_chksum, &concat_chksum_swapped);
+#if TCP_CHECKSUM_ON_COPY
+        concat_chksummed += seglen;
+#endif /* TCP_CHECKSUM_ON_COPY */
+        queuelen += pbuf_clen(concat_p);
+      } else {
+        /* Data is not copied */
+        /* If the last unsent pbuf is of type PBUF_ROM, try to extend it. */
+        struct pbuf *p;
+        for (p = last_unsent->p; p->next != NULL; p = p->next);
+        if (((p->type_internal & (PBUF_TYPE_FLAG_STRUCT_DATA_CONTIGUOUS | PBUF_TYPE_FLAG_DATA_VOLATILE)) == 0) &&
+            (const u8_t *)p->payload + p->len == (const u8_t *)arg) {
+          LWIP_ASSERT("tcp_write: ROM pbufs cannot be oversized", pos == 0);
+          extendlen = seglen;
+        } else {
+          if ((concat_p = pbuf_alloc(PBUF_RAW, seglen, PBUF_ROM)) == NULL) {
+            LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+                        ("tcp_write: could not allocate memory for zero-copy pbuf\n"));
+            goto memerr;
+          }
+          /* reference the non-volatile payload data */
+          ((struct pbuf_rom *)concat_p)->payload = (const u8_t *)arg + pos;
+          queuelen += pbuf_clen(concat_p);
+        }
+#if TCP_CHECKSUM_ON_COPY
+        /* calculate the checksum of nocopy-data */
+        tcp_seg_add_chksum(~inet_chksum((const u8_t *)arg + pos, seglen), seglen,
+                           &concat_chksum, &concat_chksum_swapped);
+        concat_chksummed += seglen;
+#endif /* TCP_CHECKSUM_ON_COPY */
+      }
+
+      pos += seglen;
+    }
+#endif /* !LWIP_NETIF_TX_SINGLE_PBUF */
+  } else {
+#if TCP_OVERSIZE
+    LWIP_ASSERT("unsent_oversize mismatch (pcb->unsent is NULL)",
+                pcb->unsent_oversize == 0);
+#endif /* TCP_OVERSIZE */
+  }
+
+  /*
+   * Phase 3: Create new segments.
+   *
+   * The new segments are chained together in the local 'queue'
+   * variable, ready to be appended to pcb->unsent.
+   */
+  while (pos < len) {
+    struct pbuf *p;
+    u16_t left = len - pos;
+    u16_t max_len = mss_local - optlen;
+    u16_t seglen = LWIP_MIN(left, max_len);
+#if TCP_CHECKSUM_ON_COPY
+    u16_t chksum = 0;
+    u8_t chksum_swapped = 0;
+#endif /* TCP_CHECKSUM_ON_COPY */
+
+    if (apiflags & TCP_WRITE_FLAG_COPY) {
+      /* If copy is set, memory should be allocated and data copied
+       * into pbuf */
+      if ((p = tcp_pbuf_prealloc(PBUF_TRANSPORT, seglen + optlen, mss_local, &oversize, pcb, apiflags, queue == NULL)) == NULL) {
+        LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("tcp_write : could not allocate memory for pbuf copy size %"U16_F"\n", seglen));
+        goto memerr;
+      }
+      LWIP_ASSERT("tcp_write: check that first pbuf can hold the complete seglen",
+                  (p->len >= seglen));
+      TCP_DATA_COPY2((char *)p->payload + optlen, (const u8_t *)arg + pos, seglen, &chksum, &chksum_swapped);
+    } else {
+      /* Copy is not set: First allocate a pbuf for holding the data.
+       * Since the referenced data is available at least until it is
+       * sent out on the link (as it has to be ACKed by the remote
+       * party) we can safely use PBUF_ROM instead of PBUF_REF here.
+       */
+      struct pbuf *p2;
+#if TCP_OVERSIZE
+      LWIP_ASSERT("oversize == 0", oversize == 0);
+#endif /* TCP_OVERSIZE */
+      if ((p2 = pbuf_alloc(PBUF_TRANSPORT, seglen, PBUF_ROM)) == NULL) {
+        LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("tcp_write: could not allocate memory for zero-copy pbuf\n"));
+        goto memerr;
+      }
+#if TCP_CHECKSUM_ON_COPY
+      /* calculate the checksum of nocopy-data */
+      chksum = ~inet_chksum((const u8_t *)arg + pos, seglen);
+      if (seglen & 1) {
+        chksum_swapped = 1;
+        chksum = SWAP_BYTES_IN_WORD(chksum);
+      }
+#endif /* TCP_CHECKSUM_ON_COPY */
+      /* reference the non-volatile payload data */
+      ((struct pbuf_rom *)p2)->payload = (const u8_t *)arg + pos;
+
+      /* Second, allocate a pbuf for the headers. */
+      if ((p = pbuf_alloc(PBUF_TRANSPORT, optlen, PBUF_RAM)) == NULL) {
+        /* If allocation fails, we have to deallocate the data pbuf as
+         * well. */
+        pbuf_free(p2);
+        LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("tcp_write: could not allocate memory for header pbuf\n"));
+        goto memerr;
+      }
+      /* Concatenate the headers and data pbufs together. */
+      pbuf_cat(p/*header*/, p2/*data*/);
+    }
+
+    queuelen += pbuf_clen(p);
+
+    /* Now that there are more segments queued, we check again if the
+     * length of the queue exceeds the configured maximum or
+     * overflows. */
+    if (queuelen > LWIP_MIN(TCP_SND_QUEUELEN, TCP_SNDQUEUELEN_OVERFLOW)) {
+      LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("tcp_write: queue too long %"U16_F" (%d)\n",
+                  queuelen, (int)TCP_SND_QUEUELEN));
+      pbuf_free(p);
+      goto memerr;
+    }
+
+    if ((seg = tcp_create_segment(pcb, p, 0, pcb->snd_lbb + pos, optflags)) == NULL) {
+      goto memerr;
+    }
+#if TCP_OVERSIZE_DBGCHECK
+    seg->oversize_left = oversize;
+#endif /* TCP_OVERSIZE_DBGCHECK */
+#if TCP_CHECKSUM_ON_COPY
+    seg->chksum = chksum;
+    seg->chksum_swapped = chksum_swapped;
+    seg->flags |= TF_SEG_DATA_CHECKSUMMED;
+#endif /* TCP_CHECKSUM_ON_COPY */
+
+    /* first segment of to-be-queued data? */
+    if (queue == NULL) {
+      queue = seg;
+    } else {
+      /* Attach the segment to the end of the queued segments */
+      LWIP_ASSERT("prev_seg != NULL", prev_seg != NULL);
+      prev_seg->next = seg;
+    }
+    /* remember last segment of to-be-queued data for next iteration */
+    prev_seg = seg;
+
+    LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_TRACE, ("tcp_write: queueing %"U32_F":%"U32_F"\n",
+                lwip_ntohl(seg->tcphdr->seqno),
+                lwip_ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg)));
+
+    pos += seglen;
+  }
+
+  /*
+   * All three segmentation phases were successful. We can commit the
+   * transaction.
+   */
+#if TCP_OVERSIZE_DBGCHECK
+  if ((last_unsent != NULL) && (oversize_add != 0)) {
+    last_unsent->oversize_left += oversize_add;
+  }
+#endif /* TCP_OVERSIZE_DBGCHECK */
+
+  /*
+   * Phase 1: If data has been added to the preallocated tail of
+   * last_unsent, we update the length fields of the pbuf chain.
+   */
+#if TCP_OVERSIZE
+  if (oversize_used > 0) {
+    struct pbuf *p;
+    /* Bump tot_len of whole chain, len of tail */
+    for (p = last_unsent->p; p; p = p->next) {
+      p->tot_len += oversize_used;
+      if (p->next == NULL) {
+        TCP_DATA_COPY((char *)p->payload + p->len, arg, oversize_used, last_unsent);
+        p->len += oversize_used;
+      }
+    }
+    last_unsent->len += oversize_used;
+#if TCP_OVERSIZE_DBGCHECK
+    LWIP_ASSERT("last_unsent->oversize_left >= oversize_used",
+                last_unsent->oversize_left >= oversize_used);
+    last_unsent->oversize_left -= oversize_used;
+#endif /* TCP_OVERSIZE_DBGCHECK */
+  }
+  pcb->unsent_oversize = oversize;
+#endif /* TCP_OVERSIZE */
+
+  /*
+   * Phase 2: concat_p can be concatenated onto last_unsent->p, unless we
+   * determined that the last ROM pbuf can be extended to include the new data.
+   */
+  if (concat_p != NULL) {
+    LWIP_ASSERT("tcp_write: cannot concatenate when pcb->unsent is empty",
+                (last_unsent != NULL));
+    pbuf_cat(last_unsent->p, concat_p);
+    last_unsent->len += concat_p->tot_len;
+  } else if (extendlen > 0) {
+    struct pbuf *p;
+    LWIP_ASSERT("tcp_write: extension of reference requires reference",
+                last_unsent != NULL && last_unsent->p != NULL);
+    for (p = last_unsent->p; p->next != NULL; p = p->next) {
+      p->tot_len += extendlen;
+    }
+    p->tot_len += extendlen;
+    p->len += extendlen;
+    last_unsent->len += extendlen;
+  }
+
+#if TCP_CHECKSUM_ON_COPY
+  if (concat_chksummed) {
+    LWIP_ASSERT("tcp_write: concat checksum needs concatenated data",
+                concat_p != NULL || extendlen > 0);
+    /*if concat checksumm swapped - swap it back */
+    if (concat_chksum_swapped) {
+      concat_chksum = SWAP_BYTES_IN_WORD(concat_chksum);
+    }
+    tcp_seg_add_chksum(concat_chksum, concat_chksummed, &last_unsent->chksum,
+                       &last_unsent->chksum_swapped);
+    last_unsent->flags |= TF_SEG_DATA_CHECKSUMMED;
+  }
+#endif /* TCP_CHECKSUM_ON_COPY */
+
+  /*
+   * Phase 3: Append queue to pcb->unsent. Queue may be NULL, but that
+   * is harmless
+   */
+  if (last_unsent == NULL) {
+    pcb->unsent = queue;
+  } else {
+    last_unsent->next = queue;
+  }
+
+  /*
+   * Finally update the pcb state.
+   */
+  pcb->snd_lbb += len;
+  pcb->snd_buf -= len;
+  pcb->snd_queuelen = queuelen;
+
+  LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_write: %"S16_F" (after enqueued)\n",
+                               pcb->snd_queuelen));
+  if (pcb->snd_queuelen != 0) {
+    LWIP_ASSERT("tcp_write: valid queue length",
+                pcb->unacked != NULL || pcb->unsent != NULL);
+  }
+
+  /* Set the PSH flag in the last segment that we enqueued. */
+  if (seg != NULL && seg->tcphdr != NULL && ((apiflags & TCP_WRITE_FLAG_MORE) == 0)) {
+    TCPH_SET_FLAG(seg->tcphdr, TCP_PSH);
+  }
+
+  return ERR_OK;
+memerr:
+  tcp_set_flags(pcb, TF_NAGLEMEMERR);
+  TCP_STATS_INC(tcp.memerr);
+
+  if (concat_p != NULL) {
+    pbuf_free(concat_p);
+  }
+  if (queue != NULL) {
+    tcp_segs_free(queue);
+  }
+  if (pcb->snd_queuelen != 0) {
+    LWIP_ASSERT("tcp_write: valid queue length", pcb->unacked != NULL ||
+                pcb->unsent != NULL);
+  }
+  LWIP_DEBUGF(TCP_QLEN_DEBUG | LWIP_DBG_STATE, ("tcp_write: %"S16_F" (with mem err)\n", pcb->snd_queuelen));
+  return ERR_MEM;
+}
+
+/**
+ * Split segment on the head of the unsent queue.  If return is not
+ * ERR_OK, existing head remains intact
+ *
+ * The split is accomplished by creating a new TCP segment and pbuf
+ * which holds the remainder payload after the split.  The original
+ * pbuf is trimmed to new length.  This allows splitting of read-only
+ * pbufs
+ *
+ * @param pcb the tcp_pcb for which to split the unsent head
+ * @param split the amount of payload to remain in the head
+ */
+err_t
+tcp_split_unsent_seg(struct tcp_pcb *pcb, u16_t split)
+{
+  struct tcp_seg *seg = NULL, *useg = NULL;
+  struct pbuf *p = NULL;
+  u8_t optlen;
+  u8_t optflags;
+  u8_t split_flags;
+  u8_t remainder_flags;
+  u16_t remainder;
+  u16_t offset;
+#if TCP_CHECKSUM_ON_COPY
+  u16_t chksum = 0;
+  u8_t chksum_swapped = 0;
+  struct pbuf *q;
+#endif /* TCP_CHECKSUM_ON_COPY */
+
+  LWIP_ASSERT("tcp_split_unsent_seg: invalid pcb", pcb != NULL);
+
+  useg = pcb->unsent;
+  if (useg == NULL) {
+    return ERR_MEM;
+  }
+
+  if (split == 0) {
+    LWIP_ASSERT("Can't split segment into length 0", 0);
+    return ERR_VAL;
+  }
+
+  if (useg->len <= split) {
+    return ERR_OK;
+  }
+
+  LWIP_ASSERT("split <= mss", split <= pcb->mss);
+  LWIP_ASSERT("useg->len > 0", useg->len > 0);
+
+  /* We should check that we don't exceed TCP_SND_QUEUELEN but we need
+   * to split this packet so we may actually exceed the max value by
+   * one!
+   */
+  LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue: split_unsent_seg: %u\n", (unsigned int)pcb->snd_queuelen));
+
+  optflags = useg->flags;
+#if TCP_CHECKSUM_ON_COPY
+  /* Remove since checksum is not stored until after tcp_create_segment() */
+  optflags &= ~TF_SEG_DATA_CHECKSUMMED;
+#endif /* TCP_CHECKSUM_ON_COPY */
+  optlen = LWIP_TCP_OPT_LENGTH(optflags);
+  remainder = useg->len - split;
+
+  /* Create new pbuf for the remainder of the split */
+  p = pbuf_alloc(PBUF_TRANSPORT, remainder + optlen, PBUF_RAM);
+  if (p == NULL) {
+    LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+                ("tcp_split_unsent_seg: could not allocate memory for pbuf remainder %u\n", remainder));
+    goto memerr;
+  }
+
+  /* Offset into the original pbuf is past TCP/IP headers, options, and split amount */
+  offset = useg->p->tot_len - useg->len + split;
+  /* Copy remainder into new pbuf, headers and options will not be filled out */
+  if (pbuf_copy_partial(useg->p, (u8_t *)p->payload + optlen, remainder, offset ) != remainder) {
+    LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+                ("tcp_split_unsent_seg: could not copy pbuf remainder %u\n", remainder));
+    goto memerr;
+  }
+#if TCP_CHECKSUM_ON_COPY
+  /* calculate the checksum on remainder data */
+  tcp_seg_add_chksum(~inet_chksum((const u8_t *)p->payload + optlen, remainder), remainder,
+                     &chksum, &chksum_swapped);
+#endif /* TCP_CHECKSUM_ON_COPY */
+
+  /* Options are created when calling tcp_output() */
+
+  /* Migrate flags from original segment */
+  split_flags = TCPH_FLAGS(useg->tcphdr);
+  remainder_flags = 0; /* ACK added in tcp_output() */
+
+  if (split_flags & TCP_PSH) {
+    split_flags &= ~TCP_PSH;
+    remainder_flags |= TCP_PSH;
+  }
+  if (split_flags & TCP_FIN) {
+    split_flags &= ~TCP_FIN;
+    remainder_flags |= TCP_FIN;
+  }
+  /* SYN should be left on split, RST should not be present with data */
+
+  seg = tcp_create_segment(pcb, p, remainder_flags, lwip_ntohl(useg->tcphdr->seqno) + split, optflags);
+  if (seg == NULL) {
+    p = NULL; /* Freed by tcp_create_segment */
+    LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+                ("tcp_split_unsent_seg: could not create new TCP segment\n"));
+    goto memerr;
+  }
+
+#if TCP_CHECKSUM_ON_COPY
+  seg->chksum = chksum;
+  seg->chksum_swapped = chksum_swapped;
+  seg->flags |= TF_SEG_DATA_CHECKSUMMED;
+#endif /* TCP_CHECKSUM_ON_COPY */
+
+  /* Remove this segment from the queue since trimming it may free pbufs */
+  pcb->snd_queuelen -= pbuf_clen(useg->p);
+
+  /* Trim the original pbuf into our split size.  At this point our remainder segment must be setup
+  successfully because we are modifying the original segment */
+  pbuf_realloc(useg->p, useg->p->tot_len - remainder);
+  useg->len -= remainder;
+  TCPH_SET_FLAG(useg->tcphdr, split_flags);
+#if TCP_OVERSIZE_DBGCHECK
+  /* By trimming, realloc may have actually shrunk the pbuf, so clear oversize_left */
+  useg->oversize_left = 0;
+#endif /* TCP_OVERSIZE_DBGCHECK */
+
+  /* Add back to the queue with new trimmed pbuf */
+  pcb->snd_queuelen += pbuf_clen(useg->p);
+
+#if TCP_CHECKSUM_ON_COPY
+  /* The checksum on the split segment is now incorrect. We need to re-run it over the split */
+  useg->chksum = 0;
+  useg->chksum_swapped = 0;
+  q = useg->p;
+  offset = q->tot_len - useg->len; /* Offset due to exposed headers */
+
+  /* Advance to the pbuf where the offset ends */
+  while (q != NULL && offset > q->len) {
+    offset -= q->len;
+    q = q->next;
+  }
+  LWIP_ASSERT("Found start of payload pbuf", q != NULL);
+  /* Checksum the first payload pbuf accounting for offset, then other pbufs are all payload */
+  for (; q != NULL; offset = 0, q = q->next) {
+    tcp_seg_add_chksum(~inet_chksum((const u8_t *)q->payload + offset, q->len - offset), q->len - offset,
+                       &useg->chksum, &useg->chksum_swapped);
+  }
+#endif /* TCP_CHECKSUM_ON_COPY */
+
+  /* Update number of segments on the queues. Note that length now may
+   * exceed TCP_SND_QUEUELEN! We don't have to touch pcb->snd_buf
+   * because the total amount of data is constant when packet is split */
+  pcb->snd_queuelen += pbuf_clen(seg->p);
+
+  /* Finally insert remainder into queue after split (which stays head) */
+  seg->next = useg->next;
+  useg->next = seg;
+
+#if TCP_OVERSIZE
+  /* If remainder is last segment on the unsent, ensure we clear the oversize amount
+   * because the remainder is always sized to the exact remaining amount */
+  if (seg->next == NULL) {
+    pcb->unsent_oversize = 0;
+  }
+#endif /* TCP_OVERSIZE */
+
+  return ERR_OK;
+memerr:
+  TCP_STATS_INC(tcp.memerr);
+
+  LWIP_ASSERT("seg == NULL", seg == NULL);
+  if (p != NULL) {
+    pbuf_free(p);
+  }
+
+  return ERR_MEM;
+}
+
+/**
+ * Called by tcp_close() to send a segment including FIN flag but not data.
+ * This FIN may be added to an existing segment or a new, otherwise empty
+ * segment is enqueued.
+ *
+ * @param pcb the tcp_pcb over which to send a segment
+ * @return ERR_OK if sent, another err_t otherwise
+ */
+err_t
+tcp_send_fin(struct tcp_pcb *pcb)
+{
+  LWIP_ASSERT("tcp_send_fin: invalid pcb", pcb != NULL);
+
+  /* first, try to add the fin to the last unsent segment */
+  if (pcb->unsent != NULL) {
+    struct tcp_seg *last_unsent;
+    for (last_unsent = pcb->unsent; last_unsent->next != NULL;
+         last_unsent = last_unsent->next);
+
+    if ((TCPH_FLAGS(last_unsent->tcphdr) & (TCP_SYN | TCP_FIN | TCP_RST)) == 0) {
+      /* no SYN/FIN/RST flag in the header, we can add the FIN flag */
+      TCPH_SET_FLAG(last_unsent->tcphdr, TCP_FIN);
+      tcp_set_flags(pcb, TF_FIN);
+      return ERR_OK;
+    }
+  }
+  /* no data, no length, flags, copy=1, no optdata */
+  return tcp_enqueue_flags(pcb, TCP_FIN);
+}
+
+/**
+ * Enqueue SYN or FIN for transmission.
+ *
+ * Called by @ref tcp_connect, tcp_listen_input, and @ref tcp_close
+ * (via @ref tcp_send_fin)
+ *
+ * @param pcb Protocol control block for the TCP connection.
+ * @param flags TCP header flags to set in the outgoing segment.
+ */
+err_t
+tcp_enqueue_flags(struct tcp_pcb *pcb, u8_t flags)
+{
+  struct pbuf *p;
+  struct tcp_seg *seg;
+  u8_t optflags = 0;
+  u8_t optlen = 0;
+
+  LWIP_ASSERT("tcp_enqueue_flags: need either TCP_SYN or TCP_FIN in flags (programmer violates API)",
+              (flags & (TCP_SYN | TCP_FIN)) != 0);
+  LWIP_ASSERT("tcp_enqueue_flags: invalid pcb", pcb != NULL);
+
+  LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue_flags: queuelen: %"U16_F"\n", (u16_t)pcb->snd_queuelen));
+
+  /* No need to check pcb->snd_queuelen if only SYN or FIN are allowed! */
+
+  /* Get options for this segment. This is a special case since this is the
+     only place where a SYN can be sent. */
+  if (flags & TCP_SYN) {
+    optflags = TF_SEG_OPTS_MSS;
+#if LWIP_WND_SCALE
+    if ((pcb->state != SYN_RCVD) || (pcb->flags & TF_WND_SCALE)) {
+      /* In a <SYN,ACK> (sent in state SYN_RCVD), the window scale option may only
+         be sent if we received a window scale option from the remote host. */
+      optflags |= TF_SEG_OPTS_WND_SCALE;
+    }
+#endif /* LWIP_WND_SCALE */
+#if LWIP_TCP_SACK_OUT
+    if ((pcb->state != SYN_RCVD) || (pcb->flags & TF_SACK)) {
+      /* In a <SYN,ACK> (sent in state SYN_RCVD), the SACK_PERM option may only
+         be sent if we received a SACK_PERM option from the remote host. */
+      optflags |= TF_SEG_OPTS_SACK_PERM;
+    }
+#endif /* LWIP_TCP_SACK_OUT */
+  }
+#if LWIP_TCP_TIMESTAMPS
+  if ((pcb->flags & TF_TIMESTAMP) || ((flags & TCP_SYN) && (pcb->state != SYN_RCVD))) {
+    /* Make sure the timestamp option is only included in data segments if we
+       agreed about it with the remote host (and in active open SYN segments). */
+    optflags |= TF_SEG_OPTS_TS;
+  }
+#endif /* LWIP_TCP_TIMESTAMPS */
+  optlen = LWIP_TCP_OPT_LENGTH_SEGMENT(optflags, pcb);
+
+  /* Allocate pbuf with room for TCP header + options */
+  if ((p = pbuf_alloc(PBUF_TRANSPORT, optlen, PBUF_RAM)) == NULL) {
+    tcp_set_flags(pcb, TF_NAGLEMEMERR);
+    TCP_STATS_INC(tcp.memerr);
+    return ERR_MEM;
+  }
+  LWIP_ASSERT("tcp_enqueue_flags: check that first pbuf can hold optlen",
+              (p->len >= optlen));
+
+  /* Allocate memory for tcp_seg, and fill in fields. */
+  if ((seg = tcp_create_segment(pcb, p, flags, pcb->snd_lbb, optflags)) == NULL) {
+    tcp_set_flags(pcb, TF_NAGLEMEMERR);
+    TCP_STATS_INC(tcp.memerr);
+    return ERR_MEM;
+  }
+  LWIP_ASSERT("seg->tcphdr not aligned", ((mem_ptr_t)seg->tcphdr % LWIP_MIN(MEM_ALIGNMENT, 4)) == 0);
+  LWIP_ASSERT("tcp_enqueue_flags: invalid segment length", seg->len == 0);
+
+  LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_TRACE,
+              ("tcp_enqueue_flags: queueing %"U32_F":%"U32_F" (0x%"X16_F")\n",
+               lwip_ntohl(seg->tcphdr->seqno),
+               lwip_ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg),
+               (u16_t)flags));
+
+  /* Now append seg to pcb->unsent queue */
+  if (pcb->unsent == NULL) {
+    pcb->unsent = seg;
+  } else {
+    struct tcp_seg *useg;
+    for (useg = pcb->unsent; useg->next != NULL; useg = useg->next);
+    useg->next = seg;
+  }
+#if TCP_OVERSIZE
+  /* The new unsent tail has no space */
+  pcb->unsent_oversize = 0;
+#endif /* TCP_OVERSIZE */
+
+  /* SYN and FIN bump the sequence number */
+  if ((flags & TCP_SYN) || (flags & TCP_FIN)) {
+    pcb->snd_lbb++;
+    /* optlen does not influence snd_buf */
+  }
+  if (flags & TCP_FIN) {
+    tcp_set_flags(pcb, TF_FIN);
+  }
+
+  /* update number of segments on the queues */
+  pcb->snd_queuelen += pbuf_clen(seg->p);
+  LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue_flags: %"S16_F" (after enqueued)\n", pcb->snd_queuelen));
+  if (pcb->snd_queuelen != 0) {
+    LWIP_ASSERT("tcp_enqueue_flags: invalid queue length",
+                pcb->unacked != NULL || pcb->unsent != NULL);
+  }
+
+  return ERR_OK;
+}
+
+#if LWIP_TCP_TIMESTAMPS
+/* Build a timestamp option (12 bytes long) at the specified options pointer)
+ *
+ * @param pcb tcp_pcb
+ * @param opts option pointer where to store the timestamp option
+ */
+static void
+tcp_build_timestamp_option(const struct tcp_pcb *pcb, u32_t *opts)
+{
+  LWIP_ASSERT("tcp_build_timestamp_option: invalid pcb", pcb != NULL);
+
+  /* Pad with two NOP options to make everything nicely aligned */
+  opts[0] = PP_HTONL(0x0101080A);
+  opts[1] = lwip_htonl(sys_now());
+  opts[2] = lwip_htonl(pcb->ts_recent);
+}
+#endif
+
+#if LWIP_TCP_SACK_OUT
+/**
+ * Calculates the number of SACK entries that should be generated.
+ * It takes into account whether TF_SACK flag is set,
+ * the number of SACK entries in tcp_pcb that are valid,
+ * as well as the available options size.
+ *
+ * @param pcb tcp_pcb
+ * @param optlen the length of other TCP options (in bytes)
+ * @return the number of SACK ranges that can be used
+ */
+static u8_t
+tcp_get_num_sacks(const struct tcp_pcb *pcb, u8_t optlen)
+{
+  u8_t num_sacks = 0;
+
+  LWIP_ASSERT("tcp_get_num_sacks: invalid pcb", pcb != NULL);
+
+  if (pcb->flags & TF_SACK) {
+    u8_t i;
+
+    /* The first SACK takes up 12 bytes (it includes SACK header and two NOP options),
+       each additional one - 8 bytes. */
+    optlen += 12;
+
+    /* Max options size = 40, number of SACK array entries = LWIP_TCP_MAX_SACK_NUM */
+    for (i = 0; (i < LWIP_TCP_MAX_SACK_NUM) && (optlen <= TCP_MAX_OPTION_BYTES) &&
+         LWIP_TCP_SACK_VALID(pcb, i); ++i) {
+      ++num_sacks;
+      optlen += 8;
+    }
+  }
+
+  return num_sacks;
+}
+
+/** Build a SACK option (12 or more bytes long) at the specified options pointer)
+ *
+ * @param pcb tcp_pcb
+ * @param opts option pointer where to store the SACK option
+ * @param num_sacks the number of SACKs to store
+ */
+static void
+tcp_build_sack_option(const struct tcp_pcb *pcb, u32_t *opts, u8_t num_sacks)
+{
+  u8_t i;
+
+  LWIP_ASSERT("tcp_build_sack_option: invalid pcb", pcb != NULL);
+  LWIP_ASSERT("tcp_build_sack_option: invalid opts", opts != NULL);
+
+  /* Pad with two NOP options to make everything nicely aligned.
+     We add the length (of just the SACK option, not the NOPs in front of it),
+     which is 2B of header, plus 8B for each SACK. */
+  *(opts++) = PP_HTONL(0x01010500 + 2 + num_sacks * 8);
+
+  for (i = 0; i < num_sacks; ++i) {
+    *(opts++) = lwip_htonl(pcb->rcv_sacks[i].left);
+    *(opts++) = lwip_htonl(pcb->rcv_sacks[i].right);
+  }
+}
+
+#endif
+
+#if LWIP_WND_SCALE
+/** Build a window scale option (3 bytes long) at the specified options pointer)
+ *
+ * @param opts option pointer where to store the window scale option
+ */
+static void
+tcp_build_wnd_scale_option(u32_t *opts)
+{
+  LWIP_ASSERT("tcp_build_wnd_scale_option: invalid opts", opts != NULL);
+
+  /* Pad with one NOP option to make everything nicely aligned */
+  opts[0] = PP_HTONL(0x01030300 | TCP_RCV_SCALE);
+}
+#endif
+
+/**
+ * @ingroup tcp_raw
+ * Find out what we can send and send it
+ *
+ * @param pcb Protocol control block for the TCP connection to send data
+ * @return ERR_OK if data has been sent or nothing to send
+ *         another err_t on error
+ */
+err_t
+tcp_output(struct tcp_pcb *pcb)
+{
+  struct tcp_seg *seg, *useg;
+  u32_t wnd, snd_nxt;
+  err_t err;
+  struct netif *netif;
+#if TCP_CWND_DEBUG
+  s16_t i = 0;
+#endif /* TCP_CWND_DEBUG */
+
+  LWIP_ASSERT_CORE_LOCKED();
+
+  LWIP_ASSERT("tcp_output: invalid pcb", pcb != NULL);
+  /* pcb->state LISTEN not allowed here */
+  LWIP_ASSERT("don't call tcp_output for listen-pcbs",
+              pcb->state != LISTEN);
+
+  /* First, check if we are invoked by the TCP input processing
+     code. If so, we do not output anything. Instead, we rely on the
+     input processing code to call us when input processing is done
+     with. */
+  if (tcp_input_pcb == pcb) {
+    return ERR_OK;
+  }
+
+  wnd = LWIP_MIN(pcb->snd_wnd, pcb->cwnd);
+
+  seg = pcb->unsent;
+
+  if (seg == NULL) {
+    LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: nothing to send (%p)\n",
+                                   (void *)pcb->unsent));
+    LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_output: snd_wnd %"TCPWNDSIZE_F
+                                 ", cwnd %"TCPWNDSIZE_F", wnd %"U32_F
+                                 ", seg == NULL, ack %"U32_F"\n",
+                                 pcb->snd_wnd, pcb->cwnd, wnd, pcb->lastack));
+
+    /* If the TF_ACK_NOW flag is set and the ->unsent queue is empty, construct
+     * an empty ACK segment and send it. */
+    if (pcb->flags & TF_ACK_NOW) {
+      return tcp_send_empty_ack(pcb);
+    }
+    /* nothing to send: shortcut out of here */
+    goto output_done;
+  } else {
+    LWIP_DEBUGF(TCP_CWND_DEBUG,
+                ("tcp_output: snd_wnd %"TCPWNDSIZE_F", cwnd %"TCPWNDSIZE_F", wnd %"U32_F
+                 ", effwnd %"U32_F", seq %"U32_F", ack %"U32_F"\n",
+                 pcb->snd_wnd, pcb->cwnd, wnd,
+                 lwip_ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len,
+                 lwip_ntohl(seg->tcphdr->seqno), pcb->lastack));
+  }
+
+  netif = tcp_route(pcb, &pcb->local_ip, &pcb->remote_ip);
+  if (netif == NULL) {
+    return ERR_RTE;
+  }
+
+  /* If we don't have a local IP address, we get one from netif */
+  if (ip_addr_isany(&pcb->local_ip)) {
+    const ip_addr_t *local_ip = ip_netif_get_local_ip(netif, &pcb->remote_ip);
+    if (local_ip == NULL) {
+      return ERR_RTE;
+    }
+    ip_addr_copy(pcb->local_ip, *local_ip);
+  }
+
+  /* Handle the current segment not fitting within the window */
+  if (lwip_ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len > wnd) {
+    /* We need to start the persistent timer when the next unsent segment does not fit
+     * within the remaining (could be 0) send window and RTO timer is not running (we
+     * have no in-flight data). If window is still too small after persist timer fires,
+     * then we split the segment. We don't consider the congestion window since a cwnd
+     * smaller than 1 SMSS implies in-flight data
+     */
+    if (wnd == pcb->snd_wnd && pcb->unacked == NULL && pcb->persist_backoff == 0) {
+      pcb->persist_cnt = 0;
+      pcb->persist_backoff = 1;
+      pcb->persist_probe = 0;
+    }
+    /* We need an ACK, but can't send data now, so send an empty ACK */
+    if (pcb->flags & TF_ACK_NOW) {
+      return tcp_send_empty_ack(pcb);
+    }
+    goto output_done;
+  }
+  /* Stop persist timer, above conditions are not active */
+  pcb->persist_backoff = 0;
+
+  /* useg should point to last segment on unacked queue */
+  useg = pcb->unacked;
+  if (useg != NULL) {
+    for (; useg->next != NULL; useg = useg->next);
+  }
+  /* data available and window allows it to be sent? */
+  while (seg != NULL &&
+         lwip_ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len <= wnd) {
+    LWIP_ASSERT("RST not expected here!",
+                (TCPH_FLAGS(seg->tcphdr) & TCP_RST) == 0);
+    /* Stop sending if the nagle algorithm would prevent it
+     * Don't stop:
+     * - if tcp_write had a memory error before (prevent delayed ACK timeout) or
+     * - if FIN was already enqueued for this PCB (SYN is always alone in a segment -
+     *   either seg->next != NULL or pcb->unacked == NULL;
+     *   RST is no sent using tcp_write/tcp_output.
+     */
+    if ((tcp_do_output_nagle(pcb) == 0) &&
+        ((pcb->flags & (TF_NAGLEMEMERR | TF_FIN)) == 0)) {
+      break;
+    }
+#if TCP_CWND_DEBUG
+    LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_output: snd_wnd %"TCPWNDSIZE_F", cwnd %"TCPWNDSIZE_F", wnd %"U32_F", effwnd %"U32_F", seq %"U32_F", ack %"U32_F", i %"S16_F"\n",
+                                 pcb->snd_wnd, pcb->cwnd, wnd,
+                                 lwip_ntohl(seg->tcphdr->seqno) + seg->len -
+                                 pcb->lastack,
+                                 lwip_ntohl(seg->tcphdr->seqno), pcb->lastack, i));
+    ++i;
+#endif /* TCP_CWND_DEBUG */
+
+    if (pcb->state != SYN_SENT) {
+      TCPH_SET_FLAG(seg->tcphdr, TCP_ACK);
+    }
+
+    err = tcp_output_segment(seg, pcb, netif);
+    if (err != ERR_OK) {
+      /* segment could not be sent, for whatever reason */
+      tcp_set_flags(pcb, TF_NAGLEMEMERR);
+      return err;
+    }
+#if TCP_OVERSIZE_DBGCHECK
+    seg->oversize_left = 0;
+#endif /* TCP_OVERSIZE_DBGCHECK */
+    pcb->unsent = seg->next;
+    if (pcb->state != SYN_SENT) {
+      tcp_clear_flags(pcb, TF_ACK_DELAY | TF_ACK_NOW);
+    }
+    snd_nxt = lwip_ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg);
+    if (TCP_SEQ_LT(pcb->snd_nxt, snd_nxt)) {
+      pcb->snd_nxt = snd_nxt;
+    }
+    /* put segment on unacknowledged list if length > 0 */
+    if (TCP_TCPLEN(seg) > 0) {
+      seg->next = NULL;
+      /* unacked list is empty? */
+      if (pcb->unacked == NULL) {
+        pcb->unacked = seg;
+        useg = seg;
+        /* unacked list is not empty? */
+      } else {
+        /* In the case of fast retransmit, the packet should not go to the tail
+         * of the unacked queue, but rather somewhere before it. We need to check for
+         * this case. -STJ Jul 27, 2004 */
+        if (TCP_SEQ_LT(lwip_ntohl(seg->tcphdr->seqno), lwip_ntohl(useg->tcphdr->seqno))) {
+          /* add segment to before tail of unacked list, keeping the list sorted */
+          struct tcp_seg **cur_seg = &(pcb->unacked);
+          while (*cur_seg &&
+                 TCP_SEQ_LT(lwip_ntohl((*cur_seg)->tcphdr->seqno), lwip_ntohl(seg->tcphdr->seqno))) {
+            cur_seg = &((*cur_seg)->next );
+          }
+          seg->next = (*cur_seg);
+          (*cur_seg) = seg;
+        } else {
+          /* add segment to tail of unacked list */
+          useg->next = seg;
+          useg = useg->next;
+        }
+      }
+      /* do not queue empty segments on the unacked list */
+    } else {
+      tcp_seg_free(seg);
+    }
+    seg = pcb->unsent;
+  }
+#if TCP_OVERSIZE
+  if (pcb->unsent == NULL) {
+    /* last unsent has been removed, reset unsent_oversize */
+    pcb->unsent_oversize = 0;
+  }
+#endif /* TCP_OVERSIZE */
+
+output_done:
+  tcp_clear_flags(pcb, TF_NAGLEMEMERR);
+  return ERR_OK;
+}
+
+/** Check if a segment's pbufs are used by someone else than TCP.
+ * This can happen on retransmission if the pbuf of this segment is still
+ * referenced by the netif driver due to deferred transmission.
+ * This is the case (only!) if someone down the TX call path called
+ * pbuf_ref() on one of the pbufs!
+ *
+ * @arg seg the tcp segment to check
+ * @return 1 if ref != 1, 0 if ref == 1
+ */
+static int
+tcp_output_segment_busy(const struct tcp_seg *seg)
+{
+  LWIP_ASSERT("tcp_output_segment_busy: invalid seg", seg != NULL);
+
+  /* We only need to check the first pbuf here:
+     If a pbuf is queued for transmission, a driver calls pbuf_ref(),
+     which only changes the ref count of the first pbuf */
+  if (seg->p->ref != 1) {
+    /* other reference found */
+    return 1;
+  }
+  /* no other references found */
+  return 0;
+}
+
+/**
+ * Called by tcp_output() to actually send a TCP segment over IP.
+ *
+ * @param seg the tcp_seg to send
+ * @param pcb the tcp_pcb for the TCP connection used to send the segment
+ * @param netif the netif used to send the segment
+ */
+static err_t
+tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb, struct netif *netif)
+{
+  err_t err;
+  u16_t len;
+  u32_t *opts;
+#if TCP_CHECKSUM_ON_COPY
+  int seg_chksum_was_swapped = 0;
+#endif
+
+  LWIP_ASSERT("tcp_output_segment: invalid seg", seg != NULL);
+  LWIP_ASSERT("tcp_output_segment: invalid pcb", pcb != NULL);
+  LWIP_ASSERT("tcp_output_segment: invalid netif", netif != NULL);
+
+  if (tcp_output_segment_busy(seg)) {
+    /* This should not happen: rexmit functions should have checked this.
+       However, since this function modifies p->len, we must not continue in this case. */
+    LWIP_DEBUGF(TCP_RTO_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("tcp_output_segment: segment busy\n"));
+    return ERR_OK;
+  }
+
+  /* The TCP header has already been constructed, but the ackno and
+   wnd fields remain. */
+  seg->tcphdr->ackno = lwip_htonl(pcb->rcv_nxt);
+
+  /* advertise our receive window size in this TCP segment */
+#if LWIP_WND_SCALE
+  if (seg->flags & TF_SEG_OPTS_WND_SCALE) {
+    /* The Window field in a SYN segment itself (the only type where we send
+       the window scale option) is never scaled. */
+    seg->tcphdr->wnd = lwip_htons(TCPWND_MIN16(pcb->rcv_ann_wnd));
+  } else
+#endif /* LWIP_WND_SCALE */
+  {
+    seg->tcphdr->wnd = lwip_htons(TCPWND_MIN16(RCV_WND_SCALE(pcb, pcb->rcv_ann_wnd)));
+  }
+
+  pcb->rcv_ann_right_edge = pcb->rcv_nxt + pcb->rcv_ann_wnd;
+
+  /* Add any requested options.  NB MSS option is only set on SYN
+     packets, so ignore it here */
+  /* cast through void* to get rid of alignment warnings */
+  opts = (u32_t *)(void *)(seg->tcphdr + 1);
+  if (seg->flags & TF_SEG_OPTS_MSS) {
+    u16_t mss;
+#if TCP_CALCULATE_EFF_SEND_MSS
+    mss = tcp_eff_send_mss_netif(TCP_MSS, netif, &pcb->remote_ip);
+#else /* TCP_CALCULATE_EFF_SEND_MSS */
+    mss = TCP_MSS;
+#endif /* TCP_CALCULATE_EFF_SEND_MSS */
+    *opts = TCP_BUILD_MSS_OPTION(mss);
+    opts += 1;
+  }
+#if LWIP_TCP_TIMESTAMPS
+  pcb->ts_lastacksent = pcb->rcv_nxt;
+
+  if (seg->flags & TF_SEG_OPTS_TS) {
+    tcp_build_timestamp_option(pcb, opts);
+    opts += 3;
+  }
+#endif
+#if LWIP_WND_SCALE
+  if (seg->flags & TF_SEG_OPTS_WND_SCALE) {
+    tcp_build_wnd_scale_option(opts);
+    opts += 1;
+  }
+#endif
+#if LWIP_TCP_SACK_OUT
+  if (seg->flags & TF_SEG_OPTS_SACK_PERM) {
+    /* Pad with two NOP options to make everything nicely aligned
+     * NOTE: When we send both timestamp and SACK_PERM options,
+     * we could use the first two NOPs before the timestamp to store SACK_PERM option,
+     * but that would complicate the code.
+     */
+    *(opts++) = PP_HTONL(0x01010402);
+  }
+#endif
+
+  /* Set retransmission timer running if it is not currently enabled
+     This must be set before checking the route. */
+  if (pcb->rtime < 0) {
+    pcb->rtime = 0;
+  }
+
+  if (pcb->rttest == 0) {
+    pcb->rttest = tcp_ticks;
+    pcb->rtseq = lwip_ntohl(seg->tcphdr->seqno);
+
+    LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_output_segment: rtseq %"U32_F"\n", pcb->rtseq));
+  }
+  LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output_segment: %"U32_F":%"U32_F"\n",
+                                 lwip_htonl(seg->tcphdr->seqno), lwip_htonl(seg->tcphdr->seqno) +
+                                 seg->len));
+
+  len = (u16_t)((u8_t *)seg->tcphdr - (u8_t *)seg->p->payload);
+  if (len == 0) {
+    /** Exclude retransmitted segments from this count. */
+    MIB2_STATS_INC(mib2.tcpoutsegs);
+  }
+
+  seg->p->len -= len;
+  seg->p->tot_len -= len;
+
+  seg->p->payload = seg->tcphdr;
+
+  seg->tcphdr->chksum = 0;
+
+#ifdef LWIP_HOOK_TCP_OUT_ADD_TCPOPTS
+  opts = LWIP_HOOK_TCP_OUT_ADD_TCPOPTS(seg->p, seg->tcphdr, pcb, opts);
+#endif
+  LWIP_ASSERT("options not filled", (u8_t *)opts == ((u8_t *)(seg->tcphdr + 1)) + LWIP_TCP_OPT_LENGTH_SEGMENT(seg->flags, pcb));
+
+#if CHECKSUM_GEN_TCP
+  IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_TCP) {
+#if TCP_CHECKSUM_ON_COPY
+    u32_t acc;
+#if TCP_CHECKSUM_ON_COPY_SANITY_CHECK
+    u16_t chksum_slow = ip_chksum_pseudo(seg->p, IP_PROTO_TCP,
+                                         seg->p->tot_len, &pcb->local_ip, &pcb->remote_ip);
+#endif /* TCP_CHECKSUM_ON_COPY_SANITY_CHECK */
+    if ((seg->flags & TF_SEG_DATA_CHECKSUMMED) == 0) {
+      LWIP_ASSERT("data included but not checksummed",
+                  seg->p->tot_len == TCPH_HDRLEN_BYTES(seg->tcphdr));
+    }
+
+    /* rebuild TCP header checksum (TCP header changes for retransmissions!) */
+    acc = ip_chksum_pseudo_partial(seg->p, IP_PROTO_TCP,
+                                   seg->p->tot_len, TCPH_HDRLEN_BYTES(seg->tcphdr), &pcb->local_ip, &pcb->remote_ip);
+    /* add payload checksum */
+    if (seg->chksum_swapped) {
+      seg_chksum_was_swapped = 1;
+      seg->chksum = SWAP_BYTES_IN_WORD(seg->chksum);
+      seg->chksum_swapped = 0;
+    }
+    acc = (u16_t)~acc + seg->chksum;
+    seg->tcphdr->chksum = (u16_t)~FOLD_U32T(acc);
+#if TCP_CHECKSUM_ON_COPY_SANITY_CHECK
+    if (chksum_slow != seg->tcphdr->chksum) {
+      TCP_CHECKSUM_ON_COPY_SANITY_CHECK_FAIL(
+        ("tcp_output_segment: calculated checksum is %"X16_F" instead of %"X16_F"\n",
+         seg->tcphdr->chksum, chksum_slow));
+      seg->tcphdr->chksum = chksum_slow;
+    }
+#endif /* TCP_CHECKSUM_ON_COPY_SANITY_CHECK */
+#else /* TCP_CHECKSUM_ON_COPY */
+    seg->tcphdr->chksum = ip_chksum_pseudo(seg->p, IP_PROTO_TCP,
+                                           seg->p->tot_len, &pcb->local_ip, &pcb->remote_ip);
+#endif /* TCP_CHECKSUM_ON_COPY */
+  }
+#endif /* CHECKSUM_GEN_TCP */
+  TCP_STATS_INC(tcp.xmit);
+
+  NETIF_SET_HINTS(netif, &(pcb->netif_hints));
+  err = ip_output_if(seg->p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl,
+                     pcb->tos, IP_PROTO_TCP, netif);
+  NETIF_RESET_HINTS(netif);
+
+#if TCP_CHECKSUM_ON_COPY
+  if (seg_chksum_was_swapped) {
+    /* if data is added to this segment later, chksum needs to be swapped,
+       so restore this now */
+    seg->chksum = SWAP_BYTES_IN_WORD(seg->chksum);
+    seg->chksum_swapped = 1;
+  }
+#endif
+
+  return err;
+}
+
+/**
+ * Requeue all unacked segments for retransmission
+ *
+ * Called by tcp_slowtmr() for slow retransmission.
+ *
+ * @param pcb the tcp_pcb for which to re-enqueue all unacked segments
+ */
+err_t
+tcp_rexmit_rto_prepare(struct tcp_pcb *pcb)
+{
+  struct tcp_seg *seg;
+
+  LWIP_ASSERT("tcp_rexmit_rto_prepare: invalid pcb", pcb != NULL);
+
+  if (pcb->unacked == NULL) {
+    return ERR_VAL;
+  }
+
+  /* Move all unacked segments to the head of the unsent queue.
+     However, give up if any of the unsent pbufs are still referenced by the
+     netif driver due to deferred transmission. No point loading the link further
+     if it is struggling to flush its buffered writes. */
+  for (seg = pcb->unacked; seg->next != NULL; seg = seg->next) {
+    if (tcp_output_segment_busy(seg)) {
+      LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_rexmit_rto: segment busy\n"));
+      return ERR_VAL;
+    }
+  }
+  if (tcp_output_segment_busy(seg)) {
+    LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_rexmit_rto: segment busy\n"));
+    return ERR_VAL;
+  }
+  /* concatenate unsent queue after unacked queue */
+  seg->next = pcb->unsent;
+#if TCP_OVERSIZE_DBGCHECK
+  /* if last unsent changed, we need to update unsent_oversize */
+  if (pcb->unsent == NULL) {
+    pcb->unsent_oversize = seg->oversize_left;
+  }
+#endif /* TCP_OVERSIZE_DBGCHECK */
+  /* unsent queue is the concatenated queue (of unacked, unsent) */
+  pcb->unsent = pcb->unacked;
+  /* unacked queue is now empty */
+  pcb->unacked = NULL;
+
+  /* Mark RTO in-progress */
+  tcp_set_flags(pcb, TF_RTO);
+  /* Record the next byte following retransmit */
+  pcb->rto_end = lwip_ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg);
+  /* Don't take any RTT measurements after retransmitting. */
+  pcb->rttest = 0;
+
+  return ERR_OK;
+}
+
+/**
+ * Requeue all unacked segments for retransmission
+ *
+ * Called by tcp_slowtmr() for slow retransmission.
+ *
+ * @param pcb the tcp_pcb for which to re-enqueue all unacked segments
+ */
+void
+tcp_rexmit_rto_commit(struct tcp_pcb *pcb)
+{
+  LWIP_ASSERT("tcp_rexmit_rto_commit: invalid pcb", pcb != NULL);
+
+  /* increment number of retransmissions */
+  if (pcb->nrtx < 0xFF) {
+    ++pcb->nrtx;
+  }
+  /* Do the actual retransmission */
+  tcp_output(pcb);
+}
+
+/**
+ * Requeue all unacked segments for retransmission
+ *
+ * Called by tcp_process() only, tcp_slowtmr() needs to do some things between
+ * "prepare" and "commit".
+ *
+ * @param pcb the tcp_pcb for which to re-enqueue all unacked segments
+ */
+void
+tcp_rexmit_rto(struct tcp_pcb *pcb)
+{
+  LWIP_ASSERT("tcp_rexmit_rto: invalid pcb", pcb != NULL);
+
+  if (tcp_rexmit_rto_prepare(pcb) == ERR_OK) {
+    tcp_rexmit_rto_commit(pcb);
+  }
+}
+
+/**
+ * Requeue the first unacked segment for retransmission
+ *
+ * Called by tcp_receive() for fast retransmit.
+ *
+ * @param pcb the tcp_pcb for which to retransmit the first unacked segment
+ */
+err_t
+tcp_rexmit(struct tcp_pcb *pcb)
+{
+  struct tcp_seg *seg;
+  struct tcp_seg **cur_seg;
+
+  LWIP_ASSERT("tcp_rexmit: invalid pcb", pcb != NULL);
+
+  if (pcb->unacked == NULL) {
+    return ERR_VAL;
+  }
+
+  seg = pcb->unacked;
+
+  /* Give up if the segment is still referenced by the netif driver
+     due to deferred transmission. */
+  if (tcp_output_segment_busy(seg)) {
+    LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_rexmit busy\n"));
+    return ERR_VAL;
+  }
+
+  /* Move the first unacked segment to the unsent queue */
+  /* Keep the unsent queue sorted. */
+  pcb->unacked = seg->next;
+
+  cur_seg = &(pcb->unsent);
+  while (*cur_seg &&
+         TCP_SEQ_LT(lwip_ntohl((*cur_seg)->tcphdr->seqno), lwip_ntohl(seg->tcphdr->seqno))) {
+    cur_seg = &((*cur_seg)->next );
+  }
+  seg->next = *cur_seg;
+  *cur_seg = seg;
+#if TCP_OVERSIZE
+  if (seg->next == NULL) {
+    /* the retransmitted segment is last in unsent, so reset unsent_oversize */
+    pcb->unsent_oversize = 0;
+  }
+#endif /* TCP_OVERSIZE */
+
+  if (pcb->nrtx < 0xFF) {
+    ++pcb->nrtx;
+  }
+
+  /* Don't take any rtt measurements after retransmitting. */
+  pcb->rttest = 0;
+
+  /* Do the actual retransmission. */
+  MIB2_STATS_INC(mib2.tcpretranssegs);
+  /* No need to call tcp_output: we are always called from tcp_input()
+     and thus tcp_output directly returns. */
+  return ERR_OK;
+}
+
+
+/**
+ * Handle retransmission after three dupacks received
+ *
+ * @param pcb the tcp_pcb for which to retransmit the first unacked segment
+ */
+void
+tcp_rexmit_fast(struct tcp_pcb *pcb)
+{
+  LWIP_ASSERT("tcp_rexmit_fast: invalid pcb", pcb != NULL);
+
+  if (pcb->unacked != NULL && !(pcb->flags & TF_INFR)) {
+    /* This is fast retransmit. Retransmit the first unacked segment. */
+    LWIP_DEBUGF(TCP_FR_DEBUG,
+                ("tcp_receive: dupacks %"U16_F" (%"U32_F
+                 "), fast retransmit %"U32_F"\n",
+                 (u16_t)pcb->dupacks, pcb->lastack,
+                 lwip_ntohl(pcb->unacked->tcphdr->seqno)));
+    if (tcp_rexmit(pcb) == ERR_OK) {
+      /* Set ssthresh to half of the minimum of the current
+       * cwnd and the advertised window */
+      pcb->ssthresh = LWIP_MIN(pcb->cwnd, pcb->snd_wnd) / 2;
+
+      /* The minimum value for ssthresh should be 2 MSS */
+      if (pcb->ssthresh < (2U * pcb->mss)) {
+        LWIP_DEBUGF(TCP_FR_DEBUG,
+                    ("tcp_receive: The minimum value for ssthresh %"TCPWNDSIZE_F
+                     " should be min 2 mss %"U16_F"...\n",
+                     pcb->ssthresh, (u16_t)(2 * pcb->mss)));
+        pcb->ssthresh = 2 * pcb->mss;
+      }
+
+      pcb->cwnd = pcb->ssthresh + 3 * pcb->mss;
+      tcp_set_flags(pcb, TF_INFR);
+
+      /* Reset the retransmission timer to prevent immediate rto retransmissions */
+      pcb->rtime = 0;
+    }
+  }
+}
+
+static struct pbuf *
+tcp_output_alloc_header_common(u32_t ackno, u16_t optlen, u16_t datalen,
+                        u32_t seqno_be /* already in network byte order */,
+                        u16_t src_port, u16_t dst_port, u8_t flags, u16_t wnd)
+{
+  struct tcp_hdr *tcphdr;
+  struct pbuf *p;
+
+  p = pbuf_alloc(PBUF_IP, TCP_HLEN + optlen + datalen, PBUF_RAM);
+  if (p != NULL) {
+    LWIP_ASSERT("check that first pbuf can hold struct tcp_hdr",
+                (p->len >= TCP_HLEN + optlen));
+    tcphdr = (struct tcp_hdr *)p->payload;
+    tcphdr->src = lwip_htons(src_port);
+    tcphdr->dest = lwip_htons(dst_port);
+    tcphdr->seqno = seqno_be;
+    tcphdr->ackno = lwip_htonl(ackno);
+    TCPH_HDRLEN_FLAGS_SET(tcphdr, (5 + optlen / 4), flags);
+    tcphdr->wnd = lwip_htons(wnd);
+    tcphdr->chksum = 0;
+    tcphdr->urgp = 0;
+  }
+  return p;
+}
+
+/** Allocate a pbuf and create a tcphdr at p->payload, used for output
+ * functions other than the default tcp_output -> tcp_output_segment
+ * (e.g. tcp_send_empty_ack, etc.)
+ *
+ * @param pcb tcp pcb for which to send a packet (used to initialize tcp_hdr)
+ * @param optlen length of header-options
+ * @param datalen length of tcp data to reserve in pbuf
+ * @param seqno_be seqno in network byte order (big-endian)
+ * @return pbuf with p->payload being the tcp_hdr
+ */
+static struct pbuf *
+tcp_output_alloc_header(struct tcp_pcb *pcb, u16_t optlen, u16_t datalen,
+                        u32_t seqno_be /* already in network byte order */)
+{
+  struct pbuf *p;
+
+  LWIP_ASSERT("tcp_output_alloc_header: invalid pcb", pcb != NULL);
+
+  p = tcp_output_alloc_header_common(pcb->rcv_nxt, optlen, datalen,
+    seqno_be, pcb->local_port, pcb->remote_port, TCP_ACK,
+    TCPWND_MIN16(RCV_WND_SCALE(pcb, pcb->rcv_ann_wnd)));
+  if (p != NULL) {
+    /* If we're sending a packet, update the announced right window edge */
+    pcb->rcv_ann_right_edge = pcb->rcv_nxt + pcb->rcv_ann_wnd;
+  }
+  return p;
+}
+
+/* Fill in options for control segments */
+static void
+tcp_output_fill_options(const struct tcp_pcb *pcb, struct pbuf *p, u8_t optflags, u8_t num_sacks)
+{
+  struct tcp_hdr *tcphdr;
+  u32_t *opts;
+  u16_t sacks_len = 0;
+
+  LWIP_ASSERT("tcp_output_fill_options: invalid pbuf", p != NULL);
+
+  tcphdr = (struct tcp_hdr *)p->payload;
+  opts = (u32_t *)(void *)(tcphdr + 1);
+
+  /* NB. MSS and window scale options are only sent on SYNs, so ignore them here */
+
+#if LWIP_TCP_TIMESTAMPS
+  if (optflags & TF_SEG_OPTS_TS) {
+    tcp_build_timestamp_option(pcb, opts);
+    opts += 3;
+  }
+#endif
+
+#if LWIP_TCP_SACK_OUT
+  if (pcb && (num_sacks > 0)) {
+    tcp_build_sack_option(pcb, opts, num_sacks);
+    /* 1 word for SACKs header (including 2xNOP), and 2 words for each SACK */
+    sacks_len = 1 + num_sacks * 2;
+    opts += sacks_len;
+  }
+#else
+  LWIP_UNUSED_ARG(num_sacks);
+#endif
+
+#ifdef LWIP_HOOK_TCP_OUT_ADD_TCPOPTS
+  opts = LWIP_HOOK_TCP_OUT_ADD_TCPOPTS(p, tcphdr, pcb, opts);
+#endif
+
+  LWIP_UNUSED_ARG(pcb);
+  LWIP_UNUSED_ARG(sacks_len);
+  LWIP_ASSERT("options not filled", (u8_t *)opts == ((u8_t *)(tcphdr + 1)) + sacks_len * 4 + LWIP_TCP_OPT_LENGTH_SEGMENT(optflags, pcb));
+  LWIP_UNUSED_ARG(optflags); /* for LWIP_NOASSERT */
+  LWIP_UNUSED_ARG(opts); /* for LWIP_NOASSERT */
+}
+
+/** Output a control segment pbuf to IP.
+ *
+ * Called from tcp_rst, tcp_send_empty_ack, tcp_keepalive and tcp_zero_window_probe,
+ * this function combines selecting a netif for transmission, generating the tcp
+ * header checksum and calling ip_output_if while handling netif hints and stats.
+ */
+static err_t
+tcp_output_control_segment(const struct tcp_pcb *pcb, struct pbuf *p,
+                           const ip_addr_t *src, const ip_addr_t *dst)
+{
+  struct netif *netif;
+
+  LWIP_ASSERT("tcp_output_control_segment: invalid pbuf", p != NULL);
+
+  netif = tcp_route(pcb, src, dst);
+  if (netif == NULL) {
+    pbuf_free(p);
+    return ERR_RTE;
+  }
+  return tcp_output_control_segment_netif(pcb, p, src, dst, netif);
+}
+
+/** Output a control segment pbuf to IP.
+ *
+ * Called instead of tcp_output_control_segment when we don't have a pcb but we
+ * do know the interface to send to.
+ */
+static err_t
+tcp_output_control_segment_netif(const struct tcp_pcb *pcb, struct pbuf *p,
+                                 const ip_addr_t *src, const ip_addr_t *dst,
+                                 struct netif *netif)
+{
+  err_t err;
+  u8_t ttl, tos;
+
+  LWIP_ASSERT("tcp_output_control_segment_netif: no netif given", netif != NULL);
+
+#if CHECKSUM_GEN_TCP
+  IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_TCP) {
+    struct tcp_hdr *tcphdr = (struct tcp_hdr *)p->payload;
+    tcphdr->chksum = ip_chksum_pseudo(p, IP_PROTO_TCP, p->tot_len,
+                                      src, dst);
+  }
+#endif
+  if (pcb != NULL) {
+    NETIF_SET_HINTS(netif, LWIP_CONST_CAST(struct netif_hint*, &(pcb->netif_hints)));
+    ttl = pcb->ttl;
+    tos = pcb->tos;
+  } else {
+    /* Send output with hardcoded TTL/HL since we have no access to the pcb */
+    ttl = TCP_TTL;
+    tos = 0;
+  }
+  TCP_STATS_INC(tcp.xmit);
+  err = ip_output_if(p, src, dst, ttl, tos, IP_PROTO_TCP, netif);
+  NETIF_RESET_HINTS(netif);
+
+  pbuf_free(p);
+  return err;
+}
+
+static struct pbuf *
+tcp_rst_common(const struct tcp_pcb *pcb, u32_t seqno, u32_t ackno,
+               const ip_addr_t *local_ip, const ip_addr_t *remote_ip,
+               u16_t local_port, u16_t remote_port)
+{
+  struct pbuf *p;
+  u16_t wnd;
+  u8_t optlen;
+
+  LWIP_ASSERT("tcp_rst: invalid local_ip", local_ip != NULL);
+  LWIP_ASSERT("tcp_rst: invalid remote_ip", remote_ip != NULL);
+
+  optlen = LWIP_TCP_OPT_LENGTH_SEGMENT(0, pcb);
+
+#if LWIP_WND_SCALE
+  wnd = PP_HTONS(((TCP_WND >> TCP_RCV_SCALE) & 0xFFFF));
+#else
+  wnd = PP_HTONS(TCP_WND);
+#endif
+
+  p = tcp_output_alloc_header_common(ackno, optlen, 0, lwip_htonl(seqno), local_port,
+    remote_port, TCP_RST | TCP_ACK, wnd);
+  if (p == NULL) {
+    LWIP_DEBUGF(TCP_DEBUG, ("tcp_rst: could not allocate memory for pbuf\n"));
+    return NULL;
+  }
+  tcp_output_fill_options(pcb, p, 0, 0);
+
+  MIB2_STATS_INC(mib2.tcpoutrsts);
+
+  LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_rst: seqno %"U32_F" ackno %"U32_F".\n", seqno, ackno));
+  return p;
+}
+
+/**
+ * Send a TCP RESET packet (empty segment with RST flag set) to abort a
+ * connection.
+ *
+ * Called by tcp_abort() (to abort a local connection), tcp_closen() (if not
+ * all data has been received by the application), tcp_timewait_input() (if a
+ * SYN is received) and tcp_process() (received segment in the wrong state).
+ *
+ * Since a RST segment is in most cases not sent for an active connection,
+ * tcp_rst() has a number of arguments that are taken from a tcp_pcb for
+ * most other segment output functions.
+ *
+ * @param pcb TCP pcb (may be NULL if no pcb is available)
+ * @param seqno the sequence number to use for the outgoing segment
+ * @param ackno the acknowledge number to use for the outgoing segment
+ * @param local_ip the local IP address to send the segment from
+ * @param remote_ip the remote IP address to send the segment to
+ * @param local_port the local TCP port to send the segment from
+ * @param remote_port the remote TCP port to send the segment to
+ */
+void
+tcp_rst(const struct tcp_pcb *pcb, u32_t seqno, u32_t ackno,
+        const ip_addr_t *local_ip, const ip_addr_t *remote_ip,
+        u16_t local_port, u16_t remote_port)
+{
+  struct pbuf *p;
+  
+  p = tcp_rst_common(pcb, seqno, ackno, local_ip, remote_ip, local_port, remote_port);
+  if (p != NULL) {
+    tcp_output_control_segment(pcb, p, local_ip, remote_ip);
+  }
+}
+
+/**
+ * Send a TCP RESET packet (empty segment with RST flag set) to show that there
+ * is no matching local connection for a received segment.
+ *
+ * Called by tcp_input() (if no matching local pcb was found) and
+ * tcp_listen_input() (if incoming segment has ACK flag set).
+ *
+ * Since a RST segment is in most cases not sent for an active connection,
+ * tcp_rst() has a number of arguments that are taken from a tcp_pcb for
+ * most other segment output functions.
+ *
+ * @param netif the netif on which to send the RST (since we have no pcb)
+ * @param seqno the sequence number to use for the outgoing segment
+ * @param ackno the acknowledge number to use for the outgoing segment
+ * @param local_ip the local IP address to send the segment from
+ * @param remote_ip the remote IP address to send the segment to
+ * @param local_port the local TCP port to send the segment from
+ * @param remote_port the remote TCP port to send the segment to
+ */
+void
+tcp_rst_netif(struct netif *netif, u32_t seqno, u32_t ackno,
+              const ip_addr_t *local_ip, const ip_addr_t *remote_ip,
+              u16_t local_port, u16_t remote_port)
+{
+  if (netif) {
+    struct pbuf *p = tcp_rst_common(NULL, seqno, ackno, local_ip, remote_ip, local_port, remote_port);
+    if (p != NULL) {
+      tcp_output_control_segment_netif(NULL, p, local_ip, remote_ip, netif);
+    }
+  } else {
+    LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_rst_netif: no netif given\n"));
+  }
+}
+
+/**
+ * Send an ACK without data.
+ *
+ * @param pcb Protocol control block for the TCP connection to send the ACK
+ */
+err_t
+tcp_send_empty_ack(struct tcp_pcb *pcb)
+{
+  err_t err;
+  struct pbuf *p;
+  u8_t optlen, optflags = 0;
+  u8_t num_sacks = 0;
+
+  LWIP_ASSERT("tcp_send_empty_ack: invalid pcb", pcb != NULL);
+
+#if LWIP_TCP_TIMESTAMPS
+  if (pcb->flags & TF_TIMESTAMP) {
+    optflags = TF_SEG_OPTS_TS;
+  }
+#endif
+  optlen = LWIP_TCP_OPT_LENGTH_SEGMENT(optflags, pcb);
+
+#if LWIP_TCP_SACK_OUT
+  /* For now, SACKs are only sent with empty ACKs */
+  if ((num_sacks = tcp_get_num_sacks(pcb, optlen)) > 0) {
+    optlen += 4 + num_sacks * 8; /* 4 bytes for header (including 2*NOP), plus 8B for each SACK */
+  }
+#endif
+
+  p = tcp_output_alloc_header(pcb, optlen, 0, lwip_htonl(pcb->snd_nxt));
+  if (p == NULL) {
+    /* let tcp_fasttmr retry sending this ACK */
+    tcp_set_flags(pcb, TF_ACK_DELAY | TF_ACK_NOW);
+    LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: (ACK) could not allocate pbuf\n"));
+    return ERR_BUF;
+  }
+  tcp_output_fill_options(pcb, p, optflags, num_sacks);
+
+#if LWIP_TCP_TIMESTAMPS
+  pcb->ts_lastacksent = pcb->rcv_nxt;
+#endif
+
+  LWIP_DEBUGF(TCP_OUTPUT_DEBUG,
+              ("tcp_output: sending ACK for %"U32_F"\n", pcb->rcv_nxt));
+  err = tcp_output_control_segment(pcb, p, &pcb->local_ip, &pcb->remote_ip);
+  if (err != ERR_OK) {
+    /* let tcp_fasttmr retry sending this ACK */
+    tcp_set_flags(pcb, TF_ACK_DELAY | TF_ACK_NOW);
+  } else {
+    /* remove ACK flags from the PCB, as we sent an empty ACK now */
+    tcp_clear_flags(pcb, TF_ACK_DELAY | TF_ACK_NOW);
+  }
+
+  return err;
+}
+
+/**
+ * Send keepalive packets to keep a connection active although
+ * no data is sent over it.
+ *
+ * Called by tcp_slowtmr()
+ *
+ * @param pcb the tcp_pcb for which to send a keepalive packet
+ */
+err_t
+tcp_keepalive(struct tcp_pcb *pcb)
+{
+  err_t err;
+  struct pbuf *p;
+  u8_t optlen = LWIP_TCP_OPT_LENGTH_SEGMENT(0, pcb);
+
+  LWIP_ASSERT("tcp_keepalive: invalid pcb", pcb != NULL);
+
+  LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: sending KEEPALIVE probe to "));
+  ip_addr_debug_print_val(TCP_DEBUG, pcb->remote_ip);
+  LWIP_DEBUGF(TCP_DEBUG, ("\n"));
+
+  LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: tcp_ticks %"U32_F"   pcb->tmr %"U32_F" pcb->keep_cnt_sent %"U16_F"\n",
+                          tcp_ticks, pcb->tmr, (u16_t)pcb->keep_cnt_sent));
+
+  p = tcp_output_alloc_header(pcb, optlen, 0, lwip_htonl(pcb->snd_nxt - 1));
+  if (p == NULL) {
+    LWIP_DEBUGF(TCP_DEBUG,
+                ("tcp_keepalive: could not allocate memory for pbuf\n"));
+    return ERR_MEM;
+  }
+  tcp_output_fill_options(pcb, p, 0, 0);
+  err = tcp_output_control_segment(pcb, p, &pcb->local_ip, &pcb->remote_ip);
+
+  LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: seqno %"U32_F" ackno %"U32_F" err %d.\n",
+                          pcb->snd_nxt - 1, pcb->rcv_nxt, (int)err));
+  return err;
+}
+
+/**
+ * Send persist timer zero-window probes to keep a connection active
+ * when a window update is lost.
+ *
+ * Called by tcp_slowtmr()
+ *
+ * @param pcb the tcp_pcb for which to send a zero-window probe packet
+ */
+err_t
+tcp_zero_window_probe(struct tcp_pcb *pcb)
+{
+  err_t err;
+  struct pbuf *p;
+  struct tcp_hdr *tcphdr;
+  struct tcp_seg *seg;
+  u16_t len;
+  u8_t is_fin;
+  u32_t snd_nxt;
+  u8_t optlen = LWIP_TCP_OPT_LENGTH_SEGMENT(0, pcb);
+
+  LWIP_ASSERT("tcp_zero_window_probe: invalid pcb", pcb != NULL);
+
+  LWIP_DEBUGF(TCP_DEBUG, ("tcp_zero_window_probe: sending ZERO WINDOW probe to "));
+  ip_addr_debug_print_val(TCP_DEBUG, pcb->remote_ip);
+  LWIP_DEBUGF(TCP_DEBUG, ("\n"));
+
+  LWIP_DEBUGF(TCP_DEBUG,
+              ("tcp_zero_window_probe: tcp_ticks %"U32_F
+               "   pcb->tmr %"U32_F" pcb->keep_cnt_sent %"U16_F"\n",
+               tcp_ticks, pcb->tmr, (u16_t)pcb->keep_cnt_sent));
+
+  /* Only consider unsent, persist timer should be off when there is data in-flight */
+  seg = pcb->unsent;
+  if (seg == NULL) {
+    /* Not expected, persist timer should be off when the send buffer is empty */
+    return ERR_OK;
+  }
+
+  /* increment probe count. NOTE: we record probe even if it fails
+     to actually transmit due to an error. This ensures memory exhaustion/
+     routing problem doesn't leave a zero-window pcb as an indefinite zombie.
+     RTO mechanism has similar behavior, see pcb->nrtx */
+  if (pcb->persist_probe < 0xFF) {
+    ++pcb->persist_probe;
+  }
+
+  is_fin = ((TCPH_FLAGS(seg->tcphdr) & TCP_FIN) != 0) && (seg->len == 0);
+  /* we want to send one seqno: either FIN or data (no options) */
+  len = is_fin ? 0 : 1;
+
+  p = tcp_output_alloc_header(pcb, optlen, len, seg->tcphdr->seqno);
+  if (p == NULL) {
+    LWIP_DEBUGF(TCP_DEBUG, ("tcp_zero_window_probe: no memory for pbuf\n"));
+    return ERR_MEM;
+  }
+  tcphdr = (struct tcp_hdr *)p->payload;
+
+  if (is_fin) {
+    /* FIN segment, no data */
+    TCPH_FLAGS_SET(tcphdr, TCP_ACK | TCP_FIN);
+  } else {
+    /* Data segment, copy in one byte from the head of the unacked queue */
+    char *d = ((char *)p->payload + TCP_HLEN);
+    /* Depending on whether the segment has already been sent (unacked) or not
+       (unsent), seg->p->payload points to the IP header or TCP header.
+       Ensure we copy the first TCP data byte: */
+    pbuf_copy_partial(seg->p, d, 1, seg->p->tot_len - seg->len);
+  }
+
+  /* The byte may be acknowledged without the window being opened. */
+  snd_nxt = lwip_ntohl(seg->tcphdr->seqno) + 1;
+  if (TCP_SEQ_LT(pcb->snd_nxt, snd_nxt)) {
+    pcb->snd_nxt = snd_nxt;
+  }
+  tcp_output_fill_options(pcb, p, 0, 0);
+
+  err = tcp_output_control_segment(pcb, p, &pcb->local_ip, &pcb->remote_ip);
+
+  LWIP_DEBUGF(TCP_DEBUG, ("tcp_zero_window_probe: seqno %"U32_F
+                          " ackno %"U32_F" err %d.\n",
+                          pcb->snd_nxt - 1, pcb->rcv_nxt, (int)err));
+  return err;
+}
+#endif /* LWIP_TCP */
diff --git a/lib/lwip/core/timeouts.c b/lib/lwip/core/timeouts.c
new file mode 100644
index 0000000000..91657eb702
--- /dev/null
+++ b/lib/lwip/core/timeouts.c
@@ -0,0 +1,451 @@
+/**
+ * @file
+ * Stack-internal timers implementation.
+ * This file includes timer callbacks for stack-internal timers as well as
+ * functions to set up or stop timers and check for expired timers.
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *         Simon Goldschmidt
+ *
+ */
+
+#include "lwip/opt.h"
+
+#include "lwip/timeouts.h"
+#include "lwip/priv/tcp_priv.h"
+
+#include "lwip/def.h"
+#include "lwip/memp.h"
+#include "lwip/priv/tcpip_priv.h"
+
+#include "lwip/ip4_frag.h"
+#include "lwip/etharp.h"
+#include "lwip/dhcp.h"
+#include "lwip/acd.h"
+#include "lwip/igmp.h"
+#include "lwip/dns.h"
+#include "lwip/nd6.h"
+#include "lwip/ip6_frag.h"
+#include "lwip/mld6.h"
+#include "lwip/dhcp6.h"
+#include "lwip/sys.h"
+#include "lwip/pbuf.h"
+
+#if LWIP_DEBUG_TIMERNAMES
+#define HANDLER(x) x, #x
+#else /* LWIP_DEBUG_TIMERNAMES */
+#define HANDLER(x) x
+#endif /* LWIP_DEBUG_TIMERNAMES */
+
+#define LWIP_MAX_TIMEOUT  0x7fffffff
+
+/* Check if timer's expiry time is greater than time and care about u32_t wraparounds */
+#define TIME_LESS_THAN(t, compare_to) ( (((u32_t)((t)-(compare_to))) > LWIP_MAX_TIMEOUT) ? 1 : 0 )
+
+/** This array contains all stack-internal cyclic timers. To get the number of
+ * timers, use LWIP_ARRAYSIZE() */
+const struct lwip_cyclic_timer lwip_cyclic_timers[] = {
+#if LWIP_TCP
+  /* The TCP timer is a special case: it does not have to run always and
+     is triggered to start from TCP using tcp_timer_needed() */
+  {TCP_TMR_INTERVAL, HANDLER(tcp_tmr)},
+#endif /* LWIP_TCP */
+#if LWIP_IPV4
+#if IP_REASSEMBLY
+  {IP_TMR_INTERVAL, HANDLER(ip_reass_tmr)},
+#endif /* IP_REASSEMBLY */
+#if LWIP_ARP
+  {ARP_TMR_INTERVAL, HANDLER(etharp_tmr)},
+#endif /* LWIP_ARP */
+#if LWIP_DHCP
+  {DHCP_COARSE_TIMER_MSECS, HANDLER(dhcp_coarse_tmr)},
+  {DHCP_FINE_TIMER_MSECS, HANDLER(dhcp_fine_tmr)},
+#endif /* LWIP_DHCP */
+#if LWIP_ACD
+  {ACD_TMR_INTERVAL, HANDLER(acd_tmr)},
+#endif /* LWIP_ACD */
+#if LWIP_IGMP
+  {IGMP_TMR_INTERVAL, HANDLER(igmp_tmr)},
+#endif /* LWIP_IGMP */
+#endif /* LWIP_IPV4 */
+#if LWIP_DNS
+  {DNS_TMR_INTERVAL, HANDLER(dns_tmr)},
+#endif /* LWIP_DNS */
+#if LWIP_IPV6
+  {ND6_TMR_INTERVAL, HANDLER(nd6_tmr)},
+#if LWIP_IPV6_REASS
+  {IP6_REASS_TMR_INTERVAL, HANDLER(ip6_reass_tmr)},
+#endif /* LWIP_IPV6_REASS */
+#if LWIP_IPV6_MLD
+  {MLD6_TMR_INTERVAL, HANDLER(mld6_tmr)},
+#endif /* LWIP_IPV6_MLD */
+#if LWIP_IPV6_DHCP6
+  {DHCP6_TIMER_MSECS, HANDLER(dhcp6_tmr)},
+#endif /* LWIP_IPV6_DHCP6 */
+#endif /* LWIP_IPV6 */
+};
+const int lwip_num_cyclic_timers = LWIP_ARRAYSIZE(lwip_cyclic_timers);
+
+#if LWIP_TIMERS && !LWIP_TIMERS_CUSTOM
+
+/** The one and only timeout list */
+static struct sys_timeo *next_timeout;
+
+static u32_t current_timeout_due_time;
+
+#if LWIP_TESTMODE
+struct sys_timeo**
+sys_timeouts_get_next_timeout(void)
+{
+  return &next_timeout;
+}
+#endif
+
+#if LWIP_TCP
+/** global variable that shows if the tcp timer is currently scheduled or not */
+static int tcpip_tcp_timer_active;
+
+/**
+ * Timer callback function that calls tcp_tmr() and reschedules itself.
+ *
+ * @param arg unused argument
+ */
+static void
+tcpip_tcp_timer(void *arg)
+{
+  LWIP_UNUSED_ARG(arg);
+
+  /* call TCP timer handler */
+  tcp_tmr();
+  /* timer still needed? */
+  if (tcp_active_pcbs || tcp_tw_pcbs) {
+    /* restart timer */
+    sys_timeout(TCP_TMR_INTERVAL, tcpip_tcp_timer, NULL);
+  } else {
+    /* disable timer */
+    tcpip_tcp_timer_active = 0;
+  }
+}
+
+/**
+ * Called from TCP_REG when registering a new PCB:
+ * the reason is to have the TCP timer only running when
+ * there are active (or time-wait) PCBs.
+ */
+void
+tcp_timer_needed(void)
+{
+  LWIP_ASSERT_CORE_LOCKED();
+
+  /* timer is off but needed again? */
+  if (!tcpip_tcp_timer_active && (tcp_active_pcbs || tcp_tw_pcbs)) {
+    /* enable and start timer */
+    tcpip_tcp_timer_active = 1;
+    sys_timeout(TCP_TMR_INTERVAL, tcpip_tcp_timer, NULL);
+  }
+}
+#endif /* LWIP_TCP */
+
+static void
+#if LWIP_DEBUG_TIMERNAMES
+sys_timeout_abs(u32_t abs_time, sys_timeout_handler handler, void *arg, const char *handler_name)
+#else /* LWIP_DEBUG_TIMERNAMES */
+sys_timeout_abs(u32_t abs_time, sys_timeout_handler handler, void *arg)
+#endif
+{
+  struct sys_timeo *timeout, *t;
+
+  timeout = (struct sys_timeo *)memp_malloc(MEMP_SYS_TIMEOUT);
+  if (timeout == NULL) {
+    LWIP_ASSERT("sys_timeout: timeout != NULL, pool MEMP_SYS_TIMEOUT is empty", timeout != NULL);
+    return;
+  }
+
+  timeout->next = NULL;
+  timeout->h = handler;
+  timeout->arg = arg;
+  timeout->time = abs_time;
+
+#if LWIP_DEBUG_TIMERNAMES
+  timeout->handler_name = handler_name;
+  LWIP_DEBUGF(TIMERS_DEBUG, ("sys_timeout: %p abs_time=%"U32_F" handler=%s arg=%p\n",
+                             (void *)timeout, abs_time, handler_name, (void *)arg));
+#endif /* LWIP_DEBUG_TIMERNAMES */
+
+  if (next_timeout == NULL) {
+    next_timeout = timeout;
+    return;
+  }
+  if (TIME_LESS_THAN(timeout->time, next_timeout->time)) {
+    timeout->next = next_timeout;
+    next_timeout = timeout;
+  } else {
+    for (t = next_timeout; t != NULL; t = t->next) {
+      if ((t->next == NULL) || TIME_LESS_THAN(timeout->time, t->next->time)) {
+        timeout->next = t->next;
+        t->next = timeout;
+        break;
+      }
+    }
+  }
+}
+
+/**
+ * Timer callback function that calls cyclic->handler() and reschedules itself.
+ *
+ * @param arg unused argument
+ */
+#if !LWIP_TESTMODE
+static
+#endif
+void
+lwip_cyclic_timer(void *arg)
+{
+  u32_t now;
+  u32_t next_timeout_time;
+  const struct lwip_cyclic_timer *cyclic = (const struct lwip_cyclic_timer *)arg;
+
+#if LWIP_DEBUG_TIMERNAMES
+  LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: %s()\n", cyclic->handler_name));
+#endif
+  cyclic->handler();
+
+  now = sys_now();
+  next_timeout_time = (u32_t)(current_timeout_due_time + cyclic->interval_ms);  /* overflow handled by TIME_LESS_THAN macro */
+  if (TIME_LESS_THAN(next_timeout_time, now)) {
+    /* timer would immediately expire again -> "overload" -> restart without any correction */
+#if LWIP_DEBUG_TIMERNAMES
+    sys_timeout_abs((u32_t)(now + cyclic->interval_ms), lwip_cyclic_timer, arg, cyclic->handler_name);
+#else
+    sys_timeout_abs((u32_t)(now + cyclic->interval_ms), lwip_cyclic_timer, arg);
+#endif
+
+  } else {
+    /* correct cyclic interval with handler execution delay and sys_check_timeouts jitter */
+#if LWIP_DEBUG_TIMERNAMES
+    sys_timeout_abs(next_timeout_time, lwip_cyclic_timer, arg, cyclic->handler_name);
+#else
+    sys_timeout_abs(next_timeout_time, lwip_cyclic_timer, arg);
+#endif
+  }
+}
+
+/** Initialize this module */
+void sys_timeouts_init(void)
+{
+  size_t i;
+  /* tcp_tmr() at index 0 is started on demand */
+  for (i = (LWIP_TCP ? 1 : 0); i < LWIP_ARRAYSIZE(lwip_cyclic_timers); i++) {
+    /* we have to cast via size_t to get rid of const warning
+      (this is OK as cyclic_timer() casts back to const* */
+    sys_timeout(lwip_cyclic_timers[i].interval_ms, lwip_cyclic_timer, LWIP_CONST_CAST(void *, &lwip_cyclic_timers[i]));
+  }
+}
+
+/**
+ * Create a one-shot timer (aka timeout). Timeouts are processed in the
+ * following cases:
+ * - while waiting for a message using sys_timeouts_mbox_fetch()
+ * - by calling sys_check_timeouts() (NO_SYS==1 only)
+ *
+ * @param msecs time in milliseconds after that the timer should expire
+ * @param handler callback function to call when msecs have elapsed
+ * @param arg argument to pass to the callback function
+ */
+#if LWIP_DEBUG_TIMERNAMES
+void
+sys_timeout_debug(u32_t msecs, sys_timeout_handler handler, void *arg, const char *handler_name)
+#else /* LWIP_DEBUG_TIMERNAMES */
+void
+sys_timeout(u32_t msecs, sys_timeout_handler handler, void *arg)
+#endif /* LWIP_DEBUG_TIMERNAMES */
+{
+  u32_t next_timeout_time;
+
+  LWIP_ASSERT_CORE_LOCKED();
+
+  LWIP_ASSERT("Timeout time too long, max is LWIP_UINT32_MAX/4 msecs", msecs <= (LWIP_UINT32_MAX / 4));
+
+  next_timeout_time = (u32_t)(sys_now() + msecs); /* overflow handled by TIME_LESS_THAN macro */
+
+#if LWIP_DEBUG_TIMERNAMES
+  sys_timeout_abs(next_timeout_time, handler, arg, handler_name);
+#else
+  sys_timeout_abs(next_timeout_time, handler, arg);
+#endif
+}
+
+/**
+ * Go through timeout list (for this task only) and remove the first matching
+ * entry (subsequent entries remain untouched), even though the timeout has not
+ * triggered yet.
+ *
+ * @param handler callback function that would be called by the timeout
+ * @param arg callback argument that would be passed to handler
+*/
+void
+sys_untimeout(sys_timeout_handler handler, void *arg)
+{
+  struct sys_timeo *prev_t, *t;
+
+  LWIP_ASSERT_CORE_LOCKED();
+
+  if (next_timeout == NULL) {
+    return;
+  }
+
+  for (t = next_timeout, prev_t = NULL; t != NULL; prev_t = t, t = t->next) {
+    if ((t->h == handler) && (t->arg == arg)) {
+      /* We have a match */
+      /* Unlink from previous in list */
+      if (prev_t == NULL) {
+        next_timeout = t->next;
+      } else {
+        prev_t->next = t->next;
+      }
+      memp_free(MEMP_SYS_TIMEOUT, t);
+      return;
+    }
+  }
+  return;
+}
+
+/**
+ * @ingroup lwip_nosys
+ * Handle timeouts for NO_SYS==1 (i.e. without using
+ * tcpip_thread/sys_timeouts_mbox_fetch(). Uses sys_now() to call timeout
+ * handler functions when timeouts expire.
+ *
+ * Must be called periodically from your main loop.
+ */
+void
+sys_check_timeouts(void)
+{
+  u32_t now;
+
+  LWIP_ASSERT_CORE_LOCKED();
+
+  /* Process only timers expired at the start of the function. */
+  now = sys_now();
+
+  do {
+    struct sys_timeo *tmptimeout;
+    sys_timeout_handler handler;
+    void *arg;
+
+    PBUF_CHECK_FREE_OOSEQ();
+
+    tmptimeout = next_timeout;
+    if (tmptimeout == NULL) {
+      return;
+    }
+
+    if (TIME_LESS_THAN(now, tmptimeout->time)) {
+      return;
+    }
+
+    /* Timeout has expired */
+    next_timeout = tmptimeout->next;
+    handler = tmptimeout->h;
+    arg = tmptimeout->arg;
+    current_timeout_due_time = tmptimeout->time;
+#if LWIP_DEBUG_TIMERNAMES
+    if (handler != NULL) {
+      LWIP_DEBUGF(TIMERS_DEBUG, ("sct calling h=%s t=%"U32_F" arg=%p\n",
+                                 tmptimeout->handler_name, sys_now() - tmptimeout->time, arg));
+    }
+#endif /* LWIP_DEBUG_TIMERNAMES */
+    memp_free(MEMP_SYS_TIMEOUT, tmptimeout);
+    if (handler != NULL) {
+      handler(arg);
+    }
+    LWIP_TCPIP_THREAD_ALIVE();
+
+    /* Repeat until all expired timers have been called */
+  } while (1);
+}
+
+/** Rebase the timeout times to the current time.
+ * This is necessary if sys_check_timeouts() hasn't been called for a long
+ * time (e.g. while saving energy) to prevent all timer functions of that
+ * period being called.
+ */
+void
+sys_restart_timeouts(void)
+{
+  u32_t now;
+  u32_t base;
+  struct sys_timeo *t;
+
+  if (next_timeout == NULL) {
+    return;
+  }
+
+  now = sys_now();
+  base = next_timeout->time;
+
+  for (t = next_timeout; t != NULL; t = t->next) {
+    t->time = (t->time - base) + now;
+  }
+}
+
+/** Return the time left before the next timeout is due. If no timeouts are
+ * enqueued, returns 0xffffffff
+ */
+u32_t
+sys_timeouts_sleeptime(void)
+{
+  u32_t now;
+
+  LWIP_ASSERT_CORE_LOCKED();
+
+  if (next_timeout == NULL) {
+    return SYS_TIMEOUTS_SLEEPTIME_INFINITE;
+  }
+  now = sys_now();
+  if (TIME_LESS_THAN(next_timeout->time, now)) {
+    return 0;
+  } else {
+    u32_t ret = (u32_t)(next_timeout->time - now);
+    LWIP_ASSERT("invalid sleeptime", ret <= LWIP_MAX_TIMEOUT);
+    return ret;
+  }
+}
+
+#else /* LWIP_TIMERS && !LWIP_TIMERS_CUSTOM */
+/* Satisfy the TCP code which calls this function */
+void
+tcp_timer_needed(void)
+{
+}
+#endif /* LWIP_TIMERS && !LWIP_TIMERS_CUSTOM */
diff --git a/lib/lwip/core/udp.c b/lib/lwip/core/udp.c
new file mode 100644
index 0000000000..23c2be27bd
--- /dev/null
+++ b/lib/lwip/core/udp.c
@@ -0,0 +1,1321 @@
+/**
+ * @file
+ * User Datagram Protocol module<br>
+ * The code for the User Datagram Protocol UDP & UDPLite (RFC 3828).<br>
+ * See also @ref udp_raw
+ *
+ * @defgroup udp_raw UDP
+ * @ingroup callbackstyle_api
+ * User Datagram Protocol module<br>
+ * @see @ref api
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+/* @todo Check the use of '(struct udp_pcb).chksum_len_rx'!
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_UDP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/udp.h"
+#include "lwip/def.h"
+#include "lwip/memp.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/ip_addr.h"
+#include "lwip/ip6.h"
+#include "lwip/ip6_addr.h"
+#include "lwip/netif.h"
+#include "lwip/icmp.h"
+#include "lwip/icmp6.h"
+#include "lwip/stats.h"
+#include "lwip/snmp.h"
+#include "lwip/dhcp.h"
+
+#include <string.h>
+
+#ifndef UDP_LOCAL_PORT_RANGE_START
+/* From http://www.iana.org/assignments/port-numbers:
+   "The Dynamic and/or Private Ports are those from 49152 through 65535" */
+#define UDP_LOCAL_PORT_RANGE_START  0xc000
+#define UDP_LOCAL_PORT_RANGE_END    0xffff
+#define UDP_ENSURE_LOCAL_PORT_RANGE(port) ((u16_t)(((port) & (u16_t)~UDP_LOCAL_PORT_RANGE_START) + UDP_LOCAL_PORT_RANGE_START))
+#endif
+
+/* last local UDP port */
+static u16_t udp_port = UDP_LOCAL_PORT_RANGE_START;
+
+/* The list of UDP PCBs */
+/* exported in udp.h (was static) */
+struct udp_pcb *udp_pcbs;
+
+/**
+ * Initialize this module.
+ */
+void
+udp_init(void)
+{
+#ifdef LWIP_RAND
+  udp_port = UDP_ENSURE_LOCAL_PORT_RANGE(LWIP_RAND());
+#endif /* LWIP_RAND */
+}
+
+/**
+ * Allocate a new local UDP port.
+ *
+ * @return a new (free) local UDP port number
+ */
+static u16_t
+udp_new_port(void)
+{
+  u16_t n = 0;
+  struct udp_pcb *pcb;
+
+again:
+  if (udp_port++ == UDP_LOCAL_PORT_RANGE_END) {
+    udp_port = UDP_LOCAL_PORT_RANGE_START;
+  }
+  /* Check all PCBs. */
+  for (pcb = udp_pcbs; pcb != NULL; pcb = pcb->next) {
+    if (pcb->local_port == udp_port) {
+      if (++n > (UDP_LOCAL_PORT_RANGE_END - UDP_LOCAL_PORT_RANGE_START)) {
+        return 0;
+      }
+      goto again;
+    }
+  }
+  return udp_port;
+}
+
+/** Common code to see if the current input packet matches the pcb
+ * (current input packet is accessed via ip(4/6)_current_* macros)
+ *
+ * @param pcb pcb to check
+ * @param inp network interface on which the datagram was received (only used for IPv4)
+ * @param broadcast 1 if his is an IPv4 broadcast (global or subnet-only), 0 otherwise (only used for IPv4)
+ * @return 1 on match, 0 otherwise
+ */
+static u8_t
+udp_input_local_match(struct udp_pcb *pcb, struct netif *inp, u8_t broadcast)
+{
+  LWIP_UNUSED_ARG(inp);       /* in IPv6 only case */
+  LWIP_UNUSED_ARG(broadcast); /* in IPv6 only case */
+
+  LWIP_ASSERT("udp_input_local_match: invalid pcb", pcb != NULL);
+  LWIP_ASSERT("udp_input_local_match: invalid netif", inp != NULL);
+
+  /* check if PCB is bound to specific netif */
+  if ((pcb->netif_idx != NETIF_NO_INDEX) &&
+      (pcb->netif_idx != netif_get_index(ip_data.current_input_netif))) {
+    return 0;
+  }
+
+  /* Dual-stack: PCBs listening to any IP type also listen to any IP address */
+  if (IP_IS_ANY_TYPE_VAL(pcb->local_ip)) {
+#if LWIP_IPV4 && IP_SOF_BROADCAST_RECV
+    if ((broadcast != 0) && !ip_get_option(pcb, SOF_BROADCAST)) {
+      return 0;
+    }
+#endif /* LWIP_IPV4 && IP_SOF_BROADCAST_RECV */
+    return 1;
+  }
+
+  /* Only need to check PCB if incoming IP version matches PCB IP version */
+  if (IP_ADDR_PCB_VERSION_MATCH_EXACT(pcb, ip_current_dest_addr())) {
+#if LWIP_IPV4
+    /* Special case: IPv4 broadcast: all or broadcasts in my subnet
+     * Note: broadcast variable can only be 1 if it is an IPv4 broadcast */
+    if (broadcast != 0) {
+#if IP_SOF_BROADCAST_RECV
+      if (ip_get_option(pcb, SOF_BROADCAST))
+#endif /* IP_SOF_BROADCAST_RECV */
+      {
+        if (ip4_addr_isany(ip_2_ip4(&pcb->local_ip)) ||
+            ((ip4_current_dest_addr()->addr == IPADDR_BROADCAST)) ||
+            ip4_addr_net_eq(ip_2_ip4(&pcb->local_ip), ip4_current_dest_addr(), netif_ip4_netmask(inp))) {
+          return 1;
+        }
+      }
+    } else
+#endif /* LWIP_IPV4 */
+      /* Handle IPv4 and IPv6: all or exact match */
+      if (ip_addr_isany(&pcb->local_ip) || ip_addr_eq(&pcb->local_ip, ip_current_dest_addr())) {
+        return 1;
+      }
+  }
+
+  return 0;
+}
+
+/**
+ * Process an incoming UDP datagram.
+ *
+ * Given an incoming UDP datagram (as a chain of pbufs) this function
+ * finds a corresponding UDP PCB and hands over the pbuf to the pcbs
+ * recv function. If no pcb is found or the datagram is incorrect, the
+ * pbuf is freed.
+ *
+ * @param p pbuf to be demultiplexed to a UDP PCB (p->payload pointing to the UDP header)
+ * @param inp network interface on which the datagram was received.
+ *
+ */
+void
+udp_input(struct pbuf *p, struct netif *inp)
+{
+  struct udp_hdr *udphdr;
+  struct udp_pcb *pcb, *prev;
+  struct udp_pcb *uncon_pcb;
+  u16_t src, dest;
+  u8_t broadcast;
+  u8_t for_us = 0;
+
+  LWIP_UNUSED_ARG(inp);
+
+  LWIP_ASSERT_CORE_LOCKED();
+
+  LWIP_ASSERT("udp_input: invalid pbuf", p != NULL);
+  LWIP_ASSERT("udp_input: invalid netif", inp != NULL);
+
+  PERF_START;
+
+  UDP_STATS_INC(udp.recv);
+
+  /* Check minimum length (UDP header) */
+  if (p->len < UDP_HLEN) {
+    /* drop short packets */
+    LWIP_DEBUGF(UDP_DEBUG,
+                ("udp_input: short UDP datagram (%"U16_F" bytes) discarded\n", p->tot_len));
+    UDP_STATS_INC(udp.lenerr);
+    UDP_STATS_INC(udp.drop);
+    MIB2_STATS_INC(mib2.udpinerrors);
+    pbuf_free(p);
+    goto end;
+  }
+
+  udphdr = (struct udp_hdr *)p->payload;
+
+  /* is broadcast packet ? */
+  broadcast = ip_addr_isbroadcast(ip_current_dest_addr(), ip_current_netif());
+
+  LWIP_DEBUGF(UDP_DEBUG, ("udp_input: received datagram of length %"U16_F"\n", p->tot_len));
+
+  /* convert src and dest ports to host byte order */
+  src = lwip_ntohs(udphdr->src);
+  dest = lwip_ntohs(udphdr->dest);
+
+  udp_debug_print(udphdr);
+
+  /* print the UDP source and destination */
+  LWIP_DEBUGF(UDP_DEBUG, ("udp ("));
+  ip_addr_debug_print_val(UDP_DEBUG, *ip_current_dest_addr());
+  LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F") <-- (", lwip_ntohs(udphdr->dest)));
+  ip_addr_debug_print_val(UDP_DEBUG, *ip_current_src_addr());
+  LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F")\n", lwip_ntohs(udphdr->src)));
+
+  pcb = NULL;
+  prev = NULL;
+  uncon_pcb = NULL;
+  /* Iterate through the UDP pcb list for a matching pcb.
+   * 'Perfect match' pcbs (connected to the remote port & ip address) are
+   * preferred. If no perfect match is found, the first unconnected pcb that
+   * matches the local port and ip address gets the datagram. */
+  for (pcb = udp_pcbs; pcb != NULL; pcb = pcb->next) {
+    /* print the PCB local and remote address */
+    LWIP_DEBUGF(UDP_DEBUG, ("pcb ("));
+    ip_addr_debug_print_val(UDP_DEBUG, pcb->local_ip);
+    LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F") <-- (", pcb->local_port));
+    ip_addr_debug_print_val(UDP_DEBUG, pcb->remote_ip);
+    LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F")\n", pcb->remote_port));
+
+    /* compare PCB local addr+port to UDP destination addr+port */
+    if ((pcb->local_port == dest) &&
+        (udp_input_local_match(pcb, inp, broadcast) != 0)) {
+      if ((pcb->flags & UDP_FLAGS_CONNECTED) == 0) {
+        if (uncon_pcb == NULL) {
+          /* the first unconnected matching PCB */
+          uncon_pcb = pcb;
+#if LWIP_IPV4
+        } else if (broadcast && ip4_current_dest_addr()->addr == IPADDR_BROADCAST) {
+          /* global broadcast address (only valid for IPv4; match was checked before) */
+          if (!IP_IS_V4_VAL(uncon_pcb->local_ip) || !ip4_addr_eq(ip_2_ip4(&uncon_pcb->local_ip), netif_ip4_addr(inp))) {
+            /* uncon_pcb does not match the input netif, check this pcb */
+            if (IP_IS_V4_VAL(pcb->local_ip) && ip4_addr_eq(ip_2_ip4(&pcb->local_ip), netif_ip4_addr(inp))) {
+              /* better match */
+              uncon_pcb = pcb;
+            }
+          }
+#endif /* LWIP_IPV4 */
+        }
+#if SO_REUSE
+        else if (!ip_addr_isany(&pcb->local_ip)) {
+          /* prefer specific IPs over catch-all */
+          uncon_pcb = pcb;
+        }
+#endif /* SO_REUSE */
+      }
+
+      /* compare PCB remote addr+port to UDP source addr+port */
+      if ((pcb->remote_port == src) &&
+          (ip_addr_isany_val(pcb->remote_ip) ||
+           ip_addr_eq(&pcb->remote_ip, ip_current_src_addr()))) {
+        /* the first fully matching PCB */
+        if (prev != NULL) {
+          /* move the pcb to the front of udp_pcbs so that is
+             found faster next time */
+          prev->next = pcb->next;
+          pcb->next = udp_pcbs;
+          udp_pcbs = pcb;
+        } else {
+          UDP_STATS_INC(udp.cachehit);
+        }
+        break;
+      }
+    }
+
+    prev = pcb;
+  }
+  /* no fully matching pcb found? then look for an unconnected pcb */
+  if (pcb == NULL) {
+    pcb = uncon_pcb;
+  }
+
+  /* Check checksum if this is a match or if it was directed at us. */
+  if (pcb != NULL) {
+    for_us = 1;
+  } else {
+#if LWIP_IPV6
+    if (ip_current_is_v6()) {
+      for_us = netif_get_ip6_addr_match(inp, ip6_current_dest_addr()) >= 0;
+    }
+#endif /* LWIP_IPV6 */
+#if LWIP_IPV4
+    if (!ip_current_is_v6()) {
+      for_us = ip4_addr_eq(netif_ip4_addr(inp), ip4_current_dest_addr());
+    }
+#endif /* LWIP_IPV4 */
+  }
+
+  if (for_us) {
+    LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_input: calculating checksum\n"));
+#if CHECKSUM_CHECK_UDP
+    IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_CHECK_UDP) {
+#if LWIP_UDPLITE
+      if (ip_current_header_proto() == IP_PROTO_UDPLITE) {
+        /* Do the UDP Lite checksum */
+        u16_t chklen = lwip_ntohs(udphdr->len);
+        if (chklen < sizeof(struct udp_hdr)) {
+          if (chklen == 0) {
+            /* For UDP-Lite, checksum length of 0 means checksum
+               over the complete packet (See RFC 3828 chap. 3.1) */
+            chklen = p->tot_len;
+          } else {
+            /* At least the UDP-Lite header must be covered by the
+               checksum! (Again, see RFC 3828 chap. 3.1) */
+            goto chkerr;
+          }
+        }
+        if (ip_chksum_pseudo_partial(p, IP_PROTO_UDPLITE,
+                                     p->tot_len, chklen,
+                                     ip_current_src_addr(), ip_current_dest_addr()) != 0) {
+          goto chkerr;
+        }
+      } else
+#endif /* LWIP_UDPLITE */
+      {
+        if (udphdr->chksum != 0) {
+          if (ip_chksum_pseudo(p, IP_PROTO_UDP, p->tot_len,
+                               ip_current_src_addr(),
+                               ip_current_dest_addr()) != 0) {
+            goto chkerr;
+          }
+        }
+      }
+    }
+#endif /* CHECKSUM_CHECK_UDP */
+    if (pbuf_remove_header(p, UDP_HLEN)) {
+      /* Can we cope with this failing? Just assert for now */
+      LWIP_ASSERT("pbuf_remove_header failed", 0);
+      UDP_STATS_INC(udp.drop);
+      MIB2_STATS_INC(mib2.udpinerrors);
+      pbuf_free(p);
+      goto end;
+    }
+
+    if (pcb != NULL) {
+      MIB2_STATS_INC(mib2.udpindatagrams);
+#if SO_REUSE && SO_REUSE_RXTOALL
+      if (ip_get_option(pcb, SOF_REUSEADDR) &&
+          (broadcast || ip_addr_ismulticast(ip_current_dest_addr()))) {
+        /* pass broadcast- or multicast packets to all multicast pcbs
+           if SOF_REUSEADDR is set on the first match */
+        struct udp_pcb *mpcb;
+        for (mpcb = udp_pcbs; mpcb != NULL; mpcb = mpcb->next) {
+          if (mpcb != pcb) {
+            /* compare PCB local addr+port to UDP destination addr+port */
+            if ((mpcb->local_port == dest) &&
+                (udp_input_local_match(mpcb, inp, broadcast) != 0)) {
+              /* pass a copy of the packet to all local matches */
+              if (mpcb->recv != NULL) {
+                struct pbuf *q;
+                q = pbuf_clone(PBUF_RAW, PBUF_POOL, p);
+                if (q != NULL) {
+                  mpcb->recv(mpcb->recv_arg, mpcb, q, ip_current_src_addr(), src);
+                }
+              }
+            }
+          }
+        }
+      }
+#endif /* SO_REUSE && SO_REUSE_RXTOALL */
+      /* callback */
+      if (pcb->recv != NULL) {
+        /* now the recv function is responsible for freeing p */
+        pcb->recv(pcb->recv_arg, pcb, p, ip_current_src_addr(), src);
+      } else {
+        /* no recv function registered? then we have to free the pbuf! */
+        pbuf_free(p);
+        goto end;
+      }
+    } else {
+      LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_input: not for us.\n"));
+
+#if LWIP_ICMP || LWIP_ICMP6
+      /* No match was found, send ICMP destination port unreachable unless
+         destination address was broadcast/multicast. */
+      if (!broadcast && !ip_addr_ismulticast(ip_current_dest_addr())) {
+        /* move payload pointer back to ip header */
+        pbuf_header_force(p, (s16_t)(ip_current_header_tot_len() + UDP_HLEN));
+        icmp_port_unreach(ip_current_is_v6(), p);
+      }
+#endif /* LWIP_ICMP || LWIP_ICMP6 */
+      UDP_STATS_INC(udp.proterr);
+      UDP_STATS_INC(udp.drop);
+      MIB2_STATS_INC(mib2.udpnoports);
+      pbuf_free(p);
+    }
+  } else {
+    pbuf_free(p);
+  }
+end:
+  PERF_STOP("udp_input");
+  return;
+#if CHECKSUM_CHECK_UDP
+chkerr:
+  LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+              ("udp_input: UDP (or UDP Lite) datagram discarded due to failing checksum\n"));
+  UDP_STATS_INC(udp.chkerr);
+  UDP_STATS_INC(udp.drop);
+  MIB2_STATS_INC(mib2.udpinerrors);
+  pbuf_free(p);
+  PERF_STOP("udp_input");
+#endif /* CHECKSUM_CHECK_UDP */
+}
+
+/**
+ * @ingroup udp_raw
+ * Sends the pbuf p using UDP. The pbuf is not deallocated.
+ *
+ *
+ * @param pcb UDP PCB used to send the data.
+ * @param p chain of pbuf's to be sent.
+ *
+ * The datagram will be sent to the current remote_ip & remote_port
+ * stored in pcb. If the pcb is not bound to a port, it will
+ * automatically be bound to a random port.
+ *
+ * @return lwIP error code.
+ * - ERR_OK. Successful. No error occurred.
+ * - ERR_MEM. Out of memory.
+ * - ERR_RTE. Could not find route to destination address.
+ * - ERR_VAL. No PCB or PCB is dual-stack
+ * - More errors could be returned by lower protocol layers.
+ *
+ * @see udp_disconnect() udp_sendto()
+ */
+err_t
+udp_send(struct udp_pcb *pcb, struct pbuf *p)
+{
+  LWIP_ERROR("udp_send: invalid pcb", pcb != NULL, return ERR_ARG);
+  LWIP_ERROR("udp_send: invalid pbuf", p != NULL, return ERR_ARG);
+
+  if (IP_IS_ANY_TYPE_VAL(pcb->remote_ip)) {
+    return ERR_VAL;
+  }
+
+  /* send to the packet using remote ip and port stored in the pcb */
+  return udp_sendto(pcb, p, &pcb->remote_ip, pcb->remote_port);
+}
+
+#if LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP
+/** @ingroup udp_raw
+ * Same as udp_send() but with checksum
+ */
+err_t
+udp_send_chksum(struct udp_pcb *pcb, struct pbuf *p,
+                u8_t have_chksum, u16_t chksum)
+{
+  LWIP_ERROR("udp_send_chksum: invalid pcb", pcb != NULL, return ERR_ARG);
+  LWIP_ERROR("udp_send_chksum: invalid pbuf", p != NULL, return ERR_ARG);
+
+  if (IP_IS_ANY_TYPE_VAL(pcb->remote_ip)) {
+    return ERR_VAL;
+  }
+
+  /* send to the packet using remote ip and port stored in the pcb */
+  return udp_sendto_chksum(pcb, p, &pcb->remote_ip, pcb->remote_port,
+                           have_chksum, chksum);
+}
+#endif /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */
+
+/**
+ * @ingroup udp_raw
+ * Send data to a specified address using UDP.
+ *
+ * @param pcb UDP PCB used to send the data.
+ * @param p chain of pbuf's to be sent.
+ * @param dst_ip Destination IP address.
+ * @param dst_port Destination UDP port.
+ *
+ * dst_ip & dst_port are expected to be in the same byte order as in the pcb.
+ *
+ * If the PCB already has a remote address association, it will
+ * be restored after the data is sent.
+ *
+ * @return lwIP error code (@see udp_send for possible error codes)
+ *
+ * @see udp_disconnect() udp_send()
+ */
+err_t
+udp_sendto(struct udp_pcb *pcb, struct pbuf *p,
+           const ip_addr_t *dst_ip, u16_t dst_port)
+{
+#if LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP
+  return udp_sendto_chksum(pcb, p, dst_ip, dst_port, 0, 0);
+}
+
+/** @ingroup udp_raw
+ * Same as udp_sendto(), but with checksum */
+err_t
+udp_sendto_chksum(struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *dst_ip,
+                  u16_t dst_port, u8_t have_chksum, u16_t chksum)
+{
+#endif /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */
+  struct netif *netif;
+
+  LWIP_ERROR("udp_sendto: invalid pcb", pcb != NULL, return ERR_ARG);
+  LWIP_ERROR("udp_sendto: invalid pbuf", p != NULL, return ERR_ARG);
+  LWIP_ERROR("udp_sendto: invalid dst_ip", dst_ip != NULL, return ERR_ARG);
+
+  if (!IP_ADDR_PCB_VERSION_MATCH(pcb, dst_ip)) {
+    return ERR_VAL;
+  }
+
+  LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_send\n"));
+
+  if (pcb->netif_idx != NETIF_NO_INDEX) {
+    netif = netif_get_by_index(pcb->netif_idx);
+  } else {
+#if LWIP_MULTICAST_TX_OPTIONS
+    netif = NULL;
+    if (ip_addr_ismulticast(dst_ip)) {
+      /* For IPv6, the interface to use for packets with a multicast destination
+       * is specified using an interface index. The same approach may be used for
+       * IPv4 as well, in which case it overrides the IPv4 multicast override
+       * address below. Here we have to look up the netif by going through the
+       * list, but by doing so we skip a route lookup. If the interface index has
+       * gone stale, we fall through and do the regular route lookup after all. */
+      if (pcb->mcast_ifindex != NETIF_NO_INDEX) {
+        netif = netif_get_by_index(pcb->mcast_ifindex);
+      }
+#if LWIP_IPV4
+      else
+#if LWIP_IPV6
+        if (IP_IS_V4(dst_ip))
+#endif /* LWIP_IPV6 */
+        {
+          /* IPv4 does not use source-based routing by default, so we use an
+             administratively selected interface for multicast by default.
+             However, this can be overridden by setting an interface address
+             in pcb->mcast_ip4 that is used for routing. If this routing lookup
+             fails, we try regular routing as though no override was set. */
+          if (!ip4_addr_isany_val(pcb->mcast_ip4) &&
+              !ip4_addr_eq(&pcb->mcast_ip4, IP4_ADDR_BROADCAST)) {
+            netif = ip4_route_src(ip_2_ip4(&pcb->local_ip), &pcb->mcast_ip4);
+          }
+        }
+#endif /* LWIP_IPV4 */
+    }
+
+    if (netif == NULL)
+#endif /* LWIP_MULTICAST_TX_OPTIONS */
+    {
+      /* find the outgoing network interface for this packet */
+      netif = ip_route(&pcb->local_ip, dst_ip);
+    }
+  }
+
+  /* no outgoing network interface could be found? */
+  if (netif == NULL) {
+    LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("udp_send: No route to "));
+    ip_addr_debug_print(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, dst_ip);
+    LWIP_DEBUGF(UDP_DEBUG, ("\n"));
+    UDP_STATS_INC(udp.rterr);
+    return ERR_RTE;
+  }
+#if LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP
+  return udp_sendto_if_chksum(pcb, p, dst_ip, dst_port, netif, have_chksum, chksum);
+#else /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */
+  return udp_sendto_if(pcb, p, dst_ip, dst_port, netif);
+#endif /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */
+}
+
+/**
+ * @ingroup udp_raw
+ * Send data to a specified address using UDP.
+ * The netif used for sending can be specified.
+ *
+ * This function exists mainly for DHCP, to be able to send UDP packets
+ * on a netif that is still down.
+ *
+ * @param pcb UDP PCB used to send the data.
+ * @param p chain of pbuf's to be sent.
+ * @param dst_ip Destination IP address.
+ * @param dst_port Destination UDP port.
+ * @param netif the netif used for sending.
+ *
+ * dst_ip & dst_port are expected to be in the same byte order as in the pcb.
+ *
+ * @return lwIP error code (@see udp_send for possible error codes)
+ *
+ * @see udp_disconnect() udp_send()
+ */
+err_t
+udp_sendto_if(struct udp_pcb *pcb, struct pbuf *p,
+              const ip_addr_t *dst_ip, u16_t dst_port, struct netif *netif)
+{
+#if LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP
+  return udp_sendto_if_chksum(pcb, p, dst_ip, dst_port, netif, 0, 0);
+}
+
+/** Same as udp_sendto_if(), but with checksum */
+err_t
+udp_sendto_if_chksum(struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *dst_ip,
+                     u16_t dst_port, struct netif *netif, u8_t have_chksum,
+                     u16_t chksum)
+{
+#endif /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */
+  const ip_addr_t *src_ip;
+
+  LWIP_ERROR("udp_sendto_if: invalid pcb", pcb != NULL, return ERR_ARG);
+  LWIP_ERROR("udp_sendto_if: invalid pbuf", p != NULL, return ERR_ARG);
+  LWIP_ERROR("udp_sendto_if: invalid dst_ip", dst_ip != NULL, return ERR_ARG);
+  LWIP_ERROR("udp_sendto_if: invalid netif", netif != NULL, return ERR_ARG);
+
+  if (!IP_ADDR_PCB_VERSION_MATCH(pcb, dst_ip)) {
+    return ERR_VAL;
+  }
+
+  /* PCB local address is IP_ANY_ADDR or multicast? */
+#if LWIP_IPV6
+  if (IP_IS_V6(dst_ip)) {
+    if (ip6_addr_isany(ip_2_ip6(&pcb->local_ip)) ||
+        ip6_addr_ismulticast(ip_2_ip6(&pcb->local_ip))) {
+      src_ip = ip6_select_source_address(netif, ip_2_ip6(dst_ip));
+      if (src_ip == NULL) {
+        /* No suitable source address was found. */
+        return ERR_RTE;
+      }
+    } else {
+      /* use UDP PCB local IPv6 address as source address, if still valid. */
+      if (netif_get_ip6_addr_match(netif, ip_2_ip6(&pcb->local_ip)) < 0) {
+        /* Address isn't valid anymore. */
+        return ERR_RTE;
+      }
+      src_ip = &pcb->local_ip;
+    }
+  }
+#endif /* LWIP_IPV6 */
+#if LWIP_IPV4 && LWIP_IPV6
+  else
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+#if LWIP_IPV4
+    if (ip4_addr_isany(ip_2_ip4(&pcb->local_ip)) ||
+        ip4_addr_ismulticast(ip_2_ip4(&pcb->local_ip))) {
+      /* if the local_ip is any or multicast
+       * use the outgoing network interface IP address as source address */
+      src_ip = netif_ip_addr4(netif);
+    } else {
+      /* check if UDP PCB local IP address is correct
+       * this could be an old address if netif->ip_addr has changed */
+      if (!ip4_addr_eq(ip_2_ip4(&(pcb->local_ip)), netif_ip4_addr(netif))) {
+        /* local_ip doesn't match, drop the packet */
+        return ERR_RTE;
+      }
+      /* use UDP PCB local IP address as source address */
+      src_ip = &pcb->local_ip;
+    }
+#endif /* LWIP_IPV4 */
+#if LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP
+  return udp_sendto_if_src_chksum(pcb, p, dst_ip, dst_port, netif, have_chksum, chksum, src_ip);
+#else /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */
+  return udp_sendto_if_src(pcb, p, dst_ip, dst_port, netif, src_ip);
+#endif /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */
+}
+
+/** @ingroup udp_raw
+ * Same as @ref udp_sendto_if, but with source address */
+err_t
+udp_sendto_if_src(struct udp_pcb *pcb, struct pbuf *p,
+                  const ip_addr_t *dst_ip, u16_t dst_port, struct netif *netif, const ip_addr_t *src_ip)
+{
+#if LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP
+  return udp_sendto_if_src_chksum(pcb, p, dst_ip, dst_port, netif, 0, 0, src_ip);
+}
+
+/** Same as udp_sendto_if_src(), but with checksum */
+err_t
+udp_sendto_if_src_chksum(struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *dst_ip,
+                         u16_t dst_port, struct netif *netif, u8_t have_chksum,
+                         u16_t chksum, const ip_addr_t *src_ip)
+{
+#endif /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */
+  struct udp_hdr *udphdr;
+  err_t err;
+  struct pbuf *q; /* q will be sent down the stack */
+  u8_t ip_proto;
+  u8_t ttl;
+
+  LWIP_ASSERT_CORE_LOCKED();
+
+  LWIP_ERROR("udp_sendto_if_src: invalid pcb", pcb != NULL, return ERR_ARG);
+  LWIP_ERROR("udp_sendto_if_src: invalid pbuf", p != NULL, return ERR_ARG);
+  LWIP_ERROR("udp_sendto_if_src: invalid dst_ip", dst_ip != NULL, return ERR_ARG);
+  LWIP_ERROR("udp_sendto_if_src: invalid src_ip", src_ip != NULL, return ERR_ARG);
+  LWIP_ERROR("udp_sendto_if_src: invalid netif", netif != NULL, return ERR_ARG);
+
+  if (!IP_ADDR_PCB_VERSION_MATCH(pcb, src_ip) ||
+      !IP_ADDR_PCB_VERSION_MATCH(pcb, dst_ip)) {
+    return ERR_VAL;
+  }
+
+#if LWIP_IPV4 && IP_SOF_BROADCAST
+  /* broadcast filter? */
+  if (!ip_get_option(pcb, SOF_BROADCAST) &&
+#if LWIP_IPV6
+      IP_IS_V4(dst_ip) &&
+#endif /* LWIP_IPV6 */
+      ip_addr_isbroadcast(dst_ip, netif)) {
+    LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+                ("udp_sendto_if: SOF_BROADCAST not enabled on pcb %p\n", (void *)pcb));
+    return ERR_VAL;
+  }
+#endif /* LWIP_IPV4 && IP_SOF_BROADCAST */
+
+  /* if the PCB is not yet bound to a port, bind it here */
+  if (pcb->local_port == 0) {
+    LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_send: not yet bound to a port, binding now\n"));
+    err = udp_bind(pcb, &pcb->local_ip, pcb->local_port);
+    if (err != ERR_OK) {
+      LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("udp_send: forced port bind failed\n"));
+      return err;
+    }
+  }
+
+  /* packet too large to add a UDP header without causing an overflow? */
+  if ((u16_t)(p->tot_len + UDP_HLEN) < p->tot_len) {
+    return ERR_MEM;
+  }
+  /* not enough space to add an UDP header to first pbuf in given p chain? */
+  if (pbuf_add_header(p, UDP_HLEN)) {
+    /* allocate header in a separate new pbuf */
+    q = pbuf_alloc(PBUF_IP, UDP_HLEN, PBUF_RAM);
+    /* new header pbuf could not be allocated? */
+    if (q == NULL) {
+      LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("udp_send: could not allocate header\n"));
+      return ERR_MEM;
+    }
+    if (p->tot_len != 0) {
+      /* chain header q in front of given pbuf p (only if p contains data) */
+      pbuf_chain(q, p);
+    }
+    /* first pbuf q points to header pbuf */
+    LWIP_DEBUGF(UDP_DEBUG,
+                ("udp_send: added header pbuf %p before given pbuf %p\n", (void *)q, (void *)p));
+  } else {
+    /* adding space for header within p succeeded */
+    /* first pbuf q equals given pbuf */
+    q = p;
+    LWIP_DEBUGF(UDP_DEBUG, ("udp_send: added header in given pbuf %p\n", (void *)p));
+  }
+  LWIP_ASSERT("check that first pbuf can hold struct udp_hdr",
+              (q->len >= sizeof(struct udp_hdr)));
+  /* q now represents the packet to be sent */
+  udphdr = (struct udp_hdr *)q->payload;
+  udphdr->src = lwip_htons(pcb->local_port);
+  udphdr->dest = lwip_htons(dst_port);
+  /* in UDP, 0 checksum means 'no checksum' */
+  udphdr->chksum = 0x0000;
+
+  /* Multicast Loop? */
+#if LWIP_MULTICAST_TX_OPTIONS
+  if (((pcb->flags & UDP_FLAGS_MULTICAST_LOOP) != 0) && ip_addr_ismulticast(dst_ip)) {
+    q->flags |= PBUF_FLAG_MCASTLOOP;
+  }
+#endif /* LWIP_MULTICAST_TX_OPTIONS */
+
+  LWIP_DEBUGF(UDP_DEBUG, ("udp_send: sending datagram of length %"U16_F"\n", q->tot_len));
+
+#if LWIP_UDPLITE
+  /* UDP Lite protocol? */
+  if (pcb->flags & UDP_FLAGS_UDPLITE) {
+    u16_t chklen, chklen_hdr;
+    LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP LITE packet length %"U16_F"\n", q->tot_len));
+    /* set UDP message length in UDP header */
+    chklen_hdr = chklen = pcb->chksum_len_tx;
+    if ((chklen < sizeof(struct udp_hdr)) || (chklen > q->tot_len)) {
+      if (chklen != 0) {
+        LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP LITE pcb->chksum_len is illegal: %"U16_F"\n", chklen));
+      }
+      /* For UDP-Lite, checksum length of 0 means checksum
+         over the complete packet. (See RFC 3828 chap. 3.1)
+         At least the UDP-Lite header must be covered by the
+         checksum, therefore, if chksum_len has an illegal
+         value, we generate the checksum over the complete
+         packet to be safe. */
+      chklen_hdr = 0;
+      chklen = q->tot_len;
+    }
+    udphdr->len = lwip_htons(chklen_hdr);
+    /* calculate checksum */
+#if CHECKSUM_GEN_UDP
+    IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_UDP) {
+#if LWIP_CHECKSUM_ON_COPY
+      if (have_chksum) {
+        chklen = UDP_HLEN;
+      }
+#endif /* LWIP_CHECKSUM_ON_COPY */
+      udphdr->chksum = ip_chksum_pseudo_partial(q, IP_PROTO_UDPLITE,
+                       q->tot_len, chklen, src_ip, dst_ip);
+#if LWIP_CHECKSUM_ON_COPY
+      if (have_chksum) {
+        u32_t acc;
+        acc = udphdr->chksum + (u16_t)~(chksum);
+        udphdr->chksum = FOLD_U32T(acc);
+      }
+#endif /* LWIP_CHECKSUM_ON_COPY */
+
+      /* chksum zero must become 0xffff, as zero means 'no checksum' */
+      if (udphdr->chksum == 0x0000) {
+        udphdr->chksum = 0xffff;
+      }
+    }
+#endif /* CHECKSUM_GEN_UDP */
+
+    ip_proto = IP_PROTO_UDPLITE;
+  } else
+#endif /* LWIP_UDPLITE */
+  {      /* UDP */
+    LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP packet length %"U16_F"\n", q->tot_len));
+    udphdr->len = lwip_htons(q->tot_len);
+    /* calculate checksum */
+#if CHECKSUM_GEN_UDP
+    IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_UDP) {
+      /* Checksum is mandatory over IPv6. */
+      if (IP_IS_V6(dst_ip) || (pcb->flags & UDP_FLAGS_NOCHKSUM) == 0) {
+        u16_t udpchksum;
+#if LWIP_CHECKSUM_ON_COPY
+        if (have_chksum) {
+          u32_t acc;
+          udpchksum = ip_chksum_pseudo_partial(q, IP_PROTO_UDP,
+                                               q->tot_len, UDP_HLEN, src_ip, dst_ip);
+          acc = udpchksum + (u16_t)~(chksum);
+          udpchksum = FOLD_U32T(acc);
+        } else
+#endif /* LWIP_CHECKSUM_ON_COPY */
+        {
+          udpchksum = ip_chksum_pseudo(q, IP_PROTO_UDP, q->tot_len,
+                                       src_ip, dst_ip);
+        }
+
+        /* chksum zero must become 0xffff, as zero means 'no checksum' */
+        if (udpchksum == 0x0000) {
+          udpchksum = 0xffff;
+        }
+        udphdr->chksum = udpchksum;
+      }
+    }
+#endif /* CHECKSUM_GEN_UDP */
+    ip_proto = IP_PROTO_UDP;
+  }
+
+  /* Determine TTL to use */
+#if LWIP_MULTICAST_TX_OPTIONS
+  ttl = (ip_addr_ismulticast(dst_ip) ? udp_get_multicast_ttl(pcb) : pcb->ttl);
+#else /* LWIP_MULTICAST_TX_OPTIONS */
+  ttl = pcb->ttl;
+#endif /* LWIP_MULTICAST_TX_OPTIONS */
+
+  LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP checksum 0x%04"X16_F"\n", udphdr->chksum));
+  LWIP_DEBUGF(UDP_DEBUG, ("udp_send: ip_output_if (,,,,0x%02"X16_F",)\n", (u16_t)ip_proto));
+  /* output to IP */
+  NETIF_SET_HINTS(netif, &(pcb->netif_hints));
+  err = ip_output_if_src(q, src_ip, dst_ip, ttl, pcb->tos, ip_proto, netif);
+  NETIF_RESET_HINTS(netif);
+
+  /* @todo: must this be increased even if error occurred? */
+  MIB2_STATS_INC(mib2.udpoutdatagrams);
+
+  /* did we chain a separate header pbuf earlier? */
+  if (q != p) {
+    /* free the header pbuf */
+    pbuf_free(q);
+    q = NULL;
+    /* p is still referenced by the caller, and will live on */
+  }
+
+  UDP_STATS_INC(udp.xmit);
+  return err;
+}
+
+/**
+ * @ingroup udp_raw
+ * Bind an UDP PCB.
+ *
+ * @param pcb UDP PCB to be bound with a local address ipaddr and port.
+ * @param ipaddr local IP address to bind with. Use IP_ANY_TYPE to
+ * bind to all local interfaces.
+ * @param port local UDP port to bind with. Use 0 to automatically bind
+ * to a random port between UDP_LOCAL_PORT_RANGE_START and
+ * UDP_LOCAL_PORT_RANGE_END.
+ *
+ * ipaddr & port are expected to be in the same byte order as in the pcb.
+ *
+ * @return lwIP error code.
+ * - ERR_OK. Successful. No error occurred.
+ * - ERR_USE. The specified ipaddr and port are already bound to by
+ * another UDP PCB.
+ *
+ * @see udp_disconnect()
+ */
+err_t
+udp_bind(struct udp_pcb *pcb, const ip_addr_t *ipaddr, u16_t port)
+{
+  struct udp_pcb *ipcb;
+  u8_t rebind;
+#if LWIP_IPV6 && LWIP_IPV6_SCOPES
+  ip_addr_t zoned_ipaddr;
+#endif /* LWIP_IPV6 && LWIP_IPV6_SCOPES */
+
+  LWIP_ASSERT_CORE_LOCKED();
+
+#if LWIP_IPV4
+  /* Don't propagate NULL pointer (IPv4 ANY) to subsequent functions */
+  if (ipaddr == NULL) {
+    ipaddr = IP4_ADDR_ANY;
+  }
+#else /* LWIP_IPV4 */
+  LWIP_ERROR("udp_bind: invalid ipaddr", ipaddr != NULL, return ERR_ARG);
+#endif /* LWIP_IPV4 */
+
+  LWIP_ERROR("udp_bind: invalid pcb", pcb != NULL, return ERR_ARG);
+
+  LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_bind(ipaddr = "));
+  ip_addr_debug_print(UDP_DEBUG | LWIP_DBG_TRACE, ipaddr);
+  LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, (", port = %"U16_F")\n", port));
+
+  rebind = 0;
+  /* Check for double bind and rebind of the same pcb */
+  for (ipcb = udp_pcbs; ipcb != NULL; ipcb = ipcb->next) {
+    /* is this UDP PCB already on active list? */
+    if (pcb == ipcb) {
+      rebind = 1;
+      break;
+    }
+  }
+
+#if LWIP_IPV6 && LWIP_IPV6_SCOPES
+  /* If the given IP address should have a zone but doesn't, assign one now.
+   * This is legacy support: scope-aware callers should always provide properly
+   * zoned source addresses. Do the zone selection before the address-in-use
+   * check below; as such we have to make a temporary copy of the address. */
+  if (IP_IS_V6(ipaddr) && ip6_addr_lacks_zone(ip_2_ip6(ipaddr), IP6_UNKNOWN)) {
+    ip_addr_copy(zoned_ipaddr, *ipaddr);
+    ip6_addr_select_zone(ip_2_ip6(&zoned_ipaddr), ip_2_ip6(&zoned_ipaddr));
+    ipaddr = &zoned_ipaddr;
+  }
+#endif /* LWIP_IPV6 && LWIP_IPV6_SCOPES */
+
+  /* no port specified? */
+  if (port == 0) {
+    port = udp_new_port();
+    if (port == 0) {
+      /* no more ports available in local range */
+      LWIP_DEBUGF(UDP_DEBUG, ("udp_bind: out of free UDP ports\n"));
+      return ERR_USE;
+    }
+  } else {
+    for (ipcb = udp_pcbs; ipcb != NULL; ipcb = ipcb->next) {
+      if (pcb != ipcb) {
+        /* By default, we don't allow to bind to a port that any other udp
+           PCB is already bound to, unless *all* PCBs with that port have tha
+           REUSEADDR flag set. */
+#if SO_REUSE
+        if (!ip_get_option(pcb, SOF_REUSEADDR) ||
+            !ip_get_option(ipcb, SOF_REUSEADDR))
+#endif /* SO_REUSE */
+        {
+          /* port matches that of PCB in list and REUSEADDR not set -> reject */
+          if ((ipcb->local_port == port) &&
+              (((IP_GET_TYPE(&ipcb->local_ip) == IP_GET_TYPE(ipaddr)) &&
+              /* IP address matches or any IP used? */
+              (ip_addr_eq(&ipcb->local_ip, ipaddr) ||
+              ip_addr_isany(ipaddr) ||
+              ip_addr_isany(&ipcb->local_ip))) ||
+              (IP_GET_TYPE(&ipcb->local_ip) == IPADDR_TYPE_ANY) ||
+              (IP_GET_TYPE(ipaddr) == IPADDR_TYPE_ANY))) {
+            /* other PCB already binds to this local IP and port */
+            LWIP_DEBUGF(UDP_DEBUG,
+                        ("udp_bind: local port %"U16_F" already bound by another pcb\n", port));
+            return ERR_USE;
+          }
+        }
+      }
+    }
+  }
+
+  ip_addr_set_ipaddr(&pcb->local_ip, ipaddr);
+
+  pcb->local_port = port;
+  mib2_udp_bind(pcb);
+  /* pcb not active yet? */
+  if (rebind == 0) {
+    /* place the PCB on the active list if not already there */
+    pcb->next = udp_pcbs;
+    udp_pcbs = pcb;
+  }
+  LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("udp_bind: bound to "));
+  ip_addr_debug_print_val(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, pcb->local_ip);
+  LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, (", port %"U16_F")\n", pcb->local_port));
+  return ERR_OK;
+}
+
+/**
+ * @ingroup udp_raw
+ * Bind an UDP PCB to a specific netif.
+ * After calling this function, all packets received via this PCB
+ * are guaranteed to have come in via the specified netif, and all
+ * outgoing packets will go out via the specified netif.
+ *
+ * @param pcb UDP PCB to be bound.
+ * @param netif netif to bind udp pcb to. Can be NULL.
+ *
+ * @see udp_disconnect()
+ */
+void
+udp_bind_netif(struct udp_pcb *pcb, const struct netif *netif)
+{
+  LWIP_ASSERT_CORE_LOCKED();
+
+  if (netif != NULL) {
+    pcb->netif_idx = netif_get_index(netif);
+  } else {
+    pcb->netif_idx = NETIF_NO_INDEX;
+  }
+}
+
+/**
+ * @ingroup udp_raw
+ * Sets the remote end of the pcb. This function does not generate any
+ * network traffic, but only sets the remote address of the pcb.
+ *
+ * @param pcb UDP PCB to be connected with remote address ipaddr and port.
+ * @param ipaddr remote IP address to connect with.
+ * @param port remote UDP port to connect with.
+ *
+ * @return lwIP error code
+ *
+ * ipaddr & port are expected to be in the same byte order as in the pcb.
+ *
+ * The udp pcb is bound to a random local port if not already bound.
+ *
+ * @see udp_disconnect()
+ */
+err_t
+udp_connect(struct udp_pcb *pcb, const ip_addr_t *ipaddr, u16_t port)
+{
+  struct udp_pcb *ipcb;
+
+  LWIP_ASSERT_CORE_LOCKED();
+
+  LWIP_ERROR("udp_connect: invalid pcb", pcb != NULL, return ERR_ARG);
+  LWIP_ERROR("udp_connect: invalid ipaddr", ipaddr != NULL, return ERR_ARG);
+
+  if (pcb->local_port == 0) {
+    err_t err = udp_bind(pcb, &pcb->local_ip, pcb->local_port);
+    if (err != ERR_OK) {
+      return err;
+    }
+  }
+
+  ip_addr_set_ipaddr(&pcb->remote_ip, ipaddr);
+#if LWIP_IPV6 && LWIP_IPV6_SCOPES
+  /* If the given IP address should have a zone but doesn't, assign one now,
+   * using the bound address to make a more informed decision when possible. */
+  if (IP_IS_V6(&pcb->remote_ip) &&
+      ip6_addr_lacks_zone(ip_2_ip6(&pcb->remote_ip), IP6_UNKNOWN)) {
+    ip6_addr_select_zone(ip_2_ip6(&pcb->remote_ip), ip_2_ip6(&pcb->local_ip));
+  }
+#endif /* LWIP_IPV6 && LWIP_IPV6_SCOPES */
+
+  pcb->remote_port = port;
+  pcb->flags |= UDP_FLAGS_CONNECTED;
+
+  LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("udp_connect: connected to "));
+  ip_addr_debug_print_val(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+                          pcb->remote_ip);
+  LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, (", port %"U16_F")\n", pcb->remote_port));
+
+  /* Insert UDP PCB into the list of active UDP PCBs. */
+  for (ipcb = udp_pcbs; ipcb != NULL; ipcb = ipcb->next) {
+    if (pcb == ipcb) {
+      /* already on the list, just return */
+      return ERR_OK;
+    }
+  }
+  /* PCB not yet on the list, add PCB now */
+  pcb->next = udp_pcbs;
+  udp_pcbs = pcb;
+  return ERR_OK;
+}
+
+/**
+ * @ingroup udp_raw
+ * Remove the remote end of the pcb. This function does not generate
+ * any network traffic, but only removes the remote address of the pcb.
+ *
+ * @param pcb the udp pcb to disconnect.
+ */
+void
+udp_disconnect(struct udp_pcb *pcb)
+{
+  LWIP_ASSERT_CORE_LOCKED();
+
+  LWIP_ERROR("udp_disconnect: invalid pcb", pcb != NULL, return);
+
+  /* reset remote address association */
+#if LWIP_IPV4 && LWIP_IPV6
+  if (IP_IS_ANY_TYPE_VAL(pcb->local_ip)) {
+    ip_addr_copy(pcb->remote_ip, *IP_ANY_TYPE);
+  } else {
+#endif
+    ip_addr_set_any(IP_IS_V6_VAL(pcb->remote_ip), &pcb->remote_ip);
+#if LWIP_IPV4 && LWIP_IPV6
+  }
+#endif
+  pcb->remote_port = 0;
+  pcb->netif_idx = NETIF_NO_INDEX;
+  /* mark PCB as unconnected */
+  udp_clear_flags(pcb, UDP_FLAGS_CONNECTED);
+}
+
+/**
+ * @ingroup udp_raw
+ * Set a receive callback for a UDP PCB.
+ * This callback will be called when receiving a datagram for the pcb.
+ *
+ * @param pcb the pcb for which to set the recv callback
+ * @param recv function pointer of the callback function
+ * @param recv_arg additional argument to pass to the callback function
+ */
+void
+udp_recv(struct udp_pcb *pcb, udp_recv_fn recv, void *recv_arg)
+{
+  LWIP_ASSERT_CORE_LOCKED();
+
+  LWIP_ERROR("udp_recv: invalid pcb", pcb != NULL, return);
+
+  /* remember recv() callback and user data */
+  pcb->recv = recv;
+  pcb->recv_arg = recv_arg;
+}
+
+/**
+ * @ingroup udp_raw
+ * Removes and deallocates the pcb.
+ *
+ * @param pcb UDP PCB to be removed. The PCB is removed from the list of
+ * UDP PCB's and the data structure is freed from memory.
+ *
+ * @see udp_new()
+ */
+void
+udp_remove(struct udp_pcb *pcb)
+{
+  struct udp_pcb *pcb2;
+
+  LWIP_ASSERT_CORE_LOCKED();
+
+  LWIP_ERROR("udp_remove: invalid pcb", pcb != NULL, return);
+
+  mib2_udp_unbind(pcb);
+  /* pcb to be removed is first in list? */
+  if (udp_pcbs == pcb) {
+    /* make list start at 2nd pcb */
+    udp_pcbs = udp_pcbs->next;
+    /* pcb not 1st in list */
+  } else {
+    for (pcb2 = udp_pcbs; pcb2 != NULL; pcb2 = pcb2->next) {
+      /* find pcb in udp_pcbs list */
+      if (pcb2->next != NULL && pcb2->next == pcb) {
+        /* remove pcb from list */
+        pcb2->next = pcb->next;
+        break;
+      }
+    }
+  }
+  memp_free(MEMP_UDP_PCB, pcb);
+}
+
+/**
+ * @ingroup udp_raw
+ * Creates a new UDP pcb which can be used for UDP communication. The
+ * pcb is not active until it has either been bound to a local address
+ * or connected to a remote address.
+ * @see MEMP_NUM_UDP_PCB
+ *
+ * @return The UDP PCB which was created. NULL if the PCB data structure
+ * could not be allocated.
+ *
+ * @see udp_remove()
+ */
+struct udp_pcb *
+udp_new(void)
+{
+  struct udp_pcb *pcb;
+
+  LWIP_ASSERT_CORE_LOCKED();
+
+  pcb = (struct udp_pcb *)memp_malloc(MEMP_UDP_PCB);
+  /* could allocate UDP PCB? */
+  if (pcb != NULL) {
+    /* UDP Lite: by initializing to all zeroes, chksum_len is set to 0
+     * which means checksum is generated over the whole datagram per default
+     * (recommended as default by RFC 3828). */
+    /* initialize PCB to all zeroes */
+    memset(pcb, 0, sizeof(struct udp_pcb));
+    pcb->ttl = UDP_TTL;
+#if LWIP_MULTICAST_TX_OPTIONS
+    udp_set_multicast_ttl(pcb, UDP_TTL);
+#endif /* LWIP_MULTICAST_TX_OPTIONS */
+    pcb_tci_init(pcb);
+  }
+  return pcb;
+}
+
+/**
+ * @ingroup udp_raw
+ * Create a UDP PCB for specific IP type.
+ * The pcb is not active until it has either been bound to a local address
+ * or connected to a remote address.
+ * @see MEMP_NUM_UDP_PCB
+ *
+ * @param type IP address type, see @ref lwip_ip_addr_type definitions.
+ * If you want to listen to IPv4 and IPv6 (dual-stack) packets,
+ * supply @ref IPADDR_TYPE_ANY as argument and bind to @ref IP_ANY_TYPE.
+ * @return The UDP PCB which was created. NULL if the PCB data structure
+ * could not be allocated.
+ *
+ * @see udp_remove()
+ */
+struct udp_pcb *
+udp_new_ip_type(u8_t type)
+{
+  struct udp_pcb *pcb;
+
+  LWIP_ASSERT_CORE_LOCKED();
+
+  pcb = udp_new();
+#if LWIP_IPV4 && LWIP_IPV6
+  if (pcb != NULL) {
+    IP_SET_TYPE_VAL(pcb->local_ip,  type);
+    IP_SET_TYPE_VAL(pcb->remote_ip, type);
+  }
+#else
+  LWIP_UNUSED_ARG(type);
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+  return pcb;
+}
+
+/** This function is called from netif.c when address is changed
+ *
+ * @param old_addr IP address of the netif before change
+ * @param new_addr IP address of the netif after change
+ */
+void udp_netif_ip_addr_changed(const ip_addr_t *old_addr, const ip_addr_t *new_addr)
+{
+  struct udp_pcb *upcb;
+
+  if (!ip_addr_isany(old_addr) && !ip_addr_isany(new_addr)) {
+    for (upcb = udp_pcbs; upcb != NULL; upcb = upcb->next) {
+      /* PCB bound to current local interface address? */
+      if (ip_addr_eq(&upcb->local_ip, old_addr)) {
+        /* The PCB is bound to the old ipaddr and
+         * is set to bound to the new one instead */
+        ip_addr_copy(upcb->local_ip, *new_addr);
+      }
+    }
+  }
+}
+
+#if UDP_DEBUG
+/**
+ * Print UDP header information for debug purposes.
+ *
+ * @param udphdr pointer to the udp header in memory.
+ */
+void
+udp_debug_print(struct udp_hdr *udphdr)
+{
+  LWIP_DEBUGF(UDP_DEBUG, ("UDP header:\n"));
+  LWIP_DEBUGF(UDP_DEBUG, ("+-------------------------------+\n"));
+  LWIP_DEBUGF(UDP_DEBUG, ("|     %5"U16_F"     |     %5"U16_F"     | (src port, dest port)\n",
+                          lwip_ntohs(udphdr->src), lwip_ntohs(udphdr->dest)));
+  LWIP_DEBUGF(UDP_DEBUG, ("+-------------------------------+\n"));
+  LWIP_DEBUGF(UDP_DEBUG, ("|     %5"U16_F"     |     0x%04"X16_F"    | (len, chksum)\n",
+                          lwip_ntohs(udphdr->len), lwip_ntohs(udphdr->chksum)));
+  LWIP_DEBUGF(UDP_DEBUG, ("+-------------------------------+\n"));
+}
+#endif /* UDP_DEBUG */
+
+#endif /* LWIP_UDP */
diff --git a/lib/lwip/include/compat/posix/arpa/inet.h b/lib/lwip/include/compat/posix/arpa/inet.h
new file mode 100644
index 0000000000..0ed9baf3d9
--- /dev/null
+++ b/lib/lwip/include/compat/posix/arpa/inet.h
@@ -0,0 +1,33 @@
+/**
+ * @file
+ * This file is a posix wrapper for lwip/sockets.h.
+ */
+
+/*
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ */
+
+#include "lwip/sockets.h"
diff --git a/lib/lwip/include/compat/posix/net/if.h b/lib/lwip/include/compat/posix/net/if.h
new file mode 100644
index 0000000000..8b8e48198d
--- /dev/null
+++ b/lib/lwip/include/compat/posix/net/if.h
@@ -0,0 +1,36 @@
+/**
+ * @file
+ * This file is a posix wrapper for lwip/if_api.h.
+ */
+
+/*
+ * Copyright (c) 2017 Joel Cunningham, Garmin International, Inc. <joel.cunningham@garmin.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ */
+
+#include "lwip/if_api.h"
diff --git a/lib/lwip/include/compat/posix/netdb.h b/lib/lwip/include/compat/posix/netdb.h
new file mode 100644
index 0000000000..12d4c7f566
--- /dev/null
+++ b/lib/lwip/include/compat/posix/netdb.h
@@ -0,0 +1,33 @@
+/**
+ * @file
+ * This file is a posix wrapper for lwip/netdb.h.
+ */
+
+/*
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ */
+
+#include "lwip/netdb.h"
diff --git a/lib/lwip/include/compat/posix/sys/socket.h b/lib/lwip/include/compat/posix/sys/socket.h
new file mode 100644
index 0000000000..0ed9baf3d9
--- /dev/null
+++ b/lib/lwip/include/compat/posix/sys/socket.h
@@ -0,0 +1,33 @@
+/**
+ * @file
+ * This file is a posix wrapper for lwip/sockets.h.
+ */
+
+/*
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ */
+
+#include "lwip/sockets.h"
diff --git a/lib/lwip/include/compat/stdc/errno.h b/lib/lwip/include/compat/stdc/errno.h
new file mode 100644
index 0000000000..98a9aec996
--- /dev/null
+++ b/lib/lwip/include/compat/stdc/errno.h
@@ -0,0 +1,33 @@
+/**
+ * @file
+ * This file is a posix/stdc wrapper for lwip/errno.h.
+ */
+
+/*
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ */
+
+#include "lwip/errno.h"
diff --git a/lib/lwip/include/lwip/acd.h b/lib/lwip/include/lwip/acd.h
new file mode 100644
index 0000000000..e2e6cddfdc
--- /dev/null
+++ b/lib/lwip/include/lwip/acd.h
@@ -0,0 +1,109 @@
+/**
+ * @file
+ *
+ * ACD IPv4 Address Conflict Detection
+ */
+
+/*
+ *
+ * Copyright (c) 2007 Dominik Spies <kontakt@dspies.de>
+ * Copyright (c) 2018 Jasper Verschueren <jasper.verschueren@apart-audio.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Jasper Verschueren <jasper.verschueren@apart-audio.com>
+ * Author: Dominik Spies <kontakt@dspies.de>
+ */
+
+#ifndef LWIP_HDR_ACD_H
+#define LWIP_HDR_ACD_H
+
+#include "lwip/opt.h"
+
+/* don't build if not configured for use in lwipopts.h */
+#if LWIP_IPV4 && LWIP_ACD
+
+#include "lwip/netif.h"
+#include "lwip/etharp.h"
+#include "lwip/prot/acd.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** ACD Timing
+ *  ACD_TMR_INTERVAL msecs, I recommend a value of 100.
+ *  The value must divide 1000 with a remainder almost 0. Possible values are
+ *  1000, 500, 333, 250, 200, 166, 142, 125, 111, 100 ....
+ */
+#define ACD_TMR_INTERVAL      100
+
+/**
+ * Callback function: Handle conflict information from ACD module
+ *
+ * @param netif   network interface to handle conflict information on
+ * @param state   acd_callback_enum_t
+ */
+typedef void (*acd_conflict_callback_t)(struct netif *netif, acd_callback_enum_t state);
+
+/** ACD state information per netif */
+struct acd
+{
+  /** next acd module */
+  struct acd *next;
+  /** the currently selected, probed, announced or used IP-Address */
+  ip4_addr_t ipaddr;
+  /** current ACD state machine state */
+  acd_state_enum_t state;
+  /** sent number of probes or announces, dependent on state */
+  u8_t sent_num;
+  /** ticks to wait, tick is ACD_TMR_INTERVAL long */
+  u16_t ttw;
+  /** ticks until a conflict can again be solved by defending */
+  u8_t lastconflict;
+  /** total number of probed/used IP-Addresses that resulted in a conflict */
+  u8_t num_conflicts;
+  /** callback function -> let's the acd user know if the address is good or
+      if a conflict is detected */
+  acd_conflict_callback_t acd_conflict_callback;
+};
+
+err_t acd_add(struct netif *netif, struct acd *acd,
+              acd_conflict_callback_t acd_conflict_callback);
+void acd_remove(struct netif *netif, struct acd *acd);
+err_t acd_start(struct netif *netif, struct acd *acd, ip4_addr_t ipaddr);
+err_t acd_stop(struct acd *acd);
+void acd_arp_reply(struct netif *netif, struct etharp_hdr *hdr);
+void acd_tmr(void);
+void acd_network_changed_link_down(struct netif *netif);
+void acd_netif_ip_addr_changed(struct netif *netif, const ip_addr_t *old_addr,
+                               const ip_addr_t *new_addr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_IPV4 && LWIP_ACD */
+
+#endif /* LWIP_HDR_ACD_H */
diff --git a/lib/lwip/include/lwip/altcp.h b/lib/lwip/include/lwip/altcp.h
new file mode 100644
index 0000000000..11acaff3c9
--- /dev/null
+++ b/lib/lwip/include/lwip/altcp.h
@@ -0,0 +1,207 @@
+/**
+ * @file
+ * Application layered TCP connection API (to be used from TCPIP thread)
+ *
+ * This file contains the generic API.
+ * For more details see @ref altcp_api.
+ */
+
+/*
+ * Copyright (c) 2017 Simon Goldschmidt
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt <goldsimon@gmx.de>
+ *
+ */
+#ifndef LWIP_HDR_ALTCP_H
+#define LWIP_HDR_ALTCP_H
+
+#include "lwip/opt.h"
+
+#if LWIP_ALTCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/tcpbase.h"
+#include "lwip/err.h"
+#include "lwip/pbuf.h"
+#include "lwip/ip_addr.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct altcp_pcb;
+struct altcp_functions;
+
+typedef err_t (*altcp_accept_fn)(void *arg, struct altcp_pcb *new_conn, err_t err);
+typedef err_t (*altcp_connected_fn)(void *arg, struct altcp_pcb *conn, err_t err);
+typedef err_t (*altcp_recv_fn)(void *arg, struct altcp_pcb *conn, struct pbuf *p, err_t err);
+typedef err_t (*altcp_sent_fn)(void *arg, struct altcp_pcb *conn, u16_t len);
+typedef err_t (*altcp_poll_fn)(void *arg, struct altcp_pcb *conn);
+typedef void  (*altcp_err_fn)(void *arg, err_t err);
+
+typedef struct altcp_pcb* (*altcp_new_fn)(void *arg, u8_t ip_type);
+
+struct altcp_pcb {
+  const struct altcp_functions *fns;
+  struct altcp_pcb *inner_conn;
+  void *arg;
+  void *state;
+  /* application callbacks */
+  altcp_accept_fn     accept;
+  altcp_connected_fn  connected;
+  altcp_recv_fn       recv;
+  altcp_sent_fn       sent;
+  altcp_poll_fn       poll;
+  altcp_err_fn        err;
+  u8_t pollinterval;
+};
+
+/** @ingroup altcp
+ *  Struct containing an allocator and its state. */
+typedef struct altcp_allocator_s {
+  /** Allocator function */
+  altcp_new_fn  alloc;
+  /** Argument to allocator function */
+  void         *arg;
+} altcp_allocator_t;
+
+struct altcp_pcb *altcp_new(altcp_allocator_t *allocator);
+struct altcp_pcb *altcp_new_ip6(altcp_allocator_t *allocator);
+struct altcp_pcb *altcp_new_ip_type(altcp_allocator_t *allocator, u8_t ip_type);
+
+void altcp_arg(struct altcp_pcb *conn, void *arg);
+void altcp_accept(struct altcp_pcb *conn, altcp_accept_fn accept);
+void altcp_recv(struct altcp_pcb *conn, altcp_recv_fn recv);
+void altcp_sent(struct altcp_pcb *conn, altcp_sent_fn sent);
+void altcp_poll(struct altcp_pcb *conn, altcp_poll_fn poll, u8_t interval);
+void altcp_err(struct altcp_pcb *conn, altcp_err_fn err);
+
+void  altcp_recved(struct altcp_pcb *conn, u16_t len);
+err_t altcp_bind(struct altcp_pcb *conn, const ip_addr_t *ipaddr, u16_t port);
+err_t altcp_connect(struct altcp_pcb *conn, const ip_addr_t *ipaddr, u16_t port, altcp_connected_fn connected);
+
+/* return conn for source code compatibility to tcp callback API only */
+struct altcp_pcb *altcp_listen_with_backlog_and_err(struct altcp_pcb *conn, u8_t backlog, err_t *err);
+#define altcp_listen_with_backlog(conn, backlog) altcp_listen_with_backlog_and_err(conn, backlog, NULL)
+/** @ingroup altcp */
+#define altcp_listen(conn) altcp_listen_with_backlog_and_err(conn, TCP_DEFAULT_LISTEN_BACKLOG, NULL)
+
+void altcp_abort(struct altcp_pcb *conn);
+err_t altcp_close(struct altcp_pcb *conn);
+err_t altcp_shutdown(struct altcp_pcb *conn, int shut_rx, int shut_tx);
+
+err_t altcp_write(struct altcp_pcb *conn, const void *dataptr, u16_t len, u8_t apiflags);
+err_t altcp_output(struct altcp_pcb *conn);
+
+u16_t altcp_mss(struct altcp_pcb *conn);
+u16_t altcp_sndbuf(struct altcp_pcb *conn);
+u16_t altcp_sndqueuelen(struct altcp_pcb *conn);
+void  altcp_nagle_disable(struct altcp_pcb *conn);
+void  altcp_nagle_enable(struct altcp_pcb *conn);
+int   altcp_nagle_disabled(struct altcp_pcb *conn);
+
+void  altcp_setprio(struct altcp_pcb *conn, u8_t prio);
+
+err_t altcp_get_tcp_addrinfo(struct altcp_pcb *conn, int local, ip_addr_t *addr, u16_t *port);
+ip_addr_t *altcp_get_ip(struct altcp_pcb *conn, int local);
+u16_t altcp_get_port(struct altcp_pcb *conn, int local);
+
+#if LWIP_TCP_KEEPALIVE
+void  altcp_keepalive_disable(struct altcp_pcb *conn);
+void  altcp_keepalive_enable(struct altcp_pcb *conn, u32_t idle, u32_t intvl, u32_t count);
+#endif
+
+#ifdef LWIP_DEBUG
+enum tcp_state altcp_dbg_get_tcp_state(struct altcp_pcb *conn);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#else /* LWIP_ALTCP */
+
+/* ALTCP disabled, define everything to link against tcp callback API (e.g. to get a small non-ssl httpd) */
+
+#include "lwip/tcp.h"
+
+#define altcp_accept_fn tcp_accept_fn
+#define altcp_connected_fn tcp_connected_fn
+#define altcp_recv_fn tcp_recv_fn
+#define altcp_sent_fn tcp_sent_fn
+#define altcp_poll_fn tcp_poll_fn
+#define altcp_err_fn tcp_err_fn
+
+#define altcp_pcb tcp_pcb
+#define altcp_tcp_new_ip_type tcp_new_ip_type
+#define altcp_tcp_new tcp_new
+#define altcp_tcp_new_ip6 tcp_new_ip6
+
+#define altcp_new(allocator) tcp_new()
+#define altcp_new_ip6(allocator) tcp_new_ip6()
+#define altcp_new_ip_type(allocator, ip_type) tcp_new_ip_type(ip_type)
+
+#define altcp_arg tcp_arg
+#define altcp_accept tcp_accept
+#define altcp_recv tcp_recv
+#define altcp_sent tcp_sent
+#define altcp_poll tcp_poll
+#define altcp_err tcp_err
+
+#define altcp_recved tcp_recved
+#define altcp_bind tcp_bind
+#define altcp_connect tcp_connect
+
+#define altcp_listen_with_backlog_and_err tcp_listen_with_backlog_and_err
+#define altcp_listen_with_backlog tcp_listen_with_backlog
+#define altcp_listen tcp_listen
+
+#define altcp_abort tcp_abort
+#define altcp_close tcp_close
+#define altcp_shutdown tcp_shutdown
+
+#define altcp_write tcp_write
+#define altcp_output tcp_output
+
+#define altcp_mss tcp_mss
+#define altcp_sndbuf tcp_sndbuf
+#define altcp_sndqueuelen tcp_sndqueuelen
+#define altcp_nagle_disable tcp_nagle_disable
+#define altcp_nagle_enable tcp_nagle_enable
+#define altcp_nagle_disabled tcp_nagle_disabled
+#define altcp_setprio tcp_setprio
+
+#define altcp_get_tcp_addrinfo tcp_get_tcp_addrinfo
+#define altcp_get_ip(pcb, local) ((local) ? (&(pcb)->local_ip) : (&(pcb)->remote_ip))
+
+#ifdef LWIP_DEBUG
+#define altcp_dbg_get_tcp_state tcp_dbg_get_tcp_state
+#endif
+
+#endif /* LWIP_ALTCP */
+
+#endif /* LWIP_HDR_ALTCP_H */
diff --git a/lib/lwip/include/lwip/altcp_tcp.h b/lib/lwip/include/lwip/altcp_tcp.h
new file mode 100644
index 0000000000..ca473796b0
--- /dev/null
+++ b/lib/lwip/include/lwip/altcp_tcp.h
@@ -0,0 +1,72 @@
+/**
+ * @file
+ * Application layered TCP connection API (to be used from TCPIP thread)<br>
+ * This interface mimics the tcp callback API to the application while preventing
+ * direct linking (much like virtual functions).
+ * This way, an application can make use of other application layer protocols
+ * on top of TCP without knowing the details (e.g. TLS, proxy connection).
+ *
+ * This file contains the base implementation calling into tcp.
+ */
+
+/*
+ * Copyright (c) 2017 Simon Goldschmidt
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt <goldsimon@gmx.de>
+ *
+ */
+#ifndef LWIP_HDR_ALTCP_TCP_H
+#define LWIP_HDR_ALTCP_TCP_H
+
+#include "lwip/opt.h"
+
+#if LWIP_ALTCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/altcp.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct altcp_pcb *altcp_tcp_new_ip_type(u8_t ip_type);
+
+#define altcp_tcp_new() altcp_tcp_new_ip_type(IPADDR_TYPE_V4)
+#define altcp_tcp_new_ip6() altcp_tcp_new_ip_type(IPADDR_TYPE_V6)
+
+struct altcp_pcb *altcp_tcp_alloc(void *arg, u8_t ip_type);
+
+struct tcp_pcb;
+struct altcp_pcb *altcp_tcp_wrap(struct tcp_pcb *tpcb);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_ALTCP */
+
+#endif /* LWIP_HDR_ALTCP_TCP_H */
diff --git a/lib/lwip/include/lwip/altcp_tls.h b/lib/lwip/include/lwip/altcp_tls.h
new file mode 100644
index 0000000000..fcb784d89d
--- /dev/null
+++ b/lib/lwip/include/lwip/altcp_tls.h
@@ -0,0 +1,196 @@
+/**
+ * @file
+ * Application layered TCP/TLS connection API (to be used from TCPIP thread)
+ *
+ * @defgroup altcp_tls TLS layer
+ * @ingroup altcp
+ * This file contains function prototypes for a TLS layer.
+ * A port to ARM mbedtls is provided in the apps/ tree
+ * (LWIP_ALTCP_TLS_MBEDTLS option).
+ */
+
+/*
+ * Copyright (c) 2017 Simon Goldschmidt
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt <goldsimon@gmx.de>
+ *
+ */
+#ifndef LWIP_HDR_ALTCP_TLS_H
+#define LWIP_HDR_ALTCP_TLS_H
+
+#include "lwip/opt.h"
+
+#if LWIP_ALTCP /* don't build if not configured for use in lwipopts.h */
+
+#if LWIP_ALTCP_TLS
+
+#include "lwip/altcp.h"
+
+/* check if mbedtls port is enabled */
+#include "lwip/apps/altcp_tls_mbedtls_opts.h"
+/* allow session structure to be fully defined when using mbedtls port */
+#if LWIP_ALTCP_TLS_MBEDTLS
+#include "mbedtls/ssl.h"
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** @ingroup altcp_tls
+ * ALTCP_TLS configuration handle, content depends on port (e.g. mbedtls)
+ */
+struct altcp_tls_config;
+
+/** @ingroup altcp_tls
+ * Create an ALTCP_TLS server configuration handle prepared for multiple certificates
+ */
+struct altcp_tls_config *altcp_tls_create_config_server(u8_t cert_count);
+
+/** @ingroup altcp_tls
+ * Add a certificate to an ALTCP_TLS server configuration handle
+ */
+err_t altcp_tls_config_server_add_privkey_cert(struct altcp_tls_config *config,
+      const u8_t *privkey, size_t privkey_len,
+      const u8_t *privkey_pass, size_t privkey_pass_len,
+      const u8_t *cert, size_t cert_len);
+
+/** @ingroup altcp_tls
+ * Create an ALTCP_TLS server configuration handle with one certificate
+ * (short version of calling @ref altcp_tls_create_config_server and
+ * @ref altcp_tls_config_server_add_privkey_cert)
+ */
+struct altcp_tls_config *altcp_tls_create_config_server_privkey_cert(const u8_t *privkey, size_t privkey_len,
+                            const u8_t *privkey_pass, size_t privkey_pass_len,
+                            const u8_t *cert, size_t cert_len);
+
+/** @ingroup altcp_tls
+ * Create an ALTCP_TLS client configuration handle
+ */
+struct altcp_tls_config *altcp_tls_create_config_client(const u8_t *cert, size_t cert_len);
+
+/** @ingroup altcp_tls
+ * Create an ALTCP_TLS client configuration handle with two-way server/client authentication
+ */
+struct altcp_tls_config *altcp_tls_create_config_client_2wayauth(const u8_t *ca, size_t ca_len, const u8_t *privkey, size_t privkey_len,
+                            const u8_t *privkey_pass, size_t privkey_pass_len,
+                            const u8_t *cert, size_t cert_len);
+
+/** @ingroup altcp_tls
+ * Configure ALPN TLS extension
+ * Example:<br>
+ * static const char *g_alpn_protocols[] = { "x-amzn-mqtt-ca", NULL };<br>
+ * tls_config = altcp_tls_create_config_client(ca, ca_len);<br>
+ * altcp_tls_conf_alpn_protocols(tls_config, g_alpn_protocols);<br>
+ */
+int altcp_tls_configure_alpn_protocols(struct altcp_tls_config *conf, const char **protos);
+
+/** @ingroup altcp_tls
+ * Free an ALTCP_TLS configuration handle
+ */
+void altcp_tls_free_config(struct altcp_tls_config *conf);
+
+/** @ingroup altcp_tls
+ * Free an ALTCP_TLS global entropy instance.
+ * All ALTCP_TLS configuration are linked to one altcp_tls_entropy_rng structure
+ * that handle an unique system entropy & ctr_drbg instance.
+ * This function allow application to free this altcp_tls_entropy_rng structure
+ * when all configuration referencing it were destroyed.
+ * This function does nothing if some ALTCP_TLS configuration handle are still
+ * active.
+ */
+void altcp_tls_free_entropy(void);
+
+/** @ingroup altcp_tls
+ * Create new ALTCP_TLS layer wrapping an existing pcb as inner connection (e.g. TLS over TCP)
+ */
+struct altcp_pcb *altcp_tls_wrap(struct altcp_tls_config *config, struct altcp_pcb *inner_pcb);
+
+/** @ingroup altcp_tls
+ * Create new ALTCP_TLS pcb and its inner tcp pcb
+ */
+struct altcp_pcb *altcp_tls_new(struct altcp_tls_config *config, u8_t ip_type);
+
+/** @ingroup altcp_tls
+ * Create new ALTCP_TLS layer pcb and its inner tcp pcb.
+ * Same as @ref altcp_tls_new but this allocator function fits to
+ * @ref altcp_allocator_t / @ref altcp_new.<br>
+ 'arg' must contain a struct altcp_tls_config *.
+ */
+struct altcp_pcb *altcp_tls_alloc(void *arg, u8_t ip_type);
+
+/** @ingroup altcp_tls
+ * Return pointer to internal TLS context so application can tweak it.
+ * Real type depends on port (e.g. mbedtls)
+ */
+void *altcp_tls_context(struct altcp_pcb *conn);
+
+/** @ingroup altcp_tls
+ * ALTCP_TLS session handle, content depends on port (e.g. mbedtls)
+ */
+struct altcp_tls_session
+#if LWIP_ALTCP_TLS_MBEDTLS
+{
+    mbedtls_ssl_session data;
+}
+#endif
+;
+
+/** @ingroup altcp_tls
+ * Initialise a TLS session buffer.
+ * Real type depends on port (e.g. mbedtls use mbedtls_ssl_session)
+ */
+void altcp_tls_init_session(struct altcp_tls_session *dest);
+
+/** @ingroup altcp_tls
+ * Save current connected session to reuse it later. Should be called after altcp_connect() succeeded.
+ * Return error if saving session fail.
+ * Real type depends on port (e.g. mbedtls use mbedtls_ssl_session)
+ */
+err_t altcp_tls_get_session(struct altcp_pcb *conn, struct altcp_tls_session *dest);
+
+/** @ingroup altcp_tls
+ * Restore a previously saved session. Must be called before altcp_connect().
+ * Return error if cannot restore session.
+ * Real type depends on port (e.g. mbedtls use mbedtls_ssl_session)
+ */
+err_t altcp_tls_set_session(struct altcp_pcb *conn, struct altcp_tls_session *from);
+
+/** @ingroup altcp_tls
+ * Free allocated data inside a TLS session buffer.
+ * Real type depends on port (e.g. mbedtls use mbedtls_ssl_session)
+ */
+void altcp_tls_free_session(struct altcp_tls_session *dest);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_ALTCP_TLS */
+#endif /* LWIP_ALTCP */
+#endif /* LWIP_HDR_ALTCP_TLS_H */
diff --git a/lib/lwip/include/lwip/api.h b/lib/lwip/include/lwip/api.h
new file mode 100644
index 0000000000..be8c22aabf
--- /dev/null
+++ b/lib/lwip/include/lwip/api.h
@@ -0,0 +1,434 @@
+/**
+ * @file
+ * netconn API (to be used from non-TCPIP threads)
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef LWIP_HDR_API_H
+#define LWIP_HDR_API_H
+
+#include "lwip/opt.h"
+
+#if LWIP_NETCONN || LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */
+/* Note: Netconn API is always available when sockets are enabled -
+ * sockets are implemented on top of them */
+
+#include "lwip/arch.h"
+#include "lwip/netbuf.h"
+#include "lwip/sys.h"
+#include "lwip/ip_addr.h"
+#include "lwip/err.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Throughout this file, IP addresses and port numbers are expected to be in
+ * the same byte order as in the corresponding pcb.
+ */
+
+/* Flags for netconn_write (u8_t) */
+#define NETCONN_NOFLAG      0x00
+#define NETCONN_NOCOPY      0x00 /* Only for source code compatibility */
+#define NETCONN_COPY        0x01
+#define NETCONN_MORE        0x02
+#define NETCONN_DONTBLOCK   0x04
+#define NETCONN_NOAUTORCVD  0x08 /* prevent netconn_recv_data_tcp() from updating the tcp window - must be done manually via netconn_tcp_recvd() */
+#define NETCONN_NOFIN       0x10 /* upper layer already received data, leave FIN in queue until called again */
+
+/* Flags for struct netconn.flags (u8_t) */
+/** This netconn had an error, don't block on recvmbox/acceptmbox any more */
+#define NETCONN_FLAG_MBOXCLOSED               0x01
+/** Should this netconn avoid blocking? */
+#define NETCONN_FLAG_NON_BLOCKING             0x02
+/** Was the last connect action a non-blocking one? */
+#define NETCONN_FLAG_IN_NONBLOCKING_CONNECT   0x04
+#if LWIP_NETCONN_FULLDUPLEX
+  /** The mbox of this netconn is being deallocated, don't use it anymore */
+#define NETCONN_FLAG_MBOXINVALID              0x08
+#endif /* LWIP_NETCONN_FULLDUPLEX */
+/** If a nonblocking write has been rejected before, poll_tcp needs to
+    check if the netconn is writable again */
+#define NETCONN_FLAG_CHECK_WRITESPACE         0x10
+#if LWIP_IPV6
+/** If this flag is set then only IPv6 communication is allowed on the
+    netconn. As per RFC#3493 this features defaults to OFF allowing
+    dual-stack usage by default. */
+#define NETCONN_FLAG_IPV6_V6ONLY              0x20
+#endif /* LWIP_IPV6 */
+#if LWIP_NETBUF_RECVINFO
+/** Received packet info will be recorded for this netconn */
+#define NETCONN_FLAG_PKTINFO                  0x40
+#endif /* LWIP_NETBUF_RECVINFO */
+/** A FIN has been received but not passed to the application yet */
+#define NETCONN_FIN_RX_PENDING                0x80
+
+/* Helpers to process several netconn_types by the same code */
+#define NETCONNTYPE_GROUP(t)         ((t)&0xF0)
+#define NETCONNTYPE_DATAGRAM(t)      ((t)&0xE0)
+#if LWIP_IPV6
+#define NETCONN_TYPE_IPV6            0x08
+#define NETCONNTYPE_ISIPV6(t)        (((t)&NETCONN_TYPE_IPV6) != 0)
+#define NETCONNTYPE_ISUDPLITE(t)     (((t)&0xF3) == NETCONN_UDPLITE)
+#define NETCONNTYPE_ISUDPNOCHKSUM(t) (((t)&0xF3) == NETCONN_UDPNOCHKSUM)
+#else /* LWIP_IPV6 */
+#define NETCONNTYPE_ISIPV6(t)        (0)
+#define NETCONNTYPE_ISUDPLITE(t)     ((t) == NETCONN_UDPLITE)
+#define NETCONNTYPE_ISUDPNOCHKSUM(t) ((t) == NETCONN_UDPNOCHKSUM)
+#endif /* LWIP_IPV6 */
+
+/** @ingroup netconn_common
+ * Protocol family and type of the netconn
+ */
+enum netconn_type {
+  NETCONN_INVALID     = 0,
+  /** TCP IPv4 */
+  NETCONN_TCP         = 0x10,
+#if LWIP_IPV6
+  /** TCP IPv6 */
+  NETCONN_TCP_IPV6    = NETCONN_TCP | NETCONN_TYPE_IPV6 /* 0x18 */,
+#endif /* LWIP_IPV6 */
+  /** UDP IPv4 */
+  NETCONN_UDP         = 0x20,
+  /** UDP IPv4 lite */
+  NETCONN_UDPLITE     = 0x21,
+  /** UDP IPv4 no checksum */
+  NETCONN_UDPNOCHKSUM = 0x22,
+
+#if LWIP_IPV6
+  /** UDP IPv6 (dual-stack by default, unless you call @ref netconn_set_ipv6only) */
+  NETCONN_UDP_IPV6         = NETCONN_UDP | NETCONN_TYPE_IPV6 /* 0x28 */,
+  /** UDP IPv6 lite (dual-stack by default, unless you call @ref netconn_set_ipv6only) */
+  NETCONN_UDPLITE_IPV6     = NETCONN_UDPLITE | NETCONN_TYPE_IPV6 /* 0x29 */,
+  /** UDP IPv6 no checksum (dual-stack by default, unless you call @ref netconn_set_ipv6only) */
+  NETCONN_UDPNOCHKSUM_IPV6 = NETCONN_UDPNOCHKSUM | NETCONN_TYPE_IPV6 /* 0x2a */,
+#endif /* LWIP_IPV6 */
+
+  /** Raw connection IPv4 */
+  NETCONN_RAW         = 0x40
+#if LWIP_IPV6
+  /** Raw connection IPv6 (dual-stack by default, unless you call @ref netconn_set_ipv6only) */
+  , NETCONN_RAW_IPV6    = NETCONN_RAW | NETCONN_TYPE_IPV6 /* 0x48 */
+#endif /* LWIP_IPV6 */
+};
+
+/** Current state of the netconn. Non-TCP netconns are always
+ * in state NETCONN_NONE! */
+enum netconn_state {
+  NETCONN_NONE,
+  NETCONN_WRITE,
+  NETCONN_LISTEN,
+  NETCONN_CONNECT,
+  NETCONN_CLOSE
+};
+
+/** Used to inform the callback function about changes
+ *
+ * Event explanation:
+ *
+ * In the netconn implementation, there are three ways to block a client:
+ *
+ * - accept mbox (sys_arch_mbox_fetch(&conn->acceptmbox, &accept_ptr, 0); in netconn_accept())
+ * - receive mbox (sys_arch_mbox_fetch(&conn->recvmbox, &buf, 0); in netconn_recv_data())
+ * - send queue is full (sys_arch_sem_wait(LWIP_API_MSG_SEM(msg), 0); in lwip_netconn_do_write())
+ *
+ * The events have to be seen as events signaling the state of these mboxes/semaphores. For non-blocking
+ * connections, you need to know in advance whether a call to a netconn function call would block or not,
+ * and these events tell you about that.
+ *
+ * RCVPLUS events say: Safe to perform a potentially blocking call call once more.
+ * They are counted in sockets - three RCVPLUS events for accept mbox means you are safe
+ * to call netconn_accept 3 times without being blocked.
+ * Same thing for receive mbox.
+ *
+ * RCVMINUS events say: Your call to to a possibly blocking function is "acknowledged".
+ * Socket implementation decrements the counter.
+ *
+ * For TX, there is no need to count, its merely a flag. SENDPLUS means you may send something.
+ * SENDPLUS occurs when enough data was delivered to peer so netconn_send() can be called again.
+ * A SENDMINUS event occurs when the next call to a netconn_send() would be blocking.
+ */
+enum netconn_evt {
+  NETCONN_EVT_RCVPLUS,
+  NETCONN_EVT_RCVMINUS,
+  NETCONN_EVT_SENDPLUS,
+  NETCONN_EVT_SENDMINUS,
+  NETCONN_EVT_ERROR
+};
+
+#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD)
+/** Used for netconn_join_leave_group() */
+enum netconn_igmp {
+  NETCONN_JOIN,
+  NETCONN_LEAVE
+};
+#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */
+
+#if LWIP_DNS
+/* Used for netconn_gethostbyname_addrtype(), these should match the DNS_ADDRTYPE defines in dns.h */
+#define NETCONN_DNS_DEFAULT   NETCONN_DNS_IPV4_IPV6
+#define NETCONN_DNS_IPV4      0
+#define NETCONN_DNS_IPV6      1
+#define NETCONN_DNS_IPV4_IPV6 2 /* try to resolve IPv4 first, try IPv6 if IPv4 fails only */
+#define NETCONN_DNS_IPV6_IPV4 3 /* try to resolve IPv6 first, try IPv4 if IPv6 fails only */
+#endif /* LWIP_DNS */
+
+/* forward-declare some structs to avoid to include their headers */
+struct ip_pcb;
+struct tcp_pcb;
+struct udp_pcb;
+struct raw_pcb;
+struct netconn;
+struct api_msg;
+
+/** A callback prototype to inform about events for a netconn */
+typedef void (* netconn_callback)(struct netconn *, enum netconn_evt, u16_t len);
+
+/** A netconn descriptor */
+struct netconn {
+  /** type of the netconn (TCP, UDP or RAW) */
+  enum netconn_type type;
+  /** current state of the netconn */
+  enum netconn_state state;
+  /** the lwIP internal protocol control block */
+  union {
+    struct ip_pcb  *ip;
+    struct tcp_pcb *tcp;
+    struct udp_pcb *udp;
+    struct raw_pcb *raw;
+  } pcb;
+  /** the last asynchronous unreported error this netconn had */
+  err_t pending_err;
+#if !LWIP_NETCONN_SEM_PER_THREAD
+  /** sem that is used to synchronously execute functions in the core context */
+  sys_sem_t op_completed;
+#endif
+  /** mbox where received packets are stored until they are fetched
+      by the netconn application thread (can grow quite big) */
+  sys_mbox_t recvmbox;
+#if LWIP_TCP
+  /** mbox where new connections are stored until processed
+      by the application thread */
+  sys_mbox_t acceptmbox;
+#endif /* LWIP_TCP */
+#if LWIP_NETCONN_FULLDUPLEX
+  /** number of threads waiting on an mbox. This is required to unblock
+      all threads when closing while threads are waiting. */
+  int mbox_threads_waiting;
+#endif
+  union {
+    int socket;
+    void *ptr;
+  } callback_arg;
+#if LWIP_SO_SNDTIMEO
+  /** timeout to wait for sending data (which means enqueueing data for sending
+      in internal buffers) in milliseconds */
+  s32_t send_timeout;
+#endif /* LWIP_SO_RCVTIMEO */
+#if LWIP_SO_RCVTIMEO
+  /** timeout in milliseconds to wait for new data to be received
+      (or connections to arrive for listening netconns) */
+  u32_t recv_timeout;
+#endif /* LWIP_SO_RCVTIMEO */
+#if LWIP_SO_RCVBUF
+  /** maximum amount of bytes queued in recvmbox
+      not used for TCP: adjust TCP_WND instead! */
+  int recv_bufsize;
+  /** number of bytes currently in recvmbox to be received,
+      tested against recv_bufsize to limit bytes on recvmbox
+      for UDP and RAW, used for FIONREAD */
+  int recv_avail;
+#endif /* LWIP_SO_RCVBUF */
+#if LWIP_SO_LINGER
+   /** values <0 mean linger is disabled, values > 0 are seconds to linger */
+  s16_t linger;
+#endif /* LWIP_SO_LINGER */
+  /** flags holding more netconn-internal state, see NETCONN_FLAG_* defines */
+  u8_t flags;
+#if LWIP_TCP
+  /** TCP: when data passed to netconn_write doesn't fit into the send buffer,
+      this temporarily stores the message.
+      Also used during connect and close. */
+  struct api_msg *current_msg;
+#endif /* LWIP_TCP */
+  /** A callback function that is informed about events for this netconn */
+  netconn_callback callback;
+};
+
+/** This vector type is passed to @ref netconn_write_vectors_partly to send
+ * multiple buffers at once.
+ * ATTENTION: This type has to directly map struct iovec since one is casted
+ *            into the other!
+ */
+struct netvector {
+  /** pointer to the application buffer that contains the data to send */
+  const void *ptr;
+  /** size of the application data to send */
+  size_t len;
+};
+
+/** Register an Network connection event */
+#define API_EVENT(c,e,l) if (c->callback) {         \
+                           (*c->callback)(c, e, l); \
+                         }
+
+/* Network connection functions: */
+
+/** @ingroup netconn_common
+ * Create new netconn connection
+ * @param t @ref netconn_type */
+#define netconn_new(t)                  netconn_new_with_proto_and_callback(t, 0, NULL)
+#define netconn_new_with_callback(t, c) netconn_new_with_proto_and_callback(t, 0, c)
+struct netconn *netconn_new_with_proto_and_callback(enum netconn_type t, u8_t proto,
+                                             netconn_callback callback);
+err_t   netconn_prepare_delete(struct netconn *conn);
+err_t   netconn_delete(struct netconn *conn);
+/** Get the type of a netconn (as enum netconn_type). */
+#define netconn_type(conn) (conn->type)
+
+err_t   netconn_getaddr(struct netconn *conn, ip_addr_t *addr,
+                        u16_t *port, u8_t local);
+/** @ingroup netconn_common */
+#define netconn_peer(c,i,p) netconn_getaddr(c,i,p,0)
+/** @ingroup netconn_common */
+#define netconn_addr(c,i,p) netconn_getaddr(c,i,p,1)
+
+err_t   netconn_bind(struct netconn *conn, const ip_addr_t *addr, u16_t port);
+err_t   netconn_bind_if(struct netconn *conn, u8_t if_idx);
+err_t   netconn_connect(struct netconn *conn, const ip_addr_t *addr, u16_t port);
+err_t   netconn_disconnect (struct netconn *conn);
+err_t   netconn_listen_with_backlog(struct netconn *conn, u8_t backlog);
+/** @ingroup netconn_tcp */
+#define netconn_listen(conn) netconn_listen_with_backlog(conn, TCP_DEFAULT_LISTEN_BACKLOG)
+err_t   netconn_accept(struct netconn *conn, struct netconn **new_conn);
+err_t   netconn_recv(struct netconn *conn, struct netbuf **new_buf);
+err_t   netconn_recv_udp_raw_netbuf(struct netconn *conn, struct netbuf **new_buf);
+err_t   netconn_recv_udp_raw_netbuf_flags(struct netconn *conn, struct netbuf **new_buf, u8_t apiflags);
+err_t   netconn_recv_tcp_pbuf(struct netconn *conn, struct pbuf **new_buf);
+err_t   netconn_recv_tcp_pbuf_flags(struct netconn *conn, struct pbuf **new_buf, u8_t apiflags);
+err_t   netconn_tcp_recvd(struct netconn *conn, size_t len);
+err_t   netconn_sendto(struct netconn *conn, struct netbuf *buf,
+                             const ip_addr_t *addr, u16_t port);
+err_t   netconn_send(struct netconn *conn, struct netbuf *buf);
+err_t   netconn_write_partly(struct netconn *conn, const void *dataptr, size_t size,
+                             u8_t apiflags, size_t *bytes_written);
+err_t   netconn_write_vectors_partly(struct netconn *conn, struct netvector *vectors, u16_t vectorcnt,
+                                     u8_t apiflags, size_t *bytes_written);
+/** @ingroup netconn_tcp */
+#define netconn_write(conn, dataptr, size, apiflags) \
+          netconn_write_partly(conn, dataptr, size, apiflags, NULL)
+err_t   netconn_close(struct netconn *conn);
+err_t   netconn_shutdown(struct netconn *conn, u8_t shut_rx, u8_t shut_tx);
+
+#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD)
+err_t   netconn_join_leave_group(struct netconn *conn, const ip_addr_t *multiaddr,
+                             const ip_addr_t *netif_addr, enum netconn_igmp join_or_leave);
+err_t   netconn_join_leave_group_netif(struct netconn *conn, const ip_addr_t *multiaddr,
+                             u8_t if_idx, enum netconn_igmp join_or_leave);
+#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */
+#if LWIP_DNS
+#if LWIP_IPV4 && LWIP_IPV6
+err_t   netconn_gethostbyname_addrtype(const char *name, ip_addr_t *addr, u8_t dns_addrtype);
+#define netconn_gethostbyname(name, addr) netconn_gethostbyname_addrtype(name, addr, NETCONN_DNS_DEFAULT)
+#else /* LWIP_IPV4 && LWIP_IPV6 */
+err_t   netconn_gethostbyname(const char *name, ip_addr_t *addr);
+#define netconn_gethostbyname_addrtype(name, addr, dns_addrtype) netconn_gethostbyname(name, addr)
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+#endif /* LWIP_DNS */
+
+err_t   netconn_err(struct netconn *conn);
+#define netconn_recv_bufsize(conn)      ((conn)->recv_bufsize)
+
+#define netconn_set_flags(conn, set_flags)     do { (conn)->flags = (u8_t)((conn)->flags |  (set_flags)); } while(0)
+#define netconn_clear_flags(conn, clr_flags)   do { (conn)->flags = (u8_t)((conn)->flags & (u8_t)(~(clr_flags) & 0xff)); } while(0)
+#define netconn_is_flag_set(conn, flag)        (((conn)->flags & (flag)) != 0)
+
+#define netconn_set_callback_arg(conn, arg)   do { (conn)->callback_arg.ptr = (arg); } while(0)
+#define netconn_get_callback_arg(conn)        ((conn)->callback_arg.ptr)
+
+/** Set the blocking status of netconn calls (@todo: write/send is missing) */
+#define netconn_set_nonblocking(conn, val)  do { if(val) { \
+  netconn_set_flags(conn, NETCONN_FLAG_NON_BLOCKING); \
+} else { \
+  netconn_clear_flags(conn, NETCONN_FLAG_NON_BLOCKING); }} while(0)
+/** Get the blocking status of netconn calls (@todo: write/send is missing) */
+#define netconn_is_nonblocking(conn)        (((conn)->flags & NETCONN_FLAG_NON_BLOCKING) != 0)
+
+#if LWIP_IPV6
+/** @ingroup netconn_common
+ * TCP: Set the IPv6 ONLY status of netconn calls (see NETCONN_FLAG_IPV6_V6ONLY)
+ */
+#define netconn_set_ipv6only(conn, val)  do { if(val) { \
+  netconn_set_flags(conn, NETCONN_FLAG_IPV6_V6ONLY); \
+} else { \
+  netconn_clear_flags(conn, NETCONN_FLAG_IPV6_V6ONLY); }} while(0)
+/** @ingroup netconn_common
+ * TCP: Get the IPv6 ONLY status of netconn calls (see NETCONN_FLAG_IPV6_V6ONLY)
+ */
+#define netconn_get_ipv6only(conn)        (((conn)->flags & NETCONN_FLAG_IPV6_V6ONLY) != 0)
+#endif /* LWIP_IPV6 */
+
+#if LWIP_SO_SNDTIMEO
+/** Set the send timeout in milliseconds */
+#define netconn_set_sendtimeout(conn, timeout)      ((conn)->send_timeout = (timeout))
+/** Get the send timeout in milliseconds */
+#define netconn_get_sendtimeout(conn)               ((conn)->send_timeout)
+#endif /* LWIP_SO_SNDTIMEO */
+#if LWIP_SO_RCVTIMEO
+/** Set the receive timeout in milliseconds */
+#define netconn_set_recvtimeout(conn, timeout)      ((conn)->recv_timeout = (timeout))
+/** Get the receive timeout in milliseconds */
+#define netconn_get_recvtimeout(conn)               ((conn)->recv_timeout)
+#endif /* LWIP_SO_RCVTIMEO */
+#if LWIP_SO_RCVBUF
+/** Set the receive buffer in bytes */
+#define netconn_set_recvbufsize(conn, recvbufsize)  ((conn)->recv_bufsize = (recvbufsize))
+/** Get the receive buffer in bytes */
+#define netconn_get_recvbufsize(conn)               ((conn)->recv_bufsize)
+#endif /* LWIP_SO_RCVBUF*/
+
+#if LWIP_NETCONN_SEM_PER_THREAD
+void netconn_thread_init(void);
+void netconn_thread_cleanup(void);
+#else /* LWIP_NETCONN_SEM_PER_THREAD */
+#define netconn_thread_init()
+#define netconn_thread_cleanup()
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_NETCONN || LWIP_SOCKET */
+
+#endif /* LWIP_HDR_API_H */
diff --git a/lib/lwip/include/lwip/apps/FILES b/lib/lwip/include/lwip/apps/FILES
new file mode 100644
index 0000000000..adfc0f3345
--- /dev/null
+++ b/lib/lwip/include/lwip/apps/FILES
@@ -0,0 +1,2 @@
+This directory contains application headers.
+Every application shall provide one api file APP.h and optionally one options file APP_opts.h
diff --git a/lib/lwip/include/lwip/apps/altcp_proxyconnect.h b/lib/lwip/include/lwip/apps/altcp_proxyconnect.h
new file mode 100644
index 0000000000..65d31279de
--- /dev/null
+++ b/lib/lwip/include/lwip/apps/altcp_proxyconnect.h
@@ -0,0 +1,79 @@
+/**
+ * @file
+ * Application layered TCP connection API that executes a proxy-connect.
+ *
+ * This file provides a starting layer that executes a proxy-connect e.g. to
+ * set up TLS connections through a http proxy.
+ */
+
+/*
+ * Copyright (c) 2018 Simon Goldschmidt
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt <goldsimon@gmx.de>
+ *
+ */
+
+#ifndef LWIP_HDR_APPS_ALTCP_PROXYCONNECT_H
+#define LWIP_HDR_APPS_ALTCP_PROXYCONNECT_H
+
+#include "lwip/opt.h"
+
+#if LWIP_ALTCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/ip_addr.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct altcp_proxyconnect_config {
+  ip_addr_t proxy_addr;
+  u16_t proxy_port;
+};
+
+
+struct altcp_pcb *altcp_proxyconnect_new(struct altcp_proxyconnect_config *config, struct altcp_pcb *inner_pcb);
+struct altcp_pcb *altcp_proxyconnect_new_tcp(struct altcp_proxyconnect_config *config, u8_t ip_type);
+
+struct altcp_pcb *altcp_proxyconnect_alloc(void *arg, u8_t ip_type);
+
+#if LWIP_ALTCP_TLS
+struct altcp_proxyconnect_tls_config {
+  struct altcp_proxyconnect_config proxy;
+  struct altcp_tls_config *tls_config;
+};
+
+struct altcp_pcb *altcp_proxyconnect_tls_alloc(void *arg, u8_t ip_type);
+#endif /* LWIP_ALTCP_TLS */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_ALTCP */
+#endif /* LWIP_HDR_APPS_ALTCP_PROXYCONNECT_H */
diff --git a/lib/lwip/include/lwip/apps/altcp_tls_mbedtls_opts.h b/lib/lwip/include/lwip/apps/altcp_tls_mbedtls_opts.h
new file mode 100644
index 0000000000..e41301c061
--- /dev/null
+++ b/lib/lwip/include/lwip/apps/altcp_tls_mbedtls_opts.h
@@ -0,0 +1,111 @@
+/**
+ * @file
+ * Application layered TCP/TLS connection API (to be used from TCPIP thread)
+ *
+ * This file contains options for an mbedtls port of the TLS layer.
+ */
+
+/*
+ * Copyright (c) 2017 Simon Goldschmidt
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt <goldsimon@gmx.de>
+ *
+ */
+#ifndef LWIP_HDR_ALTCP_TLS_OPTS_H
+#define LWIP_HDR_ALTCP_TLS_OPTS_H
+
+#include "lwip/opt.h"
+
+#if LWIP_ALTCP /* don't build if not configured for use in lwipopts.h */
+
+/** LWIP_ALTCP_TLS_MBEDTLS==1: use mbedTLS for TLS support for altcp API
+ * mbedtls include directory must be reachable via include search path
+ */
+#ifndef LWIP_ALTCP_TLS_MBEDTLS
+#define LWIP_ALTCP_TLS_MBEDTLS                        0
+#endif
+
+/** Configure debug level of this file */
+#ifndef ALTCP_MBEDTLS_DEBUG
+#define ALTCP_MBEDTLS_DEBUG                           LWIP_DBG_OFF
+#endif
+
+/** Configure lwIP debug level of the mbedTLS library */
+#ifndef ALTCP_MBEDTLS_LIB_DEBUG
+#define ALTCP_MBEDTLS_LIB_DEBUG                       LWIP_DBG_OFF
+#endif
+
+/** Configure minimum internal debug level of the mbedTLS library */
+#ifndef ALTCP_MBEDTLS_LIB_DEBUG_LEVEL_MIN
+#define ALTCP_MBEDTLS_LIB_DEBUG_LEVEL_MIN             0
+#endif
+
+/** Enable the basic session cache
+ * ATTENTION: Using a session cache can lower security by reusing keys!
+ */
+#ifndef ALTCP_MBEDTLS_USE_SESSION_CACHE
+#define ALTCP_MBEDTLS_USE_SESSION_CACHE               0
+#endif
+
+/** Maximum cache size of the basic session cache */
+#ifndef ALTCP_MBEDTLS_SESSION_CACHE_SIZE
+#define ALTCP_MBEDTLS_SESSION_CACHE_SIZE              30
+#endif
+
+/** Set a session timeout in seconds for the basic session cache  */
+#ifndef ALTCP_MBEDTLS_SESSION_CACHE_TIMEOUT_SECONDS
+#define ALTCP_MBEDTLS_SESSION_CACHE_TIMEOUT_SECONDS   (60 * 60)
+#endif
+
+/** Use session tickets to speed up connection setup (needs
+ * MBEDTLS_SSL_SESSION_TICKETS enabled in mbedTLS config).
+ * ATTENTION: Using session tickets can lower security by reusing keys!
+ */
+#ifndef ALTCP_MBEDTLS_USE_SESSION_TICKETS
+#define ALTCP_MBEDTLS_USE_SESSION_TICKETS             0
+#endif
+
+/** Session ticket cipher */
+#ifndef ALTCP_MBEDTLS_SESSION_TICKET_CIPHER
+#define ALTCP_MBEDTLS_SESSION_TICKET_CIPHER           MBEDTLS_CIPHER_AES_256_GCM
+#endif
+
+/** Maximum timeout for session tickets */
+#ifndef ALTCP_MBEDTLS_SESSION_TICKET_TIMEOUT_SECONDS
+#define ALTCP_MBEDTLS_SESSION_TICKET_TIMEOUT_SECONDS  (60 * 60 * 24)
+#endif
+
+/** Certificate verification mode: MBEDTLS_SSL_VERIFY_NONE, MBEDTLS_SSL_VERIFY_OPTIONAL (default),
+ * MBEDTLS_SSL_VERIFY_REQUIRED (recommended)*/
+#ifndef ALTCP_MBEDTLS_AUTHMODE
+#define ALTCP_MBEDTLS_AUTHMODE                        MBEDTLS_SSL_VERIFY_OPTIONAL
+#endif
+
+#endif /* LWIP_ALTCP */
+
+#endif /* LWIP_HDR_ALTCP_TLS_OPTS_H */
diff --git a/lib/lwip/include/lwip/apps/fs.h b/lib/lwip/include/lwip/apps/fs.h
new file mode 100644
index 0000000000..82b219a970
--- /dev/null
+++ b/lib/lwip/include/lwip/apps/fs.h
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef LWIP_HDR_APPS_FS_H
+#define LWIP_HDR_APPS_FS_H
+
+#include "httpd_opts.h"
+#include "lwip/err.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define FS_READ_EOF     -1
+#define FS_READ_DELAYED -2
+
+#if HTTPD_PRECALCULATED_CHECKSUM
+struct fsdata_chksum {
+  u32_t offset;
+  u16_t chksum;
+  u16_t len;
+};
+#endif /* HTTPD_PRECALCULATED_CHECKSUM */
+
+#define FS_FILE_FLAGS_HEADER_INCLUDED     0x01
+#define FS_FILE_FLAGS_HEADER_PERSISTENT   0x02
+#define FS_FILE_FLAGS_HEADER_HTTPVER_1_1  0x04
+#define FS_FILE_FLAGS_SSI                 0x08
+#define FS_FILE_FLAGS_CUSTOM              0x10
+
+/** Define FS_FILE_EXTENSION_T_DEFINED if you have typedef'ed to your private
+ * pointer type (defaults to 'void' so the default usage is 'void*')
+ */
+#ifndef FS_FILE_EXTENSION_T_DEFINED
+typedef void fs_file_extension;
+#endif
+
+struct fs_file {
+  const char *data;
+  int len;
+  int index;
+#if LWIP_HTTPD_FILE_EXTENSION
+  /* pextension is free for implementations to hold private (extensional)
+     arbitrary data, e.g. holding some file state or file system handle */
+  fs_file_extension *pextension;
+#endif /* LWIP_HTTPD_FILE_EXTENSION */
+#if HTTPD_PRECALCULATED_CHECKSUM
+  const struct fsdata_chksum *chksum;
+  u16_t chksum_count;
+#endif /* HTTPD_PRECALCULATED_CHECKSUM */
+  u8_t flags;
+#if LWIP_HTTPD_FILE_STATE
+  void *state;
+#endif /* LWIP_HTTPD_FILE_STATE */
+};
+
+#if LWIP_HTTPD_FS_ASYNC_READ
+typedef void (*fs_wait_cb)(void *arg);
+#endif /* LWIP_HTTPD_FS_ASYNC_READ */
+
+err_t fs_open(struct fs_file *file, const char *name);
+void fs_close(struct fs_file *file);
+#if LWIP_HTTPD_DYNAMIC_FILE_READ
+#if LWIP_HTTPD_FS_ASYNC_READ
+int fs_read_async(struct fs_file *file, char *buffer, int count, fs_wait_cb callback_fn, void *callback_arg);
+#else /* LWIP_HTTPD_FS_ASYNC_READ */
+int fs_read(struct fs_file *file, char *buffer, int count);
+#endif /* LWIP_HTTPD_FS_ASYNC_READ */
+#endif /* LWIP_HTTPD_DYNAMIC_FILE_READ */
+#if LWIP_HTTPD_FS_ASYNC_READ
+int fs_is_file_ready(struct fs_file *file, fs_wait_cb callback_fn, void *callback_arg);
+#endif /* LWIP_HTTPD_FS_ASYNC_READ */
+int fs_bytes_left(struct fs_file *file);
+
+#if LWIP_HTTPD_FILE_STATE
+/** This user-defined function is called when a file is opened. */
+void *fs_state_init(struct fs_file *file, const char *name);
+/** This user-defined function is called when a file is closed. */
+void fs_state_free(struct fs_file *file, void *state);
+#endif /* #if LWIP_HTTPD_FILE_STATE */
+
+struct fsdata_file {
+  const struct fsdata_file *next;
+  const unsigned char *name;
+  const unsigned char *data;
+  int len;
+  u8_t flags;
+#if HTTPD_PRECALCULATED_CHECKSUM
+  u16_t chksum_count;
+  const struct fsdata_chksum *chksum;
+#endif /* HTTPD_PRECALCULATED_CHECKSUM */
+};
+
+#if LWIP_HTTPD_CUSTOM_FILES
+/* Prototypes required to implement custom files as fs addon */
+int fs_open_custom(struct fs_file *file, const char *name);
+void fs_close_custom(struct fs_file *file);
+#if LWIP_HTTPD_FS_ASYNC_READ
+u8_t fs_canread_custom(struct fs_file *file);
+u8_t fs_wait_read_custom(struct fs_file *file, fs_wait_cb callback_fn, void *callback_arg);
+int fs_read_async_custom(struct fs_file *file, char *buffer, int count, fs_wait_cb callback_fn, void *callback_arg);
+#else /* LWIP_HTTPD_FS_ASYNC_READ */
+int fs_read_custom(struct fs_file *file, char *buffer, int count);
+#endif /* LWIP_HTTPD_FS_ASYNC_READ */
+#endif /* LWIP_HTTPD_CUSTOM_FILES */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_APPS_FS_H */
diff --git a/lib/lwip/include/lwip/apps/http_client.h b/lib/lwip/include/lwip/apps/http_client.h
new file mode 100644
index 0000000000..d39e1bf829
--- /dev/null
+++ b/lib/lwip/include/lwip/apps/http_client.h
@@ -0,0 +1,160 @@
+/**
+ * @file
+ * HTTP client
+ */
+
+/*
+ * Copyright (c) 2018 Simon Goldschmidt <goldsimon@gmx.de>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt <goldsimon@gmx.de>
+ *
+ */
+
+#ifndef LWIP_HDR_APPS_HTTP_CLIENT_H
+#define LWIP_HDR_APPS_HTTP_CLIENT_H
+
+#include "lwip/opt.h"
+#include "lwip/ip_addr.h"
+#include "lwip/err.h"
+#include "lwip/altcp.h"
+#include "lwip/prot/iana.h"
+#include "lwip/pbuf.h"
+
+#if LWIP_TCP && LWIP_CALLBACK_API
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @ingroup httpc
+ * HTTPC_HAVE_FILE_IO: define this to 1 to have functions dowloading directly
+ * to disk via fopen/fwrite.
+ * These functions are example implementations of the interface only.
+ */
+#ifndef LWIP_HTTPC_HAVE_FILE_IO
+#define LWIP_HTTPC_HAVE_FILE_IO   0
+#endif
+
+/**
+ * @ingroup httpc
+ * The default TCP port used for HTTP
+ */
+#define HTTP_DEFAULT_PORT         LWIP_IANA_PORT_HTTP
+
+/**
+ * @ingroup httpc
+ * HTTP client result codes
+ */
+typedef enum ehttpc_result {
+  /** File successfully received */
+  HTTPC_RESULT_OK            = 0,
+  /** Unknown error */
+  HTTPC_RESULT_ERR_UNKNOWN   = 1,
+  /** Connection to server failed */
+  HTTPC_RESULT_ERR_CONNECT   = 2,
+  /** Failed to resolve server hostname */
+  HTTPC_RESULT_ERR_HOSTNAME  = 3,
+  /** Connection unexpectedly closed by remote server */
+  HTTPC_RESULT_ERR_CLOSED    = 4,
+  /** Connection timed out (server didn't respond in time) */
+  HTTPC_RESULT_ERR_TIMEOUT   = 5,
+  /** Server responded with an error code */
+  HTTPC_RESULT_ERR_SVR_RESP  = 6,
+  /** Local memory error */
+  HTTPC_RESULT_ERR_MEM       = 7,
+  /** Local abort */
+  HTTPC_RESULT_LOCAL_ABORT   = 8,
+  /** Content length mismatch */
+  HTTPC_RESULT_ERR_CONTENT_LEN = 9
+} httpc_result_t;
+
+typedef struct _httpc_state httpc_state_t;
+
+/**
+ * @ingroup httpc
+ * Prototype of a http client callback function
+ *
+ * @param arg argument specified when initiating the request
+ * @param httpc_result result of the http transfer (see enum httpc_result_t)
+ * @param rx_content_len number of bytes received (without headers)
+ * @param srv_res this contains the http status code received (if any)
+ * @param err an error returned by internal lwip functions, can help to specify
+ *            the source of the error but must not necessarily be != ERR_OK
+ */
+typedef void (*httpc_result_fn)(void *arg, httpc_result_t httpc_result, u32_t rx_content_len, u32_t srv_res, err_t err);
+
+/**
+ * @ingroup httpc
+ * Prototype of http client callback: called when the headers are received
+ *
+ * @param connection http client connection
+ * @param arg argument specified when initiating the request
+ * @param hdr header pbuf(s) (may contain data also)
+ * @param hdr_len length of the heders in 'hdr'
+ * @param content_len content length as received in the headers (-1 if not received)
+ * @return if != ERR_OK is returned, the connection is aborted
+ */
+typedef err_t (*httpc_headers_done_fn)(httpc_state_t *connection, void *arg, struct pbuf *hdr, u16_t hdr_len, u32_t content_len);
+
+typedef struct _httpc_connection {
+  ip_addr_t proxy_addr;
+  u16_t proxy_port;
+  u8_t use_proxy;
+  /* @todo: add username:pass? */
+
+#if LWIP_ALTCP
+  altcp_allocator_t *altcp_allocator;
+#endif
+
+  /* this callback is called when the transfer is finished (or aborted) */
+  httpc_result_fn result_fn;
+  /* this callback is called after receiving the http headers
+     It can abort the connection by returning != ERR_OK */
+  httpc_headers_done_fn headers_done_fn;
+} httpc_connection_t;
+
+err_t httpc_get_file(const ip_addr_t* server_addr, u16_t port, const char* uri, const httpc_connection_t *settings,
+                     altcp_recv_fn recv_fn, void* callback_arg, httpc_state_t **connection);
+err_t httpc_get_file_dns(const char* server_name, u16_t port, const char* uri, const httpc_connection_t *settings,
+                     altcp_recv_fn recv_fn, void* callback_arg, httpc_state_t **connection);
+
+#if LWIP_HTTPC_HAVE_FILE_IO
+err_t httpc_get_file_to_disk(const ip_addr_t* server_addr, u16_t port, const char* uri, const httpc_connection_t *settings,
+                     void* callback_arg, const char* local_file_name, httpc_state_t **connection);
+err_t httpc_get_file_dns_to_disk(const char* server_name, u16_t port, const char* uri, const httpc_connection_t *settings,
+                     void* callback_arg, const char* local_file_name, httpc_state_t **connection);
+#endif /* LWIP_HTTPC_HAVE_FILE_IO */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_TCP && LWIP_CALLBACK_API */
+
+#endif /* LWIP_HDR_APPS_HTTP_CLIENT_H */
diff --git a/lib/lwip/include/lwip/apps/httpd.h b/lib/lwip/include/lwip/apps/httpd.h
new file mode 100644
index 0000000000..1ecdd743c2
--- /dev/null
+++ b/lib/lwip/include/lwip/apps/httpd.h
@@ -0,0 +1,256 @@
+/**
+ * @file
+ * HTTP server
+ */
+
+/*
+ * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ * This version of the file has been modified by Texas Instruments to offer
+ * simple server-side-include (SSI) and Common Gateway Interface (CGI)
+ * capability.
+ */
+
+#ifndef LWIP_HDR_APPS_HTTPD_H
+#define LWIP_HDR_APPS_HTTPD_H
+
+#include "httpd_opts.h"
+#include "lwip/err.h"
+#include "lwip/pbuf.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if LWIP_HTTPD_CGI
+
+/**
+ * @ingroup httpd
+ * Function pointer for a CGI script handler.
+ *
+ * This function is called each time the HTTPD server is asked for a file
+ * whose name was previously registered as a CGI function using a call to
+ * http_set_cgi_handlers. The iIndex parameter provides the index of the
+ * CGI within the cgis array passed to http_set_cgi_handlers. Parameters
+ * pcParam and pcValue provide access to the parameters provided along with
+ * the URI. iNumParams provides a count of the entries in the pcParam and
+ * pcValue arrays. Each entry in the pcParam array contains the name of a
+ * parameter with the corresponding entry in the pcValue array containing the
+ * value for that parameter. Note that pcParam may contain multiple elements
+ * with the same name if, for example, a multi-selection list control is used
+ * in the form generating the data.
+ *
+ * The function should return a pointer to a character string which is the
+ * path and filename of the response that is to be sent to the connected
+ * browser, for example "/thanks.htm" or "/response/error.ssi".
+ *
+ * The maximum number of parameters that will be passed to this function via
+ * iNumParams is defined by LWIP_HTTPD_MAX_CGI_PARAMETERS. Any parameters in
+ * the incoming HTTP request above this number will be discarded.
+ *
+ * Requests intended for use by this CGI mechanism must be sent using the GET
+ * method (which encodes all parameters within the URI rather than in a block
+ * later in the request). Attempts to use the POST method will result in the
+ * request being ignored.
+ *
+ */
+typedef const char *(*tCGIHandler)(int iIndex, int iNumParams, char *pcParam[],
+                             char *pcValue[]);
+
+/**
+ * @ingroup httpd
+ * Structure defining the base filename (URL) of a CGI and the associated
+ * function which is to be called when that URL is requested.
+ */
+typedef struct
+{
+    const char *pcCGIName;
+    tCGIHandler pfnCGIHandler;
+} tCGI;
+
+/** Set the array of cgi handlers. */
+void http_set_cgi_handlers(const tCGI *pCGIs, int iNumHandlers);
+
+#endif /* LWIP_HTTPD_CGI */
+
+#if LWIP_HTTPD_CGI || LWIP_HTTPD_CGI_SSI
+
+#if LWIP_HTTPD_CGI_SSI
+/* we have to prototype this struct here to make it available for the handler */
+struct fs_file;
+
+/** Define this generic CGI handler in your application.
+ * It is called once for every URI with parameters.
+ * The parameters can be stored to the object passed as connection_state, which
+ * is allocated to file->state via fs_state_init() from fs_open() or fs_open_custom().
+ * Content creation via SSI or complete dynamic files can retrieve the CGI params from there.
+ */
+extern void httpd_cgi_handler(struct fs_file *file, const char* uri, int iNumParams,
+                              char **pcParam, char **pcValue
+#if defined(LWIP_HTTPD_FILE_STATE) && LWIP_HTTPD_FILE_STATE
+                                     , void *connection_state
+#endif /* LWIP_HTTPD_FILE_STATE */
+                                     );
+#endif /* LWIP_HTTPD_CGI_SSI */
+
+#endif /* LWIP_HTTPD_CGI || LWIP_HTTPD_CGI_SSI */
+
+#if LWIP_HTTPD_SSI
+
+/**
+ * @ingroup httpd
+ * Function pointer for the SSI tag handler callback.
+ *
+ * This function will be called each time the HTTPD server detects a tag of the
+ * form <!--#name--> in files with extensions mentioned in the g_pcSSIExtensions
+ * array (currently .shtml, .shtm, .ssi, .xml, .json) where "name" appears as
+ * one of the tags supplied to http_set_ssi_handler in the tags array.  The
+ * returned insert string, which will be appended after the the string
+ * "<!--#name-->" in file sent back to the client, should be written to pointer
+ * pcInsert. iInsertLen contains the size of the buffer pointed to by
+ * pcInsert. The iIndex parameter provides the zero-based index of the tag as
+ * found in the tags array and identifies the tag that is to be processed.
+ *
+ * The handler returns the number of characters written to pcInsert excluding
+ * any terminating NULL or HTTPD_SSI_TAG_UNKNOWN when tag is not recognized.
+ *
+ * Note that the behavior of this SSI mechanism is somewhat different from the
+ * "normal" SSI processing as found in, for example, the Apache web server.  In
+ * this case, the inserted text is appended following the SSI tag rather than
+ * replacing the tag entirely.  This allows for an implementation that does not
+ * require significant additional buffering of output data yet which will still
+ * offer usable SSI functionality. One downside to this approach is when
+ * attempting to use SSI within JavaScript.  The SSI tag is structured to
+ * resemble an HTML comment but this syntax does not constitute a comment
+ * within JavaScript and, hence, leaving the tag in place will result in
+ * problems in these cases. In order to avoid these problems, define
+ * LWIP_HTTPD_SSI_INCLUDE_TAG as zero in your lwip options file, or use JavaScript
+ * style block comments in the form / * # name * / (without the spaces).
+ */
+typedef u16_t (*tSSIHandler)(
+#if LWIP_HTTPD_SSI_RAW
+                             const char* ssi_tag_name,
+#else /* LWIP_HTTPD_SSI_RAW */
+                             int iIndex,
+#endif /* LWIP_HTTPD_SSI_RAW */
+                             char *pcInsert, int iInsertLen
+#if LWIP_HTTPD_SSI_MULTIPART
+                             , u16_t current_tag_part, u16_t *next_tag_part
+#endif /* LWIP_HTTPD_SSI_MULTIPART */
+#if defined(LWIP_HTTPD_FILE_STATE) && LWIP_HTTPD_FILE_STATE
+                             , void *connection_state
+#endif /* LWIP_HTTPD_FILE_STATE */
+                             );
+
+/** Set the SSI handler function
+ * (if LWIP_HTTPD_SSI_RAW==1, only the first argument is used)
+ */
+void http_set_ssi_handler(tSSIHandler pfnSSIHandler,
+                          const char **ppcTags, int iNumTags);
+
+/** For LWIP_HTTPD_SSI_RAW==1, return this to indicate the tag is unknown.
+ * In this case, the webserver writes a warning into the page.
+ * You can also just return 0 to write nothing for unknown tags.
+ */
+#define HTTPD_SSI_TAG_UNKNOWN 0xFFFF
+
+#endif /* LWIP_HTTPD_SSI */
+
+#if LWIP_HTTPD_SUPPORT_POST
+
+/* These functions must be implemented by the application */
+
+/**
+ * @ingroup httpd
+ * Called when a POST request has been received. The application can decide
+ * whether to accept it or not.
+ *
+ * @param connection Unique connection identifier, valid until httpd_post_end
+ *        is called.
+ * @param uri The HTTP header URI receiving the POST request.
+ * @param http_request The raw HTTP request (the first packet, normally).
+ * @param http_request_len Size of 'http_request'.
+ * @param content_len Content-Length from HTTP header.
+ * @param response_uri Filename of response file, to be filled when denying the
+ *        request
+ * @param response_uri_len Size of the 'response_uri' buffer.
+ * @param post_auto_wnd Set this to 0 to let the callback code handle window
+ *        updates by calling 'httpd_post_data_recved' (to throttle rx speed)
+ *        default is 1 (httpd handles window updates automatically)
+ * @return ERR_OK: Accept the POST request, data may be passed in
+ *         another err_t: Deny the POST request, send back 'bad request'.
+ */
+err_t httpd_post_begin(void *connection, const char *uri, const char *http_request,
+                       u16_t http_request_len, int content_len, char *response_uri,
+                       u16_t response_uri_len, u8_t *post_auto_wnd);
+
+/**
+ * @ingroup httpd
+ * Called for each pbuf of data that has been received for a POST.
+ * ATTENTION: The application is responsible for freeing the pbufs passed in!
+ *
+ * @param connection Unique connection identifier.
+ * @param p Received data.
+ * @return ERR_OK: Data accepted.
+ *         another err_t: Data denied, http_post_get_response_uri will be called.
+ */
+err_t httpd_post_receive_data(void *connection, struct pbuf *p);
+
+/**
+ * @ingroup httpd
+ * Called when all data is received or when the connection is closed.
+ * The application must return the filename/URI of a file to send in response
+ * to this POST request. If the response_uri buffer is untouched, a 404
+ * response is returned.
+ *
+ * @param connection Unique connection identifier.
+ * @param response_uri Filename of response file, to be filled when denying the request
+ * @param response_uri_len Size of the 'response_uri' buffer.
+ */
+void httpd_post_finished(void *connection, char *response_uri, u16_t response_uri_len);
+
+#if LWIP_HTTPD_POST_MANUAL_WND
+void httpd_post_data_recved(void *connection, u16_t recved_len);
+#endif /* LWIP_HTTPD_POST_MANUAL_WND */
+
+#endif /* LWIP_HTTPD_SUPPORT_POST */
+
+void httpd_init(void);
+
+#if HTTPD_ENABLE_HTTPS
+struct altcp_tls_config;
+void httpd_inits(struct altcp_tls_config *conf);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_APPS_HTTPD_H */
diff --git a/lib/lwip/include/lwip/apps/httpd_opts.h b/lib/lwip/include/lwip/apps/httpd_opts.h
new file mode 100644
index 0000000000..2b66e67112
--- /dev/null
+++ b/lib/lwip/include/lwip/apps/httpd_opts.h
@@ -0,0 +1,416 @@
+/**
+ * @file
+ * HTTP server options list
+ */
+
+/*
+ * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ * This version of the file has been modified by Texas Instruments to offer
+ * simple server-side-include (SSI) and Common Gateway Interface (CGI)
+ * capability.
+ */
+
+#ifndef LWIP_HDR_APPS_HTTPD_OPTS_H
+#define LWIP_HDR_APPS_HTTPD_OPTS_H
+
+#include "lwip/opt.h"
+#include "lwip/prot/iana.h"
+
+/**
+ * @defgroup httpd_opts Options
+ * @ingroup httpd
+ * @{
+ */
+
+/** Set this to 1 to support CGI (old style).
+ *
+ * This old style CGI support works by registering an array of URLs and
+ * associated CGI handler functions (@ref http_set_cgi_handlers).
+ * This list is scanned just before fs_open is called from request handling.
+ * The handler can return a new URL that is used internally by the httpd to
+ * load the returned page (passed to fs_open).
+ *
+ * Use this CGI type e.g. to execute specific actions and return a page that
+ * does not depend on the CGI parameters.
+ */
+#if !defined LWIP_HTTPD_CGI || defined __DOXYGEN__
+#define LWIP_HTTPD_CGI            0
+#endif
+
+/** Set this to 1 to support CGI (new style).
+ *
+ * This new style CGI support works by calling a global function
+ * (@ref tCGIHandler) for all URLs that are found. fs_open is called first
+ * and the URL can not be written by the CGI handler. Instead, this handler gets
+ * passed the http file state, an object where it can store information derived
+ * from the CGI URL or parameters. This file state is later passed to SSI, so
+ * the SSI code can return data depending on CGI input.
+ *
+ * Use this CGI handler if you want CGI information passed on to SSI.
+ */
+#if !defined LWIP_HTTPD_CGI_SSI || defined __DOXYGEN__
+#define LWIP_HTTPD_CGI_SSI        0
+#endif
+
+/** Set this to 1 to support SSI (Server-Side-Includes)
+ *
+ * In contrast to other http servers, this only calls a preregistered callback
+ * function (@see http_set_ssi_handler) for each tag (in the format of
+ * <!--#tag-->) encountered in SSI-enabled pages.
+ * SSI-enabled pages must have one of the predefined SSI-enabled file extensions.
+ * All files with one of these extensions are parsed when sent.
+ *
+ * A downside of the current SSI implementation is that persistent connections
+ * don't work, as the file length is not known in advance (and httpd currently
+ * relies on the Content-Length header for persistent connections).
+ *
+ * To save memory, the maximum tag length is limited (@see LWIP_HTTPD_MAX_TAG_NAME_LEN).
+ * To save memory, the maximum insertion string length is limited (@see
+ * LWIP_HTTPD_MAX_TAG_INSERT_LEN). If this is not enough, @ref LWIP_HTTPD_SSI_MULTIPART
+ * can be used.
+ */
+#if !defined LWIP_HTTPD_SSI || defined __DOXYGEN__
+#define LWIP_HTTPD_SSI            0
+#endif
+
+/** Set this to 1 to implement an SSI tag handler callback that gets a const char*
+ * to the tag (instead of an index into a pre-registered array of known tags)
+ * If this is 0, the SSI handler callback function is only called pre-registered tags.
+ */
+#if !defined LWIP_HTTPD_SSI_RAW || defined __DOXYGEN__
+#define LWIP_HTTPD_SSI_RAW        0
+#endif
+
+/** Set this to 0 to prevent parsing the file extension at runtime to decide
+ * if a file should be scanned for SSI tags or not.
+ * Default is 1 (file extensions are checked using the g_pcSSIExtensions array)
+ * Set to 2 to override this runtime test function. In this case, you have to
+ * provide an external function that does the check:
+ *   u8_t http_uri_is_ssi(struct fs_file *file, const char *uri)
+ *
+ * This is enabled by default, but if you only use a newer version of makefsdata
+ * supporting the "-ssi" option, this info is already present in
+ */
+#if !defined LWIP_HTTPD_SSI_BY_FILE_EXTENSION || defined __DOXYGEN__
+#define LWIP_HTTPD_SSI_BY_FILE_EXTENSION  1
+#endif
+
+/** This is a list of file extensions handled as SSI files. This define
+ * is used to initialize a 'const char *const[]'. It is only used if
+ * LWIP_HTTPD_SSI_BY_FILE_EXTENSION != 0.
+ */
+#if !defined LWIP_HTTPD_SSI_EXTENSIONS || defined __DOXYGEN__
+#define LWIP_HTTPD_SSI_EXTENSIONS ".shtml", ".shtm", ".ssi", ".xml", ".json"
+#endif
+
+/** Set this to 1 to support HTTP POST */
+#if !defined LWIP_HTTPD_SUPPORT_POST || defined __DOXYGEN__
+#define LWIP_HTTPD_SUPPORT_POST   0
+#endif
+
+/* The maximum number of parameters that the CGI handler can be sent. */
+#if !defined LWIP_HTTPD_MAX_CGI_PARAMETERS || defined __DOXYGEN__
+#define LWIP_HTTPD_MAX_CGI_PARAMETERS 16
+#endif
+
+/** LWIP_HTTPD_SSI_MULTIPART==1: SSI handler function is called with 2 more
+ * arguments indicating a counter for insert string that are too long to be
+ * inserted at once: the SSI handler function must then set 'next_tag_part'
+ * which will be passed back to it in the next call. */
+#if !defined LWIP_HTTPD_SSI_MULTIPART || defined __DOXYGEN__
+#define LWIP_HTTPD_SSI_MULTIPART    0
+#endif
+
+/* The maximum length of the string comprising the SSI tag name
+ * ATTENTION: tags longer than this are ignored, not truncated!
+ */
+#if !defined LWIP_HTTPD_MAX_TAG_NAME_LEN || defined __DOXYGEN__
+#define LWIP_HTTPD_MAX_TAG_NAME_LEN 8
+#endif
+
+/* The maximum length of string that can be returned to replace any given tag
+ * If this buffer is not long enough, use LWIP_HTTPD_SSI_MULTIPART.
+ */
+#if !defined LWIP_HTTPD_MAX_TAG_INSERT_LEN || defined __DOXYGEN__
+#define LWIP_HTTPD_MAX_TAG_INSERT_LEN 192
+#endif
+
+#if !defined LWIP_HTTPD_POST_MANUAL_WND || defined __DOXYGEN__
+#define LWIP_HTTPD_POST_MANUAL_WND  0
+#endif
+
+/** This string is passed in the HTTP header as "Server: " */
+#if !defined HTTPD_SERVER_AGENT || defined __DOXYGEN__
+#define HTTPD_SERVER_AGENT "lwIP/" LWIP_VERSION_STRING " (http://savannah.nongnu.org/projects/lwip)"
+#endif
+
+/** Set this to 1 if you want to include code that creates HTTP headers
+ * at runtime. Default is off: HTTP headers are then created statically
+ * by the makefsdata tool. Static headers mean smaller code size, but
+ * the (readonly) fsdata will grow a bit as every file includes the HTTP
+ * header. */
+#if !defined LWIP_HTTPD_DYNAMIC_HEADERS || defined __DOXYGEN__
+#define LWIP_HTTPD_DYNAMIC_HEADERS 0
+#endif
+
+#if !defined HTTPD_DEBUG || defined __DOXYGEN__
+#define HTTPD_DEBUG         LWIP_DBG_OFF
+#endif
+
+/** Set this to 1 to use a memp pool for allocating
+ * struct http_state instead of the heap.
+ * If enabled, you'll need to define MEMP_NUM_PARALLEL_HTTPD_CONNS
+ * (and MEMP_NUM_PARALLEL_HTTPD_SSI_CONNS for SSI) to set the size of
+ * the pool(s).
+ */
+#if !defined HTTPD_USE_MEM_POOL || defined __DOXYGEN__
+#define HTTPD_USE_MEM_POOL  0
+#endif
+
+/** The server port for HTTPD to use */
+#if !defined HTTPD_SERVER_PORT || defined __DOXYGEN__
+#define HTTPD_SERVER_PORT                   LWIP_IANA_PORT_HTTP
+#endif
+
+/** The https server port for HTTPD to use */
+#if !defined HTTPD_SERVER_PORT_HTTPS || defined __DOXYGEN__
+#define HTTPD_SERVER_PORT_HTTPS             LWIP_IANA_PORT_HTTPS
+#endif
+
+/** Enable https support? */
+#if !defined HTTPD_ENABLE_HTTPS || defined __DOXYGEN__
+#define HTTPD_ENABLE_HTTPS                  0
+#endif
+
+/** Maximum retries before the connection is aborted/closed.
+ * - number of times pcb->poll is called -> default is 4*500ms = 2s;
+ * - reset when pcb->sent is called
+ */
+#if !defined HTTPD_MAX_RETRIES || defined __DOXYGEN__
+#define HTTPD_MAX_RETRIES                   4
+#endif
+
+/** The poll delay is X*500ms */
+#if !defined HTTPD_POLL_INTERVAL || defined __DOXYGEN__
+#define HTTPD_POLL_INTERVAL                 4
+#endif
+
+/** Priority for tcp pcbs created by HTTPD (very low by default).
+ *  Lower priorities get killed first when running out of memory.
+ */
+#if !defined HTTPD_TCP_PRIO || defined __DOXYGEN__
+#define HTTPD_TCP_PRIO                      TCP_PRIO_MIN
+#endif
+
+/** Set this to 1 to enable timing each file sent */
+#if !defined LWIP_HTTPD_TIMING || defined __DOXYGEN__
+#define LWIP_HTTPD_TIMING                   0
+#endif
+/** Set this to 1 to enable timing each file sent */
+#if !defined HTTPD_DEBUG_TIMING || defined __DOXYGEN__
+#define HTTPD_DEBUG_TIMING                  LWIP_DBG_OFF
+#endif
+
+/** Set this to one to show error pages when parsing a request fails instead
+    of simply closing the connection. */
+#if !defined LWIP_HTTPD_SUPPORT_EXTSTATUS || defined __DOXYGEN__
+#define LWIP_HTTPD_SUPPORT_EXTSTATUS        0
+#endif
+
+/** Set this to 0 to drop support for HTTP/0.9 clients (to save some bytes) */
+#if !defined LWIP_HTTPD_SUPPORT_V09 || defined __DOXYGEN__
+#define LWIP_HTTPD_SUPPORT_V09              1
+#endif
+
+/** Set this to 1 to enable HTTP/1.1 persistent connections.
+ * ATTENTION: If the generated file system includes HTTP headers, these must
+ * include the "Connection: keep-alive" header (pass argument "-11" to makefsdata).
+ */
+#if !defined LWIP_HTTPD_SUPPORT_11_KEEPALIVE || defined __DOXYGEN__
+#define LWIP_HTTPD_SUPPORT_11_KEEPALIVE     0
+#endif
+
+/** Set this to 1 to support HTTP request coming in in multiple packets/pbufs */
+#if !defined LWIP_HTTPD_SUPPORT_REQUESTLIST || defined __DOXYGEN__
+#define LWIP_HTTPD_SUPPORT_REQUESTLIST      1
+#endif
+
+#if LWIP_HTTPD_SUPPORT_REQUESTLIST
+/** Number of rx pbufs to enqueue to parse an incoming request (up to the first
+    newline) */
+#if !defined LWIP_HTTPD_REQ_QUEUELEN || defined __DOXYGEN__
+#define LWIP_HTTPD_REQ_QUEUELEN             5
+#endif
+
+/** Number of (TCP payload-) bytes (in pbufs) to enqueue to parse and incoming
+    request (up to the first double-newline) */
+#if !defined LWIP_HTTPD_REQ_BUFSIZE || defined __DOXYGEN__
+#define LWIP_HTTPD_REQ_BUFSIZE              LWIP_HTTPD_MAX_REQ_LENGTH
+#endif
+
+/** Defines the maximum length of a HTTP request line (up to the first CRLF,
+    copied from pbuf into this a global buffer when pbuf- or packet-queues
+    are received - otherwise the input pbuf is used directly) */
+#if !defined LWIP_HTTPD_MAX_REQ_LENGTH || defined __DOXYGEN__
+#define LWIP_HTTPD_MAX_REQ_LENGTH           LWIP_MIN(1023, (LWIP_HTTPD_REQ_QUEUELEN * PBUF_POOL_BUFSIZE))
+#endif
+#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */
+
+/** This is the size of a static buffer used when URIs end with '/'.
+ * In this buffer, the directory requested is concatenated with all the
+ * configured default file names.
+ * Set to 0 to disable checking default filenames on non-root directories.
+ */
+#if !defined LWIP_HTTPD_MAX_REQUEST_URI_LEN || defined __DOXYGEN__
+#define LWIP_HTTPD_MAX_REQUEST_URI_LEN      63
+#endif
+
+/** Maximum length of the filename to send as response to a POST request,
+ * filled in by the application when a POST is finished.
+ */
+#if !defined LWIP_HTTPD_POST_MAX_RESPONSE_URI_LEN || defined __DOXYGEN__
+#define LWIP_HTTPD_POST_MAX_RESPONSE_URI_LEN 63
+#endif
+
+/** Set this to 0 to not send the SSI tag (default is on, so the tag will
+ * be sent in the HTML page */
+#if !defined LWIP_HTTPD_SSI_INCLUDE_TAG || defined __DOXYGEN__
+#define LWIP_HTTPD_SSI_INCLUDE_TAG           1
+#endif
+
+/** Set this to 1 to call tcp_abort when tcp_close fails with memory error.
+ * This can be used to prevent consuming all memory in situations where the
+ * HTTP server has low priority compared to other communication. */
+#if !defined LWIP_HTTPD_ABORT_ON_CLOSE_MEM_ERROR || defined __DOXYGEN__
+#define LWIP_HTTPD_ABORT_ON_CLOSE_MEM_ERROR  0
+#endif
+
+/** Set this to 1 to kill the oldest connection when running out of
+ * memory for 'struct http_state' or 'struct http_ssi_state'.
+ * ATTENTION: This puts all connections on a linked list, so may be kind of slow.
+ */
+#if !defined LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED || defined __DOXYGEN__
+#define LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED 0
+#endif
+
+/** Set this to 1 to send URIs without extension without headers
+ * (who uses this at all??) */
+#if !defined LWIP_HTTPD_OMIT_HEADER_FOR_EXTENSIONLESS_URI || defined __DOXYGEN__
+#define LWIP_HTTPD_OMIT_HEADER_FOR_EXTENSIONLESS_URI 0
+#endif
+
+/** Default: Tags are sent from struct http_state and are therefore volatile */
+#if !defined HTTP_IS_TAG_VOLATILE || defined __DOXYGEN__
+#define HTTP_IS_TAG_VOLATILE(ptr) TCP_WRITE_FLAG_COPY
+#endif
+
+/* By default, the httpd is limited to send 2*pcb->mss to keep resource usage low
+   when http is not an important protocol in the device. */
+#if !defined HTTPD_LIMIT_SENDING_TO_2MSS || defined __DOXYGEN__
+#define HTTPD_LIMIT_SENDING_TO_2MSS 1
+#endif
+
+/* Define this to a function that returns the maximum amount of data to enqueue.
+   The function have this signature: u16_t fn(struct altcp_pcb* pcb);
+   The best place to define this is the hooks file (@see LWIP_HOOK_FILENAME) */
+#if !defined HTTPD_MAX_WRITE_LEN || defined __DOXYGEN__
+#if HTTPD_LIMIT_SENDING_TO_2MSS
+#define HTTPD_MAX_WRITE_LEN(pcb)    ((u16_t)(2 * altcp_mss(pcb)))
+#endif
+#endif
+
+/*------------------- FS OPTIONS -------------------*/
+
+/** Set this to 1 and provide the functions:
+ * - "int fs_open_custom(struct fs_file *file, const char *name)"
+ *    Called first for every opened file to allow opening files
+ *    that are not included in fsdata(_custom).c
+ * - "void fs_close_custom(struct fs_file *file)"
+ *    Called to free resources allocated by fs_open_custom().
+ */
+#if !defined LWIP_HTTPD_CUSTOM_FILES || defined __DOXYGEN__
+#define LWIP_HTTPD_CUSTOM_FILES       0
+#endif
+
+/** Set this to 1 to support fs_read() to dynamically read file data.
+ * Without this (default=off), only one-block files are supported,
+ * and the contents must be ready after fs_open().
+ */
+#if !defined LWIP_HTTPD_DYNAMIC_FILE_READ || defined __DOXYGEN__
+#define LWIP_HTTPD_DYNAMIC_FILE_READ  0
+#endif
+
+/** Set this to 1 to include an application state argument per file
+ * that is opened. This allows to keep a state per connection/file.
+ */
+#if !defined LWIP_HTTPD_FILE_STATE || defined __DOXYGEN__
+#define LWIP_HTTPD_FILE_STATE         0
+#endif
+
+/** Set this to 1 to add the pextension field to the fs_file structure.
+ * This is included here to retain compatibility with legacy code that
+ * relies on the presence of the pextension field.
+ * New code should use LWIP_HTTPD_FILE_STATE instead.
+ * This option may be removed in a future version of lwip.
+ */
+#if !defined LWIP_HTTPD_FILE_EXTENSION || defined __DOXYGEN__
+#define LWIP_HTTPD_FILE_EXTENSION     0
+#endif
+
+/** HTTPD_PRECALCULATED_CHECKSUM==1: include precompiled checksums for
+ * predefined (MSS-sized) chunks of the files to prevent having to calculate
+ * the checksums at runtime. */
+#if !defined HTTPD_PRECALCULATED_CHECKSUM || defined __DOXYGEN__
+#define HTTPD_PRECALCULATED_CHECKSUM  0
+#endif
+
+/** LWIP_HTTPD_FS_ASYNC_READ==1: support asynchronous read operations
+ * (fs_read_async returns FS_READ_DELAYED and calls a callback when finished).
+ */
+#if !defined LWIP_HTTPD_FS_ASYNC_READ || defined __DOXYGEN__
+#define LWIP_HTTPD_FS_ASYNC_READ      0
+#endif
+
+/** Filename (including path) to use as FS data file */
+#if !defined HTTPD_FSDATA_FILE || defined __DOXYGEN__
+/* HTTPD_USE_CUSTOM_FSDATA: Compatibility with deprecated lwIP option */
+#if defined(HTTPD_USE_CUSTOM_FSDATA) && (HTTPD_USE_CUSTOM_FSDATA != 0)
+#define HTTPD_FSDATA_FILE "fsdata_custom.c"
+#else
+#define HTTPD_FSDATA_FILE "fsdata.c"
+#endif
+#endif
+
+/**
+ * @}
+ */
+
+#endif /* LWIP_HDR_APPS_HTTPD_OPTS_H */
diff --git a/lib/lwip/include/lwip/apps/lwiperf.h b/lib/lwip/include/lwip/apps/lwiperf.h
new file mode 100644
index 0000000000..e6f4476a1f
--- /dev/null
+++ b/lib/lwip/include/lwip/apps/lwiperf.h
@@ -0,0 +1,109 @@
+/**
+ * @file
+ * lwIP iPerf server implementation
+ */
+
+/*
+ * Copyright (c) 2014 Simon Goldschmidt
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt
+ *
+ */
+#ifndef LWIP_HDR_APPS_LWIPERF_H
+#define LWIP_HDR_APPS_LWIPERF_H
+
+#include "lwip/opt.h"
+#include "lwip/ip_addr.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define LWIPERF_TCP_PORT_DEFAULT  5001
+
+/** lwIPerf test results */
+enum lwiperf_report_type
+{
+  /** The server side test is done */
+  LWIPERF_TCP_DONE_SERVER,
+  /** The client side test is done */
+  LWIPERF_TCP_DONE_CLIENT,
+  /** Local error lead to test abort */
+  LWIPERF_TCP_ABORTED_LOCAL,
+  /** Data check error lead to test abort */
+  LWIPERF_TCP_ABORTED_LOCAL_DATAERROR,
+  /** Transmit error lead to test abort */
+  LWIPERF_TCP_ABORTED_LOCAL_TXERROR,
+  /** Remote side aborted the test */
+  LWIPERF_TCP_ABORTED_REMOTE
+};
+
+/** Control */
+enum lwiperf_client_type
+{
+  /** Unidirectional tx only test */
+  LWIPERF_CLIENT,
+  /** Do a bidirectional test simultaneously */
+  LWIPERF_DUAL,
+  /** Do a bidirectional test individually */
+  LWIPERF_TRADEOFF
+};
+
+/** Prototype of a report function that is called when a session is finished.
+    This report function can show the test results.
+    @param arg Report_arg from when the test was started.
+    @param report_type contains the test result
+    @param local_addr The local address from the session
+    @param local_port The local port
+    @param remote_addr The remote address from the session
+    @param remote_port The remote port
+    @param bytes_transferred Total transferred bytes
+    @param ms_duration Total session duration, in milliseconds
+    @param bandwidth_kbitpsec Average bandwidth during the session, in kbps
+*/
+typedef void (*lwiperf_report_fn)(void *arg, enum lwiperf_report_type report_type,
+  const ip_addr_t* local_addr, u16_t local_port, const ip_addr_t* remote_addr, u16_t remote_port,
+  u32_t bytes_transferred, u32_t ms_duration, u32_t bandwidth_kbitpsec);
+
+void* lwiperf_start_tcp_server(const ip_addr_t* local_addr, u16_t local_port,
+                               lwiperf_report_fn report_fn, void* report_arg);
+void* lwiperf_start_tcp_server_default(lwiperf_report_fn report_fn, void* report_arg);
+void* lwiperf_start_tcp_client(const ip_addr_t* remote_addr, u16_t remote_port,
+                               enum lwiperf_client_type type,
+                               lwiperf_report_fn report_fn, void* report_arg);
+void* lwiperf_start_tcp_client_default(const ip_addr_t* remote_addr,
+                               lwiperf_report_fn report_fn, void* report_arg);
+
+void  lwiperf_abort(void* lwiperf_session);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_APPS_LWIPERF_H */
diff --git a/lib/lwip/include/lwip/apps/mdns.h b/lib/lwip/include/lwip/apps/mdns.h
new file mode 100644
index 0000000000..33da4a3e83
--- /dev/null
+++ b/lib/lwip/include/lwip/apps/mdns.h
@@ -0,0 +1,154 @@
+/**
+ * @file
+ * MDNS responder
+ */
+
+ /*
+ * Copyright (c) 2015 Verisure Innovation AB
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Erik Ekman <erik@kryo.se>
+ * Author: Jasper Verschueren <jasper.verschueren@apart-audio.com>
+ *
+ */
+
+#ifndef LWIP_HDR_APPS_MDNS_H
+#define LWIP_HDR_APPS_MDNS_H
+
+#include "lwip/apps/mdns_opts.h"
+#include "lwip/netif.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if LWIP_MDNS_RESPONDER
+
+enum mdns_sd_proto {
+  DNSSD_PROTO_UDP = 0,
+  DNSSD_PROTO_TCP = 1
+};
+
+#define MDNS_PROBING_CONFLICT   0
+#define MDNS_PROBING_SUCCESSFUL 1
+
+#define MDNS_LABEL_MAXLEN  63
+#define MDNS_DOMAIN_MAXLEN 256
+
+struct mdns_host;
+struct mdns_service;
+
+/* Domain structs */
+struct mdns_domain {
+  /* Encoded domain name */
+  u8_t name[MDNS_DOMAIN_MAXLEN];
+  /* Total length of domain name, including zero */
+  u16_t length;
+  /* Set if compression of this domain is not allowed */
+  u8_t skip_compression;
+};
+
+/** Domain, type and class.
+ *  Shared between questions and answers */
+struct mdns_rr_info {
+  struct mdns_domain domain;
+  u16_t type;
+  u16_t klass;
+};
+
+struct mdns_answer {
+  struct mdns_rr_info info;
+  /** cache flush command bit */
+  u16_t cache_flush;
+  /* Validity time in seconds */
+  u32_t ttl;
+  /** Length of variable answer */
+  u16_t rd_length;
+  /** Offset of start of variable answer in packet */
+  u16_t rd_offset;
+};
+
+/** Callback function to add text to a reply, called when generating the reply */
+typedef void (*service_get_txt_fn_t)(struct mdns_service *service, void *txt_userdata);
+
+/** Callback function to let application know the result of probing network for name
+ * uniqueness, called with result MDNS_PROBING_SUCCESSFUL if no other node claimed
+ * use for the name for the netif or a service and is safe to use, or MDNS_PROBING_CONFLICT
+ * if another node is already using it and mdns is disabled on this interface */
+typedef void (*mdns_name_result_cb_t)(struct netif* netif, u8_t result, s8_t slot);
+
+void *mdns_get_service_txt_userdata(struct netif *netif, s8_t slot);
+
+void mdns_resp_init(void);
+
+void mdns_resp_register_name_result_cb(mdns_name_result_cb_t cb);
+
+err_t mdns_resp_add_netif(struct netif *netif, const char *hostname);
+err_t mdns_resp_remove_netif(struct netif *netif);
+err_t mdns_resp_rename_netif(struct netif *netif, const char *hostname);
+int mdns_resp_netif_active(struct netif *netif);
+
+s8_t  mdns_resp_add_service(struct netif *netif, const char *name, const char *service, enum mdns_sd_proto proto, u16_t port, service_get_txt_fn_t txt_fn, void *txt_userdata);
+err_t mdns_resp_del_service(struct netif *netif, u8_t slot);
+err_t mdns_resp_rename_service(struct netif *netif, u8_t slot, const char *name);
+
+err_t mdns_resp_add_service_txtitem(struct mdns_service *service, const char *txt, u8_t txt_len);
+
+void mdns_resp_restart_delay(struct netif *netif, uint32_t delay);
+void mdns_resp_restart(struct netif *netif);
+void mdns_resp_announce(struct netif *netif);
+
+/**
+ * @ingroup mdns
+ * Announce IP settings have changed on netif.
+ * Call this in your callback registered by netif_set_status_callback().
+ * No need to call this function when LWIP_NETIF_EXT_STATUS_CALLBACK==1,
+ * this handled automatically for you.
+ * @param netif The network interface where settings have changed.
+ */
+#define mdns_resp_netif_settings_changed(netif) mdns_resp_announce(netif)
+
+#if LWIP_MDNS_SEARCH
+typedef void (*search_result_fn_t)(struct mdns_answer *answer, const char *varpart, int varlen, int flags, void *arg);
+/* flags bits, both can be set! */
+#define MDNS_SEARCH_RESULT_FIRST    1 /* First answer in received frame. */
+#define MDNS_SEARCH_RESULT_LAST     2 /* Last answer. */
+
+err_t mdns_search_service(const char *name, const char *service, enum mdns_sd_proto proto,
+                          struct netif *netif, search_result_fn_t result_fn, void *arg,
+                          u8_t *request_id);
+void mdns_search_stop(u8_t request_id);
+
+#endif /* LWIP_MDNS_SEARCH */
+
+#endif /* LWIP_MDNS_RESPONDER */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_APPS_MDNS_H */
diff --git a/lib/lwip/include/lwip/apps/mdns_domain.h b/lib/lwip/include/lwip/apps/mdns_domain.h
new file mode 100644
index 0000000000..9fa804e6b8
--- /dev/null
+++ b/lib/lwip/include/lwip/apps/mdns_domain.h
@@ -0,0 +1,80 @@
+/**
+ * @file
+ * MDNS responder - domain related functionalities 
+ */
+
+ /*
+ * Copyright (c) 2015 Verisure Innovation AB
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Erik Ekman <erik@kryo.se>
+ * Author: Jasper Verschueren <jasper.verschueren@apart-audio.com>
+ *
+ */
+
+#ifndef LWIP_HDR_APPS_MDNS_DOMAIN_H
+#define LWIP_HDR_APPS_MDNS_DOMAIN_H
+
+#include "lwip/apps/mdns_opts.h"
+#include "lwip/apps/mdns_priv.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if LWIP_MDNS_RESPONDER
+
+/* Domain methods - also visible for unit tests */
+
+err_t mdns_domain_add_label(struct mdns_domain *domain, const char *label, u8_t len);
+err_t mdns_domain_add_domain(struct mdns_domain *domain, struct mdns_domain *source);
+err_t mdns_domain_add_string(struct mdns_domain *domain, const char *source);
+u16_t mdns_readname(struct pbuf *p, u16_t offset, struct mdns_domain *domain);
+void mdns_domain_debug_print(struct mdns_domain *domain);
+int mdns_domain_eq(struct mdns_domain *a, struct mdns_domain *b);
+#if LWIP_IPV4
+err_t mdns_build_reverse_v4_domain(struct mdns_domain *domain, const ip4_addr_t *addr);
+#endif
+#if LWIP_IPV6
+err_t mdns_build_reverse_v6_domain(struct mdns_domain *domain, const ip6_addr_t *addr);
+#endif
+err_t mdns_build_host_domain(struct mdns_domain *domain, struct mdns_host *mdns);
+err_t mdns_build_dnssd_domain(struct mdns_domain *domain);
+err_t mdns_build_service_domain(struct mdns_domain *domain, struct mdns_service *service, int include_name);
+#if LWIP_MDNS_SEARCH
+err_t mdns_build_request_domain(struct mdns_domain *domain, struct mdns_request *request, int include_name);
+#endif
+u16_t mdns_compress_domain(struct pbuf *pbuf, u16_t *offset, struct mdns_domain *domain);
+err_t mdns_write_domain(struct mdns_outpacket *outpkt, struct mdns_domain *domain);
+
+#endif /* LWIP_MDNS_RESPONDER */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_APPS_MDNS_DOMAIN_H */
diff --git a/lib/lwip/include/lwip/apps/mdns_opts.h b/lib/lwip/include/lwip/apps/mdns_opts.h
new file mode 100644
index 0000000000..1eee3e3830
--- /dev/null
+++ b/lib/lwip/include/lwip/apps/mdns_opts.h
@@ -0,0 +1,121 @@
+/**
+ * @file
+ * MDNS responder
+ */
+
+ /*
+ * Copyright (c) 2015 Verisure Innovation AB
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Erik Ekman <erik@kryo.se>
+ * Author: Jasper Verschueren <jasper.verschueren@apart-audio.com>
+ *
+ */
+
+#ifndef LWIP_HDR_APPS_MDNS_OPTS_H
+#define LWIP_HDR_APPS_MDNS_OPTS_H
+
+#include "lwip/opt.h"
+
+/**
+ * @defgroup mdns_opts Options
+ * @ingroup mdns
+ * @{
+ */
+
+/**
+ * LWIP_MDNS_RESPONDER==1: Turn on multicast DNS module. UDP must be available for MDNS
+ * transport. IGMP is needed for IPv4 multicast.
+ */
+#ifndef LWIP_MDNS_RESPONDER
+#define LWIP_MDNS_RESPONDER             0
+#endif /* LWIP_MDNS_RESPONDER */
+
+/** The maximum number of services per netif */
+#ifndef MDNS_MAX_SERVICES
+#define MDNS_MAX_SERVICES               1
+#endif
+
+/** The minimum delay between probes in ms. RFC 6762 require 250ms.
+ * In noisy WiFi environment, adding 30-50ms to this value help a lot for
+ * a successful Apple BCT tests.
+ */
+#ifndef MDNS_PROBE_DELAY_MS
+#define MDNS_PROBE_DELAY_MS           250
+#endif
+
+/** The maximum number of received packets stored in chained list of known
+ * answers for pending truncated questions. This value define the size of
+ * the MDNS_PKTS mempool.
+ * Up to MDNS_MAX_STORED_PKTS pbuf can be stored in addition to TC questions
+ * that are pending.
+ */
+#ifndef MDNS_MAX_STORED_PKTS
+#define MDNS_MAX_STORED_PKTS            4
+#endif
+
+/** Payload size allocated for each outgoing UDP packet. Will be allocated with
+ * PBUF_RAM and freed after packet was sent.
+ * According to RFC 6762, there is no reason to retain the 512 bytes restriction
+ * for link-local multicast packet.
+ * 512 bytes isn't enough when 2 services need to be probed.
+ */
+#ifndef MDNS_OUTPUT_PACKET_SIZE
+#define MDNS_OUTPUT_PACKET_SIZE      ((MDNS_MAX_SERVICES == 1) ? 512 : 1450)
+#endif
+
+/** MDNS_RESP_USENETIF_EXTCALLBACK==1: register an ext_callback on the netif
+ * to automatically restart probing/announcing on status or address change.
+ */
+#ifndef MDNS_RESP_USENETIF_EXTCALLBACK
+#define MDNS_RESP_USENETIF_EXTCALLBACK  LWIP_NETIF_EXT_STATUS_CALLBACK
+#endif
+
+/**
+ * LWIP_MDNS_SEARCH==1: Turn on search over multicast DNS module.
+ */
+#ifndef LWIP_MDNS_SEARCH
+#define LWIP_MDNS_SEARCH                1
+#endif
+
+/** The maximum number of running requests */
+#ifndef MDNS_MAX_REQUESTS
+#define MDNS_MAX_REQUESTS               2
+#endif
+
+/**
+ * MDNS_DEBUG: Enable debugging for multicast DNS.
+ */
+#ifndef MDNS_DEBUG
+#define MDNS_DEBUG                       LWIP_DBG_OFF
+#endif
+
+/**
+ * @}
+ */
+
+#endif /* LWIP_HDR_APPS_MDNS_OPTS_H */
diff --git a/lib/lwip/include/lwip/apps/mdns_out.h b/lib/lwip/include/lwip/apps/mdns_out.h
new file mode 100644
index 0000000000..e6a7e3884b
--- /dev/null
+++ b/lib/lwip/include/lwip/apps/mdns_out.h
@@ -0,0 +1,138 @@
+/**
+ * @file
+ * MDNS responder - output related functionalities
+ */
+
+ /*
+ * Copyright (c) 2015 Verisure Innovation AB
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Erik Ekman <erik@kryo.se>
+ * Author: Jasper Verschueren <jasper.verschueren@apart-audio.com>
+ *
+ */
+
+#ifndef LWIP_HDR_APPS_MDNS_OUT_H
+#define LWIP_HDR_APPS_MDNS_OUT_H
+
+#include "lwip/apps/mdns_opts.h"
+#include "lwip/apps/mdns_priv.h"
+#include "lwip/netif.h"
+#include "lwip/timeouts.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if LWIP_MDNS_RESPONDER
+
+/** Bitmasks outmsg generation */
+/* Probe for ALL types with hostname */
+#define QUESTION_PROBE_HOST_ANY          0x10
+/* Probe for ALL types with service instance name */
+#define QUESTION_PROBE_SERVICE_NAME_ANY  0x10
+
+/* Lookup from hostname -> IPv4 */
+#define REPLY_HOST_A            0x01
+/* Lookup from IPv4/v6 -> hostname */
+#define REPLY_HOST_PTR_V4       0x02
+/* Lookup from hostname -> IPv6 */
+#define REPLY_HOST_AAAA         0x04
+/* Lookup from hostname -> IPv6 */
+#define REPLY_HOST_PTR_V6       0x08
+
+/* Lookup for service types */
+#define REPLY_SERVICE_TYPE_PTR  0x10
+/* Lookup for instances of service */
+#define REPLY_SERVICE_NAME_PTR  0x20
+/* Lookup for location of service instance */
+#define REPLY_SERVICE_SRV       0x40
+/* Lookup for text info on service instance */
+#define REPLY_SERVICE_TXT       0x80
+
+/* RFC6762 section 6:
+ * To protect the network against excessive packet flooding due to software bugs
+ * or malicious attack, a Multicast DNS responder MUST NOT (except in the one
+ * special case of answering probe queries) multicast a record on a given
+ * interface until at least one second has elapsed since the last time that
+ * record was multicast on that particular interface.
+ */
+#define MDNS_MULTICAST_TIMEOUT    1000
+
+/* RFC6762 section 6:
+ * In this special case only, when responding via multicast to a probe, a
+ * Multicast DNS responder is only required to delay its transmission as
+ * necessary to ensure an interval of at least 250 ms since the last time the
+ * record was multicast on that interface.
+ */
+#define MDNS_MULTICAST_PROBE_TIMEOUT    250
+
+/* RFC6762 section 5.4:
+ * When receiving a question with the unicast-response bit set, a responder
+ * SHOULD usually respond with a unicast packet directed back to the querier.
+ * However, if the responder has not multicast that record recently (within one
+ * quarter of its TTL), then the responder SHOULD instead multicast the response
+ * so as to keep all the peer caches up to date, and to permit passive conflict
+ * detection.
+ * -> we implement a stripped down version. Depending on a timeout of 30s
+ *    (25% of 120s) all QU questions are send via multicast or unicast.
+ */
+#define MDNS_MULTICAST_TIMEOUT_25TTL  30000
+
+err_t mdns_create_outpacket(struct netif *netif, struct mdns_outmsg *msg,
+                            struct mdns_outpacket *outpkt);
+err_t mdns_send_outpacket(struct mdns_outmsg *msg, struct netif *netif);
+void mdns_set_timeout(struct netif *netif, u32_t msecs,
+                        sys_timeout_handler handler, u8_t *busy_flag);
+#if LWIP_IPV4
+void mdns_multicast_timeout_reset_ipv4(void *arg);
+void mdns_multicast_probe_timeout_reset_ipv4(void *arg);
+void mdns_multicast_timeout_25ttl_reset_ipv4(void *arg);
+void mdns_send_multicast_msg_delayed_ipv4(void *arg);
+void mdns_send_unicast_msg_delayed_ipv4(void *arg);
+void mdns_start_multicast_timeouts_ipv4(struct netif *netif);
+#endif
+#if LWIP_IPV6
+void mdns_multicast_timeout_reset_ipv6(void *arg);
+void mdns_multicast_probe_timeout_reset_ipv6(void *arg);
+void mdns_multicast_timeout_25ttl_reset_ipv6(void *arg);
+void mdns_send_multicast_msg_delayed_ipv6(void *arg);
+void mdns_send_unicast_msg_delayed_ipv6(void *arg);
+void mdns_start_multicast_timeouts_ipv6(struct netif *netif);
+#endif
+void mdns_prepare_txtdata(struct mdns_service *service);
+#ifdef LWIP_MDNS_SEARCH
+err_t mdns_send_request(struct mdns_request *req, struct netif *netif, const ip_addr_t *destination);
+#endif
+
+#endif /* LWIP_MDNS_RESPONDER */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_APPS_MDNS_OUT_H */
diff --git a/lib/lwip/include/lwip/apps/mdns_priv.h b/lib/lwip/include/lwip/apps/mdns_priv.h
new file mode 100644
index 0000000000..5209ba0043
--- /dev/null
+++ b/lib/lwip/include/lwip/apps/mdns_priv.h
@@ -0,0 +1,237 @@
+/**
+ * @file
+ * MDNS responder private definitions
+ */
+
+ /*
+ * Copyright (c) 2015 Verisure Innovation AB
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Erik Ekman <erik@kryo.se>
+ * Author: Jasper Verschueren <jasper.verschueren@apart-audio.com>
+ *
+ */
+#ifndef LWIP_HDR_MDNS_PRIV_H
+#define LWIP_HDR_MDNS_PRIV_H
+
+#include "lwip/apps/mdns.h"
+#include "lwip/apps/mdns_opts.h"
+#include "lwip/pbuf.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if LWIP_MDNS_RESPONDER
+
+#define MDNS_READNAME_ERROR 0xFFFF
+#define NUM_DOMAIN_OFFSETS 10
+
+#define SRV_PRIORITY 0
+#define SRV_WEIGHT   0
+
+/* mDNS TTL: (RFC6762 section 10)
+ *  - 120 seconds if the hostname appears somewhere in the RR
+ *  - 75 minutes if not (4500 seconds)
+ *  - 10 seconds if responding to a legacy query
+ */
+#define MDNS_TTL_10    10
+#define MDNS_TTL_120   120
+#define MDNS_TTL_4500  4500
+
+/* RFC6762 section 8.1: If fifteen conflicts occur within any ten-second period,
+ * then the host MUST wait at least five seconds before each successive
+ * additional probe attempt.
+ */
+#define MDNS_PROBE_MAX_CONFLICTS_BEFORE_RATE_LIMIT  15
+#define MDNS_PROBE_MAX_CONFLICTS_TIME_WINDOW        10000
+#define MDNS_PROBE_MAX_CONFLICTS_TIMEOUT            5000
+
+#if LWIP_MDNS_SEARCH
+/** Description of a search request */
+struct mdns_request {
+  /** Name of service, like 'myweb' */
+  char name[MDNS_LABEL_MAXLEN + 1];
+  /** Type of service, like '_http' or '_services._dns-sd' */
+  struct mdns_domain service;
+  /** Callback function called for each response */
+  search_result_fn_t result_fn;
+  void *arg;
+  /** Protocol, TCP or UDP */
+  u16_t proto;
+  /** Query type (PTR, SRV, ...) */
+  u8_t qtype;
+  /** PTR only request. */
+  u16_t only_ptr;
+};
+#endif
+
+/** Description of a service */
+struct mdns_service {
+  /** TXT record to answer with */
+  struct mdns_domain txtdata;
+  /** Name of service, like 'myweb' */
+  char name[MDNS_LABEL_MAXLEN + 1];
+  /** Type of service, like '_http' */
+  char service[MDNS_LABEL_MAXLEN + 1];
+  /** Callback function and userdata
+   * to update txtdata buffer */
+  service_get_txt_fn_t txt_fn;
+  void *txt_userdata;
+  /** Protocol, TCP or UDP */
+  u16_t proto;
+  /** Port of the service */
+  u16_t port;
+};
+
+/** mDNS output packet */
+struct mdns_outpacket {
+  /** Packet data */
+  struct pbuf *pbuf;
+  /** Current write offset in packet */
+  u16_t write_offset;
+  /** Number of questions written */
+  u16_t questions;
+  /** Number of normal answers written */
+  u16_t answers;
+  /** Number of authoritative answers written */
+  u16_t authoritative;
+  /** Number of additional answers written */
+  u16_t additional;
+  /** Offsets for written domain names in packet.
+   *  Used for compression */
+  u16_t domain_offsets[NUM_DOMAIN_OFFSETS];
+};
+
+/** mDNS output message */
+struct mdns_outmsg {
+  /** Identifier. Used in legacy queries */
+  u16_t tx_id;
+  /** dns flags */
+  u8_t flags;
+  /** Destination IP/port if sent unicast */
+  ip_addr_t dest_addr;
+  u16_t dest_port;
+  /** If all answers in packet should set cache_flush bit */
+  u8_t cache_flush;
+  /** If reply should be sent unicast (as requested) */
+  u8_t unicast_reply_requested;
+  /** If legacy query. (tx_id needed, and write
+   *  question again in reply before answer) */
+  u8_t legacy_query;
+  /** If the query is a probe msg we need to respond immediately. Independent of
+   *  the QU or QM flag. */
+  u8_t probe_query_recv;
+  /* Question bitmask for host information */
+  u8_t host_questions;
+  /* Questions bitmask per service */
+  u8_t serv_questions[MDNS_MAX_SERVICES];
+  /* Reply bitmask for host information */
+  u8_t host_replies;
+  /* Bitmask for which reverse IPv6 hosts to answer */
+  u8_t host_reverse_v6_replies;
+  /* Reply bitmask per service */
+  u8_t serv_replies[MDNS_MAX_SERVICES];
+#ifdef LWIP_MDNS_SEARCH
+  /** Search query to send */
+  struct mdns_request *query;
+#endif
+};
+
+/** Delayed msg info */
+struct mdns_delayed_msg {
+  /** Signals if a multicast msg needs to be send out */
+  u8_t multicast_msg_waiting;
+  /** Multicast timeout for all multicast traffic except probe answers */
+  u8_t multicast_timeout;
+  /** Multicast timeout only for probe answers */
+  u8_t multicast_probe_timeout;
+  /** Output msg used for delayed multicast responses */
+  struct mdns_outmsg delayed_msg_multicast;
+  /** Prefer multicast over unicast timeout -> 25% of TTL = we take 30s as
+      general delay. */
+  u8_t multicast_timeout_25TTL;
+  /** Only send out new unicast message if previous was send */
+  u8_t unicast_msg_in_use;
+  /** Output msg used for delayed unicast responses */
+  struct mdns_outmsg delayed_msg_unicast;
+};
+
+/* MDNS states */
+typedef enum {
+  /* MDNS module is off */
+  MDNS_STATE_OFF,
+  /* Waiting before probing can be started */
+  MDNS_STATE_PROBE_WAIT,
+  /* Probing the unique records */
+  MDNS_STATE_PROBING,
+  /* Waiting before announcing the probed unique records */
+  MDNS_STATE_ANNOUNCE_WAIT,
+  /* Announcing all records */
+  MDNS_STATE_ANNOUNCING,
+  /* Probing and announcing completed */
+  MDNS_STATE_COMPLETE
+} mdns_resp_state_enum_t;
+
+/** Description of a host/netif */
+struct mdns_host {
+  /** Hostname */
+  char name[MDNS_LABEL_MAXLEN + 1];
+  /** Pointer to services */
+  struct mdns_service *services[MDNS_MAX_SERVICES];
+  /** Number of probes/announces sent for the current name */
+  u8_t sent_num;
+  /** State of the mdns responder */
+  mdns_resp_state_enum_t state;
+#if LWIP_IPV4
+  /** delayed msg struct for IPv4 */
+  struct mdns_delayed_msg ipv4;
+#endif
+#if LWIP_IPV6
+  /** delayed msg struct for IPv6 */
+  struct mdns_delayed_msg ipv6;
+#endif
+  /** Timestamp of probe conflict saved in list */
+  u32_t conflict_time[MDNS_PROBE_MAX_CONFLICTS_BEFORE_RATE_LIMIT];
+  /** Rate limit flag */
+  u8_t rate_limit_activated;
+  /** List index for timestamps */
+  u8_t index;
+  /** number of conflicts since startup */
+  u8_t num_conflicts;
+};
+
+struct mdns_host* netif_mdns_data(struct netif *netif);
+struct udp_pcb* get_mdns_pcb(void);
+
+#endif /* LWIP_MDNS_RESPONDER */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_MDNS_PRIV_H */
diff --git a/lib/lwip/include/lwip/apps/mqtt.h b/lib/lwip/include/lwip/apps/mqtt.h
new file mode 100644
index 0000000000..bece4005dc
--- /dev/null
+++ b/lib/lwip/include/lwip/apps/mqtt.h
@@ -0,0 +1,205 @@
+/**
+ * @file
+ * MQTT client
+ */
+
+/*
+ * Copyright (c) 2016 Erik Andersson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Erik Andersson
+ *
+ */
+#ifndef LWIP_HDR_APPS_MQTT_CLIENT_H
+#define LWIP_HDR_APPS_MQTT_CLIENT_H
+
+#include "lwip/apps/mqtt_opts.h"
+#include "lwip/err.h"
+#include "lwip/ip_addr.h"
+#include "lwip/prot/iana.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct mqtt_client_s mqtt_client_t;
+
+#if LWIP_ALTCP && LWIP_ALTCP_TLS
+struct altcp_tls_config;
+#endif
+
+/** @ingroup mqtt
+ * Default MQTT port (non-TLS) */
+#define MQTT_PORT     LWIP_IANA_PORT_MQTT
+/** @ingroup mqtt
+ * Default MQTT TLS port */
+#define MQTT_TLS_PORT LWIP_IANA_PORT_SECURE_MQTT
+
+/*---------------------------------------------------------------------------------------------- */
+/* Connection with server */
+
+/**
+ * @ingroup mqtt
+ * Client information and connection parameters */
+struct mqtt_connect_client_info_t {
+  /** Client identifier, must be set by caller */
+  const char *client_id;
+  /** User name, set to NULL if not used */
+  const char* client_user;
+  /** Password, set to NULL if not used */
+  const char* client_pass;
+  /** keep alive time in seconds, 0 to disable keep alive functionality*/
+  u16_t keep_alive;
+  /** will topic, set to NULL if will is not to be used,
+      will_msg, will_qos and will retain are then ignored */
+  const char* will_topic;
+  /** will_msg, see will_topic */
+  const char* will_msg;
+  /** will_qos, see will_topic */
+  u8_t will_qos;
+  /** will_retain, see will_topic */
+  u8_t will_retain;
+#if LWIP_ALTCP && LWIP_ALTCP_TLS
+  /** TLS configuration for secure connections */
+  struct altcp_tls_config *tls_config;
+#endif
+};
+
+/**
+ * @ingroup mqtt
+ * Connection status codes */
+typedef enum
+{
+  /** Accepted */
+  MQTT_CONNECT_ACCEPTED                 = 0,
+  /** Refused protocol version */
+  MQTT_CONNECT_REFUSED_PROTOCOL_VERSION = 1,
+  /** Refused identifier */
+  MQTT_CONNECT_REFUSED_IDENTIFIER       = 2,
+  /** Refused server */
+  MQTT_CONNECT_REFUSED_SERVER           = 3,
+  /** Refused user credentials */
+  MQTT_CONNECT_REFUSED_USERNAME_PASS    = 4,
+  /** Refused not authorized */
+  MQTT_CONNECT_REFUSED_NOT_AUTHORIZED_  = 5,
+  /** Disconnected */
+  MQTT_CONNECT_DISCONNECTED             = 256,
+  /** Timeout */
+  MQTT_CONNECT_TIMEOUT                  = 257
+} mqtt_connection_status_t;
+
+/**
+ * @ingroup mqtt
+ * Function prototype for mqtt connection status callback. Called when
+ * client has connected to the server after initiating a mqtt connection attempt by
+ * calling mqtt_client_connect() or when connection is closed by server or an error
+ *
+ * @param client MQTT client itself
+ * @param arg Additional argument to pass to the callback function
+ * @param status Connect result code or disconnection notification @see mqtt_connection_status_t
+ *
+ */
+typedef void (*mqtt_connection_cb_t)(mqtt_client_t *client, void *arg, mqtt_connection_status_t status);
+
+
+/**
+ * @ingroup mqtt
+ * Data callback flags */
+enum {
+  /** Flag set when last fragment of data arrives in data callback */
+  MQTT_DATA_FLAG_LAST = 1
+};
+
+/**
+ * @ingroup mqtt
+ * Function prototype for MQTT incoming publish data callback function. Called when data
+ * arrives to a subscribed topic @see mqtt_subscribe
+ *
+ * @param arg Additional argument to pass to the callback function
+ * @param data User data, pointed object, data may not be referenced after callback return,
+          NULL is passed when all publish data are delivered
+ * @param len Length of publish data fragment
+ * @param flags MQTT_DATA_FLAG_LAST set when this call contains the last part of data from publish message
+ *
+ */
+typedef void (*mqtt_incoming_data_cb_t)(void *arg, const u8_t *data, u16_t len, u8_t flags);
+
+
+/**
+ * @ingroup mqtt
+ * Function prototype for MQTT incoming publish function. Called when an incoming publish
+ * arrives to a subscribed topic @see mqtt_subscribe
+ *
+ * @param arg Additional argument to pass to the callback function
+ * @param topic Zero terminated Topic text string, topic may not be referenced after callback return
+ * @param tot_len Total length of publish data, if set to 0 (no publish payload) data callback will not be invoked
+ */
+typedef void (*mqtt_incoming_publish_cb_t)(void *arg, const char *topic, u32_t tot_len);
+
+
+/**
+ * @ingroup mqtt
+ * Function prototype for mqtt request callback. Called when a subscribe, unsubscribe
+ * or publish request has completed
+ * @param arg Pointer to user data supplied when invoking request
+ * @param err ERR_OK on success
+ *            ERR_TIMEOUT if no response was received within timeout,
+ *            ERR_ABRT if (un)subscribe was denied
+ */
+typedef void (*mqtt_request_cb_t)(void *arg, err_t err);
+
+
+err_t mqtt_client_connect(mqtt_client_t *client, const ip_addr_t *ipaddr, u16_t port, mqtt_connection_cb_t cb, void *arg,
+                   const struct mqtt_connect_client_info_t *client_info);
+
+void mqtt_disconnect(mqtt_client_t *client);
+
+mqtt_client_t *mqtt_client_new(void);
+void mqtt_client_free(mqtt_client_t* client);
+
+u8_t mqtt_client_is_connected(mqtt_client_t *client);
+
+void mqtt_set_inpub_callback(mqtt_client_t *client, mqtt_incoming_publish_cb_t pub_cb,
+                             mqtt_incoming_data_cb_t data_cb, void *arg);
+
+err_t mqtt_sub_unsub(mqtt_client_t *client, const char *topic, u8_t qos, mqtt_request_cb_t cb, void *arg, u8_t sub);
+
+/** @ingroup mqtt
+ *Subscribe to topic */
+#define mqtt_subscribe(client, topic, qos, cb, arg) mqtt_sub_unsub(client, topic, qos, cb, arg, 1)
+/** @ingroup mqtt
+ *  Unsubscribe to topic */
+#define mqtt_unsubscribe(client, topic, cb, arg) mqtt_sub_unsub(client, topic, 0, cb, arg, 0)
+
+err_t mqtt_publish(mqtt_client_t *client, const char *topic, const void *payload, u16_t payload_length, u8_t qos, u8_t retain,
+                                    mqtt_request_cb_t cb, void *arg);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_APPS_MQTT_CLIENT_H */
diff --git a/lib/lwip/include/lwip/apps/mqtt_opts.h b/lib/lwip/include/lwip/apps/mqtt_opts.h
new file mode 100644
index 0000000000..4226d21e84
--- /dev/null
+++ b/lib/lwip/include/lwip/apps/mqtt_opts.h
@@ -0,0 +1,103 @@
+/**
+ * @file
+ * MQTT client options
+ */
+
+/*
+ * Copyright (c) 2016 Erik Andersson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Erik Andersson
+ *
+ */
+#ifndef LWIP_HDR_APPS_MQTT_OPTS_H
+#define LWIP_HDR_APPS_MQTT_OPTS_H
+
+#include "lwip/opt.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @defgroup mqtt_opts Options
+ * @ingroup mqtt
+ * @{
+ */
+
+/**
+ * Output ring-buffer size, must be able to fit largest outgoing publish message topic+payloads
+ */
+#ifndef MQTT_OUTPUT_RINGBUF_SIZE
+#define MQTT_OUTPUT_RINGBUF_SIZE 256
+#endif
+
+/**
+ * Number of bytes in receive buffer, must be at least the size of the longest incoming topic + 8
+ * If one wants to avoid fragmented incoming publish, set length to max incoming topic length + max payload length + 8
+ */
+#ifndef MQTT_VAR_HEADER_BUFFER_LEN
+#define MQTT_VAR_HEADER_BUFFER_LEN 128
+#endif
+
+/**
+ * Maximum number of pending subscribe, unsubscribe and publish requests to server .
+ */
+#ifndef MQTT_REQ_MAX_IN_FLIGHT
+#define MQTT_REQ_MAX_IN_FLIGHT 4
+#endif
+
+/**
+ * Seconds between each cyclic timer call.
+ */
+#ifndef MQTT_CYCLIC_TIMER_INTERVAL
+#define MQTT_CYCLIC_TIMER_INTERVAL 5
+#endif
+
+/**
+ * Publish, subscribe and unsubscribe request timeout in seconds.
+ */
+#ifndef MQTT_REQ_TIMEOUT
+#define MQTT_REQ_TIMEOUT 30
+#endif
+
+/**
+ * Seconds for MQTT connect response timeout after sending connect request
+ */
+#ifndef MQTT_CONNECT_TIMOUT
+#define MQTT_CONNECT_TIMOUT 100
+#endif
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_APPS_MQTT_OPTS_H */
diff --git a/lib/lwip/include/lwip/apps/mqtt_priv.h b/lib/lwip/include/lwip/apps/mqtt_priv.h
new file mode 100644
index 0000000000..b775913cac
--- /dev/null
+++ b/lib/lwip/include/lwip/apps/mqtt_priv.h
@@ -0,0 +1,104 @@
+/**
+ * @file
+ * MQTT client (private interface)
+ */
+
+/*
+ * Copyright (c) 2016 Erik Andersson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Erik Andersson
+ *
+ */
+#ifndef LWIP_HDR_APPS_MQTT_PRIV_H
+#define LWIP_HDR_APPS_MQTT_PRIV_H
+
+#include "lwip/apps/mqtt.h"
+#include "lwip/altcp.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Pending request item, binds application callback to pending server requests */
+struct mqtt_request_t
+{
+  /** Next item in list, NULL means this is the last in chain,
+      next pointing at itself means request is unallocated */
+  struct mqtt_request_t *next;
+  /** Callback to upper layer */
+  mqtt_request_cb_t cb;
+  void *arg;
+  /** MQTT packet identifier */
+  u16_t pkt_id;
+  /** Expire time relative to element before this  */
+  u16_t timeout_diff;
+};
+
+/** Ring buffer */
+struct mqtt_ringbuf_t {
+  u16_t put;
+  u16_t get;
+  u8_t buf[MQTT_OUTPUT_RINGBUF_SIZE];
+};
+
+/** MQTT client */
+struct mqtt_client_s
+{
+  /** Timers and timeouts */
+  u16_t cyclic_tick;
+  u16_t keep_alive;
+  u16_t server_watchdog;
+  /** Packet identifier generator*/
+  u16_t pkt_id_seq;
+  /** Packet identifier of pending incoming publish */
+  u16_t inpub_pkt_id;
+  /** Connection state */
+  u8_t conn_state;
+  struct altcp_pcb *conn;
+  /** Connection callback */
+  void *connect_arg;
+  mqtt_connection_cb_t connect_cb;
+  /** Pending requests to server */
+  struct mqtt_request_t *pend_req_queue;
+  struct mqtt_request_t req_list[MQTT_REQ_MAX_IN_FLIGHT];
+  void *inpub_arg;
+  /** Incoming data callback */
+  mqtt_incoming_data_cb_t data_cb;
+  mqtt_incoming_publish_cb_t pub_cb;
+  /** Input */
+  u32_t msg_idx;
+  u8_t rx_buffer[MQTT_VAR_HEADER_BUFFER_LEN];
+  /** Output ring-buffer */
+  struct mqtt_ringbuf_t output;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_APPS_MQTT_PRIV_H */
diff --git a/lib/lwip/include/lwip/apps/netbiosns.h b/lib/lwip/include/lwip/apps/netbiosns.h
new file mode 100644
index 0000000000..a326d33b11
--- /dev/null
+++ b/lib/lwip/include/lwip/apps/netbiosns.h
@@ -0,0 +1,51 @@
+/**
+ * @file
+ * NETBIOS name service responder
+ */
+
+/*
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ */
+#ifndef LWIP_HDR_APPS_NETBIOS_H
+#define LWIP_HDR_APPS_NETBIOS_H
+
+#include "lwip/apps/netbiosns_opts.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void netbiosns_init(void);
+#ifndef NETBIOS_LWIP_NAME
+void netbiosns_set_name(const char* hostname);
+#endif
+void netbiosns_stop(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_APPS_NETBIOS_H */
diff --git a/lib/lwip/include/lwip/apps/netbiosns_opts.h b/lib/lwip/include/lwip/apps/netbiosns_opts.h
new file mode 100644
index 0000000000..cc0da9523d
--- /dev/null
+++ b/lib/lwip/include/lwip/apps/netbiosns_opts.h
@@ -0,0 +1,66 @@
+/**
+ * @file
+ * NETBIOS name service responder options
+ */
+
+/*
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ */
+#ifndef LWIP_HDR_APPS_NETBIOS_OPTS_H
+#define LWIP_HDR_APPS_NETBIOS_OPTS_H
+
+#include "lwip/opt.h"
+
+/**
+ * @defgroup netbiosns_opts Options
+ * @ingroup netbiosns
+ * @{
+ */
+
+/** NetBIOS name of lwip device
+ * This must be uppercase until NETBIOS_STRCMP() is defined to a string
+ * comparison function that is case insensitive.
+ * If you want to use the netif's hostname, use this (with LWIP_NETIF_HOSTNAME):
+ * (ip_current_netif() != NULL ? ip_current_netif()->hostname != NULL ? ip_current_netif()->hostname : "" : "")
+ *
+ * If this is not defined, netbiosns_set_name() can be called at runtime to change the name.
+ */
+#ifdef __DOXYGEN__
+#define NETBIOS_LWIP_NAME "NETBIOSLWIPDEV"
+#endif
+
+/** Respond to NetBIOS name queries
+ * Default is disabled
+ */
+#if !defined LWIP_NETBIOS_RESPOND_NAME_QUERY || defined __DOXYGEN__
+#define LWIP_NETBIOS_RESPOND_NAME_QUERY   0
+#endif
+
+/**
+ * @}
+ */
+
+#endif /* LWIP_HDR_APPS_NETBIOS_OPTS_H */
diff --git a/lib/lwip/include/lwip/apps/smtp.h b/lib/lwip/include/lwip/apps/smtp.h
new file mode 100644
index 0000000000..fb4a4a7cca
--- /dev/null
+++ b/lib/lwip/include/lwip/apps/smtp.h
@@ -0,0 +1,128 @@
+#ifndef LWIP_HDR_APPS_SMTP_H
+#define LWIP_HDR_APPS_SMTP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "lwip/apps/smtp_opts.h"
+#include "lwip/err.h"
+#include "lwip/prot/iana.h"
+
+/** The default TCP port used for SMTP */
+#define SMTP_DEFAULT_PORT         LWIP_IANA_PORT_SMTP
+/** The default TCP port used for SMTPS */
+#define SMTPS_DEFAULT_PORT        LWIP_IANA_PORT_SMTPS
+
+/** Email successfully sent */
+#define SMTP_RESULT_OK            0
+/** Unknown error */
+#define SMTP_RESULT_ERR_UNKNOWN   1
+/** Connection to server failed */
+#define SMTP_RESULT_ERR_CONNECT   2
+/** Failed to resolve server hostname */
+#define SMTP_RESULT_ERR_HOSTNAME  3
+/** Connection unexpectedly closed by remote server */
+#define SMTP_RESULT_ERR_CLOSED    4
+/** Connection timed out (server didn't respond in time) */
+#define SMTP_RESULT_ERR_TIMEOUT   5
+/** Server responded with an unknown response code */
+#define SMTP_RESULT_ERR_SVR_RESP  6
+/** Out of resources locally */
+#define SMTP_RESULT_ERR_MEM       7
+
+/** Prototype of an smtp callback function
+ *
+ * @param arg argument specified when initiating the email
+ * @param smtp_result result of the mail transfer (see defines SMTP_RESULT_*)
+ * @param srv_err if aborted by the server, this contains the error code received
+ * @param err an error returned by internal lwip functions, can help to specify
+ *            the source of the error but must not necessarily be != ERR_OK
+ */
+typedef void (*smtp_result_fn)(void *arg, u8_t smtp_result, u16_t srv_err, err_t err);
+
+/** This structure is used as argument for smtp_send_mail_int(),
+ * which in turn can be used with tcpip_callback() to send mail
+ * from interrupt context, e.g. like this:
+ *    struct smtp_send_request *req; (to be filled)
+ *    tcpip_try_callback(smtp_send_mail_int, (void*)req);
+ *
+ * For member description, see parameter description of smtp_send_mail().
+ * When using with tcpip_callback, this structure has to stay allocated
+ * (e.g. using mem_malloc/mem_free) until its 'callback_fn' is called.
+ */
+struct smtp_send_request {
+  const char *from;
+  const char* to;
+  const char* subject;
+  const char* body;
+  smtp_result_fn callback_fn;
+  void* callback_arg;
+  /** If this is != 0, data is *not* copied into an extra buffer
+   * but used from the pointers supplied in this struct.
+   * This means less memory usage, but data must stay untouched until
+   * the callback function is called. */
+  u8_t static_data;
+};
+
+
+#if SMTP_BODYDH
+
+#ifndef SMTP_BODYDH_BUFFER_SIZE
+#define SMTP_BODYDH_BUFFER_SIZE 256
+#endif /* SMTP_BODYDH_BUFFER_SIZE */
+
+struct smtp_bodydh {
+  u16_t state;
+  u16_t length; /* Length of content in buffer */
+  char buffer[SMTP_BODYDH_BUFFER_SIZE]; /* buffer for generated content */
+#ifdef SMTP_BODYDH_USER_SIZE
+  u8_t user[SMTP_BODYDH_USER_SIZE];
+#endif /* SMTP_BODYDH_USER_SIZE */
+};
+
+enum bdh_retvals_e {
+  BDH_DONE = 0,
+  BDH_WORKING
+};
+
+/** Prototype of an smtp body callback function
+ * It receives a struct smtp_bodydh, and a buffer to write data,
+ * must return BDH_WORKING to be called again and BDH_DONE when
+ * it has finished processing. This one tries to fill one TCP buffer with
+ * data, your function will be repeatedly called until that happens; so if you
+ * know you'll be taking too long to serve your request, pause once in a while
+ * by writing length=0 to avoid hogging system resources
+ *
+ * @param arg argument specified when initiating the email
+ * @param smtp_bodydh state handling + buffer structure
+ */
+typedef int (*smtp_bodycback_fn)(void *arg, struct smtp_bodydh *bodydh);
+
+err_t smtp_send_mail_bodycback(const char *from, const char* to, const char* subject,
+                     smtp_bodycback_fn bodycback_fn, smtp_result_fn callback_fn, void* callback_arg);
+
+#endif /* SMTP_BODYDH */
+
+
+err_t smtp_set_server_addr(const char* server);
+void smtp_set_server_port(u16_t port);
+#if LWIP_ALTCP && LWIP_ALTCP_TLS
+struct altcp_tls_config;
+void smtp_set_tls_config(struct altcp_tls_config *tls_config);
+#endif
+err_t smtp_set_auth(const char* username, const char* pass);
+err_t smtp_send_mail(const char *from, const char* to, const char* subject, const char* body,
+                     smtp_result_fn callback_fn, void* callback_arg);
+err_t smtp_send_mail_static(const char *from, const char* to, const char* subject, const char* body,
+                     smtp_result_fn callback_fn, void* callback_arg);
+void smtp_send_mail_int(void *arg);
+#ifdef LWIP_DEBUG
+const char* smtp_result_str(u8_t smtp_result);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_APPS_SMTP_H */
diff --git a/lib/lwip/include/lwip/apps/smtp_opts.h b/lib/lwip/include/lwip/apps/smtp_opts.h
new file mode 100644
index 0000000000..c588fb9d5e
--- /dev/null
+++ b/lib/lwip/include/lwip/apps/smtp_opts.h
@@ -0,0 +1,80 @@
+#ifndef LWIP_HDR_APPS_SMTP_OPTS_H
+#define LWIP_HDR_APPS_SMTP_OPTS_H
+
+#include "lwip/opt.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @defgroup smtp_opts Options
+ * @ingroup smtp
+ *
+ * @{
+ */
+
+/** Set this to 1 to enable data handler callback on BODY */
+#ifndef SMTP_BODYDH
+#define SMTP_BODYDH             0
+#endif
+
+/** SMTP_DEBUG: Enable debugging for SNTP. */
+#ifndef SMTP_DEBUG
+#define SMTP_DEBUG              LWIP_DBG_OFF
+#endif
+
+/** Maximum length reserved for server name including terminating 0 byte */
+#ifndef SMTP_MAX_SERVERNAME_LEN
+#define SMTP_MAX_SERVERNAME_LEN 256
+#endif
+
+/** Maximum length reserved for username */
+#ifndef SMTP_MAX_USERNAME_LEN
+#define SMTP_MAX_USERNAME_LEN   32
+#endif
+
+/** Maximum length reserved for password */
+#ifndef SMTP_MAX_PASS_LEN
+#define SMTP_MAX_PASS_LEN       32
+#endif
+
+/** Set this to 0 if you know the authentication data will not change
+ * during the smtp session, which saves some heap space. */
+#ifndef SMTP_COPY_AUTHDATA
+#define SMTP_COPY_AUTHDATA      1
+#endif
+
+/** Set this to 0 to save some code space if you know for sure that all data
+ * passed to this module conforms to the requirements in the SMTP RFC.
+ * WARNING: use this with care!
+ */
+#ifndef SMTP_CHECK_DATA
+#define SMTP_CHECK_DATA         1
+#endif
+
+/** Set this to 1 to enable AUTH PLAIN support */
+#ifndef SMTP_SUPPORT_AUTH_PLAIN
+#define SMTP_SUPPORT_AUTH_PLAIN 1
+#endif
+
+/** Set this to 1 to enable AUTH LOGIN support */
+#ifndef SMTP_SUPPORT_AUTH_LOGIN
+#define SMTP_SUPPORT_AUTH_LOGIN 1
+#endif
+
+/* Memory allocation/deallocation can be overridden... */
+#ifndef SMTP_STATE_MALLOC
+#define SMTP_STATE_MALLOC(size)       mem_malloc(size)
+#define SMTP_STATE_FREE(ptr)          mem_free(ptr)
+#endif
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SMTP_OPTS_H */
diff --git a/lib/lwip/include/lwip/apps/snmp.h b/lib/lwip/include/lwip/apps/snmp.h
new file mode 100644
index 0000000000..dc5bb0d05e
--- /dev/null
+++ b/lib/lwip/include/lwip/apps/snmp.h
@@ -0,0 +1,145 @@
+/**
+ * @file
+ * SNMP server main API - start and basic configuration
+ */
+
+/*
+ * Copyright (c) 2001, 2002 Leon Woestenberg <leon.woestenberg@axon.tv>
+ * Copyright (c) 2001, 2002 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Leon Woestenberg <leon.woestenberg@axon.tv>
+ *         Martin Hentschel <info@cl-soft.de>
+ *
+ */
+#ifndef LWIP_HDR_APPS_SNMP_H
+#define LWIP_HDR_APPS_SNMP_H
+
+#include "lwip/apps/snmp_opts.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/err.h"
+#include "lwip/apps/snmp_core.h"
+
+/** SNMP variable binding descriptor (publicly needed for traps) */
+struct snmp_varbind
+{
+  /** pointer to next varbind, NULL for last in list */
+  struct snmp_varbind *next;
+  /** pointer to previous varbind, NULL for first in list */
+  struct snmp_varbind *prev;
+
+  /** object identifier */
+  struct snmp_obj_id oid;
+
+  /** value ASN1 type */
+  u8_t type;
+  /** object value length */
+  u16_t value_len;
+  /** object value */
+  void *value;
+};
+
+/**
+ * @ingroup snmp_core
+ * Agent setup, start listening to port 161.
+ */
+void snmp_init(void);
+void snmp_set_mibs(const struct snmp_mib **mibs, u8_t num_mibs);
+
+void snmp_set_device_enterprise_oid(const struct snmp_obj_id* device_enterprise_oid);
+const struct snmp_obj_id* snmp_get_device_enterprise_oid(void);
+
+void snmp_trap_dst_enable(u8_t dst_idx, u8_t enable);
+void snmp_trap_dst_ip_set(u8_t dst_idx, const ip_addr_t *dst);
+
+/** Generic trap: cold start */
+#define SNMP_GENTRAP_COLDSTART 0
+/** Generic trap: warm start */
+#define SNMP_GENTRAP_WARMSTART 1
+/** Generic trap: link down */
+#define SNMP_GENTRAP_LINKDOWN 2
+/** Generic trap: link up */
+#define SNMP_GENTRAP_LINKUP 3
+/** Generic trap: authentication failure */
+#define SNMP_GENTRAP_AUTH_FAILURE 4
+/** Generic trap: EGP neighbor lost */
+#define SNMP_GENTRAP_EGP_NEIGHBOR_LOSS 5
+/** Generic trap: enterprise specific */
+#define SNMP_GENTRAP_ENTERPRISE_SPECIFIC 6
+
+err_t snmp_send_trap_generic(s32_t generic_trap);
+err_t snmp_send_trap_specific(s32_t specific_trap, struct snmp_varbind *varbinds);
+err_t snmp_send_trap(const struct snmp_obj_id* oid, s32_t generic_trap, s32_t specific_trap, struct snmp_varbind *varbinds);
+
+err_t snmp_send_inform_generic(s32_t generic_trap, struct snmp_varbind *varbinds, s32_t *ptr_request_id);
+err_t snmp_send_inform_specific(s32_t specific_trap, struct snmp_varbind *varbinds, s32_t *ptr_request_id);
+err_t snmp_send_inform(const struct snmp_obj_id* oid, s32_t generic_trap, s32_t specific_trap, struct snmp_varbind *varbinds, s32_t *ptr_request_id);
+struct snmp_request;
+typedef void (*snmp_inform_callback_fct)(struct snmp_request *request, void* callback_arg);
+void snmp_set_inform_callback(snmp_inform_callback_fct inform_callback, void* callback_arg);
+
+void snmp_set_default_trap_version(u8_t snmp_version);
+u8_t snmp_get_default_trap_version(void);
+
+#define SNMP_AUTH_TRAPS_DISABLED 0
+#define SNMP_AUTH_TRAPS_ENABLED  1
+void snmp_set_auth_traps_enabled(u8_t enable);
+u8_t snmp_get_auth_traps_enabled(void);
+
+u8_t snmp_v1_enabled(void);
+u8_t snmp_v2c_enabled(void);
+u8_t snmp_v3_enabled(void);
+void snmp_v1_enable(u8_t enable);
+void snmp_v2c_enable(u8_t enable);
+void snmp_v3_enable(u8_t enable);
+
+const char * snmp_get_community(void);
+const char * snmp_get_community_write(void);
+const char * snmp_get_community_trap(void);
+void snmp_set_community(const char * const community);
+void snmp_set_community_write(const char * const community);
+void snmp_set_community_trap(const char * const community);
+
+void snmp_coldstart_trap(void);
+void snmp_authfail_trap(void);
+
+typedef void (*snmp_write_callback_fct)(const u32_t* oid, u8_t oid_len, void* callback_arg);
+void snmp_set_write_callback(snmp_write_callback_fct write_callback, void* callback_arg);
+
+#endif /* LWIP_SNMP */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_APPS_SNMP_H */
diff --git a/lib/lwip/include/lwip/apps/snmp_core.h b/lib/lwip/include/lwip/apps/snmp_core.h
new file mode 100644
index 0000000000..5a8a49f8d2
--- /dev/null
+++ b/lib/lwip/include/lwip/apps/snmp_core.h
@@ -0,0 +1,377 @@
+/**
+ * @file
+ * SNMP core API for implementing MIBs
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Christiaan Simons <christiaan.simons@axon.tv>
+ *         Martin Hentschel <info@cl-soft.de>
+ */
+
+#ifndef LWIP_HDR_APPS_SNMP_CORE_H
+#define LWIP_HDR_APPS_SNMP_CORE_H
+
+#include "lwip/apps/snmp_opts.h"
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/ip_addr.h"
+#include "lwip/err.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* basic ASN1 defines */
+#define SNMP_ASN1_CLASS_UNIVERSAL   0x00
+#define SNMP_ASN1_CLASS_APPLICATION 0x40
+#define SNMP_ASN1_CLASS_CONTEXT     0x80
+#define SNMP_ASN1_CLASS_PRIVATE     0xC0
+
+#define SNMP_ASN1_CONTENTTYPE_PRIMITIVE   0x00
+#define SNMP_ASN1_CONTENTTYPE_CONSTRUCTED 0x20
+
+/* universal tags (from ASN.1 spec.) */
+#define SNMP_ASN1_UNIVERSAL_END_OF_CONTENT  0
+#define SNMP_ASN1_UNIVERSAL_INTEGER         2
+#define SNMP_ASN1_UNIVERSAL_OCTET_STRING    4
+#define SNMP_ASN1_UNIVERSAL_NULL            5
+#define SNMP_ASN1_UNIVERSAL_OBJECT_ID       6
+#define SNMP_ASN1_UNIVERSAL_SEQUENCE_OF    16
+
+/* application specific (SNMP) tags (from SNMPv2-SMI) */
+#define SNMP_ASN1_APPLICATION_IPADDR    0  /* [APPLICATION 0] IMPLICIT OCTET STRING (SIZE (4)) */
+#define SNMP_ASN1_APPLICATION_COUNTER   1  /* [APPLICATION 1] IMPLICIT INTEGER (0..4294967295) => u32_t */
+#define SNMP_ASN1_APPLICATION_GAUGE     2  /* [APPLICATION 2] IMPLICIT INTEGER (0..4294967295) => u32_t */
+#define SNMP_ASN1_APPLICATION_TIMETICKS 3  /* [APPLICATION 3] IMPLICIT INTEGER (0..4294967295) => u32_t */
+#define SNMP_ASN1_APPLICATION_OPAQUE    4  /* [APPLICATION 4] IMPLICIT OCTET STRING */
+#define SNMP_ASN1_APPLICATION_COUNTER64 6  /* [APPLICATION 6] IMPLICIT INTEGER (0..18446744073709551615) */
+
+/* context specific (SNMP) tags (from RFC 1905) */
+#define SNMP_ASN1_CONTEXT_VARBIND_NO_SUCH_INSTANCE 1
+
+/* full ASN1 type defines */
+#define SNMP_ASN1_TYPE_END_OF_CONTENT (SNMP_ASN1_CLASS_UNIVERSAL | SNMP_ASN1_CONTENTTYPE_PRIMITIVE | SNMP_ASN1_UNIVERSAL_END_OF_CONTENT)
+#define SNMP_ASN1_TYPE_INTEGER        (SNMP_ASN1_CLASS_UNIVERSAL | SNMP_ASN1_CONTENTTYPE_PRIMITIVE | SNMP_ASN1_UNIVERSAL_INTEGER)
+#define SNMP_ASN1_TYPE_OCTET_STRING   (SNMP_ASN1_CLASS_UNIVERSAL | SNMP_ASN1_CONTENTTYPE_PRIMITIVE | SNMP_ASN1_UNIVERSAL_OCTET_STRING)
+#define SNMP_ASN1_TYPE_NULL           (SNMP_ASN1_CLASS_UNIVERSAL | SNMP_ASN1_CONTENTTYPE_PRIMITIVE | SNMP_ASN1_UNIVERSAL_NULL)
+#define SNMP_ASN1_TYPE_OBJECT_ID      (SNMP_ASN1_CLASS_UNIVERSAL | SNMP_ASN1_CONTENTTYPE_PRIMITIVE | SNMP_ASN1_UNIVERSAL_OBJECT_ID)
+#define SNMP_ASN1_TYPE_SEQUENCE       (SNMP_ASN1_CLASS_UNIVERSAL | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_UNIVERSAL_SEQUENCE_OF)
+#define SNMP_ASN1_TYPE_IPADDR         (SNMP_ASN1_CLASS_APPLICATION | SNMP_ASN1_CONTENTTYPE_PRIMITIVE | SNMP_ASN1_APPLICATION_IPADDR)
+#define SNMP_ASN1_TYPE_IPADDRESS      SNMP_ASN1_TYPE_IPADDR
+#define SNMP_ASN1_TYPE_COUNTER        (SNMP_ASN1_CLASS_APPLICATION | SNMP_ASN1_CONTENTTYPE_PRIMITIVE | SNMP_ASN1_APPLICATION_COUNTER)
+#define SNMP_ASN1_TYPE_COUNTER32      SNMP_ASN1_TYPE_COUNTER
+#define SNMP_ASN1_TYPE_GAUGE          (SNMP_ASN1_CLASS_APPLICATION | SNMP_ASN1_CONTENTTYPE_PRIMITIVE | SNMP_ASN1_APPLICATION_GAUGE)
+#define SNMP_ASN1_TYPE_GAUGE32        SNMP_ASN1_TYPE_GAUGE
+#define SNMP_ASN1_TYPE_UNSIGNED32     SNMP_ASN1_TYPE_GAUGE
+#define SNMP_ASN1_TYPE_TIMETICKS      (SNMP_ASN1_CLASS_APPLICATION | SNMP_ASN1_CONTENTTYPE_PRIMITIVE | SNMP_ASN1_APPLICATION_TIMETICKS)
+#define SNMP_ASN1_TYPE_OPAQUE         (SNMP_ASN1_CLASS_APPLICATION | SNMP_ASN1_CONTENTTYPE_PRIMITIVE | SNMP_ASN1_APPLICATION_OPAQUE)
+#if LWIP_HAVE_INT64
+#define SNMP_ASN1_TYPE_COUNTER64      (SNMP_ASN1_CLASS_APPLICATION | SNMP_ASN1_CONTENTTYPE_PRIMITIVE | SNMP_ASN1_APPLICATION_COUNTER64)
+#endif
+
+#define SNMP_VARBIND_EXCEPTION_OFFSET 0xF0
+#define SNMP_VARBIND_EXCEPTION_MASK   0x0F
+
+/** error codes predefined by SNMP prot. */
+typedef enum {
+  SNMP_ERR_NOERROR             = 0,
+/*
+outdated v1 error codes. do not use anmore!
+#define SNMP_ERR_NOSUCHNAME 2  use SNMP_ERR_NOSUCHINSTANCE instead
+#define SNMP_ERR_BADVALUE   3  use SNMP_ERR_WRONGTYPE,SNMP_ERR_WRONGLENGTH,SNMP_ERR_WRONGENCODING or SNMP_ERR_WRONGVALUE instead
+#define SNMP_ERR_READONLY   4  use SNMP_ERR_NOTWRITABLE instead
+*/
+  SNMP_ERR_GENERROR            = 5,
+  SNMP_ERR_NOACCESS            = 6,
+  SNMP_ERR_WRONGTYPE           = 7,
+  SNMP_ERR_WRONGLENGTH         = 8,
+  SNMP_ERR_WRONGENCODING       = 9,
+  SNMP_ERR_WRONGVALUE          = 10,
+  SNMP_ERR_NOCREATION          = 11,
+  SNMP_ERR_INCONSISTENTVALUE   = 12,
+  SNMP_ERR_RESOURCEUNAVAILABLE = 13,
+  SNMP_ERR_COMMITFAILED        = 14,
+  SNMP_ERR_UNDOFAILED          = 15,
+  SNMP_ERR_NOTWRITABLE         = 17,
+  SNMP_ERR_INCONSISTENTNAME    = 18,
+
+  SNMP_ERR_NOSUCHINSTANCE      = SNMP_VARBIND_EXCEPTION_OFFSET + SNMP_ASN1_CONTEXT_VARBIND_NO_SUCH_INSTANCE
+} snmp_err_t;
+
+/** internal object identifier representation */
+struct snmp_obj_id
+{
+  u8_t len;
+  u32_t id[SNMP_MAX_OBJ_ID_LEN];
+};
+
+struct snmp_obj_id_const_ref
+{
+  u8_t len;
+  const u32_t* id;
+};
+
+extern const struct snmp_obj_id_const_ref snmp_zero_dot_zero; /* administrative identifier from SNMPv2-SMI */
+
+/** SNMP variant value, used as reference in struct snmp_node_instance and table implementation */
+union snmp_variant_value
+{
+  void* ptr;
+  const void* const_ptr;
+  u32_t u32;
+  s32_t s32;
+#if LWIP_HAVE_INT64
+  u64_t u64;
+#endif
+};
+
+
+/**
+SNMP MIB node types
+ tree node is the only node the stack can process in order to walk the tree,
+ all other nodes are assumed to be leaf nodes.
+ This cannot be an enum because users may want to define their own node types.
+*/
+#define SNMP_NODE_TREE         0x00
+/* predefined leaf node types */
+#define SNMP_NODE_SCALAR       0x01
+#define SNMP_NODE_SCALAR_ARRAY 0x02
+#define SNMP_NODE_TABLE        0x03
+#define SNMP_NODE_THREADSYNC   0x04
+
+/** node "base class" layout, the mandatory fields for a node  */
+struct snmp_node
+{
+  /** one out of SNMP_NODE_TREE or any leaf node type (like SNMP_NODE_SCALAR) */
+  u8_t node_type;
+  /** the number assigned to this node which used as part of the full OID */
+  u32_t oid;
+};
+
+/** SNMP node instance access types */
+typedef enum {
+  SNMP_NODE_INSTANCE_ACCESS_READ    = 1,
+  SNMP_NODE_INSTANCE_ACCESS_WRITE   = 2,
+  SNMP_NODE_INSTANCE_READ_ONLY      = SNMP_NODE_INSTANCE_ACCESS_READ,
+  SNMP_NODE_INSTANCE_READ_WRITE     = (SNMP_NODE_INSTANCE_ACCESS_READ | SNMP_NODE_INSTANCE_ACCESS_WRITE),
+  SNMP_NODE_INSTANCE_WRITE_ONLY     = SNMP_NODE_INSTANCE_ACCESS_WRITE,
+  SNMP_NODE_INSTANCE_NOT_ACCESSIBLE = 0
+} snmp_access_t;
+
+struct snmp_node_instance;
+
+typedef s16_t (*node_instance_get_value_method)(struct snmp_node_instance*, void*);
+typedef snmp_err_t (*node_instance_set_test_method)(struct snmp_node_instance*, u16_t, void*);
+typedef snmp_err_t (*node_instance_set_value_method)(struct snmp_node_instance*, u16_t, void*);
+typedef void (*node_instance_release_method)(struct snmp_node_instance*);
+
+#define SNMP_GET_VALUE_RAW_DATA 0x4000  /* do not use 0x8000 because return value of node_instance_get_value_method is signed16 and 0x8000 would be the signed bit */
+
+/** SNMP node instance */
+struct snmp_node_instance
+{
+  /** prefilled with the node, get_instance() is called on; may be changed by user to any value to pass an arbitrary node between calls to get_instance() and get_value/test_value/set_value */
+  const struct snmp_node* node;
+  /** prefilled with the instance id requested; for get_instance() this is the exact oid requested; for get_next_instance() this is the relative starting point, stack expects relative oid of next node here */
+  struct snmp_obj_id instance_oid;
+
+  /** ASN type for this object (see snmp_asn1.h for definitions) */
+  u8_t asn1_type;
+  /** one out of instance access types defined above (SNMP_NODE_INSTANCE_READ_ONLY,...) */
+  snmp_access_t access;
+
+  /** returns object value for the given object identifier. Return values <0 to indicate an error */
+  node_instance_get_value_method get_value;
+  /** tests length and/or range BEFORE setting */
+  node_instance_set_test_method set_test;
+  /** sets object value, only called when set_test() was successful */
+  node_instance_set_value_method set_value;
+  /** called in any case when the instance is not required anymore by stack (useful for freeing memory allocated in get_instance/get_next_instance methods) */
+  node_instance_release_method release_instance;
+
+  /** reference to pass arbitrary value between calls to get_instance() and get_value/test_value/set_value */
+  union snmp_variant_value reference;
+  /** see reference (if reference is a pointer, the length of underlying data may be stored here or anything else) */
+  u32_t reference_len;
+};
+
+
+/** SNMP tree node */
+struct snmp_tree_node
+{
+  /** inherited "base class" members */
+  struct snmp_node node;
+  u16_t subnode_count;
+  const struct snmp_node* const *subnodes;
+};
+
+#define SNMP_CREATE_TREE_NODE(oid, subnodes) \
+  {{ SNMP_NODE_TREE, (oid) }, \
+  (u16_t)LWIP_ARRAYSIZE(subnodes), (subnodes) }
+
+#define SNMP_CREATE_EMPTY_TREE_NODE(oid) \
+  {{ SNMP_NODE_TREE, (oid) }, \
+  0, NULL }
+
+/** SNMP leaf node */
+struct snmp_leaf_node
+{
+  /** inherited "base class" members */
+  struct snmp_node node;
+  snmp_err_t (*get_instance)(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance);
+  snmp_err_t (*get_next_instance)(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance);
+};
+
+/** represents a single mib with its base oid and root node */
+struct snmp_mib
+{
+  const u32_t *base_oid;
+  u8_t base_oid_len;
+  const struct snmp_node *root_node;
+};
+
+#define SNMP_MIB_CREATE(oid_list, root_node) { (oid_list), (u8_t)LWIP_ARRAYSIZE(oid_list), root_node }
+
+/** OID range structure */
+struct snmp_oid_range
+{
+  u32_t min;
+  u32_t max;
+};
+
+/** checks if incoming OID length and values are in allowed ranges */
+u8_t snmp_oid_in_range(const u32_t *oid_in, u8_t oid_len, const struct snmp_oid_range *oid_ranges, u8_t oid_ranges_len);
+
+typedef enum {
+  SNMP_NEXT_OID_STATUS_SUCCESS,
+  SNMP_NEXT_OID_STATUS_NO_MATCH,
+  SNMP_NEXT_OID_STATUS_BUF_TO_SMALL
+} snmp_next_oid_status_t;
+
+/** state for next_oid_init / next_oid_check functions */
+struct snmp_next_oid_state
+{
+  const u32_t* start_oid;
+  u8_t start_oid_len;
+
+  u32_t* next_oid;
+  u8_t next_oid_len;
+  u8_t next_oid_max_len;
+
+  snmp_next_oid_status_t status;
+  void* reference;
+};
+
+void snmp_next_oid_init(struct snmp_next_oid_state *state,
+  const u32_t *start_oid, u8_t start_oid_len,
+  u32_t *next_oid_buf, u8_t next_oid_max_len);
+u8_t snmp_next_oid_precheck(struct snmp_next_oid_state *state, const u32_t *oid, u8_t oid_len);
+u8_t snmp_next_oid_check(struct snmp_next_oid_state *state, const u32_t *oid, u8_t oid_len, void* reference);
+
+void snmp_oid_assign(struct snmp_obj_id* target, const u32_t *oid, u8_t oid_len);
+void snmp_oid_combine(struct snmp_obj_id* target, const u32_t *oid1, u8_t oid1_len, const u32_t *oid2, u8_t oid2_len);
+void snmp_oid_prefix(struct snmp_obj_id* target, const u32_t *oid, u8_t oid_len);
+void snmp_oid_append(struct snmp_obj_id* target, const u32_t *oid, u8_t oid_len);
+u8_t snmp_oid_equal(const u32_t *oid1, u8_t oid1_len, const u32_t *oid2, u8_t oid2_len);
+s8_t snmp_oid_compare(const u32_t *oid1, u8_t oid1_len, const u32_t *oid2, u8_t oid2_len);
+
+#if LWIP_IPV4
+u8_t snmp_oid_to_ip4(const u32_t *oid, ip4_addr_t *ip);
+void snmp_ip4_to_oid(const ip4_addr_t *ip, u32_t *oid);
+#endif /* LWIP_IPV4 */
+#if LWIP_IPV6
+u8_t snmp_oid_to_ip6(const u32_t *oid, ip6_addr_t *ip);
+void snmp_ip6_to_oid(const ip6_addr_t *ip, u32_t *oid);
+#endif /* LWIP_IPV6 */
+#if LWIP_IPV4 || LWIP_IPV6
+u8_t snmp_ip_to_oid(const ip_addr_t *ip, u32_t *oid);
+u8_t snmp_ip_port_to_oid(const ip_addr_t *ip, u16_t port, u32_t *oid);
+
+u8_t snmp_oid_to_ip(const u32_t *oid, u8_t oid_len, ip_addr_t *ip);
+u8_t snmp_oid_to_ip_port(const u32_t *oid, u8_t oid_len, ip_addr_t *ip, u16_t *port);
+#endif /* LWIP_IPV4 || LWIP_IPV6 */
+
+struct netif;
+u8_t netif_to_num(const struct netif *netif);
+
+snmp_err_t snmp_set_test_ok(struct snmp_node_instance* instance, u16_t value_len, void* value); /* generic function which can be used if test is always successful */
+
+err_t snmp_decode_bits(const u8_t *buf, u32_t buf_len, u32_t *bit_value);
+err_t snmp_decode_truthvalue(const s32_t *asn1_value, u8_t *bool_value);
+u8_t  snmp_encode_bits(u8_t *buf, u32_t buf_len, u32_t bit_value, u8_t bit_count);
+u8_t  snmp_encode_truthvalue(s32_t *asn1_value, u32_t bool_value);
+
+struct snmp_statistics
+{
+  u32_t inpkts;
+  u32_t outpkts;
+  u32_t inbadversions;
+  u32_t inbadcommunitynames;
+  u32_t inbadcommunityuses;
+  u32_t inasnparseerrs;
+  u32_t intoobigs;
+  u32_t innosuchnames;
+  u32_t inbadvalues;
+  u32_t inreadonlys;
+  u32_t ingenerrs;
+  u32_t intotalreqvars;
+  u32_t intotalsetvars;
+  u32_t ingetrequests;
+  u32_t ingetnexts;
+  u32_t insetrequests;
+  u32_t ingetresponses;
+  u32_t intraps;
+  u32_t outtoobigs;
+  u32_t outnosuchnames;
+  u32_t outbadvalues;
+  u32_t outgenerrs;
+  u32_t outgetrequests;
+  u32_t outgetnexts;
+  u32_t outsetrequests;
+  u32_t outgetresponses;
+  u32_t outtraps;
+#if LWIP_SNMP_V3
+  u32_t unsupportedseclevels;
+  u32_t notintimewindows;
+  u32_t unknownusernames;
+  u32_t unknownengineids;
+  u32_t wrongdigests;
+  u32_t decryptionerrors;
+#endif
+};
+
+extern struct snmp_statistics snmp_stats;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_SNMP */
+
+#endif /* LWIP_HDR_APPS_SNMP_CORE_H */
diff --git a/lib/lwip/include/lwip/apps/snmp_mib2.h b/lib/lwip/include/lwip/apps/snmp_mib2.h
new file mode 100644
index 0000000000..453e5195cb
--- /dev/null
+++ b/lib/lwip/include/lwip/apps/snmp_mib2.h
@@ -0,0 +1,78 @@
+/**
+ * @file
+ * SNMP MIB2 API
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Dirk Ziegelmeier <dziegel@gmx.de>
+ *
+ */
+#ifndef LWIP_HDR_APPS_SNMP_MIB2_H
+#define LWIP_HDR_APPS_SNMP_MIB2_H
+
+#include "lwip/apps/snmp_opts.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+#if SNMP_LWIP_MIB2
+
+#include "lwip/apps/snmp_core.h"
+
+extern const struct snmp_mib mib2;
+
+#if SNMP_USE_NETCONN
+#include "lwip/apps/snmp_threadsync.h"
+void snmp_mib2_lwip_synchronizer(snmp_threadsync_called_fn fn, void* arg);
+extern struct snmp_threadsync_instance snmp_mib2_lwip_locks;
+#endif
+
+#ifndef SNMP_SYSSERVICES
+#define SNMP_SYSSERVICES ((1 << 6) | (1 << 3) | ((IP_FORWARD) << 2))
+#endif
+
+void snmp_mib2_set_sysdescr(const u8_t* str, const u16_t* len); /* read-only be definition */
+void snmp_mib2_set_syscontact(u8_t *ocstr, u16_t *ocstrlen, u16_t bufsize);
+void snmp_mib2_set_syscontact_readonly(const u8_t *ocstr, const u16_t *ocstrlen);
+void snmp_mib2_set_sysname(u8_t *ocstr, u16_t *ocstrlen, u16_t bufsize);
+void snmp_mib2_set_sysname_readonly(const u8_t *ocstr, const u16_t *ocstrlen);
+void snmp_mib2_set_syslocation(u8_t *ocstr, u16_t *ocstrlen, u16_t bufsize);
+void snmp_mib2_set_syslocation_readonly(const u8_t *ocstr, const u16_t *ocstrlen);
+
+#endif /* SNMP_LWIP_MIB2 */
+#endif /* LWIP_SNMP */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_APPS_SNMP_MIB2_H */
diff --git a/lib/lwip/include/lwip/apps/snmp_opts.h b/lib/lwip/include/lwip/apps/snmp_opts.h
new file mode 100644
index 0000000000..c892d22afa
--- /dev/null
+++ b/lib/lwip/include/lwip/apps/snmp_opts.h
@@ -0,0 +1,297 @@
+/**
+ * @file
+ * SNMP server options list
+ */
+
+/*
+ * Copyright (c) 2015 Dirk Ziegelmeier
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Dirk Ziegelmeier
+ *
+ */
+#ifndef LWIP_HDR_SNMP_OPTS_H
+#define LWIP_HDR_SNMP_OPTS_H
+
+#include "lwip/opt.h"
+
+/**
+ * @defgroup snmp_opts Options
+ * @ingroup snmp
+ * @{
+ */
+
+/**
+ * LWIP_SNMP==1: This enables the lwIP SNMP agent. UDP must be available
+ * for SNMP transport.
+ * If you want to use your own SNMP agent, leave this disabled.
+ * To integrate MIB2 of an external agent, you need to enable
+ * LWIP_MIB2_CALLBACKS and MIB2_STATS. This will give you the callbacks
+ * and statistics counters you need to get MIB2 working.
+ */
+#if !defined LWIP_SNMP || defined __DOXYGEN__
+#define LWIP_SNMP                       0
+#endif
+
+/**
+ * SNMP_USE_NETCONN: Use netconn API instead of raw API.
+ * Makes SNMP agent run in a worker thread, so blocking operations
+ * can be done in MIB calls.
+ */
+#if !defined SNMP_USE_NETCONN || defined __DOXYGEN__
+#define SNMP_USE_NETCONN           0
+#endif
+
+/**
+ * SNMP_USE_RAW: Use raw API.
+ * SNMP agent does not run in a worker thread, so blocking operations
+ * should not be done in MIB calls.
+ */
+#if !defined SNMP_USE_RAW || defined __DOXYGEN__
+#define SNMP_USE_RAW               1
+#endif
+
+#if SNMP_USE_NETCONN && SNMP_USE_RAW
+#error SNMP stack can use only one of the APIs {raw, netconn}
+#endif
+
+#if LWIP_SNMP && !SNMP_USE_NETCONN && !SNMP_USE_RAW
+#error SNMP stack needs a receive API and UDP {raw, netconn}
+#endif
+
+#if SNMP_USE_NETCONN
+/**
+ * SNMP_STACK_SIZE: Stack size of SNMP netconn worker thread
+ */
+#if !defined SNMP_STACK_SIZE || defined __DOXYGEN__
+#define SNMP_STACK_SIZE            DEFAULT_THREAD_STACKSIZE
+#endif
+
+/**
+ * SNMP_THREAD_PRIO: SNMP netconn worker thread priority
+ */
+#if !defined SNMP_THREAD_PRIO || defined __DOXYGEN__
+#define SNMP_THREAD_PRIO           DEFAULT_THREAD_PRIO
+#endif
+#endif /* SNMP_USE_NETCONN */
+
+/**
+ * SNMP_TRAP_DESTINATIONS: Number of trap destinations. At least one trap
+ * destination is required
+ */
+#if !defined SNMP_TRAP_DESTINATIONS || defined __DOXYGEN__
+#define SNMP_TRAP_DESTINATIONS          1
+#endif
+
+/**
+ * Only allow SNMP write actions that are 'safe' (e.g. disabling netifs is not
+ * a safe action and disabled when SNMP_SAFE_REQUESTS = 1).
+ * Unsafe requests are disabled by default!
+ */
+#if !defined SNMP_SAFE_REQUESTS || defined __DOXYGEN__
+#define SNMP_SAFE_REQUESTS              1
+#endif
+
+/**
+ * The maximum length of strings used.
+ */
+#if !defined SNMP_MAX_OCTET_STRING_LEN || defined __DOXYGEN__
+#define SNMP_MAX_OCTET_STRING_LEN       127
+#endif
+
+/**
+ * The maximum number of Sub ID's inside an object identifier.
+ * Indirectly this also limits the maximum depth of SNMP tree.
+ */
+#if !defined SNMP_MAX_OBJ_ID_LEN || defined __DOXYGEN__
+#define SNMP_MAX_OBJ_ID_LEN             50
+#endif
+
+#if !defined SNMP_MAX_VALUE_SIZE || defined __DOXYGEN__
+/**
+ * The minimum size of a value.
+ */
+#define SNMP_MIN_VALUE_SIZE             (2 * sizeof(u32_t*)) /* size required to store the basic types (8 bytes for counter64) */
+/**
+ * The maximum size of a value.
+ */
+#define SNMP_MAX_VALUE_SIZE             LWIP_MAX(LWIP_MAX((SNMP_MAX_OCTET_STRING_LEN), sizeof(u32_t)*(SNMP_MAX_OBJ_ID_LEN)), SNMP_MIN_VALUE_SIZE)
+#endif
+
+/**
+ * The snmp read-access community. Used for write-access and traps, too
+ * unless SNMP_COMMUNITY_WRITE or SNMP_COMMUNITY_TRAP are enabled, respectively.
+ */
+#if !defined SNMP_COMMUNITY || defined __DOXYGEN__
+#define SNMP_COMMUNITY                  "public"
+#endif
+
+/**
+ * The snmp write-access community.
+ * Set this community to "" in order to disallow any write access.
+ */
+#if !defined SNMP_COMMUNITY_WRITE || defined __DOXYGEN__
+#define SNMP_COMMUNITY_WRITE            "private"
+#endif
+
+/**
+ * The snmp community used for sending traps.
+ */
+#if !defined SNMP_COMMUNITY_TRAP || defined __DOXYGEN__
+#define SNMP_COMMUNITY_TRAP             "public"
+#endif
+
+/**
+ * The maximum length of community string.
+ * If community names shall be adjusted at runtime via snmp_set_community() calls,
+ * enter here the possible maximum length (+1 for terminating null character).
+ */
+#if !defined SNMP_MAX_COMMUNITY_STR_LEN || defined __DOXYGEN__
+#define SNMP_MAX_COMMUNITY_STR_LEN LWIP_MAX(LWIP_MAX(sizeof(SNMP_COMMUNITY), sizeof(SNMP_COMMUNITY_WRITE)), sizeof(SNMP_COMMUNITY_TRAP))
+#endif
+
+/**
+ * The OID identifiying the device. This may be the enterprise OID itself or any OID located below it in tree.
+ */
+#if !defined SNMP_DEVICE_ENTERPRISE_OID || defined __DOXYGEN__
+#define SNMP_LWIP_ENTERPRISE_OID 26381
+/**
+ * IANA assigned enterprise ID for lwIP is 26381
+ * @see http://www.iana.org/assignments/enterprise-numbers
+ *
+ * @note this enterprise ID is assigned to the lwIP project,
+ * all object identifiers living under this ID are assigned
+ * by the lwIP maintainers!
+ * @note don't change this define, use snmp_set_device_enterprise_oid()
+ *
+ * If you need to create your own private MIB you'll need
+ * to apply for your own enterprise ID with IANA:
+ * http://www.iana.org/numbers.html
+ */
+#define SNMP_DEVICE_ENTERPRISE_OID {1, 3, 6, 1, 4, 1, SNMP_LWIP_ENTERPRISE_OID}
+/**
+ * Length of SNMP_DEVICE_ENTERPRISE_OID
+ */
+#define SNMP_DEVICE_ENTERPRISE_OID_LEN 7
+#endif
+
+/**
+ * SNMP_DEBUG: Enable debugging for SNMP messages.
+ */
+#if !defined SNMP_DEBUG || defined __DOXYGEN__
+#define SNMP_DEBUG                      LWIP_DBG_OFF
+#endif
+
+/**
+ * SNMP_MIB_DEBUG: Enable debugging for SNMP MIBs.
+ */
+#if !defined SNMP_MIB_DEBUG || defined __DOXYGEN__
+#define SNMP_MIB_DEBUG                  LWIP_DBG_OFF
+#endif
+
+/**
+ * Indicates if the MIB2 implementation of LWIP SNMP stack is used.
+ */
+#if !defined SNMP_LWIP_MIB2 || defined __DOXYGEN__
+#define SNMP_LWIP_MIB2                      LWIP_SNMP
+#endif
+
+/**
+ * Value return for sysDesc field of MIB2.
+ */
+#if !defined SNMP_LWIP_MIB2_SYSDESC || defined __DOXYGEN__
+#define SNMP_LWIP_MIB2_SYSDESC              "lwIP"
+#endif
+
+/**
+ * Value return for sysName field of MIB2.
+ * To make sysName field settable, call snmp_mib2_set_sysname() to provide the necessary buffers.
+ */
+#if !defined SNMP_LWIP_MIB2_SYSNAME || defined __DOXYGEN__
+#define SNMP_LWIP_MIB2_SYSNAME              "FQDN-unk"
+#endif
+
+/**
+ * Value return for sysContact field of MIB2.
+ * To make sysContact field settable, call snmp_mib2_set_syscontact() to provide the necessary buffers.
+ */
+#if !defined SNMP_LWIP_MIB2_SYSCONTACT || defined __DOXYGEN__
+#define SNMP_LWIP_MIB2_SYSCONTACT           ""
+#endif
+
+/**
+ * Value return for sysLocation field of MIB2.
+ * To make sysLocation field settable, call snmp_mib2_set_syslocation() to provide the necessary buffers.
+ */
+#if !defined SNMP_LWIP_MIB2_SYSLOCATION || defined __DOXYGEN__
+#define SNMP_LWIP_MIB2_SYSLOCATION          ""
+#endif
+
+/**
+ * This value is used to limit the repetitions processed in GetBulk requests (value == 0 means no limitation).
+ * This may be useful to limit the load for a single request.
+ * According to SNMP RFC 1905 it is allowed to not return all requested variables from a GetBulk request if system load would be too high.
+ * so the effect is that the client will do more requests to gather all data.
+ * For the stack this could be useful in case that SNMP processing is done in TCP/IP thread. In this situation a request with many
+ * repetitions could block the thread for a longer time. Setting limit here will keep the stack more responsive.
+ */
+#if !defined SNMP_LWIP_GETBULK_MAX_REPETITIONS || defined __DOXYGEN__
+#define SNMP_LWIP_GETBULK_MAX_REPETITIONS 0
+#endif
+
+/**
+ * @}
+ */
+
+/*
+   ------------------------------------
+   ---------- SNMPv3 options ----------
+   ------------------------------------
+*/
+
+/**
+ * LWIP_SNMP_V3==1: This enables EXPERIMENTAL SNMPv3 support. LWIP_SNMP must
+ * also be enabled.
+ * THIS IS UNDER DEVELOPMENT AND SHOULD NOT BE ENABLED IN PRODUCTS.
+ */
+#ifndef LWIP_SNMP_V3
+#define LWIP_SNMP_V3               0
+#endif
+
+#ifndef LWIP_SNMP_V3_MBEDTLS
+#define LWIP_SNMP_V3_MBEDTLS       LWIP_SNMP_V3
+#endif
+
+#ifndef LWIP_SNMP_V3_CRYPTO
+#define LWIP_SNMP_V3_CRYPTO        LWIP_SNMP_V3_MBEDTLS
+#endif
+
+#ifndef LWIP_SNMP_CONFIGURE_VERSIONS
+#define LWIP_SNMP_CONFIGURE_VERSIONS 0
+#endif
+
+#endif /* LWIP_HDR_SNMP_OPTS_H */
diff --git a/lib/lwip/include/lwip/apps/snmp_scalar.h b/lib/lwip/include/lwip/apps/snmp_scalar.h
new file mode 100644
index 0000000000..40a060c640
--- /dev/null
+++ b/lib/lwip/include/lwip/apps/snmp_scalar.h
@@ -0,0 +1,113 @@
+/**
+ * @file
+ * SNMP server MIB API to implement scalar nodes
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Martin Hentschel <info@cl-soft.de>
+ *
+ */
+
+#ifndef LWIP_HDR_APPS_SNMP_SCALAR_H
+#define LWIP_HDR_APPS_SNMP_SCALAR_H
+
+#include "lwip/apps/snmp_opts.h"
+#include "lwip/apps/snmp_core.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+/** basic scalar node */
+struct snmp_scalar_node
+{
+  /** inherited "base class" members */
+  struct snmp_leaf_node node;
+  u8_t asn1_type;
+  snmp_access_t access;
+  node_instance_get_value_method get_value;
+  node_instance_set_test_method set_test;
+  node_instance_set_value_method set_value;
+};
+
+
+snmp_err_t snmp_scalar_get_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance);
+snmp_err_t snmp_scalar_get_next_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance);
+
+#define SNMP_SCALAR_CREATE_NODE(oid, access, asn1_type, get_value_method, set_test_method, set_value_method) \
+  {{{ SNMP_NODE_SCALAR, (oid) }, \
+    snmp_scalar_get_instance, \
+    snmp_scalar_get_next_instance }, \
+    (asn1_type), (access), (get_value_method), (set_test_method), (set_value_method) }
+
+#define SNMP_SCALAR_CREATE_NODE_READONLY(oid, asn1_type, get_value_method) SNMP_SCALAR_CREATE_NODE(oid, SNMP_NODE_INSTANCE_READ_ONLY, asn1_type, get_value_method, NULL, NULL)
+
+/** scalar array node - a tree node which contains scalars only as children */
+struct snmp_scalar_array_node_def
+{
+  u32_t         oid;
+  u8_t          asn1_type;
+  snmp_access_t access;
+};
+
+typedef s16_t (*snmp_scalar_array_get_value_method)(const struct snmp_scalar_array_node_def*, void*);
+typedef snmp_err_t (*snmp_scalar_array_set_test_method)(const struct snmp_scalar_array_node_def*, u16_t, void*);
+typedef snmp_err_t (*snmp_scalar_array_set_value_method)(const struct snmp_scalar_array_node_def*, u16_t, void*);
+
+/** basic scalar array node */
+struct snmp_scalar_array_node
+{
+  /** inherited "base class" members */
+  struct snmp_leaf_node node;
+  u16_t array_node_count;
+  const struct snmp_scalar_array_node_def* array_nodes;
+  snmp_scalar_array_get_value_method get_value;
+  snmp_scalar_array_set_test_method set_test;
+  snmp_scalar_array_set_value_method set_value;
+};
+
+snmp_err_t snmp_scalar_array_get_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance);
+snmp_err_t snmp_scalar_array_get_next_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance);
+
+#define SNMP_SCALAR_CREATE_ARRAY_NODE(oid, array_nodes, get_value_method, set_test_method, set_value_method) \
+  {{{ SNMP_NODE_SCALAR_ARRAY, (oid) }, \
+    snmp_scalar_array_get_instance, \
+    snmp_scalar_array_get_next_instance }, \
+    (u16_t)LWIP_ARRAYSIZE(array_nodes), (array_nodes), (get_value_method), (set_test_method), (set_value_method) }
+
+#endif /* LWIP_SNMP */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_APPS_SNMP_SCALAR_H */
diff --git a/lib/lwip/include/lwip/apps/snmp_snmpv2_framework.h b/lib/lwip/include/lwip/apps/snmp_snmpv2_framework.h
new file mode 100644
index 0000000000..47409cc201
--- /dev/null
+++ b/lib/lwip/include/lwip/apps/snmp_snmpv2_framework.h
@@ -0,0 +1,32 @@
+/*
+Generated by LwipMibCompiler
+*/
+
+#ifndef LWIP_HDR_APPS_SNMP_FRAMEWORK_MIB_H
+#define LWIP_HDR_APPS_SNMP_FRAMEWORK_MIB_H
+
+#include "lwip/apps/snmp_opts.h"
+#if LWIP_SNMP
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#include "lwip/apps/snmp_core.h"
+
+extern const struct snmp_obj_id usmNoAuthProtocol;
+extern const struct snmp_obj_id usmHMACMD5AuthProtocol;
+extern const struct snmp_obj_id usmHMACSHAAuthProtocol;
+
+extern const struct snmp_obj_id usmNoPrivProtocol;
+extern const struct snmp_obj_id usmDESPrivProtocol;
+extern const struct snmp_obj_id usmAESPrivProtocol;
+
+extern const struct snmp_mib snmpframeworkmib;
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* LWIP_SNMP */
+#endif /* LWIP_HDR_APPS_SNMP_FRAMEWORK_MIB_H */
diff --git a/lib/lwip/include/lwip/apps/snmp_snmpv2_usm.h b/lib/lwip/include/lwip/apps/snmp_snmpv2_usm.h
new file mode 100644
index 0000000000..88cfcd8ebe
--- /dev/null
+++ b/lib/lwip/include/lwip/apps/snmp_snmpv2_usm.h
@@ -0,0 +1,24 @@
+/*
+Generated by LwipMibCompiler
+*/
+
+#ifndef LWIP_HDR_APPS_SNMP_USER_BASED_SM_MIB_H
+#define LWIP_HDR_APPS_SNMP_USER_BASED_SM_MIB_H
+
+#include "lwip/apps/snmp_opts.h"
+#if LWIP_SNMP
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#include "lwip/apps/snmp_core.h"
+
+extern const struct snmp_mib snmpusmmib;
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* LWIP_SNMP */
+#endif /* LWIP_HDR_APPS_SNMP_USER_BASED_SM_MIB_H */
diff --git a/lib/lwip/include/lwip/apps/snmp_table.h b/lib/lwip/include/lwip/apps/snmp_table.h
new file mode 100644
index 0000000000..4988b51c25
--- /dev/null
+++ b/lib/lwip/include/lwip/apps/snmp_table.h
@@ -0,0 +1,134 @@
+/**
+ * @file
+ * SNMP server MIB API to implement table nodes
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Martin Hentschel <info@cl-soft.de>
+ *
+ */
+
+#ifndef LWIP_HDR_APPS_SNMP_TABLE_H
+#define LWIP_HDR_APPS_SNMP_TABLE_H
+
+#include "lwip/apps/snmp_opts.h"
+#include "lwip/apps/snmp_core.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+/** default (customizable) read/write table */
+struct snmp_table_col_def
+{
+  u32_t index;
+  u8_t asn1_type;
+  snmp_access_t access;
+};
+
+/** table node */
+struct snmp_table_node
+{
+  /** inherited "base class" members */
+  struct snmp_leaf_node node;
+  u16_t column_count;
+  const struct snmp_table_col_def* columns;
+  snmp_err_t (*get_cell_instance)(const u32_t* column, const u32_t* row_oid, u8_t row_oid_len, struct snmp_node_instance* cell_instance);
+  snmp_err_t (*get_next_cell_instance)(const u32_t* column, struct snmp_obj_id* row_oid, struct snmp_node_instance* cell_instance);
+  /** returns object value for the given object identifier */
+  node_instance_get_value_method get_value;
+  /** tests length and/or range BEFORE setting */
+  node_instance_set_test_method set_test;
+  /** sets object value, only called when set_test() was successful */
+  node_instance_set_value_method set_value;
+};
+
+snmp_err_t snmp_table_get_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance);
+snmp_err_t snmp_table_get_next_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance);
+
+#define SNMP_TABLE_CREATE(oid, columns, get_cell_instance_method, get_next_cell_instance_method, get_value_method, set_test_method, set_value_method) \
+  {{{ SNMP_NODE_TABLE, (oid) }, \
+  snmp_table_get_instance, \
+  snmp_table_get_next_instance }, \
+  (u16_t)LWIP_ARRAYSIZE(columns), (columns), \
+  (get_cell_instance_method), (get_next_cell_instance_method), \
+  (get_value_method), (set_test_method), (set_value_method)}
+
+#define SNMP_TABLE_GET_COLUMN_FROM_OID(oid) ((oid)[1]) /* first array value is (fixed) row entry (fixed to 1) and 2nd value is column, follow3ed by instance */
+
+
+/** simple read-only table */
+typedef enum {
+  SNMP_VARIANT_VALUE_TYPE_U32,
+  SNMP_VARIANT_VALUE_TYPE_S32,
+  SNMP_VARIANT_VALUE_TYPE_PTR,
+  SNMP_VARIANT_VALUE_TYPE_CONST_PTR
+} snmp_table_column_data_type_t;
+
+struct snmp_table_simple_col_def
+{
+  u32_t index;
+  u8_t asn1_type;
+  snmp_table_column_data_type_t data_type; /* depending of what union member is used to store the value*/
+};
+
+/** simple read-only table node */
+struct snmp_table_simple_node
+{
+  /* inherited "base class" members */
+  struct snmp_leaf_node node;
+  u16_t column_count;
+  const struct snmp_table_simple_col_def* columns;
+  snmp_err_t (*get_cell_value)(const u32_t* column, const u32_t* row_oid, u8_t row_oid_len, union snmp_variant_value* value, u32_t* value_len);
+  snmp_err_t (*get_next_cell_instance_and_value)(const u32_t* column, struct snmp_obj_id* row_oid, union snmp_variant_value* value, u32_t* value_len);
+};
+
+snmp_err_t snmp_table_simple_get_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance);
+snmp_err_t snmp_table_simple_get_next_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance);
+
+#define SNMP_TABLE_CREATE_SIMPLE(oid, columns, get_cell_value_method, get_next_cell_instance_and_value_method) \
+  {{{ SNMP_NODE_TABLE, (oid) }, \
+  snmp_table_simple_get_instance, \
+  snmp_table_simple_get_next_instance }, \
+  (u16_t)LWIP_ARRAYSIZE(columns), (columns), (get_cell_value_method), (get_next_cell_instance_and_value_method) }
+
+s16_t snmp_table_extract_value_from_s32ref(struct snmp_node_instance* instance, void* value);
+s16_t snmp_table_extract_value_from_u32ref(struct snmp_node_instance* instance, void* value);
+s16_t snmp_table_extract_value_from_refconstptr(struct snmp_node_instance* instance, void* value);
+
+#endif /* LWIP_SNMP */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_APPS_SNMP_TABLE_H */
diff --git a/lib/lwip/include/lwip/apps/snmp_threadsync.h b/lib/lwip/include/lwip/apps/snmp_threadsync.h
new file mode 100644
index 0000000000..76f11182c2
--- /dev/null
+++ b/lib/lwip/include/lwip/apps/snmp_threadsync.h
@@ -0,0 +1,114 @@
+/**
+ * @file
+ * SNMP server MIB API to implement thread synchronization
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Dirk Ziegelmeier <dziegel@gmx.de>
+ *
+ */
+
+#ifndef LWIP_HDR_APPS_SNMP_THREADSYNC_H
+#define LWIP_HDR_APPS_SNMP_THREADSYNC_H
+
+#include "lwip/apps/snmp_opts.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/apps/snmp_core.h"
+#include "lwip/sys.h"
+
+typedef void (*snmp_threadsync_called_fn)(void* arg);
+typedef void (*snmp_threadsync_synchronizer_fn)(snmp_threadsync_called_fn fn, void* arg);
+
+
+/** Thread sync runtime data. For internal usage only. */
+struct threadsync_data
+{
+  union {
+    snmp_err_t err;
+    s16_t s16;
+  } retval;
+  union {
+    const u32_t *root_oid;
+    void *value;
+  } arg1;
+  union {
+    u8_t root_oid_len;
+    u16_t len;
+  } arg2;
+  const struct snmp_threadsync_node *threadsync_node;
+  struct snmp_node_instance proxy_instance;
+};
+
+/** Thread sync instance. Needed EXACTLY once for every thread to be synced into. */
+struct snmp_threadsync_instance
+{
+  sys_sem_t                       sem;
+  sys_mutex_t                     sem_usage_mutex;
+  snmp_threadsync_synchronizer_fn sync_fn;
+  struct threadsync_data          data;
+};
+
+/** SNMP thread sync proxy leaf node */
+struct snmp_threadsync_node
+{
+  /* inherited "base class" members */
+  struct snmp_leaf_node           node;
+
+  const struct snmp_leaf_node     *target;
+  struct snmp_threadsync_instance *instance;
+};
+
+snmp_err_t snmp_threadsync_get_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance);
+snmp_err_t snmp_threadsync_get_next_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance);
+
+/** Create thread sync proxy node */
+#define SNMP_CREATE_THREAD_SYNC_NODE(oid, target_leaf_node, threadsync_instance) \
+  {{{ SNMP_NODE_THREADSYNC, (oid) }, \
+    snmp_threadsync_get_instance, \
+    snmp_threadsync_get_next_instance }, \
+    (target_leaf_node), \
+    (threadsync_instance) }
+
+/** Create thread sync instance data */
+void snmp_threadsync_init(struct snmp_threadsync_instance *instance, snmp_threadsync_synchronizer_fn sync_fn);
+
+#endif /* LWIP_SNMP */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_APPS_SNMP_THREADSYNC_H */
diff --git a/lib/lwip/include/lwip/apps/snmpv3.h b/lib/lwip/include/lwip/apps/snmpv3.h
new file mode 100644
index 0000000000..e0dda6499b
--- /dev/null
+++ b/lib/lwip/include/lwip/apps/snmpv3.h
@@ -0,0 +1,114 @@
+/**
+ * @file
+ * Additional SNMPv3 functionality RFC3414 and RFC3826.
+ */
+
+/*
+ * Copyright (c) 2016 Elias Oenal.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Elias Oenal <lwip@eliasoenal.com>
+ */
+
+#ifndef LWIP_HDR_APPS_SNMP_V3_H
+#define LWIP_HDR_APPS_SNMP_V3_H
+
+#include "lwip/apps/snmp_opts.h"
+#include "lwip/err.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if LWIP_SNMP && LWIP_SNMP_V3
+
+typedef enum
+{
+  SNMP_V3_AUTH_ALGO_INVAL = 0,
+  SNMP_V3_AUTH_ALGO_MD5   = 1,
+  SNMP_V3_AUTH_ALGO_SHA   = 2
+} snmpv3_auth_algo_t;
+
+typedef enum
+{
+  SNMP_V3_PRIV_ALGO_INVAL = 0,
+  SNMP_V3_PRIV_ALGO_DES   = 1,
+  SNMP_V3_PRIV_ALGO_AES   = 2
+} snmpv3_priv_algo_t;
+
+typedef enum
+{
+  SNMP_V3_USER_STORAGETYPE_OTHER       = 1,
+  SNMP_V3_USER_STORAGETYPE_VOLATILE    = 2,
+  SNMP_V3_USER_STORAGETYPE_NONVOLATILE = 3,
+  SNMP_V3_USER_STORAGETYPE_PERMANENT   = 4,
+  SNMP_V3_USER_STORAGETYPE_READONLY    = 5
+} snmpv3_user_storagetype_t;
+
+/*
+ * The following callback functions must be implemented by the application.
+ * There is a dummy implementation in snmpv3_dummy.c.
+ */
+
+void snmpv3_get_engine_id(const char **id, u8_t *len);
+err_t snmpv3_set_engine_id(const char* id, u8_t len);
+
+u32_t snmpv3_get_engine_boots(void);
+void snmpv3_set_engine_boots(u32_t boots);
+
+u32_t snmpv3_get_engine_time(void);
+void snmpv3_reset_engine_time(void);
+
+err_t snmpv3_get_user(const char* username, snmpv3_auth_algo_t *auth_algo, u8_t *auth_key, snmpv3_priv_algo_t *priv_algo, u8_t *priv_key);
+u8_t snmpv3_get_amount_of_users(void);
+err_t snmpv3_get_user_storagetype(const char *username, snmpv3_user_storagetype_t *storagetype);
+err_t snmpv3_get_username(char *username, u8_t index);
+
+/* The following functions are provided by the SNMPv3 agent */
+
+void snmpv3_engine_id_changed(void);
+s32_t snmpv3_get_engine_time_internal(void);
+
+void snmpv3_password_to_key_md5(
+    const u8_t *password,     /* IN */
+    size_t      passwordlen,  /* IN */
+    const u8_t *engineID,     /* IN  - pointer to snmpEngineID  */
+    u8_t        engineLength, /* IN  - length of snmpEngineID */
+    u8_t       *key);         /* OUT - pointer to caller 16-octet buffer */
+
+void snmpv3_password_to_key_sha(
+    const u8_t *password,     /* IN */
+    size_t      passwordlen,  /* IN */
+    const u8_t *engineID,     /* IN  - pointer to snmpEngineID  */
+    u8_t        engineLength, /* IN  - length of snmpEngineID */
+    u8_t       *key);         /* OUT - pointer to caller 20-octet buffer */
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_APPS_SNMP_V3_H */
diff --git a/lib/lwip/include/lwip/apps/sntp.h b/lib/lwip/include/lwip/apps/sntp.h
new file mode 100644
index 0000000000..11dacce96e
--- /dev/null
+++ b/lib/lwip/include/lwip/apps/sntp.h
@@ -0,0 +1,81 @@
+/**
+ * @file
+ * SNTP client API
+ */
+
+/*
+ * Copyright (c) 2007-2009 Frédéric Bernon, Simon Goldschmidt
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Frédéric Bernon, Simon Goldschmidt
+ *
+ */
+#ifndef LWIP_HDR_APPS_SNTP_H
+#define LWIP_HDR_APPS_SNTP_H
+
+#include "lwip/apps/sntp_opts.h"
+#include "lwip/ip_addr.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* SNTP operating modes: default is to poll using unicast.
+   The mode has to be set before calling sntp_init(). */
+#define SNTP_OPMODE_POLL            0
+#define SNTP_OPMODE_LISTENONLY      1
+void sntp_setoperatingmode(u8_t operating_mode);
+u8_t sntp_getoperatingmode(void);
+
+void sntp_init(void);
+void sntp_stop(void);
+u8_t sntp_enabled(void);
+
+void sntp_setserver(u8_t idx, const ip_addr_t *addr);
+const ip_addr_t* sntp_getserver(u8_t idx);
+u8_t sntp_getkodreceived(u8_t idx);
+
+#if SNTP_MONITOR_SERVER_REACHABILITY
+u8_t sntp_getreachability(u8_t idx);
+#endif /* SNTP_MONITOR_SERVER_REACHABILITY */
+
+#if SNTP_SERVER_DNS
+void sntp_setservername(u8_t idx, const char *server);
+const char *sntp_getservername(u8_t idx);
+#endif /* SNTP_SERVER_DNS */
+
+#if SNTP_GET_SERVERS_FROM_DHCP || SNTP_GET_SERVERS_FROM_DHCPV6
+void sntp_servermode_dhcp(int set_servers_from_dhcp);
+#else /* SNTP_GET_SERVERS_FROM_DHCP || SNTP_GET_SERVERS_FROM_DHCPV6 */
+#define sntp_servermode_dhcp(x)
+#endif /* SNTP_GET_SERVERS_FROM_DHCP || SNTP_GET_SERVERS_FROM_DHCPV6 */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_APPS_SNTP_H */
diff --git a/lib/lwip/include/lwip/apps/sntp_opts.h b/lib/lwip/include/lwip/apps/sntp_opts.h
new file mode 100644
index 0000000000..cb62771648
--- /dev/null
+++ b/lib/lwip/include/lwip/apps/sntp_opts.h
@@ -0,0 +1,215 @@
+/**
+ * @file
+ * SNTP client options list
+ */
+
+/*
+ * Copyright (c) 2007-2009 Frédéric Bernon, Simon Goldschmidt
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Frédéric Bernon, Simon Goldschmidt
+ *
+ */
+#ifndef LWIP_HDR_APPS_SNTP_OPTS_H
+#define LWIP_HDR_APPS_SNTP_OPTS_H
+
+#include "lwip/opt.h"
+#include "lwip/prot/iana.h"
+
+/**
+ * @defgroup sntp_opts Options
+ * @ingroup sntp
+ * @{
+ */
+
+/** SNTP macro to change system time in seconds
+ * Define SNTP_SET_SYSTEM_TIME_US(sec, us) to set the time in microseconds
+ * instead of this one if you need the additional precision. Alternatively,
+ * define SNTP_SET_SYSTEM_TIME_NTP(sec, frac) in order to work with native
+ * NTP timestamps instead.
+ */
+#if !defined SNTP_SET_SYSTEM_TIME || defined __DOXYGEN__
+#define SNTP_SET_SYSTEM_TIME(sec)   LWIP_UNUSED_ARG(sec)
+#endif
+
+/** The maximum number of SNTP servers that can be set */
+#if !defined SNTP_MAX_SERVERS || defined __DOXYGEN__
+#define SNTP_MAX_SERVERS           LWIP_DHCP_MAX_NTP_SERVERS
+#endif
+
+/** Set this to 1 to implement the callback function called by dhcp when
+ * NTP servers are received. */
+#if !defined SNTP_GET_SERVERS_FROM_DHCP || defined __DOXYGEN__
+#define SNTP_GET_SERVERS_FROM_DHCP LWIP_DHCP_GET_NTP_SRV
+#endif
+
+/** Set this to 1 to implement the callback function called by dhcpv6 when
+ * NTP servers are received. */
+#if !defined SNTP_GET_SERVERS_FROM_DHCPV6 || defined __DOXYGEN__
+#define SNTP_GET_SERVERS_FROM_DHCPV6 LWIP_DHCP6_GET_NTP_SRV
+#endif
+
+/** Set this to 1 to support DNS names (or IP address strings) to set sntp servers
+ * One server address/name can be defined as default if SNTP_SERVER_DNS == 1:
+ * \#define SNTP_SERVER_ADDRESS "pool.ntp.org"
+ */
+#if !defined SNTP_SERVER_DNS || defined __DOXYGEN__
+#define SNTP_SERVER_DNS            0
+#endif
+
+/**
+ * SNTP_DEBUG: Enable debugging for SNTP.
+ */
+#if !defined SNTP_DEBUG || defined __DOXYGEN__
+#define SNTP_DEBUG                  LWIP_DBG_OFF
+#endif
+
+/** SNTP server port */
+#if !defined SNTP_PORT || defined __DOXYGEN__
+#define SNTP_PORT                   LWIP_IANA_PORT_SNTP
+#endif
+
+/** Sanity check:
+ * Define this to
+ * - 0 to turn off sanity checks (default; smaller code)
+ * - >= 1 to check address and port of the response packet to ensure the
+ *        response comes from the server we sent the request to.
+ * - >= 2 to check returned Originate Timestamp against Transmit Timestamp
+ *        sent to the server (to ensure response to older request).
+ * - >= 3 @todo: discard reply if any of the VN, Stratum, or Transmit Timestamp
+ *        fields is 0 or the Mode field is not 4 (unicast) or 5 (broadcast).
+ * - >= 4 @todo: to check that the Root Delay and Root Dispersion fields are each
+ *        greater than or equal to 0 and less than infinity, where infinity is
+ *        currently a cozy number like one second. This check avoids using a
+ *        server whose synchronization source has expired for a very long time.
+ */
+#if !defined SNTP_CHECK_RESPONSE || defined __DOXYGEN__
+#define SNTP_CHECK_RESPONSE         0
+#endif
+
+/** Enable round-trip delay compensation.
+ * Compensate for the round-trip delay by calculating the clock offset from
+ * the originate, receive, transmit and destination timestamps, as per RFC.
+ *
+ * The calculation requires compiler support for 64-bit integers. Also, either
+ * SNTP_SET_SYSTEM_TIME_US or SNTP_SET_SYSTEM_TIME_NTP has to be implemented
+ * for setting the system clock with sub-second precision. Likewise, either
+ * SNTP_GET_SYSTEM_TIME or SNTP_GET_SYSTEM_TIME_NTP needs to be implemented
+ * with sub-second precision.
+ *
+ * Although not strictly required, it makes sense to combine this option with
+ * SNTP_CHECK_RESPONSE >= 2 for sanity-checking of the received timestamps.
+ * Also, in order for the round-trip calculation to work, the difference
+ * between the local clock and the NTP server clock must not be larger than
+ * about 34 years. If that limit is exceeded, the implementation will fall back
+ * to setting the clock without compensation. In order to ensure that the local
+ * clock is always within the permitted range for compensation, even at first
+ * try, it may be necessary to store at least the current year in non-volatile
+ * memory.
+ */
+#if !defined SNTP_COMP_ROUNDTRIP || defined __DOXYGEN__
+#define SNTP_COMP_ROUNDTRIP         0
+#endif
+
+/** According to the RFC, this shall be a random delay
+ * between 1 and 5 minutes (in milliseconds) to prevent load peaks.
+ * This can be defined to a random generation function,
+ * which must return the delay in milliseconds as u32_t.
+ * Turned off by default.
+ */
+#if !defined SNTP_STARTUP_DELAY || defined __DOXYGEN__
+#ifdef LWIP_RAND
+#define SNTP_STARTUP_DELAY          1
+#else
+#define SNTP_STARTUP_DELAY          0
+#endif
+#endif
+
+/** If you want the startup delay to be a function, define this
+ * to a function (including the brackets) and define SNTP_STARTUP_DELAY to 1.
+ */
+#if !defined SNTP_STARTUP_DELAY_FUNC || defined __DOXYGEN__
+#define SNTP_STARTUP_DELAY_FUNC     (LWIP_RAND() % 5000)
+#endif
+
+/** SNTP receive timeout - in milliseconds
+ * Also used as retry timeout - this shouldn't be too low.
+ * Default is 15 seconds. Must not be beolw 15 seconds by specification (i.e. 15000)
+ */
+#if !defined SNTP_RECV_TIMEOUT || defined __DOXYGEN__
+#define SNTP_RECV_TIMEOUT           15000
+#endif
+
+/** SNTP update delay - in milliseconds
+ * Default is 1 hour. Must not be beolw 60 seconds by specification (i.e. 60000)
+ */
+#if !defined SNTP_UPDATE_DELAY || defined __DOXYGEN__
+#define SNTP_UPDATE_DELAY           3600000
+#endif
+
+/** SNTP macro to get system time, used with SNTP_CHECK_RESPONSE >= 2
+ * to send in request and compare in response. Also used for round-trip
+ * delay compensation if SNTP_COMP_ROUNDTRIP != 0.
+ * Alternatively, define SNTP_GET_SYSTEM_TIME_NTP(sec, frac) in order to
+ * work with native NTP timestamps instead.
+ */
+#if !defined SNTP_GET_SYSTEM_TIME || defined __DOXYGEN__
+#define SNTP_GET_SYSTEM_TIME(sec, us)     do { (sec) = 0; (us) = 0; } while(0)
+#endif
+
+/** Default retry timeout (in milliseconds) if the response
+ * received is invalid.
+ * This is doubled with each retry until SNTP_RETRY_TIMEOUT_MAX is reached.
+ */
+#if !defined SNTP_RETRY_TIMEOUT || defined __DOXYGEN__
+#define SNTP_RETRY_TIMEOUT          SNTP_RECV_TIMEOUT
+#endif
+
+/** Maximum retry timeout (in milliseconds). */
+#if !defined SNTP_RETRY_TIMEOUT_MAX || defined __DOXYGEN__
+#define SNTP_RETRY_TIMEOUT_MAX      (SNTP_RETRY_TIMEOUT * 10)
+#endif
+
+/** Increase retry timeout with every retry sent
+ * Default is on to conform to RFC.
+ */
+#if !defined SNTP_RETRY_TIMEOUT_EXP || defined __DOXYGEN__
+#define SNTP_RETRY_TIMEOUT_EXP      1
+#endif
+
+/** Keep a reachability shift register per server
+ * Default is on to conform to RFC.
+ */
+#if !defined SNTP_MONITOR_SERVER_REACHABILITY || defined __DOXYGEN__
+#define SNTP_MONITOR_SERVER_REACHABILITY 1
+#endif
+
+/**
+ * @}
+ */
+
+#endif /* LWIP_HDR_APPS_SNTP_OPTS_H */
diff --git a/lib/lwip/include/lwip/apps/tftp_client.h b/lib/lwip/include/lwip/apps/tftp_client.h
new file mode 100644
index 0000000000..24dbda6a8c
--- /dev/null
+++ b/lib/lwip/include/lwip/apps/tftp_client.h
@@ -0,0 +1,50 @@
+/**
+ *
+ * @file tftp_client.h
+ * TFTP client header
+ *
+ */
+
+/*
+ * Redistribution and use in source and binary forms, with or without
+ * modification,are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ */
+
+#ifndef LWIP_HDR_APPS_TFTP_CLIENT_H
+#define LWIP_HDR_APPS_TFTP_CLIENT_H
+
+#include "lwip/apps/tftp_common.h"
+
+enum tftp_transfer_mode {
+  TFTP_MODE_OCTET,
+  TFTP_MODE_NETASCII,
+  TFTP_MODE_BINARY /* used in old versions only */
+};
+
+err_t tftp_init_client(const struct tftp_context* ctx);
+err_t tftp_get(void* handle, const ip_addr_t *addr, u16_t port, const char* fname, enum tftp_transfer_mode mode);
+err_t tftp_put(void* handle, const ip_addr_t *addr, u16_t port, const char* fname, enum tftp_transfer_mode mode);
+
+#endif /* LWIP_HDR_APPS_TFTP_CLIENT_H */
diff --git a/lib/lwip/include/lwip/apps/tftp_common.h b/lib/lwip/include/lwip/apps/tftp_common.h
new file mode 100644
index 0000000000..4bc2c17347
--- /dev/null
+++ b/lib/lwip/include/lwip/apps/tftp_common.h
@@ -0,0 +1,108 @@
+/**
+ *
+ * @file tftp_common.h
+ *
+ * @author   Logan Gunthorpe <logang@deltatee.com>
+ *
+ * @brief    Trivial File Transfer Protocol (RFC 1350)
+ *
+ * Copyright (c) Deltatee Enterprises Ltd. 2013
+ * All rights reserved.
+ *
+ */
+
+/*
+ * Redistribution and use in source and binary forms, with or without
+ * modification,are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Author: Logan Gunthorpe <logang@deltatee.com>
+ *
+ */
+
+#ifndef LWIP_HDR_APPS_TFTP_COMMON_H
+#define LWIP_HDR_APPS_TFTP_COMMON_H
+
+#include "lwip/apps/tftp_opts.h"
+#include "lwip/err.h"
+#include "lwip/pbuf.h"
+#include "lwip/ip_addr.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** @ingroup tftp
+ * TFTP context containing callback functions for TFTP transfers
+ */
+struct tftp_context {
+  /**
+   * Open file for read/write (server mode only).
+   * @param fname Filename
+   * @param mode Mode string from TFTP RFC 1350 (netascii, octet, mail)
+   * @param write Flag indicating read (0) or write (!= 0) access
+   * @returns File handle supplied to other functions
+   */
+  void* (*open)(const char* fname, const char* mode, u8_t write);
+  /**
+   * Close file handle
+   * @param handle File handle returned by open()/tftp_put()/tftp_get()
+   */
+  void (*close)(void* handle);
+  /**
+   * Read from file
+   * @param handle File handle returned by open()/tftp_put()/tftp_get()
+   * @param buf Target buffer to copy read data to
+   * @param bytes Number of bytes to copy to buf
+   * @returns &gt;= 0: Success; &lt; 0: Error
+   */
+  int (*read)(void* handle, void* buf, int bytes);
+  /**
+   * Write to file
+   * @param handle File handle returned by open()/tftp_put()/tftp_get()
+   * @param pbuf PBUF adjusted such that payload pointer points
+   *             to the beginning of write data. In other words,
+   *             TFTP headers are stripped off.
+   * @returns &gt;= 0: Success; &lt; 0: Error
+   */
+  int (*write)(void* handle, struct pbuf* p);
+  /**
+   * Error indication from client or response from server
+   * @param handle File handle set by open()/tftp_get()/tftp_put()
+   * @param err error code from client or server
+   * @param msg error message from client or server
+   * @param size size of msg
+   */
+  void (*error)(void* handle, int err, const char* msg, int size);
+};
+
+#define LWIP_TFTP_MODE_SERVER       0x01
+#define LWIP_TFTP_MODE_CLIENT       0x02
+#define LWIP_TFTP_MODE_CLIENTSERVER (LWIP_TFTP_MODE_SERVER | LWIP_TFTP_MODE_CLIENT)
+
+err_t tftp_init_common(u8_t mode, const struct tftp_context* ctx);
+void tftp_cleanup(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_APPS_TFTP_COMMON_H */
diff --git a/lib/lwip/include/lwip/apps/tftp_opts.h b/lib/lwip/include/lwip/apps/tftp_opts.h
new file mode 100644
index 0000000000..509fabd679
--- /dev/null
+++ b/lib/lwip/include/lwip/apps/tftp_opts.h
@@ -0,0 +1,106 @@
+/**
+ *
+ * @file tftp_opts.h
+ *
+ * @author   Logan Gunthorpe <logang@deltatee.com>
+ *
+ * @brief    Trivial File Transfer Protocol (RFC 1350) implementation options
+ *
+ * Copyright (c) Deltatee Enterprises Ltd. 2013
+ * All rights reserved.
+ *
+ */
+
+/*
+ * Redistribution and use in source and binary forms, with or without
+ * modification,are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Author: Logan Gunthorpe <logang@deltatee.com>
+ *
+ */
+
+#ifndef LWIP_HDR_APPS_TFTP_OPTS_H
+#define LWIP_HDR_APPS_TFTP_OPTS_H
+
+#include "lwip/opt.h"
+#include "lwip/prot/iana.h"
+
+/**
+ * @defgroup tftp_opts Options
+ * @ingroup tftp
+ * @{
+ */
+
+/**
+ * Enable TFTP debug messages
+ */
+#if !defined TFTP_DEBUG || defined __DOXYGEN__
+#define TFTP_DEBUG            LWIP_DBG_OFF
+#endif
+
+/**
+ * TFTP server port
+ */
+#if !defined TFTP_PORT || defined __DOXYGEN__
+#define TFTP_PORT             LWIP_IANA_PORT_TFTP
+#endif
+
+/**
+ * TFTP timeout
+ */
+#if !defined TFTP_TIMEOUT_MSECS || defined __DOXYGEN__
+#define TFTP_TIMEOUT_MSECS    10000
+#endif
+
+/**
+ * Max. number of retries when a file is read from server
+ */
+#if !defined TFTP_MAX_RETRIES || defined __DOXYGEN__
+#define TFTP_MAX_RETRIES      5
+#endif
+
+/**
+ * TFTP timer cyclic interval
+ */
+#if !defined TFTP_TIMER_MSECS || defined __DOXYGEN__
+#define TFTP_TIMER_MSECS      (TFTP_TIMEOUT_MSECS / 10)
+#endif
+
+/**
+ * Max. length of TFTP filename
+ */
+#if !defined TFTP_MAX_FILENAME_LEN || defined __DOXYGEN__
+#define TFTP_MAX_FILENAME_LEN 20
+#endif
+
+/**
+ * Max. length of TFTP mode
+ */
+#if !defined TFTP_MAX_MODE_LEN || defined __DOXYGEN__
+#define TFTP_MAX_MODE_LEN     10
+#endif
+
+/**
+ * @}
+ */
+
+#endif /* LWIP_HDR_APPS_TFTP_OPTS_H */
diff --git a/lib/lwip/include/lwip/apps/tftp_server.h b/lib/lwip/include/lwip/apps/tftp_server.h
new file mode 100644
index 0000000000..a5769ce26b
--- /dev/null
+++ b/lib/lwip/include/lwip/apps/tftp_server.h
@@ -0,0 +1,42 @@
+/**
+ *
+ * @file tftp_server.h
+ * TFTP server header
+ *
+ */
+
+/*
+ * Redistribution and use in source and binary forms, with or without
+ * modification,are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ */
+
+#ifndef LWIP_HDR_APPS_TFTP_SERVER_H
+#define LWIP_HDR_APPS_TFTP_SERVER_H
+
+#include "lwip/apps/tftp_common.h"
+
+err_t tftp_init_server(const struct tftp_context* ctx);
+
+#endif /* LWIP_HDR_APPS_TFTP_SERVER_H */
diff --git a/lib/lwip/include/lwip/arch.h b/lib/lwip/include/lwip/arch.h
new file mode 100644
index 0000000000..596c1c9e06
--- /dev/null
+++ b/lib/lwip/include/lwip/arch.h
@@ -0,0 +1,402 @@
+/**
+ * @file
+ * Support for different processor and compiler architectures
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef LWIP_HDR_ARCH_H
+#define LWIP_HDR_ARCH_H
+
+#ifndef LITTLE_ENDIAN
+#define LITTLE_ENDIAN 1234
+#endif
+
+#ifndef BIG_ENDIAN
+#define BIG_ENDIAN 4321
+#endif
+
+#include "arch/cc.h"
+
+/**
+ * @defgroup compiler_abstraction Compiler/platform abstraction
+ * @ingroup sys_layer
+ * All defines related to this section must not be placed in lwipopts.h,
+ * but in arch/cc.h!
+ * If the compiler does not provide memset() this file must include a
+ * definition of it, or include a file which defines it.
+ * These options cannot be \#defined in lwipopts.h since they are not options
+ * of lwIP itself, but options of the lwIP port to your system.
+ * @{
+ */
+
+/** Define the byte order of the system.
+ * Needed for conversion of network data to host byte order.
+ * Allowed values: LITTLE_ENDIAN and BIG_ENDIAN
+ */
+#ifndef BYTE_ORDER
+#define BYTE_ORDER LITTLE_ENDIAN
+#endif
+
+/** Define random number generator function of your system */
+#ifdef __DOXYGEN__
+#define LWIP_RAND() ((u32_t)rand())
+#endif
+
+/** Platform specific diagnostic output.<br>
+ * Note the default implementation pulls in printf, which may
+ * in turn pull in a lot of standard library code. In resource-constrained
+ * systems, this should be defined to something less resource-consuming.
+ */
+#ifndef LWIP_PLATFORM_DIAG
+#define LWIP_PLATFORM_DIAG(x) do {printf x;} while(0)
+#include <stdio.h>
+#include <stdlib.h>
+#endif
+
+/** Platform specific assertion handling.<br>
+ * Note the default implementation pulls in printf, fflush and abort, which may
+ * in turn pull in a lot of standard library code. In resource-constrained
+ * systems, this should be defined to something less resource-consuming.
+ */
+#ifndef LWIP_PLATFORM_ASSERT
+#define LWIP_PLATFORM_ASSERT(x) do {printf("Assertion \"%s\" failed at line %d in %s\n", \
+                                     x, __LINE__, __FILE__); fflush(NULL); abort();} while(0)
+#include <stdio.h>
+#include <stdlib.h>
+#endif
+
+/** Define this to 1 in arch/cc.h of your port if you do not want to
+ * include stddef.h header to get size_t. You need to typedef size_t
+ * by yourself in this case.
+ */
+#ifndef LWIP_NO_STDDEF_H
+#define LWIP_NO_STDDEF_H 0
+#endif
+
+#if !LWIP_NO_STDDEF_H
+#include <stddef.h> /* for size_t */
+#endif
+
+/** Define this to 1 in arch/cc.h of your port if your compiler does not provide
+ * the stdint.h header. You need to typedef the generic types listed in
+ * lwip/arch.h yourself in this case (u8_t, u16_t...).
+ */
+#ifndef LWIP_NO_STDINT_H
+#define LWIP_NO_STDINT_H 0
+#endif
+
+/* Define generic types used in lwIP */
+#if !LWIP_NO_STDINT_H
+#include <stdint.h>
+/* stdint.h is C99 which should also provide support for 64-bit integers */
+#if !defined(LWIP_HAVE_INT64) && defined(UINT64_MAX)
+#define LWIP_HAVE_INT64 1
+#endif
+typedef uint8_t   u8_t;
+typedef int8_t    s8_t;
+typedef uint16_t  u16_t;
+typedef int16_t   s16_t;
+typedef uint32_t  u32_t;
+typedef int32_t   s32_t;
+#if LWIP_HAVE_INT64
+typedef uint64_t  u64_t;
+typedef int64_t   s64_t;
+#endif
+typedef uintptr_t mem_ptr_t;
+#endif
+
+/** Define this to 1 in arch/cc.h of your port if your compiler does not provide
+ * the inttypes.h header. You need to define the format strings listed in
+ * lwip/arch.h yourself in this case (X8_F, U16_F...).
+ */
+#ifndef LWIP_NO_INTTYPES_H
+#define LWIP_NO_INTTYPES_H 0
+#endif
+
+/* Define (sn)printf formatters for these lwIP types */
+#if !LWIP_NO_INTTYPES_H
+#include <inttypes.h>
+#ifndef X8_F
+#define X8_F  "02" PRIx8
+#endif
+#ifndef U16_F
+#define U16_F PRIu16
+#endif
+#ifndef S16_F
+#define S16_F PRId16
+#endif
+#ifndef X16_F
+#define X16_F PRIx16
+#endif
+#ifndef U32_F
+#define U32_F PRIu32
+#endif
+#ifndef S32_F
+#define S32_F PRId32
+#endif
+#ifndef X32_F
+#define X32_F PRIx32
+#endif
+#ifndef SZT_F
+#define SZT_F PRIuPTR
+#endif
+#endif
+
+/** Define this to 1 in arch/cc.h of your port if your compiler does not provide
+ * the limits.h header. You need to define the type limits yourself in this case
+ * (e.g. INT_MAX, SSIZE_MAX).
+ */
+#ifndef LWIP_NO_LIMITS_H
+#define LWIP_NO_LIMITS_H 0
+#endif
+
+/* Include limits.h? */
+#if !LWIP_NO_LIMITS_H
+#include <limits.h>
+#endif
+
+/* Do we need to define ssize_t? This is a compatibility hack:
+ * Unfortunately, this type seems to be unavailable on some systems (even if
+ * sys/types or unistd.h are available).
+ * Being like that, we define it to 'int' if SSIZE_MAX is not defined.
+ */
+#ifdef SSIZE_MAX
+/* If SSIZE_MAX is defined, unistd.h should provide the type as well */
+#ifndef LWIP_NO_UNISTD_H
+#define LWIP_NO_UNISTD_H 0
+#endif
+#if !LWIP_NO_UNISTD_H
+#include <unistd.h>
+#endif
+#else /* SSIZE_MAX */
+typedef int ssize_t;
+#define SSIZE_MAX INT_MAX
+#endif /* SSIZE_MAX */
+
+/* some maximum values needed in lwip code */
+#define LWIP_UINT32_MAX 0xffffffff
+
+/** Define this to 1 in arch/cc.h of your port if your compiler does not provide
+ * the ctype.h header. If ctype.h is available, a few character functions
+ * are mapped to the appropriate functions (lwip_islower, lwip_isdigit...), if
+ * not, a private implementation is provided.
+ */
+#ifndef LWIP_NO_CTYPE_H
+#define LWIP_NO_CTYPE_H 0
+#endif
+
+#if LWIP_NO_CTYPE_H
+#define lwip_in_range(c, lo, up)  ((u8_t)(c) >= (lo) && (u8_t)(c) <= (up))
+#define lwip_isdigit(c)           lwip_in_range((c), '0', '9')
+#define lwip_isxdigit(c)          (lwip_isdigit(c) || lwip_in_range((c), 'a', 'f') || lwip_in_range((c), 'A', 'F'))
+#define lwip_islower(c)           lwip_in_range((c), 'a', 'z')
+#define lwip_isspace(c)           ((c) == ' ' || (c) == '\f' || (c) == '\n' || (c) == '\r' || (c) == '\t' || (c) == '\v')
+#define lwip_isupper(c)           lwip_in_range((c), 'A', 'Z')
+#define lwip_tolower(c)           (lwip_isupper(c) ? (c) - 'A' + 'a' : c)
+#define lwip_toupper(c)           (lwip_islower(c) ? (c) - 'a' + 'A' : c)
+#else
+#include <ctype.h>
+#define lwip_isdigit(c)           isdigit((unsigned char)(c))
+#define lwip_isxdigit(c)          isxdigit((unsigned char)(c))
+#define lwip_islower(c)           islower((unsigned char)(c))
+#define lwip_isspace(c)           isspace((unsigned char)(c))
+#define lwip_isupper(c)           isupper((unsigned char)(c))
+#define lwip_tolower(c)           tolower((unsigned char)(c))
+#define lwip_toupper(c)           toupper((unsigned char)(c))
+#endif
+
+/** C++ const_cast<target_type>(val) equivalent to remove constness from a value (GCC -Wcast-qual) */
+#ifndef LWIP_CONST_CAST
+#define LWIP_CONST_CAST(target_type, val) ((target_type)((ptrdiff_t)val))
+#endif
+
+/** Get rid of alignment cast warnings (GCC -Wcast-align) */
+#ifndef LWIP_ALIGNMENT_CAST
+#define LWIP_ALIGNMENT_CAST(target_type, val) LWIP_CONST_CAST(target_type, val)
+#endif
+
+/** Get rid of warnings related to pointer-to-numeric and vice-versa casts,
+ * e.g. "conversion from 'u8_t' to 'void *' of greater size"
+ */
+#ifndef LWIP_PTR_NUMERIC_CAST
+#define LWIP_PTR_NUMERIC_CAST(target_type, val) LWIP_CONST_CAST(target_type, val)
+#endif
+
+/** Avoid warnings/errors related to implicitly casting away packed attributes by doing a explicit cast */
+#ifndef LWIP_PACKED_CAST
+#define LWIP_PACKED_CAST(target_type, val) LWIP_CONST_CAST(target_type, val)
+#endif
+
+/** Allocates a memory buffer of specified size that is of sufficient size to align
+ * its start address using LWIP_MEM_ALIGN.
+ * You can declare your own version here e.g. to enforce alignment without adding
+ * trailing padding bytes (see LWIP_MEM_ALIGN_BUFFER) or your own section placement
+ * requirements.<br>
+ * e.g. if you use gcc and need 32 bit alignment:<br>
+ * \#define LWIP_DECLARE_MEMORY_ALIGNED(variable_name, size) u8_t variable_name[size] \_\_attribute\_\_((aligned(4)))<br>
+ * or more portable:<br>
+ * \#define LWIP_DECLARE_MEMORY_ALIGNED(variable_name, size) u32_t variable_name[(size + sizeof(u32_t) - 1) / sizeof(u32_t)]
+ */
+#ifndef LWIP_DECLARE_MEMORY_ALIGNED
+#define LWIP_DECLARE_MEMORY_ALIGNED(variable_name, size) u8_t variable_name[LWIP_MEM_ALIGN_BUFFER(size)]
+#endif
+
+/** Calculate memory size for an aligned buffer - returns the next highest
+ * multiple of MEM_ALIGNMENT (e.g. LWIP_MEM_ALIGN_SIZE(3) and
+ * LWIP_MEM_ALIGN_SIZE(4) will both yield 4 for MEM_ALIGNMENT == 4).
+ */
+#ifndef LWIP_MEM_ALIGN_SIZE
+#define LWIP_MEM_ALIGN_SIZE(size) (((size) + MEM_ALIGNMENT - 1U) & ~(MEM_ALIGNMENT-1U))
+#endif
+
+/** Calculate safe memory size for an aligned buffer when using an unaligned
+ * type as storage. This includes a safety-margin on (MEM_ALIGNMENT - 1) at the
+ * start (e.g. if buffer is u8_t[] and actual data will be u32_t*)
+ */
+#ifndef LWIP_MEM_ALIGN_BUFFER
+#define LWIP_MEM_ALIGN_BUFFER(size) (((size) + MEM_ALIGNMENT - 1U))
+#endif
+
+/** Align a memory pointer to the alignment defined by MEM_ALIGNMENT
+ * so that ADDR % MEM_ALIGNMENT == 0
+ */
+#ifndef LWIP_MEM_ALIGN
+#define LWIP_MEM_ALIGN(addr) ((void *)(((mem_ptr_t)(addr) + MEM_ALIGNMENT - 1) & ~(mem_ptr_t)(MEM_ALIGNMENT-1)))
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Packed structs support.
+  * Placed BEFORE declaration of a packed struct.<br>
+  * For examples of packed struct declarations, see include/lwip/prot/ subfolder.<br>
+  * A port to GCC/clang is included in lwIP, if you use these compilers there is nothing to do here.
+  */
+#ifndef PACK_STRUCT_BEGIN
+#define PACK_STRUCT_BEGIN
+#endif /* PACK_STRUCT_BEGIN */
+
+/** Packed structs support.
+  * Placed AFTER declaration of a packed struct.<br>
+  * For examples of packed struct declarations, see include/lwip/prot/ subfolder.<br>
+  * A port to GCC/clang is included in lwIP, if you use these compilers there is nothing to do here.
+  */
+#ifndef PACK_STRUCT_END
+#define PACK_STRUCT_END
+#endif /* PACK_STRUCT_END */
+
+/** Packed structs support.
+  * Placed between end of declaration of a packed struct and trailing semicolon.<br>
+  * For examples of packed struct declarations, see include/lwip/prot/ subfolder.<br>
+  * A port to GCC/clang is included in lwIP, if you use these compilers there is nothing to do here.
+  */
+#ifndef PACK_STRUCT_STRUCT
+#if defined(__GNUC__) || defined(__clang__)
+#define PACK_STRUCT_STRUCT __attribute__((packed))
+#else
+#define PACK_STRUCT_STRUCT
+#endif
+#endif /* PACK_STRUCT_STRUCT */
+
+/** Packed structs support.
+  * Wraps u32_t and u16_t members.<br>
+  * For examples of packed struct declarations, see include/lwip/prot/ subfolder.<br>
+  * A port to GCC/clang is included in lwIP, if you use these compilers there is nothing to do here.
+  */
+#ifndef PACK_STRUCT_FIELD
+#define PACK_STRUCT_FIELD(x) x
+#endif /* PACK_STRUCT_FIELD */
+
+/** Packed structs support.
+  * Wraps u8_t members, where some compilers warn that packing is not necessary.<br>
+  * For examples of packed struct declarations, see include/lwip/prot/ subfolder.<br>
+  * A port to GCC/clang is included in lwIP, if you use these compilers there is nothing to do here.
+  */
+#ifndef PACK_STRUCT_FLD_8
+#define PACK_STRUCT_FLD_8(x) PACK_STRUCT_FIELD(x)
+#endif /* PACK_STRUCT_FLD_8 */
+
+/** Packed structs support.
+  * Wraps members that are packed structs themselves, where some compilers warn that packing is not necessary.<br>
+  * For examples of packed struct declarations, see include/lwip/prot/ subfolder.<br>
+  * A port to GCC/clang is included in lwIP, if you use these compilers there is nothing to do here.
+  */
+#ifndef PACK_STRUCT_FLD_S
+#define PACK_STRUCT_FLD_S(x) PACK_STRUCT_FIELD(x)
+#endif /* PACK_STRUCT_FLD_S */
+
+/** PACK_STRUCT_USE_INCLUDES==1: Packed structs support using \#include files before and after struct to be packed.<br>
+ * The file included BEFORE the struct is "arch/bpstruct.h".<br>
+ * The file included AFTER the struct is "arch/epstruct.h".<br>
+ * This can be used to implement struct packing on MS Visual C compilers, see
+ * the Win32 port in the lwIP/contrib subdir for reference.
+ * For examples of packed struct declarations, see include/lwip/prot/ subfolder.<br>
+ * A port to GCC/clang is included in lwIP, if you use these compilers there is nothing to do here.
+ */
+#ifdef __DOXYGEN__
+#define PACK_STRUCT_USE_INCLUDES
+#endif
+
+/** Eliminates compiler warning about unused arguments (GCC -Wextra -Wunused). */
+#ifndef LWIP_UNUSED_ARG
+#define LWIP_UNUSED_ARG(x) (void)x
+#endif /* LWIP_UNUSED_ARG */
+
+/** LWIP_PROVIDE_ERRNO==1: Let lwIP provide ERRNO values and the 'errno' variable.
+ * If this is disabled, cc.h must either define 'errno', include <errno.h>,
+ * define LWIP_ERRNO_STDINCLUDE to get <errno.h> included or
+ * define LWIP_ERRNO_INCLUDE to <errno.h> or equivalent.
+ */
+#if defined __DOXYGEN__
+#define LWIP_PROVIDE_ERRNO
+#endif
+
+/* Use a special, reproducable version of rand() for fuzz tests? */
+#ifdef LWIP_RAND_FOR_FUZZ
+#ifdef LWIP_RAND
+#undef LWIP_RAND
+#endif
+u32_t lwip_fuzz_rand(void);
+#define LWIP_RAND() lwip_fuzz_rand()
+#endif
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_ARCH_H */
diff --git a/lib/lwip/include/lwip/autoip.h b/lib/lwip/include/lwip/autoip.h
new file mode 100644
index 0000000000..b3f9feb194
--- /dev/null
+++ b/lib/lwip/include/lwip/autoip.h
@@ -0,0 +1,90 @@
+/**
+ * @file
+ *
+ * AutoIP Automatic LinkLocal IP Configuration
+ */
+
+/*
+ *
+ * Copyright (c) 2007 Dominik Spies <kontakt@dspies.de>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Dominik Spies <kontakt@dspies.de>
+ *
+ * This is a AutoIP implementation for the lwIP TCP/IP stack. It aims to conform
+ * with RFC 3927.
+ *
+ */
+
+#ifndef LWIP_HDR_AUTOIP_H
+#define LWIP_HDR_AUTOIP_H
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV4 && LWIP_AUTOIP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/netif.h"
+/* #include "lwip/udp.h" */
+#include "lwip/etharp.h"
+#include "lwip/acd.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** AutoIP state information per netif */
+struct autoip
+{
+  /** the currently selected, probed, announced or used LL IP-Address */
+  ip4_addr_t llipaddr;
+  /** current AutoIP state machine state */
+  u8_t state;
+  /** total number of probed/used Link Local IP-Addresses */
+  u8_t tried_llipaddr;
+  /** acd struct */
+  struct acd acd;
+};
+
+
+void autoip_set_struct(struct netif *netif, struct autoip *autoip);
+void autoip_remove_struct(struct netif *netif);
+err_t autoip_start(struct netif *netif);
+err_t autoip_stop(struct netif *netif);
+void autoip_network_changed_link_up(struct netif *netif);
+void autoip_network_changed_link_down(struct netif *netif);
+u8_t autoip_supplied_address(struct netif *netif);
+
+/* for lwIP internal use by ip4.c */
+u8_t autoip_accept_packet(struct netif *netif, const ip4_addr_t *addr);
+
+#define netif_autoip_data(netif) ((struct autoip*)netif_get_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_AUTOIP))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_IPV4 && LWIP_AUTOIP */
+
+#endif /* LWIP_HDR_AUTOIP_H */
diff --git a/lib/lwip/include/lwip/debug.h b/lib/lwip/include/lwip/debug.h
new file mode 100644
index 0000000000..0ec7e76ec5
--- /dev/null
+++ b/lib/lwip/include/lwip/debug.h
@@ -0,0 +1,161 @@
+/**
+ * @file
+ * Debug messages infrastructure
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef LWIP_HDR_DEBUG_H
+#define LWIP_HDR_DEBUG_H
+
+#include "lwip/arch.h"
+#include "lwip/opt.h"
+
+/**
+ * @defgroup debugging_levels LWIP_DBG_MIN_LEVEL and LWIP_DBG_TYPES_ON values
+ * @ingroup lwip_opts_debugmsg
+ * @{
+ */
+
+/** @name Debug level (LWIP_DBG_MIN_LEVEL)
+ * @{
+ */
+/** Debug level: ALL messages*/
+#define LWIP_DBG_LEVEL_ALL     0x00
+/** Debug level: Warnings. bad checksums, dropped packets, ... */
+#define LWIP_DBG_LEVEL_WARNING 0x01
+/** Debug level: Serious. memory allocation failures, ... */
+#define LWIP_DBG_LEVEL_SERIOUS 0x02
+/** Debug level: Severe */
+#define LWIP_DBG_LEVEL_SEVERE  0x03
+/**
+ * @}
+ */
+
+#define LWIP_DBG_MASK_LEVEL    0x03
+/* compatibility define only */
+#define LWIP_DBG_LEVEL_OFF     LWIP_DBG_LEVEL_ALL
+
+/** @name Enable/disable debug messages completely (LWIP_DBG_TYPES_ON)
+ * @{
+ */
+/** flag for LWIP_DEBUGF to enable that debug message */
+#define LWIP_DBG_ON            0x80U
+/** flag for LWIP_DEBUGF to disable that debug message */
+#define LWIP_DBG_OFF           0x00U
+/**
+ * @}
+ */
+
+/** @name Debug message types (LWIP_DBG_TYPES_ON)
+ * @{
+ */
+/** flag for LWIP_DEBUGF indicating a tracing message (to follow program flow) */
+#define LWIP_DBG_TRACE         0x40U
+/** flag for LWIP_DEBUGF indicating a state debug message (to follow module states) */
+#define LWIP_DBG_STATE         0x20U
+/** flag for LWIP_DEBUGF indicating newly added code, not thoroughly tested yet */
+#define LWIP_DBG_FRESH         0x10U
+/** flag for LWIP_DEBUGF to halt after printing this debug message */
+#define LWIP_DBG_HALT          0x08U
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup lwip_assertions Assertion handling
+ * @ingroup lwip_opts_debug
+ * @{
+ */
+/**
+ * LWIP_NOASSERT: Disable LWIP_ASSERT checks:
+ * To disable assertions define LWIP_NOASSERT in arch/cc.h.
+ */
+#ifdef __DOXYGEN__
+#define LWIP_NOASSERT
+#undef LWIP_NOASSERT
+#endif
+/**
+ * @}
+ */
+
+#ifndef LWIP_NOASSERT
+#define LWIP_ASSERT(message, assertion) do { if (!(assertion)) { \
+  LWIP_PLATFORM_ASSERT(message); }} while(0)
+#else  /* LWIP_NOASSERT */
+#define LWIP_ASSERT(message, assertion)
+#endif /* LWIP_NOASSERT */
+
+#ifndef LWIP_ERROR
+#ifdef LWIP_DEBUG
+#define LWIP_PLATFORM_ERROR(message) LWIP_PLATFORM_DIAG((message))
+#else
+#define LWIP_PLATFORM_ERROR(message)
+#endif
+
+/* if "expression" isn't true, then print "message" and execute "handler" expression */
+#define LWIP_ERROR(message, expression, handler) do { if (!(expression)) { \
+  LWIP_PLATFORM_ERROR(message); handler;}} while(0)
+#endif /* LWIP_ERROR */
+
+/** Enable debug message printing, but only if debug message type is enabled
+ *  AND is of correct type AND is at least LWIP_DBG_LEVEL.
+ */
+#ifdef __DOXYGEN__
+#define LWIP_DEBUG
+#undef LWIP_DEBUG
+#endif
+
+#ifdef LWIP_DEBUG
+#define LWIP_DEBUG_ENABLED(debug) (((debug) & LWIP_DBG_ON) && \
+                                   ((debug) & LWIP_DBG_TYPES_ON) && \
+                                   ((s16_t)((debug) & LWIP_DBG_MASK_LEVEL) >= LWIP_DBG_MIN_LEVEL))
+
+#define LWIP_DEBUGF(debug, message) do { \
+                               if (LWIP_DEBUG_ENABLED(debug)) { \
+                                 LWIP_PLATFORM_DIAG(message); \
+                                 if ((debug) & LWIP_DBG_HALT) { \
+                                   while(1); \
+                                 } \
+                               } \
+                             } while(0)
+
+#else  /* LWIP_DEBUG */
+#define LWIP_DEBUG_ENABLED(debug) 0
+#define LWIP_DEBUGF(debug, message)
+#endif /* LWIP_DEBUG */
+
+#endif /* LWIP_HDR_DEBUG_H */
diff --git a/lib/lwip/include/lwip/def.h b/lib/lwip/include/lwip/def.h
new file mode 100644
index 0000000000..d6bf763079
--- /dev/null
+++ b/lib/lwip/include/lwip/def.h
@@ -0,0 +1,156 @@
+/**
+ * @file
+ * various utility macros
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+/**
+ * @defgroup perf Performance measurement
+ * @ingroup sys_layer
+ * All defines related to this section must not be placed in lwipopts.h,
+ * but in arch/perf.h!
+ * Measurement calls made throughout lwip, these can be defined to nothing.
+ * - PERF_START: start measuring something.
+ * - PERF_STOP(x): stop measuring something, and record the result.
+ */
+
+#ifndef LWIP_HDR_DEF_H
+#define LWIP_HDR_DEF_H
+
+/* arch.h might define NULL already */
+#include "lwip/arch.h"
+#include "lwip/opt.h"
+#if LWIP_PERF
+#include "arch/perf.h"
+#else /* LWIP_PERF */
+#define PERF_START    /* null definition */
+#define PERF_STOP(x)  /* null definition */
+#endif /* LWIP_PERF */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define LWIP_MAX(x , y)  (((x) > (y)) ? (x) : (y))
+#define LWIP_MIN(x , y)  (((x) < (y)) ? (x) : (y))
+
+/* Get the number of entries in an array ('x' must NOT be a pointer!) */
+#define LWIP_ARRAYSIZE(x) (sizeof(x)/sizeof((x)[0]))
+
+/** Create u32_t value from bytes */
+#define LWIP_MAKEU32(a,b,c,d) (((u32_t)((a) & 0xff) << 24) | \
+                               ((u32_t)((b) & 0xff) << 16) | \
+                               ((u32_t)((c) & 0xff) << 8)  | \
+                                (u32_t)((d) & 0xff))
+
+#ifndef NULL
+#ifdef __cplusplus
+#define NULL 0
+#else
+#define NULL ((void *)0)
+#endif
+#endif
+
+#if BYTE_ORDER == BIG_ENDIAN
+#define lwip_htons(x) ((u16_t)(x))
+#define lwip_ntohs(x) ((u16_t)(x))
+#define lwip_htonl(x) ((u32_t)(x))
+#define lwip_ntohl(x) ((u32_t)(x))
+#define PP_HTONS(x)   ((u16_t)(x))
+#define PP_NTOHS(x)   ((u16_t)(x))
+#define PP_HTONL(x)   ((u32_t)(x))
+#define PP_NTOHL(x)   ((u32_t)(x))
+#else /* BYTE_ORDER != BIG_ENDIAN */
+#ifndef lwip_htons
+u16_t lwip_htons(u16_t x);
+#endif
+#define lwip_ntohs(x) lwip_htons(x)
+
+#ifndef lwip_htonl
+u32_t lwip_htonl(u32_t x);
+#endif
+#define lwip_ntohl(x) lwip_htonl(x)
+
+/* These macros should be calculated by the preprocessor and are used
+   with compile-time constants only (so that there is no little-endian
+   overhead at runtime). */
+#define PP_HTONS(x) ((u16_t)((((x) & (u16_t)0x00ffU) << 8) | (((x) & (u16_t)0xff00U) >> 8)))
+#define PP_NTOHS(x) PP_HTONS(x)
+#define PP_HTONL(x) ((((x) & (u32_t)0x000000ffUL) << 24) | \
+                     (((x) & (u32_t)0x0000ff00UL) <<  8) | \
+                     (((x) & (u32_t)0x00ff0000UL) >>  8) | \
+                     (((x) & (u32_t)0xff000000UL) >> 24))
+#define PP_NTOHL(x) PP_HTONL(x)
+#endif /* BYTE_ORDER == BIG_ENDIAN */
+
+/* Provide usual function names as macros for users, but this can be turned off */
+#ifndef LWIP_DONT_PROVIDE_BYTEORDER_FUNCTIONS
+#define htons(x) lwip_htons(x)
+#define ntohs(x) lwip_ntohs(x)
+#define htonl(x) lwip_htonl(x)
+#define ntohl(x) lwip_ntohl(x)
+#endif
+
+/* Functions that are not available as standard implementations.
+ * In cc.h, you can #define these to implementations available on
+ * your platform to save some code bytes if you use these functions
+ * in your application, too.
+ */
+
+#ifndef lwip_itoa
+/* This can be #defined to itoa() or snprintf(result, bufsize, "%d", number) depending on your platform */
+void  lwip_itoa(char* result, size_t bufsize, int number);
+#endif
+#ifndef lwip_strnicmp
+/* This can be #defined to strnicmp() or strncasecmp() depending on your platform */
+int   lwip_strnicmp(const char* str1, const char* str2, size_t len);
+#endif
+#ifndef lwip_stricmp
+/* This can be #defined to stricmp() or strcasecmp() depending on your platform */
+int   lwip_stricmp(const char* str1, const char* str2);
+#endif
+#ifndef lwip_strnstr
+/* This can be #defined to strnstr() depending on your platform */
+char* lwip_strnstr(const char* buffer, const char* token, size_t n);
+#endif
+#ifndef lwip_strnistr
+/* This can be #defined to strnistr() depending on your platform */
+char* lwip_strnistr(const char* buffer, const char* token, size_t n);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_DEF_H */
diff --git a/lib/lwip/include/lwip/dhcp.h b/lib/lwip/include/lwip/dhcp.h
new file mode 100644
index 0000000000..b413fa63f9
--- /dev/null
+++ b/lib/lwip/include/lwip/dhcp.h
@@ -0,0 +1,155 @@
+/**
+ * @file
+ * DHCP client API
+ */
+
+/*
+ * Copyright (c) 2001-2004 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * Copyright (c) 2001-2004 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Leon Woestenberg <leon.woestenberg@gmx.net>
+ *
+ */
+#ifndef LWIP_HDR_DHCP_H
+#define LWIP_HDR_DHCP_H
+
+#include "lwip/opt.h"
+
+#if LWIP_DHCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/netif.h"
+#include "lwip/udp.h"
+
+#if LWIP_DHCP_DOES_ACD_CHECK
+#include "lwip/acd.h"
+#endif /* LWIP_DHCP_DOES_ACD_CHECK */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Define DHCP_TIMEOUT_SIZE_T in opt.h if you want use a different integer than u16_t.
+ *  Especially useful if DHCP_COARSE_TIMER_SECS is in smaller units, so timeouts easily reach UINT16_MAX and more */
+#ifdef DHCP_TIMEOUT_SIZE_T
+typedef DHCP_TIMEOUT_SIZE_T dhcp_timeout_t;
+#else /* DHCP_TIMEOUT_SIZE_T */
+typedef u16_t dhcp_timeout_t;
+#endif /* DHCP_TIMEOUT_SIZE_T*/
+/** period (in seconds) of the application calling dhcp_coarse_tmr() */
+#ifndef DHCP_COARSE_TIMER_SECS
+#define DHCP_COARSE_TIMER_SECS  60
+#endif /* DHCP_COARSE_TIMER_SECS */
+/** period (in milliseconds) of the application calling dhcp_coarse_tmr() */
+#define DHCP_COARSE_TIMER_MSECS (DHCP_COARSE_TIMER_SECS * 1000UL)
+/** period (in milliseconds) of the application calling dhcp_fine_tmr() */
+#define DHCP_FINE_TIMER_MSECS   500
+
+#define DHCP_BOOT_FILE_LEN      128U
+
+#define DHCP_FLAG_SUBNET_MASK_GIVEN 0x01
+#define DHCP_FLAG_EXTERNAL_MEM      0x02
+
+/* AutoIP cooperation flags (struct dhcp.autoip_coop_state) */
+typedef enum {
+  DHCP_AUTOIP_COOP_STATE_OFF  = 0,
+  DHCP_AUTOIP_COOP_STATE_ON   = 1
+} dhcp_autoip_coop_state_enum_t;
+
+struct dhcp
+{
+  /** transaction identifier of last sent request */
+  u32_t xid;
+  /** track PCB allocation state */
+  u8_t pcb_allocated;
+  /** current DHCP state machine state */
+  u8_t state;
+  /** retries of current request */
+  u8_t tries;
+  /** see DHCP_FLAG_* */
+  u8_t flags;
+
+  dhcp_timeout_t request_timeout; /* #ticks with period DHCP_FINE_TIMER_SECS for request timeout */
+  dhcp_timeout_t t1_timeout;  /* #ticks with period DHCP_COARSE_TIMER_SECS for renewal time */
+  dhcp_timeout_t t2_timeout;  /* #ticks with period DHCP_COARSE_TIMER_SECS for rebind time */
+  dhcp_timeout_t t1_renew_time;  /* #ticks with period DHCP_COARSE_TIMER_SECS until next renew try */
+  dhcp_timeout_t t2_rebind_time; /* #ticks with period DHCP_COARSE_TIMER_SECS until next rebind try */
+  dhcp_timeout_t lease_used; /* #ticks with period DHCP_COARSE_TIMER_SECS since last received DHCP ack */
+  dhcp_timeout_t t0_timeout; /* #ticks with period DHCP_COARSE_TIMER_SECS for lease time */
+  ip_addr_t server_ip_addr; /* dhcp server address that offered this lease (ip_addr_t because passed to UDP) */
+  ip4_addr_t offered_ip_addr;
+  ip4_addr_t offered_sn_mask;
+  ip4_addr_t offered_gw_addr;
+
+  u32_t offered_t0_lease; /* lease period (in seconds) */
+  u32_t offered_t1_renew; /* recommended renew time (usually 50% of lease period) */
+  u32_t offered_t2_rebind; /* recommended rebind time (usually 87.5 of lease period)  */
+#if LWIP_DHCP_BOOTP_FILE
+  ip4_addr_t offered_si_addr;
+  char boot_file_name[DHCP_BOOT_FILE_LEN];
+#endif /* LWIP_DHCP_BOOTPFILE */
+#if LWIP_DHCP_DOES_ACD_CHECK
+  /** acd struct */
+  struct acd acd;
+#endif /* LWIP_DHCP_DOES_ACD_CHECK */
+};
+
+
+void dhcp_set_struct(struct netif *netif, struct dhcp *dhcp);
+/** Remove a struct dhcp previously set to the netif using dhcp_set_struct() */
+#define dhcp_remove_struct(netif) netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP, NULL)
+void dhcp_cleanup(struct netif *netif);
+err_t dhcp_start(struct netif *netif);
+err_t dhcp_renew(struct netif *netif);
+err_t dhcp_release(struct netif *netif);
+void dhcp_stop(struct netif *netif);
+void dhcp_release_and_stop(struct netif *netif);
+void dhcp_inform(struct netif *netif);
+void dhcp_network_changed_link_up(struct netif *netif);
+
+u8_t dhcp_supplied_address(const struct netif *netif);
+/* to be called every minute */
+void dhcp_coarse_tmr(void);
+/* to be called every half second */
+void dhcp_fine_tmr(void);
+
+#if LWIP_DHCP_GET_NTP_SRV
+/** This function must exist, in other to add offered NTP servers to
+ * the NTP (or SNTP) engine.
+ * See LWIP_DHCP_MAX_NTP_SERVERS */
+extern void dhcp_set_ntp_servers(u8_t num_ntp_servers, const ip4_addr_t* ntp_server_addrs);
+#endif /* LWIP_DHCP_GET_NTP_SRV */
+
+#define netif_dhcp_data(netif) ((struct dhcp*)netif_get_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_DHCP */
+
+#endif /*LWIP_HDR_DHCP_H*/
diff --git a/lib/lwip/include/lwip/dhcp6.h b/lib/lwip/include/lwip/dhcp6.h
new file mode 100644
index 0000000000..5cc4a01591
--- /dev/null
+++ b/lib/lwip/include/lwip/dhcp6.h
@@ -0,0 +1,104 @@
+/**
+ * @file
+ *
+ * DHCPv6 client: IPv6 address autoconfiguration as per
+ * RFC 3315 (stateful DHCPv6) and
+ * RFC 3736 (stateless DHCPv6).
+ */
+
+/*
+ * Copyright (c) 2018 Simon Goldschmidt
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt <goldsimon@gmx.de>
+ */
+
+#ifndef LWIP_HDR_IP6_DHCP6_H
+#define LWIP_HDR_IP6_DHCP6_H
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV6_DHCP6  /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/err.h"
+#include "lwip/netif.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** period (in milliseconds) of the application calling dhcp6_tmr() */
+#define DHCP6_TIMER_MSECS   500
+
+struct dhcp6
+{
+  /** transaction identifier of last sent request */
+  u32_t xid;
+  /** track PCB allocation state */
+  u8_t pcb_allocated;
+  /** current DHCPv6 state machine state */
+  u8_t state;
+  /** retries of current request */
+  u8_t tries;
+  /** if request config is triggered while another action is active, this keeps track of it */
+  u8_t request_config_pending;
+  /** #ticks with period DHCP6_TIMER_MSECS for request timeout */
+  u16_t request_timeout;
+#if LWIP_IPV6_DHCP6_STATEFUL
+  /* @todo: add more members here to keep track of stateful DHCPv6 data, like lease times */
+#endif /* LWIP_IPV6_DHCP6_STATEFUL */
+};
+
+void dhcp6_set_struct(struct netif *netif, struct dhcp6 *dhcp6);
+/** Remove a struct dhcp6 previously set to the netif using dhcp6_set_struct() */
+#define dhcp6_remove_struct(netif) netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP6, NULL)
+void dhcp6_cleanup(struct netif *netif);
+
+err_t dhcp6_enable_stateful(struct netif *netif);
+err_t dhcp6_enable_stateless(struct netif *netif);
+void dhcp6_disable(struct netif *netif);
+
+void dhcp6_tmr(void);
+
+void dhcp6_nd6_ra_trigger(struct netif *netif, u8_t managed_addr_config, u8_t other_config);
+
+#if LWIP_DHCP6_GET_NTP_SRV
+/** This function must exist, in other to add offered NTP servers to
+ * the NTP (or SNTP) engine.
+ * See LWIP_DHCP6_MAX_NTP_SERVERS */
+extern void dhcp6_set_ntp_servers(u8_t num_ntp_servers, const ip_addr_t* ntp_server_addrs);
+#endif /* LWIP_DHCP6_GET_NTP_SRV */
+
+#define netif_dhcp6_data(netif) ((struct dhcp6*)netif_get_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP6))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_IPV6_DHCP6 */
+
+#endif /* LWIP_HDR_IP6_DHCP6_H */
diff --git a/lib/lwip/include/lwip/dns.h b/lib/lwip/include/lwip/dns.h
new file mode 100644
index 0000000000..091341544f
--- /dev/null
+++ b/lib/lwip/include/lwip/dns.h
@@ -0,0 +1,131 @@
+/**
+ * @file
+ * DNS API
+ */
+
+/**
+ * lwip DNS resolver header file.
+
+ * Author: Jim Pettinato
+ *   April 2007
+
+ * ported from uIP resolv.c Copyright (c) 2002-2003, Adam Dunkels.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef LWIP_HDR_DNS_H
+#define LWIP_HDR_DNS_H
+
+#include "lwip/opt.h"
+
+#if LWIP_DNS
+
+#include "lwip/ip_addr.h"
+#include "lwip/err.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** DNS timer period */
+#define DNS_TMR_INTERVAL          1000
+
+/* DNS resolve types: */
+#define LWIP_DNS_ADDRTYPE_IPV4      0
+#define LWIP_DNS_ADDRTYPE_IPV6      1
+#define LWIP_DNS_ADDRTYPE_IPV4_IPV6 2 /* try to resolve IPv4 first, try IPv6 if IPv4 fails only */
+#define LWIP_DNS_ADDRTYPE_IPV6_IPV4 3 /* try to resolve IPv6 first, try IPv4 if IPv6 fails only */
+#if LWIP_IPV4 && LWIP_IPV6
+#ifndef LWIP_DNS_ADDRTYPE_DEFAULT
+#define LWIP_DNS_ADDRTYPE_DEFAULT   LWIP_DNS_ADDRTYPE_IPV4_IPV6
+#endif
+#elif LWIP_IPV4
+#define LWIP_DNS_ADDRTYPE_DEFAULT   LWIP_DNS_ADDRTYPE_IPV4
+#else
+#define LWIP_DNS_ADDRTYPE_DEFAULT   LWIP_DNS_ADDRTYPE_IPV6
+#endif
+
+#if DNS_LOCAL_HOSTLIST
+/** struct used for local host-list */
+struct local_hostlist_entry {
+  /** static hostname */
+  const char *name;
+  /** static host address in network byteorder */
+  ip_addr_t addr;
+  struct local_hostlist_entry *next;
+};
+#define DNS_LOCAL_HOSTLIST_ELEM(name, addr_init) {name, addr_init, NULL}
+#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC
+#ifndef DNS_LOCAL_HOSTLIST_MAX_NAMELEN
+#define DNS_LOCAL_HOSTLIST_MAX_NAMELEN  DNS_MAX_NAME_LENGTH
+#endif
+#define LOCALHOSTLIST_ELEM_SIZE ((sizeof(struct local_hostlist_entry) + DNS_LOCAL_HOSTLIST_MAX_NAMELEN + 1))
+#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
+#endif /* DNS_LOCAL_HOSTLIST */
+
+#if LWIP_IPV4
+extern const ip_addr_t dns_mquery_v4group;
+#endif /* LWIP_IPV4 */
+#if LWIP_IPV6
+extern const ip_addr_t dns_mquery_v6group;
+#endif /* LWIP_IPV6 */
+
+/** Callback which is invoked when a hostname is found.
+ * A function of this type must be implemented by the application using the DNS resolver.
+ * @param name pointer to the name that was looked up.
+ * @param ipaddr pointer to an ip_addr_t containing the IP address of the hostname,
+ *        or NULL if the name could not be found (or on any other error).
+ * @param callback_arg a user-specified callback argument passed to dns_gethostbyname
+*/
+typedef void (*dns_found_callback)(const char *name, const ip_addr_t *ipaddr, void *callback_arg);
+
+void             dns_init(void);
+void             dns_tmr(void);
+void             dns_setserver(u8_t numdns, const ip_addr_t *dnsserver);
+const ip_addr_t* dns_getserver(u8_t numdns);
+err_t            dns_gethostbyname(const char *hostname, ip_addr_t *addr,
+                                   dns_found_callback found, void *callback_arg);
+err_t            dns_gethostbyname_addrtype(const char *hostname, ip_addr_t *addr,
+                                   dns_found_callback found, void *callback_arg,
+                                   u8_t dns_addrtype);
+
+
+#if DNS_LOCAL_HOSTLIST
+size_t         dns_local_iterate(dns_found_callback iterator_fn, void *iterator_arg);
+err_t          dns_local_lookup(const char *hostname, ip_addr_t *addr, u8_t dns_addrtype);
+#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC
+int            dns_local_removehost(const char *hostname, const ip_addr_t *addr);
+err_t          dns_local_addhost(const char *hostname, const ip_addr_t *addr);
+#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
+#endif /* DNS_LOCAL_HOSTLIST */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_DNS */
+
+#endif /* LWIP_HDR_DNS_H */
diff --git a/lib/lwip/include/lwip/err.h b/lib/lwip/include/lwip/err.h
new file mode 100644
index 0000000000..887d9b3fd8
--- /dev/null
+++ b/lib/lwip/include/lwip/err.h
@@ -0,0 +1,117 @@
+/**
+ * @file
+ * lwIP Error codes
+ */
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef LWIP_HDR_ERR_H
+#define LWIP_HDR_ERR_H
+
+#include "lwip/opt.h"
+#include "lwip/arch.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @defgroup infrastructure_errors Error codes
+ * @ingroup infrastructure
+ * @{
+ */
+
+/** Definitions for error constants. */
+typedef enum {
+/** No error, everything OK. */
+  ERR_OK         = 0,
+/** Out of memory error.     */
+  ERR_MEM        = -1,
+/** Buffer error.            */
+  ERR_BUF        = -2,
+/** Timeout.                 */
+  ERR_TIMEOUT    = -3,
+/** Routing problem.         */
+  ERR_RTE        = -4,
+/** Operation in progress    */
+  ERR_INPROGRESS = -5,
+/** Illegal value.           */
+  ERR_VAL        = -6,
+/** Operation would block.   */
+  ERR_WOULDBLOCK = -7,
+/** Address in use.          */
+  ERR_USE        = -8,
+/** Already connecting.      */
+  ERR_ALREADY    = -9,
+/** Conn already established.*/
+  ERR_ISCONN     = -10,
+/** Not connected.           */
+  ERR_CONN       = -11,
+/** Low-level netif error    */
+  ERR_IF         = -12,
+
+/** Connection aborted.      */
+  ERR_ABRT       = -13,
+/** Connection reset.        */
+  ERR_RST        = -14,
+/** Connection closed.       */
+  ERR_CLSD       = -15,
+/** Illegal argument.        */
+  ERR_ARG        = -16
+} err_enum_t;
+
+/** Define LWIP_ERR_T in cc.h if you want to use
+ *  a different type for your platform (must be signed). */
+#ifdef LWIP_ERR_T
+typedef LWIP_ERR_T err_t;
+#else /* LWIP_ERR_T */
+typedef s8_t err_t;
+#endif /* LWIP_ERR_T*/
+
+/**
+ * @}
+ */
+
+#ifdef LWIP_DEBUG
+extern const char *lwip_strerr(err_t err);
+#else
+#define lwip_strerr(x) ""
+#endif /* LWIP_DEBUG */
+
+#if !NO_SYS
+int err_to_errno(err_t err);
+#endif /* !NO_SYS */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_ERR_H */
diff --git a/lib/lwip/include/lwip/errno.h b/lib/lwip/include/lwip/errno.h
new file mode 100644
index 0000000000..48d6b539d8
--- /dev/null
+++ b/lib/lwip/include/lwip/errno.h
@@ -0,0 +1,198 @@
+/**
+ * @file
+ * Posix Errno defines
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef LWIP_HDR_ERRNO_H
+#define LWIP_HDR_ERRNO_H
+
+#include "lwip/opt.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef LWIP_PROVIDE_ERRNO
+
+#define  EPERM            1  /* Operation not permitted */
+#define  ENOENT           2  /* No such file or directory */
+#define  ESRCH            3  /* No such process */
+#define  EINTR            4  /* Interrupted system call */
+#define  EIO              5  /* I/O error */
+#define  ENXIO            6  /* No such device or address */
+#define  E2BIG            7  /* Arg list too long */
+#define  ENOEXEC          8  /* Exec format error */
+#define  EBADF            9  /* Bad file number */
+#define  ECHILD          10  /* No child processes */
+#define  EAGAIN          11  /* Try again */
+#define  ENOMEM          12  /* Out of memory */
+#define  EACCES          13  /* Permission denied */
+#define  EFAULT          14  /* Bad address */
+#define  ENOTBLK         15  /* Block device required */
+#define  EBUSY           16  /* Device or resource busy */
+#define  EEXIST          17  /* File exists */
+#define  EXDEV           18  /* Cross-device link */
+#define  ENODEV          19  /* No such device */
+#define  ENOTDIR         20  /* Not a directory */
+#define  EISDIR          21  /* Is a directory */
+#define  EINVAL          22  /* Invalid argument */
+#define  ENFILE          23  /* File table overflow */
+#define  EMFILE          24  /* Too many open files */
+#define  ENOTTY          25  /* Not a typewriter */
+#define  ETXTBSY         26  /* Text file busy */
+#define  EFBIG           27  /* File too large */
+#define  ENOSPC          28  /* No space left on device */
+#define  ESPIPE          29  /* Illegal seek */
+#define  EROFS           30  /* Read-only file system */
+#define  EMLINK          31  /* Too many links */
+#define  EPIPE           32  /* Broken pipe */
+#define  EDOM            33  /* Math argument out of domain of func */
+#define  ERANGE          34  /* Math result not representable */
+#define  EDEADLK         35  /* Resource deadlock would occur */
+#define  ENAMETOOLONG    36  /* File name too long */
+#define  ENOLCK          37  /* No record locks available */
+#define  ENOSYS          38  /* Function not implemented */
+#define  ENOTEMPTY       39  /* Directory not empty */
+#define  ELOOP           40  /* Too many symbolic links encountered */
+#define  EWOULDBLOCK     EAGAIN  /* Operation would block */
+#define  ENOMSG          42  /* No message of desired type */
+#define  EIDRM           43  /* Identifier removed */
+#define  ECHRNG          44  /* Channel number out of range */
+#define  EL2NSYNC        45  /* Level 2 not synchronized */
+#define  EL3HLT          46  /* Level 3 halted */
+#define  EL3RST          47  /* Level 3 reset */
+#define  ELNRNG          48  /* Link number out of range */
+#define  EUNATCH         49  /* Protocol driver not attached */
+#define  ENOCSI          50  /* No CSI structure available */
+#define  EL2HLT          51  /* Level 2 halted */
+#define  EBADE           52  /* Invalid exchange */
+#define  EBADR           53  /* Invalid request descriptor */
+#define  EXFULL          54  /* Exchange full */
+#define  ENOANO          55  /* No anode */
+#define  EBADRQC         56  /* Invalid request code */
+#define  EBADSLT         57  /* Invalid slot */
+
+#define  EDEADLOCK       EDEADLK
+
+#define  EBFONT          59  /* Bad font file format */
+#define  ENOSTR          60  /* Device not a stream */
+#define  ENODATA         61  /* No data available */
+#define  ETIME           62  /* Timer expired */
+#define  ENOSR           63  /* Out of streams resources */
+#define  ENONET          64  /* Machine is not on the network */
+#define  ENOPKG          65  /* Package not installed */
+#define  EREMOTE         66  /* Object is remote */
+#define  ENOLINK         67  /* Link has been severed */
+#define  EADV            68  /* Advertise error */
+#define  ESRMNT          69  /* Srmount error */
+#define  ECOMM           70  /* Communication error on send */
+#define  EPROTO          71  /* Protocol error */
+#define  EMULTIHOP       72  /* Multihop attempted */
+#define  EDOTDOT         73  /* RFS specific error */
+#define  EBADMSG         74  /* Not a data message */
+#define  EOVERFLOW       75  /* Value too large for defined data type */
+#define  ENOTUNIQ        76  /* Name not unique on network */
+#define  EBADFD          77  /* File descriptor in bad state */
+#define  EREMCHG         78  /* Remote address changed */
+#define  ELIBACC         79  /* Can not access a needed shared library */
+#define  ELIBBAD         80  /* Accessing a corrupted shared library */
+#define  ELIBSCN         81  /* .lib section in a.out corrupted */
+#define  ELIBMAX         82  /* Attempting to link in too many shared libraries */
+#define  ELIBEXEC        83  /* Cannot exec a shared library directly */
+#define  EILSEQ          84  /* Illegal byte sequence */
+#define  ERESTART        85  /* Interrupted system call should be restarted */
+#define  ESTRPIPE        86  /* Streams pipe error */
+#define  EUSERS          87  /* Too many users */
+#define  ENOTSOCK        88  /* Socket operation on non-socket */
+#define  EDESTADDRREQ    89  /* Destination address required */
+#define  EMSGSIZE        90  /* Message too long */
+#define  EPROTOTYPE      91  /* Protocol wrong type for socket */
+#define  ENOPROTOOPT     92  /* Protocol not available */
+#define  EPROTONOSUPPORT 93  /* Protocol not supported */
+#define  ESOCKTNOSUPPORT 94  /* Socket type not supported */
+#define  EOPNOTSUPP      95  /* Operation not supported on transport endpoint */
+#define  EPFNOSUPPORT    96  /* Protocol family not supported */
+#define  EAFNOSUPPORT    97  /* Address family not supported by protocol */
+#define  EADDRINUSE      98  /* Address already in use */
+#define  EADDRNOTAVAIL   99  /* Cannot assign requested address */
+#define  ENETDOWN       100  /* Network is down */
+#define  ENETUNREACH    101  /* Network is unreachable */
+#define  ENETRESET      102  /* Network dropped connection because of reset */
+#define  ECONNABORTED   103  /* Software caused connection abort */
+#define  ECONNRESET     104  /* Connection reset by peer */
+#define  ENOBUFS        105  /* No buffer space available */
+#define  EISCONN        106  /* Transport endpoint is already connected */
+#define  ENOTCONN       107  /* Transport endpoint is not connected */
+#define  ESHUTDOWN      108  /* Cannot send after transport endpoint shutdown */
+#define  ETOOMANYREFS   109  /* Too many references: cannot splice */
+#define  ETIMEDOUT      110  /* Connection timed out */
+#define  ECONNREFUSED   111  /* Connection refused */
+#define  EHOSTDOWN      112  /* Host is down */
+#define  EHOSTUNREACH   113  /* No route to host */
+#define  EALREADY       114  /* Operation already in progress */
+#define  EINPROGRESS    115  /* Operation now in progress */
+#define  ESTALE         116  /* Stale NFS file handle */
+#define  EUCLEAN        117  /* Structure needs cleaning */
+#define  ENOTNAM        118  /* Not a XENIX named type file */
+#define  ENAVAIL        119  /* No XENIX semaphores available */
+#define  EISNAM         120  /* Is a named type file */
+#define  EREMOTEIO      121  /* Remote I/O error */
+#define  EDQUOT         122  /* Quota exceeded */
+
+#define  ENOMEDIUM      123  /* No medium found */
+#define  EMEDIUMTYPE    124  /* Wrong medium type */
+
+#ifndef errno
+extern int errno;
+#endif
+
+#else /* LWIP_PROVIDE_ERRNO */
+
+/* Define LWIP_ERRNO_STDINCLUDE if you want to include <errno.h> here */
+#ifdef LWIP_ERRNO_STDINCLUDE
+#include <errno.h>
+#else /* LWIP_ERRNO_STDINCLUDE */
+/* Define LWIP_ERRNO_INCLUDE to an equivalent of <errno.h> to include the error defines here */
+#ifdef LWIP_ERRNO_INCLUDE
+#include LWIP_ERRNO_INCLUDE
+#endif /* LWIP_ERRNO_INCLUDE */
+#endif /* LWIP_ERRNO_STDINCLUDE */
+
+#endif /* LWIP_PROVIDE_ERRNO */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_ERRNO_H */
diff --git a/lib/lwip/include/lwip/etharp.h b/lib/lwip/include/lwip/etharp.h
new file mode 100644
index 0000000000..48a1d229e3
--- /dev/null
+++ b/lib/lwip/include/lwip/etharp.h
@@ -0,0 +1,110 @@
+/**
+ * @file
+ * Ethernet output function - handles OUTGOING ethernet level traffic, implements
+ * ARP resolving.
+ * To be used in most low-level netif implementations
+ */
+
+/*
+ * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
+ * Copyright (c) 2003-2004 Leon Woestenberg <leon.woestenberg@axon.tv>
+ * Copyright (c) 2003-2004 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#ifndef LWIP_HDR_NETIF_ETHARP_H
+#define LWIP_HDR_NETIF_ETHARP_H
+
+#include "lwip/opt.h"
+
+#if LWIP_ARP || LWIP_ETHERNET /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/pbuf.h"
+#include "lwip/ip4_addr.h"
+#include "lwip/netif.h"
+#include "lwip/ip4.h"
+#include "lwip/prot/ethernet.h"
+
+#if LWIP_IPV4 && LWIP_ARP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/prot/etharp.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** 1 seconds period */
+#define ARP_TMR_INTERVAL 1000
+
+#if ARP_QUEUEING
+/** struct for queueing outgoing packets for unknown address
+  * defined here to be accessed by memp.h
+  */
+struct etharp_q_entry {
+  struct etharp_q_entry *next;
+  struct pbuf *p;
+};
+#endif /* ARP_QUEUEING */
+
+#define etharp_init() /* Compatibility define, no init needed. */
+void etharp_tmr(void);
+ssize_t etharp_find_addr(struct netif *netif, const ip4_addr_t *ipaddr,
+         struct eth_addr **eth_ret, const ip4_addr_t **ip_ret);
+int etharp_get_entry(size_t i, ip4_addr_t **ipaddr, struct netif **netif, struct eth_addr **eth_ret);
+err_t etharp_output(struct netif *netif, struct pbuf *q, const ip4_addr_t *ipaddr);
+err_t etharp_query(struct netif *netif, const ip4_addr_t *ipaddr, struct pbuf *q);
+err_t etharp_request(struct netif *netif, const ip4_addr_t *ipaddr);
+/** For Ethernet network interfaces, we might want to send "gratuitous ARP";
+ *  this is an ARP packet sent by a node in order to spontaneously cause other
+ *  nodes to update an entry in their ARP cache.
+ *  From RFC 3220 "IP Mobility Support for IPv4" section 4.6. */
+#define etharp_gratuitous(netif) etharp_request((netif), netif_ip4_addr(netif))
+void etharp_cleanup_netif(struct netif *netif);
+
+#if LWIP_ACD
+err_t etharp_acd_probe(struct netif *netif, const ip4_addr_t *ipaddr);
+err_t etharp_acd_announce(struct netif *netif, const ip4_addr_t *ipaddr);
+#endif /* LWIP_ACD */
+
+#if ETHARP_SUPPORT_STATIC_ENTRIES
+err_t etharp_add_static_entry(const ip4_addr_t *ipaddr, struct eth_addr *ethaddr);
+err_t etharp_remove_static_entry(const ip4_addr_t *ipaddr);
+#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
+
+void etharp_input(struct pbuf *p, struct netif *netif);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_IPV4 && LWIP_ARP */
+#endif /* LWIP_ARP || LWIP_ETHERNET */
+
+#endif /* LWIP_HDR_NETIF_ETHARP_H */
diff --git a/lib/lwip/include/lwip/ethip6.h b/lib/lwip/include/lwip/ethip6.h
new file mode 100644
index 0000000000..5e88dffd05
--- /dev/null
+++ b/lib/lwip/include/lwip/ethip6.h
@@ -0,0 +1,68 @@
+/**
+ * @file
+ *
+ * Ethernet output for IPv6. Uses ND tables for link-layer addressing.
+ */
+
+/*
+ * Copyright (c) 2010 Inico Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Ivan Delamer <delamer@inicotech.com>
+ *
+ *
+ * Please coordinate changes and requests with Ivan Delamer
+ * <delamer@inicotech.com>
+ */
+
+#ifndef LWIP_HDR_ETHIP6_H
+#define LWIP_HDR_ETHIP6_H
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV6 && LWIP_ETHERNET /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/pbuf.h"
+#include "lwip/ip6.h"
+#include "lwip/ip6_addr.h"
+#include "lwip/netif.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+err_t ethip6_output(struct netif *netif, struct pbuf *q, const ip6_addr_t *ip6addr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_IPV6 && LWIP_ETHERNET */
+
+#endif /* LWIP_HDR_ETHIP6_H */
diff --git a/lib/lwip/include/lwip/icmp.h b/lib/lwip/include/lwip/icmp.h
new file mode 100644
index 0000000000..f5a31fd4c0
--- /dev/null
+++ b/lib/lwip/include/lwip/icmp.h
@@ -0,0 +1,110 @@
+/**
+ * @file
+ * ICMP API
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef LWIP_HDR_ICMP_H
+#define LWIP_HDR_ICMP_H
+
+#include "lwip/opt.h"
+#include "lwip/pbuf.h"
+#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+#include "lwip/prot/icmp.h"
+
+#if LWIP_IPV6 && LWIP_ICMP6
+#include "lwip/icmp6.h"
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** ICMP destination unreachable codes */
+enum icmp_dur_type {
+  /** net unreachable */
+  ICMP_DUR_NET   = 0,
+  /** host unreachable */
+  ICMP_DUR_HOST  = 1,
+  /** protocol unreachable */
+  ICMP_DUR_PROTO = 2,
+  /** port unreachable */
+  ICMP_DUR_PORT  = 3,
+  /** fragmentation needed and DF set */
+  ICMP_DUR_FRAG  = 4,
+  /** source route failed */
+  ICMP_DUR_SR    = 5
+};
+
+/** ICMP time exceeded codes */
+enum icmp_te_type {
+  /** time to live exceeded in transit */
+  ICMP_TE_TTL  = 0,
+  /** fragment reassembly time exceeded */
+  ICMP_TE_FRAG = 1
+};
+
+#if LWIP_IPV4 && LWIP_ICMP /* don't build if not configured for use in lwipopts.h */
+
+void icmp_input(struct pbuf *p, struct netif *inp);
+void icmp_dest_unreach(struct pbuf *p, enum icmp_dur_type t);
+void icmp_time_exceeded(struct pbuf *p, enum icmp_te_type t);
+
+#endif /* LWIP_IPV4 && LWIP_ICMP */
+
+#if LWIP_IPV4 && LWIP_IPV6
+#if LWIP_ICMP && LWIP_ICMP6
+#define icmp_port_unreach(isipv6, pbuf) ((isipv6) ? \
+                                         icmp6_dest_unreach(pbuf, ICMP6_DUR_PORT) : \
+                                         icmp_dest_unreach(pbuf, ICMP_DUR_PORT))
+#elif LWIP_ICMP
+#define icmp_port_unreach(isipv6, pbuf) do{ if(!(isipv6)) { icmp_dest_unreach(pbuf, ICMP_DUR_PORT);}}while(0)
+#elif LWIP_ICMP6
+#define icmp_port_unreach(isipv6, pbuf) do{ if(isipv6) { icmp6_dest_unreach(pbuf, ICMP6_DUR_PORT);}}while(0)
+#else
+#define icmp_port_unreach(isipv6, pbuf)
+#endif
+#elif LWIP_IPV6 && LWIP_ICMP6
+#define icmp_port_unreach(isipv6, pbuf) icmp6_dest_unreach(pbuf, ICMP6_DUR_PORT)
+#elif LWIP_IPV4 && LWIP_ICMP
+#define icmp_port_unreach(isipv6, pbuf) icmp_dest_unreach(pbuf, ICMP_DUR_PORT)
+#else /* (LWIP_IPV6 && LWIP_ICMP6) || (LWIP_IPV4 && LWIP_ICMP) */
+#define icmp_port_unreach(isipv6, pbuf)
+#endif /* (LWIP_IPV6 && LWIP_ICMP6) || (LWIP_IPV4 && LWIP_ICMP) LWIP_IPV4*/
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_ICMP_H */
diff --git a/lib/lwip/include/lwip/icmp6.h b/lib/lwip/include/lwip/icmp6.h
new file mode 100644
index 0000000000..0ccb78994d
--- /dev/null
+++ b/lib/lwip/include/lwip/icmp6.h
@@ -0,0 +1,72 @@
+/**
+ * @file
+ *
+ * IPv6 version of ICMP, as per RFC 4443.
+ */
+
+/*
+ * Copyright (c) 2010 Inico Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Ivan Delamer <delamer@inicotech.com>
+ *
+ *
+ * Please coordinate changes and requests with Ivan Delamer
+ * <delamer@inicotech.com>
+ */
+#ifndef LWIP_HDR_ICMP6_H
+#define LWIP_HDR_ICMP6_H
+
+#include "lwip/opt.h"
+#include "lwip/pbuf.h"
+#include "lwip/ip6_addr.h"
+#include "lwip/netif.h"
+#include "lwip/prot/icmp6.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if LWIP_ICMP6 && LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */
+
+void icmp6_input(struct pbuf *p, struct netif *inp);
+void icmp6_dest_unreach(struct pbuf *p, enum icmp6_dur_code c);
+void icmp6_packet_too_big(struct pbuf *p, u32_t mtu);
+void icmp6_time_exceeded(struct pbuf *p, enum icmp6_te_code c);
+void icmp6_time_exceeded_with_addrs(struct pbuf *p, enum icmp6_te_code c,
+    const ip6_addr_t *src_addr, const ip6_addr_t *dest_addr);
+void icmp6_param_problem(struct pbuf *p, enum icmp6_pp_code c, const void *pointer);
+
+#endif /* LWIP_ICMP6 && LWIP_IPV6 */
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* LWIP_HDR_ICMP6_H */
diff --git a/lib/lwip/include/lwip/if_api.h b/lib/lwip/include/lwip/if_api.h
new file mode 100644
index 0000000000..b7269e2a62
--- /dev/null
+++ b/lib/lwip/include/lwip/if_api.h
@@ -0,0 +1,70 @@
+/**
+ * @file
+ * Interface Identification APIs from:
+ *              RFC 3493: Basic Socket Interface Extensions for IPv6
+ *                  Section 4: Interface Identification
+ */
+
+/*
+ * Copyright (c) 2017 Joel Cunningham, Garmin International, Inc. <joel.cunningham@garmin.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Joel Cunningham <joel.cunningham@me.com>
+ *
+ */
+#ifndef LWIP_HDR_IF_H
+#define LWIP_HDR_IF_H
+
+#include "lwip/opt.h"
+
+#if LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/netif.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef IF_NAMESIZE
+#define IF_NAMESIZE NETIF_NAMESIZE
+#endif
+
+char * lwip_if_indextoname(unsigned int ifindex, char *ifname);
+unsigned int lwip_if_nametoindex(const char *ifname);
+
+#if LWIP_COMPAT_SOCKETS
+#define if_indextoname(ifindex, ifname)  lwip_if_indextoname(ifindex,ifname)
+#define if_nametoindex(ifname)           lwip_if_nametoindex(ifname)
+#endif /* LWIP_COMPAT_SOCKETS */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_SOCKET */
+
+#endif /* LWIP_HDR_IF_H */
diff --git a/lib/lwip/include/lwip/igmp.h b/lib/lwip/include/lwip/igmp.h
new file mode 100644
index 0000000000..0a16db0397
--- /dev/null
+++ b/lib/lwip/include/lwip/igmp.h
@@ -0,0 +1,115 @@
+/**
+ * @file
+ * IGMP API
+ */
+
+/*
+ * Copyright (c) 2002 CITEL Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of CITEL Technologies Ltd nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY CITEL TECHNOLOGIES AND CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL CITEL TECHNOLOGIES OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This file is a contribution to the lwIP TCP/IP stack.
+ * The Swedish Institute of Computer Science and Adam Dunkels
+ * are specifically granted permission to redistribute this
+ * source code.
+*/
+
+#ifndef LWIP_HDR_IGMP_H
+#define LWIP_HDR_IGMP_H
+
+#include "lwip/opt.h"
+#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+#include "lwip/pbuf.h"
+
+#if LWIP_IPV4 && LWIP_IGMP /* don't build if not configured for use in lwipopts.h */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* IGMP timer */
+#define IGMP_TMR_INTERVAL              100 /* Milliseconds */
+#define IGMP_V1_DELAYING_MEMBER_TMR   (1000/IGMP_TMR_INTERVAL)
+#define IGMP_JOIN_DELAYING_MEMBER_TMR (500 /IGMP_TMR_INTERVAL)
+
+/* Compatibility defines (don't use for new code) */
+#define IGMP_DEL_MAC_FILTER            NETIF_DEL_MAC_FILTER
+#define IGMP_ADD_MAC_FILTER            NETIF_ADD_MAC_FILTER
+
+/**
+ * igmp group structure - there is
+ * a list of groups for each interface
+ * these should really be linked from the interface, but
+ * if we keep them separate we will not affect the lwip original code
+ * too much
+ *
+ * There will be a group for the all systems group address but this
+ * will not run the state machine as it is used to kick off reports
+ * from all the other groups
+ */
+struct igmp_group {
+  /** next link */
+  struct igmp_group *next;
+  /** multicast address */
+  ip4_addr_t         group_address;
+  /** signifies we were the last person to report */
+  u8_t               last_reporter_flag;
+  /** current state of the group */
+  u8_t               group_state;
+  /** timer for reporting, negative is OFF */
+  u16_t              timer;
+  /** counter of simultaneous uses */
+  u8_t               use;
+};
+
+/*  Prototypes */
+void   igmp_init(void);
+err_t  igmp_start(struct netif *netif);
+err_t  igmp_stop(struct netif *netif);
+void   igmp_report_groups(struct netif *netif);
+struct igmp_group *igmp_lookfor_group(struct netif *ifp, const ip4_addr_t *addr);
+void   igmp_input(struct pbuf *p, struct netif *inp, const ip4_addr_t *dest);
+err_t  igmp_joingroup(const ip4_addr_t *ifaddr, const ip4_addr_t *groupaddr);
+err_t  igmp_joingroup_netif(struct netif *netif, const ip4_addr_t *groupaddr);
+err_t  igmp_leavegroup(const ip4_addr_t *ifaddr, const ip4_addr_t *groupaddr);
+err_t  igmp_leavegroup_netif(struct netif *netif, const ip4_addr_t *groupaddr);
+void   igmp_tmr(void);
+
+/** @ingroup igmp
+ * Get list head of IGMP groups for netif.
+ * Note: The allsystems group IP is contained in the list as first entry.
+ * @see @ref netif_set_igmp_mac_filter()
+ */
+#define netif_igmp_data(netif) ((struct igmp_group *)netif_get_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_IGMP))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_IPV4 && LWIP_IGMP */
+
+#endif /* LWIP_HDR_IGMP_H */
diff --git a/lib/lwip/include/lwip/inet.h b/lib/lwip/include/lwip/inet.h
new file mode 100644
index 0000000000..097088550d
--- /dev/null
+++ b/lib/lwip/include/lwip/inet.h
@@ -0,0 +1,188 @@
+/**
+ * @file
+ * This file (together with sockets.h) aims to provide structs and functions from
+ * - arpa/inet.h
+ * - netinet/in.h
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef LWIP_HDR_INET_H
+#define LWIP_HDR_INET_H
+
+#include "lwip/opt.h"
+
+#if LWIP_SOCKET_EXTERNAL_HEADERS
+#include LWIP_SOCKET_EXTERNAL_HEADER_INET_H
+#else /* LWIP_SOCKET_EXTERNAL_HEADERS */
+
+#include "lwip/def.h"
+#include "lwip/ip_addr.h"
+#include "lwip/ip6_addr.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* If your port already typedef's in_addr_t, define IN_ADDR_T_DEFINED
+   to prevent this code from redefining it. */
+#if !defined(in_addr_t) && !defined(IN_ADDR_T_DEFINED)
+typedef u32_t in_addr_t;
+#endif
+
+struct in_addr {
+  in_addr_t s_addr;
+};
+
+struct in6_addr {
+  union {
+    u32_t u32_addr[4];
+    u8_t  u8_addr[16];
+  } un;
+#define s6_addr  un.u8_addr
+};
+
+/** 255.255.255.255 */
+#define INADDR_NONE         IPADDR_NONE
+/** 127.0.0.1 */
+#define INADDR_LOOPBACK     IPADDR_LOOPBACK
+/** 0.0.0.0 */
+#define INADDR_ANY          IPADDR_ANY
+/** 255.255.255.255 */
+#define INADDR_BROADCAST    IPADDR_BROADCAST
+
+/** This macro can be used to initialize a variable of type struct in6_addr
+    to the IPv6 wildcard address. */
+#define IN6ADDR_ANY_INIT {{{0,0,0,0}}}
+/** This macro can be used to initialize a variable of type struct in6_addr
+    to the IPv6 loopback address. */
+#define IN6ADDR_LOOPBACK_INIT {{{0,0,0,PP_HTONL(1)}}}
+/** This variable is initialized by the system to contain the wildcard IPv6 address. */
+extern const struct in6_addr in6addr_any;
+
+/* Definitions of the bits in an (IPv4) Internet address integer.
+
+   On subnets, host and network parts are found according to
+   the subnet mask, not these masks.  */
+#define IN_CLASSA(a)        IP_CLASSA(a)
+#define IN_CLASSA_NET       IP_CLASSA_NET
+#define IN_CLASSA_NSHIFT    IP_CLASSA_NSHIFT
+#define IN_CLASSA_HOST      IP_CLASSA_HOST
+#define IN_CLASSA_MAX       IP_CLASSA_MAX
+
+#define IN_CLASSB(b)        IP_CLASSB(b)
+#define IN_CLASSB_NET       IP_CLASSB_NET
+#define IN_CLASSB_NSHIFT    IP_CLASSB_NSHIFT
+#define IN_CLASSB_HOST      IP_CLASSB_HOST
+#define IN_CLASSB_MAX       IP_CLASSB_MAX
+
+#define IN_CLASSC(c)        IP_CLASSC(c)
+#define IN_CLASSC_NET       IP_CLASSC_NET
+#define IN_CLASSC_NSHIFT    IP_CLASSC_NSHIFT
+#define IN_CLASSC_HOST      IP_CLASSC_HOST
+#define IN_CLASSC_MAX       IP_CLASSC_MAX
+
+#define IN_CLASSD(d)        IP_CLASSD(d)
+#define IN_CLASSD_NET       IP_CLASSD_NET     /* These ones aren't really */
+#define IN_CLASSD_NSHIFT    IP_CLASSD_NSHIFT  /*   net and host fields, but */
+#define IN_CLASSD_HOST      IP_CLASSD_HOST    /*   routing needn't know. */
+#define IN_CLASSD_MAX       IP_CLASSD_MAX
+
+#define IN_MULTICAST(a)     IP_MULTICAST(a)
+
+#define IN_EXPERIMENTAL(a)  IP_EXPERIMENTAL(a)
+#define IN_BADCLASS(a)      IP_BADCLASS(a)
+
+#define IN_LOOPBACKNET      IP_LOOPBACKNET
+
+#define IN6_IS_ADDR_UNSPECIFIED(a)  ip6_addr_isany((ip6_addr_t*)(a))
+#define IN6_IS_ADDR_LOOPBACK(a)     ip6_addr_isloopback((ip6_addr_t*)(a))
+#define IN6_IS_ADDR_MULTICAST(a)    ip6_addr_ismulticast((ip6_addr_t*)(a))
+#define IN6_IS_ADDR_LINKLOCAL(a)    ip6_addr_islinklocal((ip6_addr_t*)(a))
+#define IN6_IS_ADDR_SITELOCAL(a)    ip6_addr_issitelocal((ip6_addr_t*)(a))
+#define IN6_IS_ADDR_V4MAPPED(a)     ip6_addr_isipv4mappedipv6((ip6_addr_t*)(a))
+#define IN6_IS_ADDR_V4COMPAT(a)     ip6_addr_isipv4compat((ip6_addr_t*)(a))
+#define IN6_IS_ADDR_MC_NODELOCAL(a) ip6_addr_ismulticast_iflocal((ip6_addr_t*)(a))
+#define IN6_IS_ADDR_MC_LINKLOCAL(a) ip6_addr_ismulticast_linklocal((ip6_addr_t*)(a))
+#define IN6_IS_ADDR_MC_SITELOCAL(a) ip6_addr_ismulticast_sitelocal((ip6_addr_t*)(a))
+#define IN6_IS_ADDR_MC_ORGLOCAL(a)  ip6_addr_ismulticast_orglocal((ip6_addr_t*)(a))
+#define IN6_IS_ADDR_MC_GLOBAL(a)    ip6_addr_ismulticast_global((ip6_addr_t*)(a))
+
+#ifndef INET_ADDRSTRLEN
+#define INET_ADDRSTRLEN     IP4ADDR_STRLEN_MAX
+#endif
+#if LWIP_IPV6
+#ifndef INET6_ADDRSTRLEN
+#define INET6_ADDRSTRLEN    IP6ADDR_STRLEN_MAX
+#endif
+#endif
+
+#if LWIP_IPV4
+
+#define inet_addr_from_ip4addr(target_inaddr, source_ipaddr) ((target_inaddr)->s_addr = ip4_addr_get_u32(source_ipaddr))
+#define inet_addr_to_ip4addr(target_ipaddr, source_inaddr)   (ip4_addr_set_u32(target_ipaddr, (source_inaddr)->s_addr))
+
+/* directly map this to the lwip internal functions */
+#define inet_addr(cp)                   ipaddr_addr(cp)
+#define inet_aton(cp, addr)             ip4addr_aton(cp, (ip4_addr_t*)addr)
+#define inet_ntoa(addr)                 ip4addr_ntoa((const ip4_addr_t*)&(addr))
+#define inet_ntoa_r(addr, buf, buflen)  ip4addr_ntoa_r((const ip4_addr_t*)&(addr), buf, buflen)
+
+#endif /* LWIP_IPV4 */
+
+#if LWIP_IPV6
+#define inet6_addr_from_ip6addr(target_in6addr, source_ip6addr) {(target_in6addr)->un.u32_addr[0] = (source_ip6addr)->addr[0]; \
+                                                                 (target_in6addr)->un.u32_addr[1] = (source_ip6addr)->addr[1]; \
+                                                                 (target_in6addr)->un.u32_addr[2] = (source_ip6addr)->addr[2]; \
+                                                                 (target_in6addr)->un.u32_addr[3] = (source_ip6addr)->addr[3];}
+#define inet6_addr_to_ip6addr(target_ip6addr, source_in6addr)   {(target_ip6addr)->addr[0] = (source_in6addr)->un.u32_addr[0]; \
+                                                                 (target_ip6addr)->addr[1] = (source_in6addr)->un.u32_addr[1]; \
+                                                                 (target_ip6addr)->addr[2] = (source_in6addr)->un.u32_addr[2]; \
+                                                                 (target_ip6addr)->addr[3] = (source_in6addr)->un.u32_addr[3]; \
+                                                                 ip6_addr_clear_zone(target_ip6addr);}
+
+/* directly map this to the lwip internal functions */
+#define inet6_aton(cp, addr)            ip6addr_aton(cp, (ip6_addr_t*)addr)
+#define inet6_ntoa(addr)                ip6addr_ntoa((const ip6_addr_t*)&(addr))
+#define inet6_ntoa_r(addr, buf, buflen) ip6addr_ntoa_r((const ip6_addr_t*)&(addr), buf, buflen)
+
+#endif /* LWIP_IPV6 */
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_SOCKET_EXTERNAL_HEADERS */
+
+#endif /* LWIP_HDR_INET_H */
diff --git a/lib/lwip/include/lwip/inet_chksum.h b/lib/lwip/include/lwip/inet_chksum.h
new file mode 100644
index 0000000000..33296d287b
--- /dev/null
+++ b/lib/lwip/include/lwip/inet_chksum.h
@@ -0,0 +1,104 @@
+/**
+ * @file
+ * IP checksum calculation functions
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef LWIP_HDR_INET_CHKSUM_H
+#define LWIP_HDR_INET_CHKSUM_H
+
+#include "lwip/opt.h"
+
+#include "lwip/pbuf.h"
+#include "lwip/ip_addr.h"
+
+/** Swap the bytes in an u16_t: much like lwip_htons() for little-endian */
+#ifndef SWAP_BYTES_IN_WORD
+#define SWAP_BYTES_IN_WORD(w) (((w) & 0xff) << 8) | (((w) & 0xff00) >> 8)
+#endif /* SWAP_BYTES_IN_WORD */
+
+/** Split an u32_t in two u16_ts and add them up */
+#ifndef FOLD_U32T
+#define FOLD_U32T(u)          ((u32_t)(((u) >> 16) + ((u) & 0x0000ffffUL)))
+#endif
+
+#if LWIP_CHECKSUM_ON_COPY
+/** Function-like macro: same as MEMCPY but returns the checksum of copied data
+    as u16_t */
+# ifndef LWIP_CHKSUM_COPY
+#  define LWIP_CHKSUM_COPY(dst, src, len) lwip_chksum_copy(dst, src, len)
+#  ifndef LWIP_CHKSUM_COPY_ALGORITHM
+#   define LWIP_CHKSUM_COPY_ALGORITHM 1
+#  endif /* LWIP_CHKSUM_COPY_ALGORITHM */
+# else /* LWIP_CHKSUM_COPY */
+#  define LWIP_CHKSUM_COPY_ALGORITHM 0
+# endif /* LWIP_CHKSUM_COPY */
+#else /* LWIP_CHECKSUM_ON_COPY */
+# define LWIP_CHKSUM_COPY_ALGORITHM 0
+#endif /* LWIP_CHECKSUM_ON_COPY */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+u16_t inet_chksum(const void *dataptr, u16_t len);
+u16_t inet_chksum_pbuf(struct pbuf *p);
+#if LWIP_CHKSUM_COPY_ALGORITHM
+u16_t lwip_chksum_copy(void *dst, const void *src, u16_t len);
+#endif /* LWIP_CHKSUM_COPY_ALGORITHM */
+
+#if LWIP_IPV4
+u16_t inet_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len,
+       const ip4_addr_t *src, const ip4_addr_t *dest);
+u16_t inet_chksum_pseudo_partial(struct pbuf *p, u8_t proto,
+       u16_t proto_len, u16_t chksum_len, const ip4_addr_t *src, const ip4_addr_t *dest);
+#endif /* LWIP_IPV4 */
+
+#if LWIP_IPV6
+u16_t ip6_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len,
+       const ip6_addr_t *src, const ip6_addr_t *dest);
+u16_t ip6_chksum_pseudo_partial(struct pbuf *p, u8_t proto, u16_t proto_len,
+       u16_t chksum_len, const ip6_addr_t *src, const ip6_addr_t *dest);
+#endif /* LWIP_IPV6 */
+
+
+u16_t ip_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len,
+       const ip_addr_t *src, const ip_addr_t *dest);
+u16_t ip_chksum_pseudo_partial(struct pbuf *p, u8_t proto, u16_t proto_len,
+       u16_t chksum_len, const ip_addr_t *src, const ip_addr_t *dest);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_INET_H */
diff --git a/lib/lwip/include/lwip/init.h b/lib/lwip/include/lwip/init.h
new file mode 100644
index 0000000000..43e5e04f23
--- /dev/null
+++ b/lib/lwip/include/lwip/init.h
@@ -0,0 +1,100 @@
+/**
+ * @file
+ * lwIP initialization API
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef LWIP_HDR_INIT_H
+#define LWIP_HDR_INIT_H
+
+#include "lwip/opt.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @defgroup lwip_version Version
+ * @ingroup lwip
+ * @{
+ */
+
+/** X.x.x: Major version of the stack */
+#define LWIP_VERSION_MAJOR      2
+/** x.X.x: Minor version of the stack */
+#define LWIP_VERSION_MINOR      2
+/** x.x.X: Revision of the stack */
+#define LWIP_VERSION_REVISION   0
+/** For release candidates, this is set to 1..254
+  * For official releases, this is set to 255 (LWIP_RC_RELEASE)
+  * For development versions (Git), this is set to 0 (LWIP_RC_DEVELOPMENT) */
+#define LWIP_VERSION_RC         LWIP_RC_RELEASE
+
+/** LWIP_VERSION_RC is set to LWIP_RC_RELEASE for official releases */
+#define LWIP_RC_RELEASE         255
+/** LWIP_VERSION_RC is set to LWIP_RC_DEVELOPMENT for Git versions */
+#define LWIP_RC_DEVELOPMENT     0
+
+#define LWIP_VERSION_IS_RELEASE     (LWIP_VERSION_RC == LWIP_RC_RELEASE)
+#define LWIP_VERSION_IS_DEVELOPMENT (LWIP_VERSION_RC == LWIP_RC_DEVELOPMENT)
+#define LWIP_VERSION_IS_RC          ((LWIP_VERSION_RC != LWIP_RC_RELEASE) && (LWIP_VERSION_RC != LWIP_RC_DEVELOPMENT))
+
+/* Some helper defines to get a version string */
+#define LWIP_VERSTR2(x) #x
+#define LWIP_VERSTR(x) LWIP_VERSTR2(x)
+#if LWIP_VERSION_IS_RELEASE
+#define LWIP_VERSION_STRING_SUFFIX ""
+#elif LWIP_VERSION_IS_DEVELOPMENT
+#define LWIP_VERSION_STRING_SUFFIX "d"
+#else
+#define LWIP_VERSION_STRING_SUFFIX "rc" LWIP_VERSTR(LWIP_VERSION_RC)
+#endif
+
+/** Provides the version of the stack */
+#define LWIP_VERSION   ((LWIP_VERSION_MAJOR) << 24   | (LWIP_VERSION_MINOR) << 16 | \
+                        (LWIP_VERSION_REVISION) << 8 | (LWIP_VERSION_RC))
+/** Provides the version of the stack as string */
+#define LWIP_VERSION_STRING     LWIP_VERSTR(LWIP_VERSION_MAJOR) "." LWIP_VERSTR(LWIP_VERSION_MINOR) "." LWIP_VERSTR(LWIP_VERSION_REVISION) LWIP_VERSION_STRING_SUFFIX
+
+/**
+ * @}
+ */
+
+/* Modules initialization */
+void lwip_init(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_INIT_H */
diff --git a/lib/lwip/include/lwip/init.h.cmake.in b/lib/lwip/include/lwip/init.h.cmake.in
new file mode 100644
index 0000000000..9b609b7426
--- /dev/null
+++ b/lib/lwip/include/lwip/init.h.cmake.in
@@ -0,0 +1,100 @@
+/**
+ * @file
+ * lwIP initialization API
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef LWIP_HDR_INIT_H
+#define LWIP_HDR_INIT_H
+
+#include "lwip/opt.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @defgroup lwip_version Version
+ * @ingroup lwip
+ * @{
+ */
+
+/** X.x.x: Major version of the stack */
+#define LWIP_VERSION_MAJOR      ${LWIP_VERSION_MAJOR}
+/** x.X.x: Minor version of the stack */
+#define LWIP_VERSION_MINOR      ${LWIP_VERSION_MINOR}
+/** x.x.X: Revision of the stack */
+#define LWIP_VERSION_REVISION   ${LWIP_VERSION_REVISION}
+/** For release candidates, this is set to 1..254
+  * For official releases, this is set to 255 (LWIP_RC_RELEASE)
+  * For development versions (Git), this is set to 0 (LWIP_RC_DEVELOPMENT) */
+#define LWIP_VERSION_RC         ${LWIP_VERSION_RC}
+
+/** LWIP_VERSION_RC is set to LWIP_RC_RELEASE for official releases */
+#define LWIP_RC_RELEASE         255
+/** LWIP_VERSION_RC is set to LWIP_RC_DEVELOPMENT for Git versions */
+#define LWIP_RC_DEVELOPMENT     0
+
+#define LWIP_VERSION_IS_RELEASE     (LWIP_VERSION_RC == LWIP_RC_RELEASE)
+#define LWIP_VERSION_IS_DEVELOPMENT (LWIP_VERSION_RC == LWIP_RC_DEVELOPMENT)
+#define LWIP_VERSION_IS_RC          ((LWIP_VERSION_RC != LWIP_RC_RELEASE) && (LWIP_VERSION_RC != LWIP_RC_DEVELOPMENT))
+
+/* Some helper defines to get a version string */
+#define LWIP_VERSTR2(x) #x
+#define LWIP_VERSTR(x) LWIP_VERSTR2(x)
+#if LWIP_VERSION_IS_RELEASE
+#define LWIP_VERSION_STRING_SUFFIX ""
+#elif LWIP_VERSION_IS_DEVELOPMENT
+#define LWIP_VERSION_STRING_SUFFIX "d"
+#else
+#define LWIP_VERSION_STRING_SUFFIX "rc" LWIP_VERSTR(LWIP_VERSION_RC)
+#endif
+
+/** Provides the version of the stack */
+#define LWIP_VERSION   ((LWIP_VERSION_MAJOR) << 24   | (LWIP_VERSION_MINOR) << 16 | \
+                        (LWIP_VERSION_REVISION) << 8 | (LWIP_VERSION_RC))
+/** Provides the version of the stack as string */
+#define LWIP_VERSION_STRING     LWIP_VERSTR(LWIP_VERSION_MAJOR) "." LWIP_VERSTR(LWIP_VERSION_MINOR) "." LWIP_VERSTR(LWIP_VERSION_REVISION) LWIP_VERSION_STRING_SUFFIX
+
+/**
+ * @}
+ */
+
+/* Modules initialization */
+void lwip_init(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_INIT_H */
diff --git a/lib/lwip/include/lwip/ip.h b/lib/lwip/include/lwip/ip.h
new file mode 100644
index 0000000000..668d83177c
--- /dev/null
+++ b/lib/lwip/include/lwip/ip.h
@@ -0,0 +1,339 @@
+/**
+ * @file
+ * IP API
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef LWIP_HDR_IP_H
+#define LWIP_HDR_IP_H
+
+#include "lwip/opt.h"
+
+#include "lwip/def.h"
+#include "lwip/pbuf.h"
+#include "lwip/ip_addr.h"
+#include "lwip/err.h"
+#include "lwip/netif.h"
+#include "lwip/ip4.h"
+#include "lwip/ip6.h"
+#include "lwip/prot/ip.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* This is passed as the destination address to ip_output_if (not
+   to ip_output), meaning that an IP header already is constructed
+   in the pbuf. This is used when TCP retransmits. */
+#define LWIP_IP_HDRINCL  NULL
+
+/** pbufs passed to IP must have a ref-count of 1 as their payload pointer
+    gets altered as the packet is passed down the stack */
+#ifndef LWIP_IP_CHECK_PBUF_REF_COUNT_FOR_TX
+#define LWIP_IP_CHECK_PBUF_REF_COUNT_FOR_TX(p) LWIP_ASSERT("p->ref == 1", (p)->ref == 1)
+#endif
+
+#if LWIP_NETIF_USE_HINTS
+#define IP_PCB_NETIFHINT ;struct netif_hint netif_hints
+#else /* LWIP_NETIF_USE_HINTS */
+#define IP_PCB_NETIFHINT
+#endif /* LWIP_NETIF_USE_HINTS */
+
+/** This is the common part of all PCB types. It needs to be at the
+   beginning of a PCB type definition. It is located here so that
+   changes to this common part are made in one location instead of
+   having to change all PCB structs. */
+#define IP_PCB                             \
+  /* ip addresses in network byte order */ \
+  ip_addr_t local_ip;                      \
+  ip_addr_t remote_ip;                     \
+  /* Bound netif index */                  \
+  u8_t netif_idx;                          \
+  /* Socket options */                     \
+  u8_t so_options;                         \
+  /* Type Of Service */                    \
+  u8_t tos;                                \
+  /* Time To Live */                       \
+  u8_t ttl                                 \
+  /* link layer address resolution hint */ \
+  IP_PCB_NETIFHINT
+
+struct ip_pcb {
+  /* Common members of all PCB types */
+  IP_PCB;
+};
+
+#if LWIP_VLAN_PCP
+#define pcb_has_tci(pcb) ((pcb)->netif_hints.tci >= 0)
+#define pcb_tci_get(pcb) ((pcb)->netif_hints.tci)
+#define pcb_tci_clear(pcb) do { (pcb)->netif_hints.tci = -1; } while(0)
+#define pcb_tci_set(pcb, tci_val) do { (pcb)->netif_hints.tci = (tci_val) & 0xffff; } while(0)
+#define pcb_tci_set_pcp_dei_vid(pcb, pcp, dei, vid) pcb_tci_set(pcb, (((pcp) & 7) << 13) | (((dei) & 1) << 12) | ((vid) & 0xFFF))
+#define pcb_tci_init(pcb) pcb_tci_clear(pcb)
+#else
+#define pcb_tci_init(pcb)
+#endif
+
+/*
+ * Option flags per-socket. These are the same like SO_XXX in sockets.h
+ */
+#define SOF_REUSEADDR     0x04U  /* allow local address reuse */
+#define SOF_KEEPALIVE     0x08U  /* keep connections alive */
+#define SOF_BROADCAST     0x20U  /* permit to send and to receive broadcast messages (see IP_SOF_BROADCAST option) */
+
+/* These flags are inherited (e.g. from a listen-pcb to a connection-pcb): */
+#define SOF_INHERITED   (SOF_REUSEADDR|SOF_KEEPALIVE)
+
+/** Global variables of this module, kept in a struct for efficient access using base+index. */
+struct ip_globals
+{
+  /** The interface that accepted the packet for the current callback invocation. */
+  struct netif *current_netif;
+  /** The interface that received the packet for the current callback invocation. */
+  struct netif *current_input_netif;
+#if LWIP_IPV4
+  /** Header of the input packet currently being processed. */
+  const struct ip_hdr *current_ip4_header;
+#endif /* LWIP_IPV4 */
+#if LWIP_IPV6
+  /** Header of the input IPv6 packet currently being processed. */
+  struct ip6_hdr *current_ip6_header;
+#endif /* LWIP_IPV6 */
+  /** Total header length of current_ip4/6_header (i.e. after this, the UDP/TCP header starts) */
+  u16_t current_ip_header_tot_len;
+  /** Source IP address of current_header */
+  ip_addr_t current_iphdr_src;
+  /** Destination IP address of current_header */
+  ip_addr_t current_iphdr_dest;
+};
+extern struct ip_globals ip_data;
+
+
+/** Get the interface that accepted the current packet.
+ * This may or may not be the receiving netif, depending on your netif/network setup.
+ * This function must only be called from a receive callback (udp_recv,
+ * raw_recv, tcp_accept). It will return NULL otherwise. */
+#define ip_current_netif()      (ip_data.current_netif)
+/** Get the interface that received the current packet.
+ * This function must only be called from a receive callback (udp_recv,
+ * raw_recv, tcp_accept). It will return NULL otherwise. */
+#define ip_current_input_netif() (ip_data.current_input_netif)
+/** Total header length of ip(6)_current_header() (i.e. after this, the UDP/TCP header starts) */
+#define ip_current_header_tot_len() (ip_data.current_ip_header_tot_len)
+/** Source IP address of current_header */
+#define ip_current_src_addr()   (&ip_data.current_iphdr_src)
+/** Destination IP address of current_header */
+#define ip_current_dest_addr()  (&ip_data.current_iphdr_dest)
+
+#if LWIP_IPV4 && LWIP_IPV6
+/** Get the IPv4 header of the current packet.
+ * This function must only be called from a receive callback (udp_recv,
+ * raw_recv, tcp_accept). It will return NULL otherwise. */
+#define ip4_current_header()     ip_data.current_ip4_header
+/** Get the IPv6 header of the current packet.
+ * This function must only be called from a receive callback (udp_recv,
+ * raw_recv, tcp_accept). It will return NULL otherwise. */
+#define ip6_current_header()      ((const struct ip6_hdr*)(ip_data.current_ip6_header))
+/** Returns TRUE if the current IP input packet is IPv6, FALSE if it is IPv4 */
+#define ip_current_is_v6()        (ip6_current_header() != NULL)
+/** Source IPv6 address of current_header */
+#define ip6_current_src_addr()    (ip_2_ip6(&ip_data.current_iphdr_src))
+/** Destination IPv6 address of current_header */
+#define ip6_current_dest_addr()   (ip_2_ip6(&ip_data.current_iphdr_dest))
+/** Get the transport layer protocol */
+#define ip_current_header_proto() (ip_current_is_v6() ? \
+                                   IP6H_NEXTH(ip6_current_header()) :\
+                                   IPH_PROTO(ip4_current_header()))
+/** Get the transport layer header */
+#define ip_next_header_ptr()     ((const void*)((ip_current_is_v6() ? \
+  (const u8_t*)ip6_current_header() : (const u8_t*)ip4_current_header())  + ip_current_header_tot_len()))
+
+/** Source IP4 address of current_header */
+#define ip4_current_src_addr()     (ip_2_ip4(&ip_data.current_iphdr_src))
+/** Destination IP4 address of current_header */
+#define ip4_current_dest_addr()    (ip_2_ip4(&ip_data.current_iphdr_dest))
+
+#elif LWIP_IPV4 /* LWIP_IPV4 && LWIP_IPV6 */
+
+/** Get the IPv4 header of the current packet.
+ * This function must only be called from a receive callback (udp_recv,
+ * raw_recv, tcp_accept). It will return NULL otherwise. */
+#define ip4_current_header()     ip_data.current_ip4_header
+/** Always returns FALSE when only supporting IPv4 only */
+#define ip_current_is_v6()        0
+/** Get the transport layer protocol */
+#define ip_current_header_proto() IPH_PROTO(ip4_current_header())
+/** Get the transport layer header */
+#define ip_next_header_ptr()     ((const void*)((const u8_t*)ip4_current_header() + ip_current_header_tot_len()))
+/** Source IP4 address of current_header */
+#define ip4_current_src_addr()     (&ip_data.current_iphdr_src)
+/** Destination IP4 address of current_header */
+#define ip4_current_dest_addr()    (&ip_data.current_iphdr_dest)
+
+#elif LWIP_IPV6 /* LWIP_IPV4 && LWIP_IPV6 */
+
+/** Get the IPv6 header of the current packet.
+ * This function must only be called from a receive callback (udp_recv,
+ * raw_recv, tcp_accept). It will return NULL otherwise. */
+#define ip6_current_header()      ((const struct ip6_hdr*)(ip_data.current_ip6_header))
+/** Always returns TRUE when only supporting IPv6 only */
+#define ip_current_is_v6()        1
+/** Get the transport layer protocol */
+#define ip_current_header_proto() IP6H_NEXTH(ip6_current_header())
+/** Get the transport layer header */
+#define ip_next_header_ptr()     ((const void*)(((const u8_t*)ip6_current_header()) + ip_current_header_tot_len()))
+/** Source IP6 address of current_header */
+#define ip6_current_src_addr()    (&ip_data.current_iphdr_src)
+/** Destination IP6 address of current_header */
+#define ip6_current_dest_addr()   (&ip_data.current_iphdr_dest)
+
+#endif /* LWIP_IPV6 */
+
+/** Union source address of current_header */
+#define ip_current_src_addr()    (&ip_data.current_iphdr_src)
+/** Union destination address of current_header */
+#define ip_current_dest_addr()   (&ip_data.current_iphdr_dest)
+
+/** Gets an IP pcb option (SOF_* flags) */
+#define ip_get_option(pcb, opt)   ((pcb)->so_options & (opt))
+/** Sets an IP pcb option (SOF_* flags) */
+#define ip_set_option(pcb, opt)   ((pcb)->so_options = (u8_t)((pcb)->so_options | (opt)))
+/** Resets an IP pcb option (SOF_* flags) */
+#define ip_reset_option(pcb, opt) ((pcb)->so_options = (u8_t)((pcb)->so_options & ~(opt)))
+
+#if LWIP_IPV4 && LWIP_IPV6
+/**
+ * @ingroup ip
+ * Output IP packet, netif is selected by source address
+ */
+#define ip_output(p, src, dest, ttl, tos, proto) \
+        (IP_IS_V6(dest) ? \
+        ip6_output(p, ip_2_ip6(src), ip_2_ip6(dest), ttl, tos, proto) : \
+        ip4_output(p, ip_2_ip4(src), ip_2_ip4(dest), ttl, tos, proto))
+/**
+ * @ingroup ip
+ * Output IP packet to specified interface
+ */
+#define ip_output_if(p, src, dest, ttl, tos, proto, netif) \
+        (IP_IS_V6(dest) ? \
+        ip6_output_if(p, ip_2_ip6(src), ip_2_ip6(dest), ttl, tos, proto, netif) : \
+        ip4_output_if(p, ip_2_ip4(src), ip_2_ip4(dest), ttl, tos, proto, netif))
+/**
+ * @ingroup ip
+ * Output IP packet to interface specifying source address
+ */
+#define ip_output_if_src(p, src, dest, ttl, tos, proto, netif) \
+        (IP_IS_V6(dest) ? \
+        ip6_output_if_src(p, ip_2_ip6(src), ip_2_ip6(dest), ttl, tos, proto, netif) : \
+        ip4_output_if_src(p, ip_2_ip4(src), ip_2_ip4(dest), ttl, tos, proto, netif))
+/** Output IP packet that already includes an IP header. */
+#define ip_output_if_hdrincl(p, src, dest, netif) \
+        (IP_IS_V6(dest) ? \
+        ip6_output_if(p, ip_2_ip6(src), LWIP_IP_HDRINCL, 0, 0, 0, netif) : \
+        ip4_output_if(p, ip_2_ip4(src), LWIP_IP_HDRINCL, 0, 0, 0, netif))
+/** Output IP packet with netif_hint */
+#define ip_output_hinted(p, src, dest, ttl, tos, proto, netif_hint) \
+        (IP_IS_V6(dest) ? \
+        ip6_output_hinted(p, ip_2_ip6(src), ip_2_ip6(dest), ttl, tos, proto, netif_hint) : \
+        ip4_output_hinted(p, ip_2_ip4(src), ip_2_ip4(dest), ttl, tos, proto, netif_hint))
+/**
+ * @ingroup ip
+ * Get netif for address combination. See \ref ip6_route and \ref ip4_route
+ */
+#define ip_route(src, dest) \
+        (IP_IS_V6(dest) ? \
+        ip6_route(ip_2_ip6(src), ip_2_ip6(dest)) : \
+        ip4_route_src(ip_2_ip4(src), ip_2_ip4(dest)))
+/**
+ * @ingroup ip
+ * Get netif for IP.
+ */
+#define ip_netif_get_local_ip(netif, dest) (IP_IS_V6(dest) ? \
+        ip6_netif_get_local_ip(netif, ip_2_ip6(dest)) : \
+        ip4_netif_get_local_ip(netif))
+#define ip_debug_print(is_ipv6, p) ((is_ipv6) ? ip6_debug_print(p) : ip4_debug_print(p))
+
+err_t ip_input(struct pbuf *p, struct netif *inp);
+
+#elif LWIP_IPV4 /* LWIP_IPV4 && LWIP_IPV6 */
+
+#define ip_output(p, src, dest, ttl, tos, proto) \
+        ip4_output(p, src, dest, ttl, tos, proto)
+#define ip_output_if(p, src, dest, ttl, tos, proto, netif) \
+        ip4_output_if(p, src, dest, ttl, tos, proto, netif)
+#define ip_output_if_src(p, src, dest, ttl, tos, proto, netif) \
+        ip4_output_if_src(p, src, dest, ttl, tos, proto, netif)
+#define ip_output_hinted(p, src, dest, ttl, tos, proto, netif_hint) \
+        ip4_output_hinted(p, src, dest, ttl, tos, proto, netif_hint)
+#define ip_output_if_hdrincl(p, src, dest, netif) \
+        ip4_output_if(p, src, LWIP_IP_HDRINCL, 0, 0, 0, netif)
+#define ip_route(src, dest) \
+        ip4_route_src(src, dest)
+#define ip_netif_get_local_ip(netif, dest) \
+        ip4_netif_get_local_ip(netif)
+#define ip_debug_print(is_ipv6, p) ip4_debug_print(p)
+
+#define ip_input ip4_input
+
+#elif LWIP_IPV6 /* LWIP_IPV4 && LWIP_IPV6 */
+
+#define ip_output(p, src, dest, ttl, tos, proto) \
+        ip6_output(p, src, dest, ttl, tos, proto)
+#define ip_output_if(p, src, dest, ttl, tos, proto, netif) \
+        ip6_output_if(p, src, dest, ttl, tos, proto, netif)
+#define ip_output_if_src(p, src, dest, ttl, tos, proto, netif) \
+        ip6_output_if_src(p, src, dest, ttl, tos, proto, netif)
+#define ip_output_hinted(p, src, dest, ttl, tos, proto, netif_hint) \
+        ip6_output_hinted(p, src, dest, ttl, tos, proto, netif_hint)
+#define ip_output_if_hdrincl(p, src, dest, netif) \
+        ip6_output_if(p, src, LWIP_IP_HDRINCL, 0, 0, 0, netif)
+#define ip_route(src, dest) \
+        ip6_route(src, dest)
+#define ip_netif_get_local_ip(netif, dest) \
+        ip6_netif_get_local_ip(netif, dest)
+#define ip_debug_print(is_ipv6, p) ip6_debug_print(p)
+
+#define ip_input ip6_input
+
+#endif /* LWIP_IPV6 */
+
+#define ip_route_get_local_ip(src, dest, netif, ipaddr) do { \
+  (netif) = ip_route(src, dest); \
+  (ipaddr) = ip_netif_get_local_ip(netif, dest); \
+}while(0)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_IP_H */
diff --git a/lib/lwip/include/lwip/ip4.h b/lib/lwip/include/lwip/ip4.h
new file mode 100644
index 0000000000..4d7228d06d
--- /dev/null
+++ b/lib/lwip/include/lwip/ip4.h
@@ -0,0 +1,109 @@
+/**
+ * @file
+ * IPv4 API
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef LWIP_HDR_IP4_H
+#define LWIP_HDR_IP4_H
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV4
+
+#include "lwip/def.h"
+#include "lwip/pbuf.h"
+#include "lwip/ip4_addr.h"
+#include "lwip/err.h"
+#include "lwip/netif.h"
+#include "lwip/prot/ip4.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef LWIP_HOOK_IP4_ROUTE_SRC
+#define LWIP_IPV4_SRC_ROUTING   1
+#else
+#define LWIP_IPV4_SRC_ROUTING   0
+#endif
+
+/** Currently, the function ip_output_if_opt() is only used with IGMP */
+#define IP_OPTIONS_SEND   (LWIP_IPV4 && LWIP_IGMP)
+
+#define ip_init() /* Compatibility define, no init needed. */
+struct netif *ip4_route(const ip4_addr_t *dest);
+#if LWIP_IPV4_SRC_ROUTING
+struct netif *ip4_route_src(const ip4_addr_t *src, const ip4_addr_t *dest);
+#else /* LWIP_IPV4_SRC_ROUTING */
+#define ip4_route_src(src, dest) ip4_route(dest)
+#endif /* LWIP_IPV4_SRC_ROUTING */
+err_t ip4_input(struct pbuf *p, struct netif *inp);
+err_t ip4_output(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest,
+       u8_t ttl, u8_t tos, u8_t proto);
+err_t ip4_output_if(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest,
+       u8_t ttl, u8_t tos, u8_t proto, struct netif *netif);
+err_t ip4_output_if_src(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest,
+       u8_t ttl, u8_t tos, u8_t proto, struct netif *netif);
+#if LWIP_NETIF_USE_HINTS
+err_t ip4_output_hinted(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest,
+       u8_t ttl, u8_t tos, u8_t proto, struct netif_hint *netif_hint);
+#endif /* LWIP_NETIF_USE_HINTS */
+#if IP_OPTIONS_SEND
+err_t ip4_output_if_opt(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest,
+       u8_t ttl, u8_t tos, u8_t proto, struct netif *netif, void *ip_options,
+       u16_t optlen);
+err_t ip4_output_if_opt_src(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest,
+       u8_t ttl, u8_t tos, u8_t proto, struct netif *netif, void *ip_options,
+       u16_t optlen);
+#endif /* IP_OPTIONS_SEND */
+
+#if LWIP_MULTICAST_TX_OPTIONS
+void  ip4_set_default_multicast_netif(struct netif* default_multicast_netif);
+#endif /* LWIP_MULTICAST_TX_OPTIONS */
+
+#define ip4_netif_get_local_ip(netif) (((netif) != NULL) ? netif_ip_addr4(netif) : NULL)
+
+#if IP_DEBUG
+void ip4_debug_print(struct pbuf *p);
+#else
+#define ip4_debug_print(p)
+#endif /* IP_DEBUG */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_IPV4 */
+
+#endif /* LWIP_HDR_IP_H */
diff --git a/lib/lwip/include/lwip/ip4_addr.h b/lib/lwip/include/lwip/ip4_addr.h
new file mode 100644
index 0000000000..9fd8a0a183
--- /dev/null
+++ b/lib/lwip/include/lwip/ip4_addr.h
@@ -0,0 +1,225 @@
+/**
+ * @file
+ * IPv4 address API
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef LWIP_HDR_IP4_ADDR_H
+#define LWIP_HDR_IP4_ADDR_H
+
+#include "lwip/opt.h"
+#include "lwip/def.h"
+
+#if LWIP_IPV4
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** This is the aligned version of ip4_addr_t,
+   used as local variable, on the stack, etc. */
+struct ip4_addr {
+  u32_t addr;
+};
+
+/** ip4_addr_t uses a struct for convenience only, so that the same defines can
+ * operate both on ip4_addr_t as well as on ip4_addr_p_t. */
+typedef struct ip4_addr ip4_addr_t;
+
+/* Forward declaration to not include netif.h */
+struct netif;
+
+/** 255.255.255.255 */
+#define IPADDR_NONE         ((u32_t)0xffffffffUL)
+/** 127.0.0.1 */
+#define IPADDR_LOOPBACK     ((u32_t)0x7f000001UL)
+/** 0.0.0.0 */
+#define IPADDR_ANY          ((u32_t)0x00000000UL)
+/** 255.255.255.255 */
+#define IPADDR_BROADCAST    ((u32_t)0xffffffffUL)
+
+/* Definitions of the bits in an Internet address integer.
+
+   On subnets, host and network parts are found according to
+   the subnet mask, not these masks.  */
+#define IP_CLASSA(a)        ((((u32_t)(a)) & 0x80000000UL) == 0)
+#define IP_CLASSA_NET       0xff000000
+#define IP_CLASSA_NSHIFT    24
+#define IP_CLASSA_HOST      (0xffffffff & ~IP_CLASSA_NET)
+#define IP_CLASSA_MAX       128
+
+#define IP_CLASSB(a)        ((((u32_t)(a)) & 0xc0000000UL) == 0x80000000UL)
+#define IP_CLASSB_NET       0xffff0000
+#define IP_CLASSB_NSHIFT    16
+#define IP_CLASSB_HOST      (0xffffffff & ~IP_CLASSB_NET)
+#define IP_CLASSB_MAX       65536
+
+#define IP_CLASSC(a)        ((((u32_t)(a)) & 0xe0000000UL) == 0xc0000000UL)
+#define IP_CLASSC_NET       0xffffff00
+#define IP_CLASSC_NSHIFT    8
+#define IP_CLASSC_HOST      (0xffffffff & ~IP_CLASSC_NET)
+
+#define IP_CLASSD(a)        (((u32_t)(a) & 0xf0000000UL) == 0xe0000000UL)
+#define IP_CLASSD_NET       0xf0000000          /* These ones aren't really */
+#define IP_CLASSD_NSHIFT    28                  /*   net and host fields, but */
+#define IP_CLASSD_HOST      0x0fffffff          /*   routing needn't know. */
+#define IP_MULTICAST(a)     IP_CLASSD(a)
+
+#define IP_EXPERIMENTAL(a)  (((u32_t)(a) & 0xf0000000UL) == 0xf0000000UL)
+#define IP_BADCLASS(a)      (((u32_t)(a) & 0xf0000000UL) == 0xf0000000UL)
+
+#define IP_LOOPBACKNET      127                 /* official! */
+
+/** Set an IP address given by the four byte-parts */
+#define IP4_ADDR(ipaddr, a,b,c,d)  (ipaddr)->addr = PP_HTONL(LWIP_MAKEU32(a,b,c,d))
+
+/** Copy IP address - faster than ip4_addr_set: no NULL check */
+#define ip4_addr_copy(dest, src) ((dest).addr = (src).addr)
+/** Safely copy one IP address to another (src may be NULL) */
+#define ip4_addr_set(dest, src) ((dest)->addr = \
+                                    ((src) == NULL ? 0 : \
+                                    (src)->addr))
+/** Set complete address to zero */
+#define ip4_addr_set_zero(ipaddr)     ((ipaddr)->addr = 0)
+/** Set address to IPADDR_ANY (no need for lwip_htonl()) */
+#define ip4_addr_set_any(ipaddr)      ((ipaddr)->addr = IPADDR_ANY)
+/** Set address to loopback address */
+#define ip4_addr_set_loopback(ipaddr) ((ipaddr)->addr = PP_HTONL(IPADDR_LOOPBACK))
+/** Check if an address is in the loopback region */
+#define ip4_addr_isloopback(ipaddr)    (((ipaddr)->addr & PP_HTONL(IP_CLASSA_NET)) == PP_HTONL(((u32_t)IP_LOOPBACKNET) << 24))
+/** Safely copy one IP address to another and change byte order
+ * from host- to network-order. */
+#define ip4_addr_set_hton(dest, src) ((dest)->addr = \
+                               ((src) == NULL ? 0:\
+                               lwip_htonl((src)->addr)))
+/** IPv4 only: set the IP address given as an u32_t */
+#define ip4_addr_set_u32(dest_ipaddr, src_u32) ((dest_ipaddr)->addr = (src_u32))
+/** IPv4 only: get the IP address as an u32_t */
+#define ip4_addr_get_u32(src_ipaddr) ((src_ipaddr)->addr)
+
+/** Get the network address by combining host address with netmask */
+#define ip4_addr_get_network(target, host, netmask) do { ((target)->addr = ((host)->addr) & ((netmask)->addr)); } while(0)
+
+/**
+ * Determine if two address are on the same network.
+ * @deprecated Renamed to @ref ip4_addr_net_eq
+ */
+#define ip4_addr_netcmp(addr1, addr2, mask) ip4_addr_net_eq(addr1, addr2, mask)
+/**
+ * Determine if two address are on the same network.
+ *
+ * @arg addr1 IP address 1
+ * @arg addr2 IP address 2
+ * @arg mask network identifier mask
+ * @return !0 if the network identifiers of both address match
+ */
+#define ip4_addr_net_eq(addr1, addr2, mask) (((addr1)->addr & \
+                                              (mask)->addr) == \
+                                             ((addr2)->addr & \
+                                              (mask)->addr))
+/**
+ * @deprecated Renamed to ip4_addr_eq
+ */
+#define ip4_addr_cmp(addr1, addr2) ip4_addr_eq(addr1, addr2)
+#define ip4_addr_eq(addr1, addr2) ((addr1)->addr == (addr2)->addr)
+
+#define ip4_addr_isany_val(addr1)   ((addr1).addr == IPADDR_ANY)
+#define ip4_addr_isany(addr1) ((addr1) == NULL || ip4_addr_isany_val(*(addr1)))
+
+#define ip4_addr_isbroadcast(addr1, netif) ip4_addr_isbroadcast_u32((addr1)->addr, netif)
+u8_t ip4_addr_isbroadcast_u32(u32_t addr, const struct netif *netif);
+
+#define ip_addr_netmask_valid(netmask) ip4_addr_netmask_valid((netmask)->addr)
+u8_t ip4_addr_netmask_valid(u32_t netmask);
+
+#define ip4_addr_ismulticast(addr1) (((addr1)->addr & PP_HTONL(0xf0000000UL)) == PP_HTONL(0xe0000000UL))
+
+#define ip4_addr_islinklocal(addr1) (((addr1)->addr & PP_HTONL(0xffff0000UL)) == PP_HTONL(0xa9fe0000UL))
+
+#define ip4_addr_debug_print_parts(debug, a, b, c, d) \
+  LWIP_DEBUGF(debug, ("%" U16_F ".%" U16_F ".%" U16_F ".%" U16_F, a, b, c, d))
+#define ip4_addr_debug_print(debug, ipaddr) \
+  ip4_addr_debug_print_parts(debug, \
+                      (u16_t)((ipaddr) != NULL ? ip4_addr1_16(ipaddr) : 0),       \
+                      (u16_t)((ipaddr) != NULL ? ip4_addr2_16(ipaddr) : 0),       \
+                      (u16_t)((ipaddr) != NULL ? ip4_addr3_16(ipaddr) : 0),       \
+                      (u16_t)((ipaddr) != NULL ? ip4_addr4_16(ipaddr) : 0))
+#define ip4_addr_debug_print_val(debug, ipaddr) \
+  ip4_addr_debug_print_parts(debug, \
+                      ip4_addr1_16_val(ipaddr),       \
+                      ip4_addr2_16_val(ipaddr),       \
+                      ip4_addr3_16_val(ipaddr),       \
+                      ip4_addr4_16_val(ipaddr))
+
+/* Get one byte from the 4-byte address */
+#define ip4_addr_get_byte(ipaddr, idx) (((const u8_t*)(&(ipaddr)->addr))[idx])
+#define ip4_addr1(ipaddr) ip4_addr_get_byte(ipaddr, 0)
+#define ip4_addr2(ipaddr) ip4_addr_get_byte(ipaddr, 1)
+#define ip4_addr3(ipaddr) ip4_addr_get_byte(ipaddr, 2)
+#define ip4_addr4(ipaddr) ip4_addr_get_byte(ipaddr, 3)
+/* Get one byte from the 4-byte address, but argument is 'ip4_addr_t',
+ * not a pointer */
+#define ip4_addr_get_byte_val(ipaddr, idx) ((u8_t)(((ipaddr).addr >> (idx * 8)) & 0xff))
+#define ip4_addr1_val(ipaddr) ip4_addr_get_byte_val(ipaddr, 0)
+#define ip4_addr2_val(ipaddr) ip4_addr_get_byte_val(ipaddr, 1)
+#define ip4_addr3_val(ipaddr) ip4_addr_get_byte_val(ipaddr, 2)
+#define ip4_addr4_val(ipaddr) ip4_addr_get_byte_val(ipaddr, 3)
+/* These are cast to u16_t, with the intent that they are often arguments
+ * to printf using the U16_F format from cc.h. */
+#define ip4_addr1_16(ipaddr) ((u16_t)ip4_addr1(ipaddr))
+#define ip4_addr2_16(ipaddr) ((u16_t)ip4_addr2(ipaddr))
+#define ip4_addr3_16(ipaddr) ((u16_t)ip4_addr3(ipaddr))
+#define ip4_addr4_16(ipaddr) ((u16_t)ip4_addr4(ipaddr))
+#define ip4_addr1_16_val(ipaddr) ((u16_t)ip4_addr1_val(ipaddr))
+#define ip4_addr2_16_val(ipaddr) ((u16_t)ip4_addr2_val(ipaddr))
+#define ip4_addr3_16_val(ipaddr) ((u16_t)ip4_addr3_val(ipaddr))
+#define ip4_addr4_16_val(ipaddr) ((u16_t)ip4_addr4_val(ipaddr))
+
+#define IP4ADDR_STRLEN_MAX  16
+
+/** For backwards compatibility */
+#define ip_ntoa(ipaddr)  ipaddr_ntoa(ipaddr)
+
+u32_t ipaddr_addr(const char *cp);
+int ip4addr_aton(const char *cp, ip4_addr_t *addr);
+/** returns ptr to static buffer; not reentrant! */
+char *ip4addr_ntoa(const ip4_addr_t *addr);
+char *ip4addr_ntoa_r(const ip4_addr_t *addr, char *buf, int buflen);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_IPV4 */
+
+#endif /* LWIP_HDR_IP_ADDR_H */
diff --git a/lib/lwip/include/lwip/ip4_frag.h b/lib/lwip/include/lwip/ip4_frag.h
new file mode 100644
index 0000000000..ed5bf14a31
--- /dev/null
+++ b/lib/lwip/include/lwip/ip4_frag.h
@@ -0,0 +1,100 @@
+/**
+ * @file
+ * IP fragmentation/reassembly
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Jani Monoses <jani@iv.ro>
+ *
+ */
+
+#ifndef LWIP_HDR_IP4_FRAG_H
+#define LWIP_HDR_IP4_FRAG_H
+
+#include "lwip/opt.h"
+#include "lwip/err.h"
+#include "lwip/pbuf.h"
+#include "lwip/netif.h"
+#include "lwip/ip_addr.h"
+#include "lwip/ip.h"
+
+#if LWIP_IPV4
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if IP_REASSEMBLY
+/* The IP reassembly timer interval in milliseconds. */
+#define IP_TMR_INTERVAL 1000
+
+/** IP reassembly helper struct.
+ * This is exported because memp needs to know the size.
+ */
+struct ip_reassdata {
+  struct ip_reassdata *next;
+  struct pbuf *p;
+  struct ip_hdr iphdr;
+  u16_t datagram_len;
+  u8_t flags;
+  u8_t timer;
+};
+
+void ip_reass_init(void);
+void ip_reass_tmr(void);
+struct pbuf * ip4_reass(struct pbuf *p);
+#endif /* IP_REASSEMBLY */
+
+#if IP_FRAG
+#if !LWIP_NETIF_TX_SINGLE_PBUF
+#ifndef LWIP_PBUF_CUSTOM_REF_DEFINED
+#define LWIP_PBUF_CUSTOM_REF_DEFINED
+/** A custom pbuf that holds a reference to another pbuf, which is freed
+ * when this custom pbuf is freed. This is used to create a custom PBUF_REF
+ * that points into the original pbuf. */
+struct pbuf_custom_ref {
+  /** 'base class' */
+  struct pbuf_custom pc;
+  /** pointer to the original pbuf that is referenced */
+  struct pbuf *original;
+};
+#endif /* LWIP_PBUF_CUSTOM_REF_DEFINED */
+#endif /* !LWIP_NETIF_TX_SINGLE_PBUF */
+
+err_t ip4_frag(struct pbuf *p, struct netif *netif, const ip4_addr_t *dest);
+#endif /* IP_FRAG */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_IPV4 */
+
+#endif /* LWIP_HDR_IP4_FRAG_H */
diff --git a/lib/lwip/include/lwip/ip6.h b/lib/lwip/include/lwip/ip6.h
new file mode 100644
index 0000000000..f894e063ef
--- /dev/null
+++ b/lib/lwip/include/lwip/ip6.h
@@ -0,0 +1,93 @@
+/**
+ * @file
+ *
+ * IPv6 layer.
+ */
+
+/*
+ * Copyright (c) 2010 Inico Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Ivan Delamer <delamer@inicotech.com>
+ *
+ *
+ * Please coordinate changes and requests with Ivan Delamer
+ * <delamer@inicotech.com>
+ */
+#ifndef LWIP_HDR_IP6_H
+#define LWIP_HDR_IP6_H
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV6  /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/ip6_addr.h"
+#include "lwip/prot/ip6.h"
+#include "lwip/def.h"
+#include "lwip/pbuf.h"
+#include "lwip/netif.h"
+
+#include "lwip/err.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct netif *ip6_route(const ip6_addr_t *src, const ip6_addr_t *dest);
+const ip_addr_t *ip6_select_source_address(struct netif *netif, const ip6_addr_t * dest);
+err_t         ip6_input(struct pbuf *p, struct netif *inp);
+err_t         ip6_output(struct pbuf *p, const ip6_addr_t *src, const ip6_addr_t *dest,
+                         u8_t hl, u8_t tc, u8_t nexth);
+err_t         ip6_output_if(struct pbuf *p, const ip6_addr_t *src, const ip6_addr_t *dest,
+                            u8_t hl, u8_t tc, u8_t nexth, struct netif *netif);
+err_t         ip6_output_if_src(struct pbuf *p, const ip6_addr_t *src, const ip6_addr_t *dest,
+                            u8_t hl, u8_t tc, u8_t nexth, struct netif *netif);
+#if LWIP_NETIF_USE_HINTS
+err_t         ip6_output_hinted(struct pbuf *p, const ip6_addr_t *src, const ip6_addr_t *dest,
+                                u8_t hl, u8_t tc, u8_t nexth, struct netif_hint *netif_hint);
+#endif /* LWIP_NETIF_USE_HINTS */
+#if LWIP_IPV6_MLD
+err_t         ip6_options_add_hbh_ra(struct pbuf * p, u8_t nexth, u8_t value);
+#endif /* LWIP_IPV6_MLD */
+
+#define ip6_netif_get_local_ip(netif, dest) (((netif) != NULL) ? \
+  ip6_select_source_address(netif, dest) : NULL)
+
+#if IP6_DEBUG
+void ip6_debug_print(struct pbuf *p);
+#else
+#define ip6_debug_print(p)
+#endif /* IP6_DEBUG */
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_IPV6 */
+
+#endif /* LWIP_HDR_IP6_H */
diff --git a/lib/lwip/include/lwip/ip6_addr.h b/lib/lwip/include/lwip/ip6_addr.h
new file mode 100644
index 0000000000..5937fed9f0
--- /dev/null
+++ b/lib/lwip/include/lwip/ip6_addr.h
@@ -0,0 +1,372 @@
+/**
+ * @file
+ *
+ * IPv6 addresses.
+ */
+
+/*
+ * Copyright (c) 2010 Inico Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Ivan Delamer <delamer@inicotech.com>
+ *
+ * Structs and macros for handling IPv6 addresses.
+ *
+ * Please coordinate changes and requests with Ivan Delamer
+ * <delamer@inicotech.com>
+ */
+#ifndef LWIP_HDR_IP6_ADDR_H
+#define LWIP_HDR_IP6_ADDR_H
+
+#include "lwip/opt.h"
+#include "def.h"
+
+#if LWIP_IPV6  /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/ip6_zone.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/** This is the aligned version of ip6_addr_t,
+    used as local variable, on the stack, etc. */
+struct ip6_addr {
+  u32_t addr[4];
+#if LWIP_IPV6_SCOPES
+  u8_t zone;
+#endif /* LWIP_IPV6_SCOPES */
+};
+
+/** IPv6 address */
+typedef struct ip6_addr ip6_addr_t;
+
+/** Set an IPv6 partial address given by byte-parts */
+#define IP6_ADDR_PART(ip6addr, index, a,b,c,d) \
+  (ip6addr)->addr[index] = PP_HTONL(LWIP_MAKEU32(a,b,c,d))
+
+/** Set a full IPv6 address by passing the 4 u32_t indices in network byte order
+    (use PP_HTONL() for constants) */
+#define IP6_ADDR(ip6addr, idx0, idx1, idx2, idx3) do { \
+  (ip6addr)->addr[0] = idx0; \
+  (ip6addr)->addr[1] = idx1; \
+  (ip6addr)->addr[2] = idx2; \
+  (ip6addr)->addr[3] = idx3; \
+  ip6_addr_clear_zone(ip6addr); } while(0)
+
+/** Access address in 16-bit block */
+#define IP6_ADDR_BLOCK1(ip6addr) ((u16_t)((lwip_htonl((ip6addr)->addr[0]) >> 16) & 0xffff))
+/** Access address in 16-bit block */
+#define IP6_ADDR_BLOCK2(ip6addr) ((u16_t)((lwip_htonl((ip6addr)->addr[0])) & 0xffff))
+/** Access address in 16-bit block */
+#define IP6_ADDR_BLOCK3(ip6addr) ((u16_t)((lwip_htonl((ip6addr)->addr[1]) >> 16) & 0xffff))
+/** Access address in 16-bit block */
+#define IP6_ADDR_BLOCK4(ip6addr) ((u16_t)((lwip_htonl((ip6addr)->addr[1])) & 0xffff))
+/** Access address in 16-bit block */
+#define IP6_ADDR_BLOCK5(ip6addr) ((u16_t)((lwip_htonl((ip6addr)->addr[2]) >> 16) & 0xffff))
+/** Access address in 16-bit block */
+#define IP6_ADDR_BLOCK6(ip6addr) ((u16_t)((lwip_htonl((ip6addr)->addr[2])) & 0xffff))
+/** Access address in 16-bit block */
+#define IP6_ADDR_BLOCK7(ip6addr) ((u16_t)((lwip_htonl((ip6addr)->addr[3]) >> 16) & 0xffff))
+/** Access address in 16-bit block */
+#define IP6_ADDR_BLOCK8(ip6addr) ((u16_t)((lwip_htonl((ip6addr)->addr[3])) & 0xffff))
+
+/** Copy IPv6 address - faster than ip6_addr_set: no NULL check */
+#define ip6_addr_copy(dest, src) do{(dest).addr[0] = (src).addr[0]; \
+                                    (dest).addr[1] = (src).addr[1]; \
+                                    (dest).addr[2] = (src).addr[2]; \
+                                    (dest).addr[3] = (src).addr[3]; \
+                                    ip6_addr_copy_zone((dest), (src)); }while(0)
+/** Safely copy one IPv6 address to another (src may be NULL) */
+#define ip6_addr_set(dest, src) do{(dest)->addr[0] = (src) == NULL ? 0 : (src)->addr[0]; \
+                                   (dest)->addr[1] = (src) == NULL ? 0 : (src)->addr[1]; \
+                                   (dest)->addr[2] = (src) == NULL ? 0 : (src)->addr[2]; \
+                                   (dest)->addr[3] = (src) == NULL ? 0 : (src)->addr[3]; \
+                                   ip6_addr_set_zone((dest), (src) == NULL ? IP6_NO_ZONE : ip6_addr_zone(src)); }while(0)
+
+/** Copy packed IPv6 address to unpacked IPv6 address; zone is not set */
+#define ip6_addr_copy_from_packed(dest, src) do{(dest).addr[0] = (src).addr[0]; \
+                                    (dest).addr[1] = (src).addr[1]; \
+                                    (dest).addr[2] = (src).addr[2]; \
+                                    (dest).addr[3] = (src).addr[3]; \
+                                    ip6_addr_clear_zone(&dest); }while(0)
+
+/** Copy unpacked IPv6 address to packed IPv6 address; zone is lost */
+#define ip6_addr_copy_to_packed(dest, src) do{(dest).addr[0] = (src).addr[0]; \
+                                    (dest).addr[1] = (src).addr[1]; \
+                                    (dest).addr[2] = (src).addr[2]; \
+                                    (dest).addr[3] = (src).addr[3]; }while(0)
+
+/** Set complete address to zero */
+#define ip6_addr_set_zero(ip6addr)    do{(ip6addr)->addr[0] = 0; \
+                                         (ip6addr)->addr[1] = 0; \
+                                         (ip6addr)->addr[2] = 0; \
+                                         (ip6addr)->addr[3] = 0; \
+                                         ip6_addr_clear_zone(ip6addr);}while(0)
+
+/** Set address to ipv6 'any' (no need for lwip_htonl()) */
+#define ip6_addr_set_any(ip6addr)       ip6_addr_set_zero(ip6addr)
+/** Set address to ipv6 loopback address */
+#define ip6_addr_set_loopback(ip6addr) do{(ip6addr)->addr[0] = 0; \
+                                          (ip6addr)->addr[1] = 0; \
+                                          (ip6addr)->addr[2] = 0; \
+                                          (ip6addr)->addr[3] = PP_HTONL(0x00000001UL); \
+                                          ip6_addr_clear_zone(ip6addr);}while(0)
+/** Safely copy one IPv6 address to another and change byte order
+ * from host- to network-order. */
+#define ip6_addr_set_hton(dest, src) do{(dest)->addr[0] = (src) == NULL ? 0 : lwip_htonl((src)->addr[0]); \
+                                        (dest)->addr[1] = (src) == NULL ? 0 : lwip_htonl((src)->addr[1]); \
+                                        (dest)->addr[2] = (src) == NULL ? 0 : lwip_htonl((src)->addr[2]); \
+                                        (dest)->addr[3] = (src) == NULL ? 0 : lwip_htonl((src)->addr[3]); \
+                                        ip6_addr_set_zone((dest), (src) == NULL ? IP6_NO_ZONE : ip6_addr_zone(src));}while(0)
+
+
+/** @deprecated Renamed to @ref ip6_addr_net_zoneless_eq */
+#define ip6_addr_netcmp_zoneless(addr1, addr2) ip6_addr_net_zoneless_eq(addr1, addr2)
+/** Compare IPv6 networks, ignoring zone information. To be used sparingly! */
+#define ip6_addr_net_zoneless_eq(addr1, addr2) (((addr1)->addr[0] == (addr2)->addr[0]) && \
+                                               ((addr1)->addr[1] == (addr2)->addr[1]))
+
+/**
+ * Determine if two IPv6 address are on the same network.
+ * @deprecated Renamed to @ref ip6_addr_net_eq
+ */
+#define ip6_addr_netcmp(addr1, addr2) ip6_addr_net_eq(addr1, addr2)
+/**
+ * Determine if two IPv6 address are on the same network.
+ *
+ * @param addr1 IPv6 address 1
+ * @param addr2 IPv6 address 2
+ * @return 1 if the network identifiers of both address match, 0 if not
+ */
+#define ip6_addr_net_eq(addr1, addr2) (ip6_addr_net_zoneless_eq((addr1), (addr2)) && \
+                                       ip6_addr_zone_eq((addr1), (addr2)))
+
+#define ip6_addr_nethostcmp(addr1, addr2) ip6_addr_nethost_eq(addr1, addr2)
+/* Exact-host comparison *after* ip6_addr_net_eq() succeeded, for efficiency. */
+#define ip6_addr_nethost_eq(addr1, addr2) (((addr1)->addr[2] == (addr2)->addr[2]) && \
+                                           ((addr1)->addr[3] == (addr2)->addr[3]))
+
+/** @deprecated Renamed to @ref ip6_addr_zoneless_eq */
+#define ip6_addr_cmp_zoneless(addr1, addr2) ip6_addr_zoneless_eq(addr1, addr2)
+/** Compare IPv6 addresses, ignoring zone information. To be used sparingly! */
+#define ip6_addr_zoneless_eq(addr1, addr2) (((addr1)->addr[0] == (addr2)->addr[0]) && \
+                                    ((addr1)->addr[1] == (addr2)->addr[1]) && \
+                                    ((addr1)->addr[2] == (addr2)->addr[2]) && \
+                                    ((addr1)->addr[3] == (addr2)->addr[3]))
+/** @deprecated Renamed to @ref ip6_addr_eq */
+#define ip6_addr_cmp(addr1, addr2) ip6_addr_eq(addr1, addr2)
+/**
+ * Determine if two IPv6 addresses are the same. In particular, the address
+ * part of both must be the same, and the zone must be compatible.
+ *
+ * @param addr1 IPv6 address 1
+ * @param addr2 IPv6 address 2
+ * @return 1 if the addresses are considered equal, 0 if not
+ */
+#define ip6_addr_eq(addr1, addr2) (ip6_addr_zoneless_eq((addr1), (addr2)) && \
+                                    ip6_addr_zone_eq((addr1), (addr2)))
+
+/** @deprecated Renamed to @ref ip6_addr_packed_eq */
+#define ip6_addr_cmp_packed(ip6addr, paddr, zone_idx) ip6_addr_packed_eq(ip6addr, paddr, zone_idx)
+/** Compare IPv6 address to packed address and zone */
+#define ip6_addr_packed_eq(ip6addr, paddr, zone_idx) (((ip6addr)->addr[0] == (paddr)->addr[0]) && \
+                                    ((ip6addr)->addr[1] == (paddr)->addr[1]) && \
+                                    ((ip6addr)->addr[2] == (paddr)->addr[2]) && \
+                                    ((ip6addr)->addr[3] == (paddr)->addr[3]) && \
+                                    ip6_addr_equals_zone((ip6addr), (zone_idx)))
+
+#define ip6_get_subnet_id(ip6addr)   (lwip_htonl((ip6addr)->addr[2]) & 0x0000ffffUL)
+
+#define ip6_addr_isany_val(ip6addr) (((ip6addr).addr[0] == 0) && \
+                                     ((ip6addr).addr[1] == 0) && \
+                                     ((ip6addr).addr[2] == 0) && \
+                                     ((ip6addr).addr[3] == 0))
+#define ip6_addr_isany(ip6addr) (((ip6addr) == NULL) || ip6_addr_isany_val(*(ip6addr)))
+
+#define ip6_addr_isloopback(ip6addr) (((ip6addr)->addr[0] == 0UL) && \
+                                      ((ip6addr)->addr[1] == 0UL) && \
+                                      ((ip6addr)->addr[2] == 0UL) && \
+                                      ((ip6addr)->addr[3] == PP_HTONL(0x00000001UL)))
+
+#define ip6_addr_isglobal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xe0000000UL)) == PP_HTONL(0x20000000UL))
+
+#define ip6_addr_islinklocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xffc00000UL)) == PP_HTONL(0xfe800000UL))
+
+#define ip6_addr_issitelocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xffc00000UL)) == PP_HTONL(0xfec00000UL))
+
+#define ip6_addr_isuniquelocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xfe000000UL)) == PP_HTONL(0xfc000000UL))
+
+#define ip6_addr_isipv4mappedipv6(ip6addr) (((ip6addr)->addr[0] == 0) && ((ip6addr)->addr[1] == 0) && (((ip6addr)->addr[2]) == PP_HTONL(0x0000FFFFUL)))
+
+#define ip6_addr_isipv4compat(ip6addr) (((ip6addr)->addr[0] == 0UL) && \
+                                        ((ip6addr)->addr[1] == 0UL) && \
+                                        ((ip6addr)->addr[2] == 0UL) && \
+                                        (htonl((ip6addr)->addr[3]) > 1))
+
+#define ip6_addr_ismulticast(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xff000000UL)) == PP_HTONL(0xff000000UL))
+#define ip6_addr_multicast_transient_flag(ip6addr)  ((ip6addr)->addr[0] & PP_HTONL(0x00100000UL))
+#define ip6_addr_multicast_prefix_flag(ip6addr)     ((ip6addr)->addr[0] & PP_HTONL(0x00200000UL))
+#define ip6_addr_multicast_rendezvous_flag(ip6addr) ((ip6addr)->addr[0] & PP_HTONL(0x00400000UL))
+#define ip6_addr_multicast_scope(ip6addr) ((lwip_htonl((ip6addr)->addr[0]) >> 16) & 0xf)
+#define IP6_MULTICAST_SCOPE_RESERVED            0x0
+#define IP6_MULTICAST_SCOPE_RESERVED0           0x0
+#define IP6_MULTICAST_SCOPE_INTERFACE_LOCAL     0x1
+#define IP6_MULTICAST_SCOPE_LINK_LOCAL          0x2
+#define IP6_MULTICAST_SCOPE_RESERVED3           0x3
+#define IP6_MULTICAST_SCOPE_ADMIN_LOCAL         0x4
+#define IP6_MULTICAST_SCOPE_SITE_LOCAL          0x5
+#define IP6_MULTICAST_SCOPE_ORGANIZATION_LOCAL  0x8
+#define IP6_MULTICAST_SCOPE_GLOBAL              0xe
+#define IP6_MULTICAST_SCOPE_RESERVEDF           0xf
+#define ip6_addr_ismulticast_iflocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xff8f0000UL)) == PP_HTONL(0xff010000UL))
+#define ip6_addr_ismulticast_linklocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xff8f0000UL)) == PP_HTONL(0xff020000UL))
+#define ip6_addr_ismulticast_adminlocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xff8f0000UL)) == PP_HTONL(0xff040000UL))
+#define ip6_addr_ismulticast_sitelocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xff8f0000UL)) == PP_HTONL(0xff050000UL))
+#define ip6_addr_ismulticast_orglocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xff8f0000UL)) == PP_HTONL(0xff080000UL))
+#define ip6_addr_ismulticast_global(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xff8f0000UL)) == PP_HTONL(0xff0e0000UL))
+
+/* Scoping note: while interface-local and link-local multicast addresses do
+ * have a scope (i.e., they are meaningful only in the context of a particular
+ * interface), the following functions are not assigning or comparing zone
+ * indices. The reason for this is backward compatibility. Any call site that
+ * produces a non-global multicast address must assign a multicast address as
+ * appropriate itself. */
+
+#define ip6_addr_isallnodes_iflocal(ip6addr) (((ip6addr)->addr[0] == PP_HTONL(0xff010000UL)) && \
+    ((ip6addr)->addr[1] == 0UL) && \
+    ((ip6addr)->addr[2] == 0UL) && \
+    ((ip6addr)->addr[3] == PP_HTONL(0x00000001UL)))
+
+#define ip6_addr_isallnodes_linklocal(ip6addr) (((ip6addr)->addr[0] == PP_HTONL(0xff020000UL)) && \
+    ((ip6addr)->addr[1] == 0UL) && \
+    ((ip6addr)->addr[2] == 0UL) && \
+    ((ip6addr)->addr[3] == PP_HTONL(0x00000001UL)))
+#define ip6_addr_set_allnodes_linklocal(ip6addr) do{(ip6addr)->addr[0] = PP_HTONL(0xff020000UL); \
+                (ip6addr)->addr[1] = 0; \
+                (ip6addr)->addr[2] = 0; \
+                (ip6addr)->addr[3] = PP_HTONL(0x00000001UL); \
+                ip6_addr_clear_zone(ip6addr); }while(0)
+
+#define ip6_addr_isallrouters_linklocal(ip6addr) (((ip6addr)->addr[0] == PP_HTONL(0xff020000UL)) && \
+    ((ip6addr)->addr[1] == 0UL) && \
+    ((ip6addr)->addr[2] == 0UL) && \
+    ((ip6addr)->addr[3] == PP_HTONL(0x00000002UL)))
+#define ip6_addr_set_allrouters_linklocal(ip6addr) do{(ip6addr)->addr[0] = PP_HTONL(0xff020000UL); \
+                (ip6addr)->addr[1] = 0; \
+                (ip6addr)->addr[2] = 0; \
+                (ip6addr)->addr[3] = PP_HTONL(0x00000002UL); \
+                ip6_addr_clear_zone(ip6addr); }while(0)
+
+#define ip6_addr_issolicitednode(ip6addr) ( ((ip6addr)->addr[0] == PP_HTONL(0xff020000UL)) && \
+        ((ip6addr)->addr[2] == PP_HTONL(0x00000001UL)) && \
+        (((ip6addr)->addr[3] & PP_HTONL(0xff000000UL)) == PP_HTONL(0xff000000UL)) )
+
+#define ip6_addr_set_solicitednode(ip6addr, if_id) do{(ip6addr)->addr[0] = PP_HTONL(0xff020000UL); \
+                (ip6addr)->addr[1] = 0; \
+                (ip6addr)->addr[2] = PP_HTONL(0x00000001UL); \
+                (ip6addr)->addr[3] = (PP_HTONL(0xff000000UL) | (if_id)); \
+                ip6_addr_clear_zone(ip6addr); }while(0)
+
+#define ip6_addr_cmp_solicitednode(ip6addr, sn_addr) ip6_addr_solicitednode_eq(ip6addr, sn_addr)
+#define ip6_addr_solicitednode_eq(ip6addr, sn_addr) (((ip6addr)->addr[0] == PP_HTONL(0xff020000UL)) && \
+                                    ((ip6addr)->addr[1] == 0) && \
+                                    ((ip6addr)->addr[2] == PP_HTONL(0x00000001UL)) && \
+                                    ((ip6addr)->addr[3] == (PP_HTONL(0xff000000UL) | (sn_addr)->addr[3])))
+
+/* IPv6 address states. */
+#define IP6_ADDR_INVALID      0x00
+#define IP6_ADDR_TENTATIVE    0x08
+#define IP6_ADDR_TENTATIVE_1  0x09 /* 1 probe sent */
+#define IP6_ADDR_TENTATIVE_2  0x0a /* 2 probes sent */
+#define IP6_ADDR_TENTATIVE_3  0x0b /* 3 probes sent */
+#define IP6_ADDR_TENTATIVE_4  0x0c /* 4 probes sent */
+#define IP6_ADDR_TENTATIVE_5  0x0d /* 5 probes sent */
+#define IP6_ADDR_TENTATIVE_6  0x0e /* 6 probes sent */
+#define IP6_ADDR_TENTATIVE_7  0x0f /* 7 probes sent */
+#define IP6_ADDR_VALID        0x10 /* This bit marks an address as valid (preferred or deprecated) */
+#define IP6_ADDR_PREFERRED    0x30
+#define IP6_ADDR_DEPRECATED   0x10 /* Same as VALID (valid but not preferred) */
+#define IP6_ADDR_DUPLICATED   0x40 /* Failed DAD test, not valid */
+
+#define IP6_ADDR_TENTATIVE_COUNT_MASK 0x07 /* 1-7 probes sent */
+
+#define ip6_addr_isinvalid(addr_state) (addr_state == IP6_ADDR_INVALID)
+#define ip6_addr_istentative(addr_state) (addr_state & IP6_ADDR_TENTATIVE)
+#define ip6_addr_isvalid(addr_state) (addr_state & IP6_ADDR_VALID) /* Include valid, preferred, and deprecated. */
+#define ip6_addr_ispreferred(addr_state) (addr_state == IP6_ADDR_PREFERRED)
+#define ip6_addr_isdeprecated(addr_state) (addr_state == IP6_ADDR_DEPRECATED)
+#define ip6_addr_isduplicated(addr_state) (addr_state == IP6_ADDR_DUPLICATED)
+
+#if LWIP_IPV6_ADDRESS_LIFETIMES
+#define IP6_ADDR_LIFE_STATIC   (0)
+#define IP6_ADDR_LIFE_INFINITE (0xffffffffUL)
+#define ip6_addr_life_isstatic(addr_life) ((addr_life) == IP6_ADDR_LIFE_STATIC)
+#define ip6_addr_life_isinfinite(addr_life) ((addr_life) == IP6_ADDR_LIFE_INFINITE)
+#endif /* LWIP_IPV6_ADDRESS_LIFETIMES */
+
+#define ip6_addr_debug_print_parts(debug, a, b, c, d, e, f, g, h) \
+  LWIP_DEBUGF(debug, ("%" X16_F ":%" X16_F ":%" X16_F ":%" X16_F ":%" X16_F ":%" X16_F ":%" X16_F ":%" X16_F, \
+                      a, b, c, d, e, f, g, h))
+#define ip6_addr_debug_print(debug, ipaddr) \
+  ip6_addr_debug_print_parts(debug, \
+                      (u16_t)((ipaddr) != NULL ? IP6_ADDR_BLOCK1(ipaddr) : 0),    \
+                      (u16_t)((ipaddr) != NULL ? IP6_ADDR_BLOCK2(ipaddr) : 0),    \
+                      (u16_t)((ipaddr) != NULL ? IP6_ADDR_BLOCK3(ipaddr) : 0),    \
+                      (u16_t)((ipaddr) != NULL ? IP6_ADDR_BLOCK4(ipaddr) : 0),    \
+                      (u16_t)((ipaddr) != NULL ? IP6_ADDR_BLOCK5(ipaddr) : 0),    \
+                      (u16_t)((ipaddr) != NULL ? IP6_ADDR_BLOCK6(ipaddr) : 0),    \
+                      (u16_t)((ipaddr) != NULL ? IP6_ADDR_BLOCK7(ipaddr) : 0),    \
+                      (u16_t)((ipaddr) != NULL ? IP6_ADDR_BLOCK8(ipaddr) : 0))
+#define ip6_addr_debug_print_val(debug, ipaddr) \
+  ip6_addr_debug_print_parts(debug, \
+                      IP6_ADDR_BLOCK1(&(ipaddr)),    \
+                      IP6_ADDR_BLOCK2(&(ipaddr)),    \
+                      IP6_ADDR_BLOCK3(&(ipaddr)),    \
+                      IP6_ADDR_BLOCK4(&(ipaddr)),    \
+                      IP6_ADDR_BLOCK5(&(ipaddr)),    \
+                      IP6_ADDR_BLOCK6(&(ipaddr)),    \
+                      IP6_ADDR_BLOCK7(&(ipaddr)),    \
+                      IP6_ADDR_BLOCK8(&(ipaddr)))
+
+#define IP6ADDR_STRLEN_MAX    46
+
+int ip6addr_aton(const char *cp, ip6_addr_t *addr);
+/** returns ptr to static buffer; not reentrant! */
+char *ip6addr_ntoa(const ip6_addr_t *addr);
+char *ip6addr_ntoa_r(const ip6_addr_t *addr, char *buf, int buflen);
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_IPV6 */
+
+#endif /* LWIP_HDR_IP6_ADDR_H */
diff --git a/lib/lwip/include/lwip/ip6_frag.h b/lib/lwip/include/lwip/ip6_frag.h
new file mode 100644
index 0000000000..87e0e86a67
--- /dev/null
+++ b/lib/lwip/include/lwip/ip6_frag.h
@@ -0,0 +1,144 @@
+/**
+ * @file
+ *
+ * IPv6 fragmentation and reassembly.
+ */
+
+/*
+ * Copyright (c) 2010 Inico Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Ivan Delamer <delamer@inicotech.com>
+ *
+ *
+ * Please coordinate changes and requests with Ivan Delamer
+ * <delamer@inicotech.com>
+ */
+#ifndef LWIP_HDR_IP6_FRAG_H
+#define LWIP_HDR_IP6_FRAG_H
+
+#include "lwip/opt.h"
+#include "lwip/pbuf.h"
+#include "lwip/ip6_addr.h"
+#include "lwip/ip6.h"
+#include "lwip/netif.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#if LWIP_IPV6 && LWIP_IPV6_REASS  /* don't build if not configured for use in lwipopts.h */
+
+/** The IPv6 reassembly timer interval in milliseconds. */
+#define IP6_REASS_TMR_INTERVAL 1000
+
+/** IP6_FRAG_COPYHEADER==1: for platforms where sizeof(void*) > 4, "struct
+ * ip6_reass_helper" is too large to be stored in the IPv6 fragment header, and
+ * will bleed into the header before it, which may be the IPv6 header or an
+ * extension header. This means that for each first fragment packet, we need to
+ * 1) make a copy of some IPv6 header fields (src+dest) that we need later on,
+ * just in case we do overwrite part of the IPv6 header, and 2) make a copy of
+ * the header data that we overwrote, so that we can restore it before either
+ * completing reassembly or sending an ICMPv6 reply. The last part is true even
+ * if this setting is disabled, but if it is enabled, we need to save a bit
+ * more data (up to the size of a pointer) because we overwrite more. */
+#ifndef IPV6_FRAG_COPYHEADER
+#define IPV6_FRAG_COPYHEADER   0
+#endif
+
+/* With IPV6_FRAG_COPYHEADER==1, a helper structure may (or, depending on the
+ * presence of extensions, may not) overwrite part of the IP header. Therefore,
+ * we copy the fields that we need from the IP header for as long as the helper
+ * structure may still be in place. This is easier than temporarily restoring
+ * those fields in the IP header each time we need to perform checks on them. */
+#if IPV6_FRAG_COPYHEADER
+#define IPV6_FRAG_SRC(ipr) ((ipr)->src)
+#define IPV6_FRAG_DEST(ipr) ((ipr)->dest)
+#else /* IPV6_FRAG_COPYHEADER */
+#define IPV6_FRAG_SRC(ipr) ((ipr)->iphdr->src)
+#define IPV6_FRAG_DEST(ipr) ((ipr)->iphdr->dest)
+#endif /* IPV6_FRAG_COPYHEADER */
+
+/** IPv6 reassembly helper struct.
+ * This is exported because memp needs to know the size.
+ */
+struct ip6_reassdata {
+  struct ip6_reassdata *next;
+  struct pbuf *p;
+  struct ip6_hdr *iphdr; /* pointer to the first (original) IPv6 header */
+#if IPV6_FRAG_COPYHEADER
+  ip6_addr_p_t src; /* copy of the source address in the IP header */
+  ip6_addr_p_t dest; /* copy of the destination address in the IP header */
+  /* This buffer (for the part of the original header that we overwrite) will
+   * be slightly oversized, but we cannot compute the exact size from here. */
+  u8_t orig_hdr[sizeof(struct ip6_frag_hdr) + sizeof(void*)];
+#else /* IPV6_FRAG_COPYHEADER */
+  /* In this case we still need the buffer, for sending ICMPv6 replies. */
+  u8_t orig_hdr[sizeof(struct ip6_frag_hdr)];
+#endif /* IPV6_FRAG_COPYHEADER */
+  u32_t identification;
+  u16_t datagram_len;
+  u8_t nexth;
+  u8_t timer;
+#if LWIP_IPV6_SCOPES
+  u8_t src_zone; /* zone of original packet's source address */
+  u8_t dest_zone; /* zone of original packet's destination address */
+#endif /* LWIP_IPV6_SCOPES */
+};
+
+#define ip6_reass_init() /* Compatibility define */
+void ip6_reass_tmr(void);
+struct pbuf *ip6_reass(struct pbuf *p);
+
+#endif /* LWIP_IPV6 && LWIP_IPV6_REASS */
+
+#if LWIP_IPV6 && LWIP_IPV6_FRAG  /* don't build if not configured for use in lwipopts.h */
+
+#ifndef LWIP_PBUF_CUSTOM_REF_DEFINED
+#define LWIP_PBUF_CUSTOM_REF_DEFINED
+/** A custom pbuf that holds a reference to another pbuf, which is freed
+ * when this custom pbuf is freed. This is used to create a custom PBUF_REF
+ * that points into the original pbuf. */
+struct pbuf_custom_ref {
+  /** 'base class' */
+  struct pbuf_custom pc;
+  /** pointer to the original pbuf that is referenced */
+  struct pbuf *original;
+};
+#endif /* LWIP_PBUF_CUSTOM_REF_DEFINED */
+
+err_t ip6_frag(struct pbuf *p, struct netif *netif, const ip6_addr_t *dest);
+
+#endif /* LWIP_IPV6 && LWIP_IPV6_FRAG */
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_IP6_FRAG_H */
diff --git a/lib/lwip/include/lwip/ip6_zone.h b/lib/lwip/include/lwip/ip6_zone.h
new file mode 100644
index 0000000000..834c32fc3e
--- /dev/null
+++ b/lib/lwip/include/lwip/ip6_zone.h
@@ -0,0 +1,306 @@
+/**
+ * @file
+ *
+ * IPv6 address scopes, zones, and scoping policy.
+ *
+ * This header provides the means to implement support for IPv6 address scopes,
+ * as per RFC 4007. An address scope can be either global or more constrained.
+ * In lwIP, we say that an address "has a scope" or "is scoped" when its scope
+ * is constrained, in which case the address is meaningful only in a specific
+ * "zone." For unicast addresses, only link-local addresses have a scope; in
+ * that case, the scope is the link. For multicast addresses, there are various
+ * scopes defined by RFC 4007 and others. For any constrained scope, a system
+ * must establish a (potentially one-to-many) mapping between zones and local
+ * interfaces. For example, a link-local address is valid on only one link (its
+ * zone). That link may be attached to one or more local interfaces. The
+ * decisions on which scopes are constrained and the mapping between zones and
+ * interfaces is together what we refer to as the "scoping policy" - more on
+ * this in a bit.
+ *
+ * In lwIP, each IPv6 address has an associated zone index. This zone index may
+ * be set to "no zone" (IP6_NO_ZONE, 0) or an actual zone. We say that an
+ * address "has a zone" or "is zoned" when its zone index is *not* set to "no
+ * zone." In lwIP, in principle, each address should be "properly zoned," which
+ * means that if the address has a zone if and only if has a scope. As such, it
+ * is a rule that an unscoped (e.g., global) address must never have a zone.
+ * Even though one could argue that there is always one zone even for global
+ * scopes, this rule exists for implementation simplicity. Violation of the
+ * rule will trigger assertions or otherwise result in undesired behavior.
+ *
+ * Backward compatibility prevents us from requiring that applications always
+ * provide properly zoned addresses. We do enforce the rule that the in the
+ * lwIP link layer (everything below netif->output_ip6() and in particular ND6)
+ * *all* addresses are properly zoned. Thus, on the output paths down the
+ * stack, various places deal with the case of addresses that lack a zone.
+ * Some of them are best-effort for efficiency (e.g. the PCB bind and connect
+ * API calls' attempts to add missing zones); ultimately the IPv6 output
+ * handler (@ref ip6_output_if_src) will set a zone if necessary.
+ *
+ * Aside from dealing with scoped addresses lacking a zone, a proper IPv6
+ * implementation must also ensure that a packet with a scoped source and/or
+ * destination address does not leave its zone. This is currently implemented
+ * in the input and forward functions. However, for output, these checks are
+ * deliberately omitted in order to keep the implementation lightweight. The
+ * routing algorithm in @ref ip6_route will take decisions such that it will
+ * not cause zone violations unless the application sets bad addresses, though.
+ *
+ * In terms of scoping policy, lwIP implements the default policy from RFC 4007
+ * using macros in this file. This policy considers link-local unicast
+ * addresses and (only) interface-local and link-local multicast addresses as
+ * having a scope. For all these addresses, the zone is equal to the interface.
+ * As shown below in this file, it is possible to implement a custom policy.
+ */
+
+/*
+ * Copyright (c) 2017 The MINIX 3 Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: David van Moolenbroek <david@minix3.org>
+ *
+ */
+#ifndef LWIP_HDR_IP6_ZONE_H
+#define LWIP_HDR_IP6_ZONE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @defgroup ip6_zones IPv6 Zones
+ * @ingroup ip6
+ * @{
+ */
+
+#if LWIP_IPV6  /* don't build if not configured for use in lwipopts.h */
+
+/** Identifier for "no zone". */
+#define IP6_NO_ZONE 0
+
+#if LWIP_IPV6_SCOPES
+
+/** Zone initializer for static IPv6 address initialization, including comma. */
+#define IPADDR6_ZONE_INIT , IP6_NO_ZONE
+
+/** Return the zone index of the given IPv6 address; possibly "no zone". */
+#define ip6_addr_zone(ip6addr) ((ip6addr)->zone)
+
+/** Does the given IPv6 address have a zone set? (0/1) */
+#define ip6_addr_has_zone(ip6addr) (ip6_addr_zone(ip6addr) != IP6_NO_ZONE)
+
+/** Set the zone field of an IPv6 address to a particular value. */
+#define ip6_addr_set_zone(ip6addr, zone_idx) ((ip6addr)->zone = (zone_idx))
+
+/** Clear the zone field of an IPv6 address, setting it to "no zone". */
+#define ip6_addr_clear_zone(ip6addr) ((ip6addr)->zone = IP6_NO_ZONE)
+
+/** Copy the zone field from the second IPv6 address to the first one. */
+#define ip6_addr_copy_zone(ip6addr1, ip6addr2) ((ip6addr1).zone = (ip6addr2).zone)
+
+/** Is the zone field of the given IPv6 address equal to the given zone index? (0/1) */
+#define ip6_addr_equals_zone(ip6addr, zone_idx) ((ip6addr)->zone == (zone_idx))
+
+/** @deprecated Renamed to @ref ip6_addr_zone_eq */
+#define ip6_addr_cmp_zone(addr1, addr2) ip6_addr_zone_eq(ip6addr1, ip6addr2)
+/** Are the zone fields of the given IPv6 addresses equal? (0/1)
+ * This macro must only be used on IPv6 addresses of the same scope. */
+#define ip6_addr_zone_eq(ip6addr1, ip6addr2) ((ip6addr1)->zone == (ip6addr2)->zone)
+
+/** Symbolic constants for the 'type' parameters in some of the macros.
+ * These exist for efficiency only, allowing the macros to avoid certain tests
+ * when the address is known not to be of a certain type. Dead code elimination
+ * will do the rest. IP6_MULTICAST is supported but currently not optimized.
+ * @see ip6_addr_has_scope, ip6_addr_assign_zone, ip6_addr_lacks_zone.
+ */
+enum lwip_ipv6_scope_type
+{
+  /** Unknown */
+  IP6_UNKNOWN   = 0,
+  /** Unicast */
+  IP6_UNICAST   = 1,
+  /** Multicast */
+  IP6_MULTICAST = 2
+};
+
+/** IPV6_CUSTOM_SCOPES: together, the following three macro definitions,
+ * @ref ip6_addr_has_scope, @ref ip6_addr_assign_zone, and
+ * @ref ip6_addr_test_zone, completely define the lwIP scoping policy.
+ * The definitions below implement the default policy from RFC 4007 Sec. 6.
+ * Should an implementation desire to implement a different policy, it can
+ * define IPV6_CUSTOM_SCOPES to 1 and supply its own definitions for the three
+ * macros instead.
+ */
+#ifndef IPV6_CUSTOM_SCOPES
+#define IPV6_CUSTOM_SCOPES 0
+#endif /* !IPV6_CUSTOM_SCOPES */
+
+#if !IPV6_CUSTOM_SCOPES
+
+/**
+ * Determine whether an IPv6 address has a constrained scope, and as such is
+ * meaningful only if accompanied by a zone index to identify the scope's zone.
+ * The given address type may be used to eliminate at compile time certain
+ * checks that will evaluate to false at run time anyway.
+ *
+ * This default implementation follows the default model of RFC 4007, where
+ * only interface-local and link-local scopes are defined.
+ *
+ * Even though the unicast loopback address does have an implied link-local
+ * scope, in this implementation it does not have an explicitly assigned zone
+ * index. As such it should not be tested for in this macro.
+ *
+ * @param ip6addr the IPv6 address (const); only its address part is examined.
+ * @param type address type; see @ref lwip_ipv6_scope_type.
+ * @return 1 if the address has a constrained scope, 0 if it does not.
+ */
+#define ip6_addr_has_scope(ip6addr, type) \
+  (ip6_addr_islinklocal(ip6addr) || (((type) != IP6_UNICAST) && \
+   (ip6_addr_ismulticast_iflocal(ip6addr) || \
+    ip6_addr_ismulticast_linklocal(ip6addr))))
+
+/**
+ * Assign a zone index to an IPv6 address, based on a network interface. If the
+ * given address has a scope, the assigned zone index is that scope's zone of
+ * the given netif; otherwise, the assigned zone index is "no zone".
+ *
+ * This default implementation follows the default model of RFC 4007, where
+ * only interface-local and link-local scopes are defined, and the zone index
+ * of both of those scopes always equals the index of the network interface.
+ * As such, this default implementation need not distinguish between different
+ * constrained scopes when assigning the zone.
+ *
+ * @param ip6addr the IPv6 address; its address part is examined, and its zone
+ *                index is assigned.
+ * @param type address type; see @ref lwip_ipv6_scope_type.
+ * @param netif the network interface (const).
+ */
+#define ip6_addr_assign_zone(ip6addr, type, netif) \
+    (ip6_addr_set_zone((ip6addr), \
+      ip6_addr_has_scope((ip6addr), (type)) ? netif_get_index(netif) : 0))
+
+/**
+ * Test whether an IPv6 address is "zone-compatible" with a network interface.
+ * That is, test whether the network interface is part of the zone associated
+ * with the address. For efficiency, this macro is only ever called if the
+ * given address is either scoped or zoned, and thus, it need not test this.
+ * If an address is scoped but not zoned, or zoned and not scoped, it is
+ * considered not zone-compatible with any netif.
+ *
+ * This default implementation follows the default model of RFC 4007, where
+ * only interface-local and link-local scopes are defined, and the zone index
+ * of both of those scopes always equals the index of the network interface.
+ * As such, there is always only one matching netif for a specific zone index,
+ * but all call sites of this macro currently support multiple matching netifs
+ * as well (at no additional expense in the common case).
+ *
+ * @param ip6addr the IPv6 address (const).
+ * @param netif the network interface (const).
+ * @return 1 if the address is scope-compatible with the netif, 0 if not.
+ */
+#define ip6_addr_test_zone(ip6addr, netif) \
+    (ip6_addr_equals_zone((ip6addr), netif_get_index(netif)))
+
+#endif /* !IPV6_CUSTOM_SCOPES */
+
+/** Does the given IPv6 address have a scope, and as such should also have a
+ * zone to be meaningful, but does not actually have a zone? (0/1) */
+#define ip6_addr_lacks_zone(ip6addr, type) \
+    (!ip6_addr_has_zone(ip6addr) && ip6_addr_has_scope((ip6addr), (type)))
+
+/**
+ * Try to select a zone for a scoped address that does not yet have a zone.
+ * Called from PCB bind and connect routines, for two reasons: 1) to save on
+ * this (relatively expensive) selection for every individual packet route
+ * operation and 2) to allow the application to obtain the selected zone from
+ * the PCB as is customary for e.g. getsockname/getpeername BSD socket calls.
+ *
+ * Ideally, callers would always supply a properly zoned address, in which case
+ * this function would not be needed. It exists both for compatibility with the
+ * BSD socket API (which accepts zoneless destination addresses) and for
+ * backward compatibility with pre-scoping lwIP code.
+ *
+ * It may be impossible to select a zone, e.g. if there are no netifs.  In that
+ * case, the address's zone field will be left as is.
+ *
+ * @param dest the IPv6 address for which to select and set a zone.
+ * @param src source IPv6 address (const); may be equal to dest.
+ */
+#define ip6_addr_select_zone(dest, src) do { struct netif *selected_netif; \
+  selected_netif = ip6_route((src), (dest)); \
+  if (selected_netif != NULL) { \
+    ip6_addr_assign_zone((dest), IP6_UNKNOWN, selected_netif); \
+  } } while (0)
+
+/**
+ * @}
+ */
+
+#else /* LWIP_IPV6_SCOPES */
+
+#define IPADDR6_ZONE_INIT
+#define ip6_addr_zone(ip6addr) (IP6_NO_ZONE)
+#define ip6_addr_has_zone(ip6addr) (0)
+#define ip6_addr_set_zone(ip6addr, zone_idx)
+#define ip6_addr_clear_zone(ip6addr)
+#define ip6_addr_copy_zone(ip6addr1, ip6addr2)
+#define ip6_addr_equals_zone(ip6addr, zone_idx) (1)
+#define ip6_addr_zone_eq(ip6addr1, ip6addr2) (1)
+#define IPV6_CUSTOM_SCOPES 0
+#define ip6_addr_has_scope(ip6addr, type) (0)
+#define ip6_addr_assign_zone(ip6addr, type, netif)
+#define ip6_addr_test_zone(ip6addr, netif) (1)
+#define ip6_addr_lacks_zone(ip6addr, type) (0)
+#define ip6_addr_select_zone(ip6addr, src)
+
+#endif /* LWIP_IPV6_SCOPES */
+
+#if LWIP_IPV6_SCOPES && LWIP_IPV6_SCOPES_DEBUG
+
+/** Verify that the given IPv6 address is properly zoned. */
+#define IP6_ADDR_ZONECHECK(ip6addr) LWIP_ASSERT("IPv6 zone check failed", \
+    ip6_addr_has_scope(ip6addr, IP6_UNKNOWN) == ip6_addr_has_zone(ip6addr))
+
+/** Verify that the given IPv6 address is properly zoned for the given netif. */
+#define IP6_ADDR_ZONECHECK_NETIF(ip6addr, netif) LWIP_ASSERT("IPv6 netif zone check failed", \
+    ip6_addr_has_scope(ip6addr, IP6_UNKNOWN) ? \
+    (ip6_addr_has_zone(ip6addr) && \
+     (((netif) == NULL) || ip6_addr_test_zone((ip6addr), (netif)))) : \
+    !ip6_addr_has_zone(ip6addr))
+
+#else /* LWIP_IPV6_SCOPES && LWIP_IPV6_SCOPES_DEBUG */
+
+#define IP6_ADDR_ZONECHECK(ip6addr)
+#define IP6_ADDR_ZONECHECK_NETIF(ip6addr, netif)
+
+#endif /* LWIP_IPV6_SCOPES && LWIP_IPV6_SCOPES_DEBUG */
+
+#endif /* LWIP_IPV6 */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_IP6_ZONE_H */
diff --git a/lib/lwip/include/lwip/ip_addr.h b/lib/lwip/include/lwip/ip_addr.h
new file mode 100644
index 0000000000..06454a4ac4
--- /dev/null
+++ b/lib/lwip/include/lwip/ip_addr.h
@@ -0,0 +1,468 @@
+/**
+ * @file
+ * IP address API (common IPv4 and IPv6)
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef LWIP_HDR_IP_ADDR_H
+#define LWIP_HDR_IP_ADDR_H
+
+#include "lwip/opt.h"
+#include "lwip/def.h"
+
+#include "lwip/ip4_addr.h"
+#include "lwip/ip6_addr.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** @ingroup ipaddr
+ * IP address types for use in ip_addr_t.type member.
+ * @see tcp_new_ip_type(), udp_new_ip_type(), raw_new_ip_type().
+ */
+enum lwip_ip_addr_type {
+  /** IPv4 */
+  IPADDR_TYPE_V4 =   0U,
+  /** IPv6 */
+  IPADDR_TYPE_V6 =   6U,
+  /** IPv4+IPv6 ("dual-stack") */
+  IPADDR_TYPE_ANY = 46U
+};
+
+#if LWIP_IPV4 && LWIP_IPV6
+/**
+ * @ingroup ipaddr
+ * A union struct for both IP version's addresses.
+ * ATTENTION: watch out for its size when adding IPv6 address scope!
+ */
+typedef struct ip_addr {
+  union {
+    ip6_addr_t ip6;
+    ip4_addr_t ip4;
+  } u_addr;
+  /** @ref lwip_ip_addr_type */
+  u8_t type;
+} ip_addr_t;
+
+extern const ip_addr_t ip_addr_any_type;
+
+/** @ingroup ip4addr */
+#define IPADDR4_INIT(u32val)          { { { { u32val, 0ul, 0ul, 0ul } IPADDR6_ZONE_INIT } }, IPADDR_TYPE_V4 }
+/** @ingroup ip4addr */
+#define IPADDR4_INIT_BYTES(a,b,c,d)   IPADDR4_INIT(PP_HTONL(LWIP_MAKEU32(a,b,c,d)))
+
+/** @ingroup ip6addr */
+#define IPADDR6_INIT(a, b, c, d)      { { { { a, b, c, d } IPADDR6_ZONE_INIT } }, IPADDR_TYPE_V6 }
+/** @ingroup ip6addr */
+#define IPADDR6_INIT_HOST(a, b, c, d) { { { { PP_HTONL(a), PP_HTONL(b), PP_HTONL(c), PP_HTONL(d) } IPADDR6_ZONE_INIT } }, IPADDR_TYPE_V6 }
+
+/** @ingroup ipaddr */
+#define IP_IS_ANY_TYPE_VAL(ipaddr)    (IP_GET_TYPE(&ipaddr) == IPADDR_TYPE_ANY)
+/** @ingroup ipaddr */
+#define IPADDR_ANY_TYPE_INIT          { { { { 0ul, 0ul, 0ul, 0ul } IPADDR6_ZONE_INIT } }, IPADDR_TYPE_ANY }
+
+/** @ingroup ip4addr */
+#define IP_IS_V4_VAL(ipaddr)          (IP_GET_TYPE(&ipaddr) == IPADDR_TYPE_V4)
+/** @ingroup ip6addr */
+#define IP_IS_V6_VAL(ipaddr)          (IP_GET_TYPE(&ipaddr) == IPADDR_TYPE_V6)
+/** @ingroup ip4addr */
+#define IP_IS_V4(ipaddr)              (((ipaddr) == NULL) || IP_IS_V4_VAL(*(ipaddr)))
+/** @ingroup ip6addr */
+#define IP_IS_V6(ipaddr)              (((ipaddr) != NULL) && IP_IS_V6_VAL(*(ipaddr)))
+
+#define IP_SET_TYPE_VAL(ipaddr, iptype) do { (ipaddr).type = (iptype); }while(0)
+#define IP_SET_TYPE(ipaddr, iptype)     do { if((ipaddr) != NULL) { IP_SET_TYPE_VAL(*(ipaddr), iptype); }}while(0)
+#define IP_GET_TYPE(ipaddr)           ((ipaddr)->type)
+
+#define IP_ADDR_RAW_SIZE(ipaddr)      (IP_GET_TYPE(&ipaddr) == IPADDR_TYPE_V4 ? sizeof(ip4_addr_t) : sizeof(ip6_addr_t))
+
+#define IP_ADDR_PCB_VERSION_MATCH_EXACT(pcb, ipaddr) (IP_GET_TYPE(&pcb->local_ip) == IP_GET_TYPE(ipaddr))
+#define IP_ADDR_PCB_VERSION_MATCH(pcb, ipaddr) (IP_IS_ANY_TYPE_VAL(pcb->local_ip) || IP_ADDR_PCB_VERSION_MATCH_EXACT(pcb, ipaddr))
+
+/** @ingroup ip6addr
+ * Convert generic ip address to specific protocol version
+ */
+#define ip_2_ip6(ipaddr)   (&((ipaddr)->u_addr.ip6))
+/** @ingroup ip4addr
+ * Convert generic ip address to specific protocol version
+ */
+#define ip_2_ip4(ipaddr)   (&((ipaddr)->u_addr.ip4))
+
+/** @ingroup ip4addr */
+#define IP_ADDR4(ipaddr,a,b,c,d)      do { IP4_ADDR(ip_2_ip4(ipaddr),a,b,c,d); \
+                                           IP_SET_TYPE_VAL(*(ipaddr), IPADDR_TYPE_V4); } while(0)
+/** @ingroup ip6addr */
+#define IP_ADDR6(ipaddr,i0,i1,i2,i3)  do { IP6_ADDR(ip_2_ip6(ipaddr),i0,i1,i2,i3); \
+                                           IP_SET_TYPE_VAL(*(ipaddr), IPADDR_TYPE_V6); } while(0)
+/** @ingroup ip6addr */
+#define IP_ADDR6_HOST(ipaddr,i0,i1,i2,i3)  IP_ADDR6(ipaddr,PP_HTONL(i0),PP_HTONL(i1),PP_HTONL(i2),PP_HTONL(i3))
+
+#define ip_clear_no4(ipaddr)  do { ip_2_ip6(ipaddr)->addr[1] = \
+                                   ip_2_ip6(ipaddr)->addr[2] = \
+                                   ip_2_ip6(ipaddr)->addr[3] = 0; \
+                                   ip6_addr_clear_zone(ip_2_ip6(ipaddr)); }while(0)
+
+/** @ingroup ipaddr */
+#define ip_addr_copy(dest, src)      do{ IP_SET_TYPE_VAL(dest, IP_GET_TYPE(&src)); if(IP_IS_V6_VAL(src)){ \
+  ip6_addr_copy(*ip_2_ip6(&(dest)), *ip_2_ip6(&(src))); }else{ \
+  ip4_addr_copy(*ip_2_ip4(&(dest)), *ip_2_ip4(&(src))); ip_clear_no4(&dest); }}while(0)
+/** @ingroup ip6addr */
+#define ip_addr_copy_from_ip6(dest, src)      do{ \
+  ip6_addr_copy(*ip_2_ip6(&(dest)), src); IP_SET_TYPE_VAL(dest, IPADDR_TYPE_V6); }while(0)
+/** @ingroup ip6addr */
+#define ip_addr_copy_from_ip6_packed(dest, src)      do{ \
+  ip6_addr_copy_from_packed(*ip_2_ip6(&(dest)), src); IP_SET_TYPE_VAL(dest, IPADDR_TYPE_V6); }while(0)
+/** @ingroup ip4addr */
+#define ip_addr_copy_from_ip4(dest, src)      do{ \
+  ip4_addr_copy(*ip_2_ip4(&(dest)), src); IP_SET_TYPE_VAL(dest, IPADDR_TYPE_V4); ip_clear_no4(&dest); }while(0)
+/** @ingroup ip4addr */
+#define ip_addr_set_ip4_u32(ipaddr, val)  do{if(ipaddr){ip4_addr_set_u32(ip_2_ip4(ipaddr), val); \
+  IP_SET_TYPE(ipaddr, IPADDR_TYPE_V4); ip_clear_no4(ipaddr); }}while(0)
+/** @ingroup ip4addr */
+#define ip_addr_set_ip4_u32_val(ipaddr, val)  do{ ip4_addr_set_u32(ip_2_ip4(&(ipaddr)), val); \
+  IP_SET_TYPE_VAL(ipaddr, IPADDR_TYPE_V4); ip_clear_no4(&ipaddr); }while(0)
+/** @ingroup ip4addr */
+#define ip_addr_get_ip4_u32(ipaddr)  (((ipaddr) && IP_IS_V4(ipaddr)) ? \
+  ip4_addr_get_u32(ip_2_ip4(ipaddr)) : 0)
+/** @ingroup ipaddr */
+#define ip_addr_set(dest, src) do{ IP_SET_TYPE(dest, IP_GET_TYPE(src)); if(IP_IS_V6(src)){ \
+  ip6_addr_set(ip_2_ip6(dest), ip_2_ip6(src)); }else{ \
+  ip4_addr_set(ip_2_ip4(dest), ip_2_ip4(src)); ip_clear_no4(dest); }}while(0)
+/** @ingroup ipaddr */
+#define ip_addr_set_ipaddr(dest, src) ip_addr_set(dest, src)
+/** @ingroup ipaddr */
+#define ip_addr_set_zero(ipaddr)     do{ \
+  ip6_addr_set_zero(ip_2_ip6(ipaddr)); IP_SET_TYPE(ipaddr, 0); }while(0)
+/** @ingroup ip5addr */
+#define ip_addr_set_zero_ip4(ipaddr)     do{ \
+  ip6_addr_set_zero(ip_2_ip6(ipaddr)); IP_SET_TYPE(ipaddr, IPADDR_TYPE_V4); }while(0)
+/** @ingroup ip6addr */
+#define ip_addr_set_zero_ip6(ipaddr)     do{ \
+  ip6_addr_set_zero(ip_2_ip6(ipaddr)); IP_SET_TYPE(ipaddr, IPADDR_TYPE_V6); }while(0)
+/** @ingroup ipaddr */
+#define ip_addr_set_any(is_ipv6, ipaddr)      do{if(is_ipv6){ \
+  ip6_addr_set_any(ip_2_ip6(ipaddr)); IP_SET_TYPE(ipaddr, IPADDR_TYPE_V6); }else{ \
+  ip4_addr_set_any(ip_2_ip4(ipaddr)); IP_SET_TYPE(ipaddr, IPADDR_TYPE_V4); ip_clear_no4(ipaddr); }}while(0)
+/** @ingroup ipaddr */
+#define ip_addr_set_any_val(is_ipv6, ipaddr)      do{if(is_ipv6){ \
+  ip6_addr_set_any(ip_2_ip6(&(ipaddr))); IP_SET_TYPE_VAL(ipaddr, IPADDR_TYPE_V6); }else{ \
+  ip4_addr_set_any(ip_2_ip4(&(ipaddr))); IP_SET_TYPE_VAL(ipaddr, IPADDR_TYPE_V4); ip_clear_no4(&ipaddr); }}while(0)
+/** @ingroup ipaddr */
+#define ip_addr_set_loopback(is_ipv6, ipaddr) do{if(is_ipv6){ \
+  ip6_addr_set_loopback(ip_2_ip6(ipaddr)); IP_SET_TYPE(ipaddr, IPADDR_TYPE_V6); }else{ \
+  ip4_addr_set_loopback(ip_2_ip4(ipaddr)); IP_SET_TYPE(ipaddr, IPADDR_TYPE_V4); ip_clear_no4(ipaddr); }}while(0)
+/** @ingroup ipaddr */
+#define ip_addr_set_loopback_val(is_ipv6, ipaddr) do{if(is_ipv6){ \
+  ip6_addr_set_loopback(ip_2_ip6(&(ipaddr))); IP_SET_TYPE_VAL(ipaddr, IPADDR_TYPE_V6); }else{ \
+  ip4_addr_set_loopback(ip_2_ip4(&(ipaddr))); IP_SET_TYPE_VAL(ipaddr, IPADDR_TYPE_V4); ip_clear_no4(&ipaddr); }}while(0)
+/** @ingroup ipaddr */
+#define ip_addr_set_hton(dest, src)  do{if(IP_IS_V6(src)){ \
+  ip6_addr_set_hton(ip_2_ip6(dest), ip_2_ip6(src)); IP_SET_TYPE(dest, IPADDR_TYPE_V6); }else{ \
+  ip4_addr_set_hton(ip_2_ip4(dest), ip_2_ip4(src)); IP_SET_TYPE(dest, IPADDR_TYPE_V4); ip_clear_no4(ipaddr); }}while(0)
+/** @ingroup ipaddr */
+#define ip_addr_get_network(target, host, netmask) do{if(IP_IS_V6(host)){ \
+  ip4_addr_set_zero(ip_2_ip4(target)); IP_SET_TYPE(target, IPADDR_TYPE_V6); } else { \
+  ip4_addr_get_network(ip_2_ip4(target), ip_2_ip4(host), ip_2_ip4(netmask)); IP_SET_TYPE(target, IPADDR_TYPE_V4); }}while(0)
+/**
+ * @ingroup ipaddr
+ * @deprecated Renamed to @ref ip_addr_net_eq
+ */
+#define ip_addr_netcmp(addr1, addr2, mask) ip_addr_net_eq((addr1), (addr2), (mask))
+/** @ingroup ipaddr
+ *  Check if two ip addresses are share the same network, for a specific netmask. */
+#define ip_addr_net_eq(addr1, addr2, mask) ((IP_IS_V6(addr1) && IP_IS_V6(addr2)) ? \
+  0 : \
+  ip4_addr_net_eq(ip_2_ip4(addr1), ip_2_ip4(addr2), mask))
+/**
+ * @ingroup ipaddr
+ * @deprecated Renamed to @ref ip_addr_eq
+ */
+#define ip_addr_cmp(addr1, addr2) ip_addr_eq((addr1), (addr2))
+/** @ingroup ipaddr
+ *  Check if two ip addresses are equal. */
+#define ip_addr_eq(addr1, addr2)    ((IP_GET_TYPE(addr1) != IP_GET_TYPE(addr2)) ? 0 : (IP_IS_V6_VAL(*(addr1)) ? \
+  ip6_addr_eq(ip_2_ip6(addr1), ip_2_ip6(addr2)) : \
+  ip4_addr_eq(ip_2_ip4(addr1), ip_2_ip4(addr2))))
+/**
+ * @ingroup ipaddr
+ * @deprecated Renamed to @ref ip_addr_zoneless_eq
+ */
+#define ip_addr_cmp_zoneless(addr1, addr2) ip_addr_zoneless_eq((addr1), (addr2))
+/** @ingroup ipaddr
+ *  Check if two ip addresses are equal, ignoring the zone. */
+#define ip_addr_zoneless_eq(addr1, addr2)    ((IP_GET_TYPE(addr1) != IP_GET_TYPE(addr2)) ? 0 : (IP_IS_V6_VAL(*(addr1)) ? \
+  ip6_addr_zoneless_eq(ip_2_ip6(addr1), ip_2_ip6(addr2)) : \
+  ip4_addr_eq(ip_2_ip4(addr1), ip_2_ip4(addr2))))
+/** @ingroup ipaddr
+ *  Check if an ip address is the 'any' address. */
+#define ip_addr_isany(ipaddr)        (((ipaddr) == NULL) ? 1 : ((IP_IS_V6(ipaddr)) ? \
+  ip6_addr_isany(ip_2_ip6(ipaddr)) : \
+  ip4_addr_isany(ip_2_ip4(ipaddr))))
+/** @ingroup ipaddr
+ *  Check if an ip address is the 'any' address, by value. */
+#define ip_addr_isany_val(ipaddr)        ((IP_IS_V6_VAL(ipaddr)) ? \
+  ip6_addr_isany_val(*ip_2_ip6(&(ipaddr))) : \
+  ip4_addr_isany_val(*ip_2_ip4(&(ipaddr))))
+/** @ingroup ipaddr
+ *  Check if an ip address is a broadcast address. */
+#define ip_addr_isbroadcast(ipaddr, netif) ((IP_IS_V6(ipaddr)) ? \
+  0 : \
+  ip4_addr_isbroadcast(ip_2_ip4(ipaddr), netif))
+/** @ingroup ipaddr
+ *  Check inf an ip address is a multicast address. */
+#define ip_addr_ismulticast(ipaddr)  ((IP_IS_V6(ipaddr)) ? \
+  ip6_addr_ismulticast(ip_2_ip6(ipaddr)) : \
+  ip4_addr_ismulticast(ip_2_ip4(ipaddr)))
+/** @ingroup ipaddr
+ *  Check inf an ip address is a loopback address. */
+#define ip_addr_isloopback(ipaddr)  ((IP_IS_V6(ipaddr)) ? \
+  ip6_addr_isloopback(ip_2_ip6(ipaddr)) : \
+  ip4_addr_isloopback(ip_2_ip4(ipaddr)))
+/** @ingroup ipaddr
+ *  Check inf an ip address is a link-local address. */
+#define ip_addr_islinklocal(ipaddr)  ((IP_IS_V6(ipaddr)) ? \
+  ip6_addr_islinklocal(ip_2_ip6(ipaddr)) : \
+  ip4_addr_islinklocal(ip_2_ip4(ipaddr)))
+#define ip_addr_debug_print(debug, ipaddr) do { if(IP_IS_V6(ipaddr)) { \
+  ip6_addr_debug_print(debug, ip_2_ip6(ipaddr)); } else { \
+  ip4_addr_debug_print(debug, ip_2_ip4(ipaddr)); }}while(0)
+#define ip_addr_debug_print_val(debug, ipaddr) do { if(IP_IS_V6_VAL(ipaddr)) { \
+  ip6_addr_debug_print_val(debug, *ip_2_ip6(&(ipaddr))); } else { \
+  ip4_addr_debug_print_val(debug, *ip_2_ip4(&(ipaddr))); }}while(0)
+char *ipaddr_ntoa(const ip_addr_t *addr);
+char *ipaddr_ntoa_r(const ip_addr_t *addr, char *buf, int buflen);
+int ipaddr_aton(const char *cp, ip_addr_t *addr);
+
+/** @ingroup ipaddr */
+#define IPADDR_STRLEN_MAX   IP6ADDR_STRLEN_MAX
+
+/** @ingroup ipaddr */
+#define ip4_2_ipv4_mapped_ipv6(ip6addr, ip4addr) do { \
+  (ip6addr)->addr[3] = (ip4addr)->addr; \
+  (ip6addr)->addr[2] = PP_HTONL(0x0000FFFFUL); \
+  (ip6addr)->addr[1] = 0; \
+  (ip6addr)->addr[0] = 0; \
+  ip6_addr_clear_zone(ip6addr); } while(0);
+
+/** @ingroup ipaddr */
+#define unmap_ipv4_mapped_ipv6(ip4addr, ip6addr) \
+  (ip4addr)->addr = (ip6addr)->addr[3];
+
+#define IP46_ADDR_ANY(type) (((type) == IPADDR_TYPE_V6)? IP6_ADDR_ANY : IP4_ADDR_ANY)
+
+#else /* LWIP_IPV4 && LWIP_IPV6 */
+
+#define IP_ADDR_PCB_VERSION_MATCH(addr, pcb)          1
+#define IP_ADDR_PCB_VERSION_MATCH_EXACT(pcb, ipaddr)  1
+
+#define ip_addr_set_any_val(is_ipv6, ipaddr)          ip_addr_set_any(is_ipv6, &(ipaddr))
+#define ip_addr_set_loopback_val(is_ipv6, ipaddr)     ip_addr_set_loopback(is_ipv6, &(ipaddr))
+
+#if LWIP_IPV4
+
+typedef ip4_addr_t ip_addr_t;
+#define IPADDR4_INIT(u32val)                    { u32val }
+#define IPADDR4_INIT_BYTES(a,b,c,d)             IPADDR4_INIT(PP_HTONL(LWIP_MAKEU32(a,b,c,d)))
+#define IP_IS_V4_VAL(ipaddr)                    1
+#define IP_IS_V6_VAL(ipaddr)                    0
+#define IP_IS_V4(ipaddr)                        1
+#define IP_IS_V6(ipaddr)                        0
+#define IP_IS_ANY_TYPE_VAL(ipaddr)              0
+#define IP_SET_TYPE_VAL(ipaddr, iptype)
+#define IP_SET_TYPE(ipaddr, iptype)
+#define IP_GET_TYPE(ipaddr)                     IPADDR_TYPE_V4
+#define IP_ADDR_RAW_SIZE(ipaddr)                sizeof(ip4_addr_t)
+#define ip_2_ip4(ipaddr)                        (ipaddr)
+#define IP_ADDR4(ipaddr,a,b,c,d)                IP4_ADDR(ipaddr,a,b,c,d)
+
+#define ip_addr_copy(dest, src)                 ip4_addr_copy(dest, src)
+#define ip_addr_copy_from_ip4(dest, src)        ip4_addr_copy(dest, src)
+#define ip_addr_set_ip4_u32(ipaddr, val)        ip4_addr_set_u32(ip_2_ip4(ipaddr), val)
+#define ip_addr_set_ip4_u32_val(ipaddr, val)    ip_addr_set_ip4_u32(&(ipaddr), val)
+#define ip_addr_get_ip4_u32(ipaddr)             ip4_addr_get_u32(ip_2_ip4(ipaddr))
+#define ip_addr_set(dest, src)                  ip4_addr_set(dest, src)
+#define ip_addr_set_ipaddr(dest, src)           ip4_addr_set(dest, src)
+#define ip_addr_set_zero(ipaddr)                ip4_addr_set_zero(ipaddr)
+#define ip_addr_set_zero_ip4(ipaddr)            ip4_addr_set_zero(ipaddr)
+#define ip_addr_set_any(is_ipv6, ipaddr)        ip4_addr_set_any(ipaddr)
+#define ip_addr_set_loopback(is_ipv6, ipaddr)   ip4_addr_set_loopback(ipaddr)
+#define ip_addr_set_hton(dest, src)             ip4_addr_set_hton(dest, src)
+#define ip_addr_get_network(target, host, mask) ip4_addr_get_network(target, host, mask)
+#define ip_addr_netcmp(addr1, addr2, mask)      ip4_addr_net_eq(addr1, addr2, mask)
+#define ip_addr_net_eq(addr1, addr2, mask)      ip4_addr_net_eq(addr1, addr2, mask)
+#define ip_addr_cmp(addr1, addr2)               ip4_addr_eq(addr1, addr2)
+#define ip_addr_eq(addr1, addr2)                ip4_addr_eq(addr1, addr2)
+#define ip_addr_isany(ipaddr)                   ip4_addr_isany(ipaddr)
+#define ip_addr_isany_val(ipaddr)               ip4_addr_isany_val(ipaddr)
+#define ip_addr_isloopback(ipaddr)              ip4_addr_isloopback(ipaddr)
+#define ip_addr_islinklocal(ipaddr)             ip4_addr_islinklocal(ipaddr)
+#define ip_addr_isbroadcast(addr, netif)        ip4_addr_isbroadcast(addr, netif)
+#define ip_addr_ismulticast(ipaddr)             ip4_addr_ismulticast(ipaddr)
+#define ip_addr_debug_print(debug, ipaddr)      ip4_addr_debug_print(debug, ipaddr)
+#define ip_addr_debug_print_val(debug, ipaddr)  ip4_addr_debug_print_val(debug, ipaddr)
+#define ipaddr_ntoa(ipaddr)                     ip4addr_ntoa(ipaddr)
+#define ipaddr_ntoa_r(ipaddr, buf, buflen)      ip4addr_ntoa_r(ipaddr, buf, buflen)
+#define ipaddr_aton(cp, addr)                   ip4addr_aton(cp, addr)
+
+#define IPADDR_STRLEN_MAX   IP4ADDR_STRLEN_MAX
+
+#define IP46_ADDR_ANY(type) (IP4_ADDR_ANY)
+
+#else /* LWIP_IPV4 */
+
+typedef ip6_addr_t ip_addr_t;
+#define IPADDR6_INIT(a, b, c, d)                { { a, b, c, d } IPADDR6_ZONE_INIT }
+#define IPADDR6_INIT_HOST(a, b, c, d)           { { PP_HTONL(a), PP_HTONL(b), PP_HTONL(c), PP_HTONL(d) } IPADDR6_ZONE_INIT }
+#define IP_IS_V4_VAL(ipaddr)                    0
+#define IP_IS_V6_VAL(ipaddr)                    1
+#define IP_IS_V4(ipaddr)                        0
+#define IP_IS_V6(ipaddr)                        1
+#define IP_IS_ANY_TYPE_VAL(ipaddr)              0
+#define IP_SET_TYPE_VAL(ipaddr, iptype)
+#define IP_SET_TYPE(ipaddr, iptype)
+#define IP_GET_TYPE(ipaddr)                     IPADDR_TYPE_V6
+#define IP_ADDR_RAW_SIZE(ipaddr)                sizeof(ip6_addr_t)
+#define ip_2_ip6(ipaddr)                        (ipaddr)
+#define IP_ADDR6(ipaddr,i0,i1,i2,i3)            IP6_ADDR(ipaddr,i0,i1,i2,i3)
+#define IP_ADDR6_HOST(ipaddr,i0,i1,i2,i3)       IP_ADDR6(ipaddr,PP_HTONL(i0),PP_HTONL(i1),PP_HTONL(i2),PP_HTONL(i3))
+
+#define ip_addr_copy(dest, src)                 ip6_addr_copy(dest, src)
+#define ip_addr_copy_from_ip6(dest, src)        ip6_addr_copy(dest, src)
+#define ip_addr_copy_from_ip6_packed(dest, src) ip6_addr_copy_from_packed(dest, src)
+#define ip_addr_set(dest, src)                  ip6_addr_set(dest, src)
+#define ip_addr_set_ipaddr(dest, src)           ip6_addr_set(dest, src)
+#define ip_addr_set_zero(ipaddr)                ip6_addr_set_zero(ipaddr)
+#define ip_addr_set_zero_ip6(ipaddr)            ip6_addr_set_zero(ipaddr)
+#define ip_addr_set_any(is_ipv6, ipaddr)        ip6_addr_set_any(ipaddr)
+#define ip_addr_set_loopback(is_ipv6, ipaddr)   ip6_addr_set_loopback(ipaddr)
+#define ip_addr_set_hton(dest, src)             ip6_addr_set_hton(dest, src)
+#define ip_addr_get_network(target, host, mask) ip6_addr_set_zero(target)
+#define ip_addr_netcmp(addr1, addr2, mask)      0
+#define ip_addr_net_eq(addr1, addr2, mask)      0
+#define ip_addr_cmp(addr1, addr2)               ip6_addr_eq(addr1, addr2)
+#define ip_addr_eq(addr1, addr2)                ip6_addr_eq(addr1, addr2)
+#define ip_addr_cmp_zoneless(addr1, addr2)      ip6_addr_zoneless_eq(addr1, addr2)
+#define ip_addr_zoneless_eq(addr1, addr2)       ip6_addr_zoneless_eq(addr1, addr2)
+#define ip_addr_isany(ipaddr)                   ip6_addr_isany(ipaddr)
+#define ip_addr_isany_val(ipaddr)               ip6_addr_isany_val(ipaddr)
+#define ip_addr_isloopback(ipaddr)              ip6_addr_isloopback(ipaddr)
+#define ip_addr_islinklocal(ipaddr)             ip6_addr_islinklocal(ipaddr)
+#define ip_addr_isbroadcast(addr, netif)        0
+#define ip_addr_ismulticast(ipaddr)             ip6_addr_ismulticast(ipaddr)
+#define ip_addr_debug_print(debug, ipaddr)      ip6_addr_debug_print(debug, ipaddr)
+#define ip_addr_debug_print_val(debug, ipaddr)  ip6_addr_debug_print_val(debug, ipaddr)
+#define ipaddr_ntoa(ipaddr)                     ip6addr_ntoa(ipaddr)
+#define ipaddr_ntoa_r(ipaddr, buf, buflen)      ip6addr_ntoa_r(ipaddr, buf, buflen)
+#define ipaddr_aton(cp, addr)                   ip6addr_aton(cp, addr)
+
+#define IPADDR_STRLEN_MAX   IP6ADDR_STRLEN_MAX
+
+#define IP46_ADDR_ANY(type) (IP6_ADDR_ANY)
+
+#endif /* LWIP_IPV4 */
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
+#if LWIP_IPV4
+
+extern const ip_addr_t ip_addr_any;
+extern const ip_addr_t ip_addr_broadcast;
+
+/**
+ * @ingroup ip4addr
+ * Can be used as a fixed/const ip_addr_t
+ * for the IP wildcard.
+ * Defined to @ref IP4_ADDR_ANY when IPv4 is enabled.
+ * Defined to @ref IP6_ADDR_ANY in IPv6 only systems.
+ * Use this if you can handle IPv4 _AND_ IPv6 addresses.
+ * Use @ref IP4_ADDR_ANY or @ref IP6_ADDR_ANY when the IP
+ * type matters.
+ */
+#define IP_ADDR_ANY         IP4_ADDR_ANY
+/**
+ * @ingroup ip4addr
+ * Can be used as a fixed/const ip_addr_t
+ * for the IPv4 wildcard and the broadcast address
+ */
+#define IP4_ADDR_ANY        (&ip_addr_any)
+/**
+ * @ingroup ip4addr
+ * Can be used as a fixed/const ip4_addr_t
+ * for the wildcard and the broadcast address
+ */
+#define IP4_ADDR_ANY4       (ip_2_ip4(&ip_addr_any))
+
+/** @ingroup ip4addr */
+#define IP_ADDR_BROADCAST   (&ip_addr_broadcast)
+/** @ingroup ip4addr */
+#define IP4_ADDR_BROADCAST  (ip_2_ip4(&ip_addr_broadcast))
+
+#endif /* LWIP_IPV4*/
+
+#if LWIP_IPV6
+
+extern const ip_addr_t ip6_addr_any;
+
+/**
+ * @ingroup ip6addr
+ * IP6_ADDR_ANY can be used as a fixed ip_addr_t
+ * for the IPv6 wildcard address
+ */
+#define IP6_ADDR_ANY   (&ip6_addr_any)
+/**
+ * @ingroup ip6addr
+ * IP6_ADDR_ANY6 can be used as a fixed ip6_addr_t
+ * for the IPv6 wildcard address
+ */
+#define IP6_ADDR_ANY6  (ip_2_ip6(&ip6_addr_any))
+
+#if !LWIP_IPV4
+/** IPv6-only configurations */
+#define IP_ADDR_ANY IP6_ADDR_ANY
+#endif /* !LWIP_IPV4 */
+
+#endif
+
+/** @ingroup ipaddr
+ *  Macro representing the 'any' address. */
+#if LWIP_IPV4 && LWIP_IPV6
+#define IP_ANY_TYPE    (&ip_addr_any_type)
+#else
+#define IP_ANY_TYPE    IP_ADDR_ANY
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_IP_ADDR_H */
diff --git a/lib/lwip/include/lwip/mem.h b/lib/lwip/include/lwip/mem.h
new file mode 100644
index 0000000000..ff208d25c3
--- /dev/null
+++ b/lib/lwip/include/lwip/mem.h
@@ -0,0 +1,82 @@
+/**
+ * @file
+ * Heap API
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef LWIP_HDR_MEM_H
+#define LWIP_HDR_MEM_H
+
+#include "lwip/opt.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if MEM_LIBC_MALLOC
+
+#include "lwip/arch.h"
+
+typedef size_t mem_size_t;
+#define MEM_SIZE_F SZT_F
+
+#elif MEM_USE_POOLS
+
+typedef u16_t mem_size_t;
+#define MEM_SIZE_F U16_F
+
+#else
+
+/* MEM_SIZE would have to be aligned, but using 64000 here instead of
+ * 65535 leaves some room for alignment...
+ */
+#if MEM_SIZE > 64000L
+typedef u32_t mem_size_t;
+#define MEM_SIZE_F U32_F
+#else
+typedef u16_t mem_size_t;
+#define MEM_SIZE_F U16_F
+#endif /* MEM_SIZE > 64000 */
+#endif
+
+void  mem_init(void);
+void *mem_trim(void *mem, mem_size_t size);
+void *mem_malloc(mem_size_t size);
+void *mem_calloc(mem_size_t count, mem_size_t size);
+void  mem_free(void *mem);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_MEM_H */
diff --git a/lib/lwip/include/lwip/memp.h b/lib/lwip/include/lwip/memp.h
new file mode 100644
index 0000000000..1630b26c2c
--- /dev/null
+++ b/lib/lwip/include/lwip/memp.h
@@ -0,0 +1,155 @@
+/**
+ * @file
+ * Memory pool API
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#ifndef LWIP_HDR_MEMP_H
+#define LWIP_HDR_MEMP_H
+
+#include "lwip/opt.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* run once with empty definition to handle all custom includes in lwippools.h */
+#define LWIP_MEMPOOL(name,num,size,desc)
+#include "lwip/priv/memp_std.h"
+
+/** Create the list of all memory pools managed by memp. MEMP_MAX represents a NULL pool at the end */
+typedef enum {
+#define LWIP_MEMPOOL(name,num,size,desc)  MEMP_##name,
+#include "lwip/priv/memp_std.h"
+  MEMP_MAX
+} memp_t;
+
+#include "lwip/priv/memp_priv.h"
+#include "lwip/stats.h"
+
+extern const struct memp_desc* const memp_pools[MEMP_MAX];
+
+/**
+ * @ingroup mempool
+ * Declare prototype for private memory pool if it is used in multiple files
+ */
+#define LWIP_MEMPOOL_PROTOTYPE(name) extern const struct memp_desc memp_ ## name
+
+#if MEMP_MEM_MALLOC
+
+#define LWIP_MEMPOOL_DECLARE(name,num,size,desc) \
+  LWIP_MEMPOOL_DECLARE_STATS_INSTANCE(memp_stats_ ## name) \
+  const struct memp_desc memp_ ## name = { \
+    DECLARE_LWIP_MEMPOOL_DESC(desc) \
+    LWIP_MEMPOOL_DECLARE_STATS_REFERENCE(memp_stats_ ## name) \
+    LWIP_MEM_ALIGN_SIZE(size) \
+  };
+
+#else /* MEMP_MEM_MALLOC */
+
+/**
+ * @ingroup mempool
+ * Declare a private memory pool
+ * Private mempools example:
+ * .h: only when pool is used in multiple .c files: LWIP_MEMPOOL_PROTOTYPE(my_private_pool);
+ * .c:
+ *   - in global variables section: LWIP_MEMPOOL_DECLARE(my_private_pool, 10, sizeof(foo), "Some description")
+ *   - call ONCE before using pool (e.g. in some init() function): LWIP_MEMPOOL_INIT(my_private_pool);
+ *   - allocate: void* my_new_mem = LWIP_MEMPOOL_ALLOC(my_private_pool);
+ *   - free: LWIP_MEMPOOL_FREE(my_private_pool, my_new_mem);
+ *
+ * To relocate a pool, declare it as extern in cc.h. Example for GCC:
+ *   extern u8_t \_\_attribute\_\_((section(".onchip_mem"))) memp_memory_my_private_pool_base[];
+ */
+#define LWIP_MEMPOOL_DECLARE(name,num,size,desc) \
+  LWIP_DECLARE_MEMORY_ALIGNED(memp_memory_ ## name ## _base, ((num) * (MEMP_SIZE + MEMP_ALIGN_SIZE(size)))); \
+    \
+  LWIP_MEMPOOL_DECLARE_STATS_INSTANCE(memp_stats_ ## name) \
+    \
+  static struct memp *memp_tab_ ## name; \
+    \
+  const struct memp_desc memp_ ## name = { \
+    DECLARE_LWIP_MEMPOOL_DESC(desc) \
+    LWIP_MEMPOOL_DECLARE_STATS_REFERENCE(memp_stats_ ## name) \
+    LWIP_MEM_ALIGN_SIZE(size), \
+    (num), \
+    memp_memory_ ## name ## _base, \
+    &memp_tab_ ## name \
+  };
+
+#endif /* MEMP_MEM_MALLOC */
+
+/**
+ * @ingroup mempool
+ * Initialize a private memory pool
+ */
+#define LWIP_MEMPOOL_INIT(name)    memp_init_pool(&memp_ ## name)
+/**
+ * @ingroup mempool
+ * Allocate from a private memory pool
+ */
+#define LWIP_MEMPOOL_ALLOC(name)   memp_malloc_pool(&memp_ ## name)
+/**
+ * @ingroup mempool
+ * Free element from a private memory pool
+ */
+#define LWIP_MEMPOOL_FREE(name, x) memp_free_pool(&memp_ ## name, (x))
+
+#if MEM_USE_POOLS
+/** This structure is used to save the pool one element came from.
+ * This has to be defined here as it is required for pool size calculation. */
+struct memp_malloc_helper
+{
+   memp_t poolnr;
+#if MEMP_OVERFLOW_CHECK || (LWIP_STATS && MEM_STATS)
+   u16_t size;
+#endif /* MEMP_OVERFLOW_CHECK || (LWIP_STATS && MEM_STATS) */
+};
+#endif /* MEM_USE_POOLS */
+
+void  memp_init(void);
+
+#if MEMP_OVERFLOW_CHECK
+void *memp_malloc_fn(memp_t type, const char* file, const int line);
+#define memp_malloc(t) memp_malloc_fn((t), __FILE__, __LINE__)
+#else
+void *memp_malloc(memp_t type);
+#endif
+void  memp_free(memp_t type, void *mem);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_MEMP_H */
diff --git a/lib/lwip/include/lwip/mld6.h b/lib/lwip/include/lwip/mld6.h
new file mode 100644
index 0000000000..2764fdd42d
--- /dev/null
+++ b/lib/lwip/include/lwip/mld6.h
@@ -0,0 +1,99 @@
+/**
+ * @file
+ *
+ * Multicast listener discovery for IPv6. Aims to be compliant with RFC 2710.
+ * No support for MLDv2.
+ */
+
+/*
+ * Copyright (c) 2010 Inico Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Ivan Delamer <delamer@inicotech.com>
+ *
+ *
+ * Please coordinate changes and requests with Ivan Delamer
+ * <delamer@inicotech.com>
+ */
+
+#ifndef LWIP_HDR_MLD6_H
+#define LWIP_HDR_MLD6_H
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV6_MLD && LWIP_IPV6  /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/pbuf.h"
+#include "lwip/netif.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** MLD group */
+struct mld_group {
+  /** next link */
+  struct mld_group *next;
+  /** multicast address */
+  ip6_addr_t         group_address;
+  /** signifies we were the last person to report */
+  u8_t               last_reporter_flag;
+  /** current state of the group */
+  u8_t               group_state;
+  /** timer for reporting */
+  u16_t              timer;
+  /** counter of simultaneous uses */
+  u8_t               use;
+};
+
+#define MLD6_TMR_INTERVAL              100 /* Milliseconds */
+
+err_t  mld6_stop(struct netif *netif);
+void   mld6_report_groups(struct netif *netif);
+void   mld6_tmr(void);
+struct mld_group *mld6_lookfor_group(struct netif *ifp, const ip6_addr_t *addr);
+void   mld6_input(struct pbuf *p, struct netif *inp);
+err_t  mld6_joingroup(const ip6_addr_t *srcaddr, const ip6_addr_t *groupaddr);
+err_t  mld6_joingroup_netif(struct netif *netif, const ip6_addr_t *groupaddr);
+err_t  mld6_leavegroup(const ip6_addr_t *srcaddr, const ip6_addr_t *groupaddr);
+err_t  mld6_leavegroup_netif(struct netif *netif, const ip6_addr_t *groupaddr);
+
+/** @ingroup mld6
+ * Get list head of MLD6 groups for netif.
+ * Note: The allnodes group IP is NOT in the list, since it must always
+ * be received for correct IPv6 operation.
+ * @see @ref netif_set_mld_mac_filter()
+ */
+#define netif_mld6_data(netif) ((struct mld_group *)netif_get_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_MLD6))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_IPV6_MLD && LWIP_IPV6 */
+
+#endif /* LWIP_HDR_MLD6_H */
diff --git a/lib/lwip/include/lwip/nd6.h b/lib/lwip/include/lwip/nd6.h
new file mode 100644
index 0000000000..c30e624f02
--- /dev/null
+++ b/lib/lwip/include/lwip/nd6.h
@@ -0,0 +1,90 @@
+/**
+ * @file
+ *
+ * Neighbor discovery and stateless address autoconfiguration for IPv6.
+ * Aims to be compliant with RFC 4861 (Neighbor discovery) and RFC 4862
+ * (Address autoconfiguration).
+ */
+
+/*
+ * Copyright (c) 2010 Inico Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Ivan Delamer <delamer@inicotech.com>
+ *
+ *
+ * Please coordinate changes and requests with Ivan Delamer
+ * <delamer@inicotech.com>
+ */
+
+#ifndef LWIP_HDR_ND6_H
+#define LWIP_HDR_ND6_H
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV6  /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/ip6_addr.h"
+#include "lwip/err.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** 1 second period */
+#define ND6_TMR_INTERVAL 1000
+
+/** Router solicitations are sent in 4 second intervals (see RFC 4861, ch. 6.3.7) */
+#ifndef ND6_RTR_SOLICITATION_INTERVAL
+#define ND6_RTR_SOLICITATION_INTERVAL  4000
+#endif
+
+struct pbuf;
+struct netif;
+
+void nd6_tmr(void);
+void nd6_input(struct pbuf *p, struct netif *inp);
+void nd6_clear_destination_cache(void);
+struct netif *nd6_find_route(const ip6_addr_t *ip6addr);
+err_t nd6_get_next_hop_addr_or_queue(struct netif *netif, struct pbuf *q, const ip6_addr_t *ip6addr, const u8_t **hwaddrp);
+u16_t nd6_get_destination_mtu(const ip6_addr_t *ip6addr, struct netif *netif);
+#if LWIP_ND6_TCP_REACHABILITY_HINTS
+void nd6_reachability_hint(const ip6_addr_t *ip6addr);
+#endif /* LWIP_ND6_TCP_REACHABILITY_HINTS */
+void nd6_cleanup_netif(struct netif *netif);
+#if LWIP_IPV6_MLD
+void nd6_adjust_mld_membership(struct netif *netif, s8_t addr_idx, u8_t new_state);
+#endif /* LWIP_IPV6_MLD */
+void nd6_restart_netif(struct netif *netif);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_IPV6 */
+
+#endif /* LWIP_HDR_ND6_H */
diff --git a/lib/lwip/include/lwip/netbuf.h b/lib/lwip/include/lwip/netbuf.h
new file mode 100644
index 0000000000..42a911bca3
--- /dev/null
+++ b/lib/lwip/include/lwip/netbuf.h
@@ -0,0 +1,116 @@
+/**
+ * @file
+ * netbuf API (for netconn API)
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef LWIP_HDR_NETBUF_H
+#define LWIP_HDR_NETBUF_H
+
+#include "lwip/opt.h"
+
+#if LWIP_NETCONN || LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */
+/* Note: Netconn API is always available when sockets are enabled -
+ * sockets are implemented on top of them */
+
+#include "lwip/pbuf.h"
+#include "lwip/ip_addr.h"
+#include "lwip/ip6_addr.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** This netbuf has dest-addr/port set */
+#define NETBUF_FLAG_DESTADDR    0x01
+/** This netbuf includes a checksum */
+#define NETBUF_FLAG_CHKSUM      0x02
+
+/** "Network buffer" - contains data and addressing info */
+struct netbuf {
+  struct pbuf *p, *ptr;
+  ip_addr_t addr;
+  u16_t port;
+#if LWIP_NETBUF_RECVINFO || LWIP_CHECKSUM_ON_COPY
+  u8_t flags;
+  u16_t toport_chksum;
+#if LWIP_NETBUF_RECVINFO
+  ip_addr_t toaddr;
+#endif /* LWIP_NETBUF_RECVINFO */
+#endif /* LWIP_NETBUF_RECVINFO || LWIP_CHECKSUM_ON_COPY */
+};
+
+/* Network buffer functions: */
+struct netbuf *   netbuf_new      (void);
+void              netbuf_delete   (struct netbuf *buf);
+void *            netbuf_alloc    (struct netbuf *buf, u16_t size);
+void              netbuf_free     (struct netbuf *buf);
+err_t             netbuf_ref      (struct netbuf *buf,
+                                   const void *dataptr, u16_t size);
+void              netbuf_chain    (struct netbuf *head, struct netbuf *tail);
+
+err_t             netbuf_data     (struct netbuf *buf,
+                                   void **dataptr, u16_t *len);
+s8_t              netbuf_next     (struct netbuf *buf);
+void              netbuf_first    (struct netbuf *buf);
+
+
+#define netbuf_copy_partial(buf, dataptr, len, offset) \
+  pbuf_copy_partial((buf)->p, (dataptr), (len), (offset))
+#define netbuf_copy(buf,dataptr,len) netbuf_copy_partial(buf, dataptr, len, 0)
+#define netbuf_take(buf, dataptr, len) pbuf_take((buf)->p, dataptr, len)
+#define netbuf_len(buf)              ((buf)->p->tot_len)
+#define netbuf_fromaddr(buf)         (&((buf)->addr))
+#define netbuf_set_fromaddr(buf, fromaddr) ip_addr_set(&((buf)->addr), fromaddr)
+#define netbuf_fromport(buf)         ((buf)->port)
+#if LWIP_NETBUF_RECVINFO
+#define netbuf_destaddr(buf)         (&((buf)->toaddr))
+#define netbuf_set_destaddr(buf, destaddr) ip_addr_set(&((buf)->toaddr), destaddr)
+#if LWIP_CHECKSUM_ON_COPY
+#define netbuf_destport(buf)         (((buf)->flags & NETBUF_FLAG_DESTADDR) ? (buf)->toport_chksum : 0)
+#else /* LWIP_CHECKSUM_ON_COPY */
+#define netbuf_destport(buf)         ((buf)->toport_chksum)
+#endif /* LWIP_CHECKSUM_ON_COPY */
+#endif /* LWIP_NETBUF_RECVINFO */
+#if LWIP_CHECKSUM_ON_COPY
+#define netbuf_set_chksum(buf, chksum) do { (buf)->flags = NETBUF_FLAG_CHKSUM; \
+                                            (buf)->toport_chksum = chksum; } while(0)
+#endif /* LWIP_CHECKSUM_ON_COPY */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_NETCONN || LWIP_SOCKET */
+
+#endif /* LWIP_HDR_NETBUF_H */
diff --git a/lib/lwip/include/lwip/netdb.h b/lib/lwip/include/lwip/netdb.h
new file mode 100644
index 0000000000..d3d15dfac5
--- /dev/null
+++ b/lib/lwip/include/lwip/netdb.h
@@ -0,0 +1,150 @@
+/**
+ * @file
+ * NETDB API (sockets)
+ */
+
+/*
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt
+ *
+ */
+#ifndef LWIP_HDR_NETDB_H
+#define LWIP_HDR_NETDB_H
+
+#include "lwip/opt.h"
+
+#if LWIP_DNS && LWIP_SOCKET
+
+#include "lwip/arch.h"
+#include "lwip/inet.h"
+#include "lwip/sockets.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* some rarely used options */
+#ifndef LWIP_DNS_API_DECLARE_H_ERRNO
+#define LWIP_DNS_API_DECLARE_H_ERRNO  1
+#endif
+
+#ifndef LWIP_DNS_API_DEFINE_ERRORS
+#define LWIP_DNS_API_DEFINE_ERRORS    1
+#endif
+
+#ifndef LWIP_DNS_API_DEFINE_FLAGS
+#define LWIP_DNS_API_DEFINE_FLAGS     1
+#endif
+
+#ifndef LWIP_DNS_API_DECLARE_STRUCTS
+#define LWIP_DNS_API_DECLARE_STRUCTS  1
+#endif
+
+#if LWIP_DNS_API_DEFINE_ERRORS
+/** Errors used by the DNS API functions, h_errno can be one of them */
+#define EAI_NONAME      200
+#define EAI_SERVICE     201
+#define EAI_FAIL        202
+#define EAI_MEMORY      203
+#define EAI_FAMILY      204
+
+#define HOST_NOT_FOUND  210
+#define NO_DATA         211
+#define NO_RECOVERY     212
+#define TRY_AGAIN       213
+#endif /* LWIP_DNS_API_DEFINE_ERRORS */
+
+#if LWIP_DNS_API_DEFINE_FLAGS
+/* input flags for struct addrinfo */
+#define AI_PASSIVE      0x01
+#define AI_CANONNAME    0x02
+#define AI_NUMERICHOST  0x04
+#define AI_NUMERICSERV  0x08
+#define AI_V4MAPPED     0x10
+#define AI_ALL          0x20
+#define AI_ADDRCONFIG   0x40
+#endif /* LWIP_DNS_API_DEFINE_FLAGS */
+
+#if LWIP_DNS_API_DECLARE_STRUCTS
+struct hostent {
+    char  *h_name;      /* Official name of the host. */
+    char **h_aliases;   /* A pointer to an array of pointers to alternative host names,
+                           terminated by a null pointer. */
+    int    h_addrtype;  /* Address type. */
+    int    h_length;    /* The length, in bytes, of the address. */
+    char **h_addr_list; /* A pointer to an array of pointers to network addresses (in
+                           network byte order) for the host, terminated by a null pointer. */
+#define h_addr h_addr_list[0] /* for backward compatibility */
+};
+
+struct addrinfo {
+    int               ai_flags;      /* Input flags. */
+    int               ai_family;     /* Address family of socket. */
+    int               ai_socktype;   /* Socket type. */
+    int               ai_protocol;   /* Protocol of socket. */
+    socklen_t         ai_addrlen;    /* Length of socket address. */
+    struct sockaddr  *ai_addr;       /* Socket address of socket. */
+    char             *ai_canonname;  /* Canonical name of service location. */
+    struct addrinfo  *ai_next;       /* Pointer to next in list. */
+};
+#endif /* LWIP_DNS_API_DECLARE_STRUCTS */
+
+#define NETDB_ELEM_SIZE           (sizeof(struct addrinfo) + sizeof(struct sockaddr_storage) + DNS_MAX_NAME_LENGTH + 1)
+
+#if LWIP_DNS_API_DECLARE_H_ERRNO
+/* application accessible error code set by the DNS API functions */
+extern int h_errno;
+#endif /* LWIP_DNS_API_DECLARE_H_ERRNO*/
+
+struct hostent *lwip_gethostbyname(const char *name);
+int lwip_gethostbyname_r(const char *name, struct hostent *ret, char *buf,
+                size_t buflen, struct hostent **result, int *h_errnop);
+void lwip_freeaddrinfo(struct addrinfo *ai);
+int lwip_getaddrinfo(const char *nodename,
+       const char *servname,
+       const struct addrinfo *hints,
+       struct addrinfo **res);
+
+#if LWIP_COMPAT_SOCKETS
+/** @ingroup netdbapi */
+#define gethostbyname(name) lwip_gethostbyname(name)
+/** @ingroup netdbapi */
+#define gethostbyname_r(name, ret, buf, buflen, result, h_errnop) \
+       lwip_gethostbyname_r(name, ret, buf, buflen, result, h_errnop)
+/** @ingroup netdbapi */
+#define freeaddrinfo(addrinfo) lwip_freeaddrinfo(addrinfo)
+/** @ingroup netdbapi */
+#define getaddrinfo(nodname, servname, hints, res) \
+       lwip_getaddrinfo(nodname, servname, hints, res)
+#endif /* LWIP_COMPAT_SOCKETS */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_DNS && LWIP_SOCKET */
+
+#endif /* LWIP_HDR_NETDB_H */
diff --git a/lib/lwip/include/lwip/netif.h b/lib/lwip/include/lwip/netif.h
new file mode 100644
index 0000000000..0cde2c2ad2
--- /dev/null
+++ b/lib/lwip/include/lwip/netif.h
@@ -0,0 +1,698 @@
+/**
+ * @file
+ * netif API (to be used from TCPIP thread)
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef LWIP_HDR_NETIF_H
+#define LWIP_HDR_NETIF_H
+
+#include "lwip/opt.h"
+
+#define ENABLE_LOOPBACK (LWIP_NETIF_LOOPBACK || LWIP_HAVE_LOOPIF)
+
+#include "lwip/err.h"
+
+#include "lwip/ip_addr.h"
+
+#include "lwip/def.h"
+#include "lwip/pbuf.h"
+#include "lwip/stats.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Throughout this file, IP addresses are expected to be in
+ * the same byte order as in IP_PCB. */
+
+/** Must be the maximum of all used hardware address lengths
+    across all types of interfaces in use.
+    This does not have to be changed, normally. */
+#ifndef NETIF_MAX_HWADDR_LEN
+#define NETIF_MAX_HWADDR_LEN 6U
+#endif
+
+/** The size of a fully constructed netif name which the
+ * netif can be identified by in APIs. Composed of
+ * 2 chars, 3 (max) digits, and 1 \0
+ */
+#define NETIF_NAMESIZE 6
+
+/**
+ * @defgroup netif_flags Flags
+ * @ingroup netif
+ * @{
+ */
+
+/** Whether the network interface is 'up'. This is
+ * a software flag used to control whether this network
+ * interface is enabled and processes traffic.
+ * It must be set by the startup code before this netif can be used
+ * (also for dhcp/autoip).
+ */
+#define NETIF_FLAG_UP           0x01U
+/** If set, the netif has broadcast capability.
+ * Set by the netif driver in its init function. */
+#define NETIF_FLAG_BROADCAST    0x02U
+/** If set, the interface has an active link
+ *  (set by the network interface driver).
+ * Either set by the netif driver in its init function (if the link
+ * is up at that time) or at a later point once the link comes up
+ * (if link detection is supported by the hardware). */
+#define NETIF_FLAG_LINK_UP      0x04U
+/** If set, the netif is an ethernet device using ARP.
+ * Set by the netif driver in its init function.
+ * Used to check input packet types and use of DHCP. */
+#define NETIF_FLAG_ETHARP       0x08U
+/** If set, the netif is an ethernet device. It might not use
+ * ARP or TCP/IP if it is used for PPPoE only.
+ */
+#define NETIF_FLAG_ETHERNET     0x10U
+/** If set, the netif has IGMP capability.
+ * Set by the netif driver in its init function. */
+#define NETIF_FLAG_IGMP         0x20U
+/** If set, the netif has MLD6 capability.
+ * Set by the netif driver in its init function. */
+#define NETIF_FLAG_MLD6         0x40U
+
+/**
+ * @}
+ */
+
+enum lwip_internal_netif_client_data_index
+{
+#if LWIP_IPV4
+#if LWIP_DHCP
+   LWIP_NETIF_CLIENT_DATA_INDEX_DHCP,
+#endif
+#if LWIP_AUTOIP
+   LWIP_NETIF_CLIENT_DATA_INDEX_AUTOIP,
+#endif
+#if LWIP_ACD
+   LWIP_NETIF_CLIENT_DATA_INDEX_ACD,
+#endif
+#if LWIP_IGMP
+   LWIP_NETIF_CLIENT_DATA_INDEX_IGMP,
+#endif
+#endif /* LWIP_IPV4 */
+#if LWIP_IPV6
+#if LWIP_IPV6_DHCP6
+   LWIP_NETIF_CLIENT_DATA_INDEX_DHCP6,
+#endif
+#if LWIP_IPV6_MLD
+   LWIP_NETIF_CLIENT_DATA_INDEX_MLD6,
+#endif
+#endif /* LWIP_IPV6 */
+   LWIP_NETIF_CLIENT_DATA_INDEX_MAX
+};
+
+#if LWIP_CHECKSUM_CTRL_PER_NETIF
+#define NETIF_CHECKSUM_GEN_IP       0x0001
+#define NETIF_CHECKSUM_GEN_UDP      0x0002
+#define NETIF_CHECKSUM_GEN_TCP      0x0004
+#define NETIF_CHECKSUM_GEN_ICMP     0x0008
+#define NETIF_CHECKSUM_GEN_ICMP6    0x0010
+#define NETIF_CHECKSUM_CHECK_IP     0x0100
+#define NETIF_CHECKSUM_CHECK_UDP    0x0200
+#define NETIF_CHECKSUM_CHECK_TCP    0x0400
+#define NETIF_CHECKSUM_CHECK_ICMP   0x0800
+#define NETIF_CHECKSUM_CHECK_ICMP6  0x1000
+#define NETIF_CHECKSUM_ENABLE_ALL   0xFFFF
+#define NETIF_CHECKSUM_DISABLE_ALL  0x0000
+#endif /* LWIP_CHECKSUM_CTRL_PER_NETIF */
+
+struct netif;
+
+/** MAC Filter Actions, these are passed to a netif's igmp_mac_filter or
+ * mld_mac_filter callback function. */
+enum netif_mac_filter_action {
+  /** Delete a filter entry */
+  NETIF_DEL_MAC_FILTER = 0,
+  /** Add a filter entry */
+  NETIF_ADD_MAC_FILTER = 1
+};
+
+/** Function prototype for netif init functions. Set up flags and output/linkoutput
+ * callback functions in this function.
+ *
+ * @param netif The netif to initialize
+ */
+typedef err_t (*netif_init_fn)(struct netif *netif);
+/** Function prototype for netif->input functions. This function is saved as 'input'
+ * callback function in the netif struct. Call it when a packet has been received.
+ *
+ * @param p The received packet, copied into a pbuf
+ * @param inp The netif which received the packet
+ * @return ERR_OK if the packet was handled
+ *         != ERR_OK is the packet was NOT handled, in this case, the caller has
+ *                   to free the pbuf
+ */
+typedef err_t (*netif_input_fn)(struct pbuf *p, struct netif *inp);
+
+#if LWIP_IPV4
+/** Function prototype for netif->output functions. Called by lwIP when a packet
+ * shall be sent. For ethernet netif, set this to 'etharp_output' and set
+ * 'linkoutput'.
+ *
+ * @param netif The netif which shall send a packet
+ * @param p The packet to send (p->payload points to IP header)
+ * @param ipaddr The IP address to which the packet shall be sent
+ */
+typedef err_t (*netif_output_fn)(struct netif *netif, struct pbuf *p,
+       const ip4_addr_t *ipaddr);
+#endif /* LWIP_IPV4*/
+
+#if LWIP_IPV6
+/** Function prototype for netif->output_ip6 functions. Called by lwIP when a packet
+ * shall be sent. For ethernet netif, set this to 'ethip6_output' and set
+ * 'linkoutput'.
+ *
+ * @param netif The netif which shall send a packet
+ * @param p The packet to send (p->payload points to IP header)
+ * @param ipaddr The IPv6 address to which the packet shall be sent
+ */
+typedef err_t (*netif_output_ip6_fn)(struct netif *netif, struct pbuf *p,
+       const ip6_addr_t *ipaddr);
+#endif /* LWIP_IPV6 */
+
+/** Function prototype for netif->linkoutput functions. Only used for ethernet
+ * netifs. This function is called by ARP when a packet shall be sent.
+ *
+ * @param netif The netif which shall send a packet
+ * @param p The packet to send (raw ethernet packet)
+ */
+typedef err_t (*netif_linkoutput_fn)(struct netif *netif, struct pbuf *p);
+/** Function prototype for netif status- or link-callback functions. */
+typedef void (*netif_status_callback_fn)(struct netif *netif);
+#if LWIP_IPV4 && LWIP_IGMP
+/** Function prototype for netif igmp_mac_filter functions */
+typedef err_t (*netif_igmp_mac_filter_fn)(struct netif *netif,
+       const ip4_addr_t *group, enum netif_mac_filter_action action);
+#endif /* LWIP_IPV4 && LWIP_IGMP */
+#if LWIP_IPV6 && LWIP_IPV6_MLD
+/** Function prototype for netif mld_mac_filter functions */
+typedef err_t (*netif_mld_mac_filter_fn)(struct netif *netif,
+       const ip6_addr_t *group, enum netif_mac_filter_action action);
+#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
+
+#if LWIP_DHCP || LWIP_AUTOIP || LWIP_IGMP || LWIP_IPV6_MLD || LWIP_IPV6_DHCP6 || (LWIP_NUM_NETIF_CLIENT_DATA > 0)
+#if LWIP_NUM_NETIF_CLIENT_DATA > 0
+u8_t netif_alloc_client_data_id(void);
+#endif
+/** @ingroup netif_cd
+ * Set client data. Obtain ID from netif_alloc_client_data_id().
+ */
+#define netif_set_client_data(netif, id, data) netif_get_client_data(netif, id) = (data)
+/** @ingroup netif_cd
+ * Get client data. Obtain ID from netif_alloc_client_data_id().
+ */
+#define netif_get_client_data(netif, id)       (netif)->client_data[(id)]
+#endif
+
+#if (LWIP_IPV4 && LWIP_ARP && (ARP_TABLE_SIZE > 0x7f)) || (LWIP_IPV6 && (LWIP_ND6_NUM_DESTINATIONS > 0x7f))
+typedef u16_t netif_addr_idx_t;
+#define NETIF_ADDR_IDX_MAX 0x7FFF
+#else
+typedef u8_t netif_addr_idx_t;
+#define NETIF_ADDR_IDX_MAX 0x7F
+#endif
+
+#if LWIP_NETIF_HWADDRHINT || LWIP_VLAN_PCP
+ #define LWIP_NETIF_USE_HINTS              1
+ struct netif_hint {
+#if LWIP_NETIF_HWADDRHINT
+   u8_t addr_hint;
+#endif
+#if LWIP_VLAN_PCP
+  /** VLAN hader is set if this is >= 0 (but must be <= 0xFFFF) */
+  s32_t tci;
+#endif
+ };
+#else /* LWIP_NETIF_HWADDRHINT || LWIP_VLAN_PCP */
+ #define LWIP_NETIF_USE_HINTS              0
+#endif /* LWIP_NETIF_HWADDRHINT || LWIP_VLAN_PCP*/
+
+/** Generic data structure used for all lwIP network interfaces.
+ *  The following fields should be filled in by the initialization
+ *  function for the device driver: hwaddr_len, hwaddr[], mtu, flags */
+struct netif {
+#if !LWIP_SINGLE_NETIF
+  /** pointer to next in linked list */
+  struct netif *next;
+#endif
+
+#if LWIP_IPV4
+  /** IP address configuration in network byte order */
+  ip_addr_t ip_addr;
+  ip_addr_t netmask;
+  ip_addr_t gw;
+#endif /* LWIP_IPV4 */
+#if LWIP_IPV6
+  /** Array of IPv6 addresses for this netif. */
+  ip_addr_t ip6_addr[LWIP_IPV6_NUM_ADDRESSES];
+  /** The state of each IPv6 address (Tentative, Preferred, etc).
+   * @see ip6_addr.h */
+  u8_t ip6_addr_state[LWIP_IPV6_NUM_ADDRESSES];
+#if LWIP_IPV6_ADDRESS_LIFETIMES
+  /** Remaining valid and preferred lifetime of each IPv6 address, in seconds.
+   * For valid lifetimes, the special value of IP6_ADDR_LIFE_STATIC (0)
+   * indicates the address is static and has no lifetimes. */
+  u32_t ip6_addr_valid_life[LWIP_IPV6_NUM_ADDRESSES];
+  u32_t ip6_addr_pref_life[LWIP_IPV6_NUM_ADDRESSES];
+#endif /* LWIP_IPV6_ADDRESS_LIFETIMES */
+#endif /* LWIP_IPV6 */
+  /** This function is called by the network device driver
+   *  to pass a packet up the TCP/IP stack. */
+  netif_input_fn input;
+#if LWIP_IPV4
+  /** This function is called by the IP module when it wants
+   *  to send a packet on the interface. This function typically
+   *  first resolves the hardware address, then sends the packet.
+   *  For ethernet physical layer, this is usually etharp_output() */
+  netif_output_fn output;
+#endif /* LWIP_IPV4 */
+  /** This function is called by ethernet_output() when it wants
+   *  to send a packet on the interface. This function outputs
+   *  the pbuf as-is on the link medium. */
+  netif_linkoutput_fn linkoutput;
+#if LWIP_IPV6
+  /** This function is called by the IPv6 module when it wants
+   *  to send a packet on the interface. This function typically
+   *  first resolves the hardware address, then sends the packet.
+   *  For ethernet physical layer, this is usually ethip6_output() */
+  netif_output_ip6_fn output_ip6;
+#endif /* LWIP_IPV6 */
+#if LWIP_NETIF_STATUS_CALLBACK
+  /** This function is called when the netif state is set to up or down
+   */
+  netif_status_callback_fn status_callback;
+#endif /* LWIP_NETIF_STATUS_CALLBACK */
+#if LWIP_NETIF_LINK_CALLBACK
+  /** This function is called when the netif link is set to up or down
+   */
+  netif_status_callback_fn link_callback;
+#endif /* LWIP_NETIF_LINK_CALLBACK */
+#if LWIP_NETIF_REMOVE_CALLBACK
+  /** This function is called when the netif has been removed */
+  netif_status_callback_fn remove_callback;
+#endif /* LWIP_NETIF_REMOVE_CALLBACK */
+  /** This field can be set by the device driver and could point
+   *  to state information for the device. */
+  void *state;
+#ifdef netif_get_client_data
+  void* client_data[LWIP_NETIF_CLIENT_DATA_INDEX_MAX + LWIP_NUM_NETIF_CLIENT_DATA];
+#endif
+#if LWIP_NETIF_HOSTNAME
+  /* the hostname for this netif, NULL is a valid value */
+  const char*  hostname;
+#endif /* LWIP_NETIF_HOSTNAME */
+#if LWIP_CHECKSUM_CTRL_PER_NETIF
+  u16_t chksum_flags;
+#endif /* LWIP_CHECKSUM_CTRL_PER_NETIF*/
+  /** maximum transfer unit (in bytes) */
+  u16_t mtu;
+#if LWIP_IPV6 && LWIP_ND6_ALLOW_RA_UPDATES
+  /** maximum transfer unit (in bytes), updated by RA */
+  u16_t mtu6;
+#endif /* LWIP_IPV6 && LWIP_ND6_ALLOW_RA_UPDATES */
+  /** link level hardware address of this interface */
+  u8_t hwaddr[NETIF_MAX_HWADDR_LEN];
+  /** number of bytes used in hwaddr */
+  u8_t hwaddr_len;
+  /** flags (@see @ref netif_flags) */
+  u8_t flags;
+  /** descriptive abbreviation */
+  char name[2];
+  /** number of this interface. Used for @ref if_api and @ref netifapi_netif,
+   * as well as for IPv6 zones */
+  u8_t num;
+#if LWIP_IPV6_AUTOCONFIG
+  /** is this netif enabled for IPv6 autoconfiguration */
+  u8_t ip6_autoconfig_enabled;
+#endif /* LWIP_IPV6_AUTOCONFIG */
+#if LWIP_IPV6_SEND_ROUTER_SOLICIT
+  /** Number of Router Solicitation messages that remain to be sent. */
+  u8_t rs_count;
+#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */
+#if MIB2_STATS
+  /** link type (from "snmp_ifType" enum from snmp_mib2.h) */
+  u8_t link_type;
+  /** (estimate) link speed */
+  u32_t link_speed;
+  /** timestamp at last change made (up/down) */
+  u32_t ts;
+  /** counters */
+  struct stats_mib2_netif_ctrs mib2_counters;
+#endif /* MIB2_STATS */
+#if LWIP_IPV4 && LWIP_IGMP
+  /** This function could be called to add or delete an entry in the multicast
+      filter table of the ethernet MAC.*/
+  netif_igmp_mac_filter_fn igmp_mac_filter;
+#endif /* LWIP_IPV4 && LWIP_IGMP */
+#if LWIP_IPV6 && LWIP_IPV6_MLD
+  /** This function could be called to add or delete an entry in the IPv6 multicast
+      filter table of the ethernet MAC. */
+  netif_mld_mac_filter_fn mld_mac_filter;
+#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
+#if LWIP_ACD
+  struct acd *acd_list;
+#endif /* LWIP_ACD */
+#if LWIP_NETIF_USE_HINTS
+  struct netif_hint *hints;
+#endif /* LWIP_NETIF_USE_HINTS */
+#if ENABLE_LOOPBACK
+  /* List of packets to be queued for ourselves. */
+  struct pbuf *loop_first;
+  struct pbuf *loop_last;
+#if LWIP_LOOPBACK_MAX_PBUFS
+  u16_t loop_cnt_current;
+#endif /* LWIP_LOOPBACK_MAX_PBUFS */
+#if LWIP_NETIF_LOOPBACK_MULTITHREADING
+  /* Used if the original scheduling failed. */
+  u8_t reschedule_poll;
+#endif /* LWIP_NETIF_LOOPBACK_MULTITHREADING */
+#endif /* ENABLE_LOOPBACK */
+};
+
+#if LWIP_CHECKSUM_CTRL_PER_NETIF
+#define NETIF_SET_CHECKSUM_CTRL(netif, chksumflags) do { \
+  (netif)->chksum_flags = chksumflags; } while(0)
+#define NETIF_CHECKSUM_ENABLED(netif, chksumflag) (((netif) == NULL) || (((netif)->chksum_flags & (chksumflag)) != 0))
+#define IF__NETIF_CHECKSUM_ENABLED(netif, chksumflag) if NETIF_CHECKSUM_ENABLED(netif, chksumflag)
+#else /* LWIP_CHECKSUM_CTRL_PER_NETIF */
+#define NETIF_CHECKSUM_ENABLED(netif, chksumflag) 0
+#define NETIF_SET_CHECKSUM_CTRL(netif, chksumflags)
+#define IF__NETIF_CHECKSUM_ENABLED(netif, chksumflag)
+#endif /* LWIP_CHECKSUM_CTRL_PER_NETIF */
+
+#if LWIP_SINGLE_NETIF
+#define NETIF_FOREACH(netif) if (((netif) = netif_default) != NULL)
+#else /* LWIP_SINGLE_NETIF */
+/** The list of network interfaces. */
+extern struct netif *netif_list;
+#define NETIF_FOREACH(netif) for ((netif) = netif_list; (netif) != NULL; (netif) = (netif)->next)
+#endif /* LWIP_SINGLE_NETIF */
+/** The default network interface. */
+extern struct netif *netif_default;
+
+void netif_init(void);
+
+struct netif *netif_add_noaddr(struct netif *netif, void *state, netif_init_fn init, netif_input_fn input);
+
+#if LWIP_IPV4
+struct netif *netif_add(struct netif *netif,
+                            const ip4_addr_t *ipaddr, const ip4_addr_t *netmask, const ip4_addr_t *gw,
+                            void *state, netif_init_fn init, netif_input_fn input);
+void netif_set_addr(struct netif *netif, const ip4_addr_t *ipaddr, const ip4_addr_t *netmask,
+                    const ip4_addr_t *gw);
+#else /* LWIP_IPV4 */
+struct netif *netif_add(struct netif *netif, void *state, netif_init_fn init, netif_input_fn input);
+#endif /* LWIP_IPV4 */
+void netif_remove(struct netif * netif);
+
+/* Returns a network interface given its name. The name is of the form
+   "et0", where the first two letters are the "name" field in the
+   netif structure, and the digit is in the num field in the same
+   structure. */
+struct netif *netif_find(const char *name);
+
+void netif_set_default(struct netif *netif);
+
+#if LWIP_IPV4
+void netif_set_ipaddr(struct netif *netif, const ip4_addr_t *ipaddr);
+void netif_set_netmask(struct netif *netif, const ip4_addr_t *netmask);
+void netif_set_gw(struct netif *netif, const ip4_addr_t *gw);
+/** @ingroup netif_ip4 */
+#define netif_ip4_addr(netif)    ((const ip4_addr_t*)ip_2_ip4(&((netif)->ip_addr)))
+/** @ingroup netif_ip4 */
+#define netif_ip4_netmask(netif) ((const ip4_addr_t*)ip_2_ip4(&((netif)->netmask)))
+/** @ingroup netif_ip4 */
+#define netif_ip4_gw(netif)      ((const ip4_addr_t*)ip_2_ip4(&((netif)->gw)))
+/** @ingroup netif_ip4 */
+#define netif_ip_addr4(netif)    ((const ip_addr_t*)&((netif)->ip_addr))
+/** @ingroup netif_ip4 */
+#define netif_ip_netmask4(netif) ((const ip_addr_t*)&((netif)->netmask))
+/** @ingroup netif_ip4 */
+#define netif_ip_gw4(netif)      ((const ip_addr_t*)&((netif)->gw))
+#endif /* LWIP_IPV4 */
+
+#define netif_set_flags(netif, set_flags)     do { (netif)->flags = (u8_t)((netif)->flags |  (set_flags)); } while(0)
+#define netif_clear_flags(netif, clr_flags)   do { (netif)->flags = (u8_t)((netif)->flags & (u8_t)(~(clr_flags) & 0xff)); } while(0)
+#define netif_is_flag_set(netif, flag)        (((netif)->flags & (flag)) != 0)
+
+void netif_set_up(struct netif *netif);
+void netif_set_down(struct netif *netif);
+/** @ingroup netif
+ * Ask if an interface is up
+ */
+#define netif_is_up(netif) (((netif)->flags & NETIF_FLAG_UP) ? (u8_t)1 : (u8_t)0)
+
+#if LWIP_NETIF_STATUS_CALLBACK
+void netif_set_status_callback(struct netif *netif, netif_status_callback_fn status_callback);
+#endif /* LWIP_NETIF_STATUS_CALLBACK */
+#if LWIP_NETIF_REMOVE_CALLBACK
+void netif_set_remove_callback(struct netif *netif, netif_status_callback_fn remove_callback);
+#endif /* LWIP_NETIF_REMOVE_CALLBACK */
+
+void netif_set_link_up(struct netif *netif);
+void netif_set_link_down(struct netif *netif);
+/** Ask if a link is up */
+#define netif_is_link_up(netif) (((netif)->flags & NETIF_FLAG_LINK_UP) ? (u8_t)1 : (u8_t)0)
+
+#if LWIP_NETIF_LINK_CALLBACK
+void netif_set_link_callback(struct netif *netif, netif_status_callback_fn link_callback);
+#endif /* LWIP_NETIF_LINK_CALLBACK */
+
+#if LWIP_NETIF_HOSTNAME
+/** @ingroup netif */
+#define netif_set_hostname(netif, name) do { if((netif) != NULL) { (netif)->hostname = name; }}while(0)
+/** @ingroup netif */
+#define netif_get_hostname(netif) (((netif) != NULL) ? ((netif)->hostname) : NULL)
+#endif /* LWIP_NETIF_HOSTNAME */
+
+#if LWIP_IGMP
+/** @ingroup netif
+ * Set igmp mac filter function for a netif. */
+#define netif_set_igmp_mac_filter(netif, function) do { if((netif) != NULL) { (netif)->igmp_mac_filter = function; }}while(0)
+/** Get the igmp mac filter function for a netif. */
+#define netif_get_igmp_mac_filter(netif) (((netif) != NULL) ? ((netif)->igmp_mac_filter) : NULL)
+#endif /* LWIP_IGMP */
+
+#if LWIP_IPV6 && LWIP_IPV6_MLD
+/** @ingroup netif
+ * Set mld mac filter function for a netif. */
+#define netif_set_mld_mac_filter(netif, function) do { if((netif) != NULL) { (netif)->mld_mac_filter = function; }}while(0)
+/** Get the mld mac filter function for a netif. */
+#define netif_get_mld_mac_filter(netif) (((netif) != NULL) ? ((netif)->mld_mac_filter) : NULL)
+#define netif_mld_mac_filter(netif, addr, action) do { if((netif) && (netif)->mld_mac_filter) { (netif)->mld_mac_filter((netif), (addr), (action)); }}while(0)
+#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
+
+#if ENABLE_LOOPBACK
+err_t netif_loop_output(struct netif *netif, struct pbuf *p);
+void netif_poll(struct netif *netif);
+#if !LWIP_NETIF_LOOPBACK_MULTITHREADING
+void netif_poll_all(void);
+#endif /* !LWIP_NETIF_LOOPBACK_MULTITHREADING */
+#endif /* ENABLE_LOOPBACK */
+
+err_t netif_input(struct pbuf *p, struct netif *inp);
+
+#if LWIP_IPV6
+/** @ingroup netif_ip6 */
+#define netif_ip_addr6(netif, i)  ((const ip_addr_t*)(&((netif)->ip6_addr[i])))
+/** @ingroup netif_ip6 */
+#define netif_ip6_addr(netif, i)  ((const ip6_addr_t*)ip_2_ip6(&((netif)->ip6_addr[i])))
+void netif_ip6_addr_set(struct netif *netif, s8_t addr_idx, const ip6_addr_t *addr6);
+void netif_ip6_addr_set_parts(struct netif *netif, s8_t addr_idx, u32_t i0, u32_t i1, u32_t i2, u32_t i3);
+#define netif_ip6_addr_state(netif, i)  ((netif)->ip6_addr_state[i])
+void netif_ip6_addr_set_state(struct netif* netif, s8_t addr_idx, u8_t state);
+s8_t netif_get_ip6_addr_match(struct netif *netif, const ip6_addr_t *ip6addr);
+void netif_create_ip6_linklocal_address(struct netif *netif, u8_t from_mac_48bit);
+err_t netif_add_ip6_address(struct netif *netif, const ip6_addr_t *ip6addr, s8_t *chosen_idx);
+#define netif_set_ip6_autoconfig_enabled(netif, action) do { if(netif) { (netif)->ip6_autoconfig_enabled = (action); }}while(0)
+#if LWIP_IPV6_ADDRESS_LIFETIMES
+#define netif_ip6_addr_valid_life(netif, i)  \
+    (((netif) != NULL) ? ((netif)->ip6_addr_valid_life[i]) : IP6_ADDR_LIFE_STATIC)
+#define netif_ip6_addr_set_valid_life(netif, i, secs) \
+    do { if (netif != NULL) { (netif)->ip6_addr_valid_life[i] = (secs); }} while (0)
+#define netif_ip6_addr_pref_life(netif, i)  \
+    (((netif) != NULL) ? ((netif)->ip6_addr_pref_life[i]) : IP6_ADDR_LIFE_STATIC)
+#define netif_ip6_addr_set_pref_life(netif, i, secs) \
+    do { if (netif != NULL) { (netif)->ip6_addr_pref_life[i] = (secs); }} while (0)
+#define netif_ip6_addr_isstatic(netif, i)  \
+    (netif_ip6_addr_valid_life((netif), (i)) == IP6_ADDR_LIFE_STATIC)
+#else /* !LWIP_IPV6_ADDRESS_LIFETIMES */
+#define netif_ip6_addr_isstatic(netif, i)  (1) /* all addresses are static */
+#endif /* !LWIP_IPV6_ADDRESS_LIFETIMES */
+#if LWIP_ND6_ALLOW_RA_UPDATES
+#define netif_mtu6(netif) ((netif)->mtu6)
+#else /* LWIP_ND6_ALLOW_RA_UPDATES */
+#define netif_mtu6(netif) ((netif)->mtu)
+#endif /* LWIP_ND6_ALLOW_RA_UPDATES */
+#endif /* LWIP_IPV6 */
+
+#if LWIP_NETIF_USE_HINTS
+#define NETIF_SET_HINTS(netif, netifhint)  (netif)->hints = (netifhint)
+#define NETIF_RESET_HINTS(netif)      (netif)->hints = NULL
+#else /* LWIP_NETIF_USE_HINTS */
+#define NETIF_SET_HINTS(netif, netifhint)
+#define NETIF_RESET_HINTS(netif)
+#endif /* LWIP_NETIF_USE_HINTS */
+
+u8_t netif_name_to_index(const char *name);
+char * netif_index_to_name(u8_t idx, char *name);
+struct netif* netif_get_by_index(u8_t idx);
+
+/* Interface indexes always start at 1 per RFC 3493, section 4, num starts at 0 (internal index is 0..254)*/
+#define netif_get_index(netif)      ((u8_t)((netif)->num + 1))
+#define NETIF_NO_INDEX              (0)
+
+/**
+ * @ingroup netif
+ * Extended netif status callback (NSC) reasons flags.
+ * May be extended in the future!
+ */
+typedef u16_t netif_nsc_reason_t;
+
+/* used for initialization only */
+#define LWIP_NSC_NONE                     0x0000
+/** netif was added. arg: NULL. Called AFTER netif was added. */
+#define LWIP_NSC_NETIF_ADDED              0x0001
+/** netif was removed. arg: NULL. Called BEFORE netif is removed. */
+#define LWIP_NSC_NETIF_REMOVED            0x0002
+/** link changed */
+#define LWIP_NSC_LINK_CHANGED             0x0004
+/** netif administrative status changed.<br>
+  * up is called AFTER netif is set up.<br>
+  * down is called BEFORE the netif is actually set down. */
+#define LWIP_NSC_STATUS_CHANGED           0x0008
+/** IPv4 address has changed */
+#define LWIP_NSC_IPV4_ADDRESS_CHANGED     0x0010
+/** IPv4 gateway has changed */
+#define LWIP_NSC_IPV4_GATEWAY_CHANGED     0x0020
+/** IPv4 netmask has changed */
+#define LWIP_NSC_IPV4_NETMASK_CHANGED     0x0040
+/** called AFTER IPv4 address/gateway/netmask changes have been applied */
+#define LWIP_NSC_IPV4_SETTINGS_CHANGED    0x0080
+/** IPv6 address was added */
+#define LWIP_NSC_IPV6_SET                 0x0100
+/** IPv6 address state has changed */
+#define LWIP_NSC_IPV6_ADDR_STATE_CHANGED  0x0200
+/** IPv4 settings: valid address set, application may start to communicate */
+#define LWIP_NSC_IPV4_ADDR_VALID          0x0400
+
+/** @ingroup netif
+ * Argument supplied to netif_ext_callback_fn.
+ */
+typedef union
+{
+  /** Args to LWIP_NSC_LINK_CHANGED callback */
+  struct link_changed_s
+  {
+    /** 1: up; 0: down */
+    u8_t state;
+  } link_changed;
+  /** Args to LWIP_NSC_STATUS_CHANGED callback */
+  struct status_changed_s
+  {
+    /** 1: up; 0: down */
+    u8_t state;
+  } status_changed;
+  /** Args to LWIP_NSC_IPV4_ADDRESS_CHANGED|LWIP_NSC_IPV4_GATEWAY_CHANGED|LWIP_NSC_IPV4_NETMASK_CHANGED|LWIP_NSC_IPV4_SETTINGS_CHANGED callback */
+  struct ipv4_changed_s
+  {
+    /** Old IPv4 address */
+    const ip_addr_t* old_address;
+    const ip_addr_t* old_netmask;
+    const ip_addr_t* old_gw;
+  } ipv4_changed;
+  /** Args to LWIP_NSC_IPV6_SET callback */
+  struct ipv6_set_s
+  {
+    /** Index of changed IPv6 address */
+    s8_t addr_index;
+    /** Old IPv6 address */
+    const ip_addr_t* old_address;
+  } ipv6_set;
+  /** Args to LWIP_NSC_IPV6_ADDR_STATE_CHANGED callback */
+  struct ipv6_addr_state_changed_s
+  {
+    /** Index of affected IPv6 address */
+    s8_t addr_index;
+    /** Old IPv6 address state */
+    u8_t old_state;
+    /** Affected IPv6 address */
+    const ip_addr_t* address;
+  } ipv6_addr_state_changed;
+} netif_ext_callback_args_t;
+
+/**
+ * @ingroup netif
+ * Function used for extended netif status callbacks
+ * Note: When parsing reason argument, keep in mind that more reasons may be added in the future!
+ * @param netif netif that is affected by change
+ * @param reason change reason
+ * @param args depends on reason, see reason description
+ */
+typedef void (*netif_ext_callback_fn)(struct netif* netif, netif_nsc_reason_t reason, const netif_ext_callback_args_t* args);
+
+#if LWIP_NETIF_EXT_STATUS_CALLBACK
+struct netif_ext_callback;
+typedef struct netif_ext_callback
+{
+  netif_ext_callback_fn callback_fn;
+  struct netif_ext_callback* next;
+} netif_ext_callback_t;
+
+#define NETIF_DECLARE_EXT_CALLBACK(name) static netif_ext_callback_t name;
+void netif_add_ext_callback(netif_ext_callback_t* callback, netif_ext_callback_fn fn);
+void netif_remove_ext_callback(netif_ext_callback_t* callback);
+void netif_invoke_ext_callback(struct netif* netif, netif_nsc_reason_t reason, const netif_ext_callback_args_t* args);
+#else
+#define NETIF_DECLARE_EXT_CALLBACK(name)
+#define netif_add_ext_callback(callback, fn)
+#define netif_remove_ext_callback(callback)
+#define netif_invoke_ext_callback(netif, reason, args)
+#endif
+
+#if LWIP_TESTMODE && LWIP_HAVE_LOOPIF
+struct netif* netif_get_loopif(void);
+#endif
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_NETIF_H */
diff --git a/lib/lwip/include/lwip/netifapi.h b/lib/lwip/include/lwip/netifapi.h
new file mode 100644
index 0000000000..e0631791b9
--- /dev/null
+++ b/lib/lwip/include/lwip/netifapi.h
@@ -0,0 +1,161 @@
+/**
+ * @file
+ * netif API (to be used from non-TCPIP threads)
+ */
+
+/*
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ */
+#ifndef LWIP_HDR_NETIFAPI_H
+#define LWIP_HDR_NETIFAPI_H
+
+#include "lwip/opt.h"
+
+#if LWIP_NETIF_API /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/sys.h"
+#include "lwip/netif.h"
+#include "lwip/dhcp.h"
+#include "lwip/autoip.h"
+#include "lwip/priv/tcpip_priv.h"
+#include "lwip/priv/api_msg.h"
+#include "lwip/prot/ethernet.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* API for application */
+#if LWIP_ARP && LWIP_IPV4
+/* Used for netfiapi_arp_* APIs */
+enum netifapi_arp_entry {
+  NETIFAPI_ARP_PERM /* Permanent entry */
+  /* Other entry types can be added here */
+};
+
+/** @ingroup netifapi_arp */
+err_t netifapi_arp_add(const ip4_addr_t *ipaddr, struct eth_addr *ethaddr, enum netifapi_arp_entry type);
+/** @ingroup netifapi_arp */
+err_t netifapi_arp_remove(const ip4_addr_t *ipaddr, enum netifapi_arp_entry type);
+#endif /* LWIP_ARP && LWIP_IPV4 */
+
+err_t netifapi_netif_add(struct netif *netif,
+#if LWIP_IPV4
+                         const ip4_addr_t *ipaddr, const ip4_addr_t *netmask, const ip4_addr_t *gw,
+#endif /* LWIP_IPV4 */
+                         void *state, netif_init_fn init, netif_input_fn input);
+
+#if LWIP_IPV4
+err_t netifapi_netif_set_addr(struct netif *netif, const ip4_addr_t *ipaddr,
+                              const ip4_addr_t *netmask, const ip4_addr_t *gw);
+#endif /* LWIP_IPV4*/
+
+err_t netifapi_netif_common(struct netif *netif, netifapi_void_fn voidfunc,
+                            netifapi_errt_fn errtfunc);
+
+/** @ingroup netifapi_netif */
+err_t netifapi_netif_name_to_index(const char *name, u8_t *index);
+/** @ingroup netifapi_netif */
+err_t netifapi_netif_index_to_name(u8_t index, char *name);
+
+/** @ingroup netifapi_netif
+  * @see netif_remove()
+  */
+#define netifapi_netif_remove(n)        netifapi_netif_common(n, netif_remove, NULL)
+/** @ingroup netifapi_netif
+  * @see netif_set_up()
+  */
+#define netifapi_netif_set_up(n)        netifapi_netif_common(n, netif_set_up, NULL)
+/** @ingroup netifapi_netif
+  * @see netif_set_down()
+  */
+#define netifapi_netif_set_down(n)      netifapi_netif_common(n, netif_set_down, NULL)
+/** @ingroup netifapi_netif
+  * @see netif_set_default()
+  */
+#define netifapi_netif_set_default(n)   netifapi_netif_common(n, netif_set_default, NULL)
+/** @ingroup netifapi_netif
+  * @see netif_set_link_up()
+  */
+#define netifapi_netif_set_link_up(n)   netifapi_netif_common(n, netif_set_link_up, NULL)
+/** @ingroup netifapi_netif
+  * @see netif_set_link_down()
+  */
+#define netifapi_netif_set_link_down(n) netifapi_netif_common(n, netif_set_link_down, NULL)
+
+/**
+ * @defgroup netifapi_dhcp4 DHCPv4
+ * @ingroup netifapi
+ * To be called from non-TCPIP threads
+ */
+/** @ingroup netifapi_dhcp4
+  * @see dhcp_start()
+  */
+#define netifapi_dhcp_start(n)            netifapi_netif_common(n, NULL, dhcp_start)
+/**
+ * @ingroup netifapi_dhcp4
+ * @deprecated Use netifapi_dhcp_release_and_stop() instead.
+ */
+#define netifapi_dhcp_stop(n)             netifapi_netif_common(n, dhcp_stop, NULL)
+/** @ingroup netifapi_dhcp4
+  * @see dhcp_inform()
+  */
+#define netifapi_dhcp_inform(n)           netifapi_netif_common(n, dhcp_inform, NULL)
+/** @ingroup netifapi_dhcp4
+  * @see dhcp_renew()
+  */
+#define netifapi_dhcp_renew(n)            netifapi_netif_common(n, NULL, dhcp_renew)
+/**
+ * @ingroup netifapi_dhcp4
+ * @deprecated Use netifapi_dhcp_release_and_stop() instead.
+ */
+#define netifapi_dhcp_release(n)          netifapi_netif_common(n, NULL, dhcp_release)
+/** @ingroup netifapi_dhcp4
+  * @see dhcp_release_and_stop()
+  */
+#define netifapi_dhcp_release_and_stop(n) netifapi_netif_common(n, dhcp_release_and_stop, NULL)
+
+/**
+ * @defgroup netifapi_autoip AUTOIP
+ * @ingroup netifapi
+ * To be called from non-TCPIP threads
+ */
+/** @ingroup netifapi_autoip
+  * @see autoip_start()
+  */
+#define netifapi_autoip_start(n)      netifapi_netif_common(n, NULL, autoip_start)
+/** @ingroup netifapi_autoip
+  * @see autoip_stop()
+  */
+#define netifapi_autoip_stop(n)       netifapi_netif_common(n, NULL, autoip_stop)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_NETIF_API */
+
+#endif /* LWIP_HDR_NETIFAPI_H */
diff --git a/lib/lwip/include/lwip/opt.h b/lib/lwip/include/lwip/opt.h
new file mode 100644
index 0000000000..c27dd0349d
--- /dev/null
+++ b/lib/lwip/include/lwip/opt.h
@@ -0,0 +1,3595 @@
+/**
+ * @file
+ *
+ * lwIP Options Configuration
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+/*
+ * NOTE: || defined __DOXYGEN__ is a workaround for doxygen bug -
+ * without this, doxygen does not see the actual #define
+ */
+
+#if !defined LWIP_HDR_OPT_H
+#define LWIP_HDR_OPT_H
+
+/*
+ * Include user defined options first. Anything not defined in these files
+ * will be set to standard values. Override anything you don't like!
+ */
+#include "lwipopts.h"
+#include "lwip/debug.h"
+
+/**
+ * @defgroup lwip_opts Options (lwipopts.h)
+ * @ingroup lwip
+ *
+ * @defgroup lwip_opts_debug Debugging
+ * @ingroup lwip_opts
+ *
+ * @defgroup lwip_opts_infrastructure Infrastructure
+ * @ingroup lwip_opts
+ *
+ * @defgroup lwip_opts_callback Callback-style APIs
+ * @ingroup lwip_opts
+ *
+ * @defgroup lwip_opts_threadsafe_apis Thread-safe APIs
+ * @ingroup lwip_opts
+ */
+
+ /*
+   ------------------------------------
+   -------------- NO SYS --------------
+   ------------------------------------
+*/
+/**
+ * @defgroup lwip_opts_nosys NO_SYS
+ * @ingroup lwip_opts_infrastructure
+ * @{
+ */
+/**
+ * NO_SYS==1: Use lwIP without OS-awareness (no thread, semaphores, mutexes or
+ * mboxes). This means threaded APIs cannot be used (socket, netconn,
+ * i.e. everything in the 'api' folder), only the callback-style raw API is
+ * available (and you have to watch out for yourself that you don't access
+ * lwIP functions/structures from more than one context at a time!)
+ */
+#if !defined NO_SYS || defined __DOXYGEN__
+#define NO_SYS                          0
+#endif
+/**
+ * @}
+ */
+
+/**
+ * @defgroup lwip_opts_timers Timers
+ * @ingroup lwip_opts_infrastructure
+ * @{
+ */
+/**
+ * LWIP_TIMERS==0: Drop support for sys_timeout and lwip-internal cyclic timers.
+ * (the array of lwip-internal cyclic timers is still provided)
+ * (check NO_SYS_NO_TIMERS for compatibility to old versions)
+ */
+#if !defined LWIP_TIMERS || defined __DOXYGEN__
+#ifdef NO_SYS_NO_TIMERS
+#define LWIP_TIMERS                     (!NO_SYS || (NO_SYS && !NO_SYS_NO_TIMERS))
+#else
+#define LWIP_TIMERS                     1
+#endif
+#endif
+
+/**
+ * LWIP_TIMERS_CUSTOM==1: Provide your own timer implementation.
+ * Function prototypes in timeouts.h and the array of lwip-internal cyclic timers
+ * are still included, but the implementation is not. The following functions
+ * will be required: sys_timeouts_init(), sys_timeout(), sys_untimeout(),
+ *                   sys_timeouts_mbox_fetch()
+ */
+#if !defined LWIP_TIMERS_CUSTOM || defined __DOXYGEN__
+#define LWIP_TIMERS_CUSTOM              0
+#endif
+/**
+ * @}
+ */
+
+/**
+ * @defgroup lwip_opts_memcpy memcpy
+ * @ingroup lwip_opts_infrastructure
+ * @{
+ */
+/**
+ * MEMCPY: override this if you have a faster implementation at hand than the
+ * one included in your C library
+ */
+#if !defined MEMCPY || defined __DOXYGEN__
+#define MEMCPY(dst,src,len)             memcpy(dst,src,len)
+#endif
+
+/**
+ * SMEMCPY: override this with care! Some compilers (e.g. gcc) can inline a
+ * call to memcpy() if the length is known at compile time and is small.
+ */
+#if !defined SMEMCPY || defined __DOXYGEN__
+#define SMEMCPY(dst,src,len)            memcpy(dst,src,len)
+#endif
+
+/**
+ * MEMMOVE: override this if you have a faster implementation at hand than the
+ * one included in your C library.  lwIP currently uses MEMMOVE only when IPv6
+ * fragmentation support is enabled.
+ */
+#if !defined MEMMOVE || defined __DOXYGEN__
+#define MEMMOVE(dst,src,len)            memmove(dst,src,len)
+#endif
+/**
+ * @}
+ */
+
+/*
+   ------------------------------------
+   ----------- Core locking -----------
+   ------------------------------------
+*/
+/**
+ * @defgroup lwip_opts_lock Core locking and MPU
+ * @ingroup lwip_opts_infrastructure
+ * @{
+ */
+/**
+ * LWIP_MPU_COMPATIBLE: enables special memory management mechanism
+ * which makes lwip able to work on MPU (Memory Protection Unit) system
+ * by not passing stack-pointers to other threads
+ * (this decreases performance as memory is allocated from pools instead
+ * of keeping it on the stack)
+ */
+#if !defined LWIP_MPU_COMPATIBLE || defined __DOXYGEN__
+#define LWIP_MPU_COMPATIBLE             0
+#endif
+
+/**
+ * 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.
+ */
+#if !defined LWIP_TCPIP_CORE_LOCKING || defined __DOXYGEN__
+#define LWIP_TCPIP_CORE_LOCKING         1
+#endif
+
+/**
+ * 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!
+ */
+#if !defined LWIP_TCPIP_CORE_LOCKING_INPUT || defined __DOXYGEN__
+#define LWIP_TCPIP_CORE_LOCKING_INPUT   0
+#endif
+
+/**
+ * SYS_LIGHTWEIGHT_PROT==1: enable inter-task protection (and task-vs-interrupt
+ * protection) for certain critical regions during buffer allocation, deallocation
+ * and memory allocation and deallocation.
+ * ATTENTION: This is required when using lwIP from more than one context! If
+ * you disable this, you must be sure what you are doing!
+ */
+#if !defined SYS_LIGHTWEIGHT_PROT || defined __DOXYGEN__
+#define SYS_LIGHTWEIGHT_PROT            1
+#endif
+
+/**
+ * Macro/function to check whether lwIP's threading/locking
+ * requirements are satisfied during current function call.
+ * This macro usually calls a function that is implemented in the OS-dependent
+ * sys layer and performs the following checks:
+ * - Not in ISR (this should be checked for NO_SYS==1, too!)
+ * - If @ref LWIP_TCPIP_CORE_LOCKING = 1: TCPIP core lock is held
+ * - If @ref LWIP_TCPIP_CORE_LOCKING = 0: function is called from TCPIP thread
+ * @see @ref multithreading
+ */
+#if !defined LWIP_ASSERT_CORE_LOCKED || defined __DOXYGEN__
+#define LWIP_ASSERT_CORE_LOCKED()
+#endif
+
+
+/**
+ * @}
+ */
+
+/*
+   ------------------------------------
+   ---------- Memory options ----------
+   ------------------------------------
+*/
+/**
+ * @defgroup lwip_opts_mem Heap and memory pools
+ * @ingroup lwip_opts_infrastructure
+ * @{
+ */
+/**
+ * MEM_LIBC_MALLOC==1: Use malloc/free/realloc provided by your C-library
+ * instead of the lwip internal allocator. Can save code size if you
+ * already use it.
+ */
+#if !defined MEM_LIBC_MALLOC || defined __DOXYGEN__
+#define MEM_LIBC_MALLOC                 0
+#endif
+
+/**
+ * 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)!
+ */
+#if !defined MEMP_MEM_MALLOC || defined __DOXYGEN__
+#define MEMP_MEM_MALLOC                 0
+#endif
+
+/**
+ * MEMP_MEM_INIT==1: Force use of memset to initialize pool memory.
+ * Useful if pool are moved in uninitialized section of memory. This will ensure
+ * default values in pcbs struct are well initialized in all conditions.
+ */
+#if !defined MEMP_MEM_INIT || defined __DOXYGEN__
+#define MEMP_MEM_INIT                   0
+#endif
+
+/**
+ * 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
+ */
+#if !defined MEM_ALIGNMENT || defined __DOXYGEN__
+#define MEM_ALIGNMENT                   1
+#endif
+
+/**
+ * MEM_SIZE: the size of the heap memory. If the application will send
+ * a lot of data that needs to be copied, this should be set high.
+ */
+#if !defined MEM_SIZE || defined __DOXYGEN__
+#define MEM_SIZE                        1600
+#endif
+
+/**
+ * MEMP_OVERFLOW_CHECK: memp overflow protection reserves a configurable
+ * amount of bytes before and after each memp element in every pool and fills
+ * it with a prominent default value.
+ *    MEMP_OVERFLOW_CHECK == 0 no checking
+ *    MEMP_OVERFLOW_CHECK == 1 checks each element when it is freed
+ *    MEMP_OVERFLOW_CHECK >= 2 checks each element in every pool every time
+ *      memp_malloc() or memp_free() is called (useful but slow!)
+ */
+#if !defined MEMP_OVERFLOW_CHECK || defined __DOXYGEN__
+#define MEMP_OVERFLOW_CHECK             0
+#endif
+
+/**
+ * MEMP_SANITY_CHECK==1: run a sanity check after each memp_free() to make
+ * sure that there are no cycles in the linked lists.
+ */
+#if !defined MEMP_SANITY_CHECK || defined __DOXYGEN__
+#define MEMP_SANITY_CHECK               0
+#endif
+
+/**
+ * MEM_OVERFLOW_CHECK: mem overflow protection reserves a configurable
+ * amount of bytes before and after each heap allocation chunk and fills
+ * it with a prominent default value.
+ *    MEM_OVERFLOW_CHECK == 0 no checking
+ *    MEM_OVERFLOW_CHECK == 1 checks each element when it is freed
+ *    MEM_OVERFLOW_CHECK >= 2 checks all heap elements every time
+ *      mem_malloc() or mem_free() is called (useful but slow!)
+ */
+#if !defined MEM_OVERFLOW_CHECK || defined __DOXYGEN__
+#define MEM_OVERFLOW_CHECK              0
+#endif
+
+/**
+ * MEM_SANITY_CHECK==1: run a sanity check after each mem_free() to make
+ * sure that the linked list of heap elements is not corrupted.
+ */
+#if !defined MEM_SANITY_CHECK || defined __DOXYGEN__
+#define MEM_SANITY_CHECK                0
+#endif
+
+/**
+ * MEM_USE_POOLS==1: Use an alternative to malloc() by allocating from a set
+ * of memory pools of various sizes. When mem_malloc is called, an element of
+ * the smallest pool that can provide the length needed is returned.
+ * To use this, MEMP_USE_CUSTOM_POOLS also has to be enabled.
+ */
+#if !defined MEM_USE_POOLS || defined __DOXYGEN__
+#define MEM_USE_POOLS                   0
+#endif
+
+/**
+ * MEM_USE_POOLS_TRY_BIGGER_POOL==1: if one malloc-pool is empty, try the next
+ * bigger pool - WARNING: THIS MIGHT WASTE MEMORY but it can make a system more
+ * reliable. */
+#if !defined MEM_USE_POOLS_TRY_BIGGER_POOL || defined __DOXYGEN__
+#define MEM_USE_POOLS_TRY_BIGGER_POOL   0
+#endif
+
+/**
+ * MEMP_USE_CUSTOM_POOLS==1: whether to include a user file lwippools.h
+ * that defines additional pools beyond the "standard" ones required
+ * by lwIP. If you set this to 1, you must have lwippools.h in your
+ * include path somewhere.
+ */
+#if !defined MEMP_USE_CUSTOM_POOLS || defined __DOXYGEN__
+#define MEMP_USE_CUSTOM_POOLS           0
+#endif
+
+/**
+ * Set this to 1 if you want to free PBUF_RAM pbufs (or call mem_free()) from
+ * interrupt context (or another context that doesn't allow waiting for a
+ * semaphore).
+ * If set to 1, mem_malloc will be protected by a semaphore and SYS_ARCH_PROTECT,
+ * while mem_free will only use SYS_ARCH_PROTECT. mem_malloc SYS_ARCH_UNPROTECTs
+ * with each loop so that mem_free can run.
+ *
+ * ATTENTION: As you can see from the above description, this leads to dis-/
+ * enabling interrupts often, which can be slow! Also, on low memory, mem_malloc
+ * can need longer.
+ *
+ * If you don't want that, at least for NO_SYS=0, you can still use the following
+ * functions to enqueue a deallocation call which then runs in the tcpip_thread
+ * context:
+ * - pbuf_free_callback(p);
+ * - mem_free_callback(m);
+ */
+#if !defined LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT || defined __DOXYGEN__
+#define LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT 0
+#endif
+/**
+ * @}
+ */
+
+/*
+   ------------------------------------------------
+   ---------- Internal Memory Pool Sizes ----------
+   ------------------------------------------------
+*/
+/**
+ * @defgroup lwip_opts_memp Internal memory pools
+ * @ingroup lwip_opts_infrastructure
+ * @{
+ */
+/**
+ * MEMP_NUM_PBUF: the number of memp struct pbufs (used for PBUF_ROM and PBUF_REF).
+ * If the application sends a lot of data out of ROM (or other static memory),
+ * this should be set high.
+ */
+#if !defined MEMP_NUM_PBUF || defined __DOXYGEN__
+#define MEMP_NUM_PBUF                   16
+#endif
+
+/**
+ * MEMP_NUM_RAW_PCB: Number of raw connection PCBs
+ * (requires the LWIP_RAW option)
+ */
+#if !defined MEMP_NUM_RAW_PCB || defined __DOXYGEN__
+#define MEMP_NUM_RAW_PCB                4
+#endif
+
+/**
+ * MEMP_NUM_UDP_PCB: the number of UDP protocol control blocks. One
+ * per active UDP "connection".
+ * (requires the LWIP_UDP option)
+ */
+#if !defined MEMP_NUM_UDP_PCB || defined __DOXYGEN__
+#define MEMP_NUM_UDP_PCB                4
+#endif
+
+/**
+ * MEMP_NUM_TCP_PCB: the number of simultaneously active TCP connections.
+ * (requires the LWIP_TCP option)
+ */
+#if !defined MEMP_NUM_TCP_PCB || defined __DOXYGEN__
+#define MEMP_NUM_TCP_PCB                5
+#endif
+
+/**
+ * MEMP_NUM_TCP_PCB_LISTEN: the number of listening TCP connections.
+ * (requires the LWIP_TCP option)
+ */
+#if !defined MEMP_NUM_TCP_PCB_LISTEN || defined __DOXYGEN__
+#define MEMP_NUM_TCP_PCB_LISTEN         8
+#endif
+
+/**
+ * MEMP_NUM_TCP_SEG: the number of simultaneously queued TCP segments.
+ * (requires the LWIP_TCP option)
+ */
+#if !defined MEMP_NUM_TCP_SEG || defined __DOXYGEN__
+#define MEMP_NUM_TCP_SEG                16
+#endif
+
+/**
+ * MEMP_NUM_ALTCP_PCB: the number of simultaneously active altcp layer pcbs.
+ * (requires the LWIP_ALTCP option)
+ * Connections with multiple layers require more than one altcp_pcb (e.g. TLS
+ * over TCP requires 2 altcp_pcbs, one for TLS and one for TCP).
+ */
+#if !defined MEMP_NUM_ALTCP_PCB || defined __DOXYGEN__
+#define MEMP_NUM_ALTCP_PCB              MEMP_NUM_TCP_PCB
+#endif
+
+/**
+ * MEMP_NUM_REASSDATA: the number of IP packets simultaneously queued for
+ * reassembly (whole packets, not fragments!)
+ */
+#if !defined MEMP_NUM_REASSDATA || defined __DOXYGEN__
+#define MEMP_NUM_REASSDATA              5
+#endif
+
+/**
+ * MEMP_NUM_FRAG_PBUF: the number of IP fragments simultaneously sent
+ * (fragments, not whole packets!).
+ * This is only used with LWIP_NETIF_TX_SINGLE_PBUF==0 and only has to be > 1
+ * with DMA-enabled MACs where the packet is not yet sent when netif->output
+ * returns.
+ */
+#if !defined MEMP_NUM_FRAG_PBUF || defined __DOXYGEN__
+#define MEMP_NUM_FRAG_PBUF              15
+#endif
+
+/**
+ * MEMP_NUM_ARP_QUEUE: the number of simultaneously queued outgoing
+ * packets (pbufs) that are waiting for an ARP request (to resolve
+ * their destination address) to finish.
+ * (requires the ARP_QUEUEING option)
+ */
+#if !defined MEMP_NUM_ARP_QUEUE || defined __DOXYGEN__
+#define MEMP_NUM_ARP_QUEUE              30
+#endif
+
+/**
+ * MEMP_NUM_IGMP_GROUP: The number of multicast groups whose network interfaces
+ * can be members at the same time (one per netif - allsystems group -, plus one
+ * per netif membership).
+ * (requires the LWIP_IGMP option)
+ */
+#if !defined MEMP_NUM_IGMP_GROUP || defined __DOXYGEN__
+#define MEMP_NUM_IGMP_GROUP             8
+#endif
+
+/**
+ * The number of sys timeouts used by the core stack (not apps)
+ * The default number of timeouts is calculated here for all enabled modules.
+ */
+#define LWIP_NUM_SYS_TIMEOUT_INTERNAL   (LWIP_TCP + IP_REASSEMBLY + LWIP_ARP + (2*LWIP_DHCP) + LWIP_ACD + LWIP_IGMP + LWIP_DNS + PPP_NUM_TIMEOUTS + (LWIP_IPV6 * (1 + LWIP_IPV6_REASS + LWIP_IPV6_MLD + LWIP_IPV6_DHCP6)))
+
+/**
+ * MEMP_NUM_SYS_TIMEOUT: the number of simultaneously active timeouts.
+ * The default number of timeouts is calculated here for all enabled modules.
+ * The formula expects settings to be either '0' or '1'.
+ */
+#if !defined MEMP_NUM_SYS_TIMEOUT || defined __DOXYGEN__
+#define MEMP_NUM_SYS_TIMEOUT            LWIP_NUM_SYS_TIMEOUT_INTERNAL
+#endif
+
+/**
+ * MEMP_NUM_NETBUF: the number of struct netbufs.
+ * (only needed if you use the sequential API, like api_lib.c)
+ */
+#if !defined MEMP_NUM_NETBUF || defined __DOXYGEN__
+#define MEMP_NUM_NETBUF                 2
+#endif
+
+/**
+ * MEMP_NUM_NETCONN: the number of struct netconns.
+ * (only needed if you use the sequential API, like api_lib.c)
+ */
+#if !defined MEMP_NUM_NETCONN || defined __DOXYGEN__
+#define MEMP_NUM_NETCONN                4
+#endif
+
+/**
+ * MEMP_NUM_SELECT_CB: the number of struct lwip_select_cb.
+ * (Only needed if you have LWIP_MPU_COMPATIBLE==1 and use the socket API.
+ * In that case, you need one per thread calling lwip_select.)
+ */
+#if !defined MEMP_NUM_SELECT_CB || defined __DOXYGEN__
+#define MEMP_NUM_SELECT_CB              4
+#endif
+
+/**
+ * MEMP_NUM_TCPIP_MSG_API: the number of struct tcpip_msg, which are used
+ * for callback/timeout API communication.
+ * (only needed if you use tcpip.c)
+ */
+#if !defined MEMP_NUM_TCPIP_MSG_API || defined __DOXYGEN__
+#define MEMP_NUM_TCPIP_MSG_API          8
+#endif
+
+/**
+ * MEMP_NUM_TCPIP_MSG_INPKT: the number of struct tcpip_msg, which are used
+ * for incoming packets.
+ * (only needed if you use tcpip.c)
+ */
+#if !defined MEMP_NUM_TCPIP_MSG_INPKT || defined __DOXYGEN__
+#define MEMP_NUM_TCPIP_MSG_INPKT        8
+#endif
+
+/**
+ * MEMP_NUM_NETDB: the number of concurrently running lwip_addrinfo() calls
+ * (before freeing the corresponding memory using lwip_freeaddrinfo()).
+ */
+#if !defined MEMP_NUM_NETDB || defined __DOXYGEN__
+#define MEMP_NUM_NETDB                  1
+#endif
+
+/**
+ * MEMP_NUM_LOCALHOSTLIST: the number of host entries in the local host list
+ * if DNS_LOCAL_HOSTLIST_IS_DYNAMIC==1.
+ */
+#if !defined MEMP_NUM_LOCALHOSTLIST || defined __DOXYGEN__
+#define MEMP_NUM_LOCALHOSTLIST          1
+#endif
+
+/**
+ * PBUF_POOL_SIZE: the number of buffers in the pbuf pool.
+ */
+#if !defined PBUF_POOL_SIZE || defined __DOXYGEN__
+#define PBUF_POOL_SIZE                  16
+#endif
+
+/** MEMP_NUM_API_MSG: the number of concurrently active calls to various
+ * socket, netconn, and tcpip functions
+ */
+#if !defined MEMP_NUM_API_MSG || defined __DOXYGEN__
+#define MEMP_NUM_API_MSG                MEMP_NUM_TCPIP_MSG_API
+#endif
+
+/** MEMP_NUM_DNS_API_MSG: the number of concurrently active calls to netconn_gethostbyname
+ */
+#if !defined MEMP_NUM_DNS_API_MSG || defined __DOXYGEN__
+#define MEMP_NUM_DNS_API_MSG            MEMP_NUM_TCPIP_MSG_API
+#endif
+
+/** MEMP_NUM_SOCKET_SETGETSOCKOPT_DATA: the number of concurrently active calls
+ * to getsockopt/setsockopt
+ */
+#if !defined MEMP_NUM_SOCKET_SETGETSOCKOPT_DATA || defined __DOXYGEN__
+#define MEMP_NUM_SOCKET_SETGETSOCKOPT_DATA MEMP_NUM_TCPIP_MSG_API
+#endif
+
+/** MEMP_NUM_NETIFAPI_MSG: the number of concurrently active calls to the
+ * netifapi functions
+ */
+#if !defined MEMP_NUM_NETIFAPI_MSG || defined __DOXYGEN__
+#define MEMP_NUM_NETIFAPI_MSG           MEMP_NUM_TCPIP_MSG_API
+#endif
+/**
+ * @}
+ */
+
+/*
+   ---------------------------------
+   ---------- ARP options ----------
+   ---------------------------------
+*/
+/**
+ * @defgroup lwip_opts_arp ARP
+ * @ingroup lwip_opts_ipv4
+ * @{
+ */
+/**
+ * LWIP_ARP==1: Enable ARP functionality.
+ */
+#if !defined LWIP_ARP || defined __DOXYGEN__
+#define LWIP_ARP                        1
+#endif
+
+/**
+ * ARP_TABLE_SIZE: Number of active MAC-IP address pairs cached.
+ */
+#if !defined ARP_TABLE_SIZE || defined __DOXYGEN__
+#define ARP_TABLE_SIZE                  10
+#endif
+
+/** the time an ARP entry stays valid after its last update,
+ *  for ARP_TMR_INTERVAL = 1000, this is
+ *  (60 * 5) seconds = 5 minutes.
+ */
+#if !defined ARP_MAXAGE || defined __DOXYGEN__
+#define ARP_MAXAGE                      300
+#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.
+ * This is sufficient for most protocols and mainly reduces TCP connection
+ * 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.
+ */
+#if !defined ARP_QUEUEING || defined __DOXYGEN__
+#define ARP_QUEUEING                    0
+#endif
+
+/** The maximum number of packets which may be queued for each
+ *  unresolved address by other network layers. Defaults to 3, 0 means disabled.
+ *  Old packets are dropped, new packets are queued.
+ */
+#if !defined ARP_QUEUE_LEN || defined __DOXYGEN__
+#define ARP_QUEUE_LEN                   3
+#endif
+
+/**
+ * ETHARP_SUPPORT_VLAN==1: support receiving and sending ethernet packets with
+ * VLAN header. See the description of LWIP_HOOK_VLAN_CHECK and
+ * LWIP_HOOK_VLAN_SET hooks to check/set VLAN headers.
+ * Additionally, you can define ETHARP_VLAN_CHECK to an u16_t VLAN ID to check.
+ * If ETHARP_VLAN_CHECK is defined, only VLAN-traffic for this VLAN is accepted.
+ * If ETHARP_VLAN_CHECK is not defined, all traffic is accepted.
+ * Alternatively, define a function/define ETHARP_VLAN_CHECK_FN(eth_hdr, vlan)
+ * that returns 1 to accept a packet or 0 to drop a packet.
+ */
+#if !defined ETHARP_SUPPORT_VLAN || defined __DOXYGEN__
+#define ETHARP_SUPPORT_VLAN             0
+#endif
+
+/**
+ * LWIP_VLAN_PCP==1: Enable outgoing VLAN taggning of frames on a per-PCB basis
+ * for QoS purposes. With this feature enabled, each PCB has a new variable:
+ * "netif_hints.tci" (Tag Control Identifier).
+ * The TCI contains three fields: VID, CFI and PCP.
+ * - VID is the VLAN ID, which should be set to zero.
+ * - The "CFI" bit is used to enable or disable VLAN tags for the PCB.
+ * - PCP (Priority Code Point) is a 3 bit field used for Ethernet level QoS.
+ * See pcb_tci_*() functions to get/set/clear this.
+ */
+#ifndef LWIP_VLAN_PCP
+#define LWIP_VLAN_PCP                   0
+#endif
+
+/** LWIP_ETHERNET==1: enable ethernet support even though ARP might be disabled
+ */
+#if !defined LWIP_ETHERNET || defined __DOXYGEN__
+#define LWIP_ETHERNET                   LWIP_ARP
+#endif
+
+/** ETH_PAD_SIZE: number of bytes added before the ethernet header to ensure
+ * alignment of payload after that header. Since the header is 14 bytes long,
+ * without this padding e.g. addresses in the IP header will not be aligned
+ * on a 32-bit boundary, so setting this to 2 can speed up 32-bit-platforms.
+ */
+#if !defined ETH_PAD_SIZE || defined __DOXYGEN__
+#define ETH_PAD_SIZE                    0
+#endif
+
+/** ETHARP_SUPPORT_STATIC_ENTRIES==1: enable code to support static ARP table
+ * entries (using etharp_add_static_entry/etharp_remove_static_entry).
+ */
+#if !defined ETHARP_SUPPORT_STATIC_ENTRIES || defined __DOXYGEN__
+#define ETHARP_SUPPORT_STATIC_ENTRIES   0
+#endif
+
+/** ETHARP_TABLE_MATCH_NETIF==1: Match netif for ARP table entries.
+ * If disabled, duplicate IP address on multiple netifs are not supported
+ * (but this should only occur for AutoIP).
+ */
+#if !defined ETHARP_TABLE_MATCH_NETIF || defined __DOXYGEN__
+#define ETHARP_TABLE_MATCH_NETIF        !LWIP_SINGLE_NETIF
+#endif
+/**
+ * @}
+ */
+
+/*
+   --------------------------------
+   ---------- IP options ----------
+   --------------------------------
+*/
+/**
+ * @defgroup lwip_opts_ipv4 IPv4
+ * @ingroup lwip_opts
+ * @{
+ */
+/**
+ * LWIP_IPV4==1: Enable IPv4
+ */
+#if !defined LWIP_IPV4 || defined __DOXYGEN__
+#define LWIP_IPV4                       1
+#endif
+
+/**
+ * IP_FORWARD==1: Enables the ability to forward IP packets across network
+ * interfaces. If you are going to run lwIP on a device with only one network
+ * interface, define this to 0.
+ */
+#if !defined IP_FORWARD || defined __DOXYGEN__
+#define IP_FORWARD                      0
+#endif
+
+/**
+ * IP_REASSEMBLY==1: Reassemble incoming fragmented IP packets. Note that
+ * this option does not affect outgoing packet sizes, which can be controlled
+ * via IP_FRAG.
+ */
+#if !defined IP_REASSEMBLY || defined __DOXYGEN__
+#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.
+ */
+#if !defined IP_FRAG || defined __DOXYGEN__
+#define IP_FRAG                         1
+#endif
+
+#if !LWIP_IPV4
+/* disable IPv4 extensions when IPv4 is disabled */
+#undef IP_FORWARD
+#define IP_FORWARD                      0
+#undef IP_REASSEMBLY
+#define IP_REASSEMBLY                   0
+#undef IP_FRAG
+#define IP_FRAG                         0
+#endif /* !LWIP_IPV4 */
+
+/**
+ * IP_OPTIONS_ALLOWED: Defines the behavior for IP options.
+ *      IP_OPTIONS_ALLOWED==0: All packets with IP options are dropped.
+ *      IP_OPTIONS_ALLOWED==1: IP options are allowed (but not parsed).
+ */
+#if !defined IP_OPTIONS_ALLOWED || defined __DOXYGEN__
+#define IP_OPTIONS_ALLOWED              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.
+ */
+#if !defined IP_REASS_MAXAGE || defined __DOXYGEN__
+#define IP_REASS_MAXAGE                 15
+#endif
+
+/**
+ * IP_REASS_MAX_PBUFS: Total maximum amount of pbufs waiting to be reassembled.
+ * Since the received pbufs are enqueued, be sure to configure
+ * 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!
+ * When IPv4 *and* IPv6 are enabled, this even changes to
+ * (PBUF_POOL_SIZE > 2 * IP_REASS_MAX_PBUFS)!
+ */
+#if !defined IP_REASS_MAX_PBUFS || defined __DOXYGEN__
+#define IP_REASS_MAX_PBUFS              10
+#endif
+
+/**
+ * IP_DEFAULT_TTL: Default value for Time-To-Live used by transport layers.
+ */
+#if !defined IP_DEFAULT_TTL || defined __DOXYGEN__
+#define IP_DEFAULT_TTL                  255
+#endif
+
+/**
+ * IP_SOF_BROADCAST=1: Use the SOF_BROADCAST field to enable broadcast
+ * filter per pcb on udp and raw send operations. To enable broadcast filter
+ * on recv operations, you also have to set IP_SOF_BROADCAST_RECV=1.
+ */
+#if !defined IP_SOF_BROADCAST || defined __DOXYGEN__
+#define IP_SOF_BROADCAST                0
+#endif
+
+/**
+ * IP_SOF_BROADCAST_RECV (requires IP_SOF_BROADCAST=1) enable the broadcast
+ * filter on recv operations.
+ */
+#if !defined IP_SOF_BROADCAST_RECV || defined __DOXYGEN__
+#define IP_SOF_BROADCAST_RECV           0
+#endif
+
+/**
+ * IP_FORWARD_ALLOW_TX_ON_RX_NETIF==1: allow ip_forward() to send packets back
+ * out on the netif where it was received. This should only be used for
+ * wireless networks.
+ * ATTENTION: When this is 1, make sure your netif driver correctly marks incoming
+ * link-layer-broadcast/multicast packets as such using the corresponding pbuf flags!
+ */
+#if !defined IP_FORWARD_ALLOW_TX_ON_RX_NETIF || defined __DOXYGEN__
+#define IP_FORWARD_ALLOW_TX_ON_RX_NETIF 0
+#endif
+/**
+ * @}
+ */
+
+/*
+   ----------------------------------
+   ---------- ICMP options ----------
+   ----------------------------------
+*/
+/**
+ * @defgroup lwip_opts_icmp ICMP
+ * @ingroup lwip_opts_ipv4
+ * @{
+ */
+/**
+ * LWIP_ICMP==1: Enable ICMP module inside the IP stack.
+ * Be careful, disable that make your product non-compliant to RFC1122
+ */
+#if !defined LWIP_ICMP || defined __DOXYGEN__
+#define LWIP_ICMP                       1
+#endif
+
+/**
+ * ICMP_TTL: Default value for Time-To-Live used by ICMP packets.
+ */
+#if !defined ICMP_TTL || defined __DOXYGEN__
+#define ICMP_TTL                        IP_DEFAULT_TTL
+#endif
+
+/**
+ * LWIP_BROADCAST_PING==1: respond to broadcast pings (default is unicast only)
+ */
+#if !defined LWIP_BROADCAST_PING || defined __DOXYGEN__
+#define LWIP_BROADCAST_PING             0
+#endif
+
+/**
+ * LWIP_MULTICAST_PING==1: respond to multicast pings (default is unicast only)
+ */
+#if !defined LWIP_MULTICAST_PING || defined __DOXYGEN__
+#define LWIP_MULTICAST_PING             0
+#endif
+/**
+ * @}
+ */
+
+/*
+   ---------------------------------
+   ---------- RAW options ----------
+   ---------------------------------
+*/
+/**
+ * @defgroup lwip_opts_raw RAW
+ * @ingroup lwip_opts_callback
+ * @{
+ */
+/**
+ * LWIP_RAW==1: Enable application layer to hook into the IP layer itself.
+ */
+#if !defined LWIP_RAW || defined __DOXYGEN__
+#define LWIP_RAW                        0
+#endif
+
+/**
+ * LWIP_RAW==1: Enable application layer to hook into the IP layer itself.
+ */
+#if !defined RAW_TTL || defined __DOXYGEN__
+#define RAW_TTL                         IP_DEFAULT_TTL
+#endif
+/**
+ * @}
+ */
+
+/*
+   ----------------------------------
+   ---------- DHCP options ----------
+   ----------------------------------
+*/
+/**
+ * @defgroup lwip_opts_dhcp DHCP
+ * @ingroup lwip_opts_ipv4
+ * @{
+ */
+/**
+ * LWIP_DHCP==1: Enable DHCP module.
+ */
+#if !defined LWIP_DHCP || defined __DOXYGEN__
+#define LWIP_DHCP                       0
+#endif
+#if !LWIP_IPV4
+/* disable DHCP when IPv4 is disabled */
+#undef LWIP_DHCP
+#define LWIP_DHCP                       0
+#endif /* !LWIP_IPV4 */
+
+/**
+ * LWIP_DHCP_DOES_ACD_CHECK==1: Perform address conflict detection on the dhcp address.
+ */
+#if !defined LWIP_DHCP_DOES_ACD_CHECK || defined __DOXYGEN__
+#define LWIP_DHCP_DOES_ACD_CHECK        LWIP_DHCP
+#endif
+
+/**
+ * LWIP_DHCP_BOOTP_FILE==1: Store offered_si_addr and boot_file_name.
+ */
+#if !defined LWIP_DHCP_BOOTP_FILE || defined __DOXYGEN__
+#define LWIP_DHCP_BOOTP_FILE            0
+#endif
+
+/**
+ * LWIP_DHCP_GETS_NTP==1: Request NTP servers with discover/select. For each
+ * response packet, an callback is called, which has to be provided by the port:
+ * void dhcp_set_ntp_servers(u8_t num_ntp_servers, ip_addr_t* ntp_server_addrs);
+*/
+#if !defined LWIP_DHCP_GET_NTP_SRV || defined __DOXYGEN__
+#define LWIP_DHCP_GET_NTP_SRV           0
+#endif
+
+/**
+ * The maximum of NTP servers requested
+ */
+#if !defined LWIP_DHCP_MAX_NTP_SERVERS || defined __DOXYGEN__
+#define LWIP_DHCP_MAX_NTP_SERVERS       1
+#endif
+
+/**
+ * LWIP_DHCP_MAX_DNS_SERVERS > 0: Request DNS servers with discover/select.
+ * DNS servers received in the response are passed to DNS via @ref dns_setserver()
+ * (up to the maximum limit defined here).
+ */
+#if !defined LWIP_DHCP_MAX_DNS_SERVERS || defined __DOXYGEN__
+#define LWIP_DHCP_MAX_DNS_SERVERS       DNS_MAX_SERVERS
+#endif
+
+/** LWIP_DHCP_DISCOVER_ADD_HOSTNAME: Set to 1 to include hostname opt in discover packets.
+ * If the hostname is not set in the DISCOVER packet, then some servers might issue an OFFER with hostname
+ * configured and consequently reject the REQUEST with any other hostname.
+ */
+#if !defined LWIP_DHCP_DISCOVER_ADD_HOSTNAME || defined __DOXYGEN__
+#define LWIP_DHCP_DISCOVER_ADD_HOSTNAME 0
+#endif /* LWIP_DHCP_DISCOVER_ADD_HOSTNAME */
+/**
+ * @}
+ */
+
+/*
+   ------------------------------------
+   ---------- AUTOIP options ----------
+   ------------------------------------
+*/
+/**
+ * @defgroup lwip_opts_autoip AUTOIP
+ * @ingroup lwip_opts_ipv4
+ * @{
+ */
+/**
+ * LWIP_AUTOIP==1: Enable AUTOIP module.
+ */
+#if !defined LWIP_AUTOIP || defined __DOXYGEN__
+#define LWIP_AUTOIP                     0
+#endif
+#if !LWIP_IPV4
+/* disable AUTOIP when IPv4 is disabled */
+#undef LWIP_AUTOIP
+#define LWIP_AUTOIP                     0
+#endif /* !LWIP_IPV4 */
+
+/**
+ * LWIP_DHCP_AUTOIP_COOP==1: Allow DHCP and AUTOIP to be both enabled on
+ * the same interface at the same time.
+ */
+#if !defined LWIP_DHCP_AUTOIP_COOP || defined __DOXYGEN__
+#define LWIP_DHCP_AUTOIP_COOP           0
+#endif
+
+/**
+ * LWIP_DHCP_AUTOIP_COOP_TRIES: Set to the number of DHCP DISCOVER probes
+ * that should be sent before falling back on AUTOIP (the DHCP client keeps
+ * running in this case). This can be set as low as 1 to get an AutoIP address
+ * very  quickly, but you should be prepared to handle a changing IP address
+ * when DHCP overrides AutoIP.
+ */
+#if !defined LWIP_DHCP_AUTOIP_COOP_TRIES || defined __DOXYGEN__
+#define LWIP_DHCP_AUTOIP_COOP_TRIES     9
+#endif
+/**
+ * @}
+ */
+
+/*
+   ------------------------------------
+   ----------- ACD options ------------
+   ------------------------------------
+*/
+/**
+ * @defgroup lwip_opts_acd ACD
+ * @ingroup lwip_opts_ipv4
+ * @{
+ */
+ /**
+  * LWIP_ACD==1: Enable ACD module. ACD module is needed when using AUTOIP.
+  */
+#if !defined LWIP_ACD || defined __DOXYGEN__
+#define LWIP_ACD                     (LWIP_AUTOIP || LWIP_DHCP_DOES_ACD_CHECK)
+#endif
+#if !LWIP_IPV4
+/* disable ACD when IPv4 is disabled */
+#undef LWIP_ACD
+#define LWIP_ACD                     0
+#endif /* !LWIP_IPV4 */
+/**
+ * @}
+ */
+
+/*
+   ----------------------------------
+   ----- SNMP MIB2 support      -----
+   ----------------------------------
+*/
+/**
+ * @defgroup lwip_opts_mib2 SNMP MIB2 callbacks
+ * @ingroup lwip_opts_infrastructure
+ * @{
+ */
+/**
+ * LWIP_MIB2_CALLBACKS==1: Turn on SNMP MIB2 callbacks.
+ * Turn this on to get callbacks needed to implement MIB2.
+ * Usually MIB2_STATS should be enabled, too.
+ */
+#if !defined LWIP_MIB2_CALLBACKS || defined __DOXYGEN__
+#define LWIP_MIB2_CALLBACKS             0
+#endif
+/**
+ * @}
+ */
+
+/*
+   ----------------------------------
+   -------- Multicast options -------
+   ----------------------------------
+*/
+/**
+ * @defgroup lwip_opts_multicast Multicast
+ * @ingroup lwip_opts_infrastructure
+ * @{
+ */
+/**
+ * LWIP_MULTICAST_TX_OPTIONS==1: Enable multicast TX support like the socket options
+ * IP_MULTICAST_TTL/IP_MULTICAST_IF/IP_MULTICAST_LOOP, as well as (currently only)
+ * core support for the corresponding IPv6 options.
+ */
+#if !defined LWIP_MULTICAST_TX_OPTIONS || defined __DOXYGEN__
+#define LWIP_MULTICAST_TX_OPTIONS       ((LWIP_IGMP || LWIP_IPV6_MLD) && (LWIP_UDP || LWIP_RAW))
+#endif
+/**
+ * @}
+ */
+
+/*
+   ----------------------------------
+   ---------- IGMP options ----------
+   ----------------------------------
+*/
+/**
+ * @defgroup lwip_opts_igmp IGMP
+ * @ingroup lwip_opts_ipv4
+ * @{
+ */
+/**
+ * LWIP_IGMP==1: Turn on IGMP module.
+ */
+#if !defined LWIP_IGMP || defined __DOXYGEN__
+#define LWIP_IGMP                       0
+#endif
+#if !LWIP_IPV4
+#undef LWIP_IGMP
+#define LWIP_IGMP                       0
+#endif
+/**
+ * @}
+ */
+
+/*
+   ----------------------------------
+   ---------- DNS options -----------
+   ----------------------------------
+*/
+/**
+ * @defgroup lwip_opts_dns DNS
+ * @ingroup lwip_opts_callback
+ * @{
+ */
+/**
+ * LWIP_DNS==1: Turn on DNS module. UDP must be available for DNS
+ * transport.
+ */
+#if !defined LWIP_DNS || defined __DOXYGEN__
+#define LWIP_DNS                        0
+#endif
+
+/** DNS maximum number of entries to maintain locally. */
+#if !defined DNS_TABLE_SIZE || defined __DOXYGEN__
+#define DNS_TABLE_SIZE                  4
+#endif
+
+/** DNS maximum host name length supported in the name table. */
+#if !defined DNS_MAX_NAME_LENGTH || defined __DOXYGEN__
+#define DNS_MAX_NAME_LENGTH             256
+#endif
+
+/** The maximum of DNS servers
+ * The first server can be initialized automatically by defining
+ * DNS_SERVER_ADDRESS(ipaddr), where 'ipaddr' is an 'ip_addr_t*'
+ */
+#if !defined DNS_MAX_SERVERS || defined __DOXYGEN__
+#define DNS_MAX_SERVERS                 2
+#endif
+
+/** DNS maximum number of retries when asking for a name, before "timeout". */
+#if !defined DNS_MAX_RETRIES || defined __DOXYGEN__
+#define DNS_MAX_RETRIES                 4
+#endif
+
+/** DNS do a name checking between the query and the response. */
+#if !defined DNS_DOES_NAME_CHECK || defined __DOXYGEN__
+#define DNS_DOES_NAME_CHECK             1
+#endif
+
+/** LWIP_DNS_SECURE: controls the security level of the DNS implementation
+ * Use all DNS security features by default.
+ * This is overridable but should only be needed by very small targets
+ * or when using against non standard DNS servers. */
+#if !defined LWIP_DNS_SECURE || defined __DOXYGEN__
+#define LWIP_DNS_SECURE (LWIP_DNS_SECURE_RAND_XID | LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING | LWIP_DNS_SECURE_RAND_SRC_PORT)
+#endif
+
+/* A list of DNS security features follows */
+#define LWIP_DNS_SECURE_RAND_XID                1
+#define LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING 2
+#define LWIP_DNS_SECURE_RAND_SRC_PORT           4
+
+/** DNS_LOCAL_HOSTLIST: Implements a local host-to-address list. If enabled, you have to define an initializer:
+ *  \#define DNS_LOCAL_HOSTLIST_INIT {DNS_LOCAL_HOSTLIST_ELEM("host_ip4", IPADDR4_INIT_BYTES(1,2,3,4)), \
+ *                                    DNS_LOCAL_HOSTLIST_ELEM("host_ip6", IPADDR6_INIT_HOST(123, 234, 345, 456)}
+ *
+ *  Instead, you can also use an external function:
+ *  \#define DNS_LOOKUP_LOCAL_EXTERN(name, namelen, addr, dns_addrtype) my_lookup_function(name, namelen, addr, dns_addrtype)
+ *  with function signature:
+ *  extern err_t my_lookup_function(const char *name, size_t namelen, ip_addr_t *addr, u8_t dns_addrtype)
+ *  that looks up the IP address and returns ERR_OK if found (LWIP_DNS_ADDRTYPE_xxx is passed in dns_addrtype).
+ */
+#if !defined DNS_LOCAL_HOSTLIST || defined __DOXYGEN__
+#define DNS_LOCAL_HOSTLIST              0
+#endif /* DNS_LOCAL_HOSTLIST */
+
+/** If this is turned on, the local host-list can be dynamically changed
+ *  at runtime. */
+#if !defined DNS_LOCAL_HOSTLIST_IS_DYNAMIC || defined __DOXYGEN__
+#define DNS_LOCAL_HOSTLIST_IS_DYNAMIC   0
+#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
+
+/** Set this to 1 to enable querying ".local" names via mDNS
+ *  using a One-Shot Multicast DNS Query */
+#if !defined LWIP_DNS_SUPPORT_MDNS_QUERIES || defined __DOXYGEN__
+#define LWIP_DNS_SUPPORT_MDNS_QUERIES   0
+#endif
+/**
+ * @}
+ */
+
+/*
+   ---------------------------------
+   ---------- UDP options ----------
+   ---------------------------------
+*/
+/**
+ * @defgroup lwip_opts_udp UDP
+ * @ingroup lwip_opts_callback
+ * @{
+ */
+/**
+ * LWIP_UDP==1: Turn on UDP.
+ */
+#if !defined LWIP_UDP || defined __DOXYGEN__
+#define LWIP_UDP                        1
+#endif
+
+/**
+ * LWIP_UDPLITE==1: Turn on UDP-Lite. (Requires LWIP_UDP)
+ */
+#if !defined LWIP_UDPLITE || defined __DOXYGEN__
+#define LWIP_UDPLITE                    0
+#endif
+
+/**
+ * UDP_TTL: Default Time-To-Live value.
+ */
+#if !defined UDP_TTL || defined __DOXYGEN__
+#define UDP_TTL                         IP_DEFAULT_TTL
+#endif
+
+/**
+ * LWIP_NETBUF_RECVINFO==1: append destination addr and port to every netbuf.
+ */
+#if !defined LWIP_NETBUF_RECVINFO || defined __DOXYGEN__
+#define LWIP_NETBUF_RECVINFO            0
+#endif
+/**
+ * @}
+ */
+
+/*
+   ---------------------------------
+   ---------- TCP options ----------
+   ---------------------------------
+*/
+/**
+ * @defgroup lwip_opts_tcp TCP
+ * @ingroup lwip_opts_callback
+ * @{
+ */
+/**
+ * LWIP_TCP==1: Turn on TCP.
+ */
+#if !defined LWIP_TCP || defined __DOXYGEN__
+#define LWIP_TCP                        1
+#endif
+
+/**
+ * TCP_TTL: Default Time-To-Live value.
+ */
+#if !defined TCP_TTL || defined __DOXYGEN__
+#define TCP_TTL                         IP_DEFAULT_TTL
+#endif
+
+/**
+ * TCP_WND: The size of a TCP window.  This must be at least
+ * (2 * TCP_MSS) for things to work well.
+ * ATTENTION: when using TCP_RCV_SCALE, TCP_WND is the total size
+ * with scaling applied. Maximum window value in the TCP header
+ * will be TCP_WND >> TCP_RCV_SCALE
+ */
+#if !defined TCP_WND || defined __DOXYGEN__
+#define TCP_WND                         (4 * TCP_MSS)
+#endif
+
+/**
+ * TCP_MAXRTX: Maximum number of retransmissions of data segments.
+ */
+#if !defined TCP_MAXRTX || defined __DOXYGEN__
+#define TCP_MAXRTX                      12
+#endif
+
+/**
+ * TCP_SYNMAXRTX: Maximum number of retransmissions of SYN segments.
+ */
+#if !defined TCP_SYNMAXRTX || defined __DOXYGEN__
+#define TCP_SYNMAXRTX                   6
+#endif
+
+/**
+ * TCP_QUEUE_OOSEQ==1: TCP will queue segments that arrive out of order.
+ * Define to 0 if your device is low on memory.
+ */
+#if !defined TCP_QUEUE_OOSEQ || defined __DOXYGEN__
+#define TCP_QUEUE_OOSEQ                 LWIP_TCP
+#endif
+
+/**
+ * LWIP_TCP_SACK_OUT==1: TCP will support sending selective acknowledgements (SACKs).
+ */
+#if !defined LWIP_TCP_SACK_OUT || defined __DOXYGEN__
+#define LWIP_TCP_SACK_OUT               0
+#endif
+
+/**
+ * LWIP_TCP_MAX_SACK_NUM: The maximum number of SACK values to include in TCP segments.
+ * Must be at least 1, but is only used if LWIP_TCP_SACK_OUT is enabled.
+ * NOTE: Even though we never send more than 3 or 4 SACK ranges in a single segment
+ * (depending on other options), setting this option to values greater than 4 is not pointless.
+ * This is basically the max number of SACK ranges we want to keep track of.
+ * As new data is delivered, some of the SACK ranges may be removed or merged.
+ * In that case some of those older SACK ranges may be used again.
+ * The amount of memory used to store SACK ranges is LWIP_TCP_MAX_SACK_NUM * 8 bytes for each TCP PCB.
+ */
+#if !defined LWIP_TCP_MAX_SACK_NUM || defined __DOXYGEN__
+#define LWIP_TCP_MAX_SACK_NUM           4
+#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.
+ */
+#if !defined TCP_MSS || defined __DOXYGEN__
+#define TCP_MSS                         536
+#endif
+
+/**
+ * TCP_CALCULATE_EFF_SEND_MSS: "The maximum size of a segment that TCP really
+ * sends, the 'effective send MSS,' MUST be the smaller of the send MSS (which
+ * reflects the available reassembly buffer size at the remote host) and the
+ * largest size permitted by the IP layer" (RFC 1122)
+ * Setting this to 1 enables code that checks TCP_MSS against the MTU of the
+ * netif used for a connection and limits the MSS if it would be too big otherwise.
+ */
+#if !defined TCP_CALCULATE_EFF_SEND_MSS || defined __DOXYGEN__
+#define TCP_CALCULATE_EFF_SEND_MSS      1
+#endif
+
+/**
+ * LWIP_TCP_RTO_TIME: Initial TCP retransmission timeout (ms).
+ * This defaults to 3 seconds as traditionally defined in the TCP protocol.
+ * For improving timely recovery on faster networks, this value could
+ * be lowered down to 1 second (RFC 6298)
+ */
+#if !defined LWIP_TCP_RTO_TIME || defined __DOXYGEN__
+#define LWIP_TCP_RTO_TIME               3000
+#endif
+
+/**
+ * TCP_SND_BUF: TCP sender buffer space (bytes).
+ * To achieve good performance, this should be at least 2 * TCP_MSS.
+ */
+#if !defined TCP_SND_BUF || defined __DOXYGEN__
+#define TCP_SND_BUF                     (2 * TCP_MSS)
+#endif
+
+/**
+ * TCP_SND_QUEUELEN: TCP sender buffer space (pbufs). This must be at least
+ * as much as (2 * TCP_SND_BUF/TCP_MSS) for things to work.
+ */
+#if !defined TCP_SND_QUEUELEN || defined __DOXYGEN__
+#define TCP_SND_QUEUELEN                ((4 * (TCP_SND_BUF) + (TCP_MSS - 1))/(TCP_MSS))
+#endif
+
+/**
+ * TCP_SNDLOWAT: TCP writable space (bytes). This must be less than
+ * TCP_SND_BUF. It is the amount of space which must be available in the
+ * TCP snd_buf for select to return writable (combined with TCP_SNDQUEUELOWAT).
+ */
+#if !defined TCP_SNDLOWAT || defined __DOXYGEN__
+#define TCP_SNDLOWAT                    LWIP_MIN(LWIP_MAX(((TCP_SND_BUF)/2), (2 * TCP_MSS) + 1), (TCP_SND_BUF) - 1)
+#endif
+
+/**
+ * TCP_SNDQUEUELOWAT: TCP writable bufs (pbuf count). This must be less
+ * than TCP_SND_QUEUELEN. If the number of pbufs queued on a pcb drops below
+ * this number, select returns writable (combined with TCP_SNDLOWAT).
+ */
+#if !defined TCP_SNDQUEUELOWAT || defined __DOXYGEN__
+#define TCP_SNDQUEUELOWAT               LWIP_MAX(((TCP_SND_QUEUELEN)/2), 5)
+#endif
+
+/**
+ * TCP_OOSEQ_MAX_BYTES: The default maximum number of bytes queued on ooseq per
+ * pcb if TCP_OOSEQ_BYTES_LIMIT is not defined. Default is 0 (no limit).
+ * Only valid for TCP_QUEUE_OOSEQ==1.
+ */
+#if !defined TCP_OOSEQ_MAX_BYTES || defined __DOXYGEN__
+#define TCP_OOSEQ_MAX_BYTES             0
+#endif
+
+/**
+ * TCP_OOSEQ_BYTES_LIMIT(pcb): Return the maximum number of bytes to be queued
+ * on ooseq per pcb, given the pcb. Only valid for TCP_QUEUE_OOSEQ==1 &&
+ * TCP_OOSEQ_MAX_BYTES==1.
+ * Use this to override TCP_OOSEQ_MAX_BYTES to a dynamic value per pcb.
+ */
+#if !defined TCP_OOSEQ_BYTES_LIMIT
+#if TCP_OOSEQ_MAX_BYTES
+#define TCP_OOSEQ_BYTES_LIMIT(pcb)      TCP_OOSEQ_MAX_BYTES
+#elif defined __DOXYGEN__
+#define TCP_OOSEQ_BYTES_LIMIT(pcb)
+#endif
+#endif
+
+/**
+ * TCP_OOSEQ_MAX_PBUFS: The default maximum number of pbufs queued on ooseq per
+ * pcb if TCP_OOSEQ_BYTES_LIMIT is not defined. Default is 0 (no limit).
+ * Only valid for TCP_QUEUE_OOSEQ==1.
+ */
+#if !defined TCP_OOSEQ_MAX_PBUFS || defined __DOXYGEN__
+#define TCP_OOSEQ_MAX_PBUFS             0
+#endif
+
+/**
+ * TCP_OOSEQ_PBUFS_LIMIT(pcb): Return the maximum number of pbufs to be queued
+ * on ooseq per pcb, given the pcb.  Only valid for TCP_QUEUE_OOSEQ==1 &&
+ * TCP_OOSEQ_MAX_PBUFS==1.
+ * Use this to override TCP_OOSEQ_MAX_PBUFS to a dynamic value per pcb.
+ */
+#if !defined TCP_OOSEQ_PBUFS_LIMIT
+#if TCP_OOSEQ_MAX_PBUFS
+#define TCP_OOSEQ_PBUFS_LIMIT(pcb)      TCP_OOSEQ_MAX_PBUFS
+#elif defined __DOXYGEN__
+#define TCP_OOSEQ_PBUFS_LIMIT(pcb)
+#endif
+#endif
+
+/**
+ * TCP_LISTEN_BACKLOG: Enable the backlog option for tcp listen pcb.
+ */
+#if !defined TCP_LISTEN_BACKLOG || defined __DOXYGEN__
+#define TCP_LISTEN_BACKLOG              0
+#endif
+
+/**
+ * The maximum allowed backlog for TCP listen netconns.
+ * This backlog is used unless another is explicitly specified.
+ * 0xff is the maximum (u8_t).
+ */
+#if !defined TCP_DEFAULT_LISTEN_BACKLOG || defined __DOXYGEN__
+#define TCP_DEFAULT_LISTEN_BACKLOG      0xff
+#endif
+
+/**
+ * TCP_OVERSIZE: The maximum number of bytes that tcp_write may
+ * allocate ahead of time in an attempt to create shorter pbuf chains
+ * for transmission. The meaningful range is 0 to TCP_MSS. Some
+ * suggested values are:
+ *
+ * 0:         Disable oversized allocation. Each tcp_write() allocates a new
+              pbuf (old behaviour).
+ * 1:         Allocate size-aligned pbufs with minimal excess. Use this if your
+ *            scatter-gather DMA requires aligned fragments.
+ * 128:       Limit the pbuf/memory overhead to 20%.
+ * TCP_MSS:   Try to create unfragmented TCP packets.
+ * TCP_MSS/4: Try to create 4 fragments or less per TCP packet.
+ */
+#if !defined TCP_OVERSIZE || defined __DOXYGEN__
+#define TCP_OVERSIZE                    TCP_MSS
+#endif
+
+/**
+ * LWIP_TCP_TIMESTAMPS==1: support the TCP timestamp option.
+ * The timestamp option is currently only used to help remote hosts, it is not
+ * really used locally. Therefore, it is only enabled when a TS option is
+ * received in the initial SYN packet from a remote host.
+ */
+#if !defined LWIP_TCP_TIMESTAMPS || defined __DOXYGEN__
+#define LWIP_TCP_TIMESTAMPS             0
+#endif
+
+/**
+ * TCP_WND_UPDATE_THRESHOLD: difference in window to trigger an
+ * explicit window update
+ */
+#if !defined TCP_WND_UPDATE_THRESHOLD || defined __DOXYGEN__
+#define TCP_WND_UPDATE_THRESHOLD        LWIP_MIN((TCP_WND / 4), (TCP_MSS * 4))
+#endif
+
+/**
+ * LWIP_EVENT_API and LWIP_CALLBACK_API: Only one of these should be set to 1.
+ *     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.
+ */
+#if !defined(LWIP_EVENT_API) && !defined(LWIP_CALLBACK_API) || defined __DOXYGEN__
+#define LWIP_EVENT_API                  0
+#define LWIP_CALLBACK_API               1
+#else
+#ifndef LWIP_EVENT_API
+#define LWIP_EVENT_API                  0
+#endif
+#ifndef LWIP_CALLBACK_API
+#define LWIP_CALLBACK_API               0
+#endif
+#endif
+
+/**
+ * LWIP_WND_SCALE and TCP_RCV_SCALE:
+ * Set LWIP_WND_SCALE to 1 to enable window scaling.
+ * Set TCP_RCV_SCALE to the desired scaling factor (shift count in the
+ * range of [0..14]).
+ * When LWIP_WND_SCALE is enabled but TCP_RCV_SCALE is 0, we can use a large
+ * send window while having a small receive window only.
+ */
+#if !defined LWIP_WND_SCALE || defined __DOXYGEN__
+#define LWIP_WND_SCALE                  0
+#define TCP_RCV_SCALE                   0
+#endif
+
+/**
+ * LWIP_TCP_PCB_NUM_EXT_ARGS:
+ * When this is > 0, every tcp pcb (including listen pcb) includes a number of
+ * additional argument entries in an array (see tcp_ext_arg_alloc_id)
+ */
+#if !defined LWIP_TCP_PCB_NUM_EXT_ARGS || defined __DOXYGEN__
+#define LWIP_TCP_PCB_NUM_EXT_ARGS       0
+#endif
+
+/** LWIP_ALTCP==1: enable the altcp API.
+ * altcp is an abstraction layer that prevents applications linking against the
+ * tcp.h functions but provides the same functionality. It is used to e.g. add
+ * SSL/TLS or proxy-connect support to an application written for the tcp callback
+ * API without that application knowing the protocol details.
+ *
+ * With LWIP_ALTCP==0, applications written against the altcp API can still be
+ * compiled but are directly linked against the tcp.h callback API and then
+ * cannot use layered protocols.
+ *
+ * See @ref altcp_api
+ */
+#if !defined LWIP_ALTCP || defined __DOXYGEN__
+#define LWIP_ALTCP                      0
+#endif
+
+/** LWIP_ALTCP_TLS==1: enable TLS support for altcp API.
+ * This needs a port of the functions in altcp_tls.h to a TLS library.
+ * A port to ARM mbedtls is provided with lwIP, see apps/altcp_tls/ directory
+ * and LWIP_ALTCP_TLS_MBEDTLS option.
+ */
+#if !defined LWIP_ALTCP_TLS || defined __DOXYGEN__
+#define LWIP_ALTCP_TLS                  0
+#endif
+
+/**
+ * @}
+ */
+
+/*
+   ----------------------------------
+   ---------- Pbuf options ----------
+   ----------------------------------
+*/
+/**
+ * @defgroup lwip_opts_pbuf PBUF
+ * @ingroup lwip_opts
+ * @{
+ */
+/**
+ * PBUF_LINK_HLEN: the number of bytes that should be allocated for a
+ * link level header. The default is 14, the standard value for
+ * Ethernet.
+ */
+ #if !defined PBUF_LINK_HLEN || defined __DOXYGEN__
+#if (defined LWIP_HOOK_VLAN_SET || LWIP_VLAN_PCP) && !defined __DOXYGEN__
+ #define PBUF_LINK_HLEN                  (18 + ETH_PAD_SIZE)
+#else /* LWIP_HOOK_VLAN_SET || LWIP_VLAN_PCP */
+ #define PBUF_LINK_HLEN                  (14 + ETH_PAD_SIZE)
+#endif /* LWIP_HOOK_VLAN_SET || LWIP_VLAN_PCP */
+ #endif
+
+/**
+ * PBUF_LINK_ENCAPSULATION_HLEN: the number of bytes that should be allocated
+ * for an additional encapsulation header before ethernet headers (e.g. 802.11)
+ */
+#if !defined PBUF_LINK_ENCAPSULATION_HLEN || defined __DOXYGEN__
+#define PBUF_LINK_ENCAPSULATION_HLEN    0
+#endif
+
+/**
+ * PBUF_POOL_BUFSIZE: the size of each pbuf in the pbuf pool. The default is
+ * designed to accommodate single full size TCP frame in one pbuf, including
+ * TCP_MSS, IP header, and link header.
+ */
+#if !defined PBUF_POOL_BUFSIZE || defined __DOXYGEN__
+#define PBUF_POOL_BUFSIZE               LWIP_MEM_ALIGN_SIZE(TCP_MSS+PBUF_IP_HLEN+PBUF_TRANSPORT_HLEN+PBUF_LINK_ENCAPSULATION_HLEN+PBUF_LINK_HLEN)
+#endif
+
+/**
+ * LWIP_PBUF_REF_T: Refcount type in pbuf.
+ * Default width of u8_t can be increased if 255 refs are not enough for you.
+ */
+#if !defined LWIP_PBUF_REF_T || defined __DOXYGEN__
+#define LWIP_PBUF_REF_T                 u8_t
+#endif
+
+/**
+ * LWIP_PBUF_CUSTOM_DATA: Store private data on pbufs (e.g. timestamps)
+ * This extends struct pbuf so user can store custom data on every pbuf.
+ */
+#if !defined LWIP_PBUF_CUSTOM_DATA || defined __DOXYGEN__
+#define LWIP_PBUF_CUSTOM_DATA
+#endif
+/**
+ * @}
+ */
+
+/*
+   ------------------------------------------------
+   ---------- Network Interfaces options ----------
+   ------------------------------------------------
+*/
+/**
+ * @defgroup lwip_opts_netif NETIF
+ * @ingroup lwip_opts
+ * @{
+ */
+/**
+ * LWIP_SINGLE_NETIF==1: use a single netif only. This is the common case for
+ * small real-life targets. Some code like routing etc. can be left out.
+ */
+#if !defined LWIP_SINGLE_NETIF || defined __DOXYGEN__
+#define LWIP_SINGLE_NETIF               0
+#endif
+
+/**
+ * LWIP_NETIF_HOSTNAME==1: use DHCP_OPTION_HOSTNAME with netif's hostname
+ * field.
+ */
+#if !defined LWIP_NETIF_HOSTNAME || defined __DOXYGEN__
+#define LWIP_NETIF_HOSTNAME             0
+#endif
+
+/**
+ * LWIP_NETIF_API==1: Support netif api (in netifapi.c)
+ */
+#if !defined LWIP_NETIF_API || defined __DOXYGEN__
+#define LWIP_NETIF_API                  0
+#endif
+
+/**
+ * LWIP_NETIF_STATUS_CALLBACK==1: Support a callback function whenever an interface
+ * changes its up/down status (i.e., due to DHCP IP acquisition)
+ */
+#if !defined LWIP_NETIF_STATUS_CALLBACK || defined __DOXYGEN__
+#define LWIP_NETIF_STATUS_CALLBACK      0
+#endif
+
+/**
+ * LWIP_NETIF_EXT_STATUS_CALLBACK==1: Support an extended callback function
+ * for several netif related event that supports multiple subscribers.
+ * @see netif_ext_status_callback
+ */
+#if !defined LWIP_NETIF_EXT_STATUS_CALLBACK || defined __DOXYGEN__
+#define LWIP_NETIF_EXT_STATUS_CALLBACK  0
+#endif
+
+/**
+ * LWIP_NETIF_LINK_CALLBACK==1: Support a callback function from an interface
+ * whenever the link changes (i.e., link down)
+ */
+#if !defined LWIP_NETIF_LINK_CALLBACK || defined __DOXYGEN__
+#define LWIP_NETIF_LINK_CALLBACK        0
+#endif
+
+/**
+ * LWIP_NETIF_REMOVE_CALLBACK==1: Support a callback function that is called
+ * when a netif has been removed
+ */
+#if !defined LWIP_NETIF_REMOVE_CALLBACK || defined __DOXYGEN__
+#define LWIP_NETIF_REMOVE_CALLBACK      0
+#endif
+
+/**
+ * LWIP_NETIF_HWADDRHINT==1: Cache link-layer-address hints (e.g. table
+ * indices) in struct netif. TCP and UDP can make use of this to prevent
+ * scanning the ARP table for every sent packet. While this is faster for big
+ * ARP tables or many concurrent connections, it might be counterproductive
+ * if you have a tiny ARP table or if there never are concurrent connections.
+ */
+#if !defined LWIP_NETIF_HWADDRHINT || defined __DOXYGEN__
+#define LWIP_NETIF_HWADDRHINT           0
+#endif
+
+/**
+ * LWIP_NETIF_TX_SINGLE_PBUF: if this is set to 1, lwIP *tries* to put all data
+ * to be sent into one single pbuf. This is for compatibility with DMA-enabled
+ * 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!
+ *
+ * ATTENTION: a driver should *NOT* rely on getting single pbufs but check TX
+ * pbufs for being in one piece. If not, @ref pbuf_clone can be used to get
+ * a single pbuf:
+ *   if (p->next != NULL) {
+ *     struct pbuf *q = pbuf_clone(PBUF_RAW, PBUF_RAM, p);
+ *     if (q == NULL) {
+ *       return ERR_MEM;
+ *     }
+ *     p = q; ATTENTION: do NOT free the old 'p' as the ref belongs to the caller!
+ *   }
+ */
+#if !defined LWIP_NETIF_TX_SINGLE_PBUF || defined __DOXYGEN__
+#define LWIP_NETIF_TX_SINGLE_PBUF       0
+#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
+
+/**
+ * LWIP_NUM_NETIF_CLIENT_DATA: Number of clients that may store
+ * data in client_data member array of struct netif (max. 256).
+ */
+#if !defined LWIP_NUM_NETIF_CLIENT_DATA || defined __DOXYGEN__
+#define LWIP_NUM_NETIF_CLIENT_DATA      0
+#endif
+/**
+ * @}
+ */
+
+/*
+   ------------------------------------
+   ---------- LOOPIF options ----------
+   ------------------------------------
+*/
+/**
+ * @defgroup lwip_opts_loop Loopback interface
+ * @ingroup lwip_opts_netif
+ * @{
+ */
+/**
+ * LWIP_HAVE_LOOPIF==1: Support loop interface (127.0.0.1).
+ * This is only needed when no real netifs are available. If at least one other
+ * netif is available, loopback traffic uses this netif.
+ */
+#if !defined LWIP_HAVE_LOOPIF || defined __DOXYGEN__
+#define LWIP_HAVE_LOOPIF                (LWIP_NETIF_LOOPBACK && !LWIP_SINGLE_NETIF)
+#endif
+
+/**
+ * LWIP_LOOPIF_MULTICAST==1: Support multicast/IGMP on loop interface (127.0.0.1).
+ */
+#if !defined LWIP_LOOPIF_MULTICAST || defined __DOXYGEN__
+#define LWIP_LOOPIF_MULTICAST           0
+#endif
+
+/**
+ * LWIP_NETIF_LOOPBACK==1: Support sending packets with a destination IP
+ * address equal to the netif IP address, looping them back up the stack.
+ */
+#if !defined LWIP_NETIF_LOOPBACK || defined __DOXYGEN__
+#define LWIP_NETIF_LOOPBACK             0
+#endif
+
+/**
+ * LWIP_LOOPBACK_MAX_PBUFS: Maximum number of pbufs on queue for loopback
+ * sending for each netif (0 = disabled)
+ */
+#if !defined LWIP_LOOPBACK_MAX_PBUFS || defined __DOXYGEN__
+#define LWIP_LOOPBACK_MAX_PBUFS         0
+#endif
+
+/**
+ * LWIP_NETIF_LOOPBACK_MULTITHREADING: Indicates whether threading is enabled in
+ * the system, as netifs must change how they behave depending on this setting
+ * for the LWIP_NETIF_LOOPBACK option to work.
+ * Setting this is needed to avoid reentering non-reentrant functions like
+ * tcp_input().
+ *    LWIP_NETIF_LOOPBACK_MULTITHREADING==1: Indicates that the user is using a
+ *       multithreaded environment like tcpip.c. In this case, netif->input()
+ *       is called directly.
+ *    LWIP_NETIF_LOOPBACK_MULTITHREADING==0: Indicates a polling (or NO_SYS) setup.
+ *       The packets are put on a list and netif_poll() must be called in
+ *       the main application loop.
+ */
+#if !defined LWIP_NETIF_LOOPBACK_MULTITHREADING || defined __DOXYGEN__
+#define LWIP_NETIF_LOOPBACK_MULTITHREADING    (!NO_SYS)
+#endif
+/**
+ * @}
+ */
+
+/*
+   ------------------------------------
+   ---------- Thread options ----------
+   ------------------------------------
+*/
+/**
+ * @defgroup lwip_opts_thread Threading
+ * @ingroup lwip_opts_infrastructure
+ * @{
+ */
+/**
+ * TCPIP_THREAD_NAME: The name assigned to the main tcpip thread.
+ */
+#if !defined TCPIP_THREAD_NAME || defined __DOXYGEN__
+#define TCPIP_THREAD_NAME               "tcpip_thread"
+#endif
+
+/**
+ * TCPIP_THREAD_STACKSIZE: The stack size used by the main tcpip thread.
+ * The stack size value itself is platform-dependent, but is passed to
+ * sys_thread_new() when the thread is created.
+ */
+#if !defined TCPIP_THREAD_STACKSIZE || defined __DOXYGEN__
+#define TCPIP_THREAD_STACKSIZE          0
+#endif
+
+/**
+ * TCPIP_THREAD_PRIO: The priority assigned to the main tcpip thread.
+ * The priority value itself is platform-dependent, but is passed to
+ * sys_thread_new() when the thread is created.
+ */
+#if !defined TCPIP_THREAD_PRIO || defined __DOXYGEN__
+#define TCPIP_THREAD_PRIO               1
+#endif
+
+/**
+ * TCPIP_MBOX_SIZE: The mailbox size for the tcpip thread messages
+ * The queue size value itself is platform-dependent, but is passed to
+ * sys_mbox_new() when tcpip_init is called.
+ */
+#if !defined TCPIP_MBOX_SIZE || defined __DOXYGEN__
+#define TCPIP_MBOX_SIZE                 0
+#endif
+
+/**
+ * Define this to something that triggers a watchdog. This is called from
+ * tcpip_thread after processing a message.
+ */
+#if !defined LWIP_TCPIP_THREAD_ALIVE || defined __DOXYGEN__
+#define LWIP_TCPIP_THREAD_ALIVE()
+#endif
+
+/**
+ * SLIPIF_THREAD_NAME: The name assigned to the slipif_loop thread.
+ */
+#if !defined SLIPIF_THREAD_NAME || defined __DOXYGEN__
+#define SLIPIF_THREAD_NAME              "slipif_loop"
+#endif
+
+/**
+ * SLIP_THREAD_STACKSIZE: The stack size used by the slipif_loop thread.
+ * The stack size value itself is platform-dependent, but is passed to
+ * sys_thread_new() when the thread is created.
+ */
+#if !defined SLIPIF_THREAD_STACKSIZE || defined __DOXYGEN__
+#define SLIPIF_THREAD_STACKSIZE         0
+#endif
+
+/**
+ * SLIPIF_THREAD_PRIO: The priority assigned to the slipif_loop thread.
+ * The priority value itself is platform-dependent, but is passed to
+ * sys_thread_new() when the thread is created.
+ */
+#if !defined SLIPIF_THREAD_PRIO || defined __DOXYGEN__
+#define SLIPIF_THREAD_PRIO              1
+#endif
+
+/**
+ * DEFAULT_THREAD_NAME: The name assigned to any other lwIP thread.
+ */
+#if !defined DEFAULT_THREAD_NAME || defined __DOXYGEN__
+#define DEFAULT_THREAD_NAME             "lwIP"
+#endif
+
+/**
+ * DEFAULT_THREAD_STACKSIZE: The stack size used by any other lwIP thread.
+ * The stack size value itself is platform-dependent, but is passed to
+ * sys_thread_new() when the thread is created.
+ */
+#if !defined DEFAULT_THREAD_STACKSIZE || defined __DOXYGEN__
+#define DEFAULT_THREAD_STACKSIZE        0
+#endif
+
+/**
+ * DEFAULT_THREAD_PRIO: The priority assigned to any other lwIP thread.
+ * The priority value itself is platform-dependent, but is passed to
+ * sys_thread_new() when the thread is created.
+ */
+#if !defined DEFAULT_THREAD_PRIO || defined __DOXYGEN__
+#define DEFAULT_THREAD_PRIO             1
+#endif
+
+/**
+ * DEFAULT_RAW_RECVMBOX_SIZE: The mailbox size for the incoming packets on a
+ * NETCONN_RAW. The queue size value itself is platform-dependent, but is passed
+ * to sys_mbox_new() when the recvmbox is created.
+ */
+#if !defined DEFAULT_RAW_RECVMBOX_SIZE || defined __DOXYGEN__
+#define DEFAULT_RAW_RECVMBOX_SIZE       0
+#endif
+
+/**
+ * DEFAULT_UDP_RECVMBOX_SIZE: The mailbox size for the incoming packets on a
+ * NETCONN_UDP. The queue size value itself is platform-dependent, but is passed
+ * to sys_mbox_new() when the recvmbox is created.
+ */
+#if !defined DEFAULT_UDP_RECVMBOX_SIZE || defined __DOXYGEN__
+#define DEFAULT_UDP_RECVMBOX_SIZE       0
+#endif
+
+/**
+ * DEFAULT_TCP_RECVMBOX_SIZE: The mailbox size for the incoming packets on a
+ * NETCONN_TCP. The queue size value itself is platform-dependent, but is passed
+ * to sys_mbox_new() when the recvmbox is created.
+ */
+#if !defined DEFAULT_TCP_RECVMBOX_SIZE || defined __DOXYGEN__
+#define DEFAULT_TCP_RECVMBOX_SIZE       0
+#endif
+
+/**
+ * DEFAULT_ACCEPTMBOX_SIZE: The mailbox size for the incoming connections.
+ * The queue size value itself is platform-dependent, but is passed to
+ * sys_mbox_new() when the acceptmbox is created.
+ */
+#if !defined DEFAULT_ACCEPTMBOX_SIZE || defined __DOXYGEN__
+#define DEFAULT_ACCEPTMBOX_SIZE         0
+#endif
+/**
+ * @}
+ */
+
+/*
+   ----------------------------------------------
+   ---------- Sequential layer options ----------
+   ----------------------------------------------
+*/
+/**
+ * @defgroup lwip_opts_netconn Netconn
+ * @ingroup lwip_opts_threadsafe_apis
+ * @{
+ */
+/**
+ * LWIP_NETCONN==1: Enable Netconn API (require to use api_lib.c)
+ */
+#if !defined LWIP_NETCONN || defined __DOXYGEN__
+#define LWIP_NETCONN                    1
+#endif
+
+/** LWIP_TCPIP_TIMEOUT==1: Enable tcpip_timeout/tcpip_untimeout to create
+ * timers running in tcpip_thread from another thread.
+ */
+#if !defined LWIP_TCPIP_TIMEOUT || defined __DOXYGEN__
+#define LWIP_TCPIP_TIMEOUT              0
+#endif
+
+/** LWIP_NETCONN_SEM_PER_THREAD==1: Use one (thread-local) semaphore per
+ * thread calling socket/netconn functions instead of allocating one
+ * semaphore per netconn (and per select etc.)
+ * ATTENTION: a thread-local semaphore for API calls is needed:
+ * - LWIP_NETCONN_THREAD_SEM_GET() returning a sys_sem_t*
+ * - LWIP_NETCONN_THREAD_SEM_ALLOC() creating the semaphore
+ * - LWIP_NETCONN_THREAD_SEM_FREE() freeing the semaphore
+ * The latter 2 can be invoked up by calling netconn_thread_init()/netconn_thread_cleanup().
+ * Ports may call these for threads created with sys_thread_new().
+ */
+#if !defined LWIP_NETCONN_SEM_PER_THREAD || defined __DOXYGEN__
+#define LWIP_NETCONN_SEM_PER_THREAD     0
+#endif
+
+/** LWIP_NETCONN_FULLDUPLEX==1: Enable code that allows reading from one thread,
+ * writing from a 2nd thread and closing from a 3rd thread at the same time.
+ * LWIP_NETCONN_SEM_PER_THREAD==1 is required to use one socket/netconn from
+ * multiple threads at once!
+ */
+#if !defined LWIP_NETCONN_FULLDUPLEX || defined __DOXYGEN__
+#define LWIP_NETCONN_FULLDUPLEX         0
+#endif
+/**
+ * @}
+ */
+
+/*
+   ------------------------------------
+   ---------- Socket options ----------
+   ------------------------------------
+*/
+/**
+ * @defgroup lwip_opts_socket Sockets
+ * @ingroup lwip_opts_threadsafe_apis
+ * @{
+ */
+/**
+ * LWIP_SOCKET==1: Enable Socket API (require to use sockets.c)
+ */
+#if !defined LWIP_SOCKET || defined __DOXYGEN__
+#define LWIP_SOCKET                     1
+#endif
+
+/**
+ * LWIP_COMPAT_SOCKETS==1: Enable BSD-style sockets functions names through defines.
+ * LWIP_COMPAT_SOCKETS==2: Same as ==1 but correctly named functions are created.
+ * While this helps code completion, it might conflict with existing libraries.
+ * (only used if you use sockets.c)
+ */
+#if !defined LWIP_COMPAT_SOCKETS || defined __DOXYGEN__
+#define LWIP_COMPAT_SOCKETS             1
+#endif
+
+/**
+ * LWIP_POSIX_SOCKETS_IO_NAMES==1: Enable POSIX-style sockets functions names.
+ * Disable this option if you use a POSIX operating system that uses the same
+ * names (read, write & close). (only used if you use sockets.c)
+ */
+#if !defined LWIP_POSIX_SOCKETS_IO_NAMES || defined __DOXYGEN__
+#define LWIP_POSIX_SOCKETS_IO_NAMES     1
+#endif
+
+/**
+ * LWIP_SOCKET_OFFSET==n: Increases the file descriptor number created by LwIP with n.
+ * This can be useful when there are multiple APIs which create file descriptors.
+ * When they all start with a different offset and you won't make them overlap you can
+ * re implement read/write/close/ioctl/fnctl to send the requested action to the right
+ * library (sharing select will need more work though).
+ */
+#if !defined LWIP_SOCKET_OFFSET || defined __DOXYGEN__
+#define LWIP_SOCKET_OFFSET              0
+#endif
+
+/**
+ * LWIP_SOCKET_EXTERNAL_HEADERS==1: Use external headers instead of sockets.h
+ * and inet.h. In this case, user must provide its own headers by setting the
+ * values for LWIP_SOCKET_EXTERNAL_HEADER_SOCKETS_H and
+ * LWIP_SOCKET_EXTERNAL_HEADER_INET_H to appropriate include file names and the
+ * whole content of the default sockets.h and inet.h is skipped.
+ */
+#if !defined LWIP_SOCKET_EXTERNAL_HEADERS || defined __DOXYGEN__
+#define LWIP_SOCKET_EXTERNAL_HEADERS    0
+#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)
+ */
+#if !defined LWIP_TCP_KEEPALIVE || defined __DOXYGEN__
+#define LWIP_TCP_KEEPALIVE              0
+#endif
+
+/**
+ * LWIP_SO_SNDTIMEO==1: Enable send timeout for sockets/netconns and
+ * SO_SNDTIMEO processing.
+ */
+#if !defined LWIP_SO_SNDTIMEO || defined __DOXYGEN__
+#define LWIP_SO_SNDTIMEO                0
+#endif
+
+/**
+ * LWIP_SO_RCVTIMEO==1: Enable receive timeout for sockets/netconns and
+ * SO_RCVTIMEO processing.
+ */
+#if !defined LWIP_SO_RCVTIMEO || defined __DOXYGEN__
+#define LWIP_SO_RCVTIMEO                0
+#endif
+
+/**
+ * LWIP_SO_SNDRCVTIMEO_NONSTANDARD==1: SO_RCVTIMEO/SO_SNDTIMEO take an int
+ * (milliseconds, much like winsock does) instead of a struct timeval (default).
+ */
+#if !defined LWIP_SO_SNDRCVTIMEO_NONSTANDARD || defined __DOXYGEN__
+#define LWIP_SO_SNDRCVTIMEO_NONSTANDARD 0
+#endif
+
+/**
+ * LWIP_SO_RCVBUF==1: Enable SO_RCVBUF processing.
+ */
+#if !defined LWIP_SO_RCVBUF || defined __DOXYGEN__
+#define LWIP_SO_RCVBUF                  0
+#endif
+
+/**
+ * LWIP_SO_LINGER==1: Enable SO_LINGER processing.
+ */
+#if !defined LWIP_SO_LINGER || defined __DOXYGEN__
+#define LWIP_SO_LINGER                  0
+#endif
+
+/**
+ * If LWIP_SO_RCVBUF is used, this is the default value for recv_bufsize.
+ */
+#if !defined RECV_BUFSIZE_DEFAULT || defined __DOXYGEN__
+#define RECV_BUFSIZE_DEFAULT            INT_MAX
+#endif
+
+/**
+ * By default, TCP socket/netconn close waits 20 seconds max to send the FIN
+ */
+#if !defined LWIP_TCP_CLOSE_TIMEOUT_MS_DEFAULT || defined __DOXYGEN__
+#define LWIP_TCP_CLOSE_TIMEOUT_MS_DEFAULT 20000
+#endif
+
+/**
+ * SO_REUSE==1: Enable SO_REUSEADDR option.
+ */
+#if !defined SO_REUSE || defined __DOXYGEN__
+#define SO_REUSE                        0
+#endif
+
+/**
+ * SO_REUSE_RXTOALL==1: Pass a copy of incoming broadcast/multicast packets
+ * to all local matches if SO_REUSEADDR is turned on.
+ * WARNING: Adds a memcpy for every packet if passing to more than one pcb!
+ */
+#if !defined SO_REUSE_RXTOALL || defined __DOXYGEN__
+#define SO_REUSE_RXTOALL                0
+#endif
+
+/**
+ * LWIP_FIONREAD_LINUXMODE==0 (default): ioctl/FIONREAD returns the amount of
+ * pending data in the network buffer. This is the way windows does it. It's
+ * the default for lwIP since it is smaller.
+ * LWIP_FIONREAD_LINUXMODE==1: ioctl/FIONREAD returns the size of the next
+ * pending datagram in bytes. This is the way linux does it. This code is only
+ * here for compatibility.
+ */
+#if !defined LWIP_FIONREAD_LINUXMODE || defined __DOXYGEN__
+#define LWIP_FIONREAD_LINUXMODE         0
+#endif
+
+/**
+ * LWIP_SOCKET_SELECT==1 (default): enable select() for sockets (uses a netconn
+ * callback to keep track of events).
+ * This saves RAM (counters per socket) and code (netconn event callback), which
+ * should improve performance a bit).
+ */
+#if !defined LWIP_SOCKET_SELECT || defined __DOXYGEN__
+#define LWIP_SOCKET_SELECT              1
+#endif
+
+/**
+ * LWIP_SOCKET_POLL==1 (default): enable poll() for sockets (including
+ * struct pollfd, nfds_t, and constants)
+ */
+#if !defined LWIP_SOCKET_POLL || defined __DOXYGEN__
+#define LWIP_SOCKET_POLL                1
+#endif
+/**
+ * @}
+ */
+
+/*
+   ----------------------------------------
+   ---------- Statistics options ----------
+   ----------------------------------------
+*/
+/**
+ * @defgroup lwip_opts_stats Statistics
+ * @ingroup lwip_opts_debug
+ * @{
+ */
+/**
+ * LWIP_STATS==1: Enable statistics collection in lwip_stats.
+ */
+#if !defined LWIP_STATS || defined __DOXYGEN__
+#define LWIP_STATS                      1
+#endif
+
+#if LWIP_STATS
+
+/**
+ * LWIP_STATS_DISPLAY==1: Compile in the statistics output functions.
+ */
+#if !defined LWIP_STATS_DISPLAY || defined __DOXYGEN__
+#define LWIP_STATS_DISPLAY              0
+#endif
+
+/**
+ * LINK_STATS==1: Enable link stats.
+ */
+#if !defined LINK_STATS || defined __DOXYGEN__
+#define LINK_STATS                      1
+#endif
+
+/**
+ * ETHARP_STATS==1: Enable etharp stats.
+ */
+#if !defined ETHARP_STATS || defined __DOXYGEN__
+#define ETHARP_STATS                    (LWIP_ARP)
+#endif
+
+/**
+ * IP_STATS==1: Enable IP stats.
+ */
+#if !defined IP_STATS || defined __DOXYGEN__
+#define IP_STATS                        1
+#endif
+
+/**
+ * IPFRAG_STATS==1: Enable IP fragmentation stats. Default is
+ * on if using either frag or reass.
+ */
+#if !defined IPFRAG_STATS || defined __DOXYGEN__
+#define IPFRAG_STATS                    (IP_REASSEMBLY || IP_FRAG)
+#endif
+
+/**
+ * ICMP_STATS==1: Enable ICMP stats.
+ */
+#if !defined ICMP_STATS || defined __DOXYGEN__
+#define ICMP_STATS                      1
+#endif
+
+/**
+ * IGMP_STATS==1: Enable IGMP stats.
+ */
+#if !defined IGMP_STATS || defined __DOXYGEN__
+#define IGMP_STATS                      (LWIP_IGMP)
+#endif
+
+/**
+ * UDP_STATS==1: Enable UDP stats. Default is on if
+ * UDP enabled, otherwise off.
+ */
+#if !defined UDP_STATS || defined __DOXYGEN__
+#define UDP_STATS                       (LWIP_UDP)
+#endif
+
+/**
+ * TCP_STATS==1: Enable TCP stats. Default is on if TCP
+ * enabled, otherwise off.
+ */
+#if !defined TCP_STATS || defined __DOXYGEN__
+#define TCP_STATS                       (LWIP_TCP)
+#endif
+
+/**
+ * MEM_STATS==1: Enable mem.c stats.
+ */
+#if !defined MEM_STATS || defined __DOXYGEN__
+#define MEM_STATS                       ((MEM_LIBC_MALLOC == 0) && (MEM_USE_POOLS == 0))
+#endif
+
+/**
+ * MEMP_STATS==1: Enable memp.c pool stats.
+ */
+#if !defined MEMP_STATS || defined __DOXYGEN__
+#define MEMP_STATS                      (MEMP_MEM_MALLOC == 0)
+#endif
+
+/**
+ * SYS_STATS==1: Enable system stats (sem and mbox counts, etc).
+ */
+#if !defined SYS_STATS || defined __DOXYGEN__
+#define SYS_STATS                       (NO_SYS == 0)
+#endif
+
+/**
+ * IP6_STATS==1: Enable IPv6 stats.
+ */
+#if !defined IP6_STATS || defined __DOXYGEN__
+#define IP6_STATS                       (LWIP_IPV6)
+#endif
+
+/**
+ * ICMP6_STATS==1: Enable ICMP for IPv6 stats.
+ */
+#if !defined ICMP6_STATS || defined __DOXYGEN__
+#define ICMP6_STATS                     (LWIP_IPV6 && LWIP_ICMP6)
+#endif
+
+/**
+ * IP6_FRAG_STATS==1: Enable IPv6 fragmentation stats.
+ */
+#if !defined IP6_FRAG_STATS || defined __DOXYGEN__
+#define IP6_FRAG_STATS                  (LWIP_IPV6 && (LWIP_IPV6_FRAG || LWIP_IPV6_REASS))
+#endif
+
+/**
+ * MLD6_STATS==1: Enable MLD for IPv6 stats.
+ */
+#if !defined MLD6_STATS || defined __DOXYGEN__
+#define MLD6_STATS                      (LWIP_IPV6 && LWIP_IPV6_MLD)
+#endif
+
+/**
+ * ND6_STATS==1: Enable Neighbor discovery for IPv6 stats.
+ */
+#if !defined ND6_STATS || defined __DOXYGEN__
+#define ND6_STATS                       (LWIP_IPV6)
+#endif
+
+/**
+ * MIB2_STATS==1: Stats for SNMP MIB2.
+ */
+#if !defined MIB2_STATS || defined __DOXYGEN__
+#define MIB2_STATS                      0
+#endif
+
+#else
+
+#define LINK_STATS                      0
+#define ETHARP_STATS                    0
+#define IP_STATS                        0
+#define IPFRAG_STATS                    0
+#define ICMP_STATS                      0
+#define IGMP_STATS                      0
+#define UDP_STATS                       0
+#define TCP_STATS                       0
+#define MEM_STATS                       0
+#define MEMP_STATS                      0
+#define SYS_STATS                       0
+#define LWIP_STATS_DISPLAY              0
+#define IP6_STATS                       0
+#define ICMP6_STATS                     0
+#define IP6_FRAG_STATS                  0
+#define MLD6_STATS                      0
+#define ND6_STATS                       0
+#define MIB2_STATS                      0
+
+#endif /* LWIP_STATS */
+/**
+ * @}
+ */
+
+/*
+   --------------------------------------
+   ---------- Checksum options ----------
+   --------------------------------------
+*/
+/**
+ * @defgroup lwip_opts_checksum Checksum
+ * @ingroup lwip_opts_infrastructure
+ * @{
+ */
+/**
+ * LWIP_CHECKSUM_CTRL_PER_NETIF==1: Checksum generation/check can be enabled/disabled
+ * per netif.
+ * ATTENTION: if enabled, the CHECKSUM_GEN_* and CHECKSUM_CHECK_* defines must be enabled!
+ */
+#if !defined LWIP_CHECKSUM_CTRL_PER_NETIF || defined __DOXYGEN__
+#define LWIP_CHECKSUM_CTRL_PER_NETIF    0
+#endif
+
+/**
+ * CHECKSUM_GEN_IP==1: Generate checksums in software for outgoing IP packets.
+ */
+#if !defined CHECKSUM_GEN_IP || defined __DOXYGEN__
+#define CHECKSUM_GEN_IP                 1
+#endif
+
+/**
+ * CHECKSUM_GEN_UDP==1: Generate checksums in software for outgoing UDP packets.
+ */
+#if !defined CHECKSUM_GEN_UDP || defined __DOXYGEN__
+#define CHECKSUM_GEN_UDP                1
+#endif
+
+/**
+ * CHECKSUM_GEN_TCP==1: Generate checksums in software for outgoing TCP packets.
+ */
+#if !defined CHECKSUM_GEN_TCP || defined __DOXYGEN__
+#define CHECKSUM_GEN_TCP                1
+#endif
+
+/**
+ * CHECKSUM_GEN_ICMP==1: Generate checksums in software for outgoing ICMP packets.
+ */
+#if !defined CHECKSUM_GEN_ICMP || defined __DOXYGEN__
+#define CHECKSUM_GEN_ICMP               1
+#endif
+
+/**
+ * CHECKSUM_GEN_ICMP6==1: Generate checksums in software for outgoing ICMP6 packets.
+ */
+#if !defined CHECKSUM_GEN_ICMP6 || defined __DOXYGEN__
+#define CHECKSUM_GEN_ICMP6              1
+#endif
+
+/**
+ * CHECKSUM_CHECK_IP==1: Check checksums in software for incoming IP packets.
+ */
+#if !defined CHECKSUM_CHECK_IP || defined __DOXYGEN__
+#define CHECKSUM_CHECK_IP               1
+#endif
+
+/**
+ * CHECKSUM_CHECK_UDP==1: Check checksums in software for incoming UDP packets.
+ */
+#if !defined CHECKSUM_CHECK_UDP || defined __DOXYGEN__
+#define CHECKSUM_CHECK_UDP              1
+#endif
+
+/**
+ * CHECKSUM_CHECK_TCP==1: Check checksums in software for incoming TCP packets.
+ */
+#if !defined CHECKSUM_CHECK_TCP || defined __DOXYGEN__
+#define CHECKSUM_CHECK_TCP              1
+#endif
+
+/**
+ * CHECKSUM_CHECK_ICMP==1: Check checksums in software for incoming ICMP packets.
+ */
+#if !defined CHECKSUM_CHECK_ICMP || defined __DOXYGEN__
+#define CHECKSUM_CHECK_ICMP             1
+#endif
+
+/**
+ * CHECKSUM_CHECK_ICMP6==1: Check checksums in software for incoming ICMPv6 packets
+ */
+#if !defined CHECKSUM_CHECK_ICMP6 || defined __DOXYGEN__
+#define CHECKSUM_CHECK_ICMP6            1
+#endif
+
+/**
+ * LWIP_CHECKSUM_ON_COPY==1: Calculate checksum when copying data from
+ * application buffers to pbufs.
+ */
+#if !defined LWIP_CHECKSUM_ON_COPY || defined __DOXYGEN__
+#define LWIP_CHECKSUM_ON_COPY           0
+#endif
+/**
+ * @}
+ */
+
+/*
+   ---------------------------------------
+   ---------- IPv6 options ---------------
+   ---------------------------------------
+*/
+/**
+ * @defgroup lwip_opts_ipv6 IPv6
+ * @ingroup lwip_opts
+ * @{
+ */
+/**
+ * LWIP_IPV6==1: Enable IPv6
+ */
+#if !defined LWIP_IPV6 || defined __DOXYGEN__
+#define LWIP_IPV6                       0
+#endif
+
+/**
+ * IPV6_REASS_MAXAGE: Maximum time (in multiples of IP6_REASS_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.
+ */
+#if !defined IPV6_REASS_MAXAGE || defined __DOXYGEN__
+#define IPV6_REASS_MAXAGE               60
+#endif
+
+/**
+ * LWIP_IPV6_SCOPES==1: Enable support for IPv6 address scopes, ensuring that
+ * e.g. link-local addresses are really treated as link-local. Disable this
+ * setting only for single-interface configurations.
+ * All addresses that have a scope according to the default policy (link-local
+ * unicast addresses, interface-local and link-local multicast addresses) should
+ * now have a zone set on them before being passed to the core API, although
+ * lwIP will currently attempt to select a zone on the caller's behalf when
+ * necessary. Applications that directly assign IPv6 addresses to interfaces
+ * (which is NOT recommended) must now ensure that link-local addresses carry
+ * the netif's zone. See the new ip6_zone.h header file for more information and
+ * relevant macros. For now it is still possible to turn off scopes support
+ * through the new LWIP_IPV6_SCOPES option. When upgrading an implementation that
+ * uses the core API directly, it is highly recommended to enable
+ * LWIP_IPV6_SCOPES_DEBUG at least for a while, to ensure e.g. proper address
+ * initialization.
+ */
+#if !defined LWIP_IPV6_SCOPES || defined __DOXYGEN__
+#define LWIP_IPV6_SCOPES                (LWIP_IPV6 && !LWIP_SINGLE_NETIF)
+#endif
+
+/**
+ * LWIP_IPV6_SCOPES_DEBUG==1: Perform run-time checks to verify that addresses
+ * are properly zoned (see ip6_zone.h on what that means) where it matters.
+ * Enabling this setting is highly recommended when upgrading from an existing
+ * installation that is not yet scope-aware; otherwise it may be too expensive.
+ */
+#if !defined LWIP_IPV6_SCOPES_DEBUG || defined __DOXYGEN__
+#define LWIP_IPV6_SCOPES_DEBUG          0
+#endif
+
+/**
+ * LWIP_IPV6_NUM_ADDRESSES: Number of IPv6 addresses per netif.
+ */
+#if !defined LWIP_IPV6_NUM_ADDRESSES || defined __DOXYGEN__
+#define LWIP_IPV6_NUM_ADDRESSES         3
+#endif
+
+/**
+ * LWIP_IPV6_FORWARD==1: Forward IPv6 packets across netifs
+ */
+#if !defined LWIP_IPV6_FORWARD || defined __DOXYGEN__
+#define LWIP_IPV6_FORWARD               0
+#endif
+
+/**
+ * LWIP_IPV6_FRAG==1: Fragment outgoing IPv6 packets that are too big.
+ */
+#if !defined LWIP_IPV6_FRAG || defined __DOXYGEN__
+#define LWIP_IPV6_FRAG                  1
+#endif
+
+/**
+ * LWIP_IPV6_REASS==1: reassemble incoming IPv6 packets that fragmented
+ */
+#if !defined LWIP_IPV6_REASS || defined __DOXYGEN__
+#define LWIP_IPV6_REASS                 LWIP_IPV6
+#endif
+
+/**
+ * LWIP_IPV6_SEND_ROUTER_SOLICIT==1: Send router solicitation messages during
+ * network startup.
+ */
+#if !defined LWIP_IPV6_SEND_ROUTER_SOLICIT || defined __DOXYGEN__
+#define LWIP_IPV6_SEND_ROUTER_SOLICIT   LWIP_IPV6
+#endif
+
+/**
+ * LWIP_IPV6_AUTOCONFIG==1: Enable stateless address autoconfiguration as per RFC 4862.
+ */
+#if !defined LWIP_IPV6_AUTOCONFIG || defined __DOXYGEN__
+#define LWIP_IPV6_AUTOCONFIG            LWIP_IPV6
+#endif
+
+/**
+ * LWIP_IPV6_ADDRESS_LIFETIMES==1: Keep valid and preferred lifetimes for each
+ * IPv6 address. Required for LWIP_IPV6_AUTOCONFIG. May still be enabled
+ * otherwise, in which case the application may assign address lifetimes with
+ * the appropriate macros. Addresses with no lifetime are assumed to be static.
+ * If this option is disabled, all addresses are assumed to be static.
+ */
+#if !defined LWIP_IPV6_ADDRESS_LIFETIMES || defined __DOXYGEN__
+#define LWIP_IPV6_ADDRESS_LIFETIMES     LWIP_IPV6_AUTOCONFIG
+#endif
+
+/**
+ * LWIP_IPV6_DUP_DETECT_ATTEMPTS=[0..7]: Number of duplicate address detection attempts.
+ */
+#if !defined LWIP_IPV6_DUP_DETECT_ATTEMPTS || defined __DOXYGEN__
+#define LWIP_IPV6_DUP_DETECT_ATTEMPTS   1
+#endif
+/**
+ * @}
+ */
+
+/**
+ * @defgroup lwip_opts_icmp6 ICMP6
+ * @ingroup lwip_opts_ipv6
+ * @{
+ */
+/**
+ * LWIP_ICMP6==1: Enable ICMPv6 (mandatory per RFC)
+ */
+#if !defined LWIP_ICMP6 || defined __DOXYGEN__
+#define LWIP_ICMP6                      LWIP_IPV6
+#endif
+
+/**
+ * LWIP_ICMP6_DATASIZE: bytes from original packet to send back in
+ * ICMPv6 error messages (0 = default of IP6_MIN_MTU_LENGTH)
+ * ATTENTION: RFC4443 section 2.4 says IP6_MIN_MTU_LENGTH is a MUST,
+ * so override this only if you absolutely have to!
+ */
+#if !defined LWIP_ICMP6_DATASIZE || defined __DOXYGEN__
+#define LWIP_ICMP6_DATASIZE             0
+#endif
+
+/**
+ * LWIP_ICMP6_HL: default hop limit for ICMPv6 messages
+ */
+#if !defined LWIP_ICMP6_HL || defined __DOXYGEN__
+#define LWIP_ICMP6_HL                   255
+#endif
+/**
+ * @}
+ */
+
+/**
+ * @defgroup lwip_opts_mld6 Multicast listener discovery
+ * @ingroup lwip_opts_ipv6
+ * @{
+ */
+/**
+ * LWIP_IPV6_MLD==1: Enable multicast listener discovery protocol.
+ * If LWIP_IPV6 is enabled but this setting is disabled, the MAC layer must
+ * indiscriminately pass all inbound IPv6 multicast traffic to lwIP.
+ */
+#if !defined LWIP_IPV6_MLD || defined __DOXYGEN__
+#define LWIP_IPV6_MLD                   LWIP_IPV6
+#endif
+
+/**
+ * MEMP_NUM_MLD6_GROUP: Max number of IPv6 multicast groups that can be joined.
+ * There must be enough groups so that each netif can join the solicited-node
+ * multicast group for each of its local addresses, plus one for MDNS if
+ * applicable, plus any number of groups to be joined on UDP sockets.
+ */
+#if !defined MEMP_NUM_MLD6_GROUP || defined __DOXYGEN__
+#define MEMP_NUM_MLD6_GROUP             4
+#endif
+/**
+ * @}
+ */
+
+/**
+ * @defgroup lwip_opts_nd6 Neighbor discovery
+ * @ingroup lwip_opts_ipv6
+ * @{
+ */
+/**
+ * LWIP_ND6_QUEUEING==1: queue outgoing IPv6 packets while MAC address
+ * is being resolved.
+ */
+#if !defined LWIP_ND6_QUEUEING || defined __DOXYGEN__
+#define LWIP_ND6_QUEUEING               LWIP_IPV6
+#endif
+
+/**
+ * MEMP_NUM_ND6_QUEUE: Max number of IPv6 packets to queue during MAC resolution.
+ */
+#if !defined MEMP_NUM_ND6_QUEUE || defined __DOXYGEN__
+#define MEMP_NUM_ND6_QUEUE              20
+#endif
+
+/**
+ * LWIP_ND6_NUM_NEIGHBORS: Number of entries in IPv6 neighbor cache
+ */
+#if !defined LWIP_ND6_NUM_NEIGHBORS || defined __DOXYGEN__
+#define LWIP_ND6_NUM_NEIGHBORS          10
+#endif
+
+/**
+ * LWIP_ND6_NUM_DESTINATIONS: number of entries in IPv6 destination cache
+ */
+#if !defined LWIP_ND6_NUM_DESTINATIONS || defined __DOXYGEN__
+#define LWIP_ND6_NUM_DESTINATIONS       10
+#endif
+
+/**
+ * LWIP_ND6_NUM_PREFIXES: number of entries in IPv6 on-link prefixes cache
+ */
+#if !defined LWIP_ND6_NUM_PREFIXES || defined __DOXYGEN__
+#define LWIP_ND6_NUM_PREFIXES           5
+#endif
+
+/**
+ * LWIP_ND6_NUM_ROUTERS: number of entries in IPv6 default router cache
+ */
+#if !defined LWIP_ND6_NUM_ROUTERS || defined __DOXYGEN__
+#define LWIP_ND6_NUM_ROUTERS            3
+#endif
+
+/**
+ * LWIP_ND6_MAX_MULTICAST_SOLICIT: max number of multicast solicit messages to send
+ * (neighbor solicit and router solicit)
+ */
+#if !defined LWIP_ND6_MAX_MULTICAST_SOLICIT || defined __DOXYGEN__
+#define LWIP_ND6_MAX_MULTICAST_SOLICIT  3
+#endif
+
+/**
+ * LWIP_ND6_MAX_UNICAST_SOLICIT: max number of unicast neighbor solicitation messages
+ * to send during neighbor reachability detection.
+ */
+#if !defined LWIP_ND6_MAX_UNICAST_SOLICIT || defined __DOXYGEN__
+#define LWIP_ND6_MAX_UNICAST_SOLICIT    3
+#endif
+
+/**
+ * Unused: See ND RFC (time in milliseconds).
+ */
+#if !defined LWIP_ND6_MAX_ANYCAST_DELAY_TIME || defined __DOXYGEN__
+#define LWIP_ND6_MAX_ANYCAST_DELAY_TIME 1000
+#endif
+
+/**
+ * Unused: See ND RFC
+ */
+#if !defined LWIP_ND6_MAX_NEIGHBOR_ADVERTISEMENT || defined __DOXYGEN__
+#define LWIP_ND6_MAX_NEIGHBOR_ADVERTISEMENT  3
+#endif
+
+/**
+ * LWIP_ND6_REACHABLE_TIME: default neighbor reachable time (in milliseconds).
+ * May be updated by router advertisement messages.
+ */
+#if !defined LWIP_ND6_REACHABLE_TIME || defined __DOXYGEN__
+#define LWIP_ND6_REACHABLE_TIME         30000
+#endif
+
+/**
+ * LWIP_ND6_RETRANS_TIMER: default retransmission timer for solicitation messages
+ */
+#if !defined LWIP_ND6_RETRANS_TIMER || defined __DOXYGEN__
+#define LWIP_ND6_RETRANS_TIMER          1000
+#endif
+
+/**
+ * LWIP_ND6_DELAY_FIRST_PROBE_TIME: Delay before first unicast neighbor solicitation
+ * message is sent, during neighbor reachability detection.
+ */
+#if !defined LWIP_ND6_DELAY_FIRST_PROBE_TIME || defined __DOXYGEN__
+#define LWIP_ND6_DELAY_FIRST_PROBE_TIME 5000
+#endif
+
+/**
+ * LWIP_ND6_ALLOW_RA_UPDATES==1: Allow Router Advertisement messages to update
+ * Reachable time and retransmission timers, and netif MTU.
+ */
+#if !defined LWIP_ND6_ALLOW_RA_UPDATES || defined __DOXYGEN__
+#define LWIP_ND6_ALLOW_RA_UPDATES       1
+#endif
+
+/**
+ * LWIP_ND6_TCP_REACHABILITY_HINTS==1: Allow TCP to provide Neighbor Discovery
+ * with reachability hints for connected destinations. This helps avoid sending
+ * unicast neighbor solicitation messages.
+ */
+#if !defined LWIP_ND6_TCP_REACHABILITY_HINTS || defined __DOXYGEN__
+#define LWIP_ND6_TCP_REACHABILITY_HINTS 1
+#endif
+
+/**
+ * LWIP_ND6_RDNSS_MAX_DNS_SERVERS > 0: Use IPv6 Router Advertisement Recursive
+ * DNS Server Option (as per RFC 6106) to copy a defined maximum number of DNS
+ * servers to the DNS module.
+ */
+#if !defined LWIP_ND6_RDNSS_MAX_DNS_SERVERS || defined __DOXYGEN__
+#define LWIP_ND6_RDNSS_MAX_DNS_SERVERS  0
+#endif
+/**
+ * @}
+ */
+
+/**
+ * @defgroup lwip_opts_dhcpv6 DHCPv6
+ * @ingroup lwip_opts_ipv6
+ * @{
+ */
+/**
+ * LWIP_IPV6_DHCP6==1: enable DHCPv6 stateful/stateless address autoconfiguration.
+ */
+#if !defined LWIP_IPV6_DHCP6 || defined __DOXYGEN__
+#define LWIP_IPV6_DHCP6                 0
+#endif
+
+/**
+ * LWIP_IPV6_DHCP6_STATEFUL==1: enable DHCPv6 stateful address autoconfiguration.
+ * (not supported, yet!)
+ */
+#if !defined LWIP_IPV6_DHCP6_STATEFUL || defined __DOXYGEN__
+#define LWIP_IPV6_DHCP6_STATEFUL        0
+#endif
+
+/**
+ * LWIP_IPV6_DHCP6_STATELESS==1: enable DHCPv6 stateless address autoconfiguration.
+ */
+#if !defined LWIP_IPV6_DHCP6_STATELESS || defined __DOXYGEN__
+#define LWIP_IPV6_DHCP6_STATELESS       LWIP_IPV6_DHCP6
+#endif
+
+/**
+ * LWIP_DHCP6_GETS_NTP==1: Request NTP servers via DHCPv6. For each
+ * response packet, a callback is called, which has to be provided by the port:
+ * void dhcp6_set_ntp_servers(u8_t num_ntp_servers, ip_addr_t* ntp_server_addrs);
+*/
+#if !defined LWIP_DHCP6_GET_NTP_SRV || defined __DOXYGEN__
+#define LWIP_DHCP6_GET_NTP_SRV          0
+#endif
+
+/**
+ * The maximum of NTP servers requested
+ */
+#if !defined LWIP_DHCP6_MAX_NTP_SERVERS || defined __DOXYGEN__
+#define LWIP_DHCP6_MAX_NTP_SERVERS      1
+#endif
+
+/**
+ * LWIP_DHCP6_MAX_DNS_SERVERS > 0: Request DNS servers via DHCPv6.
+ * DNS servers received in the response are passed to DNS via @ref dns_setserver()
+ * (up to the maximum limit defined here).
+ */
+#if !defined LWIP_DHCP6_MAX_DNS_SERVERS || defined __DOXYGEN__
+#define LWIP_DHCP6_MAX_DNS_SERVERS      DNS_MAX_SERVERS
+#endif
+/**
+ * @}
+ */
+
+/*
+   ---------------------------------------
+   ---------- Hook options ---------------
+   ---------------------------------------
+*/
+
+/**
+ * @defgroup lwip_opts_hooks Hooks
+ * @ingroup lwip_opts_infrastructure
+ * Hooks are undefined by default, define them to a function if you need them.
+ * @{
+ */
+
+/**
+ * LWIP_HOOK_FILENAME: Custom filename to \#include in files that provide hooks.
+ * Declare your hook function prototypes in there, you may also \#include all headers
+ * providing data types that are need in this file.
+ */
+#ifdef __DOXYGEN__
+#define LWIP_HOOK_FILENAME "path/to/my/lwip_hooks.h"
+#endif
+
+/**
+ * LWIP_HOOK_TCP_ISN:
+ * Hook for generation of the Initial Sequence Number (ISN) for a new TCP
+ * connection. The default lwIP ISN generation algorithm is very basic and may
+ * allow for TCP spoofing attacks. This hook provides the means to implement
+ * the standardized ISN generation algorithm from RFC 6528 (see contrib/adons/tcp_isn),
+ * or any other desired algorithm as a replacement.
+ * Called from tcp_connect() and tcp_listen_input() when an ISN is needed for
+ * a new TCP connection, if TCP support (@ref LWIP_TCP) is enabled.<br>
+ * Signature:\code{.c}
+ * u32_t my_hook_tcp_isn(const ip_addr_t* local_ip, u16_t local_port, const ip_addr_t* remote_ip, u16_t remote_port);
+ * \endcode
+ * - it may be necessary to use "struct ip_addr" (ip4_addr, ip6_addr) instead of "ip_addr_t" in function declarations<br>
+ * Arguments:
+ * - local_ip: pointer to the local IP address of the connection
+ * - local_port: local port number of the connection (host-byte order)
+ * - remote_ip: pointer to the remote IP address of the connection
+ * - remote_port: remote port number of the connection (host-byte order)<br>
+ * Return value:
+ * - the 32-bit Initial Sequence Number to use for the new TCP connection.
+ */
+#ifdef __DOXYGEN__
+#define LWIP_HOOK_TCP_ISN(local_ip, local_port, remote_ip, remote_port)
+#endif
+
+/**
+ * LWIP_HOOK_TCP_INPACKET_PCB:
+ * Hook for intercepting incoming packets before they are passed to a pcb. This
+ * allows updating some state or even dropping a packet.
+ * Signature:\code{.c}
+ * err_t my_hook_tcp_inpkt(struct tcp_pcb *pcb, struct tcp_hdr *hdr, u16_t optlen, u16_t opt1len, u8_t *opt2, struct pbuf *p);
+ * \endcode
+ * Arguments:
+ * - pcb: tcp_pcb selected for input of this packet (ATTENTION: this may be
+ *        struct tcp_pcb_listen if pcb->state == LISTEN)
+ * - hdr: pointer to tcp header (ATTENTION: tcp options may not be in one piece!)
+ * - optlen: tcp option length
+ * - opt1len: tcp option length 1st part
+ * - opt2: if this is != NULL, tcp options are split among 2 pbufs. In that case,
+ *         options start at right after the tcp header ('(u8_t*)(hdr + 1)') for
+ *         the first 'opt1len' bytes and the rest starts at 'opt2'. opt2len can
+ *         be simply calculated: 'opt2len = optlen - opt1len;'
+ * - p: input packet, p->payload points to application data (that's why tcp hdr
+ *      and options are passed in separately)
+ * Return value:
+ * - ERR_OK: continue input of this packet as normal
+ * - != ERR_OK: drop this packet for input (don't continue input processing)
+ *
+ * ATTENTION: don't call any tcp api functions that might change tcp state (pcb
+ * state or any pcb lists) from this callback!
+ */
+#ifdef __DOXYGEN__
+#define LWIP_HOOK_TCP_INPACKET_PCB(pcb, hdr, optlen, opt1len, opt2, p)
+#endif
+
+/**
+ * LWIP_HOOK_TCP_OUT_TCPOPT_LENGTH:
+ * Hook for increasing the size of the options allocated with a tcp header.
+ * Together with LWIP_HOOK_TCP_OUT_ADD_TCPOPTS, this can be used to add custom
+ * options to outgoing tcp segments.
+ * Signature:\code{.c}
+ * u8_t my_hook_tcp_out_tcpopt_length(const struct tcp_pcb *pcb, u8_t internal_option_length);
+ * \endcode
+ * Arguments:
+ * - pcb: tcp_pcb that transmits (ATTENTION: this may be NULL or
+ *        struct tcp_pcb_listen if pcb->state == LISTEN)
+ * - internal_option_length: tcp option length used by the stack internally
+ * Return value:
+ * - a number of bytes to allocate for tcp options (internal_option_length <= ret <= 40)
+ *
+ * ATTENTION: don't call any tcp api functions that might change tcp state (pcb
+ * state or any pcb lists) from this callback!
+ */
+#ifdef __DOXYGEN__
+#define LWIP_HOOK_TCP_OUT_TCPOPT_LENGTH(pcb, internal_len)
+#endif
+
+/**
+ * LWIP_HOOK_TCP_OUT_ADD_TCPOPTS:
+ * Hook for adding custom options to outgoing tcp segments.
+ * Space for these custom options has to be reserved via LWIP_HOOK_TCP_OUT_TCPOPT_LENGTH.
+ * Signature:\code{.c}
+ * u32_t *my_hook_tcp_out_add_tcpopts(struct pbuf *p, struct tcp_hdr *hdr, const struct tcp_pcb *pcb, u32_t *opts);
+ * \endcode
+ * Arguments:
+ * - p: output packet, p->payload pointing to tcp header, data follows
+ * - hdr: tcp header
+ * - pcb: tcp_pcb that transmits (ATTENTION: this may be NULL or
+ *        struct tcp_pcb_listen if pcb->state == LISTEN)
+ * - opts: pointer where to add the custom options (there may already be options
+ *         between the header and these)
+ * Return value:
+ * - pointer pointing directly after the inserted options
+ *
+ * ATTENTION: don't call any tcp api functions that might change tcp state (pcb
+ * state or any pcb lists) from this callback!
+ */
+#ifdef __DOXYGEN__
+#define LWIP_HOOK_TCP_OUT_ADD_TCPOPTS(p, hdr, pcb, opts)
+#endif
+
+/**
+ * LWIP_HOOK_IP4_INPUT(pbuf, input_netif):
+ * Called from ip_input() (IPv4)
+ * Signature:\code{.c}
+ *   int my_hook(struct pbuf *pbuf, struct netif *input_netif);
+ * \endcode
+ * Arguments:
+ * - pbuf: received struct pbuf passed to ip_input()
+ * - input_netif: struct netif on which the packet has been received
+ * Return values:
+ * - 0: Hook has not consumed the packet, packet is processed as normal
+ * - != 0: Hook has consumed the packet.
+ * If the hook consumed the packet, 'pbuf' is in the responsibility of the hook
+ * (i.e. free it when done).
+ */
+#ifdef __DOXYGEN__
+#define LWIP_HOOK_IP4_INPUT(pbuf, input_netif)
+#endif
+
+/**
+ * LWIP_HOOK_IP4_ROUTE(dest):
+ * Called from ip_route() (IPv4)
+ * Signature:\code{.c}
+ *   struct netif *my_hook(const ip4_addr_t *dest);
+ * \endcode
+ * Arguments:
+ * - dest: destination IPv4 address
+ * Returns values:
+ * - the destination netif
+ * - NULL if no destination netif is found. In that case, ip_route() continues as normal.
+ */
+#ifdef __DOXYGEN__
+#define LWIP_HOOK_IP4_ROUTE()
+#endif
+
+/**
+ * LWIP_HOOK_IP4_ROUTE_SRC(src, dest):
+ * Source-based routing for IPv4 - called from ip_route() (IPv4)
+ * Signature:\code{.c}
+ *   struct netif *my_hook(const ip4_addr_t *src, const ip4_addr_t *dest);
+ * \endcode
+ * Arguments:
+ * - src: local/source IPv4 address
+ * - dest: destination IPv4 address
+ * Returns values:
+ * - the destination netif
+ * - NULL if no destination netif is found. In that case, ip_route() continues as normal.
+ */
+#ifdef __DOXYGEN__
+#define LWIP_HOOK_IP4_ROUTE_SRC(src, dest)
+#endif
+
+/**
+ * LWIP_HOOK_IP4_CANFORWARD(src, dest):
+ * Check if an IPv4 can be forwarded - called from:
+ * ip4_input() -> ip4_forward() -> ip4_canforward() (IPv4)
+ * - source address is available via ip4_current_src_addr()
+ * - calling an output function in this context (e.g. multicast router) is allowed
+ * Signature:\code{.c}
+ *   int my_hook(struct pbuf *p, u32_t dest_addr_hostorder);
+ * \endcode
+ * Arguments:
+ * - p: packet to forward
+ * - dest: destination IPv4 address
+ * Returns values:
+ * - 1: forward
+ * - 0: don't forward
+ * - -1: no decision. In that case, ip4_canforward() continues as normal.
+ */
+#ifdef __DOXYGEN__
+#define LWIP_HOOK_IP4_CANFORWARD(src, dest)
+#endif
+
+/**
+ * LWIP_HOOK_ETHARP_GET_GW(netif, dest):
+ * Called from etharp_output() (IPv4)
+ * Signature:\code{.c}
+ *   const ip4_addr_t *my_hook(struct netif *netif, const ip4_addr_t *dest);
+ * \endcode
+ * Arguments:
+ * - netif: the netif used for sending
+ * - dest: the destination IPv4 address
+ * Return values:
+ * - the IPv4 address of the gateway to handle the specified destination IPv4 address
+ * - NULL, in which case the netif's default gateway is used
+ *
+ * The returned address MUST be directly reachable on the specified netif!
+ * This function is meant to implement advanced IPv4 routing together with
+ * LWIP_HOOK_IP4_ROUTE(). The actual routing/gateway table implementation is
+ * not part of lwIP but can e.g. be hidden in the netif's state argument.
+*/
+#ifdef __DOXYGEN__
+#define LWIP_HOOK_ETHARP_GET_GW(netif, dest)
+#endif
+
+/**
+ * LWIP_HOOK_IP6_INPUT(pbuf, input_netif):
+ * Called from ip6_input() (IPv6)
+ * Signature:\code{.c}
+ *   int my_hook(struct pbuf *pbuf, struct netif *input_netif);
+ * \endcode
+ * Arguments:
+ * - pbuf: received struct pbuf passed to ip6_input()
+ * - input_netif: struct netif on which the packet has been received
+ * Return values:
+ * - 0: Hook has not consumed the packet, packet is processed as normal
+ * - != 0: Hook has consumed the packet.
+ * If the hook consumed the packet, 'pbuf' is in the responsibility of the hook
+ * (i.e. free it when done).
+ */
+#ifdef __DOXYGEN__
+#define LWIP_HOOK_IP6_INPUT(pbuf, input_netif)
+#endif
+
+/**
+ * LWIP_HOOK_IP6_ROUTE(src, dest):
+ * Called from ip_route() (IPv6)
+ * Signature:\code{.c}
+ *   struct netif *my_hook(const ip6_addr_t *dest, const ip6_addr_t *src);
+ * \endcode
+ * Arguments:
+ * - src: source IPv6 address
+ * - dest: destination IPv6 address
+ * Return values:
+ * - the destination netif
+ * - NULL if no destination netif is found. In that case, ip6_route() continues as normal.
+ */
+#ifdef __DOXYGEN__
+#define LWIP_HOOK_IP6_ROUTE(src, dest)
+#endif
+
+/**
+ * LWIP_HOOK_ND6_GET_GW(netif, dest):
+ * Called from nd6_get_next_hop_entry() (IPv6)
+ * Signature:\code{.c}
+ *   const ip6_addr_t *my_hook(struct netif *netif, const ip6_addr_t *dest);
+ * \endcode
+ * Arguments:
+ * - netif: the netif used for sending
+ * - dest: the destination IPv6 address
+ * Return values:
+ * - the IPv6 address of the next hop to handle the specified destination IPv6 address
+ * - NULL, in which case a NDP-discovered router is used instead
+ *
+ * The returned address MUST be directly reachable on the specified netif!
+ * This function is meant to implement advanced IPv6 routing together with
+ * LWIP_HOOK_IP6_ROUTE(). The actual routing/gateway table implementation is
+ * not part of lwIP but can e.g. be hidden in the netif's state argument.
+*/
+#ifdef __DOXYGEN__
+#define LWIP_HOOK_ND6_GET_GW(netif, dest)
+#endif
+
+/**
+ * LWIP_HOOK_VLAN_CHECK(netif, eth_hdr, vlan_hdr):
+ * Called from ethernet_input() if VLAN support is enabled
+ * Signature:\code{.c}
+ *   int my_hook(struct netif *netif, struct eth_hdr *eth_hdr, struct eth_vlan_hdr *vlan_hdr);
+ * \endcode
+ * Arguments:
+ * - netif: struct netif on which the packet has been received
+ * - eth_hdr: struct eth_hdr of the packet
+ * - vlan_hdr: struct eth_vlan_hdr of the packet
+ * Return values:
+ * - 0: Packet must be dropped.
+ * - != 0: Packet must be accepted.
+ */
+#ifdef __DOXYGEN__
+#define LWIP_HOOK_VLAN_CHECK(netif, eth_hdr, vlan_hdr)
+#endif
+
+/**
+ * LWIP_HOOK_VLAN_SET:
+ * Hook can be used to set prio_vid field of vlan_hdr. If you need to store data
+ * on per-netif basis to implement this callback, see @ref netif_cd.
+ * Called from ethernet_output() if VLAN support (@ref ETHARP_SUPPORT_VLAN) is enabled.<br>
+ * Signature:\code{.c}
+ *   s32_t my_hook_vlan_set(struct netif* netif, struct pbuf* pbuf, const struct eth_addr* src, const struct eth_addr* dst, u16_t eth_type);
+ * \endcode
+ * Arguments:
+ * - netif: struct netif that the packet will be sent through
+ * - p: struct pbuf packet to be sent
+ * - src: source eth address
+ * - dst: destination eth address
+ * - eth_type: ethernet type to packet to be sent<br>
+ *
+ *
+ * Return values:
+ * - &lt;0: Packet shall not contain VLAN header.
+ * - 0 &lt;= return value &lt;= 0xFFFF: Packet shall contain VLAN header. Return value is prio_vid in host byte order.
+ */
+#ifdef __DOXYGEN__
+#define LWIP_HOOK_VLAN_SET(netif, p, src, dst, eth_type)
+#endif
+
+/**
+ * LWIP_HOOK_MEMP_AVAILABLE(memp_t_type):
+ * Called from memp_free() when a memp pool was empty and an item is now available
+ * Signature:\code{.c}
+ *   void my_hook(memp_t type);
+ * \endcode
+ */
+#ifdef __DOXYGEN__
+#define LWIP_HOOK_MEMP_AVAILABLE(memp_t_type)
+#endif
+
+/**
+ * LWIP_HOOK_UNKNOWN_ETH_PROTOCOL(pbuf, netif):
+ * Called from ethernet_input() when an unknown eth type is encountered.
+ * Signature:\code{.c}
+ *   err_t my_hook(struct pbuf* pbuf, struct netif* netif);
+ * \endcode
+ * Arguments:
+ * - p: rx packet with unknown eth type
+ * - netif: netif on which the packet has been received
+ * Return values:
+ * - ERR_OK if packet is accepted (hook function now owns the pbuf)
+ * - any error code otherwise (pbuf is freed)
+ *
+ * Payload points to ethernet header!
+ */
+#ifdef __DOXYGEN__
+#define LWIP_HOOK_UNKNOWN_ETH_PROTOCOL(pbuf, netif)
+#endif
+
+/**
+ * LWIP_HOOK_DHCP_APPEND_OPTIONS(netif, dhcp, state, msg, msg_type, options_len_ptr):
+ * Called from various dhcp functions when sending a DHCP message.
+ * This hook is called just before the DHCP message trailer is added, so the
+ * options are at the end of a DHCP message.
+ * Signature:\code{.c}
+ *   void my_hook(struct netif *netif, struct dhcp *dhcp, u8_t state, struct dhcp_msg *msg,
+ *                u8_t msg_type, u16_t *options_len_ptr);
+ * \endcode
+ * Arguments:
+ * - netif: struct netif that the packet will be sent through
+ * - dhcp: struct dhcp on that netif
+ * - state: current dhcp state (dhcp_state_enum_t as an u8_t)
+ * - msg: struct dhcp_msg that will be sent
+ * - msg_type: dhcp message type to be sent (u8_t)
+ * - options_len_ptr: pointer to the current length of options in the dhcp_msg "msg"
+ *                    (must be increased when options are added!)
+ *
+ * Options need to appended like this:
+ *   LWIP_ASSERT("dhcp option overflow", *options_len_ptr + option_len + 2 <= DHCP_OPTIONS_LEN);
+ *   msg->options[(*options_len_ptr)++] = &lt;option_number&gt;;
+ *   msg->options[(*options_len_ptr)++] = &lt;option_len&gt;;
+ *   msg->options[(*options_len_ptr)++] = &lt;option_bytes&gt;;
+ *   [...]
+ */
+#ifdef __DOXYGEN__
+#define LWIP_HOOK_DHCP_APPEND_OPTIONS(netif, dhcp, state, msg, msg_type, options_len_ptr)
+#endif
+
+/**
+ * LWIP_HOOK_DHCP_PARSE_OPTION(netif, dhcp, state, msg, msg_type, option, len, pbuf, option_value_offset):
+ * Called from dhcp_parse_reply when receiving a DHCP message.
+ * This hook is called for every option in the received message that is not handled internally.
+ * Signature:\code{.c}
+ *   void my_hook(struct netif *netif, struct dhcp *dhcp, u8_t state, struct dhcp_msg *msg,
+ *                u8_t msg_type, u8_t option, u8_t option_len, struct pbuf *pbuf, u16_t option_value_offset);
+ * \endcode
+ * Arguments:
+ * - netif: struct netif that the packet will be sent through
+ * - dhcp: struct dhcp on that netif
+ * - state: current dhcp state (dhcp_state_enum_t as an u8_t)
+ * - msg: struct dhcp_msg that was received
+ * - msg_type: dhcp message type received (u8_t, ATTENTION: only valid after
+ *             the message type option has been parsed!)
+ * - option: option value (u8_t)
+ * - len: option data length (u8_t)
+ * - pbuf: pbuf where option data is contained
+ * - option_value_offset: offset in pbuf where option data begins
+ *
+ * A nice way to get the option contents is pbuf_get_contiguous():
+ *  u8_t buf[32];
+ *  u8_t *ptr = (u8_t*)pbuf_get_contiguous(p, buf, sizeof(buf), LWIP_MIN(option_len, sizeof(buf)), offset);
+ */
+#ifdef __DOXYGEN__
+#define LWIP_HOOK_DHCP_PARSE_OPTION(netif, dhcp, state, msg, msg_type, option, len, pbuf, offset)
+#endif
+
+/**
+ * LWIP_HOOK_DHCP6_APPEND_OPTIONS(netif, dhcp6, state, msg, msg_type, options_len_ptr, max_len):
+ * Called from various dhcp6 functions when sending a DHCP6 message.
+ * This hook is called just before the DHCP6 message is sent, so the
+ * options are at the end of a DHCP6 message.
+ * Signature:\code{.c}
+ *   void my_hook(struct netif *netif, struct dhcp6 *dhcp, u8_t state, struct dhcp6_msg *msg,
+ *                u8_t msg_type, u16_t *options_len_ptr);
+ * \endcode
+ * Arguments:
+ * - netif: struct netif that the packet will be sent through
+ * - dhcp6: struct dhcp6 on that netif
+ * - state: current dhcp6 state (dhcp6_state_enum_t as an u8_t)
+ * - msg: struct dhcp6_msg that will be sent
+ * - msg_type: dhcp6 message type to be sent (u8_t)
+ * - options_len_ptr: pointer to the current length of options in the dhcp6_msg "msg"
+ *                    (must be increased when options are added!)
+ *
+ * Options need to appended like this:
+ *   u8_t *options = (u8_t *)(msg + 1);
+ *   LWIP_ASSERT("dhcp option overflow", sizeof(struct dhcp6_msg) + *options_len_ptr + newoptlen <= max_len);
+ *   options[(*options_len_ptr)++] = &lt;option_data&gt;;
+ *   [...]
+ */
+#ifdef __DOXYGEN__
+#define LWIP_HOOK_DHCP6_APPEND_OPTIONS(netif, dhcp6, state, msg, msg_type, options_len_ptr, max_len)
+#endif
+
+/**
+ * LWIP_HOOK_SOCKETS_SETSOCKOPT(s, sock, level, optname, optval, optlen, err)
+ * Called from socket API to implement setsockopt() for options not provided by lwIP.
+ * Core lock is held when this hook is called.
+ * Signature:\code{.c}
+ *   int my_hook(int s, struct lwip_sock *sock, int level, int optname, const void *optval, socklen_t optlen, int *err)
+ * \endcode
+ * Arguments:
+ * - s: socket file descriptor
+ * - sock: internal socket descriptor (see lwip/priv/sockets_priv.h)
+ * - level: protocol level at which the option resides
+ * - optname: option to set
+ * - optval: value to set
+ * - optlen: size of optval
+ * - err: output error
+ * Return values:
+ * - 0: Hook has not consumed the option, code continues as normal (to internal options)
+ * - != 0: Hook has consumed the option, 'err' is returned
+ */
+#ifdef __DOXYGEN__
+#define LWIP_HOOK_SOCKETS_SETSOCKOPT(s, sock, level, optname, optval, optlen, err)
+#endif
+
+/**
+ * LWIP_HOOK_SOCKETS_GETSOCKOPT(s, sock, level, optname, optval, optlen, err)
+ * Called from socket API to implement getsockopt() for options not provided by lwIP.
+ * Core lock is held when this hook is called.
+ * Signature:\code{.c}
+ *   int my_hook(int s, struct lwip_sock *sock, int level, int optname, void *optval, socklen_t *optlen, int *err)
+ * \endcode
+ * Arguments:
+ * - s: socket file descriptor
+ * - sock: internal socket descriptor (see lwip/priv/sockets_priv.h)
+ * - level: protocol level at which the option resides
+ * - optname: option to get
+ * - optval: value to get
+ * - optlen: size of optval
+ * - err: output error
+ * Return values:
+ * - 0: Hook has not consumed the option, code continues as normal (to internal options)
+ * - != 0: Hook has consumed the option, 'err' is returned
+ */
+#ifdef __DOXYGEN__
+#define LWIP_HOOK_SOCKETS_GETSOCKOPT(s, sock, level, optname, optval, optlen, err)
+#endif
+
+/**
+ * LWIP_HOOK_NETCONN_EXTERNAL_RESOLVE(name, addr, addrtype, err)
+ * Called from netconn APIs (not usable with callback apps) allowing an
+ * external DNS resolver (which uses sequential API) to handle the query.
+ * Signature:\code{.c}
+ *   int my_hook(const char *name, ip_addr_t *addr, u8_t addrtype, err_t *err)
+ * \endcode
+ * Arguments:
+ * - name: hostname to resolve
+ * - addr: output host address
+ * - addrtype: type of address to query
+ * - err: output error
+ * Return values:
+ * - 0: Hook has not consumed hostname query, query continues into DNS module
+ * - != 0: Hook has consumed the query
+ *
+ * err must also be checked to determine if the hook consumed the query, but
+ * the query failed
+ */
+#ifdef __DOXYGEN__
+#define LWIP_HOOK_NETCONN_EXTERNAL_RESOLVE(name, addr, addrtype, err)
+#endif
+/**
+ * @}
+ */
+
+/*
+   ---------------------------------------
+   ---------- Debugging options ----------
+   ---------------------------------------
+*/
+/**
+ * @defgroup lwip_opts_debugmsg Debug messages
+ * @ingroup lwip_opts_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.
+ * @see debugging_levels
+ */
+#if !defined LWIP_DBG_MIN_LEVEL || defined __DOXYGEN__
+#define LWIP_DBG_MIN_LEVEL              LWIP_DBG_LEVEL_ALL
+#endif
+
+/**
+ * LWIP_DBG_TYPES_ON: A mask that can be used to globally enable/disable
+ * debug messages of certain types.
+ * @see debugging_levels
+ */
+#if !defined LWIP_DBG_TYPES_ON || defined __DOXYGEN__
+#define LWIP_DBG_TYPES_ON               LWIP_DBG_ON
+#endif
+
+/**
+ * ETHARP_DEBUG: Enable debugging in etharp.c.
+ */
+#if !defined ETHARP_DEBUG || defined __DOXYGEN__
+#define ETHARP_DEBUG                    LWIP_DBG_OFF
+#endif
+
+/**
+ * NETIF_DEBUG: Enable debugging in netif.c.
+ */
+#if !defined NETIF_DEBUG || defined __DOXYGEN__
+#define NETIF_DEBUG                     LWIP_DBG_OFF
+#endif
+
+/**
+ * PBUF_DEBUG: Enable debugging in pbuf.c.
+ */
+#if !defined PBUF_DEBUG || defined __DOXYGEN__
+#define PBUF_DEBUG                      LWIP_DBG_OFF
+#endif
+
+/**
+ * API_LIB_DEBUG: Enable debugging in api_lib.c.
+ */
+#if !defined API_LIB_DEBUG || defined __DOXYGEN__
+#define API_LIB_DEBUG                   LWIP_DBG_OFF
+#endif
+
+/**
+ * API_MSG_DEBUG: Enable debugging in api_msg.c.
+ */
+#if !defined API_MSG_DEBUG || defined __DOXYGEN__
+#define API_MSG_DEBUG                   LWIP_DBG_OFF
+#endif
+
+/**
+ * SOCKETS_DEBUG: Enable debugging in sockets.c.
+ */
+#if !defined SOCKETS_DEBUG || defined __DOXYGEN__
+#define SOCKETS_DEBUG                   LWIP_DBG_OFF
+#endif
+
+/**
+ * ICMP_DEBUG: Enable debugging in icmp.c.
+ */
+#if !defined ICMP_DEBUG || defined __DOXYGEN__
+#define ICMP_DEBUG                      LWIP_DBG_OFF
+#endif
+
+/**
+ * IGMP_DEBUG: Enable debugging in igmp.c.
+ */
+#if !defined IGMP_DEBUG || defined __DOXYGEN__
+#define IGMP_DEBUG                      LWIP_DBG_OFF
+#endif
+
+/**
+ * INET_DEBUG: Enable debugging in inet.c.
+ */
+#if !defined INET_DEBUG || defined __DOXYGEN__
+#define INET_DEBUG                      LWIP_DBG_OFF
+#endif
+
+/**
+ * IP_DEBUG: Enable debugging for IP.
+ */
+#if !defined IP_DEBUG || defined __DOXYGEN__
+#define IP_DEBUG                        LWIP_DBG_OFF
+#endif
+
+/**
+ * IP_REASS_DEBUG: Enable debugging in ip_frag.c for both frag & reass.
+ */
+#if !defined IP_REASS_DEBUG || defined __DOXYGEN__
+#define IP_REASS_DEBUG                  LWIP_DBG_OFF
+#endif
+
+/**
+ * RAW_DEBUG: Enable debugging in raw.c.
+ */
+#if !defined RAW_DEBUG || defined __DOXYGEN__
+#define RAW_DEBUG                       LWIP_DBG_OFF
+#endif
+
+/**
+ * MEM_DEBUG: Enable debugging in mem.c.
+ */
+#if !defined MEM_DEBUG || defined __DOXYGEN__
+#define MEM_DEBUG                       LWIP_DBG_OFF
+#endif
+
+/**
+ * MEMP_DEBUG: Enable debugging in memp.c.
+ */
+#if !defined MEMP_DEBUG || defined __DOXYGEN__
+#define MEMP_DEBUG                      LWIP_DBG_OFF
+#endif
+
+/**
+ * SYS_DEBUG: Enable debugging in sys.c.
+ */
+#if !defined SYS_DEBUG || defined __DOXYGEN__
+#define SYS_DEBUG                       LWIP_DBG_OFF
+#endif
+
+/**
+ * TIMERS_DEBUG: Enable debugging in timers.c.
+ */
+#if !defined TIMERS_DEBUG || defined __DOXYGEN__
+#define TIMERS_DEBUG                    LWIP_DBG_OFF
+#endif
+
+/**
+ * TCP_DEBUG: Enable debugging for TCP.
+ */
+#if !defined TCP_DEBUG || defined __DOXYGEN__
+#define TCP_DEBUG                       LWIP_DBG_OFF
+#endif
+
+/**
+ * TCP_INPUT_DEBUG: Enable debugging in tcp_in.c for incoming debug.
+ */
+#if !defined TCP_INPUT_DEBUG || defined __DOXYGEN__
+#define TCP_INPUT_DEBUG                 LWIP_DBG_OFF
+#endif
+
+/**
+ * TCP_FR_DEBUG: Enable debugging in tcp_in.c for fast retransmit.
+ */
+#if !defined TCP_FR_DEBUG || defined __DOXYGEN__
+#define TCP_FR_DEBUG                    LWIP_DBG_OFF
+#endif
+
+/**
+ * TCP_RTO_DEBUG: Enable debugging in TCP for retransmit
+ * timeout.
+ */
+#if !defined TCP_RTO_DEBUG || defined __DOXYGEN__
+#define TCP_RTO_DEBUG                   LWIP_DBG_OFF
+#endif
+
+/**
+ * TCP_CWND_DEBUG: Enable debugging for TCP congestion window.
+ */
+#if !defined TCP_CWND_DEBUG || defined __DOXYGEN__
+#define TCP_CWND_DEBUG                  LWIP_DBG_OFF
+#endif
+
+/**
+ * TCP_WND_DEBUG: Enable debugging in tcp_in.c for window updating.
+ */
+#if !defined TCP_WND_DEBUG || defined __DOXYGEN__
+#define TCP_WND_DEBUG                   LWIP_DBG_OFF
+#endif
+
+/**
+ * TCP_OUTPUT_DEBUG: Enable debugging in tcp_out.c output functions.
+ */
+#if !defined TCP_OUTPUT_DEBUG || defined __DOXYGEN__
+#define TCP_OUTPUT_DEBUG                LWIP_DBG_OFF
+#endif
+
+/**
+ * TCP_RST_DEBUG: Enable debugging for TCP with the RST message.
+ */
+#if !defined TCP_RST_DEBUG || defined __DOXYGEN__
+#define TCP_RST_DEBUG                   LWIP_DBG_OFF
+#endif
+
+/**
+ * TCP_QLEN_DEBUG: Enable debugging for TCP queue lengths.
+ */
+#if !defined TCP_QLEN_DEBUG || defined __DOXYGEN__
+#define TCP_QLEN_DEBUG                  LWIP_DBG_OFF
+#endif
+
+/**
+ * UDP_DEBUG: Enable debugging in UDP.
+ */
+#if !defined UDP_DEBUG || defined __DOXYGEN__
+#define UDP_DEBUG                       LWIP_DBG_OFF
+#endif
+
+/**
+ * TCPIP_DEBUG: Enable debugging in tcpip.c.
+ */
+#if !defined TCPIP_DEBUG || defined __DOXYGEN__
+#define TCPIP_DEBUG                     LWIP_DBG_OFF
+#endif
+
+/**
+ * SLIP_DEBUG: Enable debugging in slipif.c.
+ */
+#if !defined SLIP_DEBUG || defined __DOXYGEN__
+#define SLIP_DEBUG                      LWIP_DBG_OFF
+#endif
+
+/**
+ * DHCP_DEBUG: Enable debugging in dhcp.c.
+ */
+#if !defined DHCP_DEBUG || defined __DOXYGEN__
+#define DHCP_DEBUG                      LWIP_DBG_OFF
+#endif
+
+/**
+ * AUTOIP_DEBUG: Enable debugging in autoip.c.
+ */
+#if !defined AUTOIP_DEBUG || defined __DOXYGEN__
+#define AUTOIP_DEBUG                    LWIP_DBG_OFF
+#endif
+
+/**
+ * ACD_DEBUG: Enable debugging in acd.c.
+ */
+#if !defined ACD_DEBUG || defined __DOXYGEN__
+#define ACD_DEBUG                       LWIP_DBG_OFF
+#endif
+
+/**
+ * DNS_DEBUG: Enable debugging for DNS.
+ */
+#if !defined DNS_DEBUG || defined __DOXYGEN__
+#define DNS_DEBUG                       LWIP_DBG_OFF
+#endif
+
+/**
+ * IP6_DEBUG: Enable debugging for IPv6.
+ */
+#if !defined IP6_DEBUG || defined __DOXYGEN__
+#define IP6_DEBUG                       LWIP_DBG_OFF
+#endif
+
+/**
+ * DHCP6_DEBUG: Enable debugging in dhcp6.c.
+ */
+#if !defined DHCP6_DEBUG || defined __DOXYGEN__
+#define DHCP6_DEBUG                     LWIP_DBG_OFF
+#endif
+/**
+ * @}
+ */
+
+/**
+ * LWIP_TESTMODE: Changes to make unit test possible
+ */
+#if !defined LWIP_TESTMODE
+#define LWIP_TESTMODE                   0
+#endif
+
+/*
+   --------------------------------------------------
+   ---------- Performance tracking options ----------
+   --------------------------------------------------
+*/
+/**
+ * @defgroup lwip_opts_perf Performance
+ * @ingroup lwip_opts_debug
+ * @{
+ */
+/**
+ * LWIP_PERF: Enable performance testing for lwIP
+ * (if enabled, arch/perf.h is included)
+ */
+#if !defined LWIP_PERF || defined __DOXYGEN__
+#define LWIP_PERF                       0
+#endif
+/**
+ * @}
+ */
+
+#endif /* LWIP_HDR_OPT_H */
diff --git a/lib/lwip/include/lwip/pbuf.h b/lib/lwip/include/lwip/pbuf.h
new file mode 100644
index 0000000000..5a4fc88b37
--- /dev/null
+++ b/lib/lwip/include/lwip/pbuf.h
@@ -0,0 +1,326 @@
+/**
+ * @file
+ * pbuf API
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#ifndef LWIP_HDR_PBUF_H
+#define LWIP_HDR_PBUF_H
+
+#include "lwip/opt.h"
+#include "lwip/err.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** LWIP_SUPPORT_CUSTOM_PBUF==1: Custom pbufs behave much like their pbuf type
+ * but they are allocated by external code (initialised by calling
+ * pbuf_alloced_custom()) and when pbuf_free gives up their last reference, they
+ * are freed by calling pbuf_custom->custom_free_function().
+ * Currently, the pbuf_custom code is only needed for one specific configuration
+ * of IP_FRAG, unless required by external driver/application code. */
+#ifndef LWIP_SUPPORT_CUSTOM_PBUF
+#define LWIP_SUPPORT_CUSTOM_PBUF ((IP_FRAG && !LWIP_NETIF_TX_SINGLE_PBUF) || (LWIP_IPV6 && LWIP_IPV6_FRAG))
+#endif
+
+/** @ingroup pbuf
+ * PBUF_NEEDS_COPY(p): return a boolean value indicating whether the given
+ * pbuf needs to be copied in order to be kept around beyond the current call
+ * stack without risking being corrupted. The default setting provides safety:
+ * it will make a copy iof any pbuf chain that does not consist entirely of
+ * PBUF_ROM type pbufs. For setups with zero-copy support, it may be redefined
+ * to evaluate to true in all cases, for example. However, doing so also has an
+ * effect on the application side: any buffers that are *not* copied must also
+ * *not* be reused by the application after passing them to lwIP. For example,
+ * when setting PBUF_NEEDS_COPY to (0), after using udp_send() with a PBUF_RAM
+ * pbuf, the application must free the pbuf immediately, rather than reusing it
+ * for other purposes. For more background information on this, see tasks #6735
+ * and #7896, and bugs #11400 and #49914. */
+#ifndef PBUF_NEEDS_COPY
+#define PBUF_NEEDS_COPY(p)  ((p)->type_internal & PBUF_TYPE_FLAG_DATA_VOLATILE)
+#endif /* PBUF_NEEDS_COPY */
+
+/* @todo: We need a mechanism to prevent wasting memory in every pbuf
+   (TCP vs. UDP, IPv4 vs. IPv6: UDP/IPv4 packets may waste up to 28 bytes) */
+
+#define PBUF_TRANSPORT_HLEN 20
+#if LWIP_IPV6
+#define PBUF_IP_HLEN        40
+#else
+#define PBUF_IP_HLEN        20
+#endif
+
+/**
+ * @ingroup pbuf
+ * Enumeration of pbuf layers
+ */
+typedef enum {
+  /** Includes spare room for transport layer header, e.g. UDP header.
+   * Use this if you intend to pass the pbuf to functions like udp_send().
+   */
+  PBUF_TRANSPORT = PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN,
+  /** Includes spare room for IP header.
+   * Use this if you intend to pass the pbuf to functions like raw_send().
+   */
+  PBUF_IP = PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN + PBUF_IP_HLEN,
+  /** Includes spare room for link layer header (ethernet header).
+   * Use this if you intend to pass the pbuf to functions like ethernet_output().
+   * @see PBUF_LINK_HLEN
+   */
+  PBUF_LINK = PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN,
+  /** Includes spare room for additional encapsulation header before ethernet
+   * headers (e.g. 802.11).
+   * Use this if you intend to pass the pbuf to functions like netif->linkoutput().
+   * @see PBUF_LINK_ENCAPSULATION_HLEN
+   */
+  PBUF_RAW_TX = PBUF_LINK_ENCAPSULATION_HLEN,
+  /** Use this for input packets in a netif driver when calling netif->input()
+   * in the most common case - ethernet-layer netif driver. */
+  PBUF_RAW = 0
+} pbuf_layer;
+
+
+/* Base flags for pbuf_type definitions: */
+
+/** Indicates that the payload directly follows the struct pbuf.
+ *  This makes @ref pbuf_header work in both directions. */
+#define PBUF_TYPE_FLAG_STRUCT_DATA_CONTIGUOUS       0x80
+/** Indicates the data stored in this pbuf can change. If this pbuf needs
+ * to be queued, it must be copied/duplicated. */
+#define PBUF_TYPE_FLAG_DATA_VOLATILE                0x40
+/** 4 bits are reserved for 16 allocation sources (e.g. heap, pool1, pool2, etc)
+ * Internally, we use: 0=heap, 1=MEMP_PBUF, 2=MEMP_PBUF_POOL -> 13 types free*/
+#define PBUF_TYPE_ALLOC_SRC_MASK                    0x0F
+/** Indicates this pbuf is used for RX (if not set, indicates use for TX).
+ * This information can be used to keep some spare RX buffers e.g. for
+ * receiving TCP ACKs to unblock a connection) */
+#define PBUF_ALLOC_FLAG_RX                          0x0100
+/** Indicates the application needs the pbuf payload to be in one piece */
+#define PBUF_ALLOC_FLAG_DATA_CONTIGUOUS             0x0200
+
+#define PBUF_TYPE_ALLOC_SRC_MASK_STD_HEAP           0x00
+#define PBUF_TYPE_ALLOC_SRC_MASK_STD_MEMP_PBUF      0x01
+#define PBUF_TYPE_ALLOC_SRC_MASK_STD_MEMP_PBUF_POOL 0x02
+/** First pbuf allocation type for applications */
+#define PBUF_TYPE_ALLOC_SRC_MASK_APP_MIN            0x03
+/** Last pbuf allocation type for applications */
+#define PBUF_TYPE_ALLOC_SRC_MASK_APP_MAX            PBUF_TYPE_ALLOC_SRC_MASK
+
+/**
+ * @ingroup pbuf
+ * Enumeration of pbuf types
+ */
+typedef enum {
+  /** pbuf data is stored in RAM, used for TX mostly, struct pbuf and its payload
+      are allocated in one piece of contiguous memory (so the first payload byte
+      can be calculated from struct pbuf).
+      pbuf_alloc() allocates PBUF_RAM pbufs as unchained pbufs (although that might
+      change in future versions).
+      This should be used for all OUTGOING packets (TX).*/
+  PBUF_RAM = (PBUF_ALLOC_FLAG_DATA_CONTIGUOUS | PBUF_TYPE_FLAG_STRUCT_DATA_CONTIGUOUS | PBUF_TYPE_ALLOC_SRC_MASK_STD_HEAP),
+  /** pbuf data is stored in ROM, i.e. struct pbuf and its payload are located in
+      totally different memory areas. Since it points to ROM, payload does not
+      have to be copied when queued for transmission. */
+  PBUF_ROM = PBUF_TYPE_ALLOC_SRC_MASK_STD_MEMP_PBUF,
+  /** pbuf comes from the pbuf pool. Much like PBUF_ROM but payload might change
+      so it has to be duplicated when queued before transmitting, depending on
+      who has a 'ref' to it. */
+  PBUF_REF = (PBUF_TYPE_FLAG_DATA_VOLATILE | PBUF_TYPE_ALLOC_SRC_MASK_STD_MEMP_PBUF),
+  /** pbuf payload refers to RAM. This one comes from a pool and should be used
+      for RX. Payload can be chained (scatter-gather RX) but like PBUF_RAM, struct
+      pbuf and its payload are allocated in one piece of contiguous memory (so
+      the first payload byte can be calculated from struct pbuf).
+      Don't use this for TX, if the pool becomes empty e.g. because of TCP queuing,
+      you are unable to receive TCP acks! */
+  PBUF_POOL = (PBUF_ALLOC_FLAG_RX | PBUF_TYPE_FLAG_STRUCT_DATA_CONTIGUOUS | PBUF_TYPE_ALLOC_SRC_MASK_STD_MEMP_PBUF_POOL)
+} pbuf_type;
+
+
+/** indicates this packet's data should be immediately passed to the application */
+#define PBUF_FLAG_PUSH      0x01U
+/** indicates this is a custom pbuf: pbuf_free calls pbuf_custom->custom_free_function()
+    when the last reference is released (plus custom PBUF_RAM cannot be trimmed) */
+#define PBUF_FLAG_IS_CUSTOM 0x02U
+/** indicates this pbuf is UDP multicast to be looped back */
+#define PBUF_FLAG_MCASTLOOP 0x04U
+/** indicates this pbuf was received as link-level broadcast */
+#define PBUF_FLAG_LLBCAST   0x08U
+/** indicates this pbuf was received as link-level multicast */
+#define PBUF_FLAG_LLMCAST   0x10U
+/** indicates this pbuf includes a TCP FIN flag */
+#define PBUF_FLAG_TCP_FIN   0x20U
+
+/** Main packet buffer struct */
+struct pbuf {
+  /** next pbuf in singly linked pbuf chain */
+  struct pbuf *next;
+
+  /** pointer to the actual data in the buffer */
+  void *payload;
+
+  /**
+   * total length of this buffer and all next buffers in chain
+   * belonging to the same packet.
+   *
+   * For non-queue packet chains this is the invariant:
+   * p->tot_len == p->len + (p->next? p->next->tot_len: 0)
+   */
+  u16_t tot_len;
+
+  /** length of this buffer */
+  u16_t len;
+
+  /** a bit field indicating pbuf type and allocation sources
+      (see PBUF_TYPE_FLAG_*, PBUF_ALLOC_FLAG_* and PBUF_TYPE_ALLOC_SRC_MASK)
+    */
+  u8_t type_internal;
+
+  /** misc flags */
+  u8_t flags;
+
+  /**
+   * the reference count always equals the number of pointers
+   * that refer to this pbuf. This can be pointers from an application,
+   * the stack itself, or pbuf->next pointers from a chain.
+   */
+  LWIP_PBUF_REF_T ref;
+
+  /** For incoming packets, this contains the input netif's index */
+  u8_t if_idx;
+
+  /** In case the user needs to store data custom data on a pbuf */
+  LWIP_PBUF_CUSTOM_DATA
+};
+
+
+/** Helper struct for const-correctness only.
+ * The only meaning of this one is to provide a const payload pointer
+ * for PBUF_ROM type.
+ */
+struct pbuf_rom {
+  /** next pbuf in singly linked pbuf chain */
+  struct pbuf *next;
+
+  /** pointer to the actual data in the buffer */
+  const void *payload;
+};
+
+#if LWIP_SUPPORT_CUSTOM_PBUF
+/** Prototype for a function to free a custom pbuf */
+typedef void (*pbuf_free_custom_fn)(struct pbuf *p);
+
+/** A custom pbuf: like a pbuf, but following a function pointer to free it. */
+struct pbuf_custom {
+  /** The actual pbuf */
+  struct pbuf pbuf;
+  /** This function is called when pbuf_free deallocates this pbuf(_custom) */
+  pbuf_free_custom_fn custom_free_function;
+};
+#endif /* LWIP_SUPPORT_CUSTOM_PBUF */
+
+/** Define this to 0 to prevent freeing ooseq pbufs when the PBUF_POOL is empty */
+#ifndef PBUF_POOL_FREE_OOSEQ
+#define PBUF_POOL_FREE_OOSEQ 1
+#endif /* PBUF_POOL_FREE_OOSEQ */
+#if LWIP_TCP && TCP_QUEUE_OOSEQ && NO_SYS && PBUF_POOL_FREE_OOSEQ
+extern volatile u8_t pbuf_free_ooseq_pending;
+void pbuf_free_ooseq(void);
+/** When not using sys_check_timeouts(), call PBUF_CHECK_FREE_OOSEQ()
+    at regular intervals from main level to check if ooseq pbufs need to be
+    freed! */
+#define PBUF_CHECK_FREE_OOSEQ() do { if(pbuf_free_ooseq_pending) { \
+  /* pbuf_alloc() reported PBUF_POOL to be empty -> try to free some \
+     ooseq queued pbufs now */ \
+  pbuf_free_ooseq(); }}while(0)
+#else /* LWIP_TCP && TCP_QUEUE_OOSEQ && NO_SYS && PBUF_POOL_FREE_OOSEQ */
+  /* Otherwise declare an empty PBUF_CHECK_FREE_OOSEQ */
+  #define PBUF_CHECK_FREE_OOSEQ()
+#endif /* LWIP_TCP && TCP_QUEUE_OOSEQ && NO_SYS && PBUF_POOL_FREE_OOSEQ*/
+
+/* Initializes the pbuf module. This call is empty for now, but may not be in future. */
+#define pbuf_init()
+
+struct pbuf *pbuf_alloc(pbuf_layer l, u16_t length, pbuf_type type);
+struct pbuf *pbuf_alloc_reference(void *payload, u16_t length, pbuf_type type);
+#if LWIP_SUPPORT_CUSTOM_PBUF
+struct pbuf *pbuf_alloced_custom(pbuf_layer l, u16_t length, pbuf_type type,
+                                 struct pbuf_custom *p, void *payload_mem,
+                                 u16_t payload_mem_len);
+#endif /* LWIP_SUPPORT_CUSTOM_PBUF */
+void pbuf_realloc(struct pbuf *p, u16_t size);
+#define pbuf_get_allocsrc(p)          ((p)->type_internal & PBUF_TYPE_ALLOC_SRC_MASK)
+#define pbuf_match_allocsrc(p, type)  (pbuf_get_allocsrc(p) == ((type) & PBUF_TYPE_ALLOC_SRC_MASK))
+#define pbuf_match_type(p, type)      pbuf_match_allocsrc(p, type)
+u8_t pbuf_header(struct pbuf *p, s16_t header_size);
+u8_t pbuf_header_force(struct pbuf *p, s16_t header_size);
+u8_t pbuf_add_header(struct pbuf *p, size_t header_size_increment);
+u8_t pbuf_add_header_force(struct pbuf *p, size_t header_size_increment);
+u8_t pbuf_remove_header(struct pbuf *p, size_t header_size);
+struct pbuf *pbuf_free_header(struct pbuf *q, u16_t size);
+void pbuf_ref(struct pbuf *p);
+u8_t pbuf_free(struct pbuf *p);
+u16_t pbuf_clen(const struct pbuf *p);
+void pbuf_cat(struct pbuf *head, struct pbuf *tail);
+void pbuf_chain(struct pbuf *head, struct pbuf *tail);
+struct pbuf *pbuf_dechain(struct pbuf *p);
+err_t pbuf_copy(struct pbuf *p_to, const struct pbuf *p_from);
+err_t pbuf_copy_partial_pbuf(struct pbuf *p_to, const struct pbuf *p_from, u16_t copy_len, u16_t offset);
+u16_t pbuf_copy_partial(const struct pbuf *p, void *dataptr, u16_t len, u16_t offset);
+void *pbuf_get_contiguous(const struct pbuf *p, void *buffer, size_t bufsize, u16_t len, u16_t offset);
+err_t pbuf_take(struct pbuf *buf, const void *dataptr, u16_t len);
+err_t pbuf_take_at(struct pbuf *buf, const void *dataptr, u16_t len, u16_t offset);
+struct pbuf *pbuf_skip(struct pbuf* in, u16_t in_offset, u16_t* out_offset);
+struct pbuf *pbuf_coalesce(struct pbuf *p, pbuf_layer layer);
+struct pbuf *pbuf_clone(pbuf_layer l, pbuf_type type, struct pbuf *p);
+#if LWIP_CHECKSUM_ON_COPY
+err_t pbuf_fill_chksum(struct pbuf *p, u16_t start_offset, const void *dataptr,
+                       u16_t len, u16_t *chksum);
+#endif /* LWIP_CHECKSUM_ON_COPY */
+#if LWIP_TCP && TCP_QUEUE_OOSEQ && LWIP_WND_SCALE
+void pbuf_split_64k(struct pbuf *p, struct pbuf **rest);
+#endif /* LWIP_TCP && TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */
+
+u8_t pbuf_get_at(const struct pbuf* p, u16_t offset);
+int pbuf_try_get_at(const struct pbuf* p, u16_t offset);
+void pbuf_put_at(struct pbuf* p, u16_t offset, u8_t data);
+u16_t pbuf_memcmp(const struct pbuf* p, u16_t offset, const void* s2, u16_t n);
+u16_t pbuf_memfind(const struct pbuf* p, const void* mem, u16_t mem_len, u16_t start_offset);
+u16_t pbuf_strstr(const struct pbuf* p, const char* substr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_PBUF_H */
diff --git a/lib/lwip/include/lwip/priv/altcp_priv.h b/lib/lwip/include/lwip/priv/altcp_priv.h
new file mode 100644
index 0000000000..50e6b970cd
--- /dev/null
+++ b/lib/lwip/include/lwip/priv/altcp_priv.h
@@ -0,0 +1,159 @@
+/**
+ * @file
+ * Application layered TCP connection API (to be used from TCPIP thread)<br>
+ * This interface mimics the tcp callback API to the application while preventing
+ * direct linking (much like virtual functions).
+ * This way, an application can make use of other application layer protocols
+ * on top of TCP without knowing the details (e.g. TLS, proxy connection).
+ */
+
+/*
+ * Copyright (c) 2017 Simon Goldschmidt
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt <goldsimon@gmx.de>
+ *
+ */
+#ifndef LWIP_HDR_ALTCP_PRIV_H
+#define LWIP_HDR_ALTCP_PRIV_H
+
+#include "lwip/opt.h"
+
+#if LWIP_ALTCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/altcp.h"
+#include "lwip/ip_addr.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct altcp_pcb *altcp_alloc(void);
+void altcp_free(struct altcp_pcb *conn);
+
+/* Function prototypes for application layers */
+typedef void (*altcp_set_poll_fn)(struct altcp_pcb *conn, u8_t interval);
+typedef void (*altcp_recved_fn)(struct altcp_pcb *conn, u16_t len);
+typedef err_t (*altcp_bind_fn)(struct altcp_pcb *conn, const ip_addr_t *ipaddr, u16_t port);
+typedef err_t (*altcp_connect_fn)(struct altcp_pcb *conn, const ip_addr_t *ipaddr, u16_t port, altcp_connected_fn connected);
+
+typedef struct altcp_pcb *(*altcp_listen_fn)(struct altcp_pcb *conn, u8_t backlog, err_t *err);
+
+typedef void  (*altcp_abort_fn)(struct altcp_pcb *conn);
+typedef err_t (*altcp_close_fn)(struct altcp_pcb *conn);
+typedef err_t (*altcp_shutdown_fn)(struct altcp_pcb *conn, int shut_rx, int shut_tx);
+
+typedef err_t (*altcp_write_fn)(struct altcp_pcb *conn, const void *dataptr, u16_t len, u8_t apiflags);
+typedef err_t (*altcp_output_fn)(struct altcp_pcb *conn);
+
+typedef u16_t (*altcp_mss_fn)(struct altcp_pcb *conn);
+typedef u16_t (*altcp_sndbuf_fn)(struct altcp_pcb *conn);
+typedef u16_t (*altcp_sndqueuelen_fn)(struct altcp_pcb *conn);
+typedef void  (*altcp_nagle_disable_fn)(struct altcp_pcb *conn);
+typedef void  (*altcp_nagle_enable_fn)(struct altcp_pcb *conn);
+typedef int   (*altcp_nagle_disabled_fn)(struct altcp_pcb *conn);
+
+typedef void  (*altcp_setprio_fn)(struct altcp_pcb *conn, u8_t prio);
+
+typedef void  (*altcp_dealloc_fn)(struct altcp_pcb *conn);
+
+typedef err_t (*altcp_get_tcp_addrinfo_fn)(struct altcp_pcb *conn, int local, ip_addr_t *addr, u16_t *port);
+typedef ip_addr_t *(*altcp_get_ip_fn)(struct altcp_pcb *conn, int local);
+typedef u16_t (*altcp_get_port_fn)(struct altcp_pcb *conn, int local);
+
+#if LWIP_TCP_KEEPALIVE
+typedef void  (*altcp_keepalive_disable_fn)(struct altcp_pcb *conn);
+typedef void  (*altcp_keepalive_enable_fn)(struct altcp_pcb *conn, u32_t idle, u32_t intvl, u32_t count);
+#endif
+
+#ifdef LWIP_DEBUG
+typedef enum tcp_state (*altcp_dbg_get_tcp_state_fn)(struct altcp_pcb *conn);
+#endif
+
+struct altcp_functions {
+  altcp_set_poll_fn           set_poll;
+  altcp_recved_fn             recved;
+  altcp_bind_fn               bind;
+  altcp_connect_fn            connect;
+  altcp_listen_fn             listen;
+  altcp_abort_fn              abort;
+  altcp_close_fn              close;
+  altcp_shutdown_fn           shutdown;
+  altcp_write_fn              write;
+  altcp_output_fn             output;
+  altcp_mss_fn                mss;
+  altcp_sndbuf_fn             sndbuf;
+  altcp_sndqueuelen_fn        sndqueuelen;
+  altcp_nagle_disable_fn      nagle_disable;
+  altcp_nagle_enable_fn       nagle_enable;
+  altcp_nagle_disabled_fn     nagle_disabled;
+  altcp_setprio_fn            setprio;
+  altcp_dealloc_fn            dealloc;
+  altcp_get_tcp_addrinfo_fn   addrinfo;
+  altcp_get_ip_fn             getip;
+  altcp_get_port_fn           getport;
+#if LWIP_TCP_KEEPALIVE
+  altcp_keepalive_disable_fn  keepalive_disable;
+  altcp_keepalive_enable_fn   keepalive_enable;
+#endif
+#ifdef LWIP_DEBUG
+  altcp_dbg_get_tcp_state_fn  dbg_get_tcp_state;
+#endif
+};
+
+void  altcp_default_set_poll(struct altcp_pcb *conn, u8_t interval);
+void  altcp_default_recved(struct altcp_pcb *conn, u16_t len);
+err_t altcp_default_bind(struct altcp_pcb *conn, const ip_addr_t *ipaddr, u16_t port);
+err_t altcp_default_shutdown(struct altcp_pcb *conn, int shut_rx, int shut_tx);
+err_t altcp_default_write(struct altcp_pcb *conn, const void *dataptr, u16_t len, u8_t apiflags);
+err_t altcp_default_output(struct altcp_pcb *conn);
+u16_t altcp_default_mss(struct altcp_pcb *conn);
+u16_t altcp_default_sndbuf(struct altcp_pcb *conn);
+u16_t altcp_default_sndqueuelen(struct altcp_pcb *conn);
+void  altcp_default_nagle_disable(struct altcp_pcb *conn);
+void  altcp_default_nagle_enable(struct altcp_pcb *conn);
+int   altcp_default_nagle_disabled(struct altcp_pcb *conn);
+void  altcp_default_setprio(struct altcp_pcb *conn, u8_t prio);
+void  altcp_default_dealloc(struct altcp_pcb *conn);
+err_t altcp_default_get_tcp_addrinfo(struct altcp_pcb *conn, int local, ip_addr_t *addr, u16_t *port);
+ip_addr_t *altcp_default_get_ip(struct altcp_pcb *conn, int local);
+u16_t altcp_default_get_port(struct altcp_pcb *conn, int local);
+#if LWIP_TCP_KEEPALIVE
+void  altcp_default_keepalive_disable(struct altcp_pcb *conn);
+void  altcp_default_keepalive_enable(struct altcp_pcb *conn, u32_t idle, u32_t intvl, u32_t count);
+#endif
+#ifdef LWIP_DEBUG
+enum tcp_state altcp_default_dbg_get_tcp_state(struct altcp_pcb *conn);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_ALTCP */
+
+#endif /* LWIP_HDR_ALTCP_PRIV_H */
diff --git a/lib/lwip/include/lwip/priv/api_msg.h b/lib/lwip/include/lwip/priv/api_msg.h
new file mode 100644
index 0000000000..9e8ffc9ea6
--- /dev/null
+++ b/lib/lwip/include/lwip/priv/api_msg.h
@@ -0,0 +1,272 @@
+/**
+ * @file
+ * netconn API lwIP internal implementations (do not use in application code)
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef LWIP_HDR_API_MSG_H
+#define LWIP_HDR_API_MSG_H
+
+#include "lwip/opt.h"
+
+#include "lwip/arch.h"
+#include "lwip/ip_addr.h"
+#include "lwip/err.h"
+#include "lwip/sys.h"
+#include "lwip/igmp.h"
+#include "lwip/api.h"
+#include "lwip/priv/tcpip_priv.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if LWIP_NETCONN || LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */
+/* Note: Netconn API is always available when sockets are enabled -
+ * sockets are implemented on top of them */
+
+#if LWIP_MPU_COMPATIBLE
+#if LWIP_NETCONN_SEM_PER_THREAD
+#define API_MSG_M_DEF_SEM(m)  *m
+#else
+#define API_MSG_M_DEF_SEM(m)  API_MSG_M_DEF(m)
+#endif
+#else /* LWIP_MPU_COMPATIBLE */
+#define API_MSG_M_DEF_SEM(m)  API_MSG_M_DEF(m)
+#endif /* LWIP_MPU_COMPATIBLE */
+
+/* For the netconn API, these values are use as a bitmask! */
+#define NETCONN_SHUT_RD   1
+#define NETCONN_SHUT_WR   2
+#define NETCONN_SHUT_RDWR (NETCONN_SHUT_RD | NETCONN_SHUT_WR)
+
+/* IP addresses and port numbers are expected to be in
+ * the same byte order as in the corresponding pcb.
+ */
+/** This struct includes everything that is necessary to execute a function
+    for a netconn in another thread context (mainly used to process netconns
+    in the tcpip_thread context to be thread safe). */
+struct api_msg {
+  /** The netconn which to process - always needed: it includes the semaphore
+      which is used to block the application thread until the function finished. */
+  struct netconn *conn;
+  /** The return value of the function executed in tcpip_thread. */
+  err_t err;
+  /** Depending on the executed function, one of these union members is used */
+  union {
+    /** used for lwip_netconn_do_send */
+    struct netbuf *b;
+    /** used for lwip_netconn_do_newconn */
+    struct {
+      u8_t proto;
+    } n;
+    /** used for lwip_netconn_do_bind and lwip_netconn_do_connect */
+    struct {
+      API_MSG_M_DEF_C(ip_addr_t, ipaddr);
+      u16_t port;
+      u8_t if_idx;
+    } bc;
+    /** used for lwip_netconn_do_getaddr */
+    struct {
+      ip_addr_t API_MSG_M_DEF(ipaddr);
+      u16_t API_MSG_M_DEF(port);
+      u8_t local;
+    } ad;
+    /** used for lwip_netconn_do_write */
+    struct {
+      /** current vector to write */
+      const struct netvector *vector;
+      /** number of unwritten vectors */
+      u16_t vector_cnt;
+      /** offset into current vector */
+      size_t vector_off;
+      /** total length across vectors */
+      size_t len;
+      /** offset into total length/output of bytes written when err == ERR_OK */
+      size_t offset;
+      u8_t apiflags;
+#if LWIP_SO_SNDTIMEO
+      u32_t time_started;
+#endif /* LWIP_SO_SNDTIMEO */
+    } w;
+    /** used for lwip_netconn_do_recv */
+    struct {
+      size_t len;
+    } r;
+#if LWIP_TCP
+    /** used for lwip_netconn_do_close (/shutdown) */
+    struct {
+      u8_t shut;
+#if LWIP_SO_SNDTIMEO || LWIP_SO_LINGER
+      u32_t time_started;
+#else /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
+      u8_t polls_left;
+#endif /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
+    } sd;
+#endif /* LWIP_TCP */
+#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD)
+    /** used for lwip_netconn_do_join_leave_group */
+    struct {
+      API_MSG_M_DEF_C(ip_addr_t, multiaddr);
+      API_MSG_M_DEF_C(ip_addr_t, netif_addr);
+      u8_t if_idx;
+      enum netconn_igmp join_or_leave;
+    } jl;
+#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */
+#if TCP_LISTEN_BACKLOG
+    struct {
+      u8_t backlog;
+    } lb;
+#endif /* TCP_LISTEN_BACKLOG */
+  } msg;
+#if LWIP_NETCONN_SEM_PER_THREAD
+  sys_sem_t* op_completed_sem;
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
+};
+
+#if LWIP_NETCONN_SEM_PER_THREAD
+#define LWIP_API_MSG_SEM(msg)          ((msg)->op_completed_sem)
+#else /* LWIP_NETCONN_SEM_PER_THREAD */
+#define LWIP_API_MSG_SEM(msg)          (&(msg)->conn->op_completed)
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
+
+
+#if LWIP_DNS
+/** As lwip_netconn_do_gethostbyname requires more arguments but doesn't require a netconn,
+    it has its own struct (to avoid struct api_msg getting bigger than necessary).
+    lwip_netconn_do_gethostbyname must be called using tcpip_callback instead of tcpip_apimsg
+    (see netconn_gethostbyname). */
+struct dns_api_msg {
+  /** Hostname to query or dotted IP address string */
+#if LWIP_MPU_COMPATIBLE
+  char name[DNS_MAX_NAME_LENGTH];
+#else /* LWIP_MPU_COMPATIBLE */
+  const char *name;
+#endif /* LWIP_MPU_COMPATIBLE */
+  /** The resolved address is stored here */
+  ip_addr_t API_MSG_M_DEF(addr);
+#if LWIP_IPV4 && LWIP_IPV6
+  /** Type of resolve call */
+  u8_t dns_addrtype;
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+  /** This semaphore is posted when the name is resolved, the application thread
+      should wait on it. */
+  sys_sem_t API_MSG_M_DEF_SEM(sem);
+  /** Errors are given back here */
+  err_t API_MSG_M_DEF(err);
+};
+#endif /* LWIP_DNS */
+
+#if LWIP_NETCONN_FULLDUPLEX
+int lwip_netconn_is_deallocated_msg(void *msg);
+#endif
+int lwip_netconn_is_err_msg(void *msg, err_t *err);
+void lwip_netconn_do_newconn         (void *m);
+void lwip_netconn_do_delconn         (void *m);
+void lwip_netconn_do_bind            (void *m);
+void lwip_netconn_do_bind_if         (void *m);
+void lwip_netconn_do_connect         (void *m);
+void lwip_netconn_do_disconnect      (void *m);
+void lwip_netconn_do_listen          (void *m);
+void lwip_netconn_do_send            (void *m);
+void lwip_netconn_do_recv            (void *m);
+#if TCP_LISTEN_BACKLOG
+void lwip_netconn_do_accepted        (void *m);
+#endif /* TCP_LISTEN_BACKLOG */
+void lwip_netconn_do_write           (void *m);
+void lwip_netconn_do_getaddr         (void *m);
+void lwip_netconn_do_close           (void *m);
+void lwip_netconn_do_shutdown        (void *m);
+#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD)
+void lwip_netconn_do_join_leave_group(void *m);
+void lwip_netconn_do_join_leave_group_netif(void *m);
+#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */
+
+#if LWIP_DNS
+void lwip_netconn_do_gethostbyname(void *arg);
+#endif /* LWIP_DNS */
+
+struct netconn* netconn_alloc(enum netconn_type t, netconn_callback callback);
+void netconn_free(struct netconn *conn);
+
+#endif /* LWIP_NETCONN || LWIP_SOCKET */
+
+#if LWIP_NETIF_API /* don't build if not configured for use in lwipopts.h */
+
+/* netifapi related lwIP internal definitions */
+
+#if LWIP_MPU_COMPATIBLE
+#define NETIFAPI_IPADDR_DEF(type, m)  type m
+#else /* LWIP_MPU_COMPATIBLE */
+#define NETIFAPI_IPADDR_DEF(type, m)  const type * m
+#endif /* LWIP_MPU_COMPATIBLE */
+
+typedef void (*netifapi_void_fn)(struct netif *netif);
+typedef err_t (*netifapi_errt_fn)(struct netif *netif);
+
+struct netifapi_msg {
+  struct tcpip_api_call_data call;
+  struct netif *netif;
+  union {
+    struct {
+#if LWIP_IPV4
+      NETIFAPI_IPADDR_DEF(ip4_addr_t, ipaddr);
+      NETIFAPI_IPADDR_DEF(ip4_addr_t, netmask);
+      NETIFAPI_IPADDR_DEF(ip4_addr_t, gw);
+#endif /* LWIP_IPV4 */
+      void *state;
+      netif_init_fn init;
+      netif_input_fn input;
+    } add;
+    struct {
+      netifapi_void_fn voidfunc;
+      netifapi_errt_fn errtfunc;
+    } common;
+    struct {
+#if LWIP_MPU_COMPATIBLE
+      char name[NETIF_NAMESIZE];
+#else /* LWIP_MPU_COMPATIBLE */
+      char *name;
+#endif /* LWIP_MPU_COMPATIBLE */
+      u8_t index;
+    } ifs;
+  } msg;
+};
+
+#endif /* LWIP_NETIF_API */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_API_MSG_H */
diff --git a/lib/lwip/include/lwip/priv/mem_priv.h b/lib/lwip/include/lwip/priv/mem_priv.h
new file mode 100644
index 0000000000..8630d75419
--- /dev/null
+++ b/lib/lwip/include/lwip/priv/mem_priv.h
@@ -0,0 +1,84 @@
+/**
+ * @file
+ * lwIP internal memory implementations (do not use in application code)
+ */
+
+/*
+ * Copyright (c) 2018 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt <goldsimon@gmx.de>
+ *
+ */
+
+#ifndef LWIP_HDR_MEM_PRIV_H
+#define LWIP_HDR_MEM_PRIV_H
+
+#include "lwip/opt.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "lwip/mem.h"
+
+#if MEM_OVERFLOW_CHECK || MEMP_OVERFLOW_CHECK
+/* if MEM_OVERFLOW_CHECK or MEMP_OVERFLOW_CHECK is turned on, we reserve some
+ * bytes at the beginning and at the end of each element, initialize them as
+ * 0xcd and check them later.
+ * If MEM(P)_OVERFLOW_CHECK is >= 2, on every call to mem(p)_malloc or mem(p)_free,
+ * every single element in each pool/heap is checked!
+ * This is VERY SLOW but also very helpful.
+ * MEM_SANITY_REGION_BEFORE and MEM_SANITY_REGION_AFTER can be overridden in
+ * lwipopts.h to change the amount reserved for checking. */
+#ifndef MEM_SANITY_REGION_BEFORE
+#define MEM_SANITY_REGION_BEFORE  16
+#endif /* MEM_SANITY_REGION_BEFORE*/
+#if MEM_SANITY_REGION_BEFORE > 0
+#define MEM_SANITY_REGION_BEFORE_ALIGNED    LWIP_MEM_ALIGN_SIZE(MEM_SANITY_REGION_BEFORE)
+#else
+#define MEM_SANITY_REGION_BEFORE_ALIGNED    0
+#endif /* MEM_SANITY_REGION_BEFORE*/
+#ifndef MEM_SANITY_REGION_AFTER
+#define MEM_SANITY_REGION_AFTER   16
+#endif /* MEM_SANITY_REGION_AFTER*/
+#if MEM_SANITY_REGION_AFTER > 0
+#define MEM_SANITY_REGION_AFTER_ALIGNED     LWIP_MEM_ALIGN_SIZE(MEM_SANITY_REGION_AFTER)
+#else
+#define MEM_SANITY_REGION_AFTER_ALIGNED     0
+#endif /* MEM_SANITY_REGION_AFTER*/
+
+void mem_overflow_init_raw(void *p, size_t size);
+void mem_overflow_check_raw(void *p, size_t size, const char *descr1, const char *descr2);
+
+#endif /* MEM_OVERFLOW_CHECK || MEMP_OVERFLOW_CHECK */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_MEMP_PRIV_H */
diff --git a/lib/lwip/include/lwip/priv/memp_priv.h b/lib/lwip/include/lwip/priv/memp_priv.h
new file mode 100644
index 0000000000..1f14cb162b
--- /dev/null
+++ b/lib/lwip/include/lwip/priv/memp_priv.h
@@ -0,0 +1,161 @@
+/**
+ * @file
+ * memory pools lwIP internal implementations (do not use in application code)
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#ifndef LWIP_HDR_MEMP_PRIV_H
+#define LWIP_HDR_MEMP_PRIV_H
+
+#include "lwip/opt.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "lwip/mem.h"
+#include "lwip/priv/mem_priv.h"
+
+#if MEMP_OVERFLOW_CHECK
+
+
+/* MEMP_SIZE: save space for struct memp and for sanity check */
+#define MEMP_SIZE          (LWIP_MEM_ALIGN_SIZE(sizeof(struct memp)) + MEM_SANITY_REGION_BEFORE_ALIGNED)
+#define MEMP_ALIGN_SIZE(x) (LWIP_MEM_ALIGN_SIZE(x) + MEM_SANITY_REGION_AFTER_ALIGNED)
+
+#else /* MEMP_OVERFLOW_CHECK */
+
+/* No sanity checks
+ * We don't need to preserve the struct memp while not allocated, so we
+ * can save a little space and set MEMP_SIZE to 0.
+ */
+#define MEMP_SIZE           0
+#define MEMP_ALIGN_SIZE(x) (LWIP_MEM_ALIGN_SIZE(x))
+
+#endif /* MEMP_OVERFLOW_CHECK */
+
+#if !MEMP_MEM_MALLOC || MEMP_OVERFLOW_CHECK
+struct memp {
+  struct memp *next;
+#if MEMP_OVERFLOW_CHECK
+  const char *file;
+  int line;
+#endif /* MEMP_OVERFLOW_CHECK */
+};
+#endif /* !MEMP_MEM_MALLOC || MEMP_OVERFLOW_CHECK */
+
+#if MEM_USE_POOLS && MEMP_USE_CUSTOM_POOLS
+/* Use a helper type to get the start and end of the user "memory pools" for mem_malloc */
+typedef enum {
+    /* Get the first (via:
+       MEMP_POOL_HELPER_START = ((u8_t) 1*MEMP_POOL_A + 0*MEMP_POOL_B + 0*MEMP_POOL_C + 0)*/
+    MEMP_POOL_HELPER_FIRST = ((u8_t)
+#define LWIP_MEMPOOL(name,num,size,desc)
+#define LWIP_MALLOC_MEMPOOL_START 1
+#define LWIP_MALLOC_MEMPOOL(num, size) * MEMP_POOL_##size + 0
+#define LWIP_MALLOC_MEMPOOL_END
+#include "lwip/priv/memp_std.h"
+    ) ,
+    /* Get the last (via:
+       MEMP_POOL_HELPER_END = ((u8_t) 0 + MEMP_POOL_A*0 + MEMP_POOL_B*0 + MEMP_POOL_C*1) */
+    MEMP_POOL_HELPER_LAST = ((u8_t)
+#define LWIP_MEMPOOL(name,num,size,desc)
+#define LWIP_MALLOC_MEMPOOL_START
+#define LWIP_MALLOC_MEMPOOL(num, size) 0 + MEMP_POOL_##size *
+#define LWIP_MALLOC_MEMPOOL_END 1
+#include "lwip/priv/memp_std.h"
+    )
+} memp_pool_helper_t;
+
+/* The actual start and stop values are here (cast them over)
+   We use this helper type and these defines so we can avoid using const memp_t values */
+#define MEMP_POOL_FIRST ((memp_t) MEMP_POOL_HELPER_FIRST)
+#define MEMP_POOL_LAST   ((memp_t) MEMP_POOL_HELPER_LAST)
+#endif /* MEM_USE_POOLS && MEMP_USE_CUSTOM_POOLS */
+
+/** Memory pool descriptor */
+struct memp_desc {
+#if defined(LWIP_DEBUG) || MEMP_OVERFLOW_CHECK || LWIP_STATS_DISPLAY
+  /** Textual description */
+  const char *desc;
+#endif /* LWIP_DEBUG || MEMP_OVERFLOW_CHECK || LWIP_STATS_DISPLAY */
+#if MEMP_STATS
+  /** Statistics */
+  struct stats_mem *stats;
+#endif
+
+  /** Element size */
+  u16_t size;
+
+#if !MEMP_MEM_MALLOC
+  /** Number of elements */
+  u16_t num;
+
+  /** Base address */
+  u8_t *base;
+
+  /** First free element of each pool. Elements form a linked list. */
+  struct memp **tab;
+#endif /* MEMP_MEM_MALLOC */
+};
+
+#if defined(LWIP_DEBUG) || MEMP_OVERFLOW_CHECK || LWIP_STATS_DISPLAY
+#define DECLARE_LWIP_MEMPOOL_DESC(desc) (desc),
+#else
+#define DECLARE_LWIP_MEMPOOL_DESC(desc)
+#endif
+
+#if MEMP_STATS
+#define LWIP_MEMPOOL_DECLARE_STATS_INSTANCE(name) static struct stats_mem name;
+#define LWIP_MEMPOOL_DECLARE_STATS_REFERENCE(name) &name,
+#else
+#define LWIP_MEMPOOL_DECLARE_STATS_INSTANCE(name)
+#define LWIP_MEMPOOL_DECLARE_STATS_REFERENCE(name)
+#endif
+
+void memp_init_pool(const struct memp_desc *desc);
+
+#if MEMP_OVERFLOW_CHECK
+void *memp_malloc_pool_fn(const struct memp_desc* desc, const char* file, const int line);
+#define memp_malloc_pool(d) memp_malloc_pool_fn((d), __FILE__, __LINE__)
+#else
+void *memp_malloc_pool(const struct memp_desc *desc);
+#endif
+void  memp_free_pool(const struct memp_desc* desc, void *mem);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_MEMP_PRIV_H */
diff --git a/lib/lwip/include/lwip/priv/memp_std.h b/lib/lwip/include/lwip/priv/memp_std.h
new file mode 100644
index 0000000000..669ad4d7e6
--- /dev/null
+++ b/lib/lwip/include/lwip/priv/memp_std.h
@@ -0,0 +1,153 @@
+/**
+ * @file
+ * lwIP internal memory pools (do not use in application code)
+ * This file is deliberately included multiple times: once with empty
+ * definition of LWIP_MEMPOOL() to handle all includes and multiple times
+ * to build up various lists of mem pools.
+ */
+
+/*
+ * SETUP: Make sure we define everything we will need.
+ *
+ * We have create three types of pools:
+ *   1) MEMPOOL - standard pools
+ *   2) MALLOC_MEMPOOL - to be used by mem_malloc in mem.c
+ *   3) PBUF_MEMPOOL - a mempool of pbuf's, so include space for the pbuf struct
+ *
+ * If the include'r doesn't require any special treatment of each of the types
+ * above, then will declare #2 & #3 to be just standard mempools.
+ */
+#ifndef LWIP_MALLOC_MEMPOOL
+/* This treats "malloc pools" just like any other pool.
+   The pools are a little bigger to provide 'size' as the amount of user data. */
+#define LWIP_MALLOC_MEMPOOL(num, size) LWIP_MEMPOOL(POOL_##size, num, (size + LWIP_MEM_ALIGN_SIZE(sizeof(struct memp_malloc_helper))), "MALLOC_"#size)
+#define LWIP_MALLOC_MEMPOOL_START
+#define LWIP_MALLOC_MEMPOOL_END
+#endif /* LWIP_MALLOC_MEMPOOL */
+
+#ifndef LWIP_PBUF_MEMPOOL
+/* This treats "pbuf pools" just like any other pool.
+ * Allocates buffers for a pbuf struct AND a payload size */
+#define LWIP_PBUF_MEMPOOL(name, num, payload, desc) LWIP_MEMPOOL(name, num, (LWIP_MEM_ALIGN_SIZE(sizeof(struct pbuf)) + LWIP_MEM_ALIGN_SIZE(payload)), desc)
+#endif /* LWIP_PBUF_MEMPOOL */
+
+
+/*
+ * A list of internal pools used by LWIP.
+ *
+ * LWIP_MEMPOOL(pool_name, number_elements, element_size, pool_description)
+ *     creates a pool name MEMP_pool_name. description is used in stats.c
+ */
+#if LWIP_RAW
+LWIP_MEMPOOL(RAW_PCB,        MEMP_NUM_RAW_PCB,         sizeof(struct raw_pcb),        "RAW_PCB")
+#endif /* LWIP_RAW */
+
+#if LWIP_UDP
+LWIP_MEMPOOL(UDP_PCB,        MEMP_NUM_UDP_PCB,         sizeof(struct udp_pcb),        "UDP_PCB")
+#endif /* LWIP_UDP */
+
+#if LWIP_TCP
+LWIP_MEMPOOL(TCP_PCB,        MEMP_NUM_TCP_PCB,         sizeof(struct tcp_pcb),        "TCP_PCB")
+LWIP_MEMPOOL(TCP_PCB_LISTEN, MEMP_NUM_TCP_PCB_LISTEN,  sizeof(struct tcp_pcb_listen), "TCP_PCB_LISTEN")
+LWIP_MEMPOOL(TCP_SEG,        MEMP_NUM_TCP_SEG,         sizeof(struct tcp_seg),        "TCP_SEG")
+#endif /* LWIP_TCP */
+
+#if LWIP_ALTCP && LWIP_TCP
+LWIP_MEMPOOL(ALTCP_PCB,      MEMP_NUM_ALTCP_PCB,       sizeof(struct altcp_pcb),      "ALTCP_PCB")
+#endif /* LWIP_ALTCP && LWIP_TCP */
+
+#if LWIP_IPV4 && IP_REASSEMBLY
+LWIP_MEMPOOL(REASSDATA,      MEMP_NUM_REASSDATA,       sizeof(struct ip_reassdata),   "REASSDATA")
+#endif /* LWIP_IPV4 && IP_REASSEMBLY */
+#if (IP_FRAG && !LWIP_NETIF_TX_SINGLE_PBUF) || (LWIP_IPV6 && LWIP_IPV6_FRAG)
+LWIP_MEMPOOL(FRAG_PBUF,      MEMP_NUM_FRAG_PBUF,       sizeof(struct pbuf_custom_ref),"FRAG_PBUF")
+#endif /* IP_FRAG && !LWIP_NETIF_TX_SINGLE_PBUF || (LWIP_IPV6 && LWIP_IPV6_FRAG) */
+
+#if LWIP_NETCONN || LWIP_SOCKET
+LWIP_MEMPOOL(NETBUF,         MEMP_NUM_NETBUF,          sizeof(struct netbuf),         "NETBUF")
+LWIP_MEMPOOL(NETCONN,        MEMP_NUM_NETCONN,         sizeof(struct netconn),        "NETCONN")
+#endif /* LWIP_NETCONN || LWIP_SOCKET */
+
+#if NO_SYS==0
+LWIP_MEMPOOL(TCPIP_MSG_API,  MEMP_NUM_TCPIP_MSG_API,   sizeof(struct tcpip_msg),      "TCPIP_MSG_API")
+#if LWIP_MPU_COMPATIBLE
+LWIP_MEMPOOL(API_MSG,        MEMP_NUM_API_MSG,         sizeof(struct api_msg),        "API_MSG")
+#if LWIP_DNS
+LWIP_MEMPOOL(DNS_API_MSG,    MEMP_NUM_DNS_API_MSG,     sizeof(struct dns_api_msg),    "DNS_API_MSG")
+#endif
+#if LWIP_SOCKET && !LWIP_TCPIP_CORE_LOCKING
+LWIP_MEMPOOL(SOCKET_SETGETSOCKOPT_DATA, MEMP_NUM_SOCKET_SETGETSOCKOPT_DATA, sizeof(struct lwip_setgetsockopt_data), "SOCKET_SETGETSOCKOPT_DATA")
+#endif
+#if LWIP_SOCKET && (LWIP_SOCKET_SELECT || LWIP_SOCKET_POLL)
+LWIP_MEMPOOL(SELECT_CB,      MEMP_NUM_SELECT_CB,       sizeof(struct lwip_select_cb), "SELECT_CB")
+#endif /* LWIP_SOCKET && (LWIP_SOCKET_SELECT || LWIP_SOCKET_POLL) */
+#if LWIP_NETIF_API
+LWIP_MEMPOOL(NETIFAPI_MSG,   MEMP_NUM_NETIFAPI_MSG,    sizeof(struct netifapi_msg),   "NETIFAPI_MSG")
+#endif
+#endif /* LWIP_MPU_COMPATIBLE */
+#if !LWIP_TCPIP_CORE_LOCKING_INPUT
+LWIP_MEMPOOL(TCPIP_MSG_INPKT,MEMP_NUM_TCPIP_MSG_INPKT, sizeof(struct tcpip_msg),      "TCPIP_MSG_INPKT")
+#endif /* !LWIP_TCPIP_CORE_LOCKING_INPUT */
+#endif /* NO_SYS==0 */
+
+#if LWIP_IPV4 && LWIP_ARP && ARP_QUEUEING
+LWIP_MEMPOOL(ARP_QUEUE,      MEMP_NUM_ARP_QUEUE,       sizeof(struct etharp_q_entry), "ARP_QUEUE")
+#endif /* LWIP_IPV4 && LWIP_ARP && ARP_QUEUEING */
+
+#if LWIP_IGMP
+LWIP_MEMPOOL(IGMP_GROUP,     MEMP_NUM_IGMP_GROUP,      sizeof(struct igmp_group),     "IGMP_GROUP")
+#endif /* LWIP_IGMP */
+
+#if LWIP_TIMERS && !LWIP_TIMERS_CUSTOM
+LWIP_MEMPOOL(SYS_TIMEOUT,    MEMP_NUM_SYS_TIMEOUT,     sizeof(struct sys_timeo),      "SYS_TIMEOUT")
+#endif /* LWIP_TIMERS && !LWIP_TIMERS_CUSTOM */
+
+#if LWIP_DNS && LWIP_SOCKET
+LWIP_MEMPOOL(NETDB,          MEMP_NUM_NETDB,           NETDB_ELEM_SIZE,               "NETDB")
+#endif /* LWIP_DNS && LWIP_SOCKET */
+#if LWIP_DNS && DNS_LOCAL_HOSTLIST && DNS_LOCAL_HOSTLIST_IS_DYNAMIC
+LWIP_MEMPOOL(LOCALHOSTLIST,  MEMP_NUM_LOCALHOSTLIST,   LOCALHOSTLIST_ELEM_SIZE,       "LOCALHOSTLIST")
+#endif /* LWIP_DNS && DNS_LOCAL_HOSTLIST && DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
+
+#if LWIP_IPV6 && LWIP_ND6_QUEUEING
+LWIP_MEMPOOL(ND6_QUEUE,      MEMP_NUM_ND6_QUEUE,       sizeof(struct nd6_q_entry),    "ND6_QUEUE")
+#endif /* LWIP_IPV6 && LWIP_ND6_QUEUEING */
+
+#if LWIP_IPV6 && LWIP_IPV6_REASS
+LWIP_MEMPOOL(IP6_REASSDATA,  MEMP_NUM_REASSDATA,       sizeof(struct ip6_reassdata),  "IP6_REASSDATA")
+#endif /* LWIP_IPV6 && LWIP_IPV6_REASS */
+
+#if LWIP_IPV6 && LWIP_IPV6_MLD
+LWIP_MEMPOOL(MLD6_GROUP,     MEMP_NUM_MLD6_GROUP,      sizeof(struct mld_group),      "MLD6_GROUP")
+#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
+
+
+/*
+ * A list of pools of pbuf's used by LWIP.
+ *
+ * LWIP_PBUF_MEMPOOL(pool_name, number_elements, pbuf_payload_size, pool_description)
+ *     creates a pool name MEMP_pool_name. description is used in stats.c
+ *     This allocates enough space for the pbuf struct and a payload.
+ *     (Example: pbuf_payload_size=0 allocates only size for the struct)
+ */
+LWIP_MEMPOOL(PBUF,           MEMP_NUM_PBUF,            sizeof(struct pbuf),           "PBUF_REF/ROM")
+LWIP_PBUF_MEMPOOL(PBUF_POOL, PBUF_POOL_SIZE,           PBUF_POOL_BUFSIZE,             "PBUF_POOL")
+
+
+/*
+ * Allow for user-defined pools; this must be explicitly set in lwipopts.h
+ * since the default is to NOT look for lwippools.h
+ */
+#if MEMP_USE_CUSTOM_POOLS
+#include "lwippools.h"
+#endif /* MEMP_USE_CUSTOM_POOLS */
+
+/*
+ * REQUIRED CLEANUP: Clear up so we don't get "multiply defined" error later
+ * (#undef is ignored for something that is not defined)
+ */
+#undef LWIP_MEMPOOL
+#undef LWIP_MALLOC_MEMPOOL
+#undef LWIP_MALLOC_MEMPOOL_START
+#undef LWIP_MALLOC_MEMPOOL_END
+#undef LWIP_PBUF_MEMPOOL
diff --git a/lib/lwip/include/lwip/priv/nd6_priv.h b/lib/lwip/include/lwip/priv/nd6_priv.h
new file mode 100644
index 0000000000..75d5f02d38
--- /dev/null
+++ b/lib/lwip/include/lwip/priv/nd6_priv.h
@@ -0,0 +1,143 @@
+/**
+ * @file
+ *
+ * Neighbor discovery and stateless address autoconfiguration for IPv6.
+ * Aims to be compliant with RFC 4861 (Neighbor discovery) and RFC 4862
+ * (Address autoconfiguration).
+ */
+
+/*
+ * Copyright (c) 2010 Inico Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Ivan Delamer <delamer@inicotech.com>
+ *
+ *
+ * Please coordinate changes and requests with Ivan Delamer
+ * <delamer@inicotech.com>
+ */
+
+#ifndef LWIP_HDR_ND6_PRIV_H
+#define LWIP_HDR_ND6_PRIV_H
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV6  /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/pbuf.h"
+#include "lwip/ip6_addr.h"
+#include "lwip/netif.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if LWIP_ND6_QUEUEING
+/** struct for queueing outgoing packets for unknown address
+  * defined here to be accessed by memp.h
+  */
+struct nd6_q_entry {
+  struct nd6_q_entry *next;
+  struct pbuf *p;
+};
+#endif /* LWIP_ND6_QUEUEING */
+
+/** Struct for tables. */
+struct nd6_neighbor_cache_entry {
+  ip6_addr_t next_hop_address;
+  struct netif *netif;
+  u8_t lladdr[NETIF_MAX_HWADDR_LEN];
+  /*u32_t pmtu;*/
+#if LWIP_ND6_QUEUEING
+  /** Pointer to queue of pending outgoing packets on this entry. */
+  struct nd6_q_entry *q;
+#else /* LWIP_ND6_QUEUEING */
+  /** Pointer to a single pending outgoing packet on this entry. */
+  struct pbuf *q;
+#endif /* LWIP_ND6_QUEUEING */
+  u8_t state;
+  u8_t isrouter;
+  union {
+    u32_t reachable_time; /* in seconds */
+    u32_t delay_time;     /* ticks (ND6_TMR_INTERVAL) */
+    u32_t probes_sent;
+    u32_t stale_time;     /* ticks (ND6_TMR_INTERVAL) */
+  } counter;
+};
+
+struct nd6_destination_cache_entry {
+  ip6_addr_t destination_addr;
+  ip6_addr_t next_hop_addr;
+  u16_t pmtu;
+  u8_t cached_neighbor_idx;
+  u32_t age;
+};
+
+struct nd6_prefix_list_entry {
+  ip6_addr_t prefix;
+  struct netif *netif;
+  u32_t invalidation_timer; /* in seconds */
+};
+
+struct nd6_router_list_entry {
+  struct nd6_neighbor_cache_entry *neighbor_entry;
+  u32_t invalidation_timer; /* in seconds */
+  u8_t flags;
+};
+
+enum nd6_neighbor_cache_entry_state {
+  ND6_NO_ENTRY = 0,
+  ND6_INCOMPLETE,
+  ND6_REACHABLE,
+  ND6_STALE,
+  ND6_DELAY,
+  ND6_PROBE
+};
+
+#define ND6_HOPLIM 255 /* maximum hop limit, required in all ND packets */
+
+#define ND6_2HRS 7200 /* two hours, expressed in number of seconds */
+
+/* Router tables. */
+/* @todo make these static? and entries accessible through API? */
+extern struct nd6_neighbor_cache_entry neighbor_cache[];
+extern struct nd6_destination_cache_entry destination_cache[];
+extern struct nd6_prefix_list_entry prefix_list[];
+extern struct nd6_router_list_entry default_router_list[];
+
+/* Default values, can be updated by a RA message. */
+extern u32_t reachable_time;
+extern u32_t retrans_timer;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_IPV6 */
+
+#endif /* LWIP_HDR_ND6_PRIV_H */
diff --git a/lib/lwip/include/lwip/priv/raw_priv.h b/lib/lwip/include/lwip/priv/raw_priv.h
new file mode 100644
index 0000000000..d4561d4f32
--- /dev/null
+++ b/lib/lwip/include/lwip/priv/raw_priv.h
@@ -0,0 +1,69 @@
+/**
+ * @file
+ * raw API internal implementations (do not use in application code)
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef LWIP_HDR_RAW_PRIV_H
+#define LWIP_HDR_RAW_PRIV_H
+
+#include "lwip/opt.h"
+
+#if LWIP_RAW /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/raw.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** return codes for raw_input */
+typedef enum raw_input_state
+{
+  RAW_INPUT_NONE = 0, /* pbuf did not match any pcbs */
+  RAW_INPUT_EATEN,    /* pbuf handed off and delivered to pcb */
+  RAW_INPUT_DELIVERED /* pbuf only delivered to pcb (pbuf can still be referenced) */
+} raw_input_state_t;
+
+/* The following functions are the lower layer interface to RAW. */
+raw_input_state_t raw_input(struct pbuf *p, struct netif *inp);
+
+void raw_netif_ip_addr_changed(const ip_addr_t* old_addr, const ip_addr_t* new_addr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_RAW */
+
+#endif /* LWIP_HDR_RAW_PRIV_H */
diff --git a/lib/lwip/include/lwip/priv/sockets_priv.h b/lib/lwip/include/lwip/priv/sockets_priv.h
new file mode 100644
index 0000000000..c604734c25
--- /dev/null
+++ b/lib/lwip/include/lwip/priv/sockets_priv.h
@@ -0,0 +1,175 @@
+/**
+ * @file
+ * Sockets API internal implementations (do not use in application code)
+ */
+
+/*
+ * Copyright (c) 2017 Joel Cunningham, Garmin International, Inc. <joel.cunningham@garmin.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Joel Cunningham <joel.cunningham@me.com>
+ *
+ */
+#ifndef LWIP_HDR_SOCKETS_PRIV_H
+#define LWIP_HDR_SOCKETS_PRIV_H
+
+#include "lwip/opt.h"
+
+#if LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/err.h"
+#include "lwip/sockets.h"
+#include "lwip/sys.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define NUM_SOCKETS MEMP_NUM_NETCONN
+
+/** This is overridable for the rare case where more than 255 threads
+ * select on the same socket...
+ */
+#ifndef SELWAIT_T
+#define SELWAIT_T u8_t
+#endif
+
+union lwip_sock_lastdata {
+  struct netbuf *netbuf;
+  struct pbuf *pbuf;
+};
+
+/** Contains all internal pointers and states used for a socket */
+struct lwip_sock {
+  /** sockets currently are built on netconns, each socket has one netconn */
+  struct netconn *conn;
+  /** data that was left from the previous read */
+  union lwip_sock_lastdata lastdata;
+#if LWIP_SOCKET_SELECT || LWIP_SOCKET_POLL
+  /** number of times data was received, set by event_callback(),
+      tested by the receive and select functions */
+  s16_t rcvevent;
+  /** number of times data was ACKed (free send buffer), set by event_callback(),
+      tested by select */
+  u16_t sendevent;
+  /** error happened for this socket, set by event_callback(), tested by select */
+  u16_t errevent;
+  /** counter of how many threads are waiting for this socket using select */
+  SELWAIT_T select_waiting;
+#endif /* LWIP_SOCKET_SELECT || LWIP_SOCKET_POLL */
+#if LWIP_NETCONN_FULLDUPLEX
+  /* counter of how many threads are using a struct lwip_sock (not the 'int') */
+  u8_t fd_used;
+  /* status of pending close/delete actions */
+  u8_t fd_free_pending;
+#define LWIP_SOCK_FD_FREE_TCP  1
+#define LWIP_SOCK_FD_FREE_FREE 2
+#endif
+};
+
+#ifndef set_errno
+#define set_errno(err) do { if (err) { errno = (err); } } while(0)
+#endif
+
+#if !LWIP_TCPIP_CORE_LOCKING
+/** Maximum optlen used by setsockopt/getsockopt */
+#define LWIP_SETGETSOCKOPT_MAXOPTLEN LWIP_MAX(16, sizeof(struct ifreq))
+
+/** This struct is used to pass data to the set/getsockopt_impl
+ * functions running in tcpip_thread context (only a void* is allowed) */
+struct lwip_setgetsockopt_data {
+  /** socket index for which to change options */
+  int s;
+  /** level of the option to process */
+  int level;
+  /** name of the option to process */
+  int optname;
+  /** set: value to set the option to
+    * get: value of the option is stored here */
+#if LWIP_MPU_COMPATIBLE
+  u8_t optval[LWIP_SETGETSOCKOPT_MAXOPTLEN];
+#else
+  union {
+    void *p;
+    const void *pc;
+  } optval;
+#endif
+  /** size of *optval */
+  socklen_t optlen;
+  /** if an error occurs, it is temporarily stored here */
+  int err;
+  /** semaphore to wake up the calling task */
+  void* completed_sem;
+};
+#endif /* !LWIP_TCPIP_CORE_LOCKING */
+
+#ifdef __cplusplus
+}
+#endif
+
+struct lwip_sock* lwip_socket_dbg_get_socket(int fd);
+
+#if LWIP_SOCKET_SELECT || LWIP_SOCKET_POLL
+
+#if LWIP_NETCONN_SEM_PER_THREAD
+#define SELECT_SEM_T        sys_sem_t*
+#define SELECT_SEM_PTR(sem) (sem)
+#else /* LWIP_NETCONN_SEM_PER_THREAD */
+#define SELECT_SEM_T        sys_sem_t
+#define SELECT_SEM_PTR(sem) (&(sem))
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
+
+/** Description for a task waiting in select */
+struct lwip_select_cb {
+  /** Pointer to the next waiting task */
+  struct lwip_select_cb *next;
+  /** Pointer to the previous waiting task */
+  struct lwip_select_cb *prev;
+#if LWIP_SOCKET_SELECT
+  /** readset passed to select */
+  fd_set *readset;
+  /** writeset passed to select */
+  fd_set *writeset;
+  /** unimplemented: exceptset passed to select */
+  fd_set *exceptset;
+#endif /* LWIP_SOCKET_SELECT */
+#if LWIP_SOCKET_POLL
+  /** fds passed to poll; NULL if select */
+  struct pollfd *poll_fds;
+  /** nfds passed to poll; 0 if select */
+  nfds_t poll_nfds;
+#endif /* LWIP_SOCKET_POLL */
+  /** don't signal the same semaphore twice: set to 1 when signalled */
+  int sem_signalled;
+  /** semaphore to wake up a task waiting for select */
+  SELECT_SEM_T sem;
+};
+#endif /* LWIP_SOCKET_SELECT || LWIP_SOCKET_POLL */
+
+#endif /* LWIP_SOCKET */
+
+#endif /* LWIP_HDR_SOCKETS_PRIV_H */
diff --git a/lib/lwip/include/lwip/priv/tcp_priv.h b/lib/lwip/include/lwip/priv/tcp_priv.h
new file mode 100644
index 0000000000..a8e87e5931
--- /dev/null
+++ b/lib/lwip/include/lwip/priv/tcp_priv.h
@@ -0,0 +1,523 @@
+/**
+ * @file
+ * TCP internal implementations (do not use in application code)
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef LWIP_HDR_TCP_PRIV_H
+#define LWIP_HDR_TCP_PRIV_H
+
+#include "lwip/opt.h"
+
+#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/tcp.h"
+#include "lwip/mem.h"
+#include "lwip/pbuf.h"
+#include "lwip/ip.h"
+#include "lwip/icmp.h"
+#include "lwip/err.h"
+#include "lwip/ip6.h"
+#include "lwip/ip6_addr.h"
+#include "lwip/prot/tcp.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Functions for interfacing with TCP: */
+
+/* Lower layer interface to TCP: */
+void             tcp_init    (void);  /* Initialize this module. */
+void             tcp_tmr     (void);  /* Must be called every
+                                         TCP_TMR_INTERVAL
+                                         ms. (Typically 250 ms). */
+/* It is also possible to call these two functions at the right
+   intervals (instead of calling tcp_tmr()). */
+void             tcp_slowtmr (void);
+void             tcp_fasttmr (void);
+
+/* Call this from a netif driver (watch out for threading issues!) that has
+   returned a memory error on transmit and now has free buffers to send more.
+   This iterates all active pcbs that had an error and tries to call
+   tcp_output, so use this with care as it might slow down the system. */
+void             tcp_txnow   (void);
+
+/* Only used by IP to pass a TCP segment to TCP: */
+void             tcp_input   (struct pbuf *p, struct netif *inp);
+/* Used within the TCP code only: */
+struct tcp_pcb * tcp_alloc   (u8_t prio);
+void             tcp_free    (struct tcp_pcb *pcb);
+void             tcp_abandon (struct tcp_pcb *pcb, int reset);
+err_t            tcp_send_empty_ack(struct tcp_pcb *pcb);
+err_t            tcp_rexmit  (struct tcp_pcb *pcb);
+err_t            tcp_rexmit_rto_prepare(struct tcp_pcb *pcb);
+void             tcp_rexmit_rto_commit(struct tcp_pcb *pcb);
+void             tcp_rexmit_rto  (struct tcp_pcb *pcb);
+void             tcp_rexmit_fast (struct tcp_pcb *pcb);
+u32_t            tcp_update_rcv_ann_wnd(struct tcp_pcb *pcb);
+err_t            tcp_process_refused_data(struct tcp_pcb *pcb);
+
+/**
+ * This is the Nagle algorithm: try to combine user data to send as few TCP
+ * segments as possible. Only send if
+ * - no previously transmitted data on the connection remains unacknowledged or
+ * - the TF_NODELAY flag is set (nagle algorithm turned off for this pcb) or
+ * - the only unsent segment is at least pcb->mss bytes long (or there is more
+ *   than one unsent segment - with lwIP, this can happen although unsent->len < mss)
+ * - or if we are in fast-retransmit (TF_INFR)
+ */
+#define tcp_do_output_nagle(tpcb) ((((tpcb)->unacked == NULL) || \
+                            ((tpcb)->flags & (TF_NODELAY | TF_INFR)) || \
+                            (((tpcb)->unsent != NULL) && (((tpcb)->unsent->next != NULL) || \
+                              ((tpcb)->unsent->len >= (tpcb)->mss))) || \
+                            ((tcp_sndbuf(tpcb) == 0) || (tcp_sndqueuelen(tpcb) >= TCP_SND_QUEUELEN)) \
+                            ) ? 1 : 0)
+#define tcp_output_nagle(tpcb) (tcp_do_output_nagle(tpcb) ? tcp_output(tpcb) : ERR_OK)
+
+
+#define TCP_SEQ_LT(a,b)     (((u32_t)((u32_t)(a) - (u32_t)(b)) & 0x80000000u) != 0)
+#define TCP_SEQ_LEQ(a,b)    (!(TCP_SEQ_LT(b,a)))
+#define TCP_SEQ_GT(a,b)     TCP_SEQ_LT(b,a)
+#define TCP_SEQ_GEQ(a,b)    TCP_SEQ_LEQ(b,a)
+/* is b<=a<=c? */
+#define TCP_SEQ_BETWEEN(a,b,c) (TCP_SEQ_GEQ(a,b) && TCP_SEQ_LEQ(a,c))
+
+#ifndef TCP_TMR_INTERVAL
+#define TCP_TMR_INTERVAL       250  /* The TCP timer interval in milliseconds. */
+#endif /* TCP_TMR_INTERVAL */
+
+#ifndef TCP_FAST_INTERVAL
+#define TCP_FAST_INTERVAL      TCP_TMR_INTERVAL /* the fine grained timeout in milliseconds */
+#endif /* TCP_FAST_INTERVAL */
+
+#ifndef TCP_SLOW_INTERVAL
+#define TCP_SLOW_INTERVAL      (2*TCP_TMR_INTERVAL)  /* the coarse grained timeout in milliseconds */
+#endif /* TCP_SLOW_INTERVAL */
+
+#define TCP_FIN_WAIT_TIMEOUT 20000 /* milliseconds */
+#define TCP_SYN_RCVD_TIMEOUT 20000 /* milliseconds */
+
+#define TCP_OOSEQ_TIMEOUT        6U /* x RTO */
+
+#ifndef TCP_MSL
+#define TCP_MSL 60000UL /* The maximum segment lifetime in milliseconds */
+#endif
+
+/* Keepalive values, compliant with RFC 1122. Don't change this unless you know what you're doing */
+#ifndef  TCP_KEEPIDLE_DEFAULT
+#define  TCP_KEEPIDLE_DEFAULT     7200000UL /* Default KEEPALIVE timer in milliseconds */
+#endif
+
+#ifndef  TCP_KEEPINTVL_DEFAULT
+#define  TCP_KEEPINTVL_DEFAULT    75000UL   /* Default Time between KEEPALIVE probes in milliseconds */
+#endif
+
+#ifndef  TCP_KEEPCNT_DEFAULT
+#define  TCP_KEEPCNT_DEFAULT      9U        /* Default Counter for KEEPALIVE probes */
+#endif
+
+#define  TCP_MAXIDLE              TCP_KEEPCNT_DEFAULT * TCP_KEEPINTVL_DEFAULT  /* Maximum KEEPALIVE probe time */
+
+#define TCP_TCPLEN(seg) ((seg)->len + (((TCPH_FLAGS((seg)->tcphdr) & (TCP_FIN | TCP_SYN)) != 0) ? 1U : 0U))
+
+/** Flags used on input processing, not on pcb->flags
+*/
+#define TF_RESET     (u8_t)0x08U   /* Connection was reset. */
+#define TF_CLOSED    (u8_t)0x10U   /* Connection was successfully closed. */
+#define TF_GOT_FIN   (u8_t)0x20U   /* Connection was closed by the remote end. */
+
+
+#if LWIP_EVENT_API
+
+#define TCP_EVENT_ACCEPT(lpcb,pcb,arg,err,ret) ret = lwip_tcp_event(arg, (pcb),\
+                LWIP_EVENT_ACCEPT, NULL, 0, err)
+#define TCP_EVENT_SENT(pcb,space,ret) ret = lwip_tcp_event((pcb)->callback_arg, (pcb),\
+                   LWIP_EVENT_SENT, NULL, space, ERR_OK)
+#define TCP_EVENT_RECV(pcb,p,err,ret) ret = lwip_tcp_event((pcb)->callback_arg, (pcb),\
+                LWIP_EVENT_RECV, (p), 0, (err))
+#define TCP_EVENT_CLOSED(pcb,ret) ret = lwip_tcp_event((pcb)->callback_arg, (pcb),\
+                LWIP_EVENT_RECV, NULL, 0, ERR_OK)
+#define TCP_EVENT_CONNECTED(pcb,err,ret) ret = lwip_tcp_event((pcb)->callback_arg, (pcb),\
+                LWIP_EVENT_CONNECTED, NULL, 0, (err))
+#define TCP_EVENT_POLL(pcb,ret)       do { if ((pcb)->state != SYN_RCVD) {                          \
+                ret = lwip_tcp_event((pcb)->callback_arg, (pcb), LWIP_EVENT_POLL, NULL, 0, ERR_OK); \
+                } else {                                                                            \
+                ret = ERR_ARG; } } while(0)
+/* For event API, last state SYN_RCVD must be excluded here: the application
+   has not seen this pcb, yet! */
+#define TCP_EVENT_ERR(last_state,errf,arg,err)  do { if (last_state != SYN_RCVD) {                \
+                lwip_tcp_event((arg), NULL, LWIP_EVENT_ERR, NULL, 0, (err)); } } while(0)
+
+#else /* LWIP_EVENT_API */
+
+#define TCP_EVENT_ACCEPT(lpcb,pcb,arg,err,ret)                 \
+  do {                                                         \
+    if((lpcb)->accept != NULL)                                 \
+      (ret) = (lpcb)->accept((arg),(pcb),(err));               \
+    else (ret) = ERR_ARG;                                      \
+  } while (0)
+
+#define TCP_EVENT_SENT(pcb,space,ret)                          \
+  do {                                                         \
+    if((pcb)->sent != NULL)                                    \
+      (ret) = (pcb)->sent((pcb)->callback_arg,(pcb),(space));  \
+    else (ret) = ERR_OK;                                       \
+  } while (0)
+
+#define TCP_EVENT_RECV(pcb,p,err,ret)                          \
+  do {                                                         \
+    if((pcb)->recv != NULL) {                                  \
+      (ret) = (pcb)->recv((pcb)->callback_arg,(pcb),(p),(err));\
+    } else {                                                   \
+      (ret) = tcp_recv_null(NULL, (pcb), (p), (err));          \
+    }                                                          \
+  } while (0)
+
+#define TCP_EVENT_CLOSED(pcb,ret)                                \
+  do {                                                           \
+    if(((pcb)->recv != NULL)) {                                  \
+      (ret) = (pcb)->recv((pcb)->callback_arg,(pcb),NULL,ERR_OK);\
+    } else {                                                     \
+      (ret) = ERR_OK;                                            \
+    }                                                            \
+  } while (0)
+
+#define TCP_EVENT_CONNECTED(pcb,err,ret)                         \
+  do {                                                           \
+    if((pcb)->connected != NULL)                                 \
+      (ret) = (pcb)->connected((pcb)->callback_arg,(pcb),(err)); \
+    else (ret) = ERR_OK;                                         \
+  } while (0)
+
+#define TCP_EVENT_POLL(pcb,ret)                                \
+  do {                                                         \
+    if((pcb)->poll != NULL)                                    \
+      (ret) = (pcb)->poll((pcb)->callback_arg,(pcb));          \
+    else (ret) = ERR_OK;                                       \
+  } while (0)
+
+#define TCP_EVENT_ERR(last_state,errf,arg,err)                 \
+  do {                                                         \
+    LWIP_UNUSED_ARG(last_state);                               \
+    if((errf) != NULL)                                         \
+      (errf)((arg),(err));                                     \
+  } while (0)
+
+#endif /* LWIP_EVENT_API */
+
+/** Enabled extra-check for TCP_OVERSIZE if LWIP_DEBUG is enabled */
+#if TCP_OVERSIZE && defined(LWIP_DEBUG)
+#define TCP_OVERSIZE_DBGCHECK 1
+#else
+#define TCP_OVERSIZE_DBGCHECK 0
+#endif
+
+/** Don't generate checksum on copy if CHECKSUM_GEN_TCP is disabled */
+#define TCP_CHECKSUM_ON_COPY  (LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_TCP)
+
+/* This structure represents a TCP segment on the unsent, unacked and ooseq queues */
+struct tcp_seg {
+  struct tcp_seg *next;    /* used when putting segments on a queue */
+  struct pbuf *p;          /* buffer containing data + TCP header */
+  u16_t len;               /* the TCP length of this segment */
+#if TCP_OVERSIZE_DBGCHECK
+  u16_t oversize_left;     /* Extra bytes available at the end of the last
+                              pbuf in unsent (used for asserting vs.
+                              tcp_pcb.unsent_oversize only) */
+#endif /* TCP_OVERSIZE_DBGCHECK */
+#if TCP_CHECKSUM_ON_COPY
+  u16_t chksum;
+  u8_t  chksum_swapped;
+#endif /* TCP_CHECKSUM_ON_COPY */
+  u8_t  flags;
+#define TF_SEG_OPTS_MSS         (u8_t)0x01U /* Include MSS option (only used in SYN segments) */
+#define TF_SEG_OPTS_TS          (u8_t)0x02U /* Include timestamp option. */
+#define TF_SEG_DATA_CHECKSUMMED (u8_t)0x04U /* ALL data (not the header) is
+                                               checksummed into 'chksum' */
+#define TF_SEG_OPTS_WND_SCALE   (u8_t)0x08U /* Include WND SCALE option (only used in SYN segments) */
+#define TF_SEG_OPTS_SACK_PERM   (u8_t)0x10U /* Include SACK Permitted option (only used in SYN segments) */
+  struct tcp_hdr *tcphdr;  /* the TCP header */
+};
+
+#define LWIP_TCP_OPT_EOL        0
+#define LWIP_TCP_OPT_NOP        1
+#define LWIP_TCP_OPT_MSS        2
+#define LWIP_TCP_OPT_WS         3
+#define LWIP_TCP_OPT_SACK_PERM  4
+#define LWIP_TCP_OPT_TS         8
+
+#define LWIP_TCP_OPT_LEN_MSS    4
+#if LWIP_TCP_TIMESTAMPS
+#define LWIP_TCP_OPT_LEN_TS     10
+#define LWIP_TCP_OPT_LEN_TS_OUT 12 /* aligned for output (includes NOP padding) */
+#else
+#define LWIP_TCP_OPT_LEN_TS_OUT 0
+#endif
+#if LWIP_WND_SCALE
+#define LWIP_TCP_OPT_LEN_WS     3
+#define LWIP_TCP_OPT_LEN_WS_OUT 4 /* aligned for output (includes NOP padding) */
+#else
+#define LWIP_TCP_OPT_LEN_WS_OUT 0
+#endif
+
+#if LWIP_TCP_SACK_OUT
+#define LWIP_TCP_OPT_LEN_SACK_PERM     2
+#define LWIP_TCP_OPT_LEN_SACK_PERM_OUT 4 /* aligned for output (includes NOP padding) */
+#else
+#define LWIP_TCP_OPT_LEN_SACK_PERM_OUT 0
+#endif
+
+#define LWIP_TCP_OPT_LENGTH(flags) \
+  ((flags) & TF_SEG_OPTS_MSS       ? LWIP_TCP_OPT_LEN_MSS           : 0) + \
+  ((flags) & TF_SEG_OPTS_TS        ? LWIP_TCP_OPT_LEN_TS_OUT        : 0) + \
+  ((flags) & TF_SEG_OPTS_WND_SCALE ? LWIP_TCP_OPT_LEN_WS_OUT        : 0) + \
+  ((flags) & TF_SEG_OPTS_SACK_PERM ? LWIP_TCP_OPT_LEN_SACK_PERM_OUT : 0)
+
+/** This returns a TCP header option for MSS in an u32_t */
+#define TCP_BUILD_MSS_OPTION(mss) lwip_htonl(0x02040000 | ((mss) & 0xFFFF))
+
+#if LWIP_WND_SCALE
+#define TCPWNDSIZE_F       U32_F
+#define TCPWND_MAX         0xFFFFFFFFU
+#define TCPWND_CHECK16(x)  LWIP_ASSERT("window size > 0xFFFF", (x) <= 0xFFFF)
+#define TCPWND_MIN16(x)    ((u16_t)LWIP_MIN((x), 0xFFFF))
+#else /* LWIP_WND_SCALE */
+#define TCPWNDSIZE_F       U16_F
+#define TCPWND_MAX         0xFFFFU
+#define TCPWND_CHECK16(x)
+#define TCPWND_MIN16(x)    x
+#endif /* LWIP_WND_SCALE */
+
+/* Global variables: */
+extern struct tcp_pcb *tcp_input_pcb;
+extern u32_t tcp_ticks;
+extern u8_t tcp_active_pcbs_changed;
+
+/* The TCP PCB lists. */
+union tcp_listen_pcbs_t { /* List of all TCP PCBs in LISTEN state. */
+  struct tcp_pcb_listen *listen_pcbs;
+  struct tcp_pcb *pcbs;
+};
+extern struct tcp_pcb *tcp_bound_pcbs;
+extern union tcp_listen_pcbs_t tcp_listen_pcbs;
+extern struct tcp_pcb *tcp_active_pcbs;  /* List of all TCP PCBs that are in a
+              state in which they accept or send
+              data. */
+extern struct tcp_pcb *tcp_tw_pcbs;      /* List of all TCP PCBs in TIME-WAIT. */
+
+#define NUM_TCP_PCB_LISTS_NO_TIME_WAIT  3
+#define NUM_TCP_PCB_LISTS               4
+extern struct tcp_pcb ** const tcp_pcb_lists[NUM_TCP_PCB_LISTS];
+
+/* Axioms about the above lists:
+   1) Every TCP PCB that is not CLOSED is in one of the lists.
+   2) A PCB is only in one of the lists.
+   3) All PCBs in the tcp_listen_pcbs list is in LISTEN state.
+   4) All PCBs in the tcp_tw_pcbs list is in TIME-WAIT state.
+*/
+/* Define two macros, TCP_REG and TCP_RMV that registers a TCP PCB
+   with a PCB list or removes a PCB from a list, respectively. */
+#ifndef TCP_DEBUG_PCB_LISTS
+#define TCP_DEBUG_PCB_LISTS 0
+#endif
+#if TCP_DEBUG_PCB_LISTS
+#define TCP_REG(pcbs, npcb) do {\
+                            struct tcp_pcb *tcp_tmp_pcb; \
+                            LWIP_DEBUGF(TCP_DEBUG, ("TCP_REG %p local port %"U16_F"\n", (void *)(npcb), (npcb)->local_port)); \
+                            for (tcp_tmp_pcb = *(pcbs); \
+          tcp_tmp_pcb != NULL; \
+        tcp_tmp_pcb = tcp_tmp_pcb->next) { \
+                                LWIP_ASSERT("TCP_REG: already registered", tcp_tmp_pcb != (npcb)); \
+                            } \
+                            LWIP_ASSERT("TCP_REG: pcb->state != CLOSED", ((pcbs) == &tcp_bound_pcbs) || ((npcb)->state != CLOSED)); \
+                            (npcb)->next = *(pcbs); \
+                            LWIP_ASSERT("TCP_REG: npcb->next != npcb", (npcb)->next != (npcb)); \
+                            *(pcbs) = (npcb); \
+                            LWIP_ASSERT("TCP_REG: tcp_pcbs sane", tcp_pcbs_sane()); \
+              tcp_timer_needed(); \
+                            } while(0)
+#define TCP_RMV(pcbs, npcb) do { \
+                            struct tcp_pcb *tcp_tmp_pcb; \
+                            LWIP_ASSERT("TCP_RMV: pcbs != NULL", *(pcbs) != NULL); \
+                            LWIP_DEBUGF(TCP_DEBUG, ("TCP_RMV: removing %p from %p\n", (void *)(npcb), (void *)(*(pcbs)))); \
+                            if(*(pcbs) == (npcb)) { \
+                               *(pcbs) = (*pcbs)->next; \
+                            } else for (tcp_tmp_pcb = *(pcbs); tcp_tmp_pcb != NULL; tcp_tmp_pcb = tcp_tmp_pcb->next) { \
+                               if(tcp_tmp_pcb->next == (npcb)) { \
+                                  tcp_tmp_pcb->next = (npcb)->next; \
+                                  break; \
+                               } \
+                            } \
+                            (npcb)->next = NULL; \
+                            LWIP_ASSERT("TCP_RMV: tcp_pcbs sane", tcp_pcbs_sane()); \
+                            LWIP_DEBUGF(TCP_DEBUG, ("TCP_RMV: removed %p from %p\n", (void *)(npcb), (void *)(*(pcbs)))); \
+                            } while(0)
+
+#else /* LWIP_DEBUG */
+
+#define TCP_REG(pcbs, npcb)                        \
+  do {                                             \
+    (npcb)->next = *pcbs;                          \
+    *(pcbs) = (npcb);                              \
+    tcp_timer_needed();                            \
+  } while (0)
+
+#define TCP_RMV(pcbs, npcb)                        \
+  do {                                             \
+    if(*(pcbs) == (npcb)) {                        \
+      (*(pcbs)) = (*pcbs)->next;                   \
+    }                                              \
+    else {                                         \
+      struct tcp_pcb *tcp_tmp_pcb;                 \
+      for (tcp_tmp_pcb = *pcbs;                    \
+          tcp_tmp_pcb != NULL;                     \
+          tcp_tmp_pcb = tcp_tmp_pcb->next) {       \
+        if(tcp_tmp_pcb->next == (npcb)) {          \
+          tcp_tmp_pcb->next = (npcb)->next;        \
+          break;                                   \
+        }                                          \
+      }                                            \
+    }                                              \
+    (npcb)->next = NULL;                           \
+  } while(0)
+
+#endif /* LWIP_DEBUG */
+
+#define TCP_REG_ACTIVE(npcb)                       \
+  do {                                             \
+    TCP_REG(&tcp_active_pcbs, npcb);               \
+    tcp_active_pcbs_changed = 1;                   \
+  } while (0)
+
+#define TCP_RMV_ACTIVE(npcb)                       \
+  do {                                             \
+    TCP_RMV(&tcp_active_pcbs, npcb);               \
+    tcp_active_pcbs_changed = 1;                   \
+  } while (0)
+
+#define TCP_PCB_REMOVE_ACTIVE(pcb)                 \
+  do {                                             \
+    tcp_pcb_remove(&tcp_active_pcbs, pcb);         \
+    tcp_active_pcbs_changed = 1;                   \
+  } while (0)
+
+
+/* Internal functions: */
+struct tcp_pcb *tcp_pcb_copy(struct tcp_pcb *pcb);
+void tcp_pcb_purge(struct tcp_pcb *pcb);
+void tcp_pcb_remove(struct tcp_pcb **pcblist, struct tcp_pcb *pcb);
+
+void tcp_segs_free(struct tcp_seg *seg);
+void tcp_seg_free(struct tcp_seg *seg);
+struct tcp_seg *tcp_seg_copy(struct tcp_seg *seg);
+
+#define tcp_ack(pcb)                               \
+  do {                                             \
+    if((pcb)->flags & TF_ACK_DELAY) {              \
+      tcp_clear_flags(pcb, TF_ACK_DELAY);          \
+      tcp_ack_now(pcb);                            \
+    }                                              \
+    else {                                         \
+      tcp_set_flags(pcb, TF_ACK_DELAY);            \
+    }                                              \
+  } while (0)
+
+#define tcp_ack_now(pcb)                           \
+  tcp_set_flags(pcb, TF_ACK_NOW)
+
+err_t tcp_send_fin(struct tcp_pcb *pcb);
+err_t tcp_enqueue_flags(struct tcp_pcb *pcb, u8_t flags);
+
+void tcp_rexmit_seg(struct tcp_pcb *pcb, struct tcp_seg *seg);
+
+void tcp_rst(const struct tcp_pcb* pcb, u32_t seqno, u32_t ackno,
+       const ip_addr_t *local_ip, const ip_addr_t *remote_ip,
+       u16_t local_port, u16_t remote_port);
+void tcp_rst_netif(struct netif *netif, u32_t seqno, u32_t ackno,
+                   const ip_addr_t *local_ip, const ip_addr_t *remote_ip,
+                   u16_t local_port, u16_t remote_port);
+
+u32_t tcp_next_iss(struct tcp_pcb *pcb);
+
+err_t tcp_keepalive(struct tcp_pcb *pcb);
+err_t tcp_split_unsent_seg(struct tcp_pcb *pcb, u16_t split);
+err_t tcp_zero_window_probe(struct tcp_pcb *pcb);
+void  tcp_trigger_input_pcb_close(void);
+
+#if TCP_CALCULATE_EFF_SEND_MSS
+u16_t tcp_eff_send_mss_netif(u16_t sendmss, struct netif *outif,
+                             const ip_addr_t *dest);
+#define tcp_eff_send_mss(sendmss, src, dest) \
+    tcp_eff_send_mss_netif(sendmss, ip_route(src, dest), dest)
+#endif /* TCP_CALCULATE_EFF_SEND_MSS */
+
+#if LWIP_CALLBACK_API
+err_t tcp_recv_null(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err);
+#endif /* LWIP_CALLBACK_API */
+
+#if TCP_DEBUG || TCP_INPUT_DEBUG || TCP_OUTPUT_DEBUG
+void tcp_debug_print(struct tcp_hdr *tcphdr);
+void tcp_debug_print_flags(u8_t flags);
+void tcp_debug_print_state(enum tcp_state s);
+void tcp_debug_print_pcbs(void);
+s16_t tcp_pcbs_sane(void);
+#else
+#  define tcp_debug_print(tcphdr)
+#  define tcp_debug_print_flags(flags)
+#  define tcp_debug_print_state(s)
+#  define tcp_debug_print_pcbs()
+#  define tcp_pcbs_sane() 1
+#endif /* TCP_DEBUG */
+
+/** External function (implemented in timers.c), called when TCP detects
+ * that a timer is needed (i.e. active- or time-wait-pcb found). */
+void tcp_timer_needed(void);
+
+void tcp_netif_ip_addr_changed(const ip_addr_t* old_addr, const ip_addr_t* new_addr);
+
+#if TCP_QUEUE_OOSEQ
+void tcp_free_ooseq(struct tcp_pcb *pcb);
+#endif
+
+#if LWIP_TCP_PCB_NUM_EXT_ARGS
+err_t tcp_ext_arg_invoke_callbacks_passive_open(struct tcp_pcb_listen *lpcb, struct tcp_pcb *cpcb);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_TCP */
+
+#endif /* LWIP_HDR_TCP_PRIV_H */
diff --git a/lib/lwip/include/lwip/priv/tcpip_priv.h b/lib/lwip/include/lwip/priv/tcpip_priv.h
new file mode 100644
index 0000000000..bfa88ff6a6
--- /dev/null
+++ b/lib/lwip/include/lwip/priv/tcpip_priv.h
@@ -0,0 +1,176 @@
+/**
+ * @file
+ * TCPIP API internal implementations (do not use in application code)
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef LWIP_HDR_TCPIP_PRIV_H
+#define LWIP_HDR_TCPIP_PRIV_H
+
+#include "lwip/opt.h"
+
+#if !NO_SYS /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/tcpip.h"
+#include "lwip/sys.h"
+#include "lwip/timeouts.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct pbuf;
+struct netif;
+
+#if LWIP_MPU_COMPATIBLE
+#define API_VAR_REF(name)               (*(name))
+#define API_VAR_DECLARE(type, name)     type * name
+#define API_VAR_ALLOC_EXT(type, pool, name, errorblock) do { \
+                                          name = (type *)memp_malloc(pool); \
+                                          if (name == NULL) { \
+                                            errorblock; \
+                                          } \
+                                        } while(0)
+#define API_VAR_ALLOC(type, pool, name, errorval) API_VAR_ALLOC_EXT(type, pool, name, return errorval)
+#define API_VAR_ALLOC_POOL(type, pool, name, errorval) do { \
+                                          name = (type *)LWIP_MEMPOOL_ALLOC(pool); \
+                                          if (name == NULL) { \
+                                            return errorval; \
+                                          } \
+                                        } while(0)
+#define API_VAR_FREE(pool, name)        memp_free(pool, name)
+#define API_VAR_FREE_POOL(pool, name)   LWIP_MEMPOOL_FREE(pool, name)
+#define API_EXPR_REF(expr)              (&(expr))
+#if LWIP_NETCONN_SEM_PER_THREAD
+#define API_EXPR_REF_SEM(expr)          (expr)
+#else
+#define API_EXPR_REF_SEM(expr)          API_EXPR_REF(expr)
+#endif
+#define API_EXPR_DEREF(expr)            expr
+#define API_MSG_M_DEF(m)                m
+#define API_MSG_M_DEF_C(t, m)           t m
+#else /* LWIP_MPU_COMPATIBLE */
+#define API_VAR_REF(name)               name
+#define API_VAR_DECLARE(type, name)     type name
+#define API_VAR_ALLOC_EXT(type, pool, name, errorblock)
+#define API_VAR_ALLOC(type, pool, name, errorval)
+#define API_VAR_ALLOC_POOL(type, pool, name, errorval)
+#define API_VAR_FREE(pool, name)
+#define API_VAR_FREE_POOL(pool, name)
+#define API_EXPR_REF(expr)              expr
+#define API_EXPR_REF_SEM(expr)          API_EXPR_REF(expr)
+#define API_EXPR_DEREF(expr)            (*(expr))
+#define API_MSG_M_DEF(m)                *m
+#define API_MSG_M_DEF_C(t, m)           const t * m
+#endif /* LWIP_MPU_COMPATIBLE */
+
+err_t tcpip_send_msg_wait_sem(tcpip_callback_fn fn, void *apimsg, sys_sem_t* sem);
+
+struct tcpip_api_call_data
+{
+#if !LWIP_TCPIP_CORE_LOCKING
+  err_t err;
+#if !LWIP_NETCONN_SEM_PER_THREAD
+  sys_sem_t sem;
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
+#else /* !LWIP_TCPIP_CORE_LOCKING */
+  u8_t dummy; /* avoid empty struct :-( */
+#endif /* !LWIP_TCPIP_CORE_LOCKING */
+};
+typedef err_t (*tcpip_api_call_fn)(struct tcpip_api_call_data* call);
+err_t tcpip_api_call(tcpip_api_call_fn fn, struct tcpip_api_call_data *call);
+
+enum tcpip_msg_type {
+#if !LWIP_TCPIP_CORE_LOCKING
+  TCPIP_MSG_API,
+  TCPIP_MSG_API_CALL,
+#endif /* !LWIP_TCPIP_CORE_LOCKING */
+#if !LWIP_TCPIP_CORE_LOCKING_INPUT
+  TCPIP_MSG_INPKT,
+#endif /* !LWIP_TCPIP_CORE_LOCKING_INPUT */
+#if LWIP_TCPIP_TIMEOUT && LWIP_TIMERS
+  TCPIP_MSG_TIMEOUT,
+  TCPIP_MSG_UNTIMEOUT,
+#endif /* LWIP_TCPIP_TIMEOUT && LWIP_TIMERS */
+  TCPIP_MSG_CALLBACK,
+  TCPIP_MSG_CALLBACK_STATIC,
+  TCPIP_MSG_CALLBACK_STATIC_WAIT
+};
+
+struct tcpip_msg {
+  enum tcpip_msg_type type;
+  union {
+#if !LWIP_TCPIP_CORE_LOCKING
+    struct {
+      tcpip_callback_fn function;
+      void* msg;
+    } api_msg;
+    struct {
+      tcpip_api_call_fn function;
+      struct tcpip_api_call_data *arg;
+      sys_sem_t *sem;
+    } api_call;
+    struct {
+      tcpip_callback_fn function;
+      void *ctx;
+      sys_sem_t *sem;
+    } cb_wait;
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+#if !LWIP_TCPIP_CORE_LOCKING_INPUT
+    struct {
+      struct pbuf *p;
+      struct netif *netif;
+      netif_input_fn input_fn;
+    } inp;
+#endif /* !LWIP_TCPIP_CORE_LOCKING_INPUT */
+    struct {
+      tcpip_callback_fn function;
+      void *ctx;
+    } cb;
+#if LWIP_TCPIP_TIMEOUT && LWIP_TIMERS
+    struct {
+      u32_t msecs;
+      sys_timeout_handler h;
+      void *arg;
+    } tmo;
+#endif /* LWIP_TCPIP_TIMEOUT && LWIP_TIMERS */
+  } msg;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !NO_SYS */
+
+#endif /* LWIP_HDR_TCPIP_PRIV_H */
diff --git a/lib/lwip/include/lwip/prot/acd.h b/lib/lwip/include/lwip/prot/acd.h
new file mode 100644
index 0000000000..860cae5bab
--- /dev/null
+++ b/lib/lwip/include/lwip/prot/acd.h
@@ -0,0 +1,91 @@
+/**
+ * @file
+ * ACD protocol definitions
+ */
+
+/*
+ *
+ * Copyright (c) 2007 Dominik Spies <kontakt@dspies.de>
+ * Copyright (c) 2018 Jasper Verschueren <jasper.verschueren@apart-audio.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Jasper Verschueren <jasper.verschueren@apart-audio.com>
+ * Author: Dominik Spies <kontakt@dspies.de>
+ */
+
+#ifndef LWIP_HDR_PROT_ACD_H
+#define LWIP_HDR_PROT_ACD_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* RFC 5227 and RFC 3927 Constants */
+#define PROBE_WAIT           1   /* second  (initial random delay)                    */
+#define PROBE_MIN            1   /* second  (minimum delay till repeated probe)       */
+#define PROBE_MAX            2   /* seconds (maximum delay till repeated probe)       */
+#define PROBE_NUM            3   /*         (number of probe packets)                 */
+#define ANNOUNCE_NUM         2   /*         (number of announcement packets)          */
+#define ANNOUNCE_INTERVAL    2   /* seconds (time between announcement packets)       */
+#define ANNOUNCE_WAIT        2   /* seconds (delay before announcing)                 */
+#define MAX_CONFLICTS        10  /*         (max conflicts before rate limiting)      */
+#define RATE_LIMIT_INTERVAL  60  /* seconds (delay between successive attempts)       */
+#define DEFEND_INTERVAL      10  /* seconds (minimum interval between defensive ARPs) */
+
+/* ACD states */
+typedef enum {
+  /* ACD is module is off */
+  ACD_STATE_OFF,
+  /* Waiting before probing can be started */
+  ACD_STATE_PROBE_WAIT,
+  /* Probing the ipaddr */
+  ACD_STATE_PROBING,
+  /* Waiting before announcing the probed ipaddr */
+  ACD_STATE_ANNOUNCE_WAIT,
+  /* Announcing the new ipaddr */
+  ACD_STATE_ANNOUNCING,
+  /* Performing ongoing conflict detection with one defend within defend inferval */
+  ACD_STATE_ONGOING,
+  /* Performing ongoing conflict detection but immediately back off and Release
+   * the address when a conflict occurs. This state is used for LL addresses
+   * that stay active even if the netif has a routable address selected.
+   * In such a case, we cannot defend our address */
+  ACD_STATE_PASSIVE_ONGOING,
+  /* To many conflicts occurred, we need to wait before restarting the selection
+   * process */
+  ACD_STATE_RATE_LIMIT
+} acd_state_enum_t;
+
+typedef enum {
+  ACD_IP_OK,            /* IP address is good, no conflicts found in checking state */
+  ACD_RESTART_CLIENT,   /* Conflict found -> the client should try again */
+  ACD_DECLINE           /* Decline the received IP address (rate limiting)*/
+} acd_callback_enum_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_PROT_ACD_H */
diff --git a/lib/lwip/include/lwip/prot/autoip.h b/lib/lwip/include/lwip/prot/autoip.h
new file mode 100644
index 0000000000..adbb1668e7
--- /dev/null
+++ b/lib/lwip/include/lwip/prot/autoip.h
@@ -0,0 +1,65 @@
+/**
+ * @file
+ * AutoIP protocol definitions
+ */
+
+/*
+ *
+ * Copyright (c) 2007 Dominik Spies <kontakt@dspies.de>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Dominik Spies <kontakt@dspies.de>
+ *
+ * This is a AutoIP implementation for the lwIP TCP/IP stack. It aims to conform
+ * with RFC 3927.
+ *
+ */
+
+#ifndef LWIP_HDR_PROT_AUTOIP_H
+#define LWIP_HDR_PROT_AUTOIP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* 169.254.0.0 */
+#define AUTOIP_NET              0xA9FE0000
+/* 169.254.1.0 */
+#define AUTOIP_RANGE_START      (AUTOIP_NET | 0x0100)
+/* 169.254.254.255 */
+#define AUTOIP_RANGE_END        (AUTOIP_NET | 0xFEFF)
+
+/* AutoIP client states */
+typedef enum {
+  AUTOIP_STATE_OFF,
+  AUTOIP_STATE_CHECKING,
+  AUTOIP_STATE_BOUND
+} autoip_state_enum_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_PROT_AUTOIP_H */
diff --git a/lib/lwip/include/lwip/prot/dhcp.h b/lib/lwip/include/lwip/prot/dhcp.h
new file mode 100644
index 0000000000..ab18dca313
--- /dev/null
+++ b/lib/lwip/include/lwip/prot/dhcp.h
@@ -0,0 +1,178 @@
+/**
+ * @file
+ * DHCP protocol definitions
+ */
+
+/*
+ * Copyright (c) 2001-2004 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * Copyright (c) 2001-2004 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Leon Woestenberg <leon.woestenberg@gmx.net>
+ *
+ */
+#ifndef LWIP_HDR_PROT_DHCP_H
+#define LWIP_HDR_PROT_DHCP_H
+
+#include "lwip/opt.h"
+#include "lwip/arch.h"
+#include "lwip/prot/ip4.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ /* DHCP message item offsets and length */
+#define DHCP_CHADDR_LEN   16U
+#define DHCP_SNAME_OFS    44U
+#define DHCP_SNAME_LEN    64U
+#define DHCP_FILE_OFS     108U
+#define DHCP_FILE_LEN     128U
+#define DHCP_MSG_LEN      236U
+#define DHCP_OPTIONS_OFS  (DHCP_MSG_LEN + 4U) /* 4 byte: cookie */
+
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+/** minimum set of fields of any DHCP message */
+struct dhcp_msg
+{
+  PACK_STRUCT_FLD_8(u8_t op);
+  PACK_STRUCT_FLD_8(u8_t htype);
+  PACK_STRUCT_FLD_8(u8_t hlen);
+  PACK_STRUCT_FLD_8(u8_t hops);
+  PACK_STRUCT_FIELD(u32_t xid);
+  PACK_STRUCT_FIELD(u16_t secs);
+  PACK_STRUCT_FIELD(u16_t flags);
+  PACK_STRUCT_FLD_S(ip4_addr_p_t ciaddr);
+  PACK_STRUCT_FLD_S(ip4_addr_p_t yiaddr);
+  PACK_STRUCT_FLD_S(ip4_addr_p_t siaddr);
+  PACK_STRUCT_FLD_S(ip4_addr_p_t giaddr);
+  PACK_STRUCT_FLD_8(u8_t chaddr[DHCP_CHADDR_LEN]);
+  PACK_STRUCT_FLD_8(u8_t sname[DHCP_SNAME_LEN]);
+  PACK_STRUCT_FLD_8(u8_t file[DHCP_FILE_LEN]);
+  PACK_STRUCT_FIELD(u32_t cookie);
+#define DHCP_MIN_OPTIONS_LEN 68U
+/** make sure user does not configure this too small */
+#if ((defined(DHCP_OPTIONS_LEN)) && (DHCP_OPTIONS_LEN < DHCP_MIN_OPTIONS_LEN))
+#  undef DHCP_OPTIONS_LEN
+#endif
+/** allow this to be configured in lwipopts.h, but not too small */
+#if (!defined(DHCP_OPTIONS_LEN))
+/** set this to be sufficient for your options in outgoing DHCP msgs */
+#  define DHCP_OPTIONS_LEN DHCP_MIN_OPTIONS_LEN
+#endif
+  PACK_STRUCT_FLD_8(u8_t options[DHCP_OPTIONS_LEN]);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
+
+
+/* DHCP client states */
+typedef enum {
+  DHCP_STATE_OFF             = 0,
+  DHCP_STATE_REQUESTING      = 1,
+  DHCP_STATE_INIT            = 2,
+  DHCP_STATE_REBOOTING       = 3,
+  DHCP_STATE_REBINDING       = 4,
+  DHCP_STATE_RENEWING        = 5,
+  DHCP_STATE_SELECTING       = 6,
+  DHCP_STATE_INFORMING       = 7,
+  DHCP_STATE_CHECKING        = 8,
+  DHCP_STATE_PERMANENT       = 9,  /* not yet implemented */
+  DHCP_STATE_BOUND           = 10,
+  DHCP_STATE_RELEASING       = 11, /* not yet implemented */
+  DHCP_STATE_BACKING_OFF     = 12
+} dhcp_state_enum_t;
+
+/* DHCP op codes */
+#define DHCP_BOOTREQUEST            1
+#define DHCP_BOOTREPLY              2
+
+/* DHCP message types */
+#define DHCP_DISCOVER               1
+#define DHCP_OFFER                  2
+#define DHCP_REQUEST                3
+#define DHCP_DECLINE                4
+#define DHCP_ACK                    5
+#define DHCP_NAK                    6
+#define DHCP_RELEASE                7
+#define DHCP_INFORM                 8
+
+#define DHCP_MAGIC_COOKIE           0x63825363UL
+
+/* This is a list of options for BOOTP and DHCP, see RFC 2132 for descriptions */
+
+/* BootP options */
+#define DHCP_OPTION_PAD             0
+#define DHCP_OPTION_SUBNET_MASK     1 /* RFC 2132 3.3 */
+#define DHCP_OPTION_ROUTER          3
+#define DHCP_OPTION_DNS_SERVER      6
+#define DHCP_OPTION_HOSTNAME        12
+#define DHCP_OPTION_IP_TTL          23
+#define DHCP_OPTION_MTU             26
+#define DHCP_OPTION_BROADCAST       28
+#define DHCP_OPTION_TCP_TTL         37
+#define DHCP_OPTION_NTP             42
+#define DHCP_OPTION_END             255
+
+/* DHCP options */
+#define DHCP_OPTION_REQUESTED_IP    50 /* RFC 2132 9.1, requested IP address */
+#define DHCP_OPTION_LEASE_TIME      51 /* RFC 2132 9.2, time in seconds, in 4 bytes */
+#define DHCP_OPTION_OVERLOAD        52 /* RFC2132 9.3, use file and/or sname field for options */
+
+#define DHCP_OPTION_MESSAGE_TYPE    53 /* RFC 2132 9.6, important for DHCP */
+#define DHCP_OPTION_MESSAGE_TYPE_LEN 1
+
+#define DHCP_OPTION_SERVER_ID       54 /* RFC 2132 9.7, server IP address */
+#define DHCP_OPTION_PARAMETER_REQUEST_LIST  55 /* RFC 2132 9.8, requested option types */
+
+#define DHCP_OPTION_MAX_MSG_SIZE    57 /* RFC 2132 9.10, message size accepted >= 576 */
+#define DHCP_OPTION_MAX_MSG_SIZE_LEN 2
+
+#define DHCP_OPTION_T1              58 /* T1 renewal time */
+#define DHCP_OPTION_T2              59 /* T2 rebinding time */
+#define DHCP_OPTION_US              60
+#define DHCP_OPTION_CLIENT_ID       61
+#define DHCP_OPTION_TFTP_SERVERNAME 66
+#define DHCP_OPTION_BOOTFILE        67
+
+/* possible combinations of overloading the file and sname fields with options */
+#define DHCP_OVERLOAD_NONE          0
+#define DHCP_OVERLOAD_FILE          1
+#define DHCP_OVERLOAD_SNAME         2
+#define DHCP_OVERLOAD_SNAME_FILE    3
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_PROT_DHCP_H */
diff --git a/lib/lwip/include/lwip/prot/dhcp6.h b/lib/lwip/include/lwip/prot/dhcp6.h
new file mode 100644
index 0000000000..0754c91b9b
--- /dev/null
+++ b/lib/lwip/include/lwip/prot/dhcp6.h
@@ -0,0 +1,138 @@
+/**
+ * @file
+ * DHCPv6 protocol definitions
+ */
+
+/*
+ * Copyright (c) 2017 Simon Goldschmidt <goldsimon@gmx.de>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt <goldsimon@gmx.de>
+ *
+ */
+#ifndef LWIP_HDR_PROT_DHCP6_H
+#define LWIP_HDR_PROT_DHCP6_H
+
+#include "lwip/opt.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define DHCP6_CLIENT_PORT  546
+#define DHCP6_SERVER_PORT  547
+
+
+ /* DHCPv6 message item offsets and length */
+#define DHCP6_TRANSACTION_ID_LEN   3
+
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+/** minimum set of fields of any DHCPv6 message */
+struct dhcp6_msg
+{
+  PACK_STRUCT_FLD_8(u8_t msgtype);
+  PACK_STRUCT_FLD_8(u8_t transaction_id[DHCP6_TRANSACTION_ID_LEN]);
+  /* options follow */
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
+
+
+/* DHCP6 client states */
+typedef enum {
+  DHCP6_STATE_OFF               = 0,
+  DHCP6_STATE_STATELESS_IDLE    = 1,
+  DHCP6_STATE_REQUESTING_CONFIG = 2
+} dhcp6_state_enum_t;
+
+/* DHCPv6 message types */
+#define DHCP6_SOLICIT               1
+#define DHCP6_ADVERTISE             2
+#define DHCP6_REQUEST               3
+#define DHCP6_CONFIRM               4
+#define DHCP6_RENEW                 5
+#define DHCP6_REBIND                6
+#define DHCP6_REPLY                 7
+#define DHCP6_RELEASE               8
+#define DHCP6_DECLINE               9
+#define DHCP6_RECONFIGURE           10
+#define DHCP6_INFOREQUEST           11
+#define DHCP6_RELAYFORW             12
+#define DHCP6_RELAYREPL             13
+/* More message types see https://www.iana.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xhtml */
+
+/** DHCPv6 status codes */
+#define DHCP6_STATUS_SUCCESS        0 /* Success. */
+#define DHCP6_STATUS_UNSPECFAIL     1 /* Failure, reason unspecified; this status code is sent by either a client or a server to indicate a failure not explicitly specified in this document. */
+#define DHCP6_STATUS_NOADDRSAVAIL   2 /* Server has no addresses available to assign to the IA(s). */
+#define DHCP6_STATUS_NOBINDING      3 /* Client record (binding) unavailable. */
+#define DHCP6_STATUS_NOTONLINK      4 /* The prefix for the address is not appropriate for the link to which the client is attached. */
+#define DHCP6_STATUS_USEMULTICAST   5 /* Sent by a server to a client to force the client to send messages to the server using the All_DHCP_Relay_Agents_and_Servers address. */
+/* More status codes see https://www.iana.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xhtml */
+
+/** DHCPv6 DUID types */
+#define DHCP6_DUID_LLT              1 /* LLT: Link-layer Address Plus Time */
+#define DHCP6_DUID_EN               2 /* EN: Enterprise number */
+#define DHCP6_DUID_LL               3 /* LL: Link-layer Address */
+#define DHCP6_DUID_UUID             4 /* UUID (RFC 6355) */
+
+/* DHCPv6 options */
+#define DHCP6_OPTION_CLIENTID       1
+#define DHCP6_OPTION_SERVERID       2
+#define DHCP6_OPTION_IA_NA          3
+#define DHCP6_OPTION_IA_TA          4
+#define DHCP6_OPTION_IAADDR         5
+#define DHCP6_OPTION_ORO            6
+#define DHCP6_OPTION_PREFERENCE     7
+#define DHCP6_OPTION_ELAPSED_TIME   8
+#define DHCP6_OPTION_RELAY_MSG      9
+#define DHCP6_OPTION_AUTH           11
+#define DHCP6_OPTION_UNICAST        12
+#define DHCP6_OPTION_STATUS_CODE    13
+#define DHCP6_OPTION_RAPID_COMMIT   14
+#define DHCP6_OPTION_USER_CLASS     15
+#define DHCP6_OPTION_VENDOR_CLASS   16
+#define DHCP6_OPTION_VENDOR_OPTS    17
+#define DHCP6_OPTION_INTERFACE_ID   18
+#define DHCP6_OPTION_RECONF_MSG     19
+#define DHCP6_OPTION_RECONF_ACCEPT  20
+/* More options see https://www.iana.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xhtml */
+#define DHCP6_OPTION_DNS_SERVERS    23 /* RFC 3646 */
+#define DHCP6_OPTION_DOMAIN_LIST    24 /* RFC 3646 */
+#define DHCP6_OPTION_SNTP_SERVERS   31 /* RFC 4075 */
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_PROT_DHCP6_H */
diff --git a/lib/lwip/include/lwip/prot/dns.h b/lib/lwip/include/lwip/prot/dns.h
new file mode 100644
index 0000000000..94782d6e9c
--- /dev/null
+++ b/lib/lwip/include/lwip/prot/dns.h
@@ -0,0 +1,140 @@
+/**
+ * @file
+ * DNS - host name to IP address resolver.
+ */
+
+/*
+ * Port to lwIP from uIP
+ * by Jim Pettinato April 2007
+ *
+ * security fixes and more by Simon Goldschmidt
+ *
+ * uIP version Copyright (c) 2002-2003, Adam Dunkels.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef LWIP_HDR_PROT_DNS_H
+#define LWIP_HDR_PROT_DNS_H
+
+#include "lwip/arch.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** DNS server port address */
+#ifndef DNS_SERVER_PORT
+#define DNS_SERVER_PORT           53
+#endif
+
+/* DNS field TYPE used for "Resource Records" */
+#define DNS_RRTYPE_A              1     /* a host address */
+#define DNS_RRTYPE_NS             2     /* an authoritative name server */
+#define DNS_RRTYPE_MD             3     /* a mail destination (Obsolete - use MX) */
+#define DNS_RRTYPE_MF             4     /* a mail forwarder (Obsolete - use MX) */
+#define DNS_RRTYPE_CNAME          5     /* the canonical name for an alias */
+#define DNS_RRTYPE_SOA            6     /* marks the start of a zone of authority */
+#define DNS_RRTYPE_MB             7     /* a mailbox domain name (EXPERIMENTAL) */
+#define DNS_RRTYPE_MG             8     /* a mail group member (EXPERIMENTAL) */
+#define DNS_RRTYPE_MR             9     /* a mail rename domain name (EXPERIMENTAL) */
+#define DNS_RRTYPE_NULL           10    /* a null RR (EXPERIMENTAL) */
+#define DNS_RRTYPE_WKS            11    /* a well known service description */
+#define DNS_RRTYPE_PTR            12    /* a domain name pointer */
+#define DNS_RRTYPE_HINFO          13    /* host information */
+#define DNS_RRTYPE_MINFO          14    /* mailbox or mail list information */
+#define DNS_RRTYPE_MX             15    /* mail exchange */
+#define DNS_RRTYPE_TXT            16    /* text strings */
+#define DNS_RRTYPE_AAAA           28    /* IPv6 address */
+#define DNS_RRTYPE_SRV            33    /* service location */
+#define DNS_RRTYPE_ANY            255   /* any type */
+
+/* DNS field CLASS used for "Resource Records" */
+#define DNS_RRCLASS_IN            1     /* the Internet */
+#define DNS_RRCLASS_CS            2     /* the CSNET class (Obsolete - used only for examples in some obsolete RFCs) */
+#define DNS_RRCLASS_CH            3     /* the CHAOS class */
+#define DNS_RRCLASS_HS            4     /* Hesiod [Dyer 87] */
+#define DNS_RRCLASS_ANY           255   /* any class */
+#define DNS_RRCLASS_FLUSH         0x800 /* Flush bit */
+
+/* DNS protocol flags */
+#define DNS_FLAG1_RESPONSE        0x80
+#define DNS_FLAG1_OPCODE_STATUS   0x10
+#define DNS_FLAG1_OPCODE_INVERSE  0x08
+#define DNS_FLAG1_OPCODE_STANDARD 0x00
+#define DNS_FLAG1_AUTHORATIVE     0x04
+#define DNS_FLAG1_TRUNC           0x02
+#define DNS_FLAG1_RD              0x01
+#define DNS_FLAG2_RA              0x80
+#define DNS_FLAG2_ERR_MASK        0x0f
+#define DNS_FLAG2_ERR_NONE        0x00
+#define DNS_FLAG2_ERR_NAME        0x03
+
+#define DNS_HDR_GET_OPCODE(hdr) ((((hdr)->flags1) >> 3) & 0xF)
+
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+/** DNS message header */
+struct dns_hdr {
+  PACK_STRUCT_FIELD(u16_t id);
+  PACK_STRUCT_FLD_8(u8_t flags1);
+  PACK_STRUCT_FLD_8(u8_t flags2);
+  PACK_STRUCT_FIELD(u16_t numquestions);
+  PACK_STRUCT_FIELD(u16_t numanswers);
+  PACK_STRUCT_FIELD(u16_t numauthrr);
+  PACK_STRUCT_FIELD(u16_t numextrarr);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
+#define SIZEOF_DNS_HDR 12
+
+
+/* Multicast DNS definitions */
+
+/** UDP port for multicast DNS queries */
+#ifndef DNS_MQUERY_PORT
+#define DNS_MQUERY_PORT             5353
+#endif
+
+/* IPv4 group for multicast DNS queries: 224.0.0.251 */
+#ifndef DNS_MQUERY_IPV4_GROUP_INIT
+#define DNS_MQUERY_IPV4_GROUP_INIT  IPADDR4_INIT_BYTES(224,0,0,251)
+#endif
+
+/* IPv6 group for multicast DNS queries: FF02::FB */
+#ifndef DNS_MQUERY_IPV6_GROUP_INIT
+#define DNS_MQUERY_IPV6_GROUP_INIT  IPADDR6_INIT_HOST(0xFF020000,0,0,0xFB)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_PROT_DNS_H */
diff --git a/lib/lwip/include/lwip/prot/etharp.h b/lib/lwip/include/lwip/prot/etharp.h
new file mode 100644
index 0000000000..811c228417
--- /dev/null
+++ b/lib/lwip/include/lwip/prot/etharp.h
@@ -0,0 +1,114 @@
+/**
+ * @file
+ * ARP protocol definitions
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef LWIP_HDR_PROT_ETHARP_H
+#define LWIP_HDR_PROT_ETHARP_H
+
+#include "lwip/arch.h"
+#include "lwip/prot/ethernet.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef ETHARP_HWADDR_LEN
+#define ETHARP_HWADDR_LEN     ETH_HWADDR_LEN
+#endif
+
+/**
+ * struct ip4_addr_wordaligned is used in the definition of the ARP packet format in
+ * order to support compilers that don't have structure packing.
+ */
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct ip4_addr_wordaligned {
+  PACK_STRUCT_FIELD(u16_t addrw[2]);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
+
+/** MEMCPY-like copying of IP addresses where addresses are known to be
+ * 16-bit-aligned if the port is correctly configured (so a port could define
+ * this to copying 2 u16_t's) - no NULL-pointer-checking needed. */
+#ifndef IPADDR_WORDALIGNED_COPY_TO_IP4_ADDR_T
+#define IPADDR_WORDALIGNED_COPY_TO_IP4_ADDR_T(dest, src) SMEMCPY(dest, src, sizeof(ip4_addr_t))
+#endif
+
+ /** MEMCPY-like copying of IP addresses where addresses are known to be
+ * 16-bit-aligned if the port is correctly configured (so a port could define
+ * this to copying 2 u16_t's) - no NULL-pointer-checking needed. */
+#ifndef IPADDR_WORDALIGNED_COPY_FROM_IP4_ADDR_T
+#define IPADDR_WORDALIGNED_COPY_FROM_IP4_ADDR_T(dest, src) SMEMCPY(dest, src, sizeof(ip4_addr_t))
+#endif
+
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+/** the ARP message, see RFC 826 ("Packet format") */
+struct etharp_hdr {
+  PACK_STRUCT_FIELD(u16_t hwtype);
+  PACK_STRUCT_FIELD(u16_t proto);
+  PACK_STRUCT_FLD_8(u8_t  hwlen);
+  PACK_STRUCT_FLD_8(u8_t  protolen);
+  PACK_STRUCT_FIELD(u16_t opcode);
+  PACK_STRUCT_FLD_S(struct eth_addr shwaddr);
+  PACK_STRUCT_FLD_S(struct ip4_addr_wordaligned sipaddr);
+  PACK_STRUCT_FLD_S(struct eth_addr dhwaddr);
+  PACK_STRUCT_FLD_S(struct ip4_addr_wordaligned dipaddr);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
+
+#define SIZEOF_ETHARP_HDR 28
+
+/* ARP message types (opcodes) */
+enum etharp_opcode {
+  ARP_REQUEST = 1,
+  ARP_REPLY   = 2
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_PROT_ETHARP_H */
diff --git a/lib/lwip/include/lwip/prot/ethernet.h b/lib/lwip/include/lwip/prot/ethernet.h
new file mode 100644
index 0000000000..d16b604089
--- /dev/null
+++ b/lib/lwip/include/lwip/prot/ethernet.h
@@ -0,0 +1,127 @@
+/**
+ * @file
+ * Ethernet protocol definitions
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef LWIP_HDR_PROT_ETHERNET_H
+#define LWIP_HDR_PROT_ETHERNET_H
+
+#include "lwip/arch.h"
+#include "lwip/prot/ieee.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef ETH_HWADDR_LEN
+#ifdef ETHARP_HWADDR_LEN
+#define ETH_HWADDR_LEN    ETHARP_HWADDR_LEN /* compatibility mode */
+#else
+#define ETH_HWADDR_LEN    6
+#endif
+#endif
+
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+/** An Ethernet MAC address */
+struct eth_addr {
+  PACK_STRUCT_FLD_8(u8_t addr[ETH_HWADDR_LEN]);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
+
+/** Initialize a struct eth_addr with its 6 bytes (takes care of correct braces) */
+#define ETH_ADDR(b0, b1, b2, b3, b4, b5) {{b0, b1, b2, b3, b4, b5}}
+
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+/** Ethernet header */
+struct eth_hdr {
+#if ETH_PAD_SIZE
+  PACK_STRUCT_FLD_8(u8_t padding[ETH_PAD_SIZE]);
+#endif
+  PACK_STRUCT_FLD_S(struct eth_addr dest);
+  PACK_STRUCT_FLD_S(struct eth_addr src);
+  PACK_STRUCT_FIELD(u16_t type);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
+
+#define SIZEOF_ETH_HDR (14 + ETH_PAD_SIZE)
+
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+/** VLAN header inserted between ethernet header and payload
+ * if 'type' in ethernet header is ETHTYPE_VLAN.
+ * See IEEE802.Q */
+struct eth_vlan_hdr {
+  PACK_STRUCT_FIELD(u16_t prio_vid);
+  PACK_STRUCT_FIELD(u16_t tpid);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
+
+#define SIZEOF_VLAN_HDR 4
+#define VLAN_ID(vlan_hdr) (lwip_htons((vlan_hdr)->prio_vid) & 0xFFF)
+
+/** The 24-bit IANA IPv4-multicast OUI is 01-00-5e: */
+#define LL_IP4_MULTICAST_ADDR_0 0x01
+#define LL_IP4_MULTICAST_ADDR_1 0x00
+#define LL_IP4_MULTICAST_ADDR_2 0x5e
+
+/** IPv6 multicast uses this prefix */
+#define LL_IP6_MULTICAST_ADDR_0 0x33
+#define LL_IP6_MULTICAST_ADDR_1 0x33
+
+/* eth_addr_cmp is deprecated, use eth_addr_eq */
+#define eth_addr_cmp(addr1, addr2) eth_addr_eq((addr1), (addr2))
+#define eth_addr_eq(addr1, addr2) (memcmp((addr1)->addr, (addr2)->addr, ETH_HWADDR_LEN) == 0)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_PROT_ETHERNET_H */
diff --git a/lib/lwip/include/lwip/prot/iana.h b/lib/lwip/include/lwip/prot/iana.h
new file mode 100644
index 0000000000..32890cccd3
--- /dev/null
+++ b/lib/lwip/include/lwip/prot/iana.h
@@ -0,0 +1,97 @@
+/**
+ * @file
+ * IANA assigned numbers (RFC 1700 and successors)
+ *
+ * @defgroup iana IANA assigned numbers
+ * @ingroup infrastructure
+ */
+
+/*
+ * Copyright (c) 2017 Dirk Ziegelmeier.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Dirk Ziegelmeier <dziegel@gmx.de>
+ *
+ */
+
+#ifndef LWIP_HDR_PROT_IANA_H
+#define LWIP_HDR_PROT_IANA_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @ingroup iana
+ * Hardware types
+ */
+enum lwip_iana_hwtype {
+  /** Ethernet */
+  LWIP_IANA_HWTYPE_ETHERNET = 1
+};
+
+/**
+ * @ingroup iana
+ * Port numbers
+ * https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.txt
+ */
+enum lwip_iana_port_number {
+  /** SMTP */
+  LWIP_IANA_PORT_SMTP        = 25,
+  /** DHCP server */
+  LWIP_IANA_PORT_DHCP_SERVER = 67,
+  /** DHCP client */
+  LWIP_IANA_PORT_DHCP_CLIENT = 68,
+  /** TFTP */
+  LWIP_IANA_PORT_TFTP        = 69,
+  /** HTTP */
+  LWIP_IANA_PORT_HTTP        = 80,
+  /** SNTP */
+  LWIP_IANA_PORT_SNTP        = 123,
+  /** NETBIOS */
+  LWIP_IANA_PORT_NETBIOS     = 137,
+  /** SNMP */
+  LWIP_IANA_PORT_SNMP        = 161,
+  /** SNMP traps */
+  LWIP_IANA_PORT_SNMP_TRAP   = 162,
+  /** HTTPS */
+  LWIP_IANA_PORT_HTTPS       = 443,
+  /** SMTPS */
+  LWIP_IANA_PORT_SMTPS       = 465,
+  /** MQTT */
+  LWIP_IANA_PORT_MQTT        = 1883,
+  /** MDNS */
+  LWIP_IANA_PORT_MDNS        = 5353,
+  /** Secure MQTT */
+  LWIP_IANA_PORT_SECURE_MQTT = 8883
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_PROT_IANA_H */
diff --git a/lib/lwip/include/lwip/prot/icmp.h b/lib/lwip/include/lwip/prot/icmp.h
new file mode 100644
index 0000000000..1fe12d9625
--- /dev/null
+++ b/lib/lwip/include/lwip/prot/icmp.h
@@ -0,0 +1,105 @@
+/**
+ * @file
+ * ICMP protocol definitions
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef LWIP_HDR_PROT_ICMP_H
+#define LWIP_HDR_PROT_ICMP_H
+
+#include "lwip/arch.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ICMP_ER   0    /* echo reply */
+#define ICMP_DUR  3    /* destination unreachable */
+#define ICMP_SQ   4    /* source quench */
+#define ICMP_RD   5    /* redirect */
+#define ICMP_ECHO 8    /* echo */
+#define ICMP_TE  11    /* time exceeded */
+#define ICMP_PP  12    /* parameter problem */
+#define ICMP_TS  13    /* timestamp */
+#define ICMP_TSR 14    /* timestamp reply */
+#define ICMP_IRQ 15    /* information request */
+#define ICMP_IR  16    /* information reply */
+#define ICMP_AM  17    /* address mask request */
+#define ICMP_AMR 18    /* address mask reply */
+
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
+/** The standard ICMP header (unspecified 32 bit data) */
+PACK_STRUCT_BEGIN
+struct icmp_hdr {
+  PACK_STRUCT_FLD_8(u8_t type);
+  PACK_STRUCT_FLD_8(u8_t code);
+  PACK_STRUCT_FIELD(u16_t chksum);
+  PACK_STRUCT_FIELD(u32_t data);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
+
+/* Compatibility defines, old versions used to combine type and code to an u16_t */
+#define ICMPH_TYPE(hdr) ((hdr)->type)
+#define ICMPH_CODE(hdr) ((hdr)->code)
+#define ICMPH_TYPE_SET(hdr, t) ((hdr)->type = (t))
+#define ICMPH_CODE_SET(hdr, c) ((hdr)->code = (c))
+
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
+/** This is the standard ICMP header only that the u32_t data
+ *  is split to two u16_t like ICMP echo needs it.
+ */
+PACK_STRUCT_BEGIN
+struct icmp_echo_hdr {
+  PACK_STRUCT_FLD_8(u8_t type);
+  PACK_STRUCT_FLD_8(u8_t code);
+  PACK_STRUCT_FIELD(u16_t chksum);
+  PACK_STRUCT_FIELD(u16_t id);
+  PACK_STRUCT_FIELD(u16_t seqno);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_PROT_ICMP_H */
diff --git a/lib/lwip/include/lwip/prot/icmp6.h b/lib/lwip/include/lwip/prot/icmp6.h
new file mode 100644
index 0000000000..36989f6b32
--- /dev/null
+++ b/lib/lwip/include/lwip/prot/icmp6.h
@@ -0,0 +1,172 @@
+/**
+ * @file
+ * ICMP6 protocol definitions
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef LWIP_HDR_PROT_ICMP6_H
+#define LWIP_HDR_PROT_ICMP6_H
+
+#include "lwip/arch.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** ICMP type */
+enum icmp6_type {
+  /** Destination unreachable */
+  ICMP6_TYPE_DUR = 1,
+  /** Packet too big */
+  ICMP6_TYPE_PTB = 2,
+  /** Time exceeded */
+  ICMP6_TYPE_TE = 3,
+  /** Parameter problem */
+  ICMP6_TYPE_PP = 4,
+  /** Private experimentation */
+  ICMP6_TYPE_PE1 = 100,
+  /** Private experimentation */
+  ICMP6_TYPE_PE2 = 101,
+  /** Reserved for expansion of error messages */
+  ICMP6_TYPE_RSV_ERR = 127,
+
+  /** Echo request */
+  ICMP6_TYPE_EREQ = 128,
+  /** Echo reply */
+  ICMP6_TYPE_EREP = 129,
+  /** Multicast listener query */
+  ICMP6_TYPE_MLQ = 130,
+  /** Multicast listener report */
+  ICMP6_TYPE_MLR = 131,
+  /** Multicast listener done */
+  ICMP6_TYPE_MLD = 132,
+  /** Router solicitation */
+  ICMP6_TYPE_RS = 133,
+  /** Router advertisement */
+  ICMP6_TYPE_RA = 134,
+  /** Neighbor solicitation */
+  ICMP6_TYPE_NS = 135,
+  /** Neighbor advertisement */
+  ICMP6_TYPE_NA = 136,
+  /** Redirect */
+  ICMP6_TYPE_RD = 137,
+  /** Multicast router advertisement */
+  ICMP6_TYPE_MRA = 151,
+  /** Multicast router solicitation */
+  ICMP6_TYPE_MRS = 152,
+  /** Multicast router termination */
+  ICMP6_TYPE_MRT = 153,
+  /** Private experimentation */
+  ICMP6_TYPE_PE3 = 200,
+  /** Private experimentation */
+  ICMP6_TYPE_PE4 = 201,
+  /** Reserved for expansion of informational messages */
+  ICMP6_TYPE_RSV_INF = 255
+};
+
+/** ICMP destination unreachable codes */
+enum icmp6_dur_code {
+  /** No route to destination */
+  ICMP6_DUR_NO_ROUTE = 0,
+  /** Communication with destination administratively prohibited */
+  ICMP6_DUR_PROHIBITED = 1,
+  /** Beyond scope of source address */
+  ICMP6_DUR_SCOPE = 2,
+  /** Address unreachable */
+  ICMP6_DUR_ADDRESS = 3,
+  /** Port unreachable */
+  ICMP6_DUR_PORT = 4,
+  /** Source address failed ingress/egress policy */
+  ICMP6_DUR_POLICY = 5,
+  /** Reject route to destination */
+  ICMP6_DUR_REJECT_ROUTE = 6
+};
+
+/** ICMP time exceeded codes */
+enum icmp6_te_code {
+  /** Hop limit exceeded in transit */
+  ICMP6_TE_HL = 0,
+  /** Fragment reassembly time exceeded */
+  ICMP6_TE_FRAG = 1
+};
+
+/** ICMP parameter code */
+enum icmp6_pp_code {
+  /** Erroneous header field encountered */
+  ICMP6_PP_FIELD = 0,
+  /** Unrecognized next header type encountered */
+  ICMP6_PP_HEADER = 1,
+  /** Unrecognized IPv6 option encountered */
+  ICMP6_PP_OPTION = 2
+};
+
+/** This is the standard ICMP6 header. */
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct icmp6_hdr {
+  PACK_STRUCT_FLD_8(u8_t type);
+  PACK_STRUCT_FLD_8(u8_t code);
+  PACK_STRUCT_FIELD(u16_t chksum);
+  PACK_STRUCT_FIELD(u32_t data);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
+
+#define ICMP6_HLEN 8
+
+/** This is the ICMP6 header adapted for echo req/resp. */
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct icmp6_echo_hdr {
+  PACK_STRUCT_FLD_8(u8_t type);
+  PACK_STRUCT_FLD_8(u8_t code);
+  PACK_STRUCT_FIELD(u16_t chksum);
+  PACK_STRUCT_FIELD(u16_t id);
+  PACK_STRUCT_FIELD(u16_t seqno);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_PROT_ICMP6_H */
diff --git a/lib/lwip/include/lwip/prot/ieee.h b/lib/lwip/include/lwip/prot/ieee.h
new file mode 100644
index 0000000000..cd4d2dea30
--- /dev/null
+++ b/lib/lwip/include/lwip/prot/ieee.h
@@ -0,0 +1,91 @@
+/**
+ * @file
+ * IEEE assigned numbers
+ *
+ * @defgroup ieee IEEE assigned numbers
+ * @ingroup infrastructure
+ */
+
+/*
+ * Copyright (c) 2017 Dirk Ziegelmeier.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Dirk Ziegelmeier <dziegel@gmx.de>
+ *
+ */
+
+#ifndef LWIP_HDR_PROT_IEEE_H
+#define LWIP_HDR_PROT_IEEE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @ingroup ieee
+ * A list of often ethtypes (although lwIP does not use all of them).
+ */
+enum lwip_ieee_eth_type {
+  /** Internet protocol v4 */
+  ETHTYPE_IP        = 0x0800U,
+  /** Address resolution protocol */
+  ETHTYPE_ARP       = 0x0806U,
+  /** Wake on lan */
+  ETHTYPE_WOL       = 0x0842U,
+  /** RARP */
+  ETHTYPE_RARP      = 0x8035U,
+  /** Virtual local area network */
+  ETHTYPE_VLAN      = 0x8100U,
+  /** Internet protocol v6 */
+  ETHTYPE_IPV6      = 0x86DDU,
+  /** PPP Over Ethernet Discovery Stage */
+  ETHTYPE_PPPOEDISC = 0x8863U,
+  /** PPP Over Ethernet Session Stage */
+  ETHTYPE_PPPOE     = 0x8864U,
+  /** Jumbo Frames */
+  ETHTYPE_JUMBO     = 0x8870U,
+  /** Process field network */
+  ETHTYPE_PROFINET  = 0x8892U,
+  /** Ethernet for control automation technology */
+  ETHTYPE_ETHERCAT  = 0x88A4U,
+  /** Link layer discovery protocol */
+  ETHTYPE_LLDP      = 0x88CCU,
+  /** Serial real-time communication system */
+  ETHTYPE_SERCOS    = 0x88CDU,
+  /** Media redundancy protocol */
+  ETHTYPE_MRP       = 0x88E3U,
+  /** Precision time protocol */
+  ETHTYPE_PTP       = 0x88F7U,
+  /** Q-in-Q, 802.1ad */
+  ETHTYPE_QINQ      = 0x9100U
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_PROT_IEEE_H */
diff --git a/lib/lwip/include/lwip/prot/igmp.h b/lib/lwip/include/lwip/prot/igmp.h
new file mode 100644
index 0000000000..46b81a9dd3
--- /dev/null
+++ b/lib/lwip/include/lwip/prot/igmp.h
@@ -0,0 +1,90 @@
+/**
+ * @file
+ * IGMP protocol definitions
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef LWIP_HDR_PROT_IGMP_H
+#define LWIP_HDR_PROT_IGMP_H
+
+#include "lwip/arch.h"
+#include "lwip/prot/ip4.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * IGMP constants
+ */
+#define IGMP_TTL                       1
+#define IGMP_MINLEN                    8
+#define ROUTER_ALERT                   0x9404U
+#define ROUTER_ALERTLEN                4
+
+/*
+ * IGMP message types, including version number.
+ */
+#define IGMP_MEMB_QUERY                0x11 /* Membership query         */
+#define IGMP_V1_MEMB_REPORT            0x12 /* Ver. 1 membership report */
+#define IGMP_V2_MEMB_REPORT            0x16 /* Ver. 2 membership report */
+#define IGMP_LEAVE_GROUP               0x17 /* Leave-group message      */
+
+/* Group  membership states */
+#define IGMP_GROUP_NON_MEMBER          0
+#define IGMP_GROUP_DELAYING_MEMBER     1
+#define IGMP_GROUP_IDLE_MEMBER         2
+
+/**
+ * IGMP packet format.
+ */
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct igmp_msg {
+  PACK_STRUCT_FLD_8(u8_t         igmp_msgtype);
+  PACK_STRUCT_FLD_8(u8_t         igmp_maxresp);
+  PACK_STRUCT_FIELD(u16_t        igmp_checksum);
+  PACK_STRUCT_FLD_S(ip4_addr_p_t igmp_group_address);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_PROT_IGMP_H */
diff --git a/lib/lwip/include/lwip/prot/ip.h b/lib/lwip/include/lwip/prot/ip.h
new file mode 100644
index 0000000000..223158f5ac
--- /dev/null
+++ b/lib/lwip/include/lwip/prot/ip.h
@@ -0,0 +1,59 @@
+/**
+ * @file
+ * IP protocol definitions
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef LWIP_HDR_PROT_IP_H
+#define LWIP_HDR_PROT_IP_H
+
+#include "lwip/arch.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define IP_PROTO_ICMP    1
+#define IP_PROTO_IGMP    2
+#define IP_PROTO_UDP     17
+#define IP_PROTO_UDPLITE 136
+#define IP_PROTO_TCP     6
+
+/** This operates on a void* by loading the first byte */
+#define IP_HDR_GET_VERSION(ptr)   ((*(u8_t*)(ptr)) >> 4)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_PROT_IP_H */
diff --git a/lib/lwip/include/lwip/prot/ip4.h b/lib/lwip/include/lwip/prot/ip4.h
new file mode 100644
index 0000000000..9347461154
--- /dev/null
+++ b/lib/lwip/include/lwip/prot/ip4.h
@@ -0,0 +1,131 @@
+/**
+ * @file
+ * IPv4 protocol definitions
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef LWIP_HDR_PROT_IP4_H
+#define LWIP_HDR_PROT_IP4_H
+
+#include "lwip/arch.h"
+#include "lwip/ip4_addr.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** This is the packed version of ip4_addr_t,
+    used in network headers that are itself packed */
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct ip4_addr_packed {
+  PACK_STRUCT_FIELD(u32_t addr);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
+
+typedef struct ip4_addr_packed ip4_addr_p_t;
+
+/* Size of the IPv4 header. Same as 'sizeof(struct ip_hdr)'. */
+#define IP_HLEN 20
+/* Maximum size of the IPv4 header with options. */
+#define IP_HLEN_MAX 60
+
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+/* The IPv4 header */
+struct ip_hdr {
+  /* version / header length */
+  PACK_STRUCT_FLD_8(u8_t _v_hl);
+  /* type of service */
+  PACK_STRUCT_FLD_8(u8_t _tos);
+  /* total length */
+  PACK_STRUCT_FIELD(u16_t _len);
+  /* identification */
+  PACK_STRUCT_FIELD(u16_t _id);
+  /* fragment offset field */
+  PACK_STRUCT_FIELD(u16_t _offset);
+#define IP_RF 0x8000U        /* reserved fragment flag */
+#define IP_DF 0x4000U        /* don't fragment flag */
+#define IP_MF 0x2000U        /* more fragments flag */
+#define IP_OFFMASK 0x1fffU   /* mask for fragmenting bits */
+  /* time to live */
+  PACK_STRUCT_FLD_8(u8_t _ttl);
+  /* protocol*/
+  PACK_STRUCT_FLD_8(u8_t _proto);
+  /* checksum */
+  PACK_STRUCT_FIELD(u16_t _chksum);
+  /* source and destination IP addresses */
+  PACK_STRUCT_FLD_S(ip4_addr_p_t src);
+  PACK_STRUCT_FLD_S(ip4_addr_p_t dest);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
+
+/* Macros to get struct ip_hdr fields: */
+#define IPH_V(hdr)  ((hdr)->_v_hl >> 4)
+#define IPH_HL(hdr) ((hdr)->_v_hl & 0x0f)
+#define IPH_HL_BYTES(hdr) ((u8_t)(IPH_HL(hdr) * 4))
+#define IPH_TOS(hdr) ((hdr)->_tos)
+#define IPH_LEN(hdr) ((hdr)->_len)
+#define IPH_ID(hdr) ((hdr)->_id)
+#define IPH_OFFSET(hdr) ((hdr)->_offset)
+#define IPH_OFFSET_BYTES(hdr) ((u16_t)((lwip_ntohs(IPH_OFFSET(hdr)) & IP_OFFMASK) * 8U))
+#define IPH_TTL(hdr) ((hdr)->_ttl)
+#define IPH_PROTO(hdr) ((hdr)->_proto)
+#define IPH_CHKSUM(hdr) ((hdr)->_chksum)
+
+/* Macros to set struct ip_hdr fields: */
+#define IPH_VHL_SET(hdr, v, hl) (hdr)->_v_hl = (u8_t)((((v) << 4) | (hl)))
+#define IPH_TOS_SET(hdr, tos) (hdr)->_tos = (tos)
+#define IPH_LEN_SET(hdr, len) (hdr)->_len = (len)
+#define IPH_ID_SET(hdr, id) (hdr)->_id = (id)
+#define IPH_OFFSET_SET(hdr, off) (hdr)->_offset = (off)
+#define IPH_TTL_SET(hdr, ttl) (hdr)->_ttl = (u8_t)(ttl)
+#define IPH_PROTO_SET(hdr, proto) (hdr)->_proto = (u8_t)(proto)
+#define IPH_CHKSUM_SET(hdr, chksum) (hdr)->_chksum = (chksum)
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_PROT_IP4_H */
diff --git a/lib/lwip/include/lwip/prot/ip6.h b/lib/lwip/include/lwip/prot/ip6.h
new file mode 100644
index 0000000000..7df81edd69
--- /dev/null
+++ b/lib/lwip/include/lwip/prot/ip6.h
@@ -0,0 +1,235 @@
+/**
+ * @file
+ * IPv6 protocol definitions
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef LWIP_HDR_PROT_IP6_H
+#define LWIP_HDR_PROT_IP6_H
+
+#include "lwip/arch.h"
+#include "lwip/ip6_addr.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define IP6_MIN_MTU_LENGTH 1280
+
+/** This is the packed version of ip6_addr_t,
+    used in network headers that are itself packed */
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct ip6_addr_packed {
+  PACK_STRUCT_FIELD(u32_t addr[4]);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
+typedef struct ip6_addr_packed ip6_addr_p_t;
+
+#define IP6_HLEN 40
+
+#define IP6_NEXTH_HOPBYHOP  0
+#define IP6_NEXTH_TCP       6
+#define IP6_NEXTH_UDP       17
+#define IP6_NEXTH_ENCAPS    41
+#define IP6_NEXTH_ROUTING   43
+#define IP6_NEXTH_FRAGMENT  44
+#define IP6_NEXTH_ICMP6     58
+#define IP6_NEXTH_NONE      59
+#define IP6_NEXTH_DESTOPTS  60
+#define IP6_NEXTH_UDPLITE   136
+
+/** The IPv6 header. */
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct ip6_hdr {
+  /** version / traffic class / flow label */
+  PACK_STRUCT_FIELD(u32_t _v_tc_fl);
+  /** payload length */
+  PACK_STRUCT_FIELD(u16_t _plen);
+  /** next header */
+  PACK_STRUCT_FLD_8(u8_t _nexth);
+  /** hop limit */
+  PACK_STRUCT_FLD_8(u8_t _hoplim);
+  /** source and destination IP addresses */
+  PACK_STRUCT_FLD_S(ip6_addr_p_t src);
+  PACK_STRUCT_FLD_S(ip6_addr_p_t dest);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
+#define IP6H_V(hdr)  ((lwip_ntohl((hdr)->_v_tc_fl) >> 28) & 0x0f)
+#define IP6H_TC(hdr) ((lwip_ntohl((hdr)->_v_tc_fl) >> 20) & 0xff)
+#define IP6H_FL(hdr) (lwip_ntohl((hdr)->_v_tc_fl) & 0x000fffff)
+#define IP6H_PLEN(hdr) (lwip_ntohs((hdr)->_plen))
+#define IP6H_NEXTH(hdr) ((hdr)->_nexth)
+#define IP6H_NEXTH_P(hdr) ((u8_t *)(hdr) + 6)
+#define IP6H_HOPLIM(hdr) ((hdr)->_hoplim)
+#define IP6H_VTCFL_SET(hdr, v, tc, fl) (hdr)->_v_tc_fl = (lwip_htonl((((u32_t)(v)) << 28) | (((u32_t)(tc)) << 20) | (fl)))
+#define IP6H_PLEN_SET(hdr, plen) (hdr)->_plen = lwip_htons(plen)
+#define IP6H_NEXTH_SET(hdr, nexth) (hdr)->_nexth = (nexth)
+#define IP6H_HOPLIM_SET(hdr, hl) (hdr)->_hoplim = (u8_t)(hl)
+
+/* ipv6 extended options header */
+#define IP6_PAD1_OPTION             0
+#define IP6_PADN_OPTION             1
+#define IP6_ROUTER_ALERT_OPTION     5
+#define IP6_JUMBO_OPTION            194
+#define IP6_HOME_ADDRESS_OPTION     201
+#define IP6_ROUTER_ALERT_DLEN       2
+#define IP6_ROUTER_ALERT_VALUE_MLD  0
+
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct ip6_opt_hdr {
+  /* router alert option type */
+  PACK_STRUCT_FLD_8(u8_t _opt_type);
+  /* router alert option data len */
+  PACK_STRUCT_FLD_8(u8_t _opt_dlen);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
+#define IP6_OPT_HLEN 2
+#define IP6_OPT_TYPE_ACTION(hdr) ((((hdr)->_opt_type) >> 6) & 0x3)
+#define IP6_OPT_TYPE_CHANGE(hdr) ((((hdr)->_opt_type) >> 5) & 0x1)
+#define IP6_OPT_TYPE(hdr) ((hdr)->_opt_type)
+#define IP6_OPT_DLEN(hdr) ((hdr)->_opt_dlen)
+
+/* Hop-by-Hop header. */
+#define IP6_HBH_HLEN    2
+
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct ip6_hbh_hdr {
+  /* next header */
+  PACK_STRUCT_FLD_8(u8_t _nexth);
+  /* header length in 8-octet units */
+  PACK_STRUCT_FLD_8(u8_t _hlen);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
+#define IP6_HBH_NEXTH(hdr) ((hdr)->_nexth)
+
+/* Destination header. */
+#define IP6_DEST_HLEN   2
+
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct ip6_dest_hdr {
+  /* next header */
+  PACK_STRUCT_FLD_8(u8_t _nexth);
+  /* header length in 8-octet units */
+  PACK_STRUCT_FLD_8(u8_t _hlen);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
+#define IP6_DEST_NEXTH(hdr) ((hdr)->_nexth)
+
+/* Routing header */
+#define IP6_ROUT_TYPE2  2
+#define IP6_ROUT_RPL    3
+
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct ip6_rout_hdr {
+  /* next header */
+  PACK_STRUCT_FLD_8(u8_t _nexth);
+  /* reserved */
+  PACK_STRUCT_FLD_8(u8_t _hlen);
+  /* fragment offset */
+  PACK_STRUCT_FIELD(u8_t _routing_type);
+  /* fragmented packet identification */
+  PACK_STRUCT_FIELD(u8_t _segments_left);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
+#define IP6_ROUT_NEXTH(hdr) ((hdr)->_nexth)
+#define IP6_ROUT_TYPE(hdr) ((hdr)->_routing_type)
+#define IP6_ROUT_SEG_LEFT(hdr) ((hdr)->_segments_left)
+
+/* Fragment header. */
+#define IP6_FRAG_HLEN    8
+#define IP6_FRAG_OFFSET_MASK    0xfff8
+#define IP6_FRAG_MORE_FLAG      0x0001
+
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct ip6_frag_hdr {
+  /* next header */
+  PACK_STRUCT_FLD_8(u8_t _nexth);
+  /* reserved */
+  PACK_STRUCT_FLD_8(u8_t reserved);
+  /* fragment offset */
+  PACK_STRUCT_FIELD(u16_t _fragment_offset);
+  /* fragmented packet identification */
+  PACK_STRUCT_FIELD(u32_t _identification);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
+#define IP6_FRAG_NEXTH(hdr) ((hdr)->_nexth)
+#define IP6_FRAG_MBIT(hdr) (lwip_ntohs((hdr)->_fragment_offset) & 0x1)
+#define IP6_FRAG_ID(hdr) (lwip_ntohl((hdr)->_identification))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_PROT_IP6_H */
diff --git a/lib/lwip/include/lwip/prot/mld6.h b/lib/lwip/include/lwip/prot/mld6.h
new file mode 100644
index 0000000000..71f1dcbdc5
--- /dev/null
+++ b/lib/lwip/include/lwip/prot/mld6.h
@@ -0,0 +1,71 @@
+/**
+ * @file
+ * MLD6 protocol definitions
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef LWIP_HDR_PROT_MLD6_H
+#define LWIP_HDR_PROT_MLD6_H
+
+#include "lwip/arch.h"
+#include "lwip/prot/ip6.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define MLD6_HBH_HLEN 8
+/** Multicast listener report/query/done message header. */
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct mld_header {
+  PACK_STRUCT_FLD_8(u8_t type);
+  PACK_STRUCT_FLD_8(u8_t code);
+  PACK_STRUCT_FIELD(u16_t chksum);
+  PACK_STRUCT_FIELD(u16_t max_resp_delay);
+  PACK_STRUCT_FIELD(u16_t reserved);
+  PACK_STRUCT_FLD_S(ip6_addr_p_t multicast_address);
+  /* Options follow. */
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_PROT_MLD6_H */
diff --git a/lib/lwip/include/lwip/prot/nd6.h b/lib/lwip/include/lwip/prot/nd6.h
new file mode 100644
index 0000000000..c270d07c13
--- /dev/null
+++ b/lib/lwip/include/lwip/prot/nd6.h
@@ -0,0 +1,274 @@
+/**
+ * @file
+ * ND6 protocol definitions
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef LWIP_HDR_PROT_ND6_H
+#define LWIP_HDR_PROT_ND6_H
+
+#include "lwip/arch.h"
+#include "lwip/ip6_addr.h"
+#include "lwip/prot/ip6.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Neighbor solicitation message header. */
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct ns_header {
+  PACK_STRUCT_FLD_8(u8_t type);
+  PACK_STRUCT_FLD_8(u8_t code);
+  PACK_STRUCT_FIELD(u16_t chksum);
+  PACK_STRUCT_FIELD(u32_t reserved);
+  PACK_STRUCT_FLD_S(ip6_addr_p_t target_address);
+  /* Options follow. */
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
+
+/** Neighbor advertisement message header. */
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct na_header {
+  PACK_STRUCT_FLD_8(u8_t type);
+  PACK_STRUCT_FLD_8(u8_t code);
+  PACK_STRUCT_FIELD(u16_t chksum);
+  PACK_STRUCT_FLD_8(u8_t flags);
+  PACK_STRUCT_FLD_8(u8_t reserved[3]);
+  PACK_STRUCT_FLD_S(ip6_addr_p_t target_address);
+  /* Options follow. */
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
+#define ND6_FLAG_ROUTER      (0x80)
+#define ND6_FLAG_SOLICITED   (0x40)
+#define ND6_FLAG_OVERRIDE    (0x20)
+
+/** Router solicitation message header. */
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct rs_header {
+  PACK_STRUCT_FLD_8(u8_t type);
+  PACK_STRUCT_FLD_8(u8_t code);
+  PACK_STRUCT_FIELD(u16_t chksum);
+  PACK_STRUCT_FIELD(u32_t reserved);
+  /* Options follow. */
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
+
+/** Router advertisement message header. */
+#define ND6_RA_FLAG_MANAGED_ADDR_CONFIG (0x80)
+#define ND6_RA_FLAG_OTHER_CONFIG (0x40)
+#define ND6_RA_FLAG_HOME_AGENT (0x20)
+#define ND6_RA_PREFERENCE_MASK (0x18)
+#define ND6_RA_PREFERENCE_HIGH (0x08)
+#define ND6_RA_PREFERENCE_MEDIUM (0x00)
+#define ND6_RA_PREFERENCE_LOW (0x18)
+#define ND6_RA_PREFERENCE_DISABLED (0x10)
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct ra_header {
+  PACK_STRUCT_FLD_8(u8_t type);
+  PACK_STRUCT_FLD_8(u8_t code);
+  PACK_STRUCT_FIELD(u16_t chksum);
+  PACK_STRUCT_FLD_8(u8_t current_hop_limit);
+  PACK_STRUCT_FLD_8(u8_t flags);
+  PACK_STRUCT_FIELD(u16_t router_lifetime);
+  PACK_STRUCT_FIELD(u32_t reachable_time);
+  PACK_STRUCT_FIELD(u32_t retrans_timer);
+  /* Options follow. */
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
+
+/** Redirect message header. */
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct redirect_header {
+  PACK_STRUCT_FLD_8(u8_t type);
+  PACK_STRUCT_FLD_8(u8_t code);
+  PACK_STRUCT_FIELD(u16_t chksum);
+  PACK_STRUCT_FIELD(u32_t reserved);
+  PACK_STRUCT_FLD_S(ip6_addr_p_t target_address);
+  PACK_STRUCT_FLD_S(ip6_addr_p_t destination_address);
+  /* Options follow. */
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
+
+/** Link-layer address option. */
+#define ND6_OPTION_TYPE_SOURCE_LLADDR (0x01)
+#define ND6_OPTION_TYPE_TARGET_LLADDR (0x02)
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct lladdr_option {
+  PACK_STRUCT_FLD_8(u8_t type);
+  PACK_STRUCT_FLD_8(u8_t length);
+  PACK_STRUCT_FLD_8(u8_t addr[NETIF_MAX_HWADDR_LEN]);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
+
+/** Prefix information option. */
+#define ND6_OPTION_TYPE_PREFIX_INFO (0x03)
+#define ND6_PREFIX_FLAG_ON_LINK (0x80)
+#define ND6_PREFIX_FLAG_AUTONOMOUS (0x40)
+#define ND6_PREFIX_FLAG_ROUTER_ADDRESS (0x20)
+#define ND6_PREFIX_FLAG_SITE_PREFIX (0x10)
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct prefix_option {
+  PACK_STRUCT_FLD_8(u8_t type);
+  PACK_STRUCT_FLD_8(u8_t length);
+  PACK_STRUCT_FLD_8(u8_t prefix_length);
+  PACK_STRUCT_FLD_8(u8_t flags);
+  PACK_STRUCT_FIELD(u32_t valid_lifetime);
+  PACK_STRUCT_FIELD(u32_t preferred_lifetime);
+  PACK_STRUCT_FLD_8(u8_t reserved2[3]);
+  PACK_STRUCT_FLD_8(u8_t site_prefix_length);
+  PACK_STRUCT_FLD_S(ip6_addr_p_t prefix);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
+
+/** Redirected header option. */
+#define ND6_OPTION_TYPE_REDIR_HDR (0x04)
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct redirected_header_option {
+  PACK_STRUCT_FLD_8(u8_t type);
+  PACK_STRUCT_FLD_8(u8_t length);
+  PACK_STRUCT_FLD_8(u8_t reserved[6]);
+  /* Portion of redirected packet follows. */
+  /* PACK_STRUCT_FLD_8(u8_t redirected[8]); */
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
+
+/** MTU option. */
+#define ND6_OPTION_TYPE_MTU (0x05)
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct mtu_option {
+  PACK_STRUCT_FLD_8(u8_t type);
+  PACK_STRUCT_FLD_8(u8_t length);
+  PACK_STRUCT_FIELD(u16_t reserved);
+  PACK_STRUCT_FIELD(u32_t mtu);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
+
+/** Route information option. */
+#define ND6_OPTION_TYPE_ROUTE_INFO (24)
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct route_option {
+  PACK_STRUCT_FLD_8(u8_t type);
+  PACK_STRUCT_FLD_8(u8_t length);
+  PACK_STRUCT_FLD_8(u8_t prefix_length);
+  PACK_STRUCT_FLD_8(u8_t preference);
+  PACK_STRUCT_FIELD(u32_t route_lifetime);
+  PACK_STRUCT_FLD_S(ip6_addr_p_t prefix);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
+
+/** Recursive DNS Server Option. */
+#define ND6_OPTION_TYPE_RDNSS (25)
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct rdnss_option {
+  PACK_STRUCT_FLD_8(u8_t type);
+  PACK_STRUCT_FLD_8(u8_t length);
+  PACK_STRUCT_FIELD(u16_t reserved);
+  PACK_STRUCT_FIELD(u32_t lifetime);
+  PACK_STRUCT_FLD_S(ip6_addr_p_t rdnss_address[1]);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
+
+#define SIZEOF_RDNSS_OPTION_BASE 8 /* size without addresses */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_PROT_ND6_H */
diff --git a/lib/lwip/include/lwip/prot/tcp.h b/lib/lwip/include/lwip/prot/tcp.h
new file mode 100644
index 0000000000..c1d7de1faf
--- /dev/null
+++ b/lib/lwip/include/lwip/prot/tcp.h
@@ -0,0 +1,100 @@
+/**
+ * @file
+ * TCP protocol definitions
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef LWIP_HDR_PROT_TCP_H
+#define LWIP_HDR_PROT_TCP_H
+
+#include "lwip/arch.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Length of the TCP header, excluding options. */
+#define TCP_HLEN 20
+
+/* Fields are (of course) in network byte order.
+ * Some fields are converted to host byte order in tcp_input().
+ */
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct tcp_hdr {
+  PACK_STRUCT_FIELD(u16_t src);
+  PACK_STRUCT_FIELD(u16_t dest);
+  PACK_STRUCT_FIELD(u32_t seqno);
+  PACK_STRUCT_FIELD(u32_t ackno);
+  PACK_STRUCT_FIELD(u16_t _hdrlen_rsvd_flags);
+  PACK_STRUCT_FIELD(u16_t wnd);
+  PACK_STRUCT_FIELD(u16_t chksum);
+  PACK_STRUCT_FIELD(u16_t urgp);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
+
+/* TCP header flags bits */
+#define TCP_FIN 0x01U
+#define TCP_SYN 0x02U
+#define TCP_RST 0x04U
+#define TCP_PSH 0x08U
+#define TCP_ACK 0x10U
+#define TCP_URG 0x20U
+#define TCP_ECE 0x40U
+#define TCP_CWR 0x80U
+/* Valid TCP header flags */
+#define TCP_FLAGS 0x3fU
+
+#define TCP_MAX_OPTION_BYTES 40
+
+#define TCPH_HDRLEN(phdr) ((u16_t)(lwip_ntohs((phdr)->_hdrlen_rsvd_flags) >> 12))
+#define TCPH_HDRLEN_BYTES(phdr) ((u8_t)(TCPH_HDRLEN(phdr) << 2))
+#define TCPH_FLAGS(phdr)  ((u8_t)((lwip_ntohs((phdr)->_hdrlen_rsvd_flags) & TCP_FLAGS)))
+
+#define TCPH_HDRLEN_SET(phdr, len) (phdr)->_hdrlen_rsvd_flags = lwip_htons(((len) << 12) | TCPH_FLAGS(phdr))
+#define TCPH_FLAGS_SET(phdr, flags) (phdr)->_hdrlen_rsvd_flags = (((phdr)->_hdrlen_rsvd_flags & PP_HTONS(~TCP_FLAGS)) | lwip_htons(flags))
+#define TCPH_HDRLEN_FLAGS_SET(phdr, len, flags) (phdr)->_hdrlen_rsvd_flags = (u16_t)(lwip_htons((u16_t)((len) << 12) | (flags)))
+
+#define TCPH_SET_FLAG(phdr, flags ) (phdr)->_hdrlen_rsvd_flags = ((phdr)->_hdrlen_rsvd_flags | lwip_htons(flags))
+#define TCPH_UNSET_FLAG(phdr, flags) (phdr)->_hdrlen_rsvd_flags = ((phdr)->_hdrlen_rsvd_flags & ~lwip_htons(flags))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_PROT_TCP_H */
diff --git a/lib/lwip/include/lwip/prot/udp.h b/lib/lwip/include/lwip/prot/udp.h
new file mode 100644
index 0000000000..664f19a3e7
--- /dev/null
+++ b/lib/lwip/include/lwip/prot/udp.h
@@ -0,0 +1,68 @@
+/**
+ * @file
+ * UDP protocol definitions
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef LWIP_HDR_PROT_UDP_H
+#define LWIP_HDR_PROT_UDP_H
+
+#include "lwip/arch.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define UDP_HLEN 8
+
+/* Fields are (of course) in network byte order. */
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct udp_hdr {
+  PACK_STRUCT_FIELD(u16_t src);
+  PACK_STRUCT_FIELD(u16_t dest);  /* src/dest UDP ports */
+  PACK_STRUCT_FIELD(u16_t len);
+  PACK_STRUCT_FIELD(u16_t chksum);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_PROT_UDP_H */
diff --git a/lib/lwip/include/lwip/raw.h b/lib/lwip/include/lwip/raw.h
new file mode 100644
index 0000000000..f916ff6b8e
--- /dev/null
+++ b/lib/lwip/include/lwip/raw.h
@@ -0,0 +1,143 @@
+/**
+ * @file
+ * raw API (to be used from TCPIP thread)<br>
+ * See also @ref raw_raw
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef LWIP_HDR_RAW_H
+#define LWIP_HDR_RAW_H
+
+#include "lwip/opt.h"
+
+#if LWIP_RAW /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/pbuf.h"
+#include "lwip/def.h"
+#include "lwip/ip.h"
+#include "lwip/ip_addr.h"
+#include "lwip/ip6_addr.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define RAW_FLAGS_CONNECTED      0x01U
+#define RAW_FLAGS_HDRINCL        0x02U
+#define RAW_FLAGS_MULTICAST_LOOP 0x04U
+
+struct raw_pcb;
+
+/** Function prototype for raw pcb receive callback functions.
+ * @param arg user supplied argument (raw_pcb.recv_arg)
+ * @param pcb the raw_pcb which received data
+ * @param p the packet buffer that was received
+ * @param addr the remote IP address from which the packet was received
+ * @return 1 if the packet was 'eaten' (aka. deleted),
+ *         0 if the packet lives on
+ * If returning 1, the callback is responsible for freeing the pbuf
+ * if it's not used any more.
+ */
+typedef u8_t (*raw_recv_fn)(void *arg, struct raw_pcb *pcb, struct pbuf *p,
+    const ip_addr_t *addr);
+
+/** the RAW protocol control block */
+struct raw_pcb {
+  /* Common members of all PCB types */
+  IP_PCB;
+
+  struct raw_pcb *next;
+
+  u8_t protocol;
+  u8_t flags;
+
+#if LWIP_MULTICAST_TX_OPTIONS
+  /** outgoing network interface for multicast packets, by interface index (if nonzero) */
+  u8_t mcast_ifindex;
+  /** TTL for outgoing multicast packets */
+  u8_t mcast_ttl;
+#endif /* LWIP_MULTICAST_TX_OPTIONS */
+
+  /** receive callback function */
+  raw_recv_fn recv;
+  /* user-supplied argument for the recv callback */
+  void *recv_arg;
+#if LWIP_IPV6
+  /* fields for handling checksum computations as per RFC3542. */
+  u16_t chksum_offset;
+  u8_t  chksum_reqd;
+#endif
+};
+
+/* The following functions is the application layer interface to the
+   RAW code. */
+struct raw_pcb * raw_new        (u8_t proto);
+struct raw_pcb * raw_new_ip_type(u8_t type, u8_t proto);
+void             raw_remove     (struct raw_pcb *pcb);
+err_t            raw_bind       (struct raw_pcb *pcb, const ip_addr_t *ipaddr);
+void             raw_bind_netif (struct raw_pcb *pcb, const struct netif *netif);
+err_t            raw_connect    (struct raw_pcb *pcb, const ip_addr_t *ipaddr);
+void             raw_disconnect (struct raw_pcb *pcb);
+
+err_t            raw_sendto     (struct raw_pcb *pcb, struct pbuf *p, const ip_addr_t *ipaddr);
+err_t            raw_sendto_if_src(struct raw_pcb *pcb, struct pbuf *p, const ip_addr_t *dst_ip, struct netif *netif, const ip_addr_t *src_ip);
+err_t            raw_send       (struct raw_pcb *pcb, struct pbuf *p);
+
+void             raw_recv       (struct raw_pcb *pcb, raw_recv_fn recv, void *recv_arg);
+
+#define          raw_flags(pcb) ((pcb)->flags)
+#define          raw_setflags(pcb,f)  ((pcb)->flags = (f))
+
+#define          raw_set_flags(pcb, set_flags)     do { (pcb)->flags = (u8_t)((pcb)->flags |  (set_flags)); } while(0)
+#define          raw_clear_flags(pcb, clr_flags)   do { (pcb)->flags = (u8_t)((pcb)->flags & (u8_t)(~(clr_flags) & 0xff)); } while(0)
+#define          raw_is_flag_set(pcb, flag)        (((pcb)->flags & (flag)) != 0)
+
+#define raw_init() /* Compatibility define, no init needed. */
+
+/* for compatibility with older implementation */
+#define raw_new_ip6(proto) raw_new_ip_type(IPADDR_TYPE_V6, proto)
+
+#if LWIP_MULTICAST_TX_OPTIONS
+#define raw_set_multicast_netif_index(pcb, idx) ((pcb)->mcast_ifindex = (idx))
+#define raw_get_multicast_netif_index(pcb)      ((pcb)->mcast_ifindex)
+#define raw_set_multicast_ttl(pcb, value)       ((pcb)->mcast_ttl = (value))
+#define raw_get_multicast_ttl(pcb)              ((pcb)->mcast_ttl)
+#endif /* LWIP_MULTICAST_TX_OPTIONS */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_RAW */
+
+#endif /* LWIP_HDR_RAW_H */
diff --git a/lib/lwip/include/lwip/sio.h b/lib/lwip/include/lwip/sio.h
new file mode 100644
index 0000000000..12d2a7e9b4
--- /dev/null
+++ b/lib/lwip/include/lwip/sio.h
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ */
+
+/*
+ * This is the interface to the platform specific serial IO module
+ * It needs to be implemented by those platforms which need SLIP or PPP
+ */
+
+#ifndef SIO_H
+#define SIO_H
+
+#include "lwip/arch.h"
+#include "lwip/opt.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* If you want to define sio_fd_t elsewhere or differently,
+   define this in your cc.h file. */
+#ifndef __sio_fd_t_defined
+typedef void * sio_fd_t;
+#endif
+
+/* The following functions can be defined to something else in your cc.h file
+   or be implemented in your custom sio.c file. */
+
+#ifndef sio_open
+/**
+ * Opens a serial device for communication.
+ *
+ * @param devnum device number
+ * @return handle to serial device if successful, NULL otherwise
+ */
+sio_fd_t sio_open(u8_t devnum);
+#endif
+
+#ifndef sio_send
+/**
+ * Sends a single character to the serial device.
+ *
+ * @param c character to send
+ * @param fd serial device handle
+ *
+ * @note This function will block until the character can be sent.
+ */
+void sio_send(u8_t c, sio_fd_t fd);
+#endif
+
+#ifndef sio_recv
+/**
+ * Receives a single character from the serial device.
+ *
+ * @param fd serial device handle
+ *
+ * @note This function will block until a character is received.
+ */
+u8_t sio_recv(sio_fd_t fd);
+#endif
+
+#ifndef sio_read
+/**
+ * Reads from the serial device.
+ *
+ * @param fd serial device handle
+ * @param data pointer to data buffer for receiving
+ * @param len maximum length (in bytes) of data to receive
+ * @return number of bytes actually received - may be 0 if aborted by sio_read_abort
+ *
+ * @note This function will block until data can be received. The blocking
+ * can be cancelled by calling sio_read_abort().
+ */
+u32_t sio_read(sio_fd_t fd, u8_t *data, u32_t len);
+#endif
+
+#ifndef sio_tryread
+/**
+ * Tries to read from the serial device. Same as sio_read but returns
+ * immediately if no data is available and never blocks.
+ *
+ * @param fd serial device handle
+ * @param data pointer to data buffer for receiving
+ * @param len maximum length (in bytes) of data to receive
+ * @return number of bytes actually received
+ */
+u32_t sio_tryread(sio_fd_t fd, u8_t *data, u32_t len);
+#endif
+
+#ifndef sio_write
+/**
+ * Writes to the serial device.
+ *
+ * @param fd serial device handle
+ * @param data pointer to data to send
+ * @param len length (in bytes) of data to send
+ * @return number of bytes actually sent
+ *
+ * @note This function will block until all data can be sent.
+ */
+u32_t sio_write(sio_fd_t fd, const u8_t *data, u32_t len);
+#endif
+
+#ifndef sio_read_abort
+/**
+ * Aborts a blocking sio_read() call.
+ *
+ * @param fd serial device handle
+ */
+void sio_read_abort(sio_fd_t fd);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SIO_H */
diff --git a/lib/lwip/include/lwip/snmp.h b/lib/lwip/include/lwip/snmp.h
new file mode 100644
index 0000000000..8704d0b4d2
--- /dev/null
+++ b/lib/lwip/include/lwip/snmp.h
@@ -0,0 +1,213 @@
+/**
+ * @file
+ * SNMP support API for implementing netifs and statitics for MIB2
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Dirk Ziegelmeier <dziegel@gmx.de>
+ *
+ */
+#ifndef LWIP_HDR_SNMP_H
+#define LWIP_HDR_SNMP_H
+
+#include "lwip/opt.h"
+#include "lwip/ip_addr.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct udp_pcb;
+struct netif;
+
+/**
+ * @defgroup netif_mib2 MIB2 statistics
+ * @ingroup netif
+ */
+
+/* MIB2 statistics functions */
+#if MIB2_STATS  /* don't build if not configured for use in lwipopts.h */
+/**
+ * @ingroup netif_mib2
+ * @see RFC1213, "MIB-II, 6. Definitions"
+ */
+enum snmp_ifType {
+  snmp_ifType_other=1,                /* none of the following */
+  snmp_ifType_regular1822,
+  snmp_ifType_hdh1822,
+  snmp_ifType_ddn_x25,
+  snmp_ifType_rfc877_x25,
+  snmp_ifType_ethernet_csmacd,
+  snmp_ifType_iso88023_csmacd,
+  snmp_ifType_iso88024_tokenBus,
+  snmp_ifType_iso88025_tokenRing,
+  snmp_ifType_iso88026_man,
+  snmp_ifType_starLan,
+  snmp_ifType_proteon_10Mbit,
+  snmp_ifType_proteon_80Mbit,
+  snmp_ifType_hyperchannel,
+  snmp_ifType_fddi,
+  snmp_ifType_lapb,
+  snmp_ifType_sdlc,
+  snmp_ifType_ds1,                    /* T-1 */
+  snmp_ifType_e1,                     /* european equiv. of T-1 */
+  snmp_ifType_basicISDN,
+  snmp_ifType_primaryISDN,            /* proprietary serial */
+  snmp_ifType_propPointToPointSerial,
+  snmp_ifType_ppp,
+  snmp_ifType_softwareLoopback,
+  snmp_ifType_eon,                    /* CLNP over IP [11] */
+  snmp_ifType_ethernet_3Mbit,
+  snmp_ifType_nsip,                   /* XNS over IP */
+  snmp_ifType_slip,                   /* generic SLIP */
+  snmp_ifType_ultra,                  /* ULTRA technologies */
+  snmp_ifType_ds3,                    /* T-3 */
+  snmp_ifType_sip,                    /* SMDS */
+  snmp_ifType_frame_relay
+};
+
+/** This macro has a precision of ~49 days because sys_now returns u32_t. \#define your own if you want ~490 days. */
+#ifndef MIB2_COPY_SYSUPTIME_TO
+#define MIB2_COPY_SYSUPTIME_TO(ptrToVal) (*(ptrToVal) = (sys_now() / 10))
+#endif
+
+/**
+ * @ingroup netif_mib2
+ * Increment stats member for SNMP MIB2 stats (struct stats_mib2_netif_ctrs)
+ */
+#define MIB2_STATS_NETIF_INC(n, x)      do { ++(n)->mib2_counters.x; } while(0)
+/**
+ * @ingroup netif_mib2
+ * Add value to stats member for SNMP MIB2 stats (struct stats_mib2_netif_ctrs)
+ */
+#define MIB2_STATS_NETIF_ADD(n, x, val) do { (n)->mib2_counters.x += (val); } while(0)
+
+/**
+ * @ingroup netif_mib2
+ * Init MIB2 statistic counters in netif
+ * @param netif Netif to init
+ * @param type one of enum @ref snmp_ifType
+ * @param speed your link speed here (units: bits per second)
+ */
+#define MIB2_INIT_NETIF(netif, type, speed) do { \
+  (netif)->link_type = (type);  \
+  (netif)->link_speed = (speed);\
+  (netif)->ts = 0;              \
+  (netif)->mib2_counters.ifinoctets = 0;      \
+  (netif)->mib2_counters.ifinucastpkts = 0;   \
+  (netif)->mib2_counters.ifinnucastpkts = 0;  \
+  (netif)->mib2_counters.ifindiscards = 0;    \
+  (netif)->mib2_counters.ifinerrors = 0;    \
+  (netif)->mib2_counters.ifinunknownprotos = 0;    \
+  (netif)->mib2_counters.ifoutoctets = 0;     \
+  (netif)->mib2_counters.ifoutucastpkts = 0;  \
+  (netif)->mib2_counters.ifoutnucastpkts = 0; \
+  (netif)->mib2_counters.ifoutdiscards = 0; \
+  (netif)->mib2_counters.ifouterrors = 0; } while(0)
+#else /* MIB2_STATS */
+#ifndef MIB2_COPY_SYSUPTIME_TO
+#define MIB2_COPY_SYSUPTIME_TO(ptrToVal)
+#endif
+#define MIB2_INIT_NETIF(netif, type, speed)
+#define MIB2_STATS_NETIF_INC(n, x)
+#define MIB2_STATS_NETIF_ADD(n, x, val)
+#endif /* MIB2_STATS */
+
+/* LWIP MIB2 callbacks */
+#if LWIP_MIB2_CALLBACKS /* don't build if not configured for use in lwipopts.h */
+/* network interface */
+void mib2_netif_added(struct netif *ni);
+void mib2_netif_removed(struct netif *ni);
+
+#if LWIP_IPV4 && LWIP_ARP
+/* ARP (for atTable and ipNetToMediaTable) */
+void mib2_add_arp_entry(struct netif *ni, ip4_addr_t *ip);
+void mib2_remove_arp_entry(struct netif *ni, ip4_addr_t *ip);
+#else /* LWIP_IPV4 && LWIP_ARP */
+#define mib2_add_arp_entry(ni,ip)
+#define mib2_remove_arp_entry(ni,ip)
+#endif /* LWIP_IPV4 && LWIP_ARP */
+
+/* IP */
+#if LWIP_IPV4
+void mib2_add_ip4(struct netif *ni);
+void mib2_remove_ip4(struct netif *ni);
+void mib2_add_route_ip4(u8_t dflt, struct netif *ni);
+void mib2_remove_route_ip4(u8_t dflt, struct netif *ni);
+#endif /* LWIP_IPV4 */
+
+/* UDP */
+#if LWIP_UDP
+void mib2_udp_bind(struct udp_pcb *pcb);
+void mib2_udp_unbind(struct udp_pcb *pcb);
+#endif /* LWIP_UDP */
+
+#else /* LWIP_MIB2_CALLBACKS */
+/* LWIP_MIB2_CALLBACKS support not available */
+/* define everything to be empty */
+
+/* network interface */
+#define mib2_netif_added(ni)
+#define mib2_netif_removed(ni)
+
+/* ARP */
+#define mib2_add_arp_entry(ni,ip)
+#define mib2_remove_arp_entry(ni,ip)
+
+/* IP */
+#define mib2_add_ip4(ni)
+#define mib2_remove_ip4(ni)
+#define mib2_add_route_ip4(dflt, ni)
+#define mib2_remove_route_ip4(dflt, ni)
+
+/* UDP */
+#define mib2_udp_bind(pcb)
+#define mib2_udp_unbind(pcb)
+#endif /* LWIP_MIB2_CALLBACKS */
+
+/* for source-code compatibility reasons only, can be removed (not used internally) */
+#define NETIF_INIT_SNMP                MIB2_INIT_NETIF
+#define snmp_add_ifinoctets(ni,value)  MIB2_STATS_NETIF_ADD(ni, ifinoctets, value)
+#define snmp_inc_ifinucastpkts(ni)     MIB2_STATS_NETIF_INC(ni, ifinucastpkts)
+#define snmp_inc_ifinnucastpkts(ni)    MIB2_STATS_NETIF_INC(ni, ifinnucastpkts)
+#define snmp_inc_ifindiscards(ni)      MIB2_STATS_NETIF_INC(ni, ifindiscards)
+#define snmp_inc_ifinerrors(ni)        MIB2_STATS_NETIF_INC(ni, ifinerrors)
+#define snmp_inc_ifinunknownprotos(ni) MIB2_STATS_NETIF_INC(ni, ifinunknownprotos)
+#define snmp_add_ifoutoctets(ni,value) MIB2_STATS_NETIF_ADD(ni, ifoutoctets, value)
+#define snmp_inc_ifoutucastpkts(ni)    MIB2_STATS_NETIF_INC(ni, ifoutucastpkts)
+#define snmp_inc_ifoutnucastpkts(ni)   MIB2_STATS_NETIF_INC(ni, ifoutnucastpkts)
+#define snmp_inc_ifoutdiscards(ni)     MIB2_STATS_NETIF_INC(ni, ifoutdiscards)
+#define snmp_inc_ifouterrors(ni)       MIB2_STATS_NETIF_INC(ni, ifouterrors)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_SNMP_H */
diff --git a/lib/lwip/include/lwip/sockets.h b/lib/lwip/include/lwip/sockets.h
new file mode 100644
index 0000000000..b6f3d524f1
--- /dev/null
+++ b/lib/lwip/include/lwip/sockets.h
@@ -0,0 +1,707 @@
+/**
+ * @file
+ * Socket API (to be used from non-TCPIP threads)
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+
+#ifndef LWIP_HDR_SOCKETS_H
+#define LWIP_HDR_SOCKETS_H
+
+#include "lwip/opt.h"
+
+#if LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */
+
+#if LWIP_SOCKET_EXTERNAL_HEADERS
+#include LWIP_SOCKET_EXTERNAL_HEADER_SOCKETS_H
+#else /* LWIP_SOCKET_EXTERNAL_HEADERS */
+
+#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+#include "lwip/err.h"
+#include "lwip/inet.h"
+#include "lwip/errno.h"
+
+#include <string.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* sockaddr and pals include length fields */
+#define LWIP_SOCKET_HAVE_SA_LEN  1
+
+/* If your port already typedef's sa_family_t, define SA_FAMILY_T_DEFINED
+   to prevent this code from redefining it. */
+#if !defined(sa_family_t) && !defined(SA_FAMILY_T_DEFINED)
+typedef u8_t sa_family_t;
+#endif
+/* If your port already typedef's in_port_t, define IN_PORT_T_DEFINED
+   to prevent this code from redefining it. */
+#if !defined(in_port_t) && !defined(IN_PORT_T_DEFINED)
+typedef u16_t in_port_t;
+#endif
+
+#if LWIP_IPV4
+/* members are in network byte order */
+struct sockaddr_in {
+  u8_t            sin_len;
+  sa_family_t     sin_family;
+  in_port_t       sin_port;
+  struct in_addr  sin_addr;
+#define SIN_ZERO_LEN 8
+  char            sin_zero[SIN_ZERO_LEN];
+};
+#endif /* LWIP_IPV4 */
+
+#if LWIP_IPV6
+struct sockaddr_in6 {
+  u8_t            sin6_len;      /* length of this structure    */
+  sa_family_t     sin6_family;   /* AF_INET6                    */
+  in_port_t       sin6_port;     /* Transport layer port #      */
+  u32_t           sin6_flowinfo; /* IPv6 flow information       */
+  struct in6_addr sin6_addr;     /* IPv6 address                */
+  u32_t           sin6_scope_id; /* Set of interfaces for scope */
+};
+#endif /* LWIP_IPV6 */
+
+struct sockaddr {
+  u8_t        sa_len;
+  sa_family_t sa_family;
+  char        sa_data[14];
+};
+
+struct sockaddr_storage {
+  u8_t        s2_len;
+  sa_family_t ss_family;
+  char        s2_data1[2];
+  u32_t       s2_data2[3];
+#if LWIP_IPV6
+  u32_t       s2_data3[3];
+#endif /* LWIP_IPV6 */
+};
+
+/* If your port already typedef's socklen_t, define SOCKLEN_T_DEFINED
+   to prevent this code from redefining it. */
+#if !defined(socklen_t) && !defined(SOCKLEN_T_DEFINED)
+typedef u32_t socklen_t;
+#endif
+
+#if !defined IOV_MAX
+#define IOV_MAX 0xFFFF
+#elif IOV_MAX > 0xFFFF
+#error "IOV_MAX larger than supported by LwIP"
+#endif /* IOV_MAX */
+
+#if !defined(iovec)
+struct iovec {
+  void  *iov_base;
+  size_t iov_len;
+};
+#endif
+
+typedef int msg_iovlen_t;
+
+struct msghdr {
+  void         *msg_name;
+  socklen_t     msg_namelen;
+  struct iovec *msg_iov;
+  msg_iovlen_t  msg_iovlen;
+  void         *msg_control;
+  socklen_t     msg_controllen;
+  int           msg_flags;
+};
+
+/* struct msghdr->msg_flags bit field values */
+#define MSG_TRUNC   0x04
+#define MSG_CTRUNC  0x08
+
+/* RFC 3542, Section 20: Ancillary Data */
+struct cmsghdr {
+  socklen_t  cmsg_len;   /* number of bytes, including header */
+  int        cmsg_level; /* originating protocol */
+  int        cmsg_type;  /* protocol-specific type */
+};
+/* Data section follows header and possible padding, typically referred to as
+      unsigned char cmsg_data[]; */
+
+/* cmsg header/data alignment. NOTE: we align to native word size (double word
+size on 16-bit arch) so structures are not placed at an unaligned address.
+16-bit arch needs double word to ensure 32-bit alignment because socklen_t
+could be 32 bits. If we ever have cmsg data with a 64-bit variable, alignment
+will need to increase long long */
+#define ALIGN_H(size) (((size) + sizeof(long) - 1U) & ~(sizeof(long)-1U))
+#define ALIGN_D(size) ALIGN_H(size)
+
+#define CMSG_FIRSTHDR(mhdr) \
+          ((mhdr)->msg_controllen >= sizeof(struct cmsghdr) ? \
+           (struct cmsghdr *)(mhdr)->msg_control : \
+           (struct cmsghdr *)NULL)
+
+#define CMSG_NXTHDR(mhdr, cmsg) \
+        (((cmsg) == NULL) ? CMSG_FIRSTHDR(mhdr) : \
+         (((u8_t *)(cmsg) + ALIGN_H((cmsg)->cmsg_len) \
+                            + ALIGN_D(sizeof(struct cmsghdr)) > \
+           (u8_t *)((mhdr)->msg_control) + (mhdr)->msg_controllen) ? \
+          (struct cmsghdr *)NULL : \
+          (struct cmsghdr *)((void*)((u8_t *)(cmsg) + \
+                                      ALIGN_H((cmsg)->cmsg_len)))))
+
+#define CMSG_DATA(cmsg) ((void*)((u8_t *)(cmsg) + \
+                         ALIGN_D(sizeof(struct cmsghdr))))
+
+#define CMSG_SPACE(length) (ALIGN_D(sizeof(struct cmsghdr)) + \
+                            ALIGN_H(length))
+
+#define CMSG_LEN(length) (ALIGN_D(sizeof(struct cmsghdr)) + \
+                           length)
+
+/* Set socket options argument */
+#define IFNAMSIZ NETIF_NAMESIZE
+struct ifreq {
+  char ifr_name[IFNAMSIZ]; /* Interface name */
+};
+
+/* Socket protocol types (TCP/UDP/RAW) */
+#define SOCK_STREAM     1
+#define SOCK_DGRAM      2
+#define SOCK_RAW        3
+
+/*
+ * Option flags per-socket. These must match the SOF_ flags in ip.h (checked in init.c)
+ */
+#define SO_REUSEADDR   0x0004 /* Allow local address reuse */
+#define SO_KEEPALIVE   0x0008 /* keep connections alive */
+#define SO_BROADCAST   0x0020 /* permit to send and to receive broadcast messages (see IP_SOF_BROADCAST option) */
+
+
+/*
+ * Additional options, not kept in so_options.
+ */
+#define SO_DEBUG        0x0001 /* Unimplemented: turn on debugging info recording */
+#define SO_ACCEPTCONN   0x0002 /* socket has had listen() */
+#define SO_DONTROUTE    0x0010 /* Unimplemented: just use interface addresses */
+#define SO_USELOOPBACK  0x0040 /* Unimplemented: bypass hardware when possible */
+#define SO_LINGER       0x0080 /* linger on close if data present */
+#define SO_DONTLINGER   ((int)(~SO_LINGER))
+#define SO_OOBINLINE    0x0100 /* Unimplemented: leave received OOB data in line */
+#define SO_REUSEPORT    0x0200 /* Unimplemented: allow local address & port reuse */
+#define SO_SNDBUF       0x1001 /* Unimplemented: send buffer size */
+#define SO_RCVBUF       0x1002 /* receive buffer size */
+#define SO_SNDLOWAT     0x1003 /* Unimplemented: send low-water mark */
+#define SO_RCVLOWAT     0x1004 /* Unimplemented: receive low-water mark */
+#define SO_SNDTIMEO     0x1005 /* send timeout */
+#define SO_RCVTIMEO     0x1006 /* receive timeout */
+#define SO_ERROR        0x1007 /* get error status and clear */
+#define SO_TYPE         0x1008 /* get socket type */
+#define SO_CONTIMEO     0x1009 /* Unimplemented: connect timeout */
+#define SO_NO_CHECK     0x100a /* don't create UDP checksum */
+#define SO_BINDTODEVICE 0x100b /* bind to device */
+
+/*
+ * Structure used for manipulating linger option.
+ */
+struct linger {
+  int l_onoff;                /* option on/off */
+  int l_linger;               /* linger time in seconds */
+};
+
+/*
+ * Level number for (get/set)sockopt() to apply to socket itself.
+ */
+#define  SOL_SOCKET  0xfff    /* options for socket level */
+
+
+#define AF_UNSPEC       0
+#define AF_INET         2
+#if LWIP_IPV6
+#define AF_INET6        10
+#else /* LWIP_IPV6 */
+#define AF_INET6        AF_UNSPEC
+#endif /* LWIP_IPV6 */
+#define PF_INET         AF_INET
+#define PF_INET6        AF_INET6
+#define PF_UNSPEC       AF_UNSPEC
+
+#define IPPROTO_IP      0
+#define IPPROTO_ICMP    1
+#define IPPROTO_TCP     6
+#define IPPROTO_UDP     17
+#if LWIP_IPV6
+#define IPPROTO_IPV6    41
+#define IPPROTO_ICMPV6  58
+#endif /* LWIP_IPV6 */
+#define IPPROTO_UDPLITE 136
+#define IPPROTO_RAW     255
+
+/* Flags we can use with send and recv. */
+#define MSG_PEEK       0x01    /* Peeks at an incoming message */
+#define MSG_WAITALL    0x02    /* Unimplemented: Requests that the function block until the full amount of data requested can be returned */
+#define MSG_OOB        0x04    /* Unimplemented: Requests out-of-band data. The significance and semantics of out-of-band data are protocol-specific */
+#define MSG_DONTWAIT   0x08    /* Nonblocking i/o for this operation only */
+#define MSG_MORE       0x10    /* Sender will send more */
+#define MSG_NOSIGNAL   0x20    /* Uninmplemented: Requests not to send the SIGPIPE signal if an attempt to send is made on a stream-oriented socket that is no longer connected. */
+
+
+/*
+ * Options for level IPPROTO_IP
+ */
+#define IP_TOS             1
+#define IP_TTL             2
+#define IP_PKTINFO         8
+
+#if LWIP_TCP
+/*
+ * Options for level IPPROTO_TCP
+ */
+#define TCP_NODELAY    0x01    /* don't delay send to coalesce packets */
+#define TCP_KEEPALIVE  0x02    /* send KEEPALIVE probes when idle for pcb->keep_idle milliseconds */
+#define TCP_KEEPIDLE   0x03    /* set pcb->keep_idle  - Same as TCP_KEEPALIVE, but use seconds for get/setsockopt */
+#define TCP_KEEPINTVL  0x04    /* set pcb->keep_intvl - Use seconds for get/setsockopt */
+#define TCP_KEEPCNT    0x05    /* set pcb->keep_cnt   - Use number of probes sent for get/setsockopt */
+#endif /* LWIP_TCP */
+
+#if LWIP_IPV6
+/*
+ * Options for level IPPROTO_IPV6
+ */
+#define IPV6_CHECKSUM       7  /* RFC3542: calculate and insert the ICMPv6 checksum for raw sockets. */
+#define IPV6_V6ONLY         27 /* RFC3493: boolean control to restrict AF_INET6 sockets to IPv6 communications only. */
+#endif /* LWIP_IPV6 */
+
+#if LWIP_UDP && LWIP_UDPLITE
+/*
+ * Options for level IPPROTO_UDPLITE
+ */
+#define UDPLITE_SEND_CSCOV 0x01 /* sender checksum coverage */
+#define UDPLITE_RECV_CSCOV 0x02 /* minimal receiver checksum coverage */
+#endif /* LWIP_UDP && LWIP_UDPLITE*/
+
+
+#if LWIP_MULTICAST_TX_OPTIONS
+/*
+ * Options and types for UDP multicast traffic handling
+ */
+#define IP_MULTICAST_TTL   5
+#define IP_MULTICAST_IF    6
+#define IP_MULTICAST_LOOP  7
+#endif /* LWIP_MULTICAST_TX_OPTIONS */
+
+#if LWIP_IGMP
+/*
+ * Options and types related to multicast membership
+ */
+#define IP_ADD_MEMBERSHIP  3
+#define IP_DROP_MEMBERSHIP 4
+
+typedef struct ip_mreq {
+    struct in_addr imr_multiaddr; /* IP multicast address of group */
+    struct in_addr imr_interface; /* local IP address of interface */
+} ip_mreq;
+#endif /* LWIP_IGMP */
+
+#if LWIP_IPV4
+struct in_pktinfo {
+  unsigned int   ipi_ifindex;  /* Interface index */
+  struct in_addr ipi_addr;     /* Destination (from header) address */
+};
+#endif /* LWIP_IPV4 */
+
+#if LWIP_IPV6_MLD
+/*
+ * Options and types related to IPv6 multicast membership
+ */
+#define IPV6_JOIN_GROUP      12
+#define IPV6_ADD_MEMBERSHIP  IPV6_JOIN_GROUP
+#define IPV6_LEAVE_GROUP     13
+#define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP
+
+typedef struct ipv6_mreq {
+  struct in6_addr ipv6mr_multiaddr; /*  IPv6 multicast addr */
+  unsigned int    ipv6mr_interface; /*  interface index, or 0 */
+} ipv6_mreq;
+#endif /* LWIP_IPV6_MLD */
+
+/*
+ * The Type of Service provides an indication of the abstract
+ * parameters of the quality of service desired.  These parameters are
+ * to be used to guide the selection of the actual service parameters
+ * when transmitting a datagram through a particular network.  Several
+ * networks offer service precedence, which somehow treats high
+ * precedence traffic as more important than other traffic (generally
+ * by accepting only traffic above a certain precedence at time of high
+ * load).  The major choice is a three way tradeoff between low-delay,
+ * high-reliability, and high-throughput.
+ * The use of the Delay, Throughput, and Reliability indications may
+ * increase the cost (in some sense) of the service.  In many networks
+ * better performance for one of these parameters is coupled with worse
+ * performance on another.  Except for very unusual cases at most two
+ * of these three indications should be set.
+ */
+#define IPTOS_TOS_MASK          0x1E
+#define IPTOS_TOS(tos)          ((tos) & IPTOS_TOS_MASK)
+#define IPTOS_LOWDELAY          0x10
+#define IPTOS_THROUGHPUT        0x08
+#define IPTOS_RELIABILITY       0x04
+#define IPTOS_LOWCOST           0x02
+#define IPTOS_MINCOST           IPTOS_LOWCOST
+
+/*
+ * The Network Control precedence designation is intended to be used
+ * within a network only.  The actual use and control of that
+ * designation is up to each network. The Internetwork Control
+ * designation is intended for use by gateway control originators only.
+ * If the actual use of these precedence designations is of concern to
+ * a particular network, it is the responsibility of that network to
+ * control the access to, and use of, those precedence designations.
+ */
+#define IPTOS_PREC_MASK                 0xe0
+#define IPTOS_PREC(tos)                ((tos) & IPTOS_PREC_MASK)
+#define IPTOS_PREC_NETCONTROL           0xe0
+#define IPTOS_PREC_INTERNETCONTROL      0xc0
+#define IPTOS_PREC_CRITIC_ECP           0xa0
+#define IPTOS_PREC_FLASHOVERRIDE        0x80
+#define IPTOS_PREC_FLASH                0x60
+#define IPTOS_PREC_IMMEDIATE            0x40
+#define IPTOS_PREC_PRIORITY             0x20
+#define IPTOS_PREC_ROUTINE              0x00
+
+
+/*
+ * Commands for ioctlsocket(),  taken from the BSD file fcntl.h.
+ * lwip_ioctl only supports FIONREAD and FIONBIO, for now
+ *
+ * Ioctl's have the command encoded in the lower word,
+ * and the size of any in or out parameters in the upper
+ * word.  The high 2 bits of the upper word are used
+ * to encode the in/out status of the parameter; for now
+ * we restrict parameters to at most 128 bytes.
+ */
+#if !defined(FIONREAD) || !defined(FIONBIO)
+#define IOCPARM_MASK    0x7fUL          /* parameters must be < 128 bytes */
+#define IOC_VOID        0x20000000UL    /* no parameters */
+#define IOC_OUT         0x40000000UL    /* copy out parameters */
+#define IOC_IN          0x80000000UL    /* copy in parameters */
+#define IOC_INOUT       (IOC_IN|IOC_OUT)
+                                        /* 0x20000000 distinguishes new &
+                                           old ioctl's */
+#define _IO(x,y)        ((long)(IOC_VOID|((x)<<8)|(y)))
+
+#define _IOR(x,y,t)     ((long)(IOC_OUT|((sizeof(t)&IOCPARM_MASK)<<16)|((x)<<8)|(y)))
+
+#define _IOW(x,y,t)     ((long)(IOC_IN|((sizeof(t)&IOCPARM_MASK)<<16)|((x)<<8)|(y)))
+#endif /* !defined(FIONREAD) || !defined(FIONBIO) */
+
+#ifndef FIONREAD
+#define FIONREAD    _IOR('f', 127, unsigned long) /* get # bytes to read */
+#endif
+#ifndef FIONBIO
+#define FIONBIO     _IOW('f', 126, unsigned long) /* set/clear non-blocking i/o */
+#endif
+
+/* Socket I/O Controls: unimplemented */
+#ifndef SIOCSHIWAT
+#define SIOCSHIWAT  _IOW('s',  0, unsigned long)  /* set high watermark */
+#define SIOCGHIWAT  _IOR('s',  1, unsigned long)  /* get high watermark */
+#define SIOCSLOWAT  _IOW('s',  2, unsigned long)  /* set low watermark */
+#define SIOCGLOWAT  _IOR('s',  3, unsigned long)  /* get low watermark */
+#define SIOCATMARK  _IOR('s',  7, unsigned long)  /* at oob mark? */
+#endif
+
+/* commands for fnctl */
+#ifndef F_GETFL
+#define F_GETFL 3
+#endif
+#ifndef F_SETFL
+#define F_SETFL 4
+#endif
+
+/* File status flags and file access modes for fnctl,
+   these are bits in an int. */
+#ifndef O_NONBLOCK
+#define O_NONBLOCK  1 /* nonblocking I/O */
+#endif
+#ifndef O_NDELAY
+#define O_NDELAY    O_NONBLOCK /* same as O_NONBLOCK, for compatibility */
+#endif
+#ifndef O_RDONLY
+#define O_RDONLY    2
+#endif
+#ifndef O_WRONLY
+#define O_WRONLY    4
+#endif
+#ifndef O_RDWR
+#define O_RDWR      (O_RDONLY|O_WRONLY)
+#endif
+
+#ifndef SHUT_RD
+  #define SHUT_RD   0
+  #define SHUT_WR   1
+  #define SHUT_RDWR 2
+#endif
+
+/* FD_SET used for lwip_select */
+#ifndef FD_SET
+#undef  FD_SETSIZE
+/* Make FD_SETSIZE match NUM_SOCKETS in socket.c */
+#define FD_SETSIZE    MEMP_NUM_NETCONN
+#define LWIP_SELECT_MAXNFDS (FD_SETSIZE + LWIP_SOCKET_OFFSET)
+#define FDSETSAFESET(n, code) do { \
+  if (((n) - LWIP_SOCKET_OFFSET < MEMP_NUM_NETCONN) && (((int)(n) - LWIP_SOCKET_OFFSET) >= 0)) { \
+  code; }} while(0)
+#define FDSETSAFEGET(n, code) (((n) - LWIP_SOCKET_OFFSET < MEMP_NUM_NETCONN) && (((int)(n) - LWIP_SOCKET_OFFSET) >= 0) ?\
+  (code) : 0)
+#define FD_SET(n, p)  FDSETSAFESET(n, (p)->fd_bits[((n)-LWIP_SOCKET_OFFSET)/8] = (u8_t)((p)->fd_bits[((n)-LWIP_SOCKET_OFFSET)/8] |  (1 << (((n)-LWIP_SOCKET_OFFSET) & 7))))
+#define FD_CLR(n, p)  FDSETSAFESET(n, (p)->fd_bits[((n)-LWIP_SOCKET_OFFSET)/8] = (u8_t)((p)->fd_bits[((n)-LWIP_SOCKET_OFFSET)/8] & ~(1 << (((n)-LWIP_SOCKET_OFFSET) & 7))))
+#define FD_ISSET(n,p) FDSETSAFEGET(n, (p)->fd_bits[((n)-LWIP_SOCKET_OFFSET)/8] &   (1 << (((n)-LWIP_SOCKET_OFFSET) & 7)))
+#define FD_ZERO(p)    memset((void*)(p), 0, sizeof(*(p)))
+
+typedef struct fd_set
+{
+  unsigned char fd_bits [(FD_SETSIZE+7)/8];
+} fd_set;
+
+#elif FD_SETSIZE < (LWIP_SOCKET_OFFSET + MEMP_NUM_NETCONN)
+#error "external FD_SETSIZE too small for number of sockets"
+#else
+#define LWIP_SELECT_MAXNFDS FD_SETSIZE
+#endif /* FD_SET */
+
+/* poll-related defines and types */
+/* @todo: find a better way to guard the definition of these defines and types if already defined */
+#if !defined(POLLIN) && !defined(POLLOUT)
+#define POLLIN     0x1
+#define POLLOUT    0x2
+#define POLLERR    0x4
+#define POLLNVAL   0x8
+/* Below values are unimplemented */
+#define POLLRDNORM 0x10
+#define POLLRDBAND 0x20
+#define POLLPRI    0x40
+#define POLLWRNORM 0x80
+#define POLLWRBAND 0x100
+#define POLLHUP    0x200
+typedef unsigned int nfds_t;
+struct pollfd
+{
+  int fd;
+  short events;
+  short revents;
+};
+#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 */
+#ifndef LWIP_TIMEVAL_PRIVATE
+#define LWIP_TIMEVAL_PRIVATE 1
+#endif
+
+#if LWIP_TIMEVAL_PRIVATE
+struct timeval {
+  long    tv_sec;         /* seconds */
+  long    tv_usec;        /* and microseconds */
+};
+#endif /* LWIP_TIMEVAL_PRIVATE */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_SOCKET_EXTERNAL_HEADERS */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define lwip_socket_init() /* Compatibility define, no init needed. */
+void lwip_socket_thread_init(void); /* LWIP_NETCONN_SEM_PER_THREAD==1: initialize thread-local semaphore */
+void lwip_socket_thread_cleanup(void); /* LWIP_NETCONN_SEM_PER_THREAD==1: destroy thread-local semaphore */
+
+#if LWIP_COMPAT_SOCKETS == 2
+/* This helps code parsers/code completion by not having the COMPAT functions as defines */
+#define lwip_accept       accept
+#define lwip_bind         bind
+#define lwip_shutdown     shutdown
+#define lwip_getpeername  getpeername
+#define lwip_getsockname  getsockname
+#define lwip_setsockopt   setsockopt
+#define lwip_getsockopt   getsockopt
+#define lwip_close        closesocket
+#define lwip_connect      connect
+#define lwip_listen       listen
+#define lwip_recv         recv
+#define lwip_recvmsg      recvmsg
+#define lwip_recvfrom     recvfrom
+#define lwip_send         send
+#define lwip_sendmsg      sendmsg
+#define lwip_sendto       sendto
+#define lwip_socket       socket
+#if LWIP_SOCKET_SELECT
+#define lwip_select       select
+#endif
+#if LWIP_SOCKET_POLL
+#define lwip_poll         poll
+#endif
+#define lwip_ioctl        ioctlsocket
+#define lwip_inet_ntop    inet_ntop
+#define lwip_inet_pton    inet_pton
+
+#if LWIP_POSIX_SOCKETS_IO_NAMES
+#define lwip_read         read
+#define lwip_readv        readv
+#define lwip_write        write
+#define lwip_writev       writev
+#undef lwip_close
+#define lwip_close        close
+#define closesocket(s)    close(s)
+int fcntl(int s, int cmd, ...);
+#undef lwip_ioctl
+#define lwip_ioctl        ioctl
+#define ioctlsocket       ioctl
+#endif /* LWIP_POSIX_SOCKETS_IO_NAMES */
+#endif /* LWIP_COMPAT_SOCKETS == 2 */
+
+int lwip_accept(int s, struct sockaddr *addr, socklen_t *addrlen);
+int lwip_bind(int s, const struct sockaddr *name, socklen_t namelen);
+int lwip_shutdown(int s, int how);
+int lwip_getpeername (int s, struct sockaddr *name, socklen_t *namelen);
+int lwip_getsockname (int s, struct sockaddr *name, socklen_t *namelen);
+int lwip_getsockopt (int s, int level, int optname, void *optval, socklen_t *optlen);
+int lwip_setsockopt (int s, int level, int optname, const void *optval, socklen_t optlen);
+ int lwip_close(int s);
+int lwip_connect(int s, const struct sockaddr *name, socklen_t namelen);
+int lwip_listen(int s, int backlog);
+ssize_t lwip_recv(int s, void *mem, size_t len, int flags);
+ssize_t lwip_read(int s, void *mem, size_t len);
+ssize_t lwip_readv(int s, const struct iovec *iov, int iovcnt);
+ssize_t lwip_recvfrom(int s, void *mem, size_t len, int flags,
+      struct sockaddr *from, socklen_t *fromlen);
+ssize_t lwip_recvmsg(int s, struct msghdr *message, int flags);
+ssize_t lwip_send(int s, const void *dataptr, size_t size, int flags);
+ssize_t lwip_sendmsg(int s, const struct msghdr *message, int flags);
+ssize_t lwip_sendto(int s, const void *dataptr, size_t size, int flags,
+    const struct sockaddr *to, socklen_t tolen);
+int lwip_socket(int domain, int type, int protocol);
+ssize_t lwip_write(int s, const void *dataptr, size_t size);
+ssize_t lwip_writev(int s, const struct iovec *iov, int iovcnt);
+#if LWIP_SOCKET_SELECT
+int lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset,
+                struct timeval *timeout);
+#endif
+#if LWIP_SOCKET_POLL
+int lwip_poll(struct pollfd *fds, nfds_t nfds, int timeout);
+#endif
+int lwip_ioctl(int s, long cmd, void *argp);
+int lwip_fcntl(int s, int cmd, int val);
+const char *lwip_inet_ntop(int af, const void *src, char *dst, socklen_t size);
+int lwip_inet_pton(int af, const char *src, void *dst);
+
+#if LWIP_COMPAT_SOCKETS
+#if LWIP_COMPAT_SOCKETS != 2
+/** @ingroup socket */
+#define accept(s,addr,addrlen)                    lwip_accept(s,addr,addrlen)
+/** @ingroup socket */
+#define bind(s,name,namelen)                      lwip_bind(s,name,namelen)
+/** @ingroup socket */
+#define shutdown(s,how)                           lwip_shutdown(s,how)
+/** @ingroup socket */
+#define getpeername(s,name,namelen)               lwip_getpeername(s,name,namelen)
+/** @ingroup socket */
+#define getsockname(s,name,namelen)               lwip_getsockname(s,name,namelen)
+/** @ingroup socket */
+#define setsockopt(s,level,optname,opval,optlen)  lwip_setsockopt(s,level,optname,opval,optlen)
+/** @ingroup socket */
+#define getsockopt(s,level,optname,opval,optlen)  lwip_getsockopt(s,level,optname,opval,optlen)
+/** @ingroup socket */
+#define closesocket(s)                            lwip_close(s)
+/** @ingroup socket */
+#define connect(s,name,namelen)                   lwip_connect(s,name,namelen)
+/** @ingroup socket */
+#define listen(s,backlog)                         lwip_listen(s,backlog)
+/** @ingroup socket */
+#define recv(s,mem,len,flags)                     lwip_recv(s,mem,len,flags)
+/** @ingroup socket */
+#define recvmsg(s,message,flags)                  lwip_recvmsg(s,message,flags)
+/** @ingroup socket */
+#define recvfrom(s,mem,len,flags,from,fromlen)    lwip_recvfrom(s,mem,len,flags,from,fromlen)
+/** @ingroup socket */
+#define send(s,dataptr,size,flags)                lwip_send(s,dataptr,size,flags)
+/** @ingroup socket */
+#define sendmsg(s,message,flags)                  lwip_sendmsg(s,message,flags)
+/** @ingroup socket */
+#define sendto(s,dataptr,size,flags,to,tolen)     lwip_sendto(s,dataptr,size,flags,to,tolen)
+/** @ingroup socket */
+#define socket(domain,type,protocol)              lwip_socket(domain,type,protocol)
+#if LWIP_SOCKET_SELECT
+/** @ingroup socket */
+#define select(maxfdp1,readset,writeset,exceptset,timeout)     lwip_select(maxfdp1,readset,writeset,exceptset,timeout)
+#endif
+#if LWIP_SOCKET_POLL
+/** @ingroup socket */
+#define poll(fds,nfds,timeout)                    lwip_poll(fds,nfds,timeout)
+#endif
+/** @ingroup socket */
+#define ioctlsocket(s,cmd,argp)                   lwip_ioctl(s,cmd,argp)
+/** @ingroup socket */
+#define inet_ntop(af,src,dst,size)                lwip_inet_ntop(af,src,dst,size)
+/** @ingroup socket */
+#define inet_pton(af,src,dst)                     lwip_inet_pton(af,src,dst)
+
+#if LWIP_POSIX_SOCKETS_IO_NAMES
+/** @ingroup socket */
+#define read(s,mem,len)                           lwip_read(s,mem,len)
+/** @ingroup socket */
+#define readv(s,iov,iovcnt)                       lwip_readv(s,iov,iovcnt)
+/** @ingroup socket */
+#define write(s,dataptr,len)                      lwip_write(s,dataptr,len)
+/** @ingroup socket */
+#define writev(s,iov,iovcnt)                      lwip_writev(s,iov,iovcnt)
+/** @ingroup socket */
+#define close(s)                                  lwip_close(s)
+/** @ingroup socket */
+#define fcntl(s,cmd,val)                          lwip_fcntl(s,cmd,val)
+/** @ingroup socket */
+#define ioctl(s,cmd,argp)                         lwip_ioctl(s,cmd,argp)
+#endif /* LWIP_POSIX_SOCKETS_IO_NAMES */
+#endif /* LWIP_COMPAT_SOCKETS != 2 */
+
+#endif /* LWIP_COMPAT_SOCKETS */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_SOCKET */
+
+#endif /* LWIP_HDR_SOCKETS_H */
diff --git a/lib/lwip/include/lwip/stats.h b/lib/lwip/include/lwip/stats.h
new file mode 100644
index 0000000000..b570dbacf5
--- /dev/null
+++ b/lib/lwip/include/lwip/stats.h
@@ -0,0 +1,491 @@
+/**
+ * @file
+ * Statistics API (to be used from TCPIP thread)
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef LWIP_HDR_STATS_H
+#define LWIP_HDR_STATS_H
+
+#include "lwip/opt.h"
+
+#include "lwip/mem.h"
+#include "lwip/memp.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if LWIP_STATS
+
+#ifndef LWIP_STATS_LARGE
+#define LWIP_STATS_LARGE 0
+#endif
+
+#if LWIP_STATS_LARGE
+#define STAT_COUNTER     u32_t
+#define STAT_COUNTER_F   U32_F
+#else
+#define STAT_COUNTER     u16_t
+#define STAT_COUNTER_F   U16_F
+#endif
+
+/** Protocol related stats */
+struct stats_proto {
+  STAT_COUNTER xmit;             /* Transmitted packets. */
+  STAT_COUNTER recv;             /* Received packets. */
+  STAT_COUNTER fw;               /* Forwarded packets. */
+  STAT_COUNTER drop;             /* Dropped packets. */
+  STAT_COUNTER chkerr;           /* Checksum error. */
+  STAT_COUNTER lenerr;           /* Invalid length error. */
+  STAT_COUNTER memerr;           /* Out of memory error. */
+  STAT_COUNTER rterr;            /* Routing error. */
+  STAT_COUNTER proterr;          /* Protocol error. */
+  STAT_COUNTER opterr;           /* Error in options. */
+  STAT_COUNTER err;              /* Misc error. */
+  STAT_COUNTER cachehit;
+};
+
+/** IGMP stats */
+struct stats_igmp {
+  STAT_COUNTER xmit;             /* Transmitted packets. */
+  STAT_COUNTER recv;             /* Received packets. */
+  STAT_COUNTER drop;             /* Dropped packets. */
+  STAT_COUNTER chkerr;           /* Checksum error. */
+  STAT_COUNTER lenerr;           /* Invalid length error. */
+  STAT_COUNTER memerr;           /* Out of memory error. */
+  STAT_COUNTER proterr;          /* Protocol error. */
+  STAT_COUNTER rx_v1;            /* Received v1 frames. */
+  STAT_COUNTER rx_group;         /* Received group-specific queries. */
+  STAT_COUNTER rx_general;       /* Received general queries. */
+  STAT_COUNTER rx_report;        /* Received reports. */
+  STAT_COUNTER tx_join;          /* Sent joins. */
+  STAT_COUNTER tx_leave;         /* Sent leaves. */
+  STAT_COUNTER tx_report;        /* Sent reports. */
+};
+
+/** Memory stats */
+struct stats_mem {
+#if defined(LWIP_DEBUG) || LWIP_STATS_DISPLAY
+  const char *name;
+#endif /* defined(LWIP_DEBUG) || LWIP_STATS_DISPLAY */
+  STAT_COUNTER err;
+  mem_size_t avail;
+  mem_size_t used;
+  mem_size_t max;
+  STAT_COUNTER illegal;
+};
+
+/** System element stats */
+struct stats_syselem {
+  STAT_COUNTER used;
+  STAT_COUNTER max;
+  STAT_COUNTER err;
+};
+
+/** System stats */
+struct stats_sys {
+  struct stats_syselem sem;
+  struct stats_syselem mutex;
+  struct stats_syselem mbox;
+};
+
+/** SNMP MIB2 stats */
+struct stats_mib2 {
+  /* IP */
+  u32_t ipinhdrerrors;
+  u32_t ipinaddrerrors;
+  u32_t ipinunknownprotos;
+  u32_t ipindiscards;
+  u32_t ipindelivers;
+  u32_t ipoutrequests;
+  u32_t ipoutdiscards;
+  u32_t ipoutnoroutes;
+  u32_t ipreasmoks;
+  u32_t ipreasmfails;
+  u32_t ipfragoks;
+  u32_t ipfragfails;
+  u32_t ipfragcreates;
+  u32_t ipreasmreqds;
+  u32_t ipforwdatagrams;
+  u32_t ipinreceives;
+
+  /* TCP */
+  u32_t tcpactiveopens;
+  u32_t tcppassiveopens;
+  u32_t tcpattemptfails;
+  u32_t tcpestabresets;
+  u32_t tcpoutsegs;
+  u32_t tcpretranssegs;
+  u32_t tcpinsegs;
+  u32_t tcpinerrs;
+  u32_t tcpoutrsts;
+
+  /* UDP */
+  u32_t udpindatagrams;
+  u32_t udpnoports;
+  u32_t udpinerrors;
+  u32_t udpoutdatagrams;
+
+  /* ICMP */
+  u32_t icmpinmsgs;
+  u32_t icmpinerrors;
+  u32_t icmpindestunreachs;
+  u32_t icmpintimeexcds;
+  u32_t icmpinparmprobs;
+  u32_t icmpinsrcquenchs;
+  u32_t icmpinredirects;
+  u32_t icmpinechos;
+  u32_t icmpinechoreps;
+  u32_t icmpintimestamps;
+  u32_t icmpintimestampreps;
+  u32_t icmpinaddrmasks;
+  u32_t icmpinaddrmaskreps;
+  u32_t icmpoutmsgs;
+  u32_t icmpouterrors;
+  u32_t icmpoutdestunreachs;
+  u32_t icmpouttimeexcds;
+  u32_t icmpoutechos; /* can be incremented by user application ('ping') */
+  u32_t icmpoutechoreps;
+};
+
+/**
+ * @ingroup netif_mib2
+ * SNMP MIB2 interface stats
+ */
+struct stats_mib2_netif_ctrs {
+  /** The total number of octets received on the interface, including framing characters */
+  u32_t ifinoctets;
+  /** The number of packets, delivered by this sub-layer to a higher (sub-)layer, which were
+   * not addressed to a multicast or broadcast address at this sub-layer */
+  u32_t ifinucastpkts;
+  /** The number of packets, delivered by this sub-layer to a higher (sub-)layer, which were
+   * addressed to a multicast or broadcast address at this sub-layer */
+  u32_t ifinnucastpkts;
+  /** The number of inbound packets which were chosen to be discarded even though no errors had
+   * been detected to prevent their being deliverable to a higher-layer protocol. One possible
+   * reason for discarding such a packet could be to free up buffer space */
+  u32_t ifindiscards;
+  /** For packet-oriented interfaces, the number of inbound packets that contained errors
+   * preventing them from being deliverable to a higher-layer protocol.  For character-
+   * oriented or fixed-length interfaces, the number of inbound transmission units that
+   * contained errors preventing them from being deliverable to a higher-layer protocol. */
+  u32_t ifinerrors;
+  /** For packet-oriented interfaces, the number of packets received via the interface which
+   * were discarded because of an unknown or unsupported protocol.  For character-oriented
+   * or fixed-length interfaces that support protocol multiplexing the number of transmission
+   * units received via the interface which were discarded because of an unknown or unsupported
+   * protocol. For any interface that does not support protocol multiplexing, this counter will
+   * always be 0 */
+  u32_t ifinunknownprotos;
+  /** The total number of octets transmitted out of the interface, including framing characters. */
+  u32_t ifoutoctets;
+  /** The total number of packets that higher-level protocols requested be transmitted, and
+   * which were not addressed to a multicast or broadcast address at this sub-layer, including
+   * those that were discarded or not sent. */
+  u32_t ifoutucastpkts;
+  /** The total number of packets that higher-level protocols requested be transmitted, and which
+   * were addressed to a multicast or broadcast address at this sub-layer, including
+   * those that were discarded or not sent. */
+  u32_t ifoutnucastpkts;
+  /** The number of outbound packets which were chosen to be discarded even though no errors had
+   * been detected to prevent their being transmitted.  One possible reason for discarding
+   * such a packet could be to free up buffer space. */
+  u32_t ifoutdiscards;
+  /** For packet-oriented interfaces, the number of outbound packets that could not be transmitted
+   * because of errors. For character-oriented or fixed-length interfaces, the number of outbound
+   * transmission units that could not be transmitted because of errors. */
+  u32_t ifouterrors;
+};
+
+/** lwIP stats container */
+struct stats_ {
+#if LINK_STATS
+  /** Link level */
+  struct stats_proto link;
+#endif
+#if ETHARP_STATS
+  /** ARP */
+  struct stats_proto etharp;
+#endif
+#if IPFRAG_STATS
+  /** Fragmentation */
+  struct stats_proto ip_frag;
+#endif
+#if IP_STATS
+  /** IP */
+  struct stats_proto ip;
+#endif
+#if ICMP_STATS
+  /** ICMP */
+  struct stats_proto icmp;
+#endif
+#if IGMP_STATS
+  /** IGMP */
+  struct stats_igmp igmp;
+#endif
+#if UDP_STATS
+  /** UDP */
+  struct stats_proto udp;
+#endif
+#if TCP_STATS
+  /** TCP */
+  struct stats_proto tcp;
+#endif
+#if MEM_STATS
+  /** Heap */
+  struct stats_mem mem;
+#endif
+#if MEMP_STATS
+  /** Internal memory pools */
+  struct stats_mem *memp[MEMP_MAX];
+#endif
+#if SYS_STATS
+  /** System */
+  struct stats_sys sys;
+#endif
+#if IP6_STATS
+  /** IPv6 */
+  struct stats_proto ip6;
+#endif
+#if ICMP6_STATS
+  /** ICMP6 */
+  struct stats_proto icmp6;
+#endif
+#if IP6_FRAG_STATS
+  /** IPv6 fragmentation */
+  struct stats_proto ip6_frag;
+#endif
+#if MLD6_STATS
+  /** Multicast listener discovery */
+  struct stats_igmp mld6;
+#endif
+#if ND6_STATS
+  /** Neighbor discovery */
+  struct stats_proto nd6;
+#endif
+#if MIB2_STATS
+  /** SNMP MIB2 */
+  struct stats_mib2 mib2;
+#endif
+};
+
+/** Global variable containing lwIP internal statistics. Add this to your debugger's watchlist. */
+extern struct stats_ lwip_stats;
+
+/** Init statistics */
+void stats_init(void);
+
+#define STATS_INC(x) ++lwip_stats.x
+#define STATS_DEC(x) --lwip_stats.x
+#define STATS_INC_USED(x, y, type) do { lwip_stats.x.used = (type)(lwip_stats.x.used + y); \
+                                if (lwip_stats.x.max < lwip_stats.x.used) { \
+                                    lwip_stats.x.max = lwip_stats.x.used; \
+                                } \
+                             } while(0)
+#define STATS_GET(x) lwip_stats.x
+#else /* LWIP_STATS */
+#define stats_init()
+#define STATS_INC(x)
+#define STATS_DEC(x)
+#define STATS_INC_USED(x, y, type)
+#endif /* LWIP_STATS */
+
+#if TCP_STATS
+#define TCP_STATS_INC(x) STATS_INC(x)
+#define TCP_STATS_DISPLAY() stats_display_proto(&lwip_stats.tcp, "TCP")
+#else
+#define TCP_STATS_INC(x)
+#define TCP_STATS_DISPLAY()
+#endif
+
+#if UDP_STATS
+#define UDP_STATS_INC(x) STATS_INC(x)
+#define UDP_STATS_DISPLAY() stats_display_proto(&lwip_stats.udp, "UDP")
+#else
+#define UDP_STATS_INC(x)
+#define UDP_STATS_DISPLAY()
+#endif
+
+#if ICMP_STATS
+#define ICMP_STATS_INC(x) STATS_INC(x)
+#define ICMP_STATS_DISPLAY() stats_display_proto(&lwip_stats.icmp, "ICMP")
+#else
+#define ICMP_STATS_INC(x)
+#define ICMP_STATS_DISPLAY()
+#endif
+
+#if IGMP_STATS
+#define IGMP_STATS_INC(x) STATS_INC(x)
+#define IGMP_STATS_DISPLAY() stats_display_igmp(&lwip_stats.igmp, "IGMP")
+#else
+#define IGMP_STATS_INC(x)
+#define IGMP_STATS_DISPLAY()
+#endif
+
+#if IP_STATS
+#define IP_STATS_INC(x) STATS_INC(x)
+#define IP_STATS_DISPLAY() stats_display_proto(&lwip_stats.ip, "IP")
+#else
+#define IP_STATS_INC(x)
+#define IP_STATS_DISPLAY()
+#endif
+
+#if IPFRAG_STATS
+#define IPFRAG_STATS_INC(x) STATS_INC(x)
+#define IPFRAG_STATS_DISPLAY() stats_display_proto(&lwip_stats.ip_frag, "IP_FRAG")
+#else
+#define IPFRAG_STATS_INC(x)
+#define IPFRAG_STATS_DISPLAY()
+#endif
+
+#if ETHARP_STATS
+#define ETHARP_STATS_INC(x) STATS_INC(x)
+#define ETHARP_STATS_DISPLAY() stats_display_proto(&lwip_stats.etharp, "ETHARP")
+#else
+#define ETHARP_STATS_INC(x)
+#define ETHARP_STATS_DISPLAY()
+#endif
+
+#if LINK_STATS
+#define LINK_STATS_INC(x) STATS_INC(x)
+#define LINK_STATS_DISPLAY() stats_display_proto(&lwip_stats.link, "LINK")
+#else
+#define LINK_STATS_INC(x)
+#define LINK_STATS_DISPLAY()
+#endif
+
+#if MEM_STATS
+#define MEM_STATS_AVAIL(x, y) lwip_stats.mem.x = y
+#define MEM_STATS_INC(x) STATS_INC(mem.x)
+#define MEM_STATS_INC_USED(x, y) STATS_INC_USED(mem, y, mem_size_t)
+#define MEM_STATS_DEC_USED(x, y) lwip_stats.mem.x = (mem_size_t)((lwip_stats.mem.x) - (y))
+#define MEM_STATS_DISPLAY() stats_display_mem(&lwip_stats.mem, "HEAP")
+#else
+#define MEM_STATS_AVAIL(x, y)
+#define MEM_STATS_INC(x)
+#define MEM_STATS_INC_USED(x, y)
+#define MEM_STATS_DEC_USED(x, y)
+#define MEM_STATS_DISPLAY()
+#endif
+
+ #if MEMP_STATS
+#define MEMP_STATS_DEC(x, i) STATS_DEC(memp[i]->x)
+#define MEMP_STATS_DISPLAY(i) stats_display_memp(lwip_stats.memp[i], i)
+#define MEMP_STATS_GET(x, i) STATS_GET(memp[i]->x)
+ #else
+#define MEMP_STATS_DEC(x, i)
+#define MEMP_STATS_DISPLAY(i)
+#define MEMP_STATS_GET(x, i) 0
+#endif
+
+#if SYS_STATS
+#define SYS_STATS_INC(x) STATS_INC(sys.x)
+#define SYS_STATS_DEC(x) STATS_DEC(sys.x)
+#define SYS_STATS_INC_USED(x) STATS_INC_USED(sys.x, 1, STAT_COUNTER)
+#define SYS_STATS_DISPLAY() stats_display_sys(&lwip_stats.sys)
+#else
+#define SYS_STATS_INC(x)
+#define SYS_STATS_DEC(x)
+#define SYS_STATS_INC_USED(x)
+#define SYS_STATS_DISPLAY()
+#endif
+
+#if IP6_STATS
+#define IP6_STATS_INC(x) STATS_INC(x)
+#define IP6_STATS_DISPLAY() stats_display_proto(&lwip_stats.ip6, "IPv6")
+#else
+#define IP6_STATS_INC(x)
+#define IP6_STATS_DISPLAY()
+#endif
+
+#if ICMP6_STATS
+#define ICMP6_STATS_INC(x) STATS_INC(x)
+#define ICMP6_STATS_DISPLAY() stats_display_proto(&lwip_stats.icmp6, "ICMPv6")
+#else
+#define ICMP6_STATS_INC(x)
+#define ICMP6_STATS_DISPLAY()
+#endif
+
+#if IP6_FRAG_STATS
+#define IP6_FRAG_STATS_INC(x) STATS_INC(x)
+#define IP6_FRAG_STATS_DISPLAY() stats_display_proto(&lwip_stats.ip6_frag, "IPv6 FRAG")
+#else
+#define IP6_FRAG_STATS_INC(x)
+#define IP6_FRAG_STATS_DISPLAY()
+#endif
+
+#if MLD6_STATS
+#define MLD6_STATS_INC(x) STATS_INC(x)
+#define MLD6_STATS_DISPLAY() stats_display_igmp(&lwip_stats.mld6, "MLDv1")
+#else
+#define MLD6_STATS_INC(x)
+#define MLD6_STATS_DISPLAY()
+#endif
+
+#if ND6_STATS
+#define ND6_STATS_INC(x) STATS_INC(x)
+#define ND6_STATS_DISPLAY() stats_display_proto(&lwip_stats.nd6, "ND")
+#else
+#define ND6_STATS_INC(x)
+#define ND6_STATS_DISPLAY()
+#endif
+
+#if MIB2_STATS
+#define MIB2_STATS_INC(x) STATS_INC(x)
+#else
+#define MIB2_STATS_INC(x)
+#endif
+
+/* Display of statistics */
+#if LWIP_STATS_DISPLAY
+void stats_display(void);
+void stats_display_proto(struct stats_proto *proto, const char *name);
+void stats_display_igmp(struct stats_igmp *igmp, const char *name);
+void stats_display_mem(struct stats_mem *mem, const char *name);
+void stats_display_memp(struct stats_mem *mem, int index);
+void stats_display_sys(struct stats_sys *sys);
+#else /* LWIP_STATS_DISPLAY */
+#define stats_display()
+#define stats_display_proto(proto, name)
+#define stats_display_igmp(igmp, name)
+#define stats_display_mem(mem, name)
+#define stats_display_memp(mem, index)
+#define stats_display_sys(sys)
+#endif /* LWIP_STATS_DISPLAY */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_STATS_H */
diff --git a/lib/lwip/include/lwip/sys.h b/lib/lwip/include/lwip/sys.h
new file mode 100644
index 0000000000..4bfdb13dba
--- /dev/null
+++ b/lib/lwip/include/lwip/sys.h
@@ -0,0 +1,575 @@
+/**
+ * @file
+ * OS abstraction layer
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ */
+
+#ifndef LWIP_HDR_SYS_H
+#define LWIP_HDR_SYS_H
+
+#include "lwip/opt.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if NO_SYS
+
+/* For a totally minimal and standalone system, we provide null
+   definitions of the sys_ functions. */
+typedef u8_t sys_sem_t;
+typedef u8_t sys_mutex_t;
+typedef u8_t sys_mbox_t;
+
+#define sys_sem_new(s, c) ERR_OK
+#define sys_sem_signal(s)
+#define sys_sem_wait(s)
+#define sys_arch_sem_wait(s,t)
+#define sys_sem_free(s)
+#define sys_sem_valid(s) 0
+#define sys_sem_valid_val(s) 0
+#define sys_sem_set_invalid(s)
+#define sys_sem_set_invalid_val(s)
+#define sys_mutex_new(mu) ERR_OK
+#define sys_mutex_lock(mu)
+#define sys_mutex_unlock(mu)
+#define sys_mutex_free(mu)
+#define sys_mutex_valid(mu) 0
+#define sys_mutex_set_invalid(mu)
+#define sys_mbox_new(m, s) ERR_OK
+#define sys_mbox_fetch(m,d)
+#define sys_mbox_tryfetch(m,d)
+#define sys_mbox_post(m,d)
+#define sys_mbox_trypost(m,d)
+#define sys_mbox_free(m)
+#define sys_mbox_valid(m)
+#define sys_mbox_valid_val(m)
+#define sys_mbox_set_invalid(m)
+#define sys_mbox_set_invalid_val(m)
+
+#define sys_thread_new(n,t,a,s,p)
+
+#define sys_msleep(t)
+
+#else /* NO_SYS */
+
+/** Return code for timeouts from sys_arch_mbox_fetch and sys_arch_sem_wait */
+#define SYS_ARCH_TIMEOUT 0xffffffffUL
+
+/** sys_mbox_tryfetch() returns SYS_MBOX_EMPTY if appropriate.
+ * For now we use the same magic value, but we allow this to change in future.
+ */
+#define SYS_MBOX_EMPTY SYS_ARCH_TIMEOUT
+
+#include "lwip/err.h"
+#include "arch/sys_arch.h"
+
+/** Function prototype for thread functions */
+typedef void (*lwip_thread_fn)(void *arg);
+
+/* Function prototypes for functions to be implemented by platform ports
+   (in sys_arch.c) */
+
+/* Mutex functions: */
+
+/** Define LWIP_COMPAT_MUTEX if the port has no mutexes and binary semaphores
+    should be used instead */
+#ifndef LWIP_COMPAT_MUTEX
+#define LWIP_COMPAT_MUTEX 0
+#endif
+
+#if LWIP_COMPAT_MUTEX
+/* for old ports that don't have mutexes: define them to binary semaphores */
+#define sys_mutex_t                   sys_sem_t
+#define sys_mutex_new(mutex)          sys_sem_new(mutex, 1)
+#define sys_mutex_lock(mutex)         sys_sem_wait(mutex)
+#define sys_mutex_unlock(mutex)       sys_sem_signal(mutex)
+#define sys_mutex_free(mutex)         sys_sem_free(mutex)
+#define sys_mutex_valid(mutex)        sys_sem_valid(mutex)
+#define sys_mutex_set_invalid(mutex)  sys_sem_set_invalid(mutex)
+
+#else /* LWIP_COMPAT_MUTEX */
+
+/**
+ * @ingroup sys_mutex
+ * Create a new mutex.
+ * Note that mutexes are expected to not be taken recursively by the lwIP code,
+ * so both implementation types (recursive or non-recursive) should work.
+ * The mutex is allocated to the memory that 'mutex'
+ * points to (which can be both a pointer or the actual OS structure).
+ * If the mutex has been created, ERR_OK should be returned. Returning any
+ * other error will provide a hint what went wrong, but except for assertions,
+ * no real error handling is implemented.
+ *
+ * @param mutex pointer to the mutex to create
+ * @return ERR_OK if successful, another err_t otherwise
+ */
+err_t sys_mutex_new(sys_mutex_t *mutex);
+/**
+ * @ingroup sys_mutex
+ * Blocks the thread until the mutex can be grabbed.
+ * @param mutex the mutex to lock
+ */
+void sys_mutex_lock(sys_mutex_t *mutex);
+/**
+ * @ingroup sys_mutex
+ * Releases the mutex previously locked through 'sys_mutex_lock()'.
+ * @param mutex the mutex to unlock
+ */
+void sys_mutex_unlock(sys_mutex_t *mutex);
+/**
+ * @ingroup sys_mutex
+ * Deallocates a mutex.
+ * @param mutex the mutex to delete
+ */
+void sys_mutex_free(sys_mutex_t *mutex);
+#ifndef sys_mutex_valid
+/**
+ * @ingroup sys_mutex
+ * Returns 1 if the mutes is valid, 0 if it is not valid.
+ * When using pointers, a simple way is to check the pointer for != NULL.
+ * When directly using OS structures, implementing this may be more complex.
+ * This may also be a define, in which case the function is not prototyped.
+ */
+int sys_mutex_valid(sys_mutex_t *mutex);
+#endif
+#ifndef sys_mutex_set_invalid
+/**
+ * @ingroup sys_mutex
+ * Invalidate a mutex so that sys_mutex_valid() returns 0.
+ * ATTENTION: This does NOT mean that the mutex shall be deallocated:
+ * sys_mutex_free() is always called before calling this function!
+ * This may also be a define, in which case the function is not prototyped.
+ */
+void sys_mutex_set_invalid(sys_mutex_t *mutex);
+#endif
+#endif /* LWIP_COMPAT_MUTEX */
+
+/* Semaphore functions: */
+
+/**
+ * @ingroup sys_sem
+ * Create a new semaphore
+ * Creates a new semaphore. The semaphore is allocated to the memory that 'sem'
+ * points to (which can be both a pointer or the actual OS structure).
+ * The "count" argument specifies the initial state of the semaphore (which is
+ * either 0 or 1).
+ * If the semaphore has been created, ERR_OK should be returned. Returning any
+ * other error will provide a hint what went wrong, but except for assertions,
+ * no real error handling is implemented.
+ *
+ * @param sem pointer to the semaphore to create
+ * @param count initial count of the semaphore
+ * @return ERR_OK if successful, another err_t otherwise
+ */
+err_t sys_sem_new(sys_sem_t *sem, u8_t count);
+/**
+ * @ingroup sys_sem
+ * Signals a semaphore
+ * @param sem the semaphore to signal
+ */
+void sys_sem_signal(sys_sem_t *sem);
+/**
+ * @ingroup sys_sem
+ *  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.
+ *
+ * @param sem the semaphore to wait for
+ * @param timeout timeout in milliseconds to wait (0 = wait forever)
+ * @return SYS_ARCH_TIMEOUT on timeout, any other value on success
+ */
+u32_t sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout);
+/**
+ * @ingroup sys_sem
+ * Deallocates a semaphore.
+ * @param sem semaphore to delete
+ */
+void sys_sem_free(sys_sem_t *sem);
+/** Wait for a semaphore - forever/no timeout */
+#define sys_sem_wait(sem)                  sys_arch_sem_wait(sem, 0)
+#ifndef sys_sem_valid
+/**
+ * @ingroup sys_sem
+ * Returns 1 if the semaphore is valid, 0 if it is not valid.
+ * When using pointers, a simple way is to check the pointer for != NULL.
+ * When directly using OS structures, implementing this may be more complex.
+ * This may also be a define, in which case the function is not prototyped.
+ */
+int sys_sem_valid(sys_sem_t *sem);
+#endif
+#ifndef sys_sem_set_invalid
+/**
+ * @ingroup sys_sem
+ * Invalidate a semaphore so that sys_sem_valid() returns 0.
+ * ATTENTION: This does NOT mean that the semaphore shall be deallocated:
+ * sys_sem_free() is always called before calling this function!
+ * This may also be a define, in which case the function is not prototyped.
+ */
+void sys_sem_set_invalid(sys_sem_t *sem);
+#endif
+#ifndef sys_sem_valid_val
+/**
+ * Same as sys_sem_valid() but taking a value, not a pointer
+ */
+#define sys_sem_valid_val(sem)       sys_sem_valid(&(sem))
+#endif
+#ifndef sys_sem_set_invalid_val
+/**
+ * Same as sys_sem_set_invalid() but taking a value, not a pointer
+ */
+#define sys_sem_set_invalid_val(sem) sys_sem_set_invalid(&(sem))
+#endif
+
+#ifndef sys_msleep
+/**
+ * @ingroup sys_misc
+ * Sleep for specified number of ms
+ */
+void sys_msleep(u32_t ms); /* only has a (close to) 1 ms resolution. */
+#endif
+
+/* Mailbox functions. */
+
+/**
+ * @ingroup sys_mbox
+ * Creates an empty mailbox for maximum "size" elements. Elements stored
+ * in mailboxes are pointers. You have to define macros "_MBOX_SIZE"
+ * in your lwipopts.h, or ignore this parameter in your implementation
+ * and use a default size.
+ * If the mailbox has been created, ERR_OK should be returned. Returning any
+ * other error will provide a hint what went wrong, but except for assertions,
+ * no real error handling is implemented.
+ *
+ * @param mbox pointer to the mbox to create
+ * @param size (minimum) number of messages in this mbox
+ * @return ERR_OK if successful, another err_t otherwise
+ */
+err_t sys_mbox_new(sys_mbox_t *mbox, int size);
+/**
+ * @ingroup sys_mbox
+ * Post a message to an mbox - may not fail
+ * -> blocks if full, only to be used from tasks NOT from ISR!
+ *
+ * @param mbox mbox to posts the message
+ * @param msg message to post (ATTENTION: can be NULL)
+ */
+void sys_mbox_post(sys_mbox_t *mbox, void *msg);
+/**
+ * @ingroup sys_mbox
+ * Try to post a message to an mbox - may fail if full.
+ * Can be used from ISR (if the sys arch layer allows this).
+ * Returns ERR_MEM if it is full, else, ERR_OK if the "msg" is posted.
+ *
+ * @param mbox mbox to posts the message
+ * @param msg message to post (ATTENTION: can be NULL)
+ */
+err_t sys_mbox_trypost(sys_mbox_t *mbox, void *msg);
+/**
+ * @ingroup sys_mbox
+ * Try to post a message to an mbox - may fail if full.
+ * To be be used from ISR.
+ * Returns ERR_MEM if it is full, else, ERR_OK if the "msg" is posted.
+ *
+ * @param mbox mbox to posts the message
+ * @param msg message to post (ATTENTION: can be NULL)
+ */
+err_t sys_mbox_trypost_fromisr(sys_mbox_t *mbox, void *msg);
+/**
+ * @ingroup sys_mbox
+ * Blocks the thread until a message arrives in the mailbox, but does
+ * not block the thread longer than "timeout" milliseconds (similar to
+ * the sys_arch_sem_wait() function). If "timeout" is 0, the thread should
+ * be blocked until a message arrives. The "msg" argument is a result
+ * parameter that is set by the function (i.e., by doing "*msg =
+ * ptr"). The "msg" parameter maybe NULL to indicate that the message
+ * should be dropped.
+ * The return values are the same as for the sys_arch_sem_wait() function:
+ * 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.
+ *
+ * @param mbox mbox to get a message from
+ * @param msg pointer where the message is stored
+ * @param timeout maximum time (in milliseconds) to wait for a message (0 = wait forever)
+ * @return SYS_ARCH_TIMEOUT on timeout, any other value if a message has been received
+ */
+u32_t sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout);
+/* Allow port to override with a macro, e.g. special timeout for sys_arch_mbox_fetch() */
+#ifndef sys_arch_mbox_tryfetch
+/**
+ * @ingroup sys_mbox
+ * This is similar to sys_arch_mbox_fetch, however if a message is not
+ * present in the mailbox, it immediately returns with the code
+ * SYS_MBOX_EMPTY. On success 0 is returned.
+ * To allow for efficient implementations, this can be defined as a
+ * function-like macro in sys_arch.h instead of a normal function. For
+ * example, a naive implementation could be:
+ * \#define sys_arch_mbox_tryfetch(mbox,msg) sys_arch_mbox_fetch(mbox,msg,1)
+ * although this would introduce unnecessary delays.
+ *
+ * @param mbox mbox to get a message from
+ * @param msg pointer where the message is stored
+ * @return 0 (milliseconds) if a message has been received
+ *         or SYS_MBOX_EMPTY if the mailbox is empty
+ */
+u32_t sys_arch_mbox_tryfetch(sys_mbox_t *mbox, void **msg);
+#endif
+/**
+ * For now, we map straight to sys_arch implementation.
+ */
+#define sys_mbox_tryfetch(mbox, msg) sys_arch_mbox_tryfetch(mbox, msg)
+/**
+ * @ingroup sys_mbox
+ * Deallocates a mailbox. If there are messages still present in the
+ * mailbox when the mailbox is deallocated, it is an indication of a
+ * programming error in lwIP and the developer should be notified.
+ *
+ * @param mbox mbox to delete
+ */
+void sys_mbox_free(sys_mbox_t *mbox);
+#define sys_mbox_fetch(mbox, msg) sys_arch_mbox_fetch(mbox, msg, 0)
+#ifndef sys_mbox_valid
+/**
+ * @ingroup sys_mbox
+ * Returns 1 if the mailbox is valid, 0 if it is not valid.
+ * When using pointers, a simple way is to check the pointer for != NULL.
+ * When directly using OS structures, implementing this may be more complex.
+ * This may also be a define, in which case the function is not prototyped.
+ */
+int sys_mbox_valid(sys_mbox_t *mbox);
+#endif
+#ifndef sys_mbox_set_invalid
+/**
+ * @ingroup sys_mbox
+ * Invalidate a mailbox so that sys_mbox_valid() returns 0.
+ * ATTENTION: This does NOT mean that the mailbox shall be deallocated:
+ * sys_mbox_free() is always called before calling this function!
+ * This may also be a define, in which case the function is not prototyped.
+ */
+void sys_mbox_set_invalid(sys_mbox_t *mbox);
+#endif
+#ifndef sys_mbox_valid_val
+/**
+ * Same as sys_mbox_valid() but taking a value, not a pointer
+ */
+#define sys_mbox_valid_val(mbox)       sys_mbox_valid(&(mbox))
+#endif
+#ifndef sys_mbox_set_invalid_val
+/**
+ * Same as sys_mbox_set_invalid() but taking a value, not a pointer
+ */
+#define sys_mbox_set_invalid_val(mbox) sys_mbox_set_invalid(&(mbox))
+#endif
+
+
+/**
+ * @ingroup sys_misc
+ * The only thread function:
+ * Starts a new thread named "name" with priority "prio" that will begin its
+ * execution in the function "thread()". The "arg" argument will be passed as an
+ * argument to the thread() function. The stack size to used for this thread is
+ * the "stacksize" parameter. The id of the new thread is returned. Both the id
+ * and the priority are system dependent.
+ * ATTENTION: although this function returns a value, it MUST NOT FAIL (ports have to assert this!)
+ *
+ * @param name human-readable name for the thread (used for debugging purposes)
+ * @param thread thread-function
+ * @param arg parameter passed to 'thread'
+ * @param stacksize stack size in bytes for the new thread (may be ignored by ports)
+ * @param prio priority of the new thread (may be ignored by ports) */
+sys_thread_t sys_thread_new(const char *name, lwip_thread_fn thread, void *arg, int stacksize, int prio);
+
+#endif /* NO_SYS */
+
+/**
+ * @ingroup lwip_opts_lock
+ * Called as first thing in the lwIP TCPIP thread. Can be used in conjunction
+ * with @ref LWIP_ASSERT_CORE_LOCKED to check core locking.
+ * @see @ref multithreading
+ */
+#if !defined LWIP_MARK_TCPIP_THREAD || defined __DOXYGEN__
+#define LWIP_MARK_TCPIP_THREAD()
+#endif
+
+/**
+ * @ingroup sys_misc
+ * sys_init() must be called before anything else.
+ * Initialize the sys_arch layer.
+ */
+void sys_init(void);
+
+#ifndef sys_jiffies
+/**
+ * Ticks/jiffies since power up.
+ */
+u32_t sys_jiffies(void);
+#endif
+
+#ifdef LWIP_FUZZ_SYS_NOW
+/* This offset should be added to the time 'sys_now()' returns */
+extern u32_t sys_now_offset;
+#endif
+
+/**
+ * @ingroup sys_time
+ * Returns the current time in milliseconds,
+ * may be the same as sys_jiffies or at least based on it.
+ * Don't care for wraparound, this is only used for time diffs.
+ * Not implementing this function means you cannot use some modules (e.g. TCP
+ * timestamps, internal timeouts for NO_SYS==1).
+ */
+u32_t sys_now(void);
+
+/* Critical Region Protection */
+/* These functions must be implemented in the sys_arch.c file.
+   In some implementations they can provide a more light-weight protection
+   mechanism than using semaphores. Otherwise semaphores can be used for
+   implementation */
+#ifndef SYS_ARCH_PROTECT
+/** SYS_LIGHTWEIGHT_PROT
+ * define SYS_LIGHTWEIGHT_PROT in lwipopts.h if you want inter-task protection
+ * for certain critical regions during buffer allocation, deallocation and memory
+ * allocation and deallocation.
+ */
+#if SYS_LIGHTWEIGHT_PROT
+
+/**
+ * @ingroup sys_prot
+ * SYS_ARCH_DECL_PROTECT
+ * declare a protection variable. This macro will default to defining a variable of
+ * type sys_prot_t. If a particular port needs a different implementation, then
+ * this macro may be defined in sys_arch.h.
+ */
+#define SYS_ARCH_DECL_PROTECT(lev) sys_prot_t lev
+/**
+ * @ingroup sys_prot
+ * SYS_ARCH_PROTECT
+ * Perform a "fast" protect. This could be implemented by
+ * disabling interrupts for an embedded system or by using a semaphore or
+ * mutex. The implementation should allow calling SYS_ARCH_PROTECT when
+ * already protected. The old protection level is returned in the variable
+ * "lev". This macro will default to calling the sys_arch_protect() function
+ * which should be implemented in sys_arch.c. If a particular port needs a
+ * different implementation, then this macro may be defined in sys_arch.h
+ */
+#define SYS_ARCH_PROTECT(lev) lev = sys_arch_protect()
+/**
+ * @ingroup sys_prot
+ * SYS_ARCH_UNPROTECT
+ * Perform a "fast" set of the protection level to "lev". This could be
+ * implemented by setting the interrupt level to "lev" within the MACRO or by
+ * using a semaphore or mutex.  This macro will default to calling the
+ * sys_arch_unprotect() function which should be implemented in
+ * sys_arch.c. If a particular port needs a different implementation, then
+ * this macro may be defined in sys_arch.h
+ */
+#define SYS_ARCH_UNPROTECT(lev) sys_arch_unprotect(lev)
+sys_prot_t sys_arch_protect(void);
+void sys_arch_unprotect(sys_prot_t pval);
+
+#else
+
+#define SYS_ARCH_DECL_PROTECT(lev)
+#define SYS_ARCH_PROTECT(lev)
+#define SYS_ARCH_UNPROTECT(lev)
+
+#endif /* SYS_LIGHTWEIGHT_PROT */
+
+#endif /* SYS_ARCH_PROTECT */
+
+/*
+ * Macros to set/get and increase/decrease variables in a thread-safe way.
+ * Use these for accessing variable that are used from more than one thread.
+ */
+
+#ifndef SYS_ARCH_INC
+#define SYS_ARCH_INC(var, val) do { \
+                                SYS_ARCH_DECL_PROTECT(old_level); \
+                                SYS_ARCH_PROTECT(old_level); \
+                                var += val; \
+                                SYS_ARCH_UNPROTECT(old_level); \
+                              } while(0)
+#endif /* SYS_ARCH_INC */
+
+#ifndef SYS_ARCH_DEC
+#define SYS_ARCH_DEC(var, val) do { \
+                                SYS_ARCH_DECL_PROTECT(old_level); \
+                                SYS_ARCH_PROTECT(old_level); \
+                                var -= val; \
+                                SYS_ARCH_UNPROTECT(old_level); \
+                              } while(0)
+#endif /* SYS_ARCH_DEC */
+
+#ifndef SYS_ARCH_GET
+#define SYS_ARCH_GET(var, ret) do { \
+                                SYS_ARCH_DECL_PROTECT(old_level); \
+                                SYS_ARCH_PROTECT(old_level); \
+                                ret = var; \
+                                SYS_ARCH_UNPROTECT(old_level); \
+                              } while(0)
+#endif /* SYS_ARCH_GET */
+
+#ifndef SYS_ARCH_SET
+#define SYS_ARCH_SET(var, val) do { \
+                                SYS_ARCH_DECL_PROTECT(old_level); \
+                                SYS_ARCH_PROTECT(old_level); \
+                                var = val; \
+                                SYS_ARCH_UNPROTECT(old_level); \
+                              } while(0)
+#endif /* SYS_ARCH_SET */
+
+#ifndef SYS_ARCH_LOCKED
+#define SYS_ARCH_LOCKED(code) do { \
+                                SYS_ARCH_DECL_PROTECT(old_level); \
+                                SYS_ARCH_PROTECT(old_level); \
+                                code; \
+                                SYS_ARCH_UNPROTECT(old_level); \
+                              } while(0)
+#endif /* SYS_ARCH_LOCKED */
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_SYS_H */
diff --git a/lib/lwip/include/lwip/tcp.h b/lib/lwip/include/lwip/tcp.h
new file mode 100644
index 0000000000..3991fd6e7b
--- /dev/null
+++ b/lib/lwip/include/lwip/tcp.h
@@ -0,0 +1,500 @@
+/**
+ * @file
+ * TCP API (to be used from TCPIP thread)<br>
+ * See also @ref tcp_raw
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef LWIP_HDR_TCP_H
+#define LWIP_HDR_TCP_H
+
+#include "lwip/opt.h"
+
+#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/tcpbase.h"
+#include "lwip/mem.h"
+#include "lwip/pbuf.h"
+#include "lwip/ip.h"
+#include "lwip/icmp.h"
+#include "lwip/err.h"
+#include "lwip/ip6.h"
+#include "lwip/ip6_addr.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct tcp_pcb;
+struct tcp_pcb_listen;
+
+/** Function prototype for tcp accept callback functions. Called when a new
+ * connection can be accepted on a listening pcb.
+ *
+ * @param arg Additional argument to pass to the callback function (@see tcp_arg())
+ * @param newpcb The new connection pcb
+ * @param err An error code if there has been an error accepting.
+ *            Only return ERR_ABRT if you have called tcp_abort from within the
+ *            callback function!
+ */
+typedef err_t (*tcp_accept_fn)(void *arg, struct tcp_pcb *newpcb, err_t err);
+
+/** Function prototype for tcp receive callback functions. Called when data has
+ * been received.
+ *
+ * @param arg Additional argument to pass to the callback function (@see tcp_arg())
+ * @param tpcb The connection pcb which received data
+ * @param p The received data (or NULL when the connection has been closed!)
+ * @param err An error code if there has been an error receiving
+ *            Only return ERR_ABRT if you have called tcp_abort from within the
+ *            callback function!
+ */
+typedef err_t (*tcp_recv_fn)(void *arg, struct tcp_pcb *tpcb,
+                             struct pbuf *p, err_t err);
+
+/** Function prototype for tcp sent callback functions. Called when sent data has
+ * been acknowledged by the remote side. Use it to free corresponding resources.
+ * This also means that the pcb has now space available to send new data.
+ *
+ * @param arg Additional argument to pass to the callback function (@see tcp_arg())
+ * @param tpcb The connection pcb for which data has been acknowledged
+ * @param len The amount of bytes acknowledged
+ * @return ERR_OK: try to send some data by calling tcp_output
+ *            Only return ERR_ABRT if you have called tcp_abort from within the
+ *            callback function!
+ */
+typedef err_t (*tcp_sent_fn)(void *arg, struct tcp_pcb *tpcb,
+                              u16_t len);
+
+/** Function prototype for tcp poll callback functions. Called periodically as
+ * specified by @see tcp_poll.
+ *
+ * @param arg Additional argument to pass to the callback function (@see tcp_arg())
+ * @param tpcb tcp pcb
+ * @return ERR_OK: try to send some data by calling tcp_output
+ *            Only return ERR_ABRT if you have called tcp_abort from within the
+ *            callback function!
+ */
+typedef err_t (*tcp_poll_fn)(void *arg, struct tcp_pcb *tpcb);
+
+/** Function prototype for tcp error callback functions. Called when the pcb
+ * receives a RST or is unexpectedly closed for any other reason.
+ *
+ * @note The corresponding pcb is already freed when this callback is called!
+ *
+ * @param arg Additional argument to pass to the callback function (@see tcp_arg())
+ * @param err Error code to indicate why the pcb has been closed
+ *            ERR_ABRT: aborted through tcp_abort or by a TCP timer
+ *            ERR_RST: the connection was reset by the remote host
+ */
+typedef void  (*tcp_err_fn)(void *arg, err_t err);
+
+/** Function prototype for tcp connected callback functions. Called when a pcb
+ * is connected to the remote side after initiating a connection attempt by
+ * calling tcp_connect().
+ *
+ * @param arg Additional argument to pass to the callback function (@see tcp_arg())
+ * @param tpcb The connection pcb which is connected
+ * @param err An unused error code, always ERR_OK currently ;-) @todo!
+ *            Only return ERR_ABRT if you have called tcp_abort from within the
+ *            callback function!
+ *
+ * @note When a connection attempt fails, the error callback is currently called!
+ */
+typedef err_t (*tcp_connected_fn)(void *arg, struct tcp_pcb *tpcb, err_t err);
+
+#if LWIP_WND_SCALE
+#define RCV_WND_SCALE(pcb, wnd) (((wnd) >> (pcb)->rcv_scale))
+#define SND_WND_SCALE(pcb, wnd) (((wnd) << (pcb)->snd_scale))
+#define TCPWND16(x)             ((u16_t)LWIP_MIN((x), 0xFFFF))
+#define TCP_WND_MAX(pcb)        ((tcpwnd_size_t)(((pcb)->flags & TF_WND_SCALE) ? TCP_WND : TCPWND16(TCP_WND)))
+#else
+#define RCV_WND_SCALE(pcb, wnd) (wnd)
+#define SND_WND_SCALE(pcb, wnd) (wnd)
+#define TCPWND16(x)             (x)
+#define TCP_WND_MAX(pcb)        TCP_WND
+#endif
+/* Increments a tcpwnd_size_t and holds at max value rather than rollover */
+#define TCP_WND_INC(wnd, inc)   do { \
+                                  if ((tcpwnd_size_t)(wnd + inc) >= wnd) { \
+                                    wnd = (tcpwnd_size_t)(wnd + inc); \
+                                  } else { \
+                                    wnd = (tcpwnd_size_t)-1; \
+                                  } \
+                                } while(0)
+
+#if LWIP_TCP_SACK_OUT
+/** SACK ranges to include in ACK packets.
+ * SACK entry is invalid if left==right. */
+struct tcp_sack_range {
+  /** Left edge of the SACK: the first acknowledged sequence number. */
+  u32_t left;
+  /** Right edge of the SACK: the last acknowledged sequence number +1 (so first NOT acknowledged). */
+  u32_t right;
+};
+#endif /* LWIP_TCP_SACK_OUT */
+
+/** Function prototype for deallocation of arguments. Called *just before* the
+ * pcb is freed, so don't expect to be able to do anything with this pcb!
+ *
+ * @param id ext arg id (allocated via @ref tcp_ext_arg_alloc_id)
+ * @param data pointer to the data (set via @ref tcp_ext_arg_set before)
+ */
+typedef void (*tcp_extarg_callback_pcb_destroyed_fn)(u8_t id, void *data);
+
+/** Function prototype to transition arguments from a listening pcb to an accepted pcb
+ *
+ * @param id ext arg id (allocated via @ref tcp_ext_arg_alloc_id)
+ * @param lpcb the listening pcb accepting a connection
+ * @param cpcb the newly allocated connection pcb
+ * @return ERR_OK if OK, any error if connection should be dropped
+ */
+typedef err_t (*tcp_extarg_callback_passive_open_fn)(u8_t id, struct tcp_pcb_listen *lpcb, struct tcp_pcb *cpcb);
+
+/** A table of callback functions that is invoked for ext arguments */
+struct tcp_ext_arg_callbacks {
+  /** @ref tcp_extarg_callback_pcb_destroyed_fn */
+  tcp_extarg_callback_pcb_destroyed_fn destroy;
+  /** @ref tcp_extarg_callback_passive_open_fn */
+  tcp_extarg_callback_passive_open_fn passive_open;
+};
+
+#define LWIP_TCP_PCB_NUM_EXT_ARG_ID_INVALID 0xFF
+
+#if LWIP_TCP_PCB_NUM_EXT_ARGS
+/* This is the structure for ext args in tcp pcbs (used as array) */
+struct tcp_pcb_ext_args {
+  const struct tcp_ext_arg_callbacks *callbacks;
+  void *data;
+};
+/* This is a helper define to prevent zero size arrays if disabled */
+#define TCP_PCB_EXTARGS struct tcp_pcb_ext_args ext_args[LWIP_TCP_PCB_NUM_EXT_ARGS];
+#else
+#define TCP_PCB_EXTARGS
+#endif
+
+typedef u16_t tcpflags_t;
+#define TCP_ALLFLAGS 0xffffU
+
+/**
+ * members common to struct tcp_pcb and struct tcp_listen_pcb
+ */
+#define TCP_PCB_COMMON(type) \
+  type *next; /* for the linked list */ \
+  void *callback_arg; \
+  TCP_PCB_EXTARGS \
+  enum tcp_state state; /* TCP state */ \
+  u8_t prio; \
+  /* ports are in host byte order */ \
+  u16_t local_port
+
+
+/** the TCP protocol control block for listening pcbs */
+struct tcp_pcb_listen {
+/** Common members of all PCB types */
+  IP_PCB;
+/** Protocol specific PCB members */
+  TCP_PCB_COMMON(struct tcp_pcb_listen);
+
+#if LWIP_CALLBACK_API
+  /* Function to call when a listener has been connected. */
+  tcp_accept_fn accept;
+#endif /* LWIP_CALLBACK_API */
+
+#if TCP_LISTEN_BACKLOG
+  u8_t backlog;
+  u8_t accepts_pending;
+#endif /* TCP_LISTEN_BACKLOG */
+};
+
+
+/** the TCP protocol control block */
+struct tcp_pcb {
+/** common PCB members */
+  IP_PCB;
+/** protocol specific PCB members */
+  TCP_PCB_COMMON(struct tcp_pcb);
+
+  /* ports are in host byte order */
+  u16_t remote_port;
+
+  tcpflags_t flags;
+#define TF_ACK_DELAY   0x01U   /* Delayed ACK. */
+#define TF_ACK_NOW     0x02U   /* Immediate ACK. */
+#define TF_INFR        0x04U   /* In fast recovery. */
+#define TF_CLOSEPEND   0x08U   /* If this is set, tcp_close failed to enqueue the FIN (retried in tcp_tmr) */
+#define TF_RXCLOSED    0x10U   /* rx closed by tcp_shutdown */
+#define TF_FIN         0x20U   /* Connection was closed locally (FIN segment enqueued). */
+#define TF_NODELAY     0x40U   /* Disable Nagle algorithm */
+#define TF_NAGLEMEMERR 0x80U   /* nagle enabled, memerr, try to output to prevent delayed ACK to happen */
+#if LWIP_WND_SCALE
+#define TF_WND_SCALE   0x0100U /* Window Scale option enabled */
+#endif
+#if TCP_LISTEN_BACKLOG
+#define TF_BACKLOGPEND 0x0200U /* If this is set, a connection pcb has increased the backlog on its listener */
+#endif
+#if LWIP_TCP_TIMESTAMPS
+#define TF_TIMESTAMP   0x0400U   /* Timestamp option enabled */
+#endif
+#define TF_RTO         0x0800U /* RTO timer has fired, in-flight data moved to unsent and being retransmitted */
+#if LWIP_TCP_SACK_OUT
+#define TF_SACK        0x1000U /* Selective ACKs enabled */
+#endif
+
+  /* the rest of the fields are in host byte order
+     as we have to do some math with them */
+
+  /* Timers */
+  u8_t polltmr, pollinterval;
+  u8_t last_timer;
+  u32_t tmr;
+
+  /* receiver variables */
+  u32_t rcv_nxt;   /* next seqno expected */
+  tcpwnd_size_t rcv_wnd;   /* receiver window available */
+  tcpwnd_size_t rcv_ann_wnd; /* receiver window to announce */
+  u32_t rcv_ann_right_edge; /* announced right edge of window */
+
+#if LWIP_TCP_SACK_OUT
+  /* SACK ranges to include in ACK packets (entry is invalid if left==right) */
+  struct tcp_sack_range rcv_sacks[LWIP_TCP_MAX_SACK_NUM];
+#define LWIP_TCP_SACK_VALID(pcb, idx) ((pcb)->rcv_sacks[idx].left != (pcb)->rcv_sacks[idx].right)
+#endif /* LWIP_TCP_SACK_OUT */
+
+  /* Retransmission timer. */
+  s16_t rtime;
+
+  u16_t mss;   /* maximum segment size */
+
+  /* RTT (round trip time) estimation variables */
+  u32_t rttest; /* RTT estimate in 500ms ticks */
+  u32_t rtseq;  /* sequence number being timed */
+  s16_t sa, sv; /* @see "Congestion Avoidance and Control" by Van Jacobson and Karels */
+
+  s16_t rto;    /* retransmission time-out (in ticks of TCP_SLOW_INTERVAL) */
+  u8_t nrtx;    /* number of retransmissions */
+
+  /* fast retransmit/recovery */
+  u8_t dupacks;
+  u32_t lastack; /* Highest acknowledged seqno. */
+
+  /* congestion avoidance/control variables */
+  tcpwnd_size_t cwnd;
+  tcpwnd_size_t ssthresh;
+
+  /* first byte following last rto byte */
+  u32_t rto_end;
+
+  /* sender variables */
+  u32_t snd_nxt;   /* next new seqno to be sent */
+  u32_t snd_wl1, snd_wl2; /* Sequence and acknowledgement numbers of last
+                             window update. */
+  u32_t snd_lbb;       /* Sequence number of next byte to be buffered. */
+  tcpwnd_size_t snd_wnd;   /* sender window */
+  tcpwnd_size_t snd_wnd_max; /* the maximum sender window announced by the remote host */
+
+  tcpwnd_size_t snd_buf;   /* Available buffer space for sending (in bytes). */
+#define TCP_SNDQUEUELEN_OVERFLOW (0xffffU-3)
+  u16_t snd_queuelen; /* Number of pbufs currently in the send buffer. */
+
+#if TCP_OVERSIZE
+  /* Extra bytes available at the end of the last pbuf in unsent. */
+  u16_t unsent_oversize;
+#endif /* TCP_OVERSIZE */
+
+  tcpwnd_size_t bytes_acked;
+
+  /* These are ordered by sequence number: */
+  struct tcp_seg *unsent;   /* Unsent (queued) segments. */
+  struct tcp_seg *unacked;  /* Sent but unacknowledged segments. */
+#if TCP_QUEUE_OOSEQ
+  struct tcp_seg *ooseq;    /* Received out of sequence segments. */
+#endif /* TCP_QUEUE_OOSEQ */
+
+  struct pbuf *refused_data; /* Data previously received but not yet taken by upper layer */
+
+#if LWIP_CALLBACK_API || TCP_LISTEN_BACKLOG
+  struct tcp_pcb_listen* listener;
+#endif /* LWIP_CALLBACK_API || TCP_LISTEN_BACKLOG */
+
+#if LWIP_CALLBACK_API
+  /* Function to be called when more send buffer space is available. */
+  tcp_sent_fn sent;
+  /* Function to be called when (in-sequence) data has arrived. */
+  tcp_recv_fn recv;
+  /* Function to be called when a connection has been set up. */
+  tcp_connected_fn connected;
+  /* Function which is called periodically. */
+  tcp_poll_fn poll;
+  /* Function to be called whenever a fatal error occurs. */
+  tcp_err_fn errf;
+#endif /* LWIP_CALLBACK_API */
+
+#if LWIP_TCP_TIMESTAMPS
+  u32_t ts_lastacksent;
+  u32_t ts_recent;
+#endif /* LWIP_TCP_TIMESTAMPS */
+
+  /* idle time before KEEPALIVE is sent */
+  u32_t keep_idle;
+#if LWIP_TCP_KEEPALIVE
+  u32_t keep_intvl;
+  u32_t keep_cnt;
+#endif /* LWIP_TCP_KEEPALIVE */
+
+  /* Persist timer counter */
+  u8_t persist_cnt;
+  /* Persist timer back-off */
+  u8_t persist_backoff;
+  /* Number of persist probes */
+  u8_t persist_probe;
+
+  /* KEEPALIVE counter */
+  u8_t keep_cnt_sent;
+
+#if LWIP_WND_SCALE
+  u8_t snd_scale;
+  u8_t rcv_scale;
+#endif
+};
+
+#if LWIP_EVENT_API
+
+enum lwip_event {
+  LWIP_EVENT_ACCEPT,
+  LWIP_EVENT_SENT,
+  LWIP_EVENT_RECV,
+  LWIP_EVENT_CONNECTED,
+  LWIP_EVENT_POLL,
+  LWIP_EVENT_ERR
+};
+
+err_t lwip_tcp_event(void *arg, struct tcp_pcb *pcb,
+         enum lwip_event,
+         struct pbuf *p,
+         u16_t size,
+         err_t err);
+
+#endif /* LWIP_EVENT_API */
+
+/* Application program's interface: */
+struct tcp_pcb * tcp_new     (void);
+struct tcp_pcb * tcp_new_ip_type (u8_t type);
+
+void             tcp_arg     (struct tcp_pcb *pcb, void *arg);
+#if LWIP_CALLBACK_API
+void             tcp_recv    (struct tcp_pcb *pcb, tcp_recv_fn recv);
+void             tcp_sent    (struct tcp_pcb *pcb, tcp_sent_fn sent);
+void             tcp_err     (struct tcp_pcb *pcb, tcp_err_fn err);
+void             tcp_accept  (struct tcp_pcb *pcb, tcp_accept_fn accept);
+#endif /* LWIP_CALLBACK_API */
+void             tcp_poll    (struct tcp_pcb *pcb, tcp_poll_fn poll, u8_t interval);
+
+#define          tcp_set_flags(pcb, set_flags)     do { (pcb)->flags = (tcpflags_t)((pcb)->flags |  (set_flags)); } while(0)
+#define          tcp_clear_flags(pcb, clr_flags)   do { (pcb)->flags = (tcpflags_t)((pcb)->flags & (tcpflags_t)(~(clr_flags) & TCP_ALLFLAGS)); } while(0)
+#define          tcp_is_flag_set(pcb, flag)        (((pcb)->flags & (flag)) != 0)
+
+#if LWIP_TCP_TIMESTAMPS
+#define          tcp_mss(pcb)             (((pcb)->flags & TF_TIMESTAMP) ? ((pcb)->mss - 12)  : (pcb)->mss)
+#else /* LWIP_TCP_TIMESTAMPS */
+/** @ingroup tcp_raw */
+#define          tcp_mss(pcb)             ((pcb)->mss)
+#endif /* LWIP_TCP_TIMESTAMPS */
+/** @ingroup tcp_raw */
+#define          tcp_sndbuf(pcb)          (TCPWND16((pcb)->snd_buf))
+/** @ingroup tcp_raw */
+#define          tcp_sndqueuelen(pcb)     ((pcb)->snd_queuelen)
+/** @ingroup tcp_raw */
+#define          tcp_nagle_disable(pcb)   tcp_set_flags(pcb, TF_NODELAY)
+/** @ingroup tcp_raw */
+#define          tcp_nagle_enable(pcb)    tcp_clear_flags(pcb, TF_NODELAY)
+/** @ingroup tcp_raw */
+#define          tcp_nagle_disabled(pcb)  tcp_is_flag_set(pcb, TF_NODELAY)
+
+#if TCP_LISTEN_BACKLOG
+#define          tcp_backlog_set(pcb, new_backlog) do { \
+  LWIP_ASSERT("pcb->state == LISTEN (called for wrong pcb?)", (pcb)->state == LISTEN); \
+  ((struct tcp_pcb_listen *)(pcb))->backlog = ((new_backlog) ? (new_backlog) : 1); } while(0)
+void             tcp_backlog_delayed(struct tcp_pcb* pcb);
+void             tcp_backlog_accepted(struct tcp_pcb* pcb);
+#else  /* TCP_LISTEN_BACKLOG */
+#define          tcp_backlog_set(pcb, new_backlog)
+#define          tcp_backlog_delayed(pcb)
+#define          tcp_backlog_accepted(pcb)
+#endif /* TCP_LISTEN_BACKLOG */
+#define          tcp_accepted(pcb) do { LWIP_UNUSED_ARG(pcb); } while(0) /* compatibility define, not needed any more */
+
+void             tcp_recved  (struct tcp_pcb *pcb, u16_t len);
+err_t            tcp_bind    (struct tcp_pcb *pcb, const ip_addr_t *ipaddr,
+                              u16_t port);
+void             tcp_bind_netif(struct tcp_pcb *pcb, const struct netif *netif);
+err_t            tcp_connect (struct tcp_pcb *pcb, const ip_addr_t *ipaddr,
+                              u16_t port, tcp_connected_fn connected);
+
+struct tcp_pcb * tcp_listen_with_backlog_and_err(struct tcp_pcb *pcb, u8_t backlog, err_t *err);
+struct tcp_pcb * tcp_listen_with_backlog(struct tcp_pcb *pcb, u8_t backlog);
+/** @ingroup tcp_raw */
+#define          tcp_listen(pcb) tcp_listen_with_backlog(pcb, TCP_DEFAULT_LISTEN_BACKLOG)
+
+void             tcp_abort (struct tcp_pcb *pcb);
+err_t            tcp_close   (struct tcp_pcb *pcb);
+err_t            tcp_shutdown(struct tcp_pcb *pcb, int shut_rx, int shut_tx);
+
+err_t            tcp_write   (struct tcp_pcb *pcb, const void *dataptr, u16_t len,
+                              u8_t apiflags);
+
+void             tcp_setprio (struct tcp_pcb *pcb, u8_t prio);
+
+err_t            tcp_output  (struct tcp_pcb *pcb);
+
+err_t            tcp_tcp_get_tcp_addrinfo(struct tcp_pcb *pcb, int local, ip_addr_t *addr, u16_t *port);
+
+#define tcp_dbg_get_tcp_state(pcb) ((pcb)->state)
+
+/* for compatibility with older implementation */
+#define tcp_new_ip6() tcp_new_ip_type(IPADDR_TYPE_V6)
+
+#if LWIP_TCP_PCB_NUM_EXT_ARGS
+u8_t tcp_ext_arg_alloc_id(void);
+void tcp_ext_arg_set_callbacks(struct tcp_pcb *pcb, u8_t id, const struct tcp_ext_arg_callbacks * const callbacks);
+void tcp_ext_arg_set(struct tcp_pcb *pcb, u8_t id, void *arg);
+void *tcp_ext_arg_get(const struct tcp_pcb *pcb, u8_t id);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_TCP */
+
+#endif /* LWIP_HDR_TCP_H */
diff --git a/lib/lwip/include/lwip/tcpbase.h b/lib/lwip/include/lwip/tcpbase.h
new file mode 100644
index 0000000000..018790f1f9
--- /dev/null
+++ b/lib/lwip/include/lwip/tcpbase.h
@@ -0,0 +1,88 @@
+/**
+ * @file
+ * Base TCP API definitions shared by TCP and ALTCP<br>
+ * See also @ref tcp_raw
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef LWIP_HDR_TCPBASE_H
+#define LWIP_HDR_TCPBASE_H
+
+#include "lwip/opt.h"
+
+#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#if LWIP_WND_SCALE
+typedef u32_t tcpwnd_size_t;
+#else
+typedef u16_t tcpwnd_size_t;
+#endif
+
+enum tcp_state {
+  CLOSED      = 0,
+  LISTEN      = 1,
+  SYN_SENT    = 2,
+  SYN_RCVD    = 3,
+  ESTABLISHED = 4,
+  FIN_WAIT_1  = 5,
+  FIN_WAIT_2  = 6,
+  CLOSE_WAIT  = 7,
+  CLOSING     = 8,
+  LAST_ACK    = 9,
+  TIME_WAIT   = 10
+};
+/* ATTENTION: this depends on state number ordering! */
+#define TCP_STATE_IS_CLOSING(state) ((state) >= FIN_WAIT_1)
+
+/* Flags for "apiflags" parameter in tcp_write */
+#define TCP_WRITE_FLAG_COPY 0x01
+#define TCP_WRITE_FLAG_MORE 0x02
+
+#define TCP_PRIO_MIN    1
+#define TCP_PRIO_NORMAL 64
+#define TCP_PRIO_MAX    127
+
+const char* tcp_debug_state_str(enum tcp_state s);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_TCP */
+
+#endif /* LWIP_HDR_TCPBASE_H */
diff --git a/lib/lwip/include/lwip/tcpip.h b/lib/lwip/include/lwip/tcpip.h
new file mode 100644
index 0000000000..30ce4fef1e
--- /dev/null
+++ b/lib/lwip/include/lwip/tcpip.h
@@ -0,0 +1,114 @@
+/**
+ * @file
+ * Functions to sync with TCPIP thread
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef LWIP_HDR_TCPIP_H
+#define LWIP_HDR_TCPIP_H
+
+#include "lwip/opt.h"
+
+#if !NO_SYS /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/err.h"
+#include "lwip/timeouts.h"
+#include "lwip/netif.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if LWIP_TCPIP_CORE_LOCKING
+/** The global semaphore to lock the stack. */
+extern sys_mutex_t lock_tcpip_core;
+#if !defined LOCK_TCPIP_CORE || defined __DOXYGEN__
+/** Lock lwIP core mutex (needs @ref LWIP_TCPIP_CORE_LOCKING 1) */
+#define LOCK_TCPIP_CORE()     sys_mutex_lock(&lock_tcpip_core)
+/** Unlock lwIP core mutex (needs @ref LWIP_TCPIP_CORE_LOCKING 1) */
+#define UNLOCK_TCPIP_CORE()   sys_mutex_unlock(&lock_tcpip_core)
+#endif /* LOCK_TCPIP_CORE */
+#else /* LWIP_TCPIP_CORE_LOCKING */
+#define LOCK_TCPIP_CORE()
+#define UNLOCK_TCPIP_CORE()
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+
+struct pbuf;
+struct netif;
+
+/** Function prototype for the init_done function passed to tcpip_init */
+typedef void (*tcpip_init_done_fn)(void *arg);
+/** Function prototype for functions passed to tcpip_callback() */
+typedef void (*tcpip_callback_fn)(void *ctx);
+
+/* Forward declarations */
+struct tcpip_callback_msg;
+
+void   tcpip_init(tcpip_init_done_fn tcpip_init_done, void *arg);
+
+err_t  tcpip_inpkt(struct pbuf *p, struct netif *inp, netif_input_fn input_fn);
+err_t  tcpip_input(struct pbuf *p, struct netif *inp);
+
+err_t  tcpip_try_callback(tcpip_callback_fn function, void *ctx);
+err_t  tcpip_callback(tcpip_callback_fn function, void *ctx);
+err_t  tcpip_callback_wait(tcpip_callback_fn function, void *ctx);
+/**  @ingroup lwip_os
+ * @deprecated use tcpip_try_callback() or tcpip_callback() instead
+ */
+#define tcpip_callback_with_block(function, ctx, block) ((block != 0)? tcpip_callback(function, ctx) : tcpip_try_callback(function, ctx))
+
+struct tcpip_callback_msg* tcpip_callbackmsg_new(tcpip_callback_fn function, void *ctx);
+void   tcpip_callbackmsg_delete(struct tcpip_callback_msg* msg);
+err_t  tcpip_callbackmsg_trycallback(struct tcpip_callback_msg* msg);
+err_t  tcpip_callbackmsg_trycallback_fromisr(struct tcpip_callback_msg* msg);
+
+/* free pbufs or heap memory from another context without blocking */
+err_t  pbuf_free_callback(struct pbuf *p);
+err_t  mem_free_callback(void *m);
+
+#if LWIP_TCPIP_TIMEOUT && LWIP_TIMERS
+err_t  tcpip_timeout(u32_t msecs, sys_timeout_handler h, void *arg);
+err_t  tcpip_untimeout(sys_timeout_handler h, void *arg);
+#endif /* LWIP_TCPIP_TIMEOUT && LWIP_TIMERS */
+
+#ifdef TCPIP_THREAD_TEST
+int tcpip_thread_poll_one(void);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !NO_SYS */
+
+#endif /* LWIP_HDR_TCPIP_H */
diff --git a/lib/lwip/include/lwip/timeouts.h b/lib/lwip/include/lwip/timeouts.h
new file mode 100644
index 0000000000..b601f9eb34
--- /dev/null
+++ b/lib/lwip/include/lwip/timeouts.h
@@ -0,0 +1,128 @@
+/**
+ * @file
+ * Timer implementations
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *         Simon Goldschmidt
+ *
+ */
+#ifndef LWIP_HDR_TIMEOUTS_H
+#define LWIP_HDR_TIMEOUTS_H
+
+#include "lwip/opt.h"
+#include "lwip/err.h"
+#if !NO_SYS
+#include "lwip/sys.h"
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef LWIP_DEBUG_TIMERNAMES
+#ifdef LWIP_DEBUG
+#define LWIP_DEBUG_TIMERNAMES SYS_DEBUG
+#else /* LWIP_DEBUG */
+#define LWIP_DEBUG_TIMERNAMES 0
+#endif /* LWIP_DEBUG*/
+#endif
+
+/** Returned by sys_timeouts_sleeptime() to indicate there is no timer, so we
+ * can sleep forever.
+ */
+#define SYS_TIMEOUTS_SLEEPTIME_INFINITE 0xFFFFFFFF
+
+/** Function prototype for a stack-internal timer function that has to be
+ * called at a defined interval */
+typedef void (* lwip_cyclic_timer_handler)(void);
+
+/** This struct contains information about a stack-internal timer function
+ that has to be called at a defined interval */
+struct lwip_cyclic_timer {
+  u32_t interval_ms;
+  lwip_cyclic_timer_handler handler;
+#if LWIP_DEBUG_TIMERNAMES
+  const char* handler_name;
+#endif /* LWIP_DEBUG_TIMERNAMES */
+};
+
+/** This array contains all stack-internal cyclic timers. To get the number of
+ * timers, use lwip_num_cyclic_timers */
+extern const struct lwip_cyclic_timer lwip_cyclic_timers[];
+/** Array size of lwip_cyclic_timers[] */
+extern const int lwip_num_cyclic_timers;
+
+#if LWIP_TIMERS
+
+/** Function prototype for a timeout callback function. Register such a function
+ * using sys_timeout().
+ *
+ * @param arg Additional argument to pass to the function - set up by sys_timeout()
+ */
+typedef void (* sys_timeout_handler)(void *arg);
+
+struct sys_timeo {
+  struct sys_timeo *next;
+  u32_t time;
+  sys_timeout_handler h;
+  void *arg;
+#if LWIP_DEBUG_TIMERNAMES
+  const char* handler_name;
+#endif /* LWIP_DEBUG_TIMERNAMES */
+};
+
+void sys_timeouts_init(void);
+
+#if LWIP_DEBUG_TIMERNAMES
+void sys_timeout_debug(u32_t msecs, sys_timeout_handler handler, void *arg, const char* handler_name);
+#define sys_timeout(msecs, handler, arg) sys_timeout_debug(msecs, handler, arg, #handler)
+#else /* LWIP_DEBUG_TIMERNAMES */
+void sys_timeout(u32_t msecs, sys_timeout_handler handler, void *arg);
+#endif /* LWIP_DEBUG_TIMERNAMES */
+
+void sys_untimeout(sys_timeout_handler handler, void *arg);
+void sys_restart_timeouts(void);
+void sys_check_timeouts(void);
+u32_t sys_timeouts_sleeptime(void);
+
+#if LWIP_TESTMODE
+struct sys_timeo** sys_timeouts_get_next_timeout(void);
+void lwip_cyclic_timer(void *arg);
+#endif
+
+#endif /* LWIP_TIMERS */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_TIMEOUTS_H */
diff --git a/lib/lwip/include/lwip/udp.h b/lib/lwip/include/lwip/udp.h
new file mode 100644
index 0000000000..f1deae3e44
--- /dev/null
+++ b/lib/lwip/include/lwip/udp.h
@@ -0,0 +1,195 @@
+/**
+ * @file
+ * UDP API (to be used from TCPIP thread)<br>
+ * See also @ref udp_raw
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef LWIP_HDR_UDP_H
+#define LWIP_HDR_UDP_H
+
+#include "lwip/opt.h"
+
+#if LWIP_UDP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/pbuf.h"
+#include "lwip/netif.h"
+#include "lwip/ip_addr.h"
+#include "lwip/ip.h"
+#include "lwip/ip6_addr.h"
+#include "lwip/prot/udp.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define UDP_FLAGS_NOCHKSUM       0x01U
+#define UDP_FLAGS_UDPLITE        0x02U
+#define UDP_FLAGS_CONNECTED      0x04U
+#define UDP_FLAGS_MULTICAST_LOOP 0x08U
+
+struct udp_pcb;
+
+/** Function prototype for udp pcb receive callback functions
+ * addr and port are in same byte order as in the pcb
+ * The callback is responsible for freeing the pbuf
+ * if it's not used any more.
+ *
+ * ATTENTION: Be aware that 'addr' might point into the pbuf 'p' so freeing this pbuf
+ *            can make 'addr' invalid, too.
+ *
+ * @param arg user supplied argument (udp_pcb.recv_arg)
+ * @param pcb the udp_pcb which received data
+ * @param p the packet buffer that was received
+ * @param addr the remote IP address from which the packet was received
+ * @param port the remote port from which the packet was received
+ */
+typedef void (*udp_recv_fn)(void *arg, struct udp_pcb *pcb, struct pbuf *p,
+    const ip_addr_t *addr, u16_t port);
+
+/** the UDP protocol control block */
+struct udp_pcb {
+/** Common members of all PCB types */
+  IP_PCB;
+
+/* Protocol specific PCB members */
+
+  struct udp_pcb *next;
+
+  u8_t flags;
+  /** ports are in host byte order */
+  u16_t local_port, remote_port;
+
+#if LWIP_MULTICAST_TX_OPTIONS
+#if LWIP_IPV4
+  /** outgoing network interface for multicast packets, by IPv4 address (if not 'any') */
+  ip4_addr_t mcast_ip4;
+#endif /* LWIP_IPV4 */
+  /** outgoing network interface for multicast packets, by interface index (if nonzero) */
+  u8_t mcast_ifindex;
+  /** TTL for outgoing multicast packets */
+  u8_t mcast_ttl;
+#endif /* LWIP_MULTICAST_TX_OPTIONS */
+
+#if LWIP_UDPLITE
+  /** used for UDP_LITE only */
+  u16_t chksum_len_rx, chksum_len_tx;
+#endif /* LWIP_UDPLITE */
+
+  /** receive callback function */
+  udp_recv_fn recv;
+  /** user-supplied argument for the recv callback */
+  void *recv_arg;
+};
+/* udp_pcbs export for external reference (e.g. SNMP agent) */
+extern struct udp_pcb *udp_pcbs;
+
+/* The following functions is the application layer interface to the
+   UDP code. */
+struct udp_pcb * udp_new        (void);
+struct udp_pcb * udp_new_ip_type(u8_t type);
+void             udp_remove     (struct udp_pcb *pcb);
+err_t            udp_bind       (struct udp_pcb *pcb, const ip_addr_t *ipaddr,
+                                 u16_t port);
+void             udp_bind_netif (struct udp_pcb *pcb, const struct netif* netif);
+err_t            udp_connect    (struct udp_pcb *pcb, const ip_addr_t *ipaddr,
+                                 u16_t port);
+void             udp_disconnect (struct udp_pcb *pcb);
+void             udp_recv       (struct udp_pcb *pcb, udp_recv_fn recv,
+                                 void *recv_arg);
+err_t            udp_sendto_if  (struct udp_pcb *pcb, struct pbuf *p,
+                                 const ip_addr_t *dst_ip, u16_t dst_port,
+                                 struct netif *netif);
+err_t            udp_sendto_if_src(struct udp_pcb *pcb, struct pbuf *p,
+                                 const ip_addr_t *dst_ip, u16_t dst_port,
+                                 struct netif *netif, const ip_addr_t *src_ip);
+err_t            udp_sendto     (struct udp_pcb *pcb, struct pbuf *p,
+                                 const ip_addr_t *dst_ip, u16_t dst_port);
+err_t            udp_send       (struct udp_pcb *pcb, struct pbuf *p);
+
+#if LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP
+err_t            udp_sendto_if_chksum(struct udp_pcb *pcb, struct pbuf *p,
+                                 const ip_addr_t *dst_ip, u16_t dst_port,
+                                 struct netif *netif, u8_t have_chksum,
+                                 u16_t chksum);
+err_t            udp_sendto_chksum(struct udp_pcb *pcb, struct pbuf *p,
+                                 const ip_addr_t *dst_ip, u16_t dst_port,
+                                 u8_t have_chksum, u16_t chksum);
+err_t            udp_send_chksum(struct udp_pcb *pcb, struct pbuf *p,
+                                 u8_t have_chksum, u16_t chksum);
+err_t            udp_sendto_if_src_chksum(struct udp_pcb *pcb, struct pbuf *p,
+                                 const ip_addr_t *dst_ip, u16_t dst_port, struct netif *netif,
+                                 u8_t have_chksum, u16_t chksum, const ip_addr_t *src_ip);
+#endif /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */
+
+#define          udp_flags(pcb) ((pcb)->flags)
+#define          udp_setflags(pcb, f)  ((pcb)->flags = (f))
+
+#define          udp_set_flags(pcb, set_flags)     do { (pcb)->flags = (u8_t)((pcb)->flags |  (set_flags)); } while(0)
+#define          udp_clear_flags(pcb, clr_flags)   do { (pcb)->flags = (u8_t)((pcb)->flags & (u8_t)(~(clr_flags) & 0xff)); } while(0)
+#define          udp_is_flag_set(pcb, flag)        (((pcb)->flags & (flag)) != 0)
+
+/* The following functions are the lower layer interface to UDP. */
+void             udp_input      (struct pbuf *p, struct netif *inp);
+
+void             udp_init       (void);
+
+/* for compatibility with older implementation */
+#define udp_new_ip6() udp_new_ip_type(IPADDR_TYPE_V6)
+
+#if LWIP_MULTICAST_TX_OPTIONS
+#if LWIP_IPV4
+#define udp_set_multicast_netif_addr(pcb, ip4addr) ip4_addr_copy((pcb)->mcast_ip4, *(ip4addr))
+#define udp_get_multicast_netif_addr(pcb)          (&(pcb)->mcast_ip4)
+#endif /* LWIP_IPV4 */
+#define udp_set_multicast_netif_index(pcb, idx)    ((pcb)->mcast_ifindex = (idx))
+#define udp_get_multicast_netif_index(pcb)         ((pcb)->mcast_ifindex)
+#define udp_set_multicast_ttl(pcb, value)          ((pcb)->mcast_ttl = (value))
+#define udp_get_multicast_ttl(pcb)                 ((pcb)->mcast_ttl)
+#endif /* LWIP_MULTICAST_TX_OPTIONS */
+
+#if UDP_DEBUG
+void udp_debug_print(struct udp_hdr *udphdr);
+#else
+#define udp_debug_print(udphdr)
+#endif
+
+void udp_netif_ip_addr_changed(const ip_addr_t* old_addr, const ip_addr_t* new_addr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_UDP */
+
+#endif /* LWIP_HDR_UDP_H */
diff --git a/lib/lwip/include/netif/bridgeif.h b/lib/lwip/include/netif/bridgeif.h
new file mode 100644
index 0000000000..f4f8cf1440
--- /dev/null
+++ b/lib/lwip/include/netif/bridgeif.h
@@ -0,0 +1,127 @@
+/**
+ * @file
+ * lwIP netif implementing an IEEE 802.1D MAC Bridge
+ */
+
+/*
+ * Copyright (c) 2017 Simon Goldschmidt.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt <goldsimon@gmx.de>
+ *
+ */
+#ifndef LWIP_HDR_NETIF_BRIDGEIF_H
+#define LWIP_HDR_NETIF_BRIDGEIF_H
+
+#include "netif/bridgeif_opts.h"
+
+#include "lwip/err.h"
+#include "lwip/prot/ethernet.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct netif;
+
+#if (BRIDGEIF_MAX_PORTS < 0) || (BRIDGEIF_MAX_PORTS >= 64)
+#error BRIDGEIF_MAX_PORTS must be [1..63]
+#elif BRIDGEIF_MAX_PORTS < 8
+typedef u8_t bridgeif_portmask_t;
+#elif BRIDGEIF_MAX_PORTS < 16
+typedef u16_t bridgeif_portmask_t;
+#elif BRIDGEIF_MAX_PORTS < 32
+typedef u32_t bridgeif_portmask_t;
+#elif BRIDGEIF_MAX_PORTS < 64
+typedef u64_t bridgeif_portmask_t;
+#endif
+
+#define BR_FLOOD ((bridgeif_portmask_t)-1)
+
+/** @ingroup bridgeif
+ * Initialisation data for @ref bridgeif_init.
+ * An instance of this type must be passed as parameter 'state' to @ref netif_add
+ * when the bridge is added.
+ */
+typedef struct bridgeif_initdata_s {
+  /** MAC address of the bridge (cannot use the netif's addresses) */
+  struct eth_addr ethaddr;
+  /** Maximum number of ports in the bridge (ports are stored in an array, this
+      influences memory allocated for netif->state of the bridge netif). */
+  u8_t            max_ports;
+  /** Maximum number of dynamic/learning entries in the bridge's forwarding database.
+      In the default implementation, this controls memory consumption only. */
+  u16_t           max_fdb_dynamic_entries;
+  /** Maximum number of static forwarding entries. Influences memory consumption! */
+  u16_t           max_fdb_static_entries;
+} bridgeif_initdata_t;
+
+/** @ingroup bridgeif
+ * Use this for constant initialization of a bridgeif_initdat_t
+ * (ethaddr must be passed as ETH_ADDR())
+ */
+#define BRIDGEIF_INITDATA1(max_ports, max_fdb_dynamic_entries, max_fdb_static_entries, ethaddr) {ethaddr, max_ports, max_fdb_dynamic_entries, max_fdb_static_entries}
+/** @ingroup bridgeif
+ * Use this for constant initialization of a bridgeif_initdat_t
+ * (each byte of ethaddr must be passed)
+ */
+#define BRIDGEIF_INITDATA2(max_ports, max_fdb_dynamic_entries, max_fdb_static_entries, e0, e1, e2, e3, e4, e5) {{e0, e1, e2, e3, e4, e5}, max_ports, max_fdb_dynamic_entries, max_fdb_static_entries}
+
+err_t bridgeif_init(struct netif *netif);
+err_t bridgeif_add_port(struct netif *bridgeif, struct netif *portif);
+err_t bridgeif_fdb_add(struct netif *bridgeif, const struct eth_addr *addr, bridgeif_portmask_t ports);
+err_t bridgeif_fdb_remove(struct netif *bridgeif, const struct eth_addr *addr);
+
+/* FDB interface, can be replaced by own implementation */
+void                bridgeif_fdb_update_src(void *fdb_ptr, struct eth_addr *src_addr, u8_t port_idx);
+bridgeif_portmask_t bridgeif_fdb_get_dst_ports(void *fdb_ptr, struct eth_addr *dst_addr);
+void*               bridgeif_fdb_init(u16_t max_fdb_entries);
+
+#if BRIDGEIF_PORT_NETIFS_OUTPUT_DIRECT
+#ifndef BRIDGEIF_DECL_PROTECT
+/* define bridgeif protection to sys_arch_protect... */
+#include "lwip/sys.h"
+#define BRIDGEIF_DECL_PROTECT(lev)    SYS_ARCH_DECL_PROTECT(lev)
+#define BRIDGEIF_READ_PROTECT(lev)    SYS_ARCH_PROTECT(lev)
+#define BRIDGEIF_READ_UNPROTECT(lev)  SYS_ARCH_UNPROTECT(lev)
+#define BRIDGEIF_WRITE_PROTECT(lev)
+#define BRIDGEIF_WRITE_UNPROTECT(lev)
+#endif
+#else /* BRIDGEIF_PORT_NETIFS_OUTPUT_DIRECT */
+#include "lwip/tcpip.h"
+#define BRIDGEIF_DECL_PROTECT(lev)
+#define BRIDGEIF_READ_PROTECT(lev)
+#define BRIDGEIF_READ_UNPROTECT(lev)
+#define BRIDGEIF_WRITE_PROTECT(lev)
+#define BRIDGEIF_WRITE_UNPROTECT(lev)
+#endif /* BRIDGEIF_PORT_NETIFS_OUTPUT_DIRECT */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_NETIF_BRIDGEIF_H */
diff --git a/lib/lwip/include/netif/bridgeif_opts.h b/lib/lwip/include/netif/bridgeif_opts.h
new file mode 100644
index 0000000000..b85c301749
--- /dev/null
+++ b/lib/lwip/include/netif/bridgeif_opts.h
@@ -0,0 +1,90 @@
+/**
+ * @file
+ * lwIP netif implementing an IEEE 802.1D MAC Bridge
+ */
+
+/*
+ * Copyright (c) 2017 Simon Goldschmidt.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt <goldsimon@gmx.de>
+ *
+ */
+
+#ifndef LWIP_HDR_NETIF_BRIDGEIF_OPTS_H
+#define LWIP_HDR_NETIF_BRIDGEIF_OPTS_H
+
+#include "lwip/opt.h"
+
+/**
+ * @defgroup bridgeif_opts Options
+ * @ingroup bridgeif
+ * @{
+ */
+
+/** BRIDGEIF_PORT_NETIFS_OUTPUT_DIRECT==1: set port netif's 'input' function
+ * to call directly into bridgeif code and on top of that, directly call into
+ * the selected forwarding port's 'linkoutput' function.
+ * This means that the bridgeif input/output path is protected from concurrent access
+ * but as well, *all* bridge port netif's drivers must correctly handle concurrent access!
+ * == 0: get into tcpip_thread for every input packet (no multithreading)
+ * ATTENTION: as ==0 relies on tcpip.h, the default depends on NO_SYS setting
+ */
+#ifndef BRIDGEIF_PORT_NETIFS_OUTPUT_DIRECT
+#define BRIDGEIF_PORT_NETIFS_OUTPUT_DIRECT  NO_SYS
+#endif
+
+/** BRIDGEIF_MAX_PORTS: this is used to create a typedef used for forwarding
+ * bit-fields: the number of bits required is this + 1 (for the internal/cpu port)
+ * (63 is the maximum, resulting in an u64_t for the bit mask)
+ * ATTENTION: this controls the maximum number of the implementation only!
+ * The max. number of ports per bridge must still be passed via netif_add parameter!
+ */
+#ifndef BRIDGEIF_MAX_PORTS
+#define BRIDGEIF_MAX_PORTS                  7
+#endif
+
+/** BRIDGEIF_DEBUG: Enable generic debugging in bridgeif.c. */
+#ifndef BRIDGEIF_DEBUG
+#define BRIDGEIF_DEBUG                      LWIP_DBG_OFF
+#endif
+
+/** BRIDGEIF_DEBUG: Enable FDB debugging in bridgeif.c. */
+#ifndef BRIDGEIF_FDB_DEBUG
+#define BRIDGEIF_FDB_DEBUG                  LWIP_DBG_OFF
+#endif
+
+/** BRIDGEIF_DEBUG: Enable forwarding debugging in bridgeif.c. */
+#ifndef BRIDGEIF_FW_DEBUG
+#define BRIDGEIF_FW_DEBUG                   LWIP_DBG_OFF
+#endif
+
+/**
+ * @}
+ */
+
+#endif /* LWIP_HDR_NETIF_BRIDGEIF_OPTS_H */
diff --git a/lib/lwip/include/netif/etharp.h b/lib/lwip/include/netif/etharp.h
new file mode 100644
index 0000000000..b536fd280f
--- /dev/null
+++ b/lib/lwip/include/netif/etharp.h
@@ -0,0 +1,3 @@
+/* ARP has been moved to core/ipv4, provide this #include for compatibility only */
+#include "lwip/etharp.h"
+#include "netif/ethernet.h"
diff --git a/lib/lwip/include/netif/ethernet.h b/lib/lwip/include/netif/ethernet.h
new file mode 100644
index 0000000000..49649cbf8b
--- /dev/null
+++ b/lib/lwip/include/netif/ethernet.h
@@ -0,0 +1,77 @@
+/**
+ * @file
+ * Ethernet input function - handles INCOMING ethernet level traffic
+ * To be used in most low-level netif implementations
+ */
+
+/*
+ * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
+ * Copyright (c) 2003-2004 Leon Woestenberg <leon.woestenberg@axon.tv>
+ * Copyright (c) 2003-2004 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#ifndef LWIP_HDR_NETIF_ETHERNET_H
+#define LWIP_HDR_NETIF_ETHERNET_H
+
+#include "lwip/opt.h"
+
+#include "lwip/pbuf.h"
+#include "lwip/netif.h"
+#include "lwip/prot/ethernet.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if LWIP_ARP || LWIP_ETHERNET
+
+/** Define this to 1 and define LWIP_ARP_FILTER_NETIF_FN(pbuf, netif, type)
+ * to a filter function that returns the correct netif when using multiple
+ * netifs on one hardware interface where the netif's low-level receive
+ * routine cannot decide for the correct netif (e.g. when mapping multiple
+ * IP addresses to one hardware interface).
+ */
+#ifndef LWIP_ARP_FILTER_NETIF
+#define LWIP_ARP_FILTER_NETIF 0
+#endif
+
+err_t ethernet_input(struct pbuf *p, struct netif *netif);
+err_t ethernet_output(struct netif* netif, struct pbuf* p, const struct eth_addr* src, const struct eth_addr* dst, u16_t eth_type);
+
+extern const struct eth_addr ethbroadcast, ethzero;
+
+#endif /* LWIP_ARP || LWIP_ETHERNET */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_NETIF_ETHERNET_H */
diff --git a/lib/lwip/include/netif/ieee802154.h b/lib/lwip/include/netif/ieee802154.h
new file mode 100644
index 0000000000..54e019fd0a
--- /dev/null
+++ b/lib/lwip/include/netif/ieee802154.h
@@ -0,0 +1,112 @@
+/**
+ * @file
+ * Definitions for IEEE 802.15.4 MAC frames
+ */
+
+/*
+ * Copyright (c) 2018 Simon Goldschmidt.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt <goldsimon@gmx.de>
+ *
+ */
+#ifndef LWIP_HDR_NETIF_IEEE802154_H
+#define LWIP_HDR_NETIF_IEEE802154_H
+
+#include "lwip/opt.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+/** General MAC frame format
+ * This shows the full featured header, mainly for documentation.
+ * Some fields are omitted or shortened to achieve frame compression.
+ */
+struct ieee_802154_hdr {
+  /** See IEEE_802154_FC_* defines */
+  PACK_STRUCT_FIELD(u16_t frame_control);
+  /** Sequence number is omitted if IEEE_802154_FC_SEQNO_SUPPR is set in frame_control */
+  PACK_STRUCT_FLD_8(u8_t  sequence_number);
+  /** Destination PAN ID is omitted if Destination Addressing Mode is 0 */
+  PACK_STRUCT_FIELD(u16_t destination_pan_id);
+  /** Destination Address is omitted if Destination Addressing Mode is 0 */
+  PACK_STRUCT_FLD_8(u8_t destination_address[8]);
+  /** Source PAN ID is omitted if Source Addressing Mode is 0
+      or if IEEE_802154_FC_PANID_COMPR is set in frame control*/
+  PACK_STRUCT_FIELD(u16_t source_pan_id);
+  /** Source Address is omitted if Source Addressing Mode is 0 */
+  PACK_STRUCT_FLD_8(u8_t source_address[8]);
+  /* The rest is variable */
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
+
+/* Addressing modes (2 bits) */
+#define IEEE_802154_ADDR_MODE_NO_ADDR   0x00 /* PAN ID and address fields are not present */
+#define IEEE_802154_ADDR_MODE_RESERVED  0x01 /* Reserved */
+#define IEEE_802154_ADDR_MODE_SHORT     0x02 /* Address field contains a short address (16 bit) */
+#define IEEE_802154_ADDR_MODE_EXT       0x03 /* Address field contains an extended address (64 bit) */
+
+/* IEEE 802.15.4 Frame Control definitions (2 bytes; see IEEE 802.15.4-2015 ch. 7.2.1) */
+#define IEEE_802154_FC_FT_MASK             0x0007 /* bits 0..2: Frame Type */
+#define IEEE_802154_FC_FT_BEACON               0x00
+#define IEEE_802154_FC_FT_DATA                 0x01
+#define IEEE_802154_FC_FT_ACK                  0x02
+#define IEEE_802154_FC_FT_MAC_CMD              0x03
+#define IEEE_802154_FC_FT_RESERVED             0x04
+#define IEEE_802154_FC_FT_MULTIPURPOSE         0x05
+#define IEEE_802154_FC_FT_FRAG                 0x06
+#define IEEE_802154_FC_FT_EXT                  0x07
+#define IEEE_802154_FC_SEC_EN              0x0008 /* bit 3: Security Enabled */
+#define IEEE_802154_FC_FRAME_PEND          0x0010 /* bit 4: Frame Pending */
+#define IEEE_802154_FC_ACK_REQ             0x0020 /* bit 5: AR (ACK required) */
+#define IEEE_802154_FC_PANID_COMPR         0x0040 /* bit 6: PAN ID Compression (src and dst are equal, src PAN ID omitted) */
+#define IEEE_802154_FC_RESERVED            0x0080
+#define IEEE_802154_FC_SEQNO_SUPPR         0x0100 /* bit 8: Sequence Number Suppression */
+#define IEEE_802154_FC_IE_PRESENT          0x0200 /* bit 9: IE Present */
+#define IEEE_802154_FC_DST_ADDR_MODE_MASK  0x0c00 /* bits 10..11: Destination Addressing Mode */
+#define IEEE_802154_FC_DST_ADDR_MODE_NO_ADDR   (IEEE_802154_ADDR_MODE_NO_ADDR << 10)
+#define IEEE_802154_FC_DST_ADDR_MODE_SHORT     (IEEE_802154_ADDR_MODE_SHORT << 10)
+#define IEEE_802154_FC_DST_ADDR_MODE_EXT       (IEEE_802154_ADDR_MODE_EXT << 10)
+#define IEEE_802154_FC_FRAME_VERSION_MASK  0x3000 /* bits 12..13: Frame Version */
+#define IEEE_802154_FC_FRAME_VERSION_GET(x)    (((x) & IEEE_802154_FC_FRAME_VERSION_MASK) >> 12)
+#define IEEE_802154_FC_SRC_ADDR_MODE_MASK  0xc000 /* bits 14..15: Source Addressing Mode */
+#define IEEE_802154_FC_SRC_ADDR_MODE_SHORT     (IEEE_802154_ADDR_MODE_SHORT << 14)
+#define IEEE_802154_FC_SRC_ADDR_MODE_EXT       (IEEE_802154_ADDR_MODE_EXT << 14)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_NETIF_IEEE802154_H */
diff --git a/lib/lwip/include/netif/lowpan6.h b/lib/lwip/include/netif/lowpan6.h
new file mode 100644
index 0000000000..ecff24ba66
--- /dev/null
+++ b/lib/lwip/include/netif/lowpan6.h
@@ -0,0 +1,89 @@
+/**
+ * @file
+ *
+ * 6LowPAN output for IPv6. Uses ND tables for link-layer addressing. Fragments packets to 6LowPAN units.
+ */
+
+/*
+ * Copyright (c) 2015 Inico Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Ivan Delamer <delamer@inicotech.com>
+ *
+ *
+ * Please coordinate changes and requests with Ivan Delamer
+ * <delamer@inicotech.com>
+ */
+
+#ifndef LWIP_HDR_LOWPAN6_H
+#define LWIP_HDR_LOWPAN6_H
+
+#include "netif/lowpan6_opts.h"
+
+#if LWIP_IPV6
+
+#include "netif/lowpan6_common.h"
+#include "lwip/pbuf.h"
+#include "lwip/ip.h"
+#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** 1 second period for reassembly */
+#define LOWPAN6_TMR_INTERVAL 1000
+
+void lowpan6_tmr(void);
+
+err_t lowpan6_set_context(u8_t idx, const ip6_addr_t * context);
+err_t lowpan6_set_short_addr(u8_t addr_high, u8_t addr_low);
+
+#if LWIP_IPV4
+err_t lowpan4_output(struct netif *netif, struct pbuf *q, const ip4_addr_t *ipaddr);
+#endif /* LWIP_IPV4 */
+err_t lowpan6_output(struct netif *netif, struct pbuf *q, const ip6_addr_t *ip6addr);
+err_t lowpan6_input(struct pbuf * p, struct netif *netif);
+err_t lowpan6_if_init(struct netif *netif);
+
+/* pan_id in network byte order. */
+err_t lowpan6_set_pan_id(u16_t pan_id);
+
+u16_t lowpan6_calc_crc(const void *buf, u16_t len);
+
+#if !NO_SYS
+err_t tcpip_6lowpan_input(struct pbuf *p, struct netif *inp);
+#endif /* !NO_SYS */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_IPV6 */
+
+#endif /* LWIP_HDR_LOWPAN6_H */
diff --git a/lib/lwip/include/netif/lowpan6_ble.h b/lib/lwip/include/netif/lowpan6_ble.h
new file mode 100644
index 0000000000..ff35fcd37b
--- /dev/null
+++ b/lib/lwip/include/netif/lowpan6_ble.h
@@ -0,0 +1,78 @@
+/**
+ * @file
+ * 6LowPAN over BLE for IPv6 (RFC7668).
+ */
+
+/*
+ * Copyright (c) 2017 Benjamin Aigner
+ * Copyright (c) 2015 Inico Technologies Ltd. , Author: Ivan Delamer <delamer@inicotech.com>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Benjamin Aigner <aignerb@technikum-wien.at>
+ *
+ * Based on the original 6lowpan implementation of lwIP ( @see 6lowpan.c)
+ */
+
+#ifndef LWIP_HDR_LOWPAN6_BLE_H
+#define LWIP_HDR_LOWPAN6_BLE_H
+
+#include "netif/lowpan6_opts.h"
+
+#if LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */
+
+#include "netif/lowpan6_common.h"
+#include "lwip/pbuf.h"
+#include "lwip/ip.h"
+#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+err_t rfc7668_output(struct netif *netif, struct pbuf *q, const ip6_addr_t *ip6addr);
+err_t rfc7668_input(struct pbuf * p, struct netif *netif);
+err_t rfc7668_set_local_addr_eui64(struct netif *netif, const u8_t *local_addr, size_t local_addr_len);
+err_t rfc7668_set_local_addr_mac48(struct netif *netif, const u8_t *local_addr, size_t local_addr_len, int is_public_addr);
+err_t rfc7668_set_peer_addr_eui64(struct netif *netif, const u8_t *peer_addr, size_t peer_addr_len);
+err_t rfc7668_set_peer_addr_mac48(struct netif *netif, const u8_t *peer_addr, size_t peer_addr_len, int is_public_addr);
+err_t rfc7668_set_context(u8_t index, const ip6_addr_t * context);
+err_t rfc7668_if_init(struct netif *netif);
+
+#if !NO_SYS
+err_t tcpip_rfc7668_input(struct pbuf *p, struct netif *inp);
+#endif
+
+void ble_addr_to_eui64(u8_t *dst, const u8_t *src, int public_addr);
+void eui64_to_ble_addr(u8_t *dst, const u8_t *src);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_IPV6 */
+
+#endif /* LWIP_HDR_LOWPAN6_BLE_H */
diff --git a/lib/lwip/include/netif/lowpan6_common.h b/lib/lwip/include/netif/lowpan6_common.h
new file mode 100644
index 0000000000..0dc13ab5bf
--- /dev/null
+++ b/lib/lwip/include/netif/lowpan6_common.h
@@ -0,0 +1,82 @@
+/**
+ * @file
+ *
+ * Common 6LowPAN routines for IPv6. Uses ND tables for link-layer addressing. Fragments packets to 6LowPAN units.
+ */
+
+/*
+ * Copyright (c) 2015 Inico Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Ivan Delamer <delamer@inicotech.com>
+ *
+ *
+ * Please coordinate changes and requests with Ivan Delamer
+ * <delamer@inicotech.com>
+ */
+
+#ifndef LWIP_HDR_LOWPAN6_COMMON_H
+#define LWIP_HDR_LOWPAN6_COMMON_H
+
+#include "netif/lowpan6_opts.h"
+
+#if LWIP_IPV6 /* don't build if IPv6 is disabled in lwipopts.h */
+
+#include "lwip/pbuf.h"
+#include "lwip/ip.h"
+#include "lwip/ip6_addr.h"
+#include "lwip/netif.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Helper define for a link layer address, which can be encoded as 0, 2 or 8 bytes */
+struct lowpan6_link_addr {
+  /* encoded length of the address */
+  u8_t addr_len;
+  /* address bytes */
+  u8_t addr[8];
+};
+
+s8_t lowpan6_get_address_mode(const ip6_addr_t *ip6addr, const struct lowpan6_link_addr *mac_addr);
+
+#if LWIP_6LOWPAN_IPHC
+err_t lowpan6_compress_headers(struct netif *netif, u8_t *inbuf, size_t inbuf_size, u8_t *outbuf, size_t outbuf_size,
+                               u8_t *lowpan6_header_len_out, u8_t *hidden_header_len_out, ip6_addr_t *lowpan6_contexts,
+                               const struct lowpan6_link_addr *src, const struct lowpan6_link_addr *dst);
+struct pbuf *lowpan6_decompress(struct pbuf *p, u16_t datagram_size, ip6_addr_t *lowpan6_contexts,
+                                struct lowpan6_link_addr *src, struct lowpan6_link_addr *dest);
+#endif /* LWIP_6LOWPAN_IPHC */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_IPV6 */
+
+#endif /* LWIP_HDR_LOWPAN6_COMMON_H */
diff --git a/lib/lwip/include/netif/lowpan6_opts.h b/lib/lwip/include/netif/lowpan6_opts.h
new file mode 100644
index 0000000000..32e5c5ee14
--- /dev/null
+++ b/lib/lwip/include/netif/lowpan6_opts.h
@@ -0,0 +1,122 @@
+/**
+ * @file
+ * 6LowPAN options list
+ */
+
+/*
+ * Copyright (c) 2015 Inico Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Ivan Delamer <delamer@inicotech.com>
+ *
+ *
+ * Please coordinate changes and requests with Ivan Delamer
+ * <delamer@inicotech.com>
+ */
+
+#ifndef LWIP_HDR_LOWPAN6_OPTS_H
+#define LWIP_HDR_LOWPAN6_OPTS_H
+
+#include "lwip/opt.h"
+
+/** LWIP_6LOWPAN_NUM_CONTEXTS: define the number of compression
+ * contexts per netif type
+ */
+#ifndef LWIP_6LOWPAN_NUM_CONTEXTS
+#define LWIP_6LOWPAN_NUM_CONTEXTS        10
+#endif
+
+/** LWIP_6LOWPAN_INFER_SHORT_ADDRESS: set this to 0 to disable creating
+ * short addresses for matching addresses (debug only)
+ */
+#ifndef LWIP_6LOWPAN_INFER_SHORT_ADDRESS
+#define LWIP_6LOWPAN_INFER_SHORT_ADDRESS 1
+#endif
+
+/** LWIP_6LOWPAN_IPHC: set this to 0 to disable IP header compression as per
+ * RFC 6282 (which is mandatory for BLE)
+ */
+#ifndef LWIP_6LOWPAN_IPHC
+#define LWIP_6LOWPAN_IPHC                1
+#endif
+
+/** Set this to 1 if your IEEE 802.15.4 interface can calculate and check the
+ * CRC in hardware. This means TX packets get 2 zero bytes added on transmission
+ * which are to be filled with the CRC.
+ */
+#ifndef LWIP_6LOWPAN_802154_HW_CRC
+#define LWIP_6LOWPAN_802154_HW_CRC       0
+#endif
+
+/** If LWIP_6LOWPAN_802154_HW_CRC==0, this can override the default slow
+ * implementation of the CRC used for 6LoWPAN over IEEE 802.15.4 (which uses
+ * a shift register).
+ */
+#ifndef LWIP_6LOWPAN_CALC_CRC
+#define LWIP_6LOWPAN_CALC_CRC(buf, len)  lowpan6_calc_crc(buf, len)
+#endif
+
+/** Debug level for 6LoWPAN in general */
+#ifndef LWIP_LOWPAN6_DEBUG
+#define LWIP_LOWPAN6_DEBUG               LWIP_DBG_OFF
+#endif
+
+/** Debug level for 6LoWPAN over IEEE 802.15.4 */
+#ifndef LWIP_LOWPAN6_802154_DEBUG
+#define LWIP_LOWPAN6_802154_DEBUG        LWIP_DBG_OFF
+#endif
+
+/** LWIP_LOWPAN6_IP_COMPRESSED_DEBUG: enable compressed IP frame
+ * output debugging
+ */
+#ifndef LWIP_LOWPAN6_IP_COMPRESSED_DEBUG
+#define LWIP_LOWPAN6_IP_COMPRESSED_DEBUG      LWIP_DBG_OFF
+#endif
+
+/** LWIP_LOWPAN6_DECOMPRESSION_DEBUG: enable decompression debug output
+ */
+#ifndef LWIP_LOWPAN6_DECOMPRESSION_DEBUG
+#define LWIP_LOWPAN6_DECOMPRESSION_DEBUG      LWIP_DBG_OFF
+#endif
+
+/** LWIP_RFC7668_IP_UNCOMPRESSED_DEBUG: enable decompressed IP frame
+ * output debugging */
+#ifndef LWIP_RFC7668_IP_UNCOMPRESSED_DEBUG
+#define LWIP_RFC7668_IP_UNCOMPRESSED_DEBUG    LWIP_DBG_OFF
+#endif
+
+/** LWIP_RFC7668_LINUX_WORKAROUND_PUBLIC_ADDRESS:
+ * Currently, the linux kernel driver for 6lowpan sets/clears a bit in
+ * the address, depending on the BD address (either public or not).
+ * Might not be RFC7668 conform, so you may select to do that (=1) or
+ * not (=0) */
+#ifndef LWIP_RFC7668_LINUX_WORKAROUND_PUBLIC_ADDRESS
+#define LWIP_RFC7668_LINUX_WORKAROUND_PUBLIC_ADDRESS 1
+#endif
+
+
+#endif /* LWIP_HDR_LOWPAN6_OPTS_H */
diff --git a/lib/lwip/include/netif/ppp/ccp.h b/lib/lwip/include/netif/ppp/ccp.h
new file mode 100644
index 0000000000..b2285228b8
--- /dev/null
+++ b/lib/lwip/include/netif/ppp/ccp.h
@@ -0,0 +1,164 @@
+/*
+ * ccp.h - Definitions for PPP Compression Control Protocol.
+ *
+ * Copyright (c) 1994-2002 Paul Mackerras. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 3. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Paul Mackerras
+ *     <paulus@samba.org>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id: ccp.h,v 1.12 2004/11/04 10:02:26 paulus Exp $
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && CCP_SUPPORT  /* don't build if not configured for use in lwipopts.h */
+
+#ifndef CCP_H
+#define CCP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * CCP codes.
+ */
+
+#define CCP_CONFREQ	1
+#define CCP_CONFACK	2
+#define CCP_TERMREQ	5
+#define CCP_TERMACK	6
+#define CCP_RESETREQ	14
+#define CCP_RESETACK	15
+
+/*
+ * Max # bytes for a CCP option
+ */
+
+#define CCP_MAX_OPTION_LENGTH	32
+
+/*
+ * Parts of a CCP packet.
+ */
+
+#define CCP_CODE(dp)		((dp)[0])
+#define CCP_ID(dp)		((dp)[1])
+#define CCP_LENGTH(dp)		(((dp)[2] << 8) + (dp)[3])
+#define CCP_HDRLEN		4
+
+#define CCP_OPT_CODE(dp)	((dp)[0])
+#define CCP_OPT_LENGTH(dp)	((dp)[1])
+#define CCP_OPT_MINLEN		2
+
+#if BSDCOMPRESS_SUPPORT
+/*
+ * Definitions for BSD-Compress.
+ */
+
+#define CI_BSD_COMPRESS		21	/* config. option for BSD-Compress */
+#define CILEN_BSD_COMPRESS	3	/* length of config. option */
+
+/* Macros for handling the 3rd byte of the BSD-Compress config option. */
+#define BSD_NBITS(x)		((x) & 0x1F)	/* number of bits requested */
+#define BSD_VERSION(x)		((x) >> 5)	/* version of option format */
+#define BSD_CURRENT_VERSION	1		/* current version number */
+#define BSD_MAKE_OPT(v, n)	(((v) << 5) | (n))
+
+#define BSD_MIN_BITS		9	/* smallest code size supported */
+#define BSD_MAX_BITS		15	/* largest code size supported */
+#endif /* BSDCOMPRESS_SUPPORT */
+
+#if DEFLATE_SUPPORT
+/*
+ * Definitions for Deflate.
+ */
+
+#define CI_DEFLATE		26	/* config option for Deflate */
+#define CI_DEFLATE_DRAFT	24	/* value used in original draft RFC */
+#define CILEN_DEFLATE		4	/* length of its config option */
+
+#define DEFLATE_MIN_SIZE	9
+#define DEFLATE_MAX_SIZE	15
+#define DEFLATE_METHOD_VAL	8
+#define DEFLATE_SIZE(x)		(((x) >> 4) + 8)
+#define DEFLATE_METHOD(x)	((x) & 0x0F)
+#define DEFLATE_MAKE_OPT(w)	((((w) - 8) << 4) + DEFLATE_METHOD_VAL)
+#define DEFLATE_CHK_SEQUENCE	0
+#endif /* DEFLATE_SUPPORT */
+
+#if MPPE_SUPPORT
+/*
+ * Definitions for MPPE.
+ */
+
+#define CI_MPPE                18      /* config option for MPPE */
+#define CILEN_MPPE              6      /* length of config option */
+#endif /* MPPE_SUPPORT */
+
+#if PREDICTOR_SUPPORT
+/*
+ * Definitions for other, as yet unsupported, compression methods.
+ */
+
+#define CI_PREDICTOR_1		1	/* config option for Predictor-1 */
+#define CILEN_PREDICTOR_1	2	/* length of its config option */
+#define CI_PREDICTOR_2		2	/* config option for Predictor-2 */
+#define CILEN_PREDICTOR_2	2	/* length of its config option */
+#endif /* PREDICTOR_SUPPORT */
+
+typedef struct ccp_options {
+#if DEFLATE_SUPPORT
+    unsigned int deflate          :1; /* do Deflate? */
+    unsigned int deflate_correct  :1; /* use correct code for deflate? */
+    unsigned int deflate_draft    :1; /* use draft RFC code for deflate? */
+#endif /* DEFLATE_SUPPORT */
+#if BSDCOMPRESS_SUPPORT
+    unsigned int bsd_compress     :1; /* do BSD Compress? */
+#endif /* BSDCOMPRESS_SUPPORT */
+#if PREDICTOR_SUPPORT
+    unsigned int predictor_1      :1; /* do Predictor-1? */
+    unsigned int predictor_2      :1; /* do Predictor-2? */
+#endif /* PREDICTOR_SUPPORT */
+
+#if MPPE_SUPPORT
+    u8_t mppe;			/* MPPE bitfield */
+#endif /* MPPE_SUPPORT */
+#if BSDCOMPRESS_SUPPORT
+    u_short bsd_bits;		/* # bits/code for BSD Compress */
+#endif /* BSDCOMPRESS_SUPPORT */
+#if DEFLATE_SUPPORT
+    u_short deflate_size;	/* lg(window size) for Deflate */
+#endif /* DEFLATE_SUPPORT */
+    u8_t method;		/* code for chosen compression method */
+} ccp_options;
+
+extern const struct protent ccp_protent;
+
+void ccp_resetrequest(ppp_pcb *pcb);  /* Issue a reset-request. */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* CCP_H */
+#endif /* PPP_SUPPORT && CCP_SUPPORT */
diff --git a/lib/lwip/include/netif/ppp/chap-md5.h b/lib/lwip/include/netif/ppp/chap-md5.h
new file mode 100644
index 0000000000..eb0269fe50
--- /dev/null
+++ b/lib/lwip/include/netif/ppp/chap-md5.h
@@ -0,0 +1,36 @@
+/*
+ * chap-md5.h - New CHAP/MD5 implementation.
+ *
+ * Copyright (c) 2003 Paul Mackerras. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 3. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Paul Mackerras
+ *     <paulus@samba.org>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && CHAP_SUPPORT  /* don't build if not configured for use in lwipopts.h */
+
+extern const struct chap_digest_type md5_digest;
+
+#endif /* PPP_SUPPORT && CHAP_SUPPORT */
diff --git a/lib/lwip/include/netif/ppp/chap-new.h b/lib/lwip/include/netif/ppp/chap-new.h
new file mode 100644
index 0000000000..2d8cd9ca99
--- /dev/null
+++ b/lib/lwip/include/netif/ppp/chap-new.h
@@ -0,0 +1,200 @@
+/*
+ * chap-new.c - New CHAP implementation.
+ *
+ * Copyright (c) 2003 Paul Mackerras. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 3. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Paul Mackerras
+ *     <paulus@samba.org>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && CHAP_SUPPORT  /* don't build if not configured for use in lwipopts.h */
+
+#ifndef CHAP_H
+#define CHAP_H
+
+#include "ppp.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * CHAP packets begin with a standard header with code, id, len (2 bytes).
+ */
+#define CHAP_HDRLEN	4
+
+/*
+ * Values for the code field.
+ */
+#define CHAP_CHALLENGE	1
+#define CHAP_RESPONSE	2
+#define CHAP_SUCCESS	3
+#define CHAP_FAILURE	4
+
+/*
+ * CHAP digest codes.
+ */
+#define CHAP_MD5		5
+#if MSCHAP_SUPPORT
+#define CHAP_MICROSOFT		0x80
+#define CHAP_MICROSOFT_V2	0x81
+#endif /* MSCHAP_SUPPORT */
+
+/*
+ * Semi-arbitrary limits on challenge and response fields.
+ */
+#define MAX_CHALLENGE_LEN	64
+#define MAX_RESPONSE_LEN	64
+
+/*
+ * These limits apply to challenge and response packets we send.
+ * The +4 is the +1 that we actually need rounded up.
+ */
+#define CHAL_MAX_PKTLEN	(PPP_HDRLEN + CHAP_HDRLEN + 4 + MAX_CHALLENGE_LEN + MAXNAMELEN)
+#define RESP_MAX_PKTLEN	(PPP_HDRLEN + CHAP_HDRLEN + 4 + MAX_RESPONSE_LEN + MAXNAMELEN)
+
+/* bitmask of supported algorithms */
+#if MSCHAP_SUPPORT
+#define MDTYPE_MICROSOFT_V2	0x1
+#define MDTYPE_MICROSOFT	0x2
+#endif /* MSCHAP_SUPPORT */
+#define MDTYPE_MD5		0x4
+#define MDTYPE_NONE		0
+
+#if MSCHAP_SUPPORT
+/* Return the digest alg. ID for the most preferred digest type. */
+#define CHAP_DIGEST(mdtype) \
+    ((mdtype) & MDTYPE_MD5)? CHAP_MD5: \
+    ((mdtype) & MDTYPE_MICROSOFT_V2)? CHAP_MICROSOFT_V2: \
+    ((mdtype) & MDTYPE_MICROSOFT)? CHAP_MICROSOFT: \
+    0
+#else /* !MSCHAP_SUPPORT */
+#define CHAP_DIGEST(mdtype) \
+    ((mdtype) & MDTYPE_MD5)? CHAP_MD5: \
+    0
+#endif /* MSCHAP_SUPPORT */
+
+/* Return the bit flag (lsb set) for our most preferred digest type. */
+#define CHAP_MDTYPE(mdtype) ((mdtype) ^ ((mdtype) - 1)) & (mdtype)
+
+/* Return the bit flag for a given digest algorithm ID. */
+#if MSCHAP_SUPPORT
+#define CHAP_MDTYPE_D(digest) \
+    ((digest) == CHAP_MICROSOFT_V2)? MDTYPE_MICROSOFT_V2: \
+    ((digest) == CHAP_MICROSOFT)? MDTYPE_MICROSOFT: \
+    ((digest) == CHAP_MD5)? MDTYPE_MD5: \
+    0
+#else /* !MSCHAP_SUPPORT */
+#define CHAP_MDTYPE_D(digest) \
+    ((digest) == CHAP_MD5)? MDTYPE_MD5: \
+    0
+#endif /* MSCHAP_SUPPORT */
+
+/* Can we do the requested digest? */
+#if MSCHAP_SUPPORT
+#define CHAP_CANDIGEST(mdtype, digest) \
+    ((digest) == CHAP_MICROSOFT_V2)? (mdtype) & MDTYPE_MICROSOFT_V2: \
+    ((digest) == CHAP_MICROSOFT)? (mdtype) & MDTYPE_MICROSOFT: \
+    ((digest) == CHAP_MD5)? (mdtype) & MDTYPE_MD5: \
+    0
+#else /* !MSCHAP_SUPPORT */
+#define CHAP_CANDIGEST(mdtype, digest) \
+    ((digest) == CHAP_MD5)? (mdtype) & MDTYPE_MD5: \
+    0
+#endif /* MSCHAP_SUPPORT */
+
+/*
+ * The code for each digest type has to supply one of these.
+ */
+struct chap_digest_type {
+	int code;
+
+#if PPP_SERVER
+	/*
+	 * Note: challenge and response arguments below are formatted as
+	 * a length byte followed by the actual challenge/response data.
+	 */
+	void (*generate_challenge)(ppp_pcb *pcb, unsigned char *challenge);
+	int (*verify_response)(ppp_pcb *pcb, int id, const char *name,
+		const unsigned char *secret, int secret_len,
+		const unsigned char *challenge, const unsigned char *response,
+		char *message, int message_space);
+#endif /* PPP_SERVER */
+	void (*make_response)(ppp_pcb *pcb, unsigned char *response, int id, const char *our_name,
+		const unsigned char *challenge, const char *secret, int secret_len,
+		unsigned char *priv);
+	int (*check_success)(ppp_pcb *pcb, unsigned char *pkt, int len, unsigned char *priv);
+	void (*handle_failure)(ppp_pcb *pcb, unsigned char *pkt, int len);
+};
+
+/*
+ * Each interface is described by chap structure.
+ */
+#if CHAP_SUPPORT
+typedef struct chap_client_state {
+	u8_t flags;
+	const char *name;
+	const struct chap_digest_type *digest;
+	unsigned char priv[64];		/* private area for digest's use */
+} chap_client_state;
+
+#if PPP_SERVER
+typedef struct chap_server_state {
+	u8_t flags;
+	u8_t id;
+	const char *name;
+	const struct chap_digest_type *digest;
+	int challenge_xmits;
+	int challenge_pktlen;
+	unsigned char challenge[CHAL_MAX_PKTLEN];
+} chap_server_state;
+#endif /* PPP_SERVER */
+#endif /* CHAP_SUPPORT */
+
+#if 0 /* UNUSED */
+/* Hook for a plugin to validate CHAP challenge */
+extern int (*chap_verify_hook)(char *name, char *ourname, int id,
+			const struct chap_digest_type *digest,
+			unsigned char *challenge, unsigned char *response,
+			char *message, int message_space);
+#endif /* UNUSED */
+
+#if PPP_SERVER
+/* Called by authentication code to start authenticating the peer. */
+extern void chap_auth_peer(ppp_pcb *pcb, const char *our_name, int digest_code);
+#endif /* PPP_SERVER */
+
+/* Called by auth. code to start authenticating us to the peer. */
+extern void chap_auth_with_peer(ppp_pcb *pcb, const char *our_name, int digest_code);
+
+/* Represents the CHAP protocol to the main pppd code */
+extern const struct protent chap_protent;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* CHAP_H */
+#endif /* PPP_SUPPORT && CHAP_SUPPORT */
diff --git a/lib/lwip/include/netif/ppp/chap_ms.h b/lib/lwip/include/netif/ppp/chap_ms.h
new file mode 100644
index 0000000000..0795291158
--- /dev/null
+++ b/lib/lwip/include/netif/ppp/chap_ms.h
@@ -0,0 +1,44 @@
+/*
+ * chap_ms.h - Challenge Handshake Authentication Protocol definitions.
+ *
+ * Copyright (c) 1995 Eric Rosenquist.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id: chap_ms.h,v 1.13 2004/11/15 22:13:26 paulus Exp $
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && MSCHAP_SUPPORT  /* don't build if not configured for use in lwipopts.h */
+
+#ifndef CHAPMS_INCLUDE
+#define CHAPMS_INCLUDE
+
+extern const struct chap_digest_type chapms_digest;
+extern const struct chap_digest_type chapms2_digest;
+
+#endif /* CHAPMS_INCLUDE */
+
+#endif /* PPP_SUPPORT && MSCHAP_SUPPORT */
diff --git a/lib/lwip/include/netif/ppp/eap.h b/lib/lwip/include/netif/ppp/eap.h
new file mode 100644
index 0000000000..32434ed582
--- /dev/null
+++ b/lib/lwip/include/netif/ppp/eap.h
@@ -0,0 +1,169 @@
+/*
+ * eap.h - Extensible Authentication Protocol for PPP (RFC 2284)
+ *
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ *
+ * Non-exclusive rights to redistribute, modify, translate, and use
+ * this software in source and binary forms, in whole or in part, is
+ * hereby granted, provided that the above copyright notice is
+ * duplicated in any source form, and that neither the name of the
+ * copyright holder nor the author is used to endorse or promote
+ * products derived from this software.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Original version by James Carlson
+ *
+ * $Id: eap.h,v 1.2 2003/06/11 23:56:26 paulus Exp $
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && EAP_SUPPORT  /* don't build if not configured for use in lwipopts.h */
+
+#ifndef PPP_EAP_H
+#define	PPP_EAP_H
+
+#include "ppp.h"
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+/*
+ * Packet header = Code, id, length.
+ */
+#define	EAP_HEADERLEN	4
+
+
+/* EAP message codes. */
+#define	EAP_REQUEST	1
+#define	EAP_RESPONSE	2
+#define	EAP_SUCCESS	3
+#define	EAP_FAILURE	4
+
+/* EAP types */
+#define	EAPT_IDENTITY		1
+#define	EAPT_NOTIFICATION	2
+#define	EAPT_NAK		3	/* (response only) */
+#define	EAPT_MD5CHAP		4
+#define	EAPT_OTP		5	/* One-Time Password; RFC 1938 */
+#define	EAPT_TOKEN		6	/* Generic Token Card */
+/* 7 and 8 are unassigned. */
+#define	EAPT_RSA		9	/* RSA Public Key Authentication */
+#define	EAPT_DSS		10	/* DSS Unilateral */
+#define	EAPT_KEA		11	/* KEA */
+#define	EAPT_KEA_VALIDATE	12	/* KEA-VALIDATE	*/
+#define	EAPT_TLS		13	/* EAP-TLS */
+#define	EAPT_DEFENDER		14	/* Defender Token (AXENT) */
+#define	EAPT_W2K		15	/* Windows 2000 EAP */
+#define	EAPT_ARCOT		16	/* Arcot Systems */
+#define	EAPT_CISCOWIRELESS	17	/* Cisco Wireless */
+#define	EAPT_NOKIACARD		18	/* Nokia IP smart card */
+#define	EAPT_SRP		19	/* Secure Remote Password */
+/* 20 is deprecated */
+
+/* EAP SRP-SHA1 Subtypes */
+#define	EAPSRP_CHALLENGE	1	/* Request 1 - Challenge */
+#define	EAPSRP_CKEY		1	/* Response 1 - Client Key */
+#define	EAPSRP_SKEY		2	/* Request 2 - Server Key */
+#define	EAPSRP_CVALIDATOR	2	/* Response 2 - Client Validator */
+#define	EAPSRP_SVALIDATOR	3	/* Request 3 - Server Validator */
+#define	EAPSRP_ACK		3	/* Response 3 - final ack */
+#define	EAPSRP_LWRECHALLENGE	4	/* Req/resp 4 - Lightweight rechal */
+
+#define	SRPVAL_EBIT	0x00000001	/* Use shared key for ECP */
+
+#define	SRP_PSEUDO_ID	"pseudo_"
+#define	SRP_PSEUDO_LEN	7
+
+#define MD5_SIGNATURE_SIZE	16
+#define EAP_MIN_CHALLENGE_LENGTH	17
+#define EAP_MAX_CHALLENGE_LENGTH	24
+#define EAP_MIN_MAX_POWER_OF_TWO_CHALLENGE_LENGTH     3   /* 2^3-1 = 7, 17+7 = 24 */
+
+#define	EAP_STATES	\
+	"Initial", "Pending", "Closed", "Listen", "Identify", \
+	"SRP1", "SRP2", "SRP3", "MD5Chall", "Open", "SRP4", "BadAuth"
+
+#define	eap_client_active(pcb)	((pcb)->eap.es_client.ea_state == eapListen)
+#if PPP_SERVER
+#define	eap_server_active(pcb)	\
+	((pcb)->eap.es_server.ea_state >= eapIdentify && \
+	 (pcb)->eap.es_server.ea_state <= eapMD5Chall)
+#endif /* PPP_SERVER */
+
+/*
+ * Complete EAP state for one PPP session.
+ */
+enum eap_state_code {
+	eapInitial = 0,	/* No EAP authentication yet requested */
+	eapPending,	/* Waiting for LCP (no timer) */
+	eapClosed,	/* Authentication not in use */
+	eapListen,	/* Client ready (and timer running) */
+	eapIdentify,	/* EAP Identify sent */
+	eapSRP1,	/* Sent EAP SRP-SHA1 Subtype 1 */
+	eapSRP2,	/* Sent EAP SRP-SHA1 Subtype 2 */
+	eapSRP3,	/* Sent EAP SRP-SHA1 Subtype 3 */
+	eapMD5Chall,	/* Sent MD5-Challenge */
+	eapOpen,	/* Completed authentication */
+	eapSRP4,	/* Sent EAP SRP-SHA1 Subtype 4 */
+	eapBadAuth	/* Failed authentication */
+};
+
+struct eap_auth {
+	const char *ea_name;	/* Our name */
+	char ea_peer[MAXNAMELEN +1];	/* Peer's name */
+	void *ea_session;	/* Authentication library linkage */
+	u_char *ea_skey;	/* Shared encryption key */
+	u_short ea_namelen;	/* Length of our name */
+	u_short ea_peerlen;	/* Length of peer's name */
+	enum eap_state_code ea_state;
+	u_char ea_id;		/* Current id */
+	u_char ea_requests;	/* Number of Requests sent/received */
+	u_char ea_responses;	/* Number of Responses */
+	u_char ea_type;		/* One of EAPT_* */
+	u32_t ea_keyflags;	/* SRP shared key usage flags */
+};
+
+#ifndef EAP_MAX_CHALLENGE_LENGTH
+#define EAP_MAX_CHALLENGE_LENGTH	24
+#endif
+typedef struct eap_state {
+	struct eap_auth es_client;	/* Client (authenticatee) data */
+#if PPP_SERVER
+	struct eap_auth es_server;	/* Server (authenticator) data */
+#endif /* PPP_SERVER */
+	int es_savedtime;		/* Saved timeout */
+	int es_rechallenge;		/* EAP rechallenge interval */
+	int es_lwrechallenge;		/* SRP lightweight rechallenge inter */
+	u8_t es_usepseudo;		/* Use SRP Pseudonym if offered one */
+	int es_usedpseudo;		/* Set if we already sent PN */
+	int es_challen;			/* Length of challenge string */
+	u_char es_challenge[EAP_MAX_CHALLENGE_LENGTH];
+} eap_state;
+
+/*
+ * Timeouts.
+ */
+#if 0 /* moved to ppp_opts.h */
+#define	EAP_DEFTIMEOUT		3	/* Timeout (seconds) for rexmit */
+#define	EAP_DEFTRANSMITS	10	/* max # times to transmit */
+#define	EAP_DEFREQTIME		20	/* Time to wait for peer request */
+#define	EAP_DEFALLOWREQ		20	/* max # times to accept requests */
+#endif /* moved to ppp_opts.h */
+
+void eap_authwithpeer(ppp_pcb *pcb, const char *localname);
+void eap_authpeer(ppp_pcb *pcb, const char *localname);
+
+extern const struct protent eap_protent;
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif /* PPP_EAP_H */
+
+#endif /* PPP_SUPPORT && EAP_SUPPORT */
diff --git a/lib/lwip/include/netif/ppp/ecp.h b/lib/lwip/include/netif/ppp/ecp.h
new file mode 100644
index 0000000000..d8808c3ab2
--- /dev/null
+++ b/lib/lwip/include/netif/ppp/ecp.h
@@ -0,0 +1,62 @@
+/*
+ * ecp.h - Definitions for PPP Encryption Control Protocol.
+ *
+ * Copyright (c) 2002 Google, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id: ecp.h,v 1.2 2003/01/10 07:12:36 fcusack Exp $
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && ECP_SUPPORT  /* don't build if not configured for use in lwipopts.h */
+
+#ifndef ECP_H
+#define ECP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct ecp_options {
+    bool required;		/* Is ECP required? */
+    unsigned enctype;		/* Encryption type */
+} ecp_options;
+
+extern fsm ecp_fsm[];
+extern ecp_options ecp_wantoptions[];
+extern ecp_options ecp_gotoptions[];
+extern ecp_options ecp_allowoptions[];
+extern ecp_options ecp_hisoptions[];
+
+extern const struct protent ecp_protent;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ECP_H */
+#endif /* PPP_SUPPORT && ECP_SUPPORT */
diff --git a/lib/lwip/include/netif/ppp/eui64.h b/lib/lwip/include/netif/ppp/eui64.h
new file mode 100644
index 0000000000..5adeb482a8
--- /dev/null
+++ b/lib/lwip/include/netif/ppp/eui64.h
@@ -0,0 +1,102 @@
+/*
+ * eui64.h - EUI64 routines for IPv6CP.
+ *
+ * Copyright (c) 1999 Tommi Komulainen.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Tommi Komulainen
+ *     <Tommi.Komulainen@iki.fi>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id: eui64.h,v 1.6 2002/12/04 23:03:32 paulus Exp $
+*/
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && PPP_IPV6_SUPPORT  /* don't build if not configured for use in lwipopts.h */
+
+#ifndef EUI64_H
+#define EUI64_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * @todo:
+ *
+ * Maybe this should be done by processing struct in6_addr directly...
+ */
+typedef union
+{
+    u8_t e8[8];
+    u16_t e16[4];
+    u32_t e32[2];
+} eui64_t;
+
+#define eui64_iszero(e)		(((e).e32[0] | (e).e32[1]) == 0)
+#define eui64_equals(e, o)	(((e).e32[0] == (o).e32[0]) && \
+				((e).e32[1] == (o).e32[1]))
+#define eui64_zero(e)		(e).e32[0] = (e).e32[1] = 0;
+
+#define eui64_copy(s, d)	memcpy(&(d), &(s), sizeof(eui64_t))
+
+#define eui64_magic(e)		do {			\
+				(e).e32[0] = magic();	\
+				(e).e32[1] = magic();	\
+				(e).e8[0] &= ~2;	\
+				} while (0)
+#define eui64_magic_nz(x)	do {				\
+				eui64_magic(x);			\
+				} while (eui64_iszero(x))
+#define eui64_magic_ne(x, y)	do {				\
+				eui64_magic(x);			\
+				} while (eui64_equals(x, y))
+
+#define eui64_get(ll, cp)	do {				\
+				eui64_copy((*cp), (ll));	\
+				(cp) += sizeof(eui64_t);	\
+				} while (0)
+
+#define eui64_put(ll, cp)	do {				\
+				eui64_copy((ll), (*cp));	\
+				(cp) += sizeof(eui64_t);	\
+				} while (0)
+
+#define eui64_set32(e, l)	do {			\
+				(e).e32[0] = 0;		\
+				(e).e32[1] = lwip_htonl(l);	\
+				} while (0)
+#define eui64_setlo32(e, l)	eui64_set32(e, l)
+
+char *eui64_ntoa(eui64_t);	/* Returns ascii representation of id */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* EUI64_H */
+#endif /* PPP_SUPPORT && PPP_IPV6_SUPPORT */
diff --git a/lib/lwip/include/netif/ppp/fsm.h b/lib/lwip/include/netif/ppp/fsm.h
new file mode 100644
index 0000000000..8dec700e07
--- /dev/null
+++ b/lib/lwip/include/netif/ppp/fsm.h
@@ -0,0 +1,182 @@
+/*
+ * fsm.h - {Link, IP} Control Protocol Finite State Machine definitions.
+ *
+ * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name "Carnegie Mellon University" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For permission or any legal
+ *    details, please contact
+ *      Office of Technology Transfer
+ *      Carnegie Mellon University
+ *      5000 Forbes Avenue
+ *      Pittsburgh, PA  15213-3890
+ *      (412) 268-4387, fax: (412) 268-7395
+ *      tech-transfer@andrew.cmu.edu
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Computing Services
+ *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id: fsm.h,v 1.10 2004/11/13 02:28:15 paulus Exp $
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#ifndef FSM_H
+#define	FSM_H
+
+#include "ppp.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Packet header = Code, id, length.
+ */
+#define HEADERLEN	4
+
+
+/*
+ *  CP (LCP, IPCP, etc.) codes.
+ */
+#define CONFREQ		1	/* Configuration Request */
+#define CONFACK		2	/* Configuration Ack */
+#define CONFNAK		3	/* Configuration Nak */
+#define CONFREJ		4	/* Configuration Reject */
+#define TERMREQ		5	/* Termination Request */
+#define TERMACK		6	/* Termination Ack */
+#define CODEREJ		7	/* Code Reject */
+
+
+/*
+ * Each FSM is described by an fsm structure and fsm callbacks.
+ */
+typedef struct fsm {
+    ppp_pcb *pcb;		/* PPP Interface */
+    const struct fsm_callbacks *callbacks;	/* Callback routines */
+    const char *term_reason;	/* Reason for closing protocol */
+    u8_t seen_ack;		/* Have received valid Ack/Nak/Rej to Req */
+				  /* -- This is our only flag, we might use u_int :1 if we have more flags */
+    u16_t protocol;		/* Data Link Layer Protocol field value */
+    u8_t state;			/* State */
+    u8_t flags;			/* Contains option bits */
+    u8_t id;			/* Current id */
+    u8_t reqid;			/* Current request id */
+    u8_t retransmits;		/* Number of retransmissions left */
+    u8_t nakloops;		/* Number of nak loops since last ack */
+    u8_t rnakloops;		/* Number of naks received */
+    u8_t maxnakloops;		/* Maximum number of nak loops tolerated
+				   (necessary because IPCP require a custom large max nak loops value) */
+    u8_t term_reason_len;	/* Length of term_reason */
+} fsm;
+
+
+typedef struct fsm_callbacks {
+    void (*resetci)		/* Reset our Configuration Information */
+		(fsm *);
+    int  (*cilen)		/* Length of our Configuration Information */
+		(fsm *);
+    void (*addci) 		/* Add our Configuration Information */
+		(fsm *, u_char *, int *);
+    int  (*ackci)		/* ACK our Configuration Information */
+		(fsm *, u_char *, int);
+    int  (*nakci)		/* NAK our Configuration Information */
+		(fsm *, u_char *, int, int);
+    int  (*rejci)		/* Reject our Configuration Information */
+		(fsm *, u_char *, int);
+    int  (*reqci)		/* Request peer's Configuration Information */
+		(fsm *, u_char *, int *, int);
+    void (*up)			/* Called when fsm reaches PPP_FSM_OPENED state */
+		(fsm *);
+    void (*down)		/* Called when fsm leaves PPP_FSM_OPENED state */
+		(fsm *);
+    void (*starting)		/* Called when we want the lower layer */
+		(fsm *);
+    void (*finished)		/* Called when we don't want the lower layer */
+		(fsm *);
+    void (*protreject)		/* Called when Protocol-Reject received */
+		(int);
+    void (*retransmit)		/* Retransmission is necessary */
+		(fsm *);
+    int  (*extcode)		/* Called when unknown code received */
+		(fsm *, int, int, u_char *, int);
+    const char *proto_name;	/* String name for protocol (for messages) */
+} fsm_callbacks;
+
+
+/*
+ * Link states.
+ */
+#define PPP_FSM_INITIAL		0	/* Down, hasn't been opened */
+#define PPP_FSM_STARTING	1	/* Down, been opened */
+#define PPP_FSM_CLOSED		2	/* Up, hasn't been opened */
+#define PPP_FSM_STOPPED		3	/* Open, waiting for down event */
+#define PPP_FSM_CLOSING		4	/* Terminating the connection, not open */
+#define PPP_FSM_STOPPING	5	/* Terminating, but open */
+#define PPP_FSM_REQSENT		6	/* We've sent a Config Request */
+#define PPP_FSM_ACKRCVD		7	/* We've received a Config Ack */
+#define PPP_FSM_ACKSENT		8	/* We've sent a Config Ack */
+#define PPP_FSM_OPENED		9	/* Connection available */
+
+
+/*
+ * Flags - indicate options controlling FSM operation
+ */
+#define OPT_PASSIVE	1	/* Don't die if we don't get a response */
+#define OPT_RESTART	2	/* Treat 2nd OPEN as DOWN, UP */
+#define OPT_SILENT	4	/* Wait for peer to speak first */
+
+
+/*
+ * Timeouts.
+ */
+#if 0 /* moved to ppp_opts.h */
+#define DEFTIMEOUT	3	/* Timeout time in seconds */
+#define DEFMAXTERMREQS	2	/* Maximum Terminate-Request transmissions */
+#define DEFMAXCONFREQS	10	/* Maximum Configure-Request transmissions */
+#define DEFMAXNAKLOOPS	5	/* Maximum number of nak loops */
+#endif /* moved to ppp_opts.h */
+
+
+/*
+ * Prototypes
+ */
+void fsm_init(fsm *f);
+void fsm_lowerup(fsm *f);
+void fsm_lowerdown(fsm *f);
+void fsm_open(fsm *f);
+void fsm_close(fsm *f, const char *reason);
+void fsm_input(fsm *f, u_char *inpacket, int l);
+void fsm_protreject(fsm *f);
+void fsm_sdata(fsm *f, u_char code, u_char id, const u_char *data, int datalen);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FSM_H */
+#endif /* PPP_SUPPORT */
diff --git a/lib/lwip/include/netif/ppp/ipcp.h b/lib/lwip/include/netif/ppp/ipcp.h
new file mode 100644
index 0000000000..32fdd1c641
--- /dev/null
+++ b/lib/lwip/include/netif/ppp/ipcp.h
@@ -0,0 +1,134 @@
+/*
+ * ipcp.h - IP Control Protocol definitions.
+ *
+ * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name "Carnegie Mellon University" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For permission or any legal
+ *    details, please contact
+ *      Office of Technology Transfer
+ *      Carnegie Mellon University
+ *      5000 Forbes Avenue
+ *      Pittsburgh, PA  15213-3890
+ *      (412) 268-4387, fax: (412) 268-7395
+ *      tech-transfer@andrew.cmu.edu
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Computing Services
+ *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id: ipcp.h,v 1.14 2002/12/04 23:03:32 paulus Exp $
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && PPP_IPV4_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#ifndef IPCP_H
+#define	IPCP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Options.
+ */
+#define CI_ADDRS	1	/* IP Addresses */
+#if VJ_SUPPORT
+#define CI_COMPRESSTYPE	2	/* Compression Type */
+#endif /* VJ_SUPPORT */
+#define	CI_ADDR		3
+
+#if LWIP_DNS
+#define CI_MS_DNS1      129	/* Primary DNS value */
+#define CI_MS_DNS2      131     /* Secondary DNS value */
+#endif /* LWIP_DNS */
+#if 0 /* UNUSED - WINS */
+#define CI_MS_WINS1     130     /* Primary WINS value */
+#define CI_MS_WINS2     132	/* Secondary WINS value */
+#endif /* UNUSED - WINS */
+
+#if VJ_SUPPORT
+#define MAX_STATES 16		/* from slcompress.h */
+
+#define IPCP_VJMODE_OLD 1	/* "old" mode (option # = 0x0037) */
+#define IPCP_VJMODE_RFC1172 2	/* "old-rfc"mode (option # = 0x002d) */
+#define IPCP_VJMODE_RFC1332 3	/* "new-rfc"mode (option # = 0x002d, */
+                                /*  maxslot and slot number compression) */
+
+#define IPCP_VJ_COMP 0x002d	/* current value for VJ compression option*/
+#define IPCP_VJ_COMP_OLD 0x0037	/* "old" (i.e, broken) value for VJ */
+				/* compression option*/ 
+#endif /* VJ_SUPPORT */
+
+typedef struct ipcp_options {
+    unsigned int neg_addr               :1; /* Negotiate IP Address? */
+    unsigned int old_addrs              :1; /* Use old (IP-Addresses) option? */
+    unsigned int req_addr               :1; /* Ask peer to send IP address? */
+#if 0 /* UNUSED */
+    unsigned int default_route          :1; /* Assign default route through interface? */
+    unsigned int replace_default_route  :1; /* Replace default route through interface? */
+#endif /* UNUSED */
+#if 0 /* UNUSED - PROXY ARP */
+    unsigned int proxy_arp              :1; /* Make proxy ARP entry for peer? */
+#endif /* UNUSED - PROXY ARP */
+#if VJ_SUPPORT
+    unsigned int neg_vj                 :1; /* Van Jacobson Compression? */
+    unsigned int old_vj                 :1; /* use old (short) form of VJ option? */
+    unsigned int cflag                  :1;
+#endif /* VJ_SUPPORT */
+    unsigned int accept_local           :1; /* accept peer's value for ouraddr */
+    unsigned int accept_remote          :1; /* accept peer's value for hisaddr */
+#if LWIP_DNS
+    unsigned int req_dns1               :1; /* Ask peer to send primary DNS address? */
+    unsigned int req_dns2               :1; /* Ask peer to send secondary DNS address? */
+#endif /* LWIP_DNS */
+
+    u32_t ouraddr, hisaddr;	/* Addresses in NETWORK BYTE ORDER */
+#if LWIP_DNS
+    u32_t dnsaddr[2];	/* Primary and secondary MS DNS entries */
+#endif /* LWIP_DNS */
+#if 0 /* UNUSED - WINS */
+    u32_t winsaddr[2];	/* Primary and secondary MS WINS entries */
+#endif /* UNUSED - WINS */
+
+#if VJ_SUPPORT
+    u16_t vj_protocol;		/* protocol value to use in VJ option */
+    u8_t  maxslotindex;		/* values for RFC1332 VJ compression neg. */
+#endif /* VJ_SUPPORT */
+} ipcp_options;
+
+#if 0 /* UNUSED, already defined by lwIP */
+char *ip_ntoa (u32_t);
+#endif /* UNUSED, already defined by lwIP */
+
+extern const struct protent ipcp_protent;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* IPCP_H */
+#endif /* PPP_SUPPORT && PPP_IPV4_SUPPORT */
diff --git a/lib/lwip/include/netif/ppp/ipv6cp.h b/lib/lwip/include/netif/ppp/ipv6cp.h
new file mode 100644
index 0000000000..9099973869
--- /dev/null
+++ b/lib/lwip/include/netif/ppp/ipv6cp.h
@@ -0,0 +1,191 @@
+/*
+ * ipv6cp.h - PPP IPV6 Control Protocol.
+ *
+ * Copyright (c) 1999 Tommi Komulainen.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Tommi Komulainen
+ *     <Tommi.Komulainen@iki.fi>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+/*  Original version, based on RFC2023 :
+
+    Copyright (c) 1995, 1996, 1997 Francis.Dupont@inria.fr, INRIA Rocquencourt,
+    Alain.Durand@imag.fr, IMAG,
+    Jean-Luc.Richier@imag.fr, IMAG-LSR.
+
+    Copyright (c) 1998, 1999 Francis.Dupont@inria.fr, GIE DYADE,
+    Alain.Durand@imag.fr, IMAG,
+    Jean-Luc.Richier@imag.fr, IMAG-LSR.
+
+    Ce travail a été fait au sein du GIE DYADE (Groupement d'Intérêt
+    Économique ayant pour membres BULL S.A. et l'INRIA).
+
+    Ce logiciel informatique est disponible aux conditions
+    usuelles dans la recherche, c'est-à-dire qu'il peut
+    être utilisé, copié, modifié, distribué à l'unique
+    condition que ce texte soit conservé afin que
+    l'origine de ce logiciel soit reconnue.
+
+    Le nom de l'Institut National de Recherche en Informatique
+    et en Automatique (INRIA), de l'IMAG, ou d'une personne morale
+    ou physique ayant participé à l'élaboration de ce logiciel ne peut
+    être utilisé sans son accord préalable explicite.
+
+    Ce logiciel est fourni tel quel sans aucune garantie,
+    support ou responsabilité d'aucune sorte.
+    Ce logiciel est dérivé de sources d'origine
+    "University of California at Berkeley" et
+    "Digital Equipment Corporation" couvertes par des copyrights.
+
+    L'Institut d'Informatique et de Mathématiques Appliquées de Grenoble (IMAG)
+    est une fédération d'unités mixtes de recherche du CNRS, de l'Institut National
+    Polytechnique de Grenoble et de l'Université Joseph Fourier regroupant
+    sept laboratoires dont le laboratoire Logiciels, Systèmes, Réseaux (LSR).
+
+    This work has been done in the context of GIE DYADE (joint R & D venture
+    between BULL S.A. and INRIA).
+
+    This software is available with usual "research" terms
+    with the aim of retain credits of the software. 
+    Permission to use, copy, modify and distribute this software for any
+    purpose and without fee is hereby granted, provided that the above
+    copyright notice and this permission notice appear in all copies,
+    and the name of INRIA, IMAG, or any contributor not be used in advertising
+    or publicity pertaining to this material without the prior explicit
+    permission. The software is provided "as is" without any
+    warranties, support or liabilities of any kind.
+    This software is derived from source code from
+    "University of California at Berkeley" and
+    "Digital Equipment Corporation" protected by copyrights.
+
+    Grenoble's Institute of Computer Science and Applied Mathematics (IMAG)
+    is a federation of seven research units funded by the CNRS, National
+    Polytechnic Institute of Grenoble and University Joseph Fourier.
+    The research unit in Software, Systems, Networks (LSR) is member of IMAG.
+*/
+
+/*
+ * Derived from :
+ *
+ *
+ * ipcp.h - IP Control Protocol definitions.
+ *
+ * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name "Carnegie Mellon University" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For permission or any legal
+ *    details, please contact
+ *      Office of Technology Transfer
+ *      Carnegie Mellon University
+ *      5000 Forbes Avenue
+ *      Pittsburgh, PA  15213-3890
+ *      (412) 268-4387, fax: (412) 268-7395
+ *      tech-transfer@andrew.cmu.edu
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Computing Services
+ *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id: ipv6cp.h,v 1.7 2002/12/04 23:03:32 paulus Exp $
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && PPP_IPV6_SUPPORT  /* don't build if not configured for use in lwipopts.h */
+
+#ifndef IPV6CP_H
+#define	IPV6CP_H
+
+#include "eui64.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Options.
+ */
+#define CI_IFACEID	1	/* Interface Identifier */
+#ifdef IPV6CP_COMP
+#define CI_COMPRESSTYPE	2	/* Compression Type     */
+#endif /* IPV6CP_COMP */
+
+/* No compression types yet defined.
+ *#define IPV6CP_COMP	0x004f
+ */
+typedef struct ipv6cp_options {
+    unsigned int neg_ifaceid    :1;  /* Negotiate interface identifier? */
+    unsigned int req_ifaceid    :1;  /* Ask peer to send interface identifier? */
+    unsigned int accept_local   :1;  /* accept peer's value for iface id? */
+    unsigned int opt_local      :1;  /* ourtoken set by option */
+    unsigned int opt_remote     :1;  /* histoken set by option */
+    unsigned int use_ip         :1;  /* use IP as interface identifier */
+#if 0
+    unsigned int use_persistent :1;  /* use uniquely persistent value for address */
+#endif
+#ifdef IPV6CP_COMP
+    unsigned int neg_vj         :1;  /* Van Jacobson Compression? */
+#endif /* IPV6CP_COMP */
+
+#ifdef IPV6CP_COMP
+    u_short vj_protocol;        /* protocol value to use in VJ option */
+#endif /* IPV6CP_COMP */
+    eui64_t ourid, hisid;       /* Interface identifiers */
+} ipv6cp_options;
+
+extern const struct protent ipv6cp_protent;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* IPV6CP_H */
+#endif /* PPP_SUPPORT && PPP_IPV6_SUPPORT */
diff --git a/lib/lwip/include/netif/ppp/lcp.h b/lib/lwip/include/netif/ppp/lcp.h
new file mode 100644
index 0000000000..18ad1cb23b
--- /dev/null
+++ b/lib/lwip/include/netif/ppp/lcp.h
@@ -0,0 +1,179 @@
+/*
+ * lcp.h - Link Control Protocol definitions.
+ *
+ * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name "Carnegie Mellon University" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For permission or any legal
+ *    details, please contact
+ *      Office of Technology Transfer
+ *      Carnegie Mellon University
+ *      5000 Forbes Avenue
+ *      Pittsburgh, PA  15213-3890
+ *      (412) 268-4387, fax: (412) 268-7395
+ *      tech-transfer@andrew.cmu.edu
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Computing Services
+ *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id: lcp.h,v 1.20 2004/11/14 22:53:42 carlsonj Exp $
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#ifndef LCP_H
+#define	LCP_H
+
+#include "ppp.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Options.
+ */
+#define CI_VENDOR	0	/* Vendor Specific */
+#define CI_MRU		1	/* Maximum Receive Unit */
+#define CI_ASYNCMAP	2	/* Async Control Character Map */
+#define CI_AUTHTYPE	3	/* Authentication Type */
+#define CI_QUALITY	4	/* Quality Protocol */
+#define CI_MAGICNUMBER	5	/* Magic Number */
+#define CI_PCOMPRESSION	7	/* Protocol Field Compression */
+#define CI_ACCOMPRESSION 8	/* Address/Control Field Compression */
+#define CI_FCSALTERN	9	/* FCS-Alternatives */
+#define CI_SDP		10	/* Self-Describing-Pad */
+#define CI_NUMBERED	11	/* Numbered-Mode */
+#define CI_CALLBACK	13	/* callback */
+#define CI_MRRU		17	/* max reconstructed receive unit; multilink */
+#define CI_SSNHF	18	/* short sequence numbers for multilink */
+#define CI_EPDISC	19	/* endpoint discriminator */
+#define CI_MPPLUS	22	/* Multi-Link-Plus-Procedure */
+#define CI_LDISC	23	/* Link-Discriminator */
+#define CI_LCPAUTH	24	/* LCP Authentication */
+#define CI_COBS		25	/* Consistent Overhead Byte Stuffing */
+#define CI_PREFELIS	26	/* Prefix Elision */
+#define CI_MPHDRFMT	27	/* MP Header Format */
+#define CI_I18N		28	/* Internationalization */
+#define CI_SDL		29	/* Simple Data Link */
+
+/*
+ * LCP-specific packet types (code numbers).
+ */
+#define PROTREJ		8	/* Protocol Reject */
+#define ECHOREQ		9	/* Echo Request */
+#define ECHOREP		10	/* Echo Reply */
+#define DISCREQ		11	/* Discard Request */
+#define IDENTIF		12	/* Identification */
+#define TIMEREM		13	/* Time Remaining */
+
+/* Value used as data for CI_CALLBACK option */
+#define CBCP_OPT	6	/* Use callback control protocol */
+
+#if 0 /* moved to ppp_opts.h */
+#define DEFMRU	1500		/* Try for this */
+#define MINMRU	128		/* No MRUs below this */
+#define MAXMRU	16384		/* Normally limit MRU to this */
+#endif /* moved to ppp_opts.h */
+
+/* An endpoint discriminator, used with multilink. */
+#define MAX_ENDP_LEN	20	/* maximum length of discriminator value */
+struct epdisc {
+    unsigned char	class_; /* -- The word "class" is reserved in C++. */
+    unsigned char	length;
+    unsigned char	value[MAX_ENDP_LEN];
+};
+
+/*
+ * The state of options is described by an lcp_options structure.
+ */
+typedef struct lcp_options {
+    unsigned int passive           :1; /* Don't die if we don't get a response */
+    unsigned int silent            :1; /* Wait for the other end to start first */
+#if 0 /* UNUSED */
+    unsigned int restart           :1; /* Restart vs. exit after close */
+#endif /* UNUSED */
+    unsigned int neg_mru           :1; /* Negotiate the MRU? */
+    unsigned int neg_asyncmap      :1; /* Negotiate the async map? */
+#if PAP_SUPPORT
+    unsigned int neg_upap          :1; /* Ask for UPAP authentication? */
+#endif /* PAP_SUPPORT */
+#if CHAP_SUPPORT
+    unsigned int neg_chap          :1; /* Ask for CHAP authentication? */
+#endif /* CHAP_SUPPORT */
+#if EAP_SUPPORT
+    unsigned int neg_eap           :1; /* Ask for EAP authentication? */
+#endif /* EAP_SUPPORT */
+    unsigned int neg_magicnumber   :1; /* Ask for magic number? */
+    unsigned int neg_pcompression  :1; /* HDLC Protocol Field Compression? */
+    unsigned int neg_accompression :1; /* HDLC Address/Control Field Compression? */
+#if LQR_SUPPORT
+    unsigned int neg_lqr           :1; /* Negotiate use of Link Quality Reports */
+#endif /* LQR_SUPPORT */
+    unsigned int neg_cbcp          :1; /* Negotiate use of CBCP */
+#ifdef HAVE_MULTILINK
+    unsigned int neg_mrru          :1; /* negotiate multilink MRRU */
+#endif /* HAVE_MULTILINK */
+    unsigned int neg_ssnhf         :1; /* negotiate short sequence numbers */
+    unsigned int neg_endpoint      :1; /* negotiate endpoint discriminator */
+
+    u16_t mru;			/* Value of MRU */
+#ifdef HAVE_MULTILINK
+    u16_t mrru;			/* Value of MRRU, and multilink enable */
+#endif /* MULTILINK */
+#if CHAP_SUPPORT
+    u8_t chap_mdtype;		/* which MD types (hashing algorithm) */
+#endif /* CHAP_SUPPORT */
+    u32_t asyncmap;		/* Value of async map */
+    u32_t magicnumber;
+    u8_t  numloops;		/* Number of loops during magic number neg. */
+#if LQR_SUPPORT
+    u32_t lqr_period;	/* Reporting period for LQR 1/100ths second */
+#endif /* LQR_SUPPORT */
+    struct epdisc endpoint;	/* endpoint discriminator */
+} lcp_options;
+
+void lcp_open(ppp_pcb *pcb);
+void lcp_close(ppp_pcb *pcb, const char *reason);
+void lcp_lowerup(ppp_pcb *pcb);
+void lcp_lowerdown(ppp_pcb *pcb);
+void lcp_sprotrej(ppp_pcb *pcb, u_char *p, int len);    /* send protocol reject */
+
+extern const struct protent lcp_protent;
+
+#if 0 /* moved to ppp_opts.h */
+/* Default number of times we receive our magic number from the peer
+   before deciding the link is looped-back. */
+#define DEFLOOPBACKFAIL	10
+#endif /* moved to ppp_opts.h */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LCP_H */
+#endif /* PPP_SUPPORT */
diff --git a/lib/lwip/include/netif/ppp/magic.h b/lib/lwip/include/netif/ppp/magic.h
new file mode 100644
index 0000000000..12a002cd5c
--- /dev/null
+++ b/lib/lwip/include/netif/ppp/magic.h
@@ -0,0 +1,130 @@
+/*
+ * magic.h - PPP Magic Number definitions.
+ *
+ * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name "Carnegie Mellon University" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For permission or any legal
+ *    details, please contact
+ *      Office of Technology Transfer
+ *      Carnegie Mellon University
+ *      5000 Forbes Avenue
+ *      Pittsburgh, PA  15213-3890
+ *      (412) 268-4387, fax: (412) 268-7395
+ *      tech-transfer@andrew.cmu.edu
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Computing Services
+ *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id: magic.h,v 1.5 2003/06/11 23:56:26 paulus Exp $
+ */
+/*****************************************************************************
+* randm.h - Random number generator header file.
+*
+* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
+* Copyright (c) 1998 Global Election Systems Inc.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 03-01-01 Marc Boucher <marc@mbsi.ca>
+*   Ported to lwIP.
+* 98-05-29 Guy Lancaster <glanca@gesn.com>, Global Election Systems Inc.
+*   Extracted from avos.
+*****************************************************************************/
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#ifndef MAGIC_H
+#define MAGIC_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/***********************
+*** PUBLIC FUNCTIONS ***
+***********************/
+
+/*
+ * Initialize the random number generator.
+ */
+void magic_init(void);
+
+/*
+ * Randomize our random seed value.  To be called for truly random events
+ * such as user operations and network traffic.
+ */
+void magic_randomize(void);
+
+/*
+ * Return a new random number.
+ */
+u32_t magic(void);	/* Returns the next magic number */
+
+/*
+ * Fill buffer with random bytes
+ *
+ * Use the random pool to generate random data.  This degrades to pseudo
+ * random when used faster than randomness is supplied using magic_churnrand().
+ * Thus it's important to make sure that the results of this are not
+ * published directly because one could predict the next result to at
+ * least some degree.  Also, it's important to get a good seed before
+ * the first use.
+ */
+void magic_random_bytes(unsigned char *buf, u32_t buf_len);
+
+/*
+ * Return a new random number between 0 and (2^pow)-1 included.
+ */
+u32_t magic_pow(u8_t pow);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* MAGIC_H */
+
+#endif /* PPP_SUPPORT */
diff --git a/lib/lwip/include/netif/ppp/mppe.h b/lib/lwip/include/netif/ppp/mppe.h
new file mode 100644
index 0000000000..5de1128479
--- /dev/null
+++ b/lib/lwip/include/netif/ppp/mppe.h
@@ -0,0 +1,181 @@
+/*
+ * mppe.h - Definitions for MPPE
+ *
+ * Copyright (c) 2008 Paul Mackerras. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Paul Mackerras
+ *     <paulus@samba.org>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && MPPE_SUPPORT  /* don't build if not configured for use in lwipopts.h */
+
+#ifndef MPPE_H
+#define MPPE_H
+
+#include "netif/ppp/pppcrypt.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define MPPE_PAD		4	/* MPPE growth per frame */
+#define MPPE_MAX_KEY_LEN	16	/* largest key length (128-bit) */
+
+/* option bits for ccp_options.mppe */
+#define MPPE_OPT_40		0x01	/* 40 bit */
+#define MPPE_OPT_128		0x02	/* 128 bit */
+#define MPPE_OPT_STATEFUL	0x04	/* stateful mode */
+/* unsupported opts */
+#define MPPE_OPT_56		0x08	/* 56 bit */
+#define MPPE_OPT_MPPC		0x10	/* MPPC compression */
+#define MPPE_OPT_D		0x20	/* Unknown */
+#define MPPE_OPT_UNSUPPORTED (MPPE_OPT_56|MPPE_OPT_MPPC|MPPE_OPT_D)
+#define MPPE_OPT_UNKNOWN	0x40	/* Bits !defined in RFC 3078 were set */
+
+/*
+ * This is not nice ... the alternative is a bitfield struct though.
+ * And unfortunately, we cannot share the same bits for the option
+ * names above since C and H are the same bit.  We could do a u_int32
+ * but then we have to do a lwip_htonl() all the time and/or we still need
+ * to know which octet is which.
+ */
+#define MPPE_C_BIT		0x01	/* MPPC */
+#define MPPE_D_BIT		0x10	/* Obsolete, usage unknown */
+#define MPPE_L_BIT		0x20	/* 40-bit */
+#define MPPE_S_BIT		0x40	/* 128-bit */
+#define MPPE_M_BIT		0x80	/* 56-bit, not supported */
+#define MPPE_H_BIT		0x01	/* Stateless (in a different byte) */
+
+/* Does not include H bit; used for least significant octet only. */
+#define MPPE_ALL_BITS (MPPE_D_BIT|MPPE_L_BIT|MPPE_S_BIT|MPPE_M_BIT|MPPE_H_BIT)
+
+/* Build a CI from mppe opts (see RFC 3078) */
+#define MPPE_OPTS_TO_CI(opts, ci)		\
+    do {					\
+	u_char *ptr = ci; /* u_char[4] */	\
+						\
+	/* H bit */				\
+	if (opts & MPPE_OPT_STATEFUL)		\
+	    *ptr++ = 0x0;			\
+	else					\
+	    *ptr++ = MPPE_H_BIT;		\
+	*ptr++ = 0;				\
+	*ptr++ = 0;				\
+						\
+	/* S,L bits */				\
+	*ptr = 0;				\
+	if (opts & MPPE_OPT_128)		\
+	    *ptr |= MPPE_S_BIT;			\
+	if (opts & MPPE_OPT_40)			\
+	    *ptr |= MPPE_L_BIT;			\
+	/* M,D,C bits not supported */		\
+    } while (/* CONSTCOND */ 0)
+
+/* The reverse of the above */
+#define MPPE_CI_TO_OPTS(ci, opts)		\
+    do {					\
+	const u_char *ptr = ci; /* u_char[4] */	\
+						\
+	opts = 0;				\
+						\
+	/* H bit */				\
+	if (!(ptr[0] & MPPE_H_BIT))		\
+	    opts |= MPPE_OPT_STATEFUL;		\
+						\
+	/* S,L bits */				\
+	if (ptr[3] & MPPE_S_BIT)		\
+	    opts |= MPPE_OPT_128;		\
+	if (ptr[3] & MPPE_L_BIT)		\
+	    opts |= MPPE_OPT_40;		\
+						\
+	/* M,D,C bits */			\
+	if (ptr[3] & MPPE_M_BIT)		\
+	    opts |= MPPE_OPT_56;		\
+	if (ptr[3] & MPPE_D_BIT)		\
+	    opts |= MPPE_OPT_D;			\
+	if (ptr[3] & MPPE_C_BIT)		\
+	    opts |= MPPE_OPT_MPPC;		\
+						\
+	/* Other bits */			\
+	if (ptr[0] & ~MPPE_H_BIT)		\
+	    opts |= MPPE_OPT_UNKNOWN;		\
+	if (ptr[1] || ptr[2])			\
+	    opts |= MPPE_OPT_UNKNOWN;		\
+	if (ptr[3] & ~MPPE_ALL_BITS)		\
+	    opts |= MPPE_OPT_UNKNOWN;		\
+    } while (/* CONSTCOND */ 0)
+
+/* Shared MPPE padding between MSCHAP and MPPE */
+#define SHA1_PAD_SIZE 40
+
+static const u8_t mppe_sha1_pad1[SHA1_PAD_SIZE] = {
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+static const u8_t mppe_sha1_pad2[SHA1_PAD_SIZE] = {
+  0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
+  0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
+  0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
+  0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2
+};
+
+/*
+ * State for an MPPE (de)compressor.
+ */
+typedef struct ppp_mppe_state {
+	lwip_arc4_context arc4;
+	u8_t master_key[MPPE_MAX_KEY_LEN];
+	u8_t session_key[MPPE_MAX_KEY_LEN];
+	u8_t keylen;                /* key length in bytes */
+	/* NB: 128-bit == 16, 40-bit == 8!
+	 * If we want to support 56-bit, the unit has to change to bits
+	 */
+	u8_t bits;                  /* MPPE control bits */
+	u16_t ccount;               /* 12-bit coherency count (seqno)  */
+	u16_t sanity_errors;        /* take down LCP if too many */
+	unsigned int stateful  :1;  /* stateful mode flag */
+	unsigned int discard   :1;  /* stateful mode packet loss flag */
+} ppp_mppe_state;
+
+void mppe_set_key(ppp_pcb *pcb, ppp_mppe_state *state, u8_t *key);
+void mppe_init(ppp_pcb *pcb, ppp_mppe_state *state, u8_t options);
+void mppe_comp_reset(ppp_pcb *pcb, ppp_mppe_state *state);
+err_t mppe_compress(ppp_pcb *pcb, ppp_mppe_state *state, struct pbuf **pb, u16_t protocol);
+void mppe_decomp_reset(ppp_pcb *pcb, ppp_mppe_state *state);
+err_t mppe_decompress(ppp_pcb *pcb, ppp_mppe_state *state, struct pbuf **pb);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* MPPE_H */
+#endif /* PPP_SUPPORT && MPPE_SUPPORT */
diff --git a/lib/lwip/include/netif/ppp/polarssl/arc4.h b/lib/lwip/include/netif/ppp/polarssl/arc4.h
new file mode 100644
index 0000000000..4af724cd90
--- /dev/null
+++ b/lib/lwip/include/netif/ppp/polarssl/arc4.h
@@ -0,0 +1,81 @@
+/**
+ * \file arc4.h
+ *
+ *  Based on XySSL: Copyright (C) 2006-2008  Christophe Devine
+ *
+ *  Copyright (C) 2009  Paul Bakker <polarssl_maintainer at polarssl dot org>
+ *
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions
+ *  are met:
+ *  
+ *    * Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *    * Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *    * Neither the names of PolarSSL or XySSL nor the names of its contributors
+ *      may be used to endorse or promote products derived from this software
+ *      without specific prior written permission.
+ *  
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ *  TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if LWIP_INCLUDED_POLARSSL_ARC4
+
+#ifndef LWIP_INCLUDED_POLARSSL_ARC4_H
+#define LWIP_INCLUDED_POLARSSL_ARC4_H
+
+/**
+ * \brief          ARC4 context structure
+ */
+typedef struct
+{
+    int x;                      /*!< permutation index */
+    int y;                      /*!< permutation index */
+    unsigned char m[256];       /*!< permutation table */
+}
+arc4_context;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief          ARC4 key schedule
+ *
+ * \param ctx      ARC4 context to be initialized
+ * \param key      the secret key
+ * \param keylen   length of the key
+ */
+void arc4_setup( arc4_context *ctx, unsigned char *key, int keylen );
+
+/**
+ * \brief          ARC4 cipher function
+ *
+ * \param ctx      ARC4 context
+ * \param buf      buffer to be processed
+ * \param buflen   amount of data in buf
+ */
+void arc4_crypt( arc4_context *ctx, unsigned char *buf, int buflen );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_INCLUDED_POLARSSL_ARC4_H */
+
+#endif /* LWIP_INCLUDED_POLARSSL_ARC4 */
diff --git a/lib/lwip/include/netif/ppp/polarssl/des.h b/lib/lwip/include/netif/ppp/polarssl/des.h
new file mode 100644
index 0000000000..e893890ed7
--- /dev/null
+++ b/lib/lwip/include/netif/ppp/polarssl/des.h
@@ -0,0 +1,92 @@
+/**
+ * \file des.h
+ *
+ *  Based on XySSL: Copyright (C) 2006-2008  Christophe Devine
+ *
+ *  Copyright (C) 2009  Paul Bakker <polarssl_maintainer at polarssl dot org>
+ *
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions
+ *  are met:
+ *  
+ *    * Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *    * Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *    * Neither the names of PolarSSL or XySSL nor the names of its contributors
+ *      may be used to endorse or promote products derived from this software
+ *      without specific prior written permission.
+ *  
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ *  TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if LWIP_INCLUDED_POLARSSL_DES
+
+#ifndef LWIP_INCLUDED_POLARSSL_DES_H
+#define LWIP_INCLUDED_POLARSSL_DES_H
+
+#define DES_ENCRYPT     1
+#define DES_DECRYPT     0
+
+/**
+ * \brief          DES context structure
+ */
+typedef struct
+{
+    int mode;                   /*!<  encrypt/decrypt   */
+    unsigned long sk[32];       /*!<  DES subkeys       */
+}
+des_context;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief          DES key schedule (56-bit, encryption)
+ *
+ * \param ctx      DES context to be initialized
+ * \param key      8-byte secret key
+ */
+void des_setkey_enc( des_context *ctx, unsigned char key[8] );
+
+/**
+ * \brief          DES key schedule (56-bit, decryption)
+ *
+ * \param ctx      DES context to be initialized
+ * \param key      8-byte secret key
+ */
+void des_setkey_dec( des_context *ctx, unsigned char key[8] );
+
+/**
+ * \brief          DES-ECB block encryption/decryption
+ *
+ * \param ctx      DES context
+ * \param input    64-bit input block
+ * \param output   64-bit output block
+ */
+void des_crypt_ecb( des_context *ctx,
+                    const unsigned char input[8],
+                    unsigned char output[8] );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_INCLUDED_POLARSSL_DES_H */
+
+#endif /* LWIP_INCLUDED_POLARSSL_DES */
diff --git a/lib/lwip/include/netif/ppp/polarssl/md4.h b/lib/lwip/include/netif/ppp/polarssl/md4.h
new file mode 100644
index 0000000000..570445687e
--- /dev/null
+++ b/lib/lwip/include/netif/ppp/polarssl/md4.h
@@ -0,0 +1,97 @@
+/**
+ * \file md4.h
+ *
+ *  Based on XySSL: Copyright (C) 2006-2008  Christophe Devine
+ *
+ *  Copyright (C) 2009  Paul Bakker <polarssl_maintainer at polarssl dot org>
+ *
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions
+ *  are met:
+ *  
+ *    * Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *    * Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *    * Neither the names of PolarSSL or XySSL nor the names of its contributors
+ *      may be used to endorse or promote products derived from this software
+ *      without specific prior written permission.
+ *  
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ *  TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if LWIP_INCLUDED_POLARSSL_MD4
+
+#ifndef LWIP_INCLUDED_POLARSSL_MD4_H
+#define LWIP_INCLUDED_POLARSSL_MD4_H
+
+/**
+ * \brief          MD4 context structure
+ */
+typedef struct
+{
+    unsigned long total[2];     /*!< number of bytes processed  */
+    unsigned long state[4];     /*!< intermediate digest state  */
+    unsigned char buffer[64];   /*!< data block being processed */
+}
+md4_context;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief          MD4 context setup
+ *
+ * \param ctx      context to be initialized
+ */
+void md4_starts( md4_context *ctx );
+
+/**
+ * \brief          MD4 process buffer
+ *
+ * \param ctx      MD4 context
+ * \param input    buffer holding the  data
+ * \param ilen     length of the input data
+ */
+void md4_update( md4_context *ctx, const unsigned char *input, int ilen );
+
+/**
+ * \brief          MD4 final digest
+ *
+ * \param ctx      MD4 context
+ * \param output   MD4 checksum result
+ */
+void md4_finish( md4_context *ctx, unsigned char output[16] );
+
+/**
+ * \brief          Output = MD4( input buffer )
+ *
+ * \param input    buffer holding the  data
+ * \param ilen     length of the input data
+ * \param output   MD4 checksum result
+ */
+void md4( unsigned char *input, int ilen, unsigned char output[16] );
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_INCLUDED_POLARSSL_MD4_H */
+
+#endif /* LWIP_INCLUDED_POLARSSL_MD4 */
diff --git a/lib/lwip/include/netif/ppp/polarssl/md5.h b/lib/lwip/include/netif/ppp/polarssl/md5.h
new file mode 100644
index 0000000000..1244011890
--- /dev/null
+++ b/lib/lwip/include/netif/ppp/polarssl/md5.h
@@ -0,0 +1,96 @@
+/**
+ * \file md5.h
+ *
+ *  Based on XySSL: Copyright (C) 2006-2008  Christophe Devine
+ *
+ *  Copyright (C) 2009  Paul Bakker <polarssl_maintainer at polarssl dot org>
+ *
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions
+ *  are met:
+ *  
+ *    * Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *    * Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *    * Neither the names of PolarSSL or XySSL nor the names of its contributors
+ *      may be used to endorse or promote products derived from this software
+ *      without specific prior written permission.
+ *  
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ *  TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if LWIP_INCLUDED_POLARSSL_MD5
+
+#ifndef LWIP_INCLUDED_POLARSSL_MD5_H
+#define LWIP_INCLUDED_POLARSSL_MD5_H
+
+/**
+ * \brief          MD5 context structure
+ */
+typedef struct
+{
+    unsigned long total[2];     /*!< number of bytes processed  */
+    unsigned long state[4];     /*!< intermediate digest state  */
+    unsigned char buffer[64];   /*!< data block being processed */
+}
+md5_context;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief          MD5 context setup
+ *
+ * \param ctx      context to be initialized
+ */
+void md5_starts( md5_context *ctx );
+
+/**
+ * \brief          MD5 process buffer
+ *
+ * \param ctx      MD5 context
+ * \param input    buffer holding the  data
+ * \param ilen     length of the input data
+ */
+void md5_update( md5_context *ctx, const unsigned char *input, int ilen );
+
+/**
+ * \brief          MD5 final digest
+ *
+ * \param ctx      MD5 context
+ * \param output   MD5 checksum result
+ */
+void md5_finish( md5_context *ctx, unsigned char output[16] );
+
+/**
+ * \brief          Output = MD5( input buffer )
+ *
+ * \param input    buffer holding the  data
+ * \param ilen     length of the input data
+ * \param output   MD5 checksum result
+ */
+void md5( unsigned char *input, int ilen, unsigned char output[16] );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_INCLUDED_POLARSSL_MD5_H */
+
+#endif /* LWIP_INCLUDED_POLARSSL_MD5 */
diff --git a/lib/lwip/include/netif/ppp/polarssl/sha1.h b/lib/lwip/include/netif/ppp/polarssl/sha1.h
new file mode 100644
index 0000000000..a4c53e07c5
--- /dev/null
+++ b/lib/lwip/include/netif/ppp/polarssl/sha1.h
@@ -0,0 +1,96 @@
+/**
+ * \file sha1.h
+ *
+ *  Based on XySSL: Copyright (C) 2006-2008  Christophe Devine
+ *
+ *  Copyright (C) 2009  Paul Bakker <polarssl_maintainer at polarssl dot org>
+ *
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions
+ *  are met:
+ *  
+ *    * Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *    * Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *    * Neither the names of PolarSSL or XySSL nor the names of its contributors
+ *      may be used to endorse or promote products derived from this software
+ *      without specific prior written permission.
+ *  
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ *  TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if LWIP_INCLUDED_POLARSSL_SHA1
+
+#ifndef LWIP_INCLUDED_POLARSSL_SHA1_H
+#define LWIP_INCLUDED_POLARSSL_SHA1_H
+
+/**
+ * \brief          SHA-1 context structure
+ */
+typedef struct
+{
+    unsigned long total[2];     /*!< number of bytes processed  */
+    unsigned long state[5];     /*!< intermediate digest state  */
+    unsigned char buffer[64];   /*!< data block being processed */
+}
+sha1_context;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief          SHA-1 context setup
+ *
+ * \param ctx      context to be initialized
+ */
+void sha1_starts( sha1_context *ctx );
+
+/**
+ * \brief          SHA-1 process buffer
+ *
+ * \param ctx      SHA-1 context
+ * \param input    buffer holding the  data
+ * \param ilen     length of the input data
+ */
+void sha1_update( sha1_context *ctx, const unsigned char *input, int ilen );
+
+/**
+ * \brief          SHA-1 final digest
+ *
+ * \param ctx      SHA-1 context
+ * \param output   SHA-1 checksum result
+ */
+void sha1_finish( sha1_context *ctx, unsigned char output[20] );
+
+/**
+ * \brief          Output = SHA-1( input buffer )
+ *
+ * \param input    buffer holding the  data
+ * \param ilen     length of the input data
+ * \param output   SHA-1 checksum result
+ */
+void sha1( unsigned char *input, int ilen, unsigned char output[20] );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_INCLUDED_POLARSSL_SHA1_H */
+
+#endif /* LWIP_INCLUDED_POLARSSL_SHA1 */
diff --git a/lib/lwip/include/netif/ppp/ppp.h b/lib/lwip/include/netif/ppp/ppp.h
new file mode 100644
index 0000000000..f93747fbd0
--- /dev/null
+++ b/lib/lwip/include/netif/ppp/ppp.h
@@ -0,0 +1,698 @@
+/*****************************************************************************
+* ppp.h - Network Point to Point Protocol header file.
+*
+* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
+* portions Copyright (c) 1997 Global Election Systems Inc.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any 
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 03-01-01 Marc Boucher <marc@mbsi.ca>
+*   Ported to lwIP.
+* 97-11-05 Guy Lancaster <glanca@gesn.com>, Global Election Systems Inc.
+*   Original derived from BSD codes.
+*****************************************************************************/
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#ifndef PPP_H
+#define PPP_H
+
+#include "lwip/def.h"
+#include "lwip/stats.h"
+#include "lwip/mem.h"
+#include "lwip/netif.h"
+#include "lwip/sys.h"
+#include "lwip/timeouts.h"
+#if PPP_IPV6_SUPPORT
+#include "lwip/ip6_addr.h"
+#endif /* PPP_IPV6_SUPPORT */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Disable non-working or rarely used PPP feature, so rarely that we don't want to bloat ppp_opts.h with them */
+#ifndef PPP_OPTIONS
+#define PPP_OPTIONS         0
+#endif
+
+#ifndef PPP_NOTIFY
+#define PPP_NOTIFY          0
+#endif
+
+#ifndef PPP_REMOTENAME
+#define PPP_REMOTENAME      0
+#endif
+
+#ifndef PPP_IDLETIMELIMIT
+#define PPP_IDLETIMELIMIT   0
+#endif
+
+#ifndef PPP_LCP_ADAPTIVE
+#define PPP_LCP_ADAPTIVE    0
+#endif
+
+#ifndef PPP_MAXCONNECT
+#define PPP_MAXCONNECT      0
+#endif
+
+#ifndef PPP_ALLOWED_ADDRS
+#define PPP_ALLOWED_ADDRS   0
+#endif
+
+#ifndef PPP_PROTOCOLNAME
+#define PPP_PROTOCOLNAME    0
+#endif
+
+#ifndef PPP_STATS_SUPPORT
+#define PPP_STATS_SUPPORT   0
+#endif
+
+#ifndef DEFLATE_SUPPORT
+#define DEFLATE_SUPPORT     0
+#endif
+
+#ifndef BSDCOMPRESS_SUPPORT
+#define BSDCOMPRESS_SUPPORT 0
+#endif
+
+#ifndef PREDICTOR_SUPPORT
+#define PREDICTOR_SUPPORT   0
+#endif
+
+/*************************
+*** PUBLIC DEFINITIONS ***
+*************************/
+
+/*
+ * The basic PPP frame.
+ */
+#define PPP_HDRLEN	4	/* octets for standard ppp header */
+#define PPP_FCSLEN	2	/* octets for FCS */
+
+/*
+ * Values for phase.
+ */
+#define PPP_PHASE_DEAD          0
+#define PPP_PHASE_MASTER        1
+#define PPP_PHASE_HOLDOFF       2
+#define PPP_PHASE_INITIALIZE    3
+#define PPP_PHASE_SERIALCONN    4
+#define PPP_PHASE_DORMANT       5
+#define PPP_PHASE_ESTABLISH     6
+#define PPP_PHASE_AUTHENTICATE  7
+#define PPP_PHASE_CALLBACK      8
+#define PPP_PHASE_NETWORK       9
+#define PPP_PHASE_RUNNING       10
+#define PPP_PHASE_TERMINATE     11
+#define PPP_PHASE_DISCONNECT    12
+
+/* Error codes. */
+#define PPPERR_NONE         0  /* No error. */
+#define PPPERR_PARAM        1  /* Invalid parameter. */
+#define PPPERR_OPEN         2  /* Unable to open PPP session. */
+#define PPPERR_DEVICE       3  /* Invalid I/O device for PPP. */
+#define PPPERR_ALLOC        4  /* Unable to allocate resources. */
+#define PPPERR_USER         5  /* User interrupt. */
+#define PPPERR_CONNECT      6  /* Connection lost. */
+#define PPPERR_AUTHFAIL     7  /* Failed authentication challenge. */
+#define PPPERR_PROTOCOL     8  /* Failed to meet protocol. */
+#define PPPERR_PEERDEAD     9  /* Connection timeout */
+#define PPPERR_IDLETIMEOUT  10 /* Idle Timeout */
+#define PPPERR_CONNECTTIME  11 /* Max connect time reached */
+#define PPPERR_LOOPBACK     12 /* Loopback detected */
+
+/* Whether auth support is enabled at all */
+#define PPP_AUTH_SUPPORT (PAP_SUPPORT || CHAP_SUPPORT || EAP_SUPPORT)
+
+/************************
+*** PUBLIC DATA TYPES ***
+************************/
+
+/*
+ * Other headers require ppp_pcb definition for prototypes, but ppp_pcb
+ * require some structure definition from other headers as well, we are
+ * fixing the dependency loop here by declaring the ppp_pcb type then
+ * by including headers containing necessary struct definition for ppp_pcb
+ */
+typedef struct ppp_pcb_s ppp_pcb;
+
+/* Type definitions for BSD code. */
+#ifndef __u_char_defined
+typedef unsigned long  u_long;
+typedef unsigned int   u_int;
+typedef unsigned short u_short;
+typedef unsigned char  u_char;
+#endif
+
+#include "fsm.h"
+#include "lcp.h"
+#if CCP_SUPPORT
+#include "ccp.h"
+#endif /* CCP_SUPPORT */
+#if MPPE_SUPPORT
+#include "mppe.h"
+#endif /* MPPE_SUPPORT */
+#if PPP_IPV4_SUPPORT
+#include "ipcp.h"
+#endif /* PPP_IPV4_SUPPORT */
+#if PPP_IPV6_SUPPORT
+#include "ipv6cp.h"
+#endif /* PPP_IPV6_SUPPORT */
+#if PAP_SUPPORT
+#include "upap.h"
+#endif /* PAP_SUPPORT */
+#if CHAP_SUPPORT
+#include "chap-new.h"
+#endif /* CHAP_SUPPORT */
+#if EAP_SUPPORT
+#include "eap.h"
+#endif /* EAP_SUPPORT */
+#if VJ_SUPPORT
+#include "vj.h"
+#endif /* VJ_SUPPORT */
+
+/* Link status callback function prototype */
+typedef void (*ppp_link_status_cb_fn)(ppp_pcb *pcb, int err_code, void *ctx);
+
+/*
+ * PPP configuration.
+ */
+typedef struct ppp_settings_s {
+
+#if PPP_SERVER && PPP_AUTH_SUPPORT
+  unsigned int  auth_required       :1;      /* Peer is required to authenticate */
+  unsigned int  null_login          :1;      /* Username of "" and a password of "" are acceptable */
+#endif /* PPP_SERVER && PPP_AUTH_SUPPORT */
+#if PPP_REMOTENAME
+  unsigned int  explicit_remote     :1;      /* remote_name specified with remotename opt */
+#endif /* PPP_REMOTENAME */
+#if PAP_SUPPORT
+  unsigned int  refuse_pap          :1;      /* Don't proceed auth. with PAP */
+#endif /* PAP_SUPPORT */
+#if CHAP_SUPPORT
+  unsigned int  refuse_chap         :1;      /* Don't proceed auth. with CHAP */
+#endif /* CHAP_SUPPORT */
+#if MSCHAP_SUPPORT
+  unsigned int  refuse_mschap       :1;      /* Don't proceed auth. with MS-CHAP */
+  unsigned int  refuse_mschap_v2    :1;      /* Don't proceed auth. with MS-CHAPv2 */
+#endif /* MSCHAP_SUPPORT */
+#if EAP_SUPPORT
+  unsigned int  refuse_eap          :1;      /* Don't proceed auth. with EAP */
+#endif /* EAP_SUPPORT */
+#if LWIP_DNS
+  unsigned int  usepeerdns          :1;      /* Ask peer for DNS adds */
+#endif /* LWIP_DNS */
+  unsigned int  persist             :1;      /* Persist mode, always try to open the connection */
+#if PRINTPKT_SUPPORT
+  unsigned int  hide_password       :1;      /* Hide password in dumped packets */
+#endif /* PRINTPKT_SUPPORT */
+  unsigned int  noremoteip          :1;      /* Let him have no IP address */
+  unsigned int  lax_recv            :1;      /* accept control chars in asyncmap */
+  unsigned int  noendpoint          :1;      /* don't send/accept endpoint discriminator */
+#if PPP_LCP_ADAPTIVE
+  unsigned int lcp_echo_adaptive    :1;      /* request echo only if the link was idle */
+#endif /* PPP_LCP_ADAPTIVE */
+#if MPPE_SUPPORT
+  unsigned int require_mppe         :1;      /* Require MPPE (Microsoft Point to Point Encryption) */
+  unsigned int refuse_mppe_40       :1;      /* Allow MPPE 40-bit mode? */
+  unsigned int refuse_mppe_128      :1;      /* Allow MPPE 128-bit mode? */
+  unsigned int refuse_mppe_stateful :1;      /* Allow MPPE stateful mode? */
+#endif /* MPPE_SUPPORT */
+
+  u16_t  listen_time;                 /* time to listen first (ms), waiting for peer to send LCP packet */
+
+#if PPP_IDLETIMELIMIT
+  u16_t  idle_time_limit;             /* Disconnect if idle for this many seconds */
+#endif /* PPP_IDLETIMELIMIT */
+#if PPP_MAXCONNECT
+  u32_t  maxconnect;                  /* Maximum connect time (seconds) */
+#endif /* PPP_MAXCONNECT */
+
+#if PPP_AUTH_SUPPORT
+  /* auth data */
+  const char  *user;                   /* Username for PAP */
+  const char  *passwd;                 /* Password for PAP, secret for CHAP */
+#if PPP_REMOTENAME
+  char  remote_name[MAXNAMELEN   + 1]; /* Peer's name for authentication */
+#endif /* PPP_REMOTENAME */
+
+#if PAP_SUPPORT
+  u8_t  pap_timeout_time;        /* Timeout (seconds) for auth-req retrans. */
+  u8_t  pap_max_transmits;       /* Number of auth-reqs sent */
+#if PPP_SERVER
+  u8_t  pap_req_timeout;         /* Time to wait for auth-req from peer */
+#endif /* PPP_SERVER */
+#endif /* PAP_SUPPPORT */
+
+#if CHAP_SUPPORT
+#if PPP_SERVER
+  u8_t  chap_timeout_time;       /* Timeout (seconds) for retransmitting req */
+  u8_t  chap_max_transmits;      /* max # times to send challenge */
+  u8_t  chap_rechallenge_time;   /* Time to wait for auth-req from peer */
+#endif /* PPP_SERVER */
+#endif /* CHAP_SUPPPORT */
+
+#if EAP_SUPPORT
+  u8_t  eap_req_time;            /* Time to wait (for retransmit/fail) */
+  u8_t  eap_allow_req;           /* Max Requests allowed */
+#if PPP_SERVER
+  u8_t  eap_timeout_time;        /* Time to wait (for retransmit/fail) */
+  u8_t  eap_max_transmits;       /* Max Requests allowed */
+#endif /* PPP_SERVER */
+#endif /* EAP_SUPPORT */
+
+#endif /* PPP_AUTH_SUPPORT */
+
+  u8_t  fsm_timeout_time;            /* Timeout time in seconds */
+  u8_t  fsm_max_conf_req_transmits;  /* Maximum Configure-Request transmissions */
+  u8_t  fsm_max_term_transmits;      /* Maximum Terminate-Request transmissions */
+  u8_t  fsm_max_nak_loops;           /* Maximum number of nak loops tolerated */
+
+  u8_t  lcp_loopbackfail;     /* Number of times we receive our magic number from the peer
+                                 before deciding the link is looped-back. */
+  u8_t  lcp_echo_interval;    /* Interval between LCP echo-requests */
+  u8_t  lcp_echo_fails;       /* Tolerance to unanswered echo-requests */
+
+} ppp_settings;
+
+#if PPP_SERVER
+struct ppp_addrs {
+#if PPP_IPV4_SUPPORT
+  ip4_addr_t our_ipaddr, his_ipaddr, netmask;
+#if LWIP_DNS
+  ip4_addr_t dns1, dns2;
+#endif /* LWIP_DNS */
+#endif /* PPP_IPV4_SUPPORT */
+#if PPP_IPV6_SUPPORT
+  ip6_addr_t our6_ipaddr, his6_ipaddr;
+#endif /* PPP_IPV6_SUPPORT */
+};
+#endif /* PPP_SERVER */
+
+/*
+ * PPP interface control block.
+ */
+struct ppp_pcb_s {
+  ppp_settings settings;
+  const struct link_callbacks *link_cb;
+  void *link_ctx_cb;
+  void (*link_status_cb)(ppp_pcb *pcb, int err_code, void *ctx);  /* Status change callback */
+#if PPP_NOTIFY_PHASE
+  void (*notify_phase_cb)(ppp_pcb *pcb, u8_t phase, void *ctx);   /* Notify phase callback */
+#endif /* PPP_NOTIFY_PHASE */
+  void *ctx_cb;                  /* Callbacks optional pointer */
+  struct netif *netif;           /* PPP interface */
+  u8_t phase;                    /* where the link is at */
+  u8_t err_code;                 /* Code indicating why interface is down. */
+
+  /* flags */
+#if PPP_IPV4_SUPPORT
+  unsigned int ask_for_local           :1; /* request our address from peer */
+  unsigned int ipcp_is_open            :1; /* haven't called np_finished() */
+  unsigned int ipcp_is_up              :1; /* have called ipcp_up() */
+  unsigned int if4_up                  :1; /* True when the IPv4 interface is up. */
+#if 0 /* UNUSED - PROXY ARP */
+  unsigned int proxy_arp_set           :1; /* Have created proxy arp entry */
+#endif /* UNUSED - PROXY ARP */
+#endif /* PPP_IPV4_SUPPORT */
+#if PPP_IPV6_SUPPORT
+  unsigned int ipv6cp_is_up            :1; /* have called ip6cp_up() */
+  unsigned int if6_up                  :1; /* True when the IPv6 interface is up. */
+#endif /* PPP_IPV6_SUPPORT */
+  unsigned int lcp_echo_timer_running  :1; /* set if a timer is running */
+#if VJ_SUPPORT
+  unsigned int vj_enabled              :1; /* Flag indicating VJ compression enabled. */
+#endif /* VJ_SUPPORT */
+#if CCP_SUPPORT
+  unsigned int ccp_all_rejected        :1; /* we rejected all peer's options */
+#endif /* CCP_SUPPORT */
+#if MPPE_SUPPORT
+  unsigned int mppe_keys_set           :1; /* Have the MPPE keys been set? */
+#endif /* MPPE_SUPPORT */
+
+#if PPP_AUTH_SUPPORT
+  /* auth data */
+#if PPP_SERVER && defined(HAVE_MULTILINK)
+  char peer_authname[MAXNAMELEN + 1]; /* The name by which the peer authenticated itself to us. */
+#endif /* PPP_SERVER && defined(HAVE_MULTILINK) */
+  u16_t auth_pending;        /* Records which authentication operations haven't completed yet. */
+  u16_t auth_done;           /* Records which authentication operations have been completed. */
+
+#if PAP_SUPPORT
+  upap_state upap;           /* PAP data */
+#endif /* PAP_SUPPORT */
+
+#if CHAP_SUPPORT
+  chap_client_state chap_client;  /* CHAP client data */
+#if PPP_SERVER
+  chap_server_state chap_server;  /* CHAP server data */
+#endif /* PPP_SERVER */
+#endif /* CHAP_SUPPORT */
+
+#if EAP_SUPPORT
+  eap_state eap;            /* EAP data */
+#endif /* EAP_SUPPORT */
+#endif /* PPP_AUTH_SUPPORT */
+
+  fsm lcp_fsm;                   /* LCP fsm structure */
+  lcp_options lcp_wantoptions;   /* Options that we want to request */
+  lcp_options lcp_gotoptions;    /* Options that peer ack'd */
+  lcp_options lcp_allowoptions;  /* Options we allow peer to request */
+  lcp_options lcp_hisoptions;    /* Options that we ack'd */
+  u16_t peer_mru;                /* currently negotiated peer MRU */
+  u8_t lcp_echos_pending;        /* Number of outstanding echo msgs */
+  u8_t lcp_echo_number;          /* ID number of next echo frame */
+
+  u8_t num_np_open;              /* Number of network protocols which we have opened. */
+  u8_t num_np_up;                /* Number of network protocols which have come up. */
+
+#if VJ_SUPPORT
+  struct vjcompress vj_comp;     /* Van Jacobson compression header. */
+#endif /* VJ_SUPPORT */
+
+#if CCP_SUPPORT
+  fsm ccp_fsm;                   /* CCP fsm structure */
+  ccp_options ccp_wantoptions;   /* what to request the peer to use */
+  ccp_options ccp_gotoptions;    /* what the peer agreed to do */
+  ccp_options ccp_allowoptions;  /* what we'll agree to do */
+  ccp_options ccp_hisoptions;    /* what we agreed to do */
+  u8_t ccp_localstate;           /* Local state (mainly for handling reset-reqs and reset-acks). */
+  u8_t ccp_receive_method;       /* Method chosen on receive path */
+  u8_t ccp_transmit_method;      /* Method chosen on transmit path */
+#if MPPE_SUPPORT
+  ppp_mppe_state mppe_comp;      /* MPPE "compressor" structure */
+  ppp_mppe_state mppe_decomp;    /* MPPE "decompressor" structure */
+#endif /* MPPE_SUPPORT */
+#endif /* CCP_SUPPORT */
+
+#if PPP_IPV4_SUPPORT
+  fsm ipcp_fsm;                   /* IPCP fsm structure */
+  ipcp_options ipcp_wantoptions;  /* Options that we want to request */
+  ipcp_options ipcp_gotoptions;   /* Options that peer ack'd */
+  ipcp_options ipcp_allowoptions; /* Options we allow peer to request */
+  ipcp_options ipcp_hisoptions;   /* Options that we ack'd */
+#endif /* PPP_IPV4_SUPPORT */
+
+#if PPP_IPV6_SUPPORT
+  fsm ipv6cp_fsm;                     /* IPV6CP fsm structure */
+  ipv6cp_options ipv6cp_wantoptions;  /* Options that we want to request */
+  ipv6cp_options ipv6cp_gotoptions;   /* Options that peer ack'd */
+  ipv6cp_options ipv6cp_allowoptions; /* Options we allow peer to request */
+  ipv6cp_options ipv6cp_hisoptions;   /* Options that we ack'd */
+#endif /* PPP_IPV6_SUPPORT */
+};
+
+/************************
+ *** PUBLIC FUNCTIONS ***
+ ************************/
+
+/*
+ * WARNING: For multi-threads environment, all ppp_set_* functions most
+ * only be called while the PPP is in the dead phase (i.e. disconnected).
+ */
+
+#if PPP_AUTH_SUPPORT
+/*
+ * Set PPP authentication.
+ *
+ * Warning: Using PPPAUTHTYPE_ANY might have security consequences.
+ * RFC 1994 says:
+ *
+ * In practice, within or associated with each PPP server, there is a
+ * database which associates "user" names with authentication
+ * information ("secrets").  It is not anticipated that a particular
+ * named user would be authenticated by multiple methods.  This would
+ * make the user vulnerable to attacks which negotiate the least secure
+ * method from among a set (such as PAP rather than CHAP).  If the same
+ * secret was used, PAP would reveal the secret to be used later with
+ * CHAP.
+ *
+ * Instead, for each user name there should be an indication of exactly
+ * one method used to authenticate that user name.  If a user needs to
+ * make use of different authentication methods under different
+ * circumstances, then distinct user names SHOULD be employed, each of
+ * which identifies exactly one authentication method.
+ *
+ * Default is none auth type, unset (NULL) user and passwd.
+ */
+#define PPPAUTHTYPE_NONE      0x00
+#define PPPAUTHTYPE_PAP       0x01
+#define PPPAUTHTYPE_CHAP      0x02
+#define PPPAUTHTYPE_MSCHAP    0x04
+#define PPPAUTHTYPE_MSCHAP_V2 0x08
+#define PPPAUTHTYPE_EAP       0x10
+#define PPPAUTHTYPE_ANY       0xff
+void ppp_set_auth(ppp_pcb *pcb, u8_t authtype, const char *user, const char *passwd);
+
+/*
+ * If set, peer is required to authenticate. This is mostly necessary for PPP server support.
+ *
+ * Default is false.
+ */
+#define ppp_set_auth_required(ppp, boolval) (ppp->settings.auth_required = boolval)
+#endif /* PPP_AUTH_SUPPORT */
+
+#if PPP_IPV4_SUPPORT
+/*
+ * Set PPP interface "our" and "his" IPv4 addresses. This is mostly necessary for PPP server
+ * support but it can also be used on a PPP link where each side choose its own IP address.
+ *
+ * Default is unset (0.0.0.0).
+ */
+#define ppp_set_ipcp_ouraddr(ppp, addr) do { ppp->ipcp_wantoptions.ouraddr = ip4_addr_get_u32(addr); \
+                                             ppp->ask_for_local = ppp->ipcp_wantoptions.ouraddr != 0; } while(0)
+#define ppp_set_ipcp_hisaddr(ppp, addr) (ppp->ipcp_wantoptions.hisaddr = ip4_addr_get_u32(addr))
+#if LWIP_DNS
+/*
+ * Set DNS server addresses that are sent if the peer asks for them. This is mostly necessary
+ * for PPP server support.
+ *
+ * Default is unset (0.0.0.0).
+ */
+#define ppp_set_ipcp_dnsaddr(ppp, index, addr) (ppp->ipcp_allowoptions.dnsaddr[index] = ip4_addr_get_u32(addr))
+
+/*
+ * If set, we ask the peer for up to 2 DNS server addresses. Received DNS server addresses are
+ * registered using the dns_setserver() function.
+ *
+ * Default is false.
+ */
+#define ppp_set_usepeerdns(ppp, boolval) (ppp->settings.usepeerdns = boolval)
+#endif /* LWIP_DNS */
+#endif /* PPP_IPV4_SUPPORT */
+
+#if MPPE_SUPPORT
+/* Disable MPPE (Microsoft Point to Point Encryption). This parameter is exclusive. */
+#define PPP_MPPE_DISABLE           0x00
+/* Require the use of MPPE (Microsoft Point to Point Encryption). */
+#define PPP_MPPE_ENABLE            0x01
+/* Allow MPPE to use stateful mode. Stateless mode is still attempted first. */
+#define PPP_MPPE_ALLOW_STATEFUL    0x02
+/* Refuse the use of MPPE with 40-bit encryption. Conflict with PPP_MPPE_REFUSE_128. */
+#define PPP_MPPE_REFUSE_40         0x04
+/* Refuse the use of MPPE with 128-bit encryption. Conflict with PPP_MPPE_REFUSE_40. */
+#define PPP_MPPE_REFUSE_128        0x08
+/*
+ * Set MPPE configuration
+ *
+ * Default is disabled.
+ */
+void ppp_set_mppe(ppp_pcb *pcb, u8_t flags);
+#endif /* MPPE_SUPPORT */
+
+/*
+ * Wait for up to intval milliseconds for a valid PPP packet from the peer.
+ * At the end of this  time, or when a valid PPP packet is received from the
+ * peer, we commence negotiation by sending our first LCP packet.
+ *
+ * Default is 0.
+ */
+#define ppp_set_listen_time(ppp, intval) (ppp->settings.listen_time = intval)
+
+/*
+ * If set, we will attempt to initiate a connection but if no reply is received from
+ * the peer, we will then just wait passively for a valid LCP packet from the peer.
+ *
+ * Default is false.
+ */
+#define ppp_set_passive(ppp, boolval) (ppp->lcp_wantoptions.passive = boolval)
+
+/*
+ * If set, we will not transmit LCP packets to initiate a connection until a valid
+ * LCP packet is received from the peer. This is what we usually call the server mode.
+ *
+ * Default is false.
+ */
+#define ppp_set_silent(ppp, boolval) (ppp->lcp_wantoptions.silent = boolval)
+
+/*
+ * If set, enable protocol field compression negotiation in both the receive and
+ * the transmit direction.
+ *
+ * Default is true.
+ */
+#define ppp_set_neg_pcomp(ppp, boolval) (ppp->lcp_wantoptions.neg_pcompression = \
+                                         ppp->lcp_allowoptions.neg_pcompression = boolval)
+
+/*
+ * If set, enable Address/Control compression in both the receive and the transmit
+ * direction.
+ *
+ * Default is true.
+ */
+#define ppp_set_neg_accomp(ppp, boolval) (ppp->lcp_wantoptions.neg_accompression = \
+                                          ppp->lcp_allowoptions.neg_accompression = boolval)
+
+/*
+ * If set, enable asyncmap negotiation. Otherwise forcing all control characters to
+ * be escaped for both the transmit and the receive direction.
+ *
+ * Default is true.
+ */
+#define ppp_set_neg_asyncmap(ppp, boolval) (ppp->lcp_wantoptions.neg_asyncmap = \
+                                            ppp->lcp_allowoptions.neg_asyncmap = boolval)
+
+/*
+ * This option sets the Async-Control-Character-Map (ACCM) for this end of the link.
+ * The ACCM is a set of 32 bits, one for each of the ASCII control characters with
+ * values from 0 to 31, where a 1 bit  indicates that the corresponding control
+ * character should not be used in PPP packets sent to this system. The map is
+ * an unsigned 32 bits integer where the least significant bit (00000001) represents
+ * character 0 and the most significant bit (80000000) represents character 31.
+ * We will then ask the peer to send these characters as a 2-byte escape sequence.
+ *
+ * Default is 0.
+ */
+#define ppp_set_asyncmap(ppp, intval) (ppp->lcp_wantoptions.asyncmap = intval)
+
+/*
+ * Set a PPP interface as the default network interface
+ * (used to output all packets for which no specific route is found).
+ */
+#define ppp_set_default(ppp)         netif_set_default(ppp->netif)
+
+#if PPP_NOTIFY_PHASE
+/*
+ * Set a PPP notify phase callback.
+ *
+ * This can be used for example to set a LED pattern depending on the
+ * current phase of the PPP session.
+ */
+typedef void (*ppp_notify_phase_cb_fn)(ppp_pcb *pcb, u8_t phase, void *ctx);
+void ppp_set_notify_phase_callback(ppp_pcb *pcb, ppp_notify_phase_cb_fn notify_phase_cb);
+#endif /* PPP_NOTIFY_PHASE */
+
+/*
+ * Initiate a PPP connection.
+ *
+ * This can only be called if PPP is in the dead phase.
+ *
+ * Holdoff is the time to wait (in seconds) before initiating
+ * the connection.
+ *
+ * If this port connects to a modem, the modem connection must be
+ * established before calling this.
+ */
+err_t ppp_connect(ppp_pcb *pcb, u16_t holdoff);
+
+#if PPP_SERVER
+/*
+ * Listen for an incoming PPP connection.
+ *
+ * This can only be called if PPP is in the dead phase.
+ *
+ * If this port connects to a modem, the modem connection must be
+ * established before calling this.
+ */
+err_t ppp_listen(ppp_pcb *pcb);
+#endif /* PPP_SERVER */
+
+/*
+ * Initiate the end of a PPP connection.
+ * Any outstanding packets in the queues are dropped.
+ *
+ * Setting nocarrier to 1 close the PPP connection without initiating the
+ * shutdown procedure. Always using nocarrier = 0 is still recommended,
+ * this is going to take a little longer time if your link is down, but
+ * is a safer choice for the PPP state machine.
+ *
+ * Return 0 on success, an error code on failure.
+ */
+err_t ppp_close(ppp_pcb *pcb, u8_t nocarrier);
+
+/*
+ * Release the control block.
+ *
+ * This can only be called if PPP is in the dead phase.
+ *
+ * You must use ppp_close() before if you wish to terminate
+ * an established PPP session.
+ *
+ * Return 0 on success, an error code on failure.
+ */
+err_t ppp_free(ppp_pcb *pcb);
+
+/*
+ * PPP IOCTL commands.
+ *
+ * Get the up status - 0 for down, non-zero for up.  The argument must
+ * point to an int.
+ */
+#define PPPCTLG_UPSTATUS 0
+
+/*
+ * Get the PPP error code.  The argument must point to an int.
+ * Returns a PPPERR_* value.
+ */
+#define PPPCTLG_ERRCODE  1
+
+/*
+ * Get the fd associated with a PPP over serial
+ */
+#define PPPCTLG_FD       2
+
+/*
+ * Get and set parameters for the given connection.
+ * Return 0 on success, an error code on failure.
+ */
+err_t ppp_ioctl(ppp_pcb *pcb, u8_t cmd, void *arg);
+
+/* Get the PPP netif interface */
+#define ppp_netif(ppp)               (ppp->netif)
+
+/* Set an lwIP-style status-callback for the selected PPP device */
+#define ppp_set_netif_statuscallback(ppp, status_cb)       \
+        netif_set_status_callback(ppp->netif, status_cb);
+
+/* Set an lwIP-style link-callback for the selected PPP device */
+#define ppp_set_netif_linkcallback(ppp, link_cb)           \
+        netif_set_link_callback(ppp->netif, link_cb);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* PPP_H */
+
+#endif /* PPP_SUPPORT */
diff --git a/lib/lwip/include/netif/ppp/ppp_impl.h b/lib/lwip/include/netif/ppp/ppp_impl.h
new file mode 100644
index 0000000000..22828311a7
--- /dev/null
+++ b/lib/lwip/include/netif/ppp/ppp_impl.h
@@ -0,0 +1,736 @@
+/*****************************************************************************
+* ppp.h - Network Point to Point Protocol header file.
+*
+* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
+* portions Copyright (c) 1997 Global Election Systems Inc.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 03-01-01 Marc Boucher <marc@mbsi.ca>
+*   Ported to lwIP.
+* 97-11-05 Guy Lancaster <glanca@gesn.com>, Global Election Systems Inc.
+*   Original derived from BSD codes.
+*****************************************************************************/
+#ifndef LWIP_HDR_PPP_IMPL_H
+#define LWIP_HDR_PPP_IMPL_H
+
+#include "netif/ppp/ppp_opts.h"
+
+#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#ifdef PPP_INCLUDE_SETTINGS_HEADER
+#include "ppp_settings.h"
+#endif
+
+#include <stdio.h> /* formats */
+#include <stdarg.h>
+#include <string.h>
+#include <stdlib.h> /* strtol() */
+
+#include "lwip/netif.h"
+#include "lwip/def.h"
+#include "lwip/timeouts.h"
+
+#include "ppp.h"
+#include "pppdebug.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Memory used for control packets.
+ *
+ * PPP_CTRL_PBUF_UNKNOWN_SIZE is the amount of memory we allocate when we
+ * cannot figure out how much we are going to use before filling the buffer.
+ */
+#define PPP_CTRL_PBUF_UNKNOWN_SIZE   512
+
+/*
+ * The basic PPP frame.
+ */
+#define PPP_ADDRESS(p)	(((u_char *)(p))[0])
+#define PPP_CONTROL(p)	(((u_char *)(p))[1])
+#define PPP_PROTOCOL(p)	((((u_char *)(p))[2] << 8) + ((u_char *)(p))[3])
+
+/*
+ * Significant octet values.
+ */
+#define	PPP_ALLSTATIONS	0xff	/* All-Stations broadcast address */
+#define	PPP_UI		0x03	/* Unnumbered Information */
+#define	PPP_FLAG	0x7e	/* Flag Sequence */
+#define	PPP_ESCAPE	0x7d	/* Asynchronous Control Escape */
+#define	PPP_TRANS	0x20	/* Asynchronous transparency modifier */
+
+/*
+ * PPP_DEFMRU: MRU value used prior negotiation and unless negotiated later.
+ * Must be 1500.
+ */
+#define PPP_DEFMRU      1500
+
+/*
+ * Protocol field values.
+ */
+#if PPP_IPV4_SUPPORT
+#define PPP_IP		0x21	/* Internet Protocol */
+#endif /* PPP_IPV4_SUPPORT */
+#if 0 /* UNUSED */
+#define PPP_AT		0x29	/* AppleTalk Protocol */
+#define PPP_IPX		0x2b	/* IPX protocol */
+#endif /* UNUSED */
+#if VJ_SUPPORT
+#define	PPP_VJC_COMP	0x2d	/* VJ compressed TCP */
+#define	PPP_VJC_UNCOMP	0x2f	/* VJ uncompressed TCP */
+#endif /* VJ_SUPPORT */
+#if PPP_IPV6_SUPPORT
+#define PPP_IPV6	0x57	/* Internet Protocol Version 6 */
+#endif /* PPP_IPV6_SUPPORT */
+#if CCP_SUPPORT
+#define PPP_COMP	0xfd	/* compressed packet */
+#endif /* CCP_SUPPORT */
+#define PPP_IPCP	0x8021	/* IP Control Protocol */
+#if 0 /* UNUSED */
+#define PPP_ATCP	0x8029	/* AppleTalk Control Protocol */
+#define PPP_IPXCP	0x802b	/* IPX Control Protocol */
+#endif /* UNUSED */
+#if PPP_IPV6_SUPPORT
+#define PPP_IPV6CP	0x8057	/* IPv6 Control Protocol */
+#endif /* PPP_IPV6_SUPPORT */
+#if CCP_SUPPORT
+#define PPP_CCP		0x80fd	/* Compression Control Protocol */
+#endif /* CCP_SUPPORT */
+#if ECP_SUPPORT
+#define PPP_ECP		0x8053	/* Encryption Control Protocol */
+#endif /* ECP_SUPPORT */
+#define PPP_LCP		0xc021	/* Link Control Protocol */
+#if PAP_SUPPORT
+#define PPP_PAP		0xc023	/* Password Authentication Protocol */
+#endif /* PAP_SUPPORT */
+#if LQR_SUPPORT
+#define PPP_LQR		0xc025	/* Link Quality Report protocol */
+#endif /* LQR_SUPPORT */
+#if CHAP_SUPPORT
+#define PPP_CHAP	0xc223	/* Cryptographic Handshake Auth. Protocol */
+#endif /* CHAP_SUPPORT */
+#if CBCP_SUPPORT
+#define PPP_CBCP	0xc029	/* Callback Control Protocol */
+#endif /* CBCP_SUPPORT */
+#if EAP_SUPPORT
+#define PPP_EAP		0xc227	/* Extensible Authentication Protocol */
+#endif /* EAP_SUPPORT */
+
+/*
+ * The following struct gives the addresses of procedures to call
+ * for a particular lower link level protocol.
+ */
+struct link_callbacks {
+  /* Start a connection (e.g. Initiate discovery phase) */
+  void (*connect) (ppp_pcb *pcb, void *ctx);
+#if PPP_SERVER
+  /* Listen for an incoming connection (Passive mode) */
+  void (*listen) (ppp_pcb *pcb, void *ctx);
+#endif /* PPP_SERVER */
+  /* End a connection (i.e. initiate disconnect phase) */
+  void (*disconnect) (ppp_pcb *pcb, void *ctx);
+  /* Free lower protocol control block */
+  err_t (*free) (ppp_pcb *pcb, void *ctx);
+  /* Write a pbuf to a ppp link, only used from PPP functions to send PPP packets. */
+  err_t (*write)(ppp_pcb *pcb, void *ctx, struct pbuf *p);
+  /* Send a packet from lwIP core (IPv4 or IPv6) */
+  err_t (*netif_output)(ppp_pcb *pcb, void *ctx, struct pbuf *p, u_short protocol);
+  /* configure the transmit-side characteristics of the PPP interface */
+  void (*send_config)(ppp_pcb *pcb, void *ctx, u32_t accm, int pcomp, int accomp);
+  /* confire the receive-side characteristics of the PPP interface */
+  void (*recv_config)(ppp_pcb *pcb, void *ctx, u32_t accm, int pcomp, int accomp);
+};
+
+/*
+ * What to do with network protocol (NP) packets.
+ */
+enum NPmode {
+    NPMODE_PASS,		/* pass the packet through */
+    NPMODE_DROP,		/* silently drop the packet */
+    NPMODE_ERROR,		/* return an error */
+    NPMODE_QUEUE		/* save it up for later. */
+};
+
+/*
+ * Statistics.
+ */
+#if PPP_STATS_SUPPORT
+struct pppstat	{
+    unsigned int ppp_ibytes;	/* bytes received */
+    unsigned int ppp_ipackets;	/* packets received */
+    unsigned int ppp_ierrors;	/* receive errors */
+    unsigned int ppp_obytes;	/* bytes sent */
+    unsigned int ppp_opackets;	/* packets sent */
+    unsigned int ppp_oerrors;	/* transmit errors */
+};
+
+#if VJ_SUPPORT
+struct vjstat {
+    unsigned int vjs_packets;	/* outbound packets */
+    unsigned int vjs_compressed; /* outbound compressed packets */
+    unsigned int vjs_searches;	/* searches for connection state */
+    unsigned int vjs_misses;	/* times couldn't find conn. state */
+    unsigned int vjs_uncompressedin; /* inbound uncompressed packets */
+    unsigned int vjs_compressedin; /* inbound compressed packets */
+    unsigned int vjs_errorin;	/* inbound unknown type packets */
+    unsigned int vjs_tossed;	/* inbound packets tossed because of error */
+};
+#endif /* VJ_SUPPORT */
+
+struct ppp_stats {
+    struct pppstat p;		/* basic PPP statistics */
+#if VJ_SUPPORT
+    struct vjstat vj;		/* VJ header compression statistics */
+#endif /* VJ_SUPPORT */
+};
+
+#if CCP_SUPPORT
+struct compstat {
+    unsigned int unc_bytes;	/* total uncompressed bytes */
+    unsigned int unc_packets;	/* total uncompressed packets */
+    unsigned int comp_bytes;	/* compressed bytes */
+    unsigned int comp_packets;	/* compressed packets */
+    unsigned int inc_bytes;	/* incompressible bytes */
+    unsigned int inc_packets;	/* incompressible packets */
+    unsigned int ratio;		/* recent compression ratio << 8 */
+};
+
+struct ppp_comp_stats {
+    struct compstat c;		/* packet compression statistics */
+    struct compstat d;		/* packet decompression statistics */
+};
+#endif /* CCP_SUPPORT */
+
+#endif /* PPP_STATS_SUPPORT */
+
+#if PPP_IDLETIMELIMIT
+/*
+ * The following structure records the time in seconds since
+ * the last NP packet was sent or received.
+ */
+struct ppp_idle {
+    time_t xmit_idle;		/* time since last NP packet sent */
+    time_t recv_idle;		/* time since last NP packet received */
+};
+#endif /* PPP_IDLETIMELIMIT */
+
+/* values for epdisc.class */
+#define EPD_NULL	0	/* null discriminator, no data */
+#define EPD_LOCAL	1
+#define EPD_IP		2
+#define EPD_MAC		3
+#define EPD_MAGIC	4
+#define EPD_PHONENUM	5
+
+/*
+ * Global variables.
+ */
+#ifdef HAVE_MULTILINK
+extern u8_t	multilink;	/* enable multilink operation */
+extern u8_t	doing_multilink;
+extern u8_t	multilink_master;
+extern u8_t	bundle_eof;
+extern u8_t	bundle_terminating;
+#endif
+
+#ifdef MAXOCTETS
+extern unsigned int maxoctets;	     /* Maximum octetes per session (in bytes) */
+extern int       maxoctets_dir;      /* Direction :
+				      0 - in+out (default)
+				      1 - in
+				      2 - out
+				      3 - max(in,out) */
+extern int       maxoctets_timeout;  /* Timeout for check of octets limit */
+#define PPP_OCTETS_DIRECTION_SUM        0
+#define PPP_OCTETS_DIRECTION_IN         1
+#define PPP_OCTETS_DIRECTION_OUT        2
+#define PPP_OCTETS_DIRECTION_MAXOVERAL  3
+/* same as previous, but little different on RADIUS side */
+#define PPP_OCTETS_DIRECTION_MAXSESSION 4
+#endif
+
+/* Data input may be used by CCP and ECP, remove this entry
+ * from struct protent to save some flash
+ */
+#define PPP_DATAINPUT 0
+
+/*
+ * The following struct gives the addresses of procedures to call
+ * for a particular protocol.
+ */
+struct protent {
+    u_short protocol;		/* PPP protocol number */
+    /* Initialization procedure */
+    void (*init) (ppp_pcb *pcb);
+    /* Process a received packet */
+    void (*input) (ppp_pcb *pcb, u_char *pkt, int len);
+    /* Process a received protocol-reject */
+    void (*protrej) (ppp_pcb *pcb);
+    /* Lower layer has come up */
+    void (*lowerup) (ppp_pcb *pcb);
+    /* Lower layer has gone down */
+    void (*lowerdown) (ppp_pcb *pcb);
+    /* Open the protocol */
+    void (*open) (ppp_pcb *pcb);
+    /* Close the protocol */
+    void (*close) (ppp_pcb *pcb, const char *reason);
+#if PRINTPKT_SUPPORT
+    /* Print a packet in readable form */
+    int  (*printpkt) (const u_char *pkt, int len,
+			  void (*printer) (void *, const char *, ...),
+			  void *arg);
+#endif /* PRINTPKT_SUPPORT */
+#if PPP_DATAINPUT
+    /* Process a received data packet */
+    void (*datainput) (ppp_pcb *pcb, u_char *pkt, int len);
+#endif /* PPP_DATAINPUT */
+#if PRINTPKT_SUPPORT
+    const char *name;		/* Text name of protocol */
+    const char *data_name;	/* Text name of corresponding data protocol */
+#endif /* PRINTPKT_SUPPORT */
+#if PPP_OPTIONS
+    option_t *options;		/* List of command-line options */
+    /* Check requested options, assign defaults */
+    void (*check_options) (void);
+#endif /* PPP_OPTIONS */
+#if DEMAND_SUPPORT
+    /* Configure interface for demand-dial */
+    int  (*demand_conf) (int unit);
+    /* Say whether to bring up link for this pkt */
+    int  (*active_pkt) (u_char *pkt, int len);
+#endif /* DEMAND_SUPPORT */
+};
+
+/* Table of pointers to supported protocols */
+extern const struct protent* const protocols[];
+
+
+/* Values for auth_pending, auth_done */
+#if PAP_SUPPORT
+#define PAP_WITHPEER	0x1
+#define PAP_PEER	0x2
+#endif /* PAP_SUPPORT */
+#if CHAP_SUPPORT
+#define CHAP_WITHPEER	0x4
+#define CHAP_PEER	0x8
+#endif /* CHAP_SUPPORT */
+#if EAP_SUPPORT
+#define EAP_WITHPEER	0x10
+#define EAP_PEER	0x20
+#endif /* EAP_SUPPORT */
+
+/* Values for auth_done only */
+#if CHAP_SUPPORT
+#define CHAP_MD5_WITHPEER	0x40
+#define CHAP_MD5_PEER		0x80
+#if MSCHAP_SUPPORT
+#define CHAP_MS_SHIFT		8	/* LSB position for MS auths */
+#define CHAP_MS_WITHPEER	0x100
+#define CHAP_MS_PEER		0x200
+#define CHAP_MS2_WITHPEER	0x400
+#define CHAP_MS2_PEER		0x800
+#endif /* MSCHAP_SUPPORT */
+#endif /* CHAP_SUPPORT */
+
+/* Supported CHAP protocols */
+#if CHAP_SUPPORT
+
+#if MSCHAP_SUPPORT
+#define CHAP_MDTYPE_SUPPORTED (MDTYPE_MICROSOFT_V2 | MDTYPE_MICROSOFT | MDTYPE_MD5)
+#else /* MSCHAP_SUPPORT */
+#define CHAP_MDTYPE_SUPPORTED (MDTYPE_MD5)
+#endif /* MSCHAP_SUPPORT */
+
+#else /* CHAP_SUPPORT */
+#define CHAP_MDTYPE_SUPPORTED (MDTYPE_NONE)
+#endif /* CHAP_SUPPORT */
+
+#if PPP_STATS_SUPPORT
+/*
+ * PPP statistics structure
+ */
+struct pppd_stats {
+    unsigned int	bytes_in;
+    unsigned int	bytes_out;
+    unsigned int	pkts_in;
+    unsigned int	pkts_out;
+};
+#endif /* PPP_STATS_SUPPORT */
+
+
+/*
+ * PPP private functions
+ */
+
+ 
+/*
+ * Functions called from lwIP core.
+ */
+
+/* initialize the PPP subsystem */
+int ppp_init(void);
+
+/*
+ * Functions called from PPP link protocols.
+ */
+
+/* Create a new PPP control block */
+ppp_pcb *ppp_new(struct netif *pppif, const struct link_callbacks *callbacks, void *link_ctx_cb,
+                 ppp_link_status_cb_fn link_status_cb, void *ctx_cb);
+
+/* Initiate LCP open request */
+void ppp_start(ppp_pcb *pcb);
+
+/* Called when link failed to setup */
+void ppp_link_failed(ppp_pcb *pcb);
+
+/* Called when link is normally down (i.e. it was asked to end) */
+void ppp_link_end(ppp_pcb *pcb);
+
+/* function called to process input packet */
+void ppp_input(ppp_pcb *pcb, struct pbuf *pb);
+
+
+/*
+ * Functions called by PPP protocols.
+ */
+
+/* function called by all PPP subsystems to send packets */
+err_t ppp_write(ppp_pcb *pcb, struct pbuf *p);
+
+/* functions called by auth.c link_terminated() */
+void ppp_link_terminated(ppp_pcb *pcb);
+
+void new_phase(ppp_pcb *pcb, int p);
+
+int ppp_send_config(ppp_pcb *pcb, int mtu, u32_t accm, int pcomp, int accomp);
+int ppp_recv_config(ppp_pcb *pcb, int mru, u32_t accm, int pcomp, int accomp);
+
+#if PPP_IPV4_SUPPORT
+int sifaddr(ppp_pcb *pcb, u32_t our_adr, u32_t his_adr, u32_t netmask);
+int cifaddr(ppp_pcb *pcb, u32_t our_adr, u32_t his_adr);
+#if 0 /* UNUSED - PROXY ARP */
+int sifproxyarp(ppp_pcb *pcb, u32_t his_adr);
+int cifproxyarp(ppp_pcb *pcb, u32_t his_adr);
+#endif /* UNUSED - PROXY ARP */
+#if LWIP_DNS
+int sdns(ppp_pcb *pcb, u32_t ns1, u32_t ns2);
+int cdns(ppp_pcb *pcb, u32_t ns1, u32_t ns2);
+#endif /* LWIP_DNS */
+#if VJ_SUPPORT
+int sifvjcomp(ppp_pcb *pcb, int vjcomp, int cidcomp, int maxcid);
+#endif /* VJ_SUPPORT */
+int sifup(ppp_pcb *pcb);
+int sifdown (ppp_pcb *pcb);
+u32_t get_mask(u32_t addr);
+#endif /* PPP_IPV4_SUPPORT */
+
+#if PPP_IPV6_SUPPORT
+int sif6addr(ppp_pcb *pcb, eui64_t our_eui64, eui64_t his_eui64);
+int cif6addr(ppp_pcb *pcb, eui64_t our_eui64, eui64_t his_eui64);
+int sif6up(ppp_pcb *pcb);
+int sif6down (ppp_pcb *pcb);
+#endif /* PPP_IPV6_SUPPORT */
+
+#if DEMAND_SUPPORT
+int sifnpmode(ppp_pcb *pcb, int proto, enum NPmode mode);
+#endif /* DEMAND_SUPPORt */
+
+void ppp_netif_set_mtu(ppp_pcb *pcb, int mtu);
+int ppp_netif_get_mtu(ppp_pcb *pcb);
+
+#if CCP_SUPPORT
+#if 0 /* unused */
+int ccp_test(ppp_pcb *pcb, u_char *opt_ptr, int opt_len, int for_transmit);
+#endif /* unused */
+void ccp_set(ppp_pcb *pcb, u8_t isopen, u8_t isup, u8_t receive_method, u8_t transmit_method);
+void ccp_reset_comp(ppp_pcb *pcb);
+void ccp_reset_decomp(ppp_pcb *pcb);
+#if 0 /* unused */
+int ccp_fatal_error(ppp_pcb *pcb);
+#endif /* unused */
+#endif /* CCP_SUPPORT */
+
+#if PPP_IDLETIMELIMIT
+int get_idle_time(ppp_pcb *pcb, struct ppp_idle *ip);
+#endif /* PPP_IDLETIMELIMIT */
+
+#if DEMAND_SUPPORT
+int get_loop_output(void);
+#endif /* DEMAND_SUPPORT */
+
+/* Optional protocol names list, to make our messages a little more informative. */
+#if PPP_PROTOCOLNAME
+const char * protocol_name(int proto);
+#endif /* PPP_PROTOCOLNAME  */
+
+/* Optional stats support, to get some statistics on the PPP interface */
+#if PPP_STATS_SUPPORT
+void print_link_stats(void); /* Print stats, if available */
+void reset_link_stats(int u); /* Reset (init) stats when link goes up */
+void update_link_stats(int u); /* Get stats at link termination */
+#endif /* PPP_STATS_SUPPORT */
+
+
+
+/*
+ * Inline versions of get/put char/short/long.
+ * Pointer is advanced; we assume that both arguments
+ * are lvalues and will already be in registers.
+ * cp MUST be u_char *.
+ */
+#define GETCHAR(c, cp) { \
+	(c) = *(cp)++; \
+}
+#define PUTCHAR(c, cp) { \
+	*(cp)++ = (u_char) (c); \
+}
+#define GETSHORT(s, cp) { \
+	(s) = *(cp)++ << 8; \
+	(s) |= *(cp)++; \
+}
+#define PUTSHORT(s, cp) { \
+	*(cp)++ = (u_char) ((s) >> 8); \
+	*(cp)++ = (u_char) (s); \
+}
+#define GETLONG(l, cp) { \
+	(l) = *(cp)++ << 8; \
+	(l) |= *(cp)++; (l) <<= 8; \
+	(l) |= *(cp)++; (l) <<= 8; \
+	(l) |= *(cp)++; \
+}
+#define PUTLONG(l, cp) { \
+	*(cp)++ = (u_char) ((l) >> 24); \
+	*(cp)++ = (u_char) ((l) >> 16); \
+	*(cp)++ = (u_char) ((l) >> 8); \
+	*(cp)++ = (u_char) (l); \
+}
+
+#define INCPTR(n, cp)	((cp) += (n))
+#define DECPTR(n, cp)	((cp) -= (n))
+
+/*
+ * System dependent definitions for user-level 4.3BSD UNIX implementation.
+ */
+#define TIMEOUT(f, a, t)        do { sys_untimeout((f), (a)); sys_timeout((t)*1000, (f), (a)); } while(0)
+#define TIMEOUTMS(f, a, t)      do { sys_untimeout((f), (a)); sys_timeout((t), (f), (a)); } while(0)
+#define UNTIMEOUT(f, a)         sys_untimeout((f), (a))
+
+#define BZERO(s, n)		memset(s, 0, n)
+#define	BCMP(s1, s2, l)		memcmp(s1, s2, l)
+
+#define PRINTMSG(m, l)		{ ppp_info(("Remote message: %0.*v", l, m)); }
+
+/*
+ * MAKEHEADER - Add Header fields to a packet.
+ */
+#define MAKEHEADER(p, t) { \
+    PUTCHAR(PPP_ALLSTATIONS, p); \
+    PUTCHAR(PPP_UI, p); \
+    PUTSHORT(t, p); }
+
+/* Procedures exported from auth.c */
+void link_required(ppp_pcb *pcb);     /* we are starting to use the link */
+void link_terminated(ppp_pcb *pcb);   /* we are finished with the link */
+void link_down(ppp_pcb *pcb);	      /* the LCP layer has left the Opened state */
+void upper_layers_down(ppp_pcb *pcb); /* take all NCPs down */
+void link_established(ppp_pcb *pcb);  /* the link is up; authenticate now */
+void start_networks(ppp_pcb *pcb);    /* start all the network control protos */
+void continue_networks(ppp_pcb *pcb); /* start network [ip, etc] control protos */
+#if PPP_AUTH_SUPPORT
+#if PPP_SERVER
+int auth_check_passwd(ppp_pcb *pcb, char *auser, unsigned int userlen, char *apasswd, unsigned int passwdlen, const char **msg, int *msglen);
+                                /* check the user name and passwd against configuration */
+void auth_peer_fail(ppp_pcb *pcb, int protocol);
+				/* peer failed to authenticate itself */
+void auth_peer_success(ppp_pcb *pcb, int protocol, int prot_flavor, const char *name, int namelen);
+				/* peer successfully authenticated itself */
+#endif /* PPP_SERVER */
+void auth_withpeer_fail(ppp_pcb *pcb, int protocol);
+				/* we failed to authenticate ourselves */
+void auth_withpeer_success(ppp_pcb *pcb, int protocol, int prot_flavor);
+				/* we successfully authenticated ourselves */
+#endif /* PPP_AUTH_SUPPORT */
+void np_up(ppp_pcb *pcb, int proto);    /* a network protocol has come up */
+void np_down(ppp_pcb *pcb, int proto);  /* a network protocol has gone down */
+void np_finished(ppp_pcb *pcb, int proto); /* a network protocol no longer needs link */
+#if PPP_AUTH_SUPPORT
+int get_secret(ppp_pcb *pcb, const char *client, const char *server, char *secret, int *secret_len, int am_server);
+				/* get "secret" for chap */
+#endif /* PPP_AUTH_SUPPORT */
+
+/* Procedures exported from ipcp.c */
+/* int parse_dotted_ip (char *, u32_t *); */
+
+/* Procedures exported from demand.c */
+#if DEMAND_SUPPORT
+void demand_conf (void);	/* config interface(s) for demand-dial */
+void demand_block (void);	/* set all NPs to queue up packets */
+void demand_unblock (void); /* set all NPs to pass packets */
+void demand_discard (void); /* set all NPs to discard packets */
+void demand_rexmit (int, u32_t); /* retransmit saved frames for an NP*/
+int  loop_chars (unsigned char *, int); /* process chars from loopback */
+int  loop_frame (unsigned char *, int); /* should we bring link up? */
+#endif /* DEMAND_SUPPORT */
+
+/* Procedures exported from multilink.c */
+#ifdef HAVE_MULTILINK
+void mp_check_options (void); /* Check multilink-related options */
+int  mp_join_bundle (void);  /* join our link to an appropriate bundle */
+void mp_exit_bundle (void);  /* have disconnected our link from bundle */
+void mp_bundle_terminated (void);
+char *epdisc_to_str (struct epdisc *); /* string from endpoint discrim. */
+int  str_to_epdisc (struct epdisc *, char *); /* endpt disc. from str */
+#else
+#define mp_bundle_terminated()	/* nothing */
+#define mp_exit_bundle()	/* nothing */
+#define doing_multilink		0
+#define multilink_master	0
+#endif
+
+/* Procedures exported from utils.c. */
+void ppp_print_string(const u_char *p, int len, void (*printer) (void *, const char *, ...), void *arg);   /* Format a string for output */
+int ppp_slprintf(char *buf, int buflen, const char *fmt, ...);            /* sprintf++ */
+int ppp_vslprintf(char *buf, int buflen, const char *fmt, va_list args);  /* vsprintf++ */
+size_t ppp_strlcpy(char *dest, const char *src, size_t len);        /* safe strcpy */
+size_t ppp_strlcat(char *dest, const char *src, size_t len);        /* safe strncpy */
+void ppp_dbglog_impl(const char *fmt, ...);    /* log a debug message */
+void ppp_info_impl(const char *fmt, ...);      /* log an informational message */
+void ppp_notice_impl(const char *fmt, ...);    /* log a notice-level message */
+void ppp_warn_impl(const char *fmt, ...);      /* log a warning message */
+void ppp_error_impl(const char *fmt, ...);     /* log an error message */
+void ppp_fatal_impl(const char *fmt, ...);     /* log an error message and die(1) */
+/* wrap all the above functions so they will only be linked when enabled */
+#define ppp_dbglog(x) do { if (LWIP_DEBUG_ENABLED(LOG_DEBUG)) { ppp_dbglog_impl x; }} while(0)
+#define ppp_info(x)   do { if (LWIP_DEBUG_ENABLED(LOG_INFO)) { ppp_info_impl x; }} while(0)
+#define ppp_notice(x) do { if (LWIP_DEBUG_ENABLED(LOG_NOTICE)) { ppp_notice_impl x; }} while(0)
+#define ppp_warn(x)   do { if (LWIP_DEBUG_ENABLED(LOG_WARNING)) { ppp_warn_impl x; }} while(0)
+#define ppp_error(x)  do { if (LWIP_DEBUG_ENABLED(LOG_ERR)) { ppp_error_impl x; }} while(0)
+#define ppp_fatal(x)  do { if (LWIP_DEBUG_ENABLED(LOG_CRITICAL)) { ppp_fatal_impl x; }} while(0)
+#if PRINTPKT_SUPPORT
+void ppp_dump_packet(ppp_pcb *pcb, const char *tag, unsigned char *p, int len);
+                                /* dump packet to debug log if interesting */
+#endif /* PRINTPKT_SUPPORT */
+
+/*
+ * Number of necessary timers analysis.
+ *
+ * PPP use at least one timer per each of its protocol, but not all protocols are
+ * active at the same time, thus the number of necessary timeouts is actually
+ * lower than enabled protocols. Here is the actual necessary timeouts based
+ * on code analysis.
+ *
+ * Note that many features analysed here are not working at all and are only
+ * there for a comprehensive analysis of necessary timers in order to prevent
+ * having to redo that each time we add a feature.
+ *
+ * Timer list
+ *
+ * | holdoff timeout
+ *  | low level protocol timeout (PPPoE or PPPoL2P)
+ *   | LCP delayed UP
+ *    | LCP retransmit (FSM)
+ *     | LCP Echo timer
+ *     .| PAP or CHAP or EAP authentication
+ *     . | ECP retransmit (FSM)
+ *     .  | CCP retransmit (FSM) when MPPE is enabled
+ *     .   | CCP retransmit (FSM) when MPPE is NOT enabled
+ *     .    | IPCP retransmit (FSM)
+ *     .    .| IP6CP retransmit (FSM)
+ *     .    . | Idle time limit
+ *     .    .  | Max connect time
+ *     .    .   | Max octets
+ *     .    .    | CCP RACK timeout
+ *     .    .    .
+ * PPP_PHASE_DEAD
+ * PPP_PHASE_HOLDOFF
+ * |   .    .    .
+ * PPP_PHASE_INITIALIZE
+ *  |  .    .    .
+ * PPP_PHASE_ESTABLISH
+ *   | .    .    .
+ *    |.    .    .
+ *     |    .    .
+ * PPP_PHASE_AUTHENTICATE
+ *     ||   .    .
+ * PPP_PHASE_NETWORK
+ *     |||| .    .
+ *     ||  |||   .
+ * PPP_PHASE_RUNNING
+ *     |    .|||||
+ *     |    . ||||
+ * PPP_PHASE_TERMINATE
+ *     |    . ||||
+ * PPP_PHASE_NETWORK
+ *    |.         .
+ * PPP_PHASE_ESTABLISH
+ * PPP_PHASE_DISCONNECT
+ * PPP_PHASE_DEAD
+ *
+ * Alright, PPP basic retransmission and LCP Echo consume one timer.
+ *  1
+ *
+ * If authentication is enabled one timer is necessary during authentication.
+ * This timer might still be running up to network phase for any necessary
+ * rechallenge, mostly for PPP server support.
+ *  1 + PPP_AUTH_SUPPORT
+ *
+ * If ECP is enabled one timer is necessary before IPCP and/or IP6CP, one more
+ * is necessary if CCP is enabled (only with MPPE support but we don't care much
+ * up to this detail level).
+ *  1 + PPP_AUTH_SUPPORT + ECP_SUPPORT + CCP_SUPPORT
+ *
+ * If CCP is enabled it might consume a timer during IPCP or IP6CP, thus
+ * we might use AUTH, IPCP, IP6CP and CCP timers simultaneously.
+ *  1 + PPP_AUTH_SUPPORT + PPP_IPV4_SUPPORT + PPP_IPV6_SUPPORT + CCP_SUPPORT
+ *
+ * When entering running phase, IPCP or IP6CP is still running. If idle time limit
+ * is enabled one more timer is necessary. Same for max connect time and max
+ * octets features. Furthermore CCP RACK might be used past this point.
+ *  1 + PPP_IPV4_SUPPORT + PPP_IPV6_SUPPORT -1 + PPP_IDLETIMELIMIT + PPP_MAXCONNECT + MAXOCTETS + CCP_SUPPORT
+ *
+ * Then the maximum number of simultaneously running timers is given by:
+ *  1 + MAX(PPP_AUTH_SUPPORT + PPP_IPV4_SUPPORT + PPP_IPV6_SUPPORT + CCP_SUPPORT,
+ *          PPP_IPV4_SUPPORT + PPP_IPV6_SUPPORT -1 + PPP_IDLETIMELIMIT + PPP_MAXCONNECT + MAXOCTETS + CCP_SUPPORT)
+ *
+ * We don't support ECP_SUPPORT + PPP_IDLETIMELIMIT + PPP_MAXCONNECT + MAXOCTETS features
+ * and adding those defines to ppp_opts.h just for having the value always defined to 0
+ * is not worth it, thus reducing the overall complexity.
+ *  1 + MAX(PPP_AUTH_SUPPORT + PPP_IPV4_SUPPORT + PPP_IPV6_SUPPORT + CCP_SUPPORT,
+ *          PPP_IPV4_SUPPORT + PPP_IPV6_SUPPORT -1 + CCP_SUPPORT)
+ *
+ * PPP_AUTH_SUPPORT is not available in ppp_opts.h because it is defined later in ppp.h,
+ * but we do not need to be that picky about the real number of simultaneously running
+ * timers so we just set the base number of timeouts to 2, thus the following is enough
+ * for now.
+ *  2 + PPP_IPV4_SUPPORT + PPP_IPV6_SUPPORT + CCP_SUPPORT
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* PPP_SUPPORT */
+#endif /* LWIP_HDR_PPP_IMPL_H */
diff --git a/lib/lwip/include/netif/ppp/ppp_opts.h b/lib/lwip/include/netif/ppp/ppp_opts.h
new file mode 100644
index 0000000000..be22c4ce25
--- /dev/null
+++ b/lib/lwip/include/netif/ppp/ppp_opts.h
@@ -0,0 +1,610 @@
+/*
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ */
+
+#ifndef LWIP_PPP_OPTS_H
+#define LWIP_PPP_OPTS_H
+
+#include "lwip/opt.h"
+
+/**
+ * PPP_SUPPORT==1: Enable PPP.
+ */
+#ifndef PPP_SUPPORT
+#define PPP_SUPPORT                     0
+#endif
+
+/**
+ * PPPOE_SUPPORT==1: Enable PPP Over Ethernet
+ */
+#ifndef PPPOE_SUPPORT
+#define PPPOE_SUPPORT                   0
+#endif
+
+/**
+ * PPPOE_SCNAME_SUPPORT==1: Enable PPP Over Ethernet Service Name and Concentrator Name support
+ */
+#ifndef PPPOE_SCNAME_SUPPORT
+#define PPPOE_SCNAME_SUPPORT            0
+#endif
+
+/**
+ * PPPOL2TP_SUPPORT==1: Enable PPP Over L2TP
+ */
+#ifndef PPPOL2TP_SUPPORT
+#define PPPOL2TP_SUPPORT                0
+#endif
+
+/**
+ * PPPOL2TP_AUTH_SUPPORT==1: Enable PPP Over L2TP Auth (enable MD5 support)
+ */
+#ifndef PPPOL2TP_AUTH_SUPPORT
+#define PPPOL2TP_AUTH_SUPPORT           PPPOL2TP_SUPPORT
+#endif
+
+/**
+ * PPPOS_SUPPORT==1: Enable PPP Over Serial
+ */
+#ifndef PPPOS_SUPPORT
+#define PPPOS_SUPPORT                   PPP_SUPPORT
+#endif
+
+/**
+ * LWIP_PPP_API==1: Enable PPP API (in pppapi.c)
+ */
+#ifndef LWIP_PPP_API
+#define LWIP_PPP_API                    (PPP_SUPPORT && (NO_SYS == 0))
+#endif
+
+#if PPP_SUPPORT
+
+/**
+ * MEMP_NUM_PPP_PCB: the number of simultaneously active PPP
+ * connections (requires the PPP_SUPPORT option)
+ */
+#ifndef MEMP_NUM_PPP_PCB
+#define MEMP_NUM_PPP_PCB                1
+#endif
+
+/**
+ * PPP_NUM_TIMEOUTS_PER_PCB: the number of sys_timeouts running in parallel per
+ * ppp_pcb. See the detailed explanation at the end of ppp_impl.h about simultaneous
+ * timers analysis.
+ */
+#ifndef PPP_NUM_TIMEOUTS_PER_PCB
+#define PPP_NUM_TIMEOUTS_PER_PCB        (2 + PPP_IPV4_SUPPORT + PPP_IPV6_SUPPORT + CCP_SUPPORT)
+#endif
+
+/* The number of sys_timeouts required for the PPP module */
+#define PPP_NUM_TIMEOUTS                (PPP_SUPPORT * PPP_NUM_TIMEOUTS_PER_PCB * MEMP_NUM_PPP_PCB)
+
+/**
+ * MEMP_NUM_PPPOS_INTERFACES: the number of concurrently active PPPoS
+ * interfaces (only used with PPPOS_SUPPORT==1)
+ */
+#ifndef MEMP_NUM_PPPOS_INTERFACES
+#define MEMP_NUM_PPPOS_INTERFACES       MEMP_NUM_PPP_PCB
+#endif
+
+/**
+ * MEMP_NUM_PPPOE_INTERFACES: the number of concurrently active PPPoE
+ * interfaces (only used with PPPOE_SUPPORT==1)
+ */
+#ifndef MEMP_NUM_PPPOE_INTERFACES
+#define MEMP_NUM_PPPOE_INTERFACES       1
+#endif
+
+/**
+ * MEMP_NUM_PPPOL2TP_INTERFACES: the number of concurrently active PPPoL2TP
+ * interfaces (only used with PPPOL2TP_SUPPORT==1)
+ */
+#ifndef MEMP_NUM_PPPOL2TP_INTERFACES
+#define MEMP_NUM_PPPOL2TP_INTERFACES       1
+#endif
+
+/**
+ * MEMP_NUM_PPP_API_MSG: Number of concurrent PPP API messages (in pppapi.c)
+ */
+#ifndef MEMP_NUM_PPP_API_MSG
+#define MEMP_NUM_PPP_API_MSG 5
+#endif
+
+/**
+ * PPP_DEBUG: Enable debugging for PPP.
+ */
+#ifndef PPP_DEBUG
+#define PPP_DEBUG                       LWIP_DBG_OFF
+#endif
+
+/**
+ * PPP_INPROC_IRQ_SAFE==1 call pppos_input() using tcpip_callback().
+ *
+ * Please read the "PPPoS input path" chapter in the PPP documentation about this option.
+ */
+#ifndef PPP_INPROC_IRQ_SAFE
+#define PPP_INPROC_IRQ_SAFE             0
+#endif
+
+/**
+ * PRINTPKT_SUPPORT==1: Enable PPP print packet support
+ *
+ * Mandatory for debugging, it displays exchanged packet content in debug trace.
+ */
+#ifndef PRINTPKT_SUPPORT
+#define PRINTPKT_SUPPORT                0
+#endif
+
+/**
+ * PPP_IPV4_SUPPORT==1: Enable PPP IPv4 support
+ */
+#ifndef PPP_IPV4_SUPPORT
+#define PPP_IPV4_SUPPORT                (LWIP_IPV4)
+#endif
+
+/**
+ * PPP_IPV6_SUPPORT==1: Enable PPP IPv6 support
+ */
+#ifndef PPP_IPV6_SUPPORT
+#define PPP_IPV6_SUPPORT                (LWIP_IPV6)
+#endif
+
+/**
+ * PPP_NOTIFY_PHASE==1: Support PPP notify phase support
+ *
+ * PPP notify phase support allows you to set a callback which is
+ * called on change of the internal PPP state machine.
+ *
+ * This can be used for example to set a LED pattern depending on the
+ * current phase of the PPP session.
+ */
+#ifndef PPP_NOTIFY_PHASE
+#define PPP_NOTIFY_PHASE                0
+#endif
+
+/**
+ * PPP_FCS_TABLE: Keep a 256*2 byte table to speed up FCS calculation for PPPoS
+ */
+#ifndef PPP_FCS_TABLE
+#define PPP_FCS_TABLE                   1
+#endif
+
+/**
+ * PAP_SUPPORT==1: Support PAP.
+ */
+#ifndef PAP_SUPPORT
+#define PAP_SUPPORT                     0
+#endif
+
+/**
+ * CHAP_SUPPORT==1: Support CHAP.
+ */
+#ifndef CHAP_SUPPORT
+#define CHAP_SUPPORT                    0
+#endif
+
+/**
+ * MSCHAP_SUPPORT==1: Support MSCHAP.
+ */
+#ifndef MSCHAP_SUPPORT
+#define MSCHAP_SUPPORT                  0
+#endif
+#if MSCHAP_SUPPORT
+/* MSCHAP requires CHAP support */
+#undef CHAP_SUPPORT
+#define CHAP_SUPPORT                    1
+#endif /* MSCHAP_SUPPORT */
+
+/**
+ * EAP_SUPPORT==1: Support EAP.
+ */
+#ifndef EAP_SUPPORT
+#define EAP_SUPPORT                     0
+#endif
+
+/**
+ * CCP_SUPPORT==1: Support CCP.
+ */
+#ifndef CCP_SUPPORT
+#define CCP_SUPPORT                     0
+#endif
+
+/**
+ * MPPE_SUPPORT==1: Support MPPE.
+ */
+#ifndef MPPE_SUPPORT
+#define MPPE_SUPPORT                    0
+#endif
+#if MPPE_SUPPORT
+/* MPPE requires CCP support */
+#undef CCP_SUPPORT
+#define CCP_SUPPORT                     1
+/* MPPE requires MSCHAP support */
+#undef MSCHAP_SUPPORT
+#define MSCHAP_SUPPORT                  1
+/* MSCHAP requires CHAP support */
+#undef CHAP_SUPPORT
+#define CHAP_SUPPORT                    1
+#endif /* MPPE_SUPPORT */
+
+/**
+ * CBCP_SUPPORT==1: Support CBCP. CURRENTLY NOT SUPPORTED! DO NOT SET!
+ */
+#ifndef CBCP_SUPPORT
+#define CBCP_SUPPORT                    0
+#endif
+
+/**
+ * ECP_SUPPORT==1: Support ECP. CURRENTLY NOT SUPPORTED! DO NOT SET!
+ */
+#ifndef ECP_SUPPORT
+#define ECP_SUPPORT                     0
+#endif
+
+/**
+ * DEMAND_SUPPORT==1: Support dial on demand. CURRENTLY NOT SUPPORTED! DO NOT SET!
+ */
+#ifndef DEMAND_SUPPORT
+#define DEMAND_SUPPORT                  0
+#endif
+
+/**
+ * LQR_SUPPORT==1: Support Link Quality Report. Do nothing except exchanging some LCP packets.
+ */
+#ifndef LQR_SUPPORT
+#define LQR_SUPPORT                     0
+#endif
+
+/**
+ * PPP_SERVER==1: Enable PPP server support (waiting for incoming PPP session).
+ *
+ * Currently only supported for PPPoS.
+ */
+#ifndef PPP_SERVER
+#define PPP_SERVER                      0
+#endif
+
+#if PPP_SERVER
+/*
+ * PPP_OUR_NAME: Our name for authentication purposes
+ */
+#ifndef PPP_OUR_NAME
+#define PPP_OUR_NAME                    "lwIP"
+#endif
+#endif /* PPP_SERVER */
+
+/**
+ * VJ_SUPPORT==1: Support VJ header compression.
+ *
+ * BEWARE: It is known to be broken when built with some compiler optimizations enabled.
+ */
+#ifndef VJ_SUPPORT
+#define VJ_SUPPORT                      0
+#endif
+/* VJ compression is only supported for TCP over IPv4 over PPPoS. */
+#if !PPPOS_SUPPORT || !PPP_IPV4_SUPPORT || !LWIP_TCP
+#undef VJ_SUPPORT
+#define VJ_SUPPORT                      0
+#endif /* !PPPOS_SUPPORT */
+
+/**
+ * PPP_MD5_RANDM==1: Use MD5 for better randomness.
+ * Enabled by default if CHAP, EAP, or L2TP AUTH support is enabled.
+ */
+#ifndef PPP_MD5_RANDM
+#define PPP_MD5_RANDM                   (CHAP_SUPPORT || EAP_SUPPORT || PPPOL2TP_AUTH_SUPPORT)
+#endif
+
+/**
+ * PolarSSL embedded library
+ *
+ *
+ * lwIP contains some files fetched from the latest BSD release of
+ * the PolarSSL project (PolarSSL 0.10.1-bsd) for ciphers and encryption
+ * methods we need for lwIP PPP support.
+ *
+ * The PolarSSL files were cleaned to contain only the necessary struct
+ * fields and functions needed for lwIP.
+ *
+ * The PolarSSL API was not changed at all, so if you are already using
+ * PolarSSL you can choose to skip the compilation of the included PolarSSL
+ * library into lwIP.
+ *
+ * If you are not using the embedded copy you must include external
+ * libraries into your arch/cc.h port file.
+ *
+ * Beware of the stack requirements which can be a lot larger if you are not
+ * using our cleaned PolarSSL library.
+ */
+
+/**
+ * LWIP_USE_EXTERNAL_POLARSSL: Use external PolarSSL library
+ */
+#ifndef LWIP_USE_EXTERNAL_POLARSSL
+#define LWIP_USE_EXTERNAL_POLARSSL      0
+#endif
+
+/**
+ * LWIP_USE_EXTERNAL_MBEDTLS: Use external mbed TLS library
+ */
+#ifndef LWIP_USE_EXTERNAL_MBEDTLS
+#define LWIP_USE_EXTERNAL_MBEDTLS       0
+#endif
+
+/*
+ * PPP Timeouts
+ */
+
+/**
+ * FSM_DEFTIMEOUT: Timeout time in seconds
+ */
+#ifndef FSM_DEFTIMEOUT
+#define FSM_DEFTIMEOUT                  6
+#endif
+
+/**
+ * FSM_DEFMAXTERMREQS: Maximum Terminate-Request transmissions
+ */
+#ifndef FSM_DEFMAXTERMREQS
+#define FSM_DEFMAXTERMREQS              2
+#endif
+
+/**
+ * FSM_DEFMAXCONFREQS: Maximum Configure-Request transmissions
+ */
+#ifndef FSM_DEFMAXCONFREQS
+#define FSM_DEFMAXCONFREQS              10
+#endif
+
+/**
+ * FSM_DEFMAXNAKLOOPS: Maximum number of nak loops
+ */
+#ifndef FSM_DEFMAXNAKLOOPS
+#define FSM_DEFMAXNAKLOOPS              5
+#endif
+
+/**
+ * UPAP_DEFTIMEOUT: Timeout (seconds) for retransmitting req
+ */
+#ifndef UPAP_DEFTIMEOUT
+#define UPAP_DEFTIMEOUT                 6
+#endif
+
+/**
+ * UPAP_DEFTRANSMITS: Maximum number of auth-reqs to send
+ */
+#ifndef UPAP_DEFTRANSMITS
+#define UPAP_DEFTRANSMITS               10
+#endif
+
+#if PPP_SERVER
+/**
+ * UPAP_DEFREQTIME: Time to wait for auth-req from peer
+ */
+#ifndef UPAP_DEFREQTIME
+#define UPAP_DEFREQTIME                 30
+#endif
+#endif /* PPP_SERVER */
+
+/**
+ * CHAP_DEFTIMEOUT: Timeout (seconds) for retransmitting req
+ */
+#ifndef CHAP_DEFTIMEOUT
+#define CHAP_DEFTIMEOUT                 6
+#endif
+
+/**
+ * CHAP_DEFTRANSMITS: max # times to send challenge
+ */
+#ifndef CHAP_DEFTRANSMITS
+#define CHAP_DEFTRANSMITS               10
+#endif
+
+#if PPP_SERVER
+/**
+ * CHAP_DEFRECHALLENGETIME: If this option is > 0, rechallenge the peer every n seconds
+ */
+#ifndef CHAP_DEFRECHALLENGETIME
+#define CHAP_DEFRECHALLENGETIME         0
+#endif
+#endif /* PPP_SERVER */
+
+/**
+ * EAP_DEFREQTIME: Time to wait for peer request
+ */
+#ifndef EAP_DEFREQTIME
+#define EAP_DEFREQTIME                  6
+#endif
+
+/**
+ * EAP_DEFALLOWREQ: max # times to accept requests
+ */
+#ifndef EAP_DEFALLOWREQ
+#define EAP_DEFALLOWREQ                 10
+#endif
+
+#if PPP_SERVER
+/**
+ * EAP_DEFTIMEOUT: Timeout (seconds) for rexmit
+ */
+#ifndef EAP_DEFTIMEOUT
+#define EAP_DEFTIMEOUT                  6
+#endif
+
+/**
+ * EAP_DEFTRANSMITS: max # times to transmit
+ */
+#ifndef EAP_DEFTRANSMITS
+#define EAP_DEFTRANSMITS                10
+#endif
+#endif /* PPP_SERVER */
+
+/**
+ * LCP_DEFLOOPBACKFAIL: Default number of times we receive our magic number from the peer
+ * before deciding the link is looped-back.
+ */
+#ifndef LCP_DEFLOOPBACKFAIL
+#define LCP_DEFLOOPBACKFAIL             10
+#endif
+
+/**
+ * LCP_ECHOINTERVAL: Interval in seconds between keepalive echo requests, 0 to disable.
+ */
+#ifndef LCP_ECHOINTERVAL
+#define LCP_ECHOINTERVAL                0
+#endif
+
+/**
+ * LCP_MAXECHOFAILS: Number of unanswered echo requests before failure.
+ */
+#ifndef LCP_MAXECHOFAILS
+#define LCP_MAXECHOFAILS                3
+#endif
+
+/**
+ * PPP_MAXIDLEFLAG: Max Xmit idle time (in ms) before resend flag char.
+ */
+#ifndef PPP_MAXIDLEFLAG
+#define PPP_MAXIDLEFLAG                 100
+#endif
+
+/**
+ * PPP Packet sizes
+ */
+
+/**
+ * PPP_MRU: MRU value we want to negotiate (peer MTU)
+ *
+ * It only affects PPPoS because PPPoE value is derived from the
+ * Ethernet interface MTU and PPPoL2TP have a separate setting.
+ */
+#ifndef PPP_MRU
+#define PPP_MRU                         1500
+#endif
+
+/**
+ * PPP_MAXMRU: Normally limit peer MRU to this
+ *
+ * This is the upper limit value to which we set our interface MTU.
+ * If the peer sends a larger number, we will just ignore it as we
+ * are not required to maximize the use of the peer capacity.
+ *
+ * It only affects PPPoS because PPPoE value is derived from the
+ * Ethernet interface MTU and PPPoL2TP have a separate setting.
+ */
+#ifndef PPP_MAXMRU
+#define PPP_MAXMRU                      1500
+#endif
+
+/**
+ * PPP_MINMRU: No peer MRUs below this
+ *
+ * Peer must be able to receive at least our minimum MTU.
+ */
+#ifndef PPP_MINMRU
+#define PPP_MINMRU                      128
+#endif
+
+/**
+ * PPPOL2TP_DEFMRU: Default MTU and MRU for L2TP
+ * Default = 1500 - PPPoE(6) - PPP Protocol(2) - IPv4 header(20) - UDP Header(8)
+ * - L2TP Header(6) - HDLC Header(2) - PPP Protocol(2) - MPPE Header(2) - PPP Protocol(2)
+ */
+#if PPPOL2TP_SUPPORT
+#ifndef PPPOL2TP_DEFMRU
+#define PPPOL2TP_DEFMRU                 1450
+#endif
+#endif /* PPPOL2TP_SUPPORT */
+
+/**
+ * MAXNAMELEN: max length of hostname or name for auth
+ */
+#ifndef MAXNAMELEN
+#define MAXNAMELEN                      256
+#endif
+
+/**
+ * MAXSECRETLEN: max length of password or secret
+ */
+#ifndef MAXSECRETLEN
+#define MAXSECRETLEN                    256
+#endif
+
+/* ------------------------------------------------------------------------- */
+
+/*
+ * Build triggers for embedded PolarSSL
+ */
+#if !LWIP_USE_EXTERNAL_POLARSSL && !LWIP_USE_EXTERNAL_MBEDTLS
+
+/* CHAP, EAP, L2TP AUTH and MD5 Random require MD5 support */
+#if CHAP_SUPPORT || EAP_SUPPORT || PPPOL2TP_AUTH_SUPPORT || PPP_MD5_RANDM
+#define LWIP_INCLUDED_POLARSSL_MD5      1
+#endif /* CHAP_SUPPORT || EAP_SUPPORT || PPPOL2TP_AUTH_SUPPORT || PPP_MD5_RANDM */
+
+#if MSCHAP_SUPPORT
+
+/* MSCHAP require MD4 support */
+#define LWIP_INCLUDED_POLARSSL_MD4      1
+/* MSCHAP require SHA1 support */
+#define LWIP_INCLUDED_POLARSSL_SHA1     1
+/* MSCHAP require DES support */
+#define LWIP_INCLUDED_POLARSSL_DES      1
+
+/* MS-CHAP support is required for MPPE */
+#if MPPE_SUPPORT
+/* MPPE require ARC4 support */
+#define LWIP_INCLUDED_POLARSSL_ARC4     1
+#endif /* MPPE_SUPPORT */
+
+#endif /* MSCHAP_SUPPORT */
+
+#endif /* !LWIP_USE_EXTERNAL_POLARSSL && !LWIP_USE_EXTERNAL_MBEDTLS */
+
+/* Default value if unset */
+#ifndef LWIP_INCLUDED_POLARSSL_MD4
+#define LWIP_INCLUDED_POLARSSL_MD4      0
+#endif /* LWIP_INCLUDED_POLARSSL_MD4 */
+#ifndef LWIP_INCLUDED_POLARSSL_MD5
+#define LWIP_INCLUDED_POLARSSL_MD5      0
+#endif /* LWIP_INCLUDED_POLARSSL_MD5 */
+#ifndef LWIP_INCLUDED_POLARSSL_SHA1
+#define LWIP_INCLUDED_POLARSSL_SHA1     0
+#endif /* LWIP_INCLUDED_POLARSSL_SHA1 */
+#ifndef LWIP_INCLUDED_POLARSSL_DES
+#define LWIP_INCLUDED_POLARSSL_DES      0
+#endif /* LWIP_INCLUDED_POLARSSL_DES */
+#ifndef LWIP_INCLUDED_POLARSSL_ARC4
+#define LWIP_INCLUDED_POLARSSL_ARC4     0
+#endif /* LWIP_INCLUDED_POLARSSL_ARC4 */
+
+#endif /* PPP_SUPPORT */
+
+/* Default value if unset */
+#ifndef PPP_NUM_TIMEOUTS
+#define PPP_NUM_TIMEOUTS                0
+#endif /* PPP_NUM_TIMEOUTS */
+
+#endif /* LWIP_PPP_OPTS_H */
diff --git a/lib/lwip/include/netif/ppp/pppapi.h b/lib/lwip/include/netif/ppp/pppapi.h
new file mode 100644
index 0000000000..913d93f749
--- /dev/null
+++ b/lib/lwip/include/netif/ppp/pppapi.h
@@ -0,0 +1,137 @@
+/*
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ */
+
+#ifndef LWIP_PPPAPI_H
+#define LWIP_PPPAPI_H
+
+#include "netif/ppp/ppp_opts.h"
+
+#if LWIP_PPP_API /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/sys.h"
+#include "lwip/netif.h"
+#include "lwip/priv/tcpip_priv.h"
+#include "netif/ppp/ppp.h"
+#if PPPOS_SUPPORT
+#include "netif/ppp/pppos.h"
+#endif /* PPPOS_SUPPORT */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct pppapi_msg_msg {
+  ppp_pcb *ppp;
+  union {
+#if PPP_NOTIFY_PHASE
+    struct {
+      ppp_notify_phase_cb_fn notify_phase_cb;
+    } setnotifyphasecb;
+#endif /* PPP_NOTIFY_PHASE */
+#if PPPOS_SUPPORT
+    struct {
+      struct netif *pppif;
+      pppos_output_cb_fn output_cb;
+      ppp_link_status_cb_fn link_status_cb;
+      void *ctx_cb;
+    } serialcreate;
+#endif /* PPPOS_SUPPORT */
+#if PPPOE_SUPPORT
+    struct {
+      struct netif *pppif;
+      struct netif *ethif;
+      const char *service_name;
+      const char *concentrator_name;
+      ppp_link_status_cb_fn link_status_cb;
+      void *ctx_cb;
+    } ethernetcreate;
+#endif /* PPPOE_SUPPORT */
+#if PPPOL2TP_SUPPORT
+    struct {
+      struct netif *pppif;
+      struct netif *netif;
+      API_MSG_M_DEF_C(ip_addr_t, ipaddr);
+      u16_t port;
+#if PPPOL2TP_AUTH_SUPPORT
+      const u8_t *secret;
+      u8_t secret_len;
+#endif /* PPPOL2TP_AUTH_SUPPORT */
+      ppp_link_status_cb_fn link_status_cb;
+      void *ctx_cb;
+    } l2tpcreate;
+#endif /* PPPOL2TP_SUPPORT */
+    struct {
+      u16_t holdoff;
+    } connect;
+    struct {
+      u8_t nocarrier;
+    } close;
+    struct {
+      u8_t cmd;
+      void *arg;
+    } ioctl;
+  } msg;
+};
+
+struct pppapi_msg {
+  struct tcpip_api_call_data call;
+  struct pppapi_msg_msg msg;
+};
+
+/* API for application */
+err_t pppapi_set_default(ppp_pcb *pcb);
+#if PPP_NOTIFY_PHASE
+err_t pppapi_set_notify_phase_callback(ppp_pcb *pcb, ppp_notify_phase_cb_fn notify_phase_cb);
+#endif /* PPP_NOTIFY_PHASE */
+#if PPPOS_SUPPORT
+ppp_pcb *pppapi_pppos_create(struct netif *pppif, pppos_output_cb_fn output_cb, ppp_link_status_cb_fn link_status_cb, void *ctx_cb);
+#endif /* PPPOS_SUPPORT */
+#if PPPOE_SUPPORT
+ppp_pcb *pppapi_pppoe_create(struct netif *pppif, struct netif *ethif, const char *service_name,
+                                const char *concentrator_name, ppp_link_status_cb_fn link_status_cb,
+                                void *ctx_cb);
+#endif /* PPPOE_SUPPORT */
+#if PPPOL2TP_SUPPORT
+ppp_pcb *pppapi_pppol2tp_create(struct netif *pppif, struct netif *netif, ip_addr_t *ipaddr, u16_t port,
+                            const u8_t *secret, u8_t secret_len,
+                            ppp_link_status_cb_fn link_status_cb, void *ctx_cb);
+#endif /* PPPOL2TP_SUPPORT */
+err_t pppapi_connect(ppp_pcb *pcb, u16_t holdoff);
+#if PPP_SERVER
+err_t pppapi_listen(ppp_pcb *pcb);
+#endif /* PPP_SERVER */
+err_t pppapi_close(ppp_pcb *pcb, u8_t nocarrier);
+err_t pppapi_free(ppp_pcb *pcb);
+err_t pppapi_ioctl(ppp_pcb *pcb, u8_t cmd, void *arg);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_PPP_API */
+
+#endif /* LWIP_PPPAPI_H */
diff --git a/lib/lwip/include/netif/ppp/pppcrypt.h b/lib/lwip/include/netif/ppp/pppcrypt.h
new file mode 100644
index 0000000000..c0230bbcb7
--- /dev/null
+++ b/lib/lwip/include/netif/ppp/pppcrypt.h
@@ -0,0 +1,144 @@
+/*
+ * pppcrypt.c - PPP/DES linkage for MS-CHAP and EAP SRP-SHA1
+ *
+ * Extracted from chap_ms.c by James Carlson.
+ *
+ * Copyright (c) 1995 Eric Rosenquist.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+/* This header file is included in all PPP modules needing hashes and/or ciphers */
+
+#ifndef PPPCRYPT_H
+#define	PPPCRYPT_H
+
+/*
+ * If included PolarSSL copy is not used, user is expected to include
+ * external libraries in arch/cc.h (which is included by lwip/arch.h).
+ */
+#include "lwip/arch.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Map hashes and ciphers functions to PolarSSL
+ */
+#if !LWIP_USE_EXTERNAL_MBEDTLS
+
+#include "netif/ppp/polarssl/md4.h"
+#define lwip_md4_context md4_context
+#define lwip_md4_init(context)
+#define lwip_md4_starts md4_starts
+#define lwip_md4_update md4_update
+#define lwip_md4_finish md4_finish
+#define lwip_md4_free(context)
+
+#include "netif/ppp/polarssl/md5.h"
+#define lwip_md5_context md5_context
+#define lwip_md5_init(context)
+#define lwip_md5_starts md5_starts
+#define lwip_md5_update md5_update
+#define lwip_md5_finish md5_finish
+#define lwip_md5_free(context)
+
+#include "netif/ppp/polarssl/sha1.h"
+#define lwip_sha1_context sha1_context
+#define lwip_sha1_init(context)
+#define lwip_sha1_starts sha1_starts
+#define lwip_sha1_update sha1_update
+#define lwip_sha1_finish sha1_finish
+#define lwip_sha1_free(context)
+
+#include "netif/ppp/polarssl/des.h"
+#define lwip_des_context des_context
+#define lwip_des_init(context)
+#define lwip_des_setkey_enc des_setkey_enc
+#define lwip_des_crypt_ecb des_crypt_ecb
+#define lwip_des_free(context)
+
+#include "netif/ppp/polarssl/arc4.h"
+#define lwip_arc4_context arc4_context
+#define lwip_arc4_init(context)
+#define lwip_arc4_setup arc4_setup
+#define lwip_arc4_crypt arc4_crypt
+#define lwip_arc4_free(context)
+
+#endif /* !LWIP_USE_EXTERNAL_MBEDTLS */
+
+/*
+ * Map hashes and ciphers functions to mbed TLS
+ */
+#if LWIP_USE_EXTERNAL_MBEDTLS
+
+#define lwip_md4_context mbedtls_md4_context
+#define lwip_md4_init mbedtls_md4_init
+#define lwip_md4_starts mbedtls_md4_starts
+#define lwip_md4_update mbedtls_md4_update
+#define lwip_md4_finish mbedtls_md4_finish
+#define lwip_md4_free mbedtls_md4_free
+
+#define lwip_md5_context mbedtls_md5_context
+#define lwip_md5_init mbedtls_md5_init
+#define lwip_md5_starts mbedtls_md5_starts
+#define lwip_md5_update mbedtls_md5_update
+#define lwip_md5_finish mbedtls_md5_finish
+#define lwip_md5_free mbedtls_md5_free
+
+#define lwip_sha1_context mbedtls_sha1_context
+#define lwip_sha1_init mbedtls_sha1_init
+#define lwip_sha1_starts mbedtls_sha1_starts
+#define lwip_sha1_update mbedtls_sha1_update
+#define lwip_sha1_finish mbedtls_sha1_finish
+#define lwip_sha1_free mbedtls_sha1_free
+
+#define lwip_des_context mbedtls_des_context
+#define lwip_des_init mbedtls_des_init
+#define lwip_des_setkey_enc mbedtls_des_setkey_enc
+#define lwip_des_crypt_ecb mbedtls_des_crypt_ecb
+#define lwip_des_free mbedtls_des_free
+
+#define lwip_arc4_context mbedtls_arc4_context
+#define lwip_arc4_init mbedtls_arc4_init
+#define lwip_arc4_setup mbedtls_arc4_setup
+#define lwip_arc4_crypt(context, buffer, length) mbedtls_arc4_crypt(context, length, buffer, buffer)
+#define lwip_arc4_free mbedtls_arc4_free
+
+#endif /* LWIP_USE_EXTERNAL_MBEDTLS */
+
+void pppcrypt_56_to_64_bit_key(u_char *key, u_char *des_key);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* PPPCRYPT_H */
+
+#endif /* PPP_SUPPORT */
diff --git a/lib/lwip/include/netif/ppp/pppdebug.h b/lib/lwip/include/netif/ppp/pppdebug.h
new file mode 100644
index 0000000000..36ee4f9bf9
--- /dev/null
+++ b/lib/lwip/include/netif/ppp/pppdebug.h
@@ -0,0 +1,88 @@
+/*****************************************************************************
+* pppdebug.h - System debugging utilities.
+*
+* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
+* portions Copyright (c) 1998 Global Election Systems Inc.
+* portions Copyright (c) 2001 by Cognizant Pty Ltd.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any 
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY (please don't use tabs!)
+*
+* 03-01-01 Marc Boucher <marc@mbsi.ca>
+*   Ported to lwIP.
+* 98-07-29 Guy Lancaster <lancasterg@acm.org>, Global Election Systems Inc.
+*   Original.
+*
+*****************************************************************************
+*/
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#ifndef PPPDEBUG_H
+#define PPPDEBUG_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Trace levels. */
+#define LOG_CRITICAL  (PPP_DEBUG | LWIP_DBG_LEVEL_SEVERE)
+#define LOG_ERR       (PPP_DEBUG | LWIP_DBG_LEVEL_SEVERE)
+#define LOG_NOTICE    (PPP_DEBUG | LWIP_DBG_LEVEL_WARNING)
+#define LOG_WARNING   (PPP_DEBUG | LWIP_DBG_LEVEL_WARNING)
+#define LOG_INFO      (PPP_DEBUG)
+#define LOG_DETAIL    (PPP_DEBUG)
+#define LOG_DEBUG     (PPP_DEBUG)
+
+#if PPP_DEBUG
+
+#define MAINDEBUG(a)    LWIP_DEBUGF(LWIP_DBG_LEVEL_WARNING, a)
+#define SYSDEBUG(a)     LWIP_DEBUGF(LWIP_DBG_LEVEL_WARNING, a)
+#define FSMDEBUG(a)     LWIP_DEBUGF(LWIP_DBG_LEVEL_WARNING, a)
+#define LCPDEBUG(a)     LWIP_DEBUGF(LWIP_DBG_LEVEL_WARNING, a)
+#define IPCPDEBUG(a)    LWIP_DEBUGF(LWIP_DBG_LEVEL_WARNING, a)
+#define IPV6CPDEBUG(a)  LWIP_DEBUGF(LWIP_DBG_LEVEL_WARNING, a)
+#define UPAPDEBUG(a)    LWIP_DEBUGF(LWIP_DBG_LEVEL_WARNING, a)
+#define CHAPDEBUG(a)    LWIP_DEBUGF(LWIP_DBG_LEVEL_WARNING, a)
+#define PPPDEBUG(a, b)  LWIP_DEBUGF(a, b)
+
+#else /* PPP_DEBUG */
+
+#define MAINDEBUG(a)
+#define SYSDEBUG(a)
+#define FSMDEBUG(a)
+#define LCPDEBUG(a)
+#define IPCPDEBUG(a)
+#define IPV6CPDEBUG(a)
+#define UPAPDEBUG(a)
+#define CHAPDEBUG(a)
+#define PPPDEBUG(a, b)
+
+#endif /* PPP_DEBUG */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* PPPDEBUG_H */
+
+#endif /* PPP_SUPPORT */
diff --git a/lib/lwip/include/netif/ppp/pppoe.h b/lib/lwip/include/netif/ppp/pppoe.h
new file mode 100644
index 0000000000..8994d38c01
--- /dev/null
+++ b/lib/lwip/include/netif/ppp/pppoe.h
@@ -0,0 +1,187 @@
+/*****************************************************************************
+* pppoe.h - PPP Over Ethernet implementation for lwIP.
+*
+* Copyright (c) 2006 by Marc Boucher, Services Informatiques (MBSI) inc.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any 
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 06-01-01 Marc Boucher <marc@mbsi.ca>
+*   Ported to lwIP.
+*****************************************************************************/
+
+
+
+/* based on NetBSD: if_pppoe.c,v 1.64 2006/01/31 23:50:15 martin Exp */
+
+/*-
+ * Copyright (c) 2002 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Martin Husemann <martin@NetBSD.org>.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *        This product includes software developed by the NetBSD
+ *        Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && PPPOE_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#ifndef PPP_OE_H
+#define PPP_OE_H
+
+#include "ppp.h"
+#include "lwip/etharp.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct pppoehdr {
+  PACK_STRUCT_FLD_8(u8_t vertype);
+  PACK_STRUCT_FLD_8(u8_t code);
+  PACK_STRUCT_FIELD(u16_t session);
+  PACK_STRUCT_FIELD(u16_t plen);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
+
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct pppoetag {
+  PACK_STRUCT_FIELD(u16_t tag);
+  PACK_STRUCT_FIELD(u16_t len);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
+
+
+#define PPPOE_STATE_INITIAL   0
+#define PPPOE_STATE_PADI_SENT 1
+#define PPPOE_STATE_PADR_SENT 2
+#define PPPOE_STATE_SESSION   3
+/* passive */
+#define PPPOE_STATE_PADO_SENT 1
+
+#define PPPOE_HEADERLEN       sizeof(struct pppoehdr)
+#define PPPOE_VERTYPE         0x11    /* VER=1, TYPE = 1 */
+
+#define PPPOE_TAG_EOL         0x0000  /* end of list */
+#define PPPOE_TAG_SNAME       0x0101  /* service name */
+#define PPPOE_TAG_ACNAME      0x0102  /* access concentrator name */
+#define PPPOE_TAG_HUNIQUE     0x0103  /* host unique */
+#define PPPOE_TAG_ACCOOKIE    0x0104  /* AC cookie */
+#define PPPOE_TAG_VENDOR      0x0105  /* vendor specific */
+#define PPPOE_TAG_RELAYSID    0x0110  /* relay session id */
+#define PPPOE_TAG_SNAME_ERR   0x0201  /* service name error */
+#define PPPOE_TAG_ACSYS_ERR   0x0202  /* AC system error */
+#define PPPOE_TAG_GENERIC_ERR 0x0203  /* gerneric error */
+
+#define PPPOE_CODE_PADI       0x09    /* Active Discovery Initiation */
+#define PPPOE_CODE_PADO       0x07    /* Active Discovery Offer */
+#define PPPOE_CODE_PADR       0x19    /* Active Discovery Request */
+#define PPPOE_CODE_PADS       0x65    /* Active Discovery Session confirmation */
+#define PPPOE_CODE_PADT       0xA7    /* Active Discovery Terminate */
+
+#ifndef PPPOE_MAX_AC_COOKIE_LEN
+#define PPPOE_MAX_AC_COOKIE_LEN   64
+#endif
+
+struct pppoe_softc {
+  struct pppoe_softc *next;
+  struct netif *sc_ethif;      /* ethernet interface we are using */
+  ppp_pcb *pcb;                /* PPP PCB */
+
+  struct eth_addr sc_dest;     /* hardware address of concentrator */
+  u16_t sc_session;            /* PPPoE session id */
+  u8_t sc_state;               /* discovery phase or session connected */
+
+#if PPPOE_SCNAME_SUPPORT
+  const char *sc_service_name;      /* if != NULL: requested name of service */
+  const char *sc_concentrator_name; /* if != NULL: requested concentrator id */
+#endif /* PPPOE_SCNAME_SUPPORT */
+  u8_t sc_ac_cookie[PPPOE_MAX_AC_COOKIE_LEN]; /* content of AC cookie we must echo back */
+  u8_t sc_ac_cookie_len;       /* length of cookie data */
+#ifdef PPPOE_SERVER
+  u8_t *sc_hunique;            /* content of host unique we must echo back */
+  u8_t sc_hunique_len;         /* length of host unique */
+#endif
+  u8_t sc_padi_retried;        /* number of PADI retries already done */
+  u8_t sc_padr_retried;        /* number of PADR retries already done */
+};
+
+
+#define pppoe_init() /* compatibility define, no initialization needed */
+
+ppp_pcb *pppoe_create(struct netif *pppif,
+       struct netif *ethif,
+       const char *service_name, const char *concentrator_name,
+       ppp_link_status_cb_fn link_status_cb, void *ctx_cb);
+
+/*
+ * Functions called from lwIP
+ * DO NOT CALL FROM lwIP USER APPLICATION.
+ */
+void pppoe_disc_input(struct netif *netif, struct pbuf *p);
+void pppoe_data_input(struct netif *netif, struct pbuf *p);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* PPP_OE_H */
+
+#endif /* PPP_SUPPORT && PPPOE_SUPPORT */
diff --git a/lib/lwip/include/netif/ppp/pppol2tp.h b/lib/lwip/include/netif/ppp/pppol2tp.h
new file mode 100644
index 0000000000..1221ca15b7
--- /dev/null
+++ b/lib/lwip/include/netif/ppp/pppol2tp.h
@@ -0,0 +1,209 @@
+/**
+ * @file
+ * Network Point to Point Protocol over Layer 2 Tunneling Protocol header file.
+ *
+ */
+
+/*
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && PPPOL2TP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#ifndef PPPOL2TP_H
+#define PPPOL2TP_H
+
+#include "ppp.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Timeout */
+#define PPPOL2TP_CONTROL_TIMEOUT         (5*1000)  /* base for quick timeout calculation */
+#define PPPOL2TP_SLOW_RETRY              (60*1000) /* persistent retry interval */
+
+#define PPPOL2TP_MAXSCCRQ                4         /* retry SCCRQ four times (quickly) */
+#define PPPOL2TP_MAXICRQ                 4         /* retry IRCQ four times */
+#define PPPOL2TP_MAXICCN                 4         /* retry ICCN four times */
+
+/* L2TP header flags */
+#define PPPOL2TP_HEADERFLAG_CONTROL      0x8000
+#define PPPOL2TP_HEADERFLAG_LENGTH       0x4000
+#define PPPOL2TP_HEADERFLAG_SEQUENCE     0x0800
+#define PPPOL2TP_HEADERFLAG_OFFSET       0x0200
+#define PPPOL2TP_HEADERFLAG_PRIORITY     0x0100
+#define PPPOL2TP_HEADERFLAG_VERSION      0x0002
+
+/* Mandatory bits for control: Control, Length, Sequence, Version 2 */
+#define PPPOL2TP_HEADERFLAG_CONTROL_MANDATORY     (PPPOL2TP_HEADERFLAG_CONTROL|PPPOL2TP_HEADERFLAG_LENGTH|PPPOL2TP_HEADERFLAG_SEQUENCE|PPPOL2TP_HEADERFLAG_VERSION)
+/* Forbidden bits for control: Offset, Priority */
+#define PPPOL2TP_HEADERFLAG_CONTROL_FORBIDDEN     (PPPOL2TP_HEADERFLAG_OFFSET|PPPOL2TP_HEADERFLAG_PRIORITY)
+
+/* Mandatory bits for data: Version 2 */
+#define PPPOL2TP_HEADERFLAG_DATA_MANDATORY        (PPPOL2TP_HEADERFLAG_VERSION)
+
+/* AVP (Attribute Value Pair) header */
+#define PPPOL2TP_AVPHEADERFLAG_MANDATORY  0x8000
+#define PPPOL2TP_AVPHEADERFLAG_HIDDEN     0x4000
+#define PPPOL2TP_AVPHEADERFLAG_LENGTHMASK 0x03ff
+
+/* -- AVP - Message type */
+#define PPPOL2TP_AVPTYPE_MESSAGE      0 /* Message type */
+
+/* Control Connection Management */
+#define PPPOL2TP_MESSAGETYPE_SCCRQ    1 /* Start Control Connection Request */
+#define PPPOL2TP_MESSAGETYPE_SCCRP    2 /* Start Control Connection Reply */
+#define PPPOL2TP_MESSAGETYPE_SCCCN    3 /* Start Control Connection Connected */
+#define PPPOL2TP_MESSAGETYPE_STOPCCN  4 /* Stop Control Connection Notification */
+#define PPPOL2TP_MESSAGETYPE_HELLO    6 /* Hello */
+/* Call Management */
+#define PPPOL2TP_MESSAGETYPE_OCRQ     7 /* Outgoing Call Request */
+#define PPPOL2TP_MESSAGETYPE_OCRP     8 /* Outgoing Call Reply */
+#define PPPOL2TP_MESSAGETYPE_OCCN     9 /* Outgoing Call Connected */
+#define PPPOL2TP_MESSAGETYPE_ICRQ    10 /* Incoming Call Request */
+#define PPPOL2TP_MESSAGETYPE_ICRP    11 /* Incoming Call Reply */
+#define PPPOL2TP_MESSAGETYPE_ICCN    12 /* Incoming Call Connected */
+#define PPPOL2TP_MESSAGETYPE_CDN     14 /* Call Disconnect Notify */
+/* Error reporting */
+#define PPPOL2TP_MESSAGETYPE_WEN     15 /* WAN Error Notify */
+/* PPP Session Control */
+#define PPPOL2TP_MESSAGETYPE_SLI     16 /* Set Link Info */
+
+/* -- AVP - Result code */
+#define PPPOL2TP_AVPTYPE_RESULTCODE   1 /* Result code */
+#define PPPOL2TP_RESULTCODE           1 /* General request to clear control connection */
+
+/* -- AVP - Protocol version (!= L2TP Header version) */
+#define PPPOL2TP_AVPTYPE_VERSION      2
+#define PPPOL2TP_VERSION         0x0100 /* L2TP Protocol version 1, revision 0 */
+
+/* -- AVP - Framing capabilities */
+#define PPPOL2TP_AVPTYPE_FRAMINGCAPABILITIES           3 /* Bearer capabilities */
+#define PPPOL2TP_FRAMINGCAPABILITIES          0x00000003 /* Async + Sync framing */
+
+/* -- AVP - Bearer capabilities */
+#define PPPOL2TP_AVPTYPE_BEARERCAPABILITIES           4 /* Bearer capabilities */
+#define PPPOL2TP_BEARERCAPABILITIES          0x00000003 /* Analog + Digital Access */
+
+/* -- AVP - Tie breaker */
+#define PPPOL2TP_AVPTYPE_TIEBREAKER   5
+
+/* -- AVP - Host name */
+#define PPPOL2TP_AVPTYPE_HOSTNAME     7 /* Host name */
+#define PPPOL2TP_HOSTNAME        "lwIP" /* FIXME: make it configurable */
+
+/* -- AVP - Vendor name */
+#define PPPOL2TP_AVPTYPE_VENDORNAME   8 /* Vendor name */
+#define PPPOL2TP_VENDORNAME      "lwIP" /* FIXME: make it configurable */
+
+/* -- AVP - Assign tunnel ID */
+#define PPPOL2TP_AVPTYPE_TUNNELID     9 /* Assign Tunnel ID */
+
+/* -- AVP - Receive window size */
+#define PPPOL2TP_AVPTYPE_RECEIVEWINDOWSIZE  10 /* Receive window size */
+#define PPPOL2TP_RECEIVEWINDOWSIZE           8 /* FIXME: make it configurable */
+
+/* -- AVP - Challenge */
+#define PPPOL2TP_AVPTYPE_CHALLENGE   11 /* Challenge */
+
+/* -- AVP - Cause code */
+#define PPPOL2TP_AVPTYPE_CAUSECODE   12 /* Cause code*/
+
+/* -- AVP - Challenge response */
+#define PPPOL2TP_AVPTYPE_CHALLENGERESPONSE   13 /* Challenge response */
+#define PPPOL2TP_AVPTYPE_CHALLENGERESPONSE_SIZE  16
+
+/* -- AVP - Assign session ID */
+#define PPPOL2TP_AVPTYPE_SESSIONID   14 /* Assign Session ID */
+
+/* -- AVP - Call serial number */
+#define PPPOL2TP_AVPTYPE_CALLSERIALNUMBER   15 /* Call Serial Number */
+
+/* -- AVP - Framing type */
+#define PPPOL2TP_AVPTYPE_FRAMINGTYPE         19 /* Framing Type */
+#define PPPOL2TP_FRAMINGTYPE         0x00000001 /* Sync framing */
+
+/* -- AVP - TX Connect Speed */
+#define PPPOL2TP_AVPTYPE_TXCONNECTSPEED      24 /* TX Connect Speed */
+#define PPPOL2TP_TXCONNECTSPEED       100000000 /* Connect speed: 100 Mbits/s */
+
+/* L2TP Session state */
+#define PPPOL2TP_STATE_INITIAL     0
+#define PPPOL2TP_STATE_SCCRQ_SENT  1
+#define PPPOL2TP_STATE_ICRQ_SENT   2
+#define PPPOL2TP_STATE_ICCN_SENT   3
+#define PPPOL2TP_STATE_DATA        4
+
+#define PPPOL2TP_OUTPUT_DATA_HEADER_LEN   6 /* Our data header len */
+
+/*
+ * PPPoL2TP interface control block.
+ */
+typedef struct pppol2tp_pcb_s pppol2tp_pcb;
+struct pppol2tp_pcb_s {
+  ppp_pcb *ppp;                /* PPP PCB */
+  u8_t phase;                  /* L2TP phase */
+  struct udp_pcb *udp;         /* UDP L2TP Socket */
+  struct netif *netif;         /* Output interface, used as a default route */
+  ip_addr_t remote_ip;         /* LNS IP Address */
+  u16_t remote_port;           /* LNS port */
+#if PPPOL2TP_AUTH_SUPPORT
+  const u8_t *secret;          /* Secret string */
+  u8_t secret_len;             /* Secret string length */
+  u8_t secret_rv[16];          /* Random vector */
+  u8_t challenge_hash[16];     /* Challenge response */
+  u8_t send_challenge;         /* Boolean whether the next sent packet should contains a challenge response */
+#endif /* PPPOL2TP_AUTH_SUPPORT */
+
+  u16_t tunnel_port;           /* Tunnel port */
+  u16_t our_ns;                /* NS to peer */
+  u16_t peer_nr;               /* NR from peer */
+  u16_t peer_ns;               /* Expected NS from peer */
+  u16_t source_tunnel_id;      /* Tunnel ID assigned by peer */
+  u16_t remote_tunnel_id;      /* Tunnel ID assigned to peer */
+  u16_t source_session_id;     /* Session ID assigned by peer */
+  u16_t remote_session_id;     /* Session ID assigned to peer */
+
+  u8_t sccrq_retried;          /* number of SCCRQ retries already done */
+  u8_t icrq_retried;           /* number of ICRQ retries already done */
+  u8_t iccn_retried;           /* number of ICCN retries already done */
+};
+
+
+/* Create a new L2TP session. */
+ppp_pcb *pppol2tp_create(struct netif *pppif,
+       struct netif *netif, const ip_addr_t *ipaddr, u16_t port,
+       const u8_t *secret, u8_t secret_len,
+       ppp_link_status_cb_fn link_status_cb, void *ctx_cb);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* PPPOL2TP_H */
+#endif /* PPP_SUPPORT && PPPOL2TP_SUPPORT */
diff --git a/lib/lwip/include/netif/ppp/pppos.h b/lib/lwip/include/netif/ppp/pppos.h
new file mode 100644
index 0000000000..f587498dfa
--- /dev/null
+++ b/lib/lwip/include/netif/ppp/pppos.h
@@ -0,0 +1,125 @@
+/**
+ * @file
+ * Network Point to Point Protocol over Serial header file.
+ *
+ */
+
+/*
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && PPPOS_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#ifndef PPPOS_H
+#define PPPOS_H
+
+#include "lwip/sys.h"
+
+#include "ppp.h"
+#include "vj.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* PPP packet parser states.  Current state indicates operation yet to be
+ * completed. */
+enum {
+  PDIDLE = 0,  /* Idle state - waiting. */
+  PDADDRESS,   /* Process address field. */
+  PDCONTROL,   /* Process control field. */
+  PDPROTOCOL1, /* Process protocol field 1. */
+  PDPROTOCOL2, /* Process protocol field 2. */
+  PDDATA       /* Process data byte. */
+};
+
+/* PPPoS serial output callback function prototype */
+typedef u32_t (*pppos_output_cb_fn)(ppp_pcb *pcb, const void *data, u32_t len, void *ctx);
+
+/*
+ * Extended asyncmap - allows any character to be escaped.
+ */
+typedef u8_t ext_accm[32];
+
+/*
+ * PPPoS interface control block.
+ */
+typedef struct pppos_pcb_s pppos_pcb;
+struct pppos_pcb_s {
+  /* -- below are data that will NOT be cleared between two sessions */
+  ppp_pcb *ppp;                    /* PPP PCB */
+  pppos_output_cb_fn output_cb;    /* PPP serial output callback */
+
+  /* -- below are data that will be cleared between two sessions
+   *
+   * last_xmit must be the first member of cleared members, because it is
+   * used to know which part must not be cleared.
+   */
+  u32_t last_xmit;                 /* Time of last transmission. */
+  ext_accm out_accm;               /* Async-Ctl-Char-Map for output. */
+
+  /* flags */
+  unsigned int open            :1; /* Set if PPPoS is open */
+  unsigned int pcomp           :1; /* Does peer accept protocol compression? */
+  unsigned int accomp          :1; /* Does peer accept addr/ctl compression? */
+
+  /* PPPoS rx */
+  ext_accm in_accm;                /* Async-Ctl-Char-Map for input. */
+  struct pbuf *in_head, *in_tail;  /* The input packet. */
+  u16_t in_protocol;               /* The input protocol code. */
+  u16_t in_fcs;                    /* Input Frame Check Sequence value. */
+  u8_t in_state;                   /* The input process state. */
+  u8_t in_escaped;                 /* Escape next character. */
+};
+
+/* Create a new PPPoS session. */
+ppp_pcb *pppos_create(struct netif *pppif, pppos_output_cb_fn output_cb,
+       ppp_link_status_cb_fn link_status_cb, void *ctx_cb);
+
+#if !NO_SYS && !PPP_INPROC_IRQ_SAFE
+/* Pass received raw characters to PPPoS to be decoded through lwIP TCPIP thread. */
+err_t pppos_input_tcpip(ppp_pcb *ppp, const void *s, int l);
+#endif /* !NO_SYS && !PPP_INPROC_IRQ_SAFE */
+
+/* PPP over Serial: this is the input function to be called for received data. */
+void pppos_input(ppp_pcb *ppp, const void* data, int len);
+
+
+/*
+ * Functions called from lwIP
+ * DO NOT CALL FROM lwIP USER APPLICATION.
+ */
+#if !NO_SYS && !PPP_INPROC_IRQ_SAFE
+err_t pppos_input_sys(struct pbuf *p, struct netif *inp);
+#endif /* !NO_SYS && !PPP_INPROC_IRQ_SAFE */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* PPPOS_H */
+#endif /* PPP_SUPPORT && PPPOL2TP_SUPPORT */
diff --git a/lib/lwip/include/netif/ppp/upap.h b/lib/lwip/include/netif/ppp/upap.h
new file mode 100644
index 0000000000..540d981492
--- /dev/null
+++ b/lib/lwip/include/netif/ppp/upap.h
@@ -0,0 +1,131 @@
+/*
+ * upap.h - User/Password Authentication Protocol definitions.
+ *
+ * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name "Carnegie Mellon University" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For permission or any legal
+ *    details, please contact
+ *      Office of Technology Transfer
+ *      Carnegie Mellon University
+ *      5000 Forbes Avenue
+ *      Pittsburgh, PA  15213-3890
+ *      (412) 268-4387, fax: (412) 268-7395
+ *      tech-transfer@andrew.cmu.edu
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Computing Services
+ *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id: upap.h,v 1.8 2002/12/04 23:03:33 paulus Exp $
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && PAP_SUPPORT  /* don't build if not configured for use in lwipopts.h */
+
+#ifndef UPAP_H
+#define UPAP_H
+
+#include "ppp.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Packet header = Code, id, length.
+ */
+#define UPAP_HEADERLEN	4
+
+
+/*
+ * UPAP codes.
+ */
+#define UPAP_AUTHREQ	1	/* Authenticate-Request */
+#define UPAP_AUTHACK	2	/* Authenticate-Ack */
+#define UPAP_AUTHNAK	3	/* Authenticate-Nak */
+
+
+/*
+ * Client states.
+ */
+#define UPAPCS_INITIAL	0	/* Connection down */
+#define UPAPCS_CLOSED	1	/* Connection up, haven't requested auth */
+#define UPAPCS_PENDING	2	/* Connection down, have requested auth */
+#define UPAPCS_AUTHREQ	3	/* We've sent an Authenticate-Request */
+#define UPAPCS_OPEN	4	/* We've received an Ack */
+#define UPAPCS_BADAUTH	5	/* We've received a Nak */
+
+/*
+ * Server states.
+ */
+#define UPAPSS_INITIAL	0	/* Connection down */
+#define UPAPSS_CLOSED	1	/* Connection up, haven't requested auth */
+#define UPAPSS_PENDING	2	/* Connection down, have requested auth */
+#define UPAPSS_LISTEN	3	/* Listening for an Authenticate */
+#define UPAPSS_OPEN	4	/* We've sent an Ack */
+#define UPAPSS_BADAUTH	5	/* We've sent a Nak */
+
+
+/*
+ * Timeouts.
+ */
+#if 0 /* moved to ppp_opts.h */
+#define UPAP_DEFTIMEOUT	3	/* Timeout (seconds) for retransmitting req */
+#define UPAP_DEFREQTIME	30	/* Time to wait for auth-req from peer */
+#endif /* moved to ppp_opts.h */
+
+/*
+ * Each interface is described by upap structure.
+ */
+#if PAP_SUPPORT
+typedef struct upap_state {
+    const char *us_user;	/* User */
+    u8_t us_userlen;		/* User length */
+    const char *us_passwd;	/* Password */
+    u8_t us_passwdlen;		/* Password length */
+    u8_t us_clientstate;	/* Client state */
+#if PPP_SERVER
+    u8_t us_serverstate;	/* Server state */
+#endif /* PPP_SERVER */
+    u8_t us_id;		        /* Current id */
+    u8_t us_transmits;		/* Number of auth-reqs sent */
+} upap_state;
+#endif /* PAP_SUPPORT */
+
+
+void upap_authwithpeer(ppp_pcb *pcb, const char *user, const char *password);
+#if PPP_SERVER
+void upap_authpeer(ppp_pcb *pcb);
+#endif /* PPP_SERVER */
+
+extern const struct protent pap_protent;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* UPAP_H */
+#endif /* PPP_SUPPORT && PAP_SUPPORT */
diff --git a/lib/lwip/include/netif/ppp/vj.h b/lib/lwip/include/netif/ppp/vj.h
new file mode 100644
index 0000000000..4e6601c1bc
--- /dev/null
+++ b/lib/lwip/include/netif/ppp/vj.h
@@ -0,0 +1,169 @@
+/*
+ * Definitions for tcp compression routines.
+ *
+ * $Id: vj.h,v 1.7 2010/02/22 17:52:09 goldsimon Exp $
+ *
+ * Copyright (c) 1989 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley.  The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989:
+ * - Initial distribution.
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && VJ_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#ifndef VJ_H
+#define VJ_H
+
+#include "lwip/ip.h"
+#include "lwip/priv/tcp_priv.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define MAX_SLOTS 16 /* must be > 2 and < 256 */
+#define MAX_HDR   128
+
+/*
+ * Compressed packet format:
+ *
+ * The first octet contains the packet type (top 3 bits), TCP
+ * 'push' bit, and flags that indicate which of the 4 TCP sequence
+ * numbers have changed (bottom 5 bits).  The next octet is a
+ * conversation number that associates a saved IP/TCP header with
+ * the compressed packet.  The next two octets are the TCP checksum
+ * from the original datagram.  The next 0 to 15 octets are
+ * sequence number changes, one change per bit set in the header
+ * (there may be no changes and there are two special cases where
+ * the receiver implicitly knows what changed -- see below).
+ *
+ * There are 5 numbers which can change (they are always inserted
+ * in the following order): TCP urgent pointer, window,
+ * acknowlegement, sequence number and IP ID.  (The urgent pointer
+ * is different from the others in that its value is sent, not the
+ * change in value.)  Since typical use of SLIP links is biased
+ * toward small packets (see comments on MTU/MSS below), changes
+ * use a variable length coding with one octet for numbers in the
+ * range 1 - 255 and 3 octets (0, MSB, LSB) for numbers in the
+ * range 256 - 65535 or 0.  (If the change in sequence number or
+ * ack is more than 65535, an uncompressed packet is sent.)
+ */
+
+/*
+ * Packet types (must not conflict with IP protocol version)
+ *
+ * The top nibble of the first octet is the packet type.  There are
+ * three possible types: IP (not proto TCP or tcp with one of the
+ * control flags set); uncompressed TCP (a normal IP/TCP packet but
+ * with the 8-bit protocol field replaced by an 8-bit connection id --
+ * this type of packet syncs the sender & receiver); and compressed
+ * TCP (described above).
+ *
+ * LSB of 4-bit field is TCP "PUSH" bit (a worthless anachronism) and
+ * is logically part of the 4-bit "changes" field that follows.  Top
+ * three bits are actual packet type.  For backward compatibility
+ * and in the interest of conserving bits, numbers are chosen so the
+ * IP protocol version number (4) which normally appears in this nibble
+ * means "IP packet".
+ */
+
+/* packet types */
+#define TYPE_IP               0x40
+#define TYPE_UNCOMPRESSED_TCP 0x70
+#define TYPE_COMPRESSED_TCP   0x80
+#define TYPE_ERROR            0x00
+
+/* Bits in first octet of compressed packet */
+#define NEW_C 0x40 /* flag bits for what changed in a packet */
+#define NEW_I 0x20
+#define NEW_S 0x08
+#define NEW_A 0x04
+#define NEW_W 0x02
+#define NEW_U 0x01
+
+/* reserved, special-case values of above */
+#define SPECIAL_I (NEW_S|NEW_W|NEW_U) /* echoed interactive traffic */
+#define SPECIAL_D (NEW_S|NEW_A|NEW_W|NEW_U) /* unidirectional data */
+#define SPECIALS_MASK (NEW_S|NEW_A|NEW_W|NEW_U)
+
+#define TCP_PUSH_BIT 0x10
+
+
+/*
+ * "state" data for each active tcp conversation on the wire.  This is
+ * basically a copy of the entire IP/TCP header from the last packet
+ * we saw from the conversation together with a small identifier
+ * the transmit & receive ends of the line use to locate saved header.
+ */
+struct cstate {
+  struct cstate *cs_next; /* next most recently used state (xmit only) */
+  u16_t cs_hlen;        /* size of hdr (receive only) */
+  u8_t cs_id;           /* connection # associated with this state */
+  u8_t cs_filler;
+  union {
+    char csu_hdr[MAX_HDR];
+    struct ip_hdr csu_ip;     /* ip/tcp hdr from most recent packet */
+  } vjcs_u;
+};
+#define cs_ip vjcs_u.csu_ip
+#define cs_hdr vjcs_u.csu_hdr
+
+
+struct vjstat {
+  u32_t vjs_packets;        /* outbound packets */
+  u32_t vjs_compressed;     /* outbound compressed packets */
+  u32_t vjs_searches;       /* searches for connection state */
+  u32_t vjs_misses;         /* times couldn't find conn. state */
+  u32_t vjs_uncompressedin; /* inbound uncompressed packets */
+  u32_t vjs_compressedin;   /* inbound compressed packets */
+  u32_t vjs_errorin;        /* inbound unknown type packets */
+  u32_t vjs_tossed;         /* inbound packets tossed because of error */
+};
+
+/*
+ * all the state data for one serial line (we need one of these per line).
+ */
+struct vjcompress {
+  struct cstate *last_cs;          /* most recently used tstate */
+  u8_t last_recv;                /* last rcvd conn. id */
+  u8_t last_xmit;                /* last sent conn. id */
+  u16_t flags;
+  u8_t maxSlotIndex;
+  u8_t compressSlot;             /* Flag indicating OK to compress slot ID. */
+#if LINK_STATS
+  struct vjstat stats;
+#endif
+  struct cstate tstate[MAX_SLOTS]; /* xmit connection states */
+  struct cstate rstate[MAX_SLOTS]; /* receive connection states */
+};
+
+/* flag values */
+#define VJF_TOSS 1U /* tossing rcvd frames because of input err */
+
+extern void  vj_compress_init    (struct vjcompress *comp);
+extern u8_t  vj_compress_tcp     (struct vjcompress *comp, struct pbuf **pb);
+extern void  vj_uncompress_err   (struct vjcompress *comp);
+extern int   vj_uncompress_uncomp(struct pbuf *nb, struct vjcompress *comp);
+extern int   vj_uncompress_tcp   (struct pbuf **nb, struct vjcompress *comp);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* VJ_H */
+
+#endif /* PPP_SUPPORT && VJ_SUPPORT */
diff --git a/lib/lwip/include/netif/slipif.h b/lib/lwip/include/netif/slipif.h
new file mode 100644
index 0000000000..46d2010115
--- /dev/null
+++ b/lib/lwip/include/netif/slipif.h
@@ -0,0 +1,86 @@
+/**
+ * @file
+ *
+ * SLIP netif API
+ */
+
+/*
+ * Copyright (c) 2001, Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the Institute nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef LWIP_HDR_NETIF_SLIPIF_H
+#define LWIP_HDR_NETIF_SLIPIF_H
+
+#include "lwip/opt.h"
+#include "lwip/netif.h"
+
+/** Set this to 1 to start a thread that blocks reading on the serial line
+ * (using sio_read()).
+ */
+#ifndef SLIP_USE_RX_THREAD
+#define SLIP_USE_RX_THREAD !NO_SYS
+#endif
+
+/** Set this to 1 to enable functions to pass in RX bytes from ISR context.
+ * If enabled, slipif_received_byte[s]() process incoming bytes and put assembled
+ * packets on a queue, which is fed into lwIP from slipif_poll().
+ * If disabled, slipif_poll() polls the serial line (using sio_tryread()).
+ */
+#ifndef SLIP_RX_FROM_ISR
+#define SLIP_RX_FROM_ISR 0
+#endif
+
+/** Set this to 1 (default for SLIP_RX_FROM_ISR) to queue incoming packets
+ * received by slipif_received_byte[s]() as long as PBUF_POOL pbufs are available.
+ * If disabled, packets will be dropped if more than one packet is received.
+ */
+#ifndef SLIP_RX_QUEUE
+#define SLIP_RX_QUEUE SLIP_RX_FROM_ISR
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+err_t slipif_init(struct netif * netif);
+void slipif_poll(struct netif *netif);
+#if SLIP_RX_FROM_ISR
+void slipif_process_rxqueue(struct netif *netif);
+void slipif_received_byte(struct netif *netif, u8_t data);
+void slipif_received_bytes(struct netif *netif, u8_t *data, u8_t len);
+#endif /* SLIP_RX_FROM_ISR */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_NETIF_SLIPIF_H */
diff --git a/lib/lwip/include/netif/zepif.h b/lib/lwip/include/netif/zepif.h
new file mode 100644
index 0000000000..2a801b4c40
--- /dev/null
+++ b/lib/lwip/include/netif/zepif.h
@@ -0,0 +1,81 @@
+/**
+ * @file
+ *
+ * A netif implementing the ZigBee Eencapsulation Protocol (ZEP).
+ * This is used to tunnel 6LowPAN over UDP.
+ */
+
+/*
+ * Copyright (c) 2018 Simon Goldschmidt
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt <goldsimon@gmx.de>
+ *
+ */
+
+#ifndef LWIP_HDR_ZEPIF_H
+#define LWIP_HDR_ZEPIF_H
+
+#include "lwip/opt.h"
+#include "netif/lowpan6.h"
+
+#if LWIP_IPV6 && LWIP_UDP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/netif.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ZEPIF_DEFAULT_UDP_PORT  17754
+
+/** Pass this struct as 'state' to netif_add to control the behaviour
+ * of this netif. If NULL is passed, default behaviour is chosen */
+struct zepif_init {
+  /** The UDP port used to ZEP frames from (0 = default) */
+  u16_t               zep_src_udp_port;
+  /** The UDP port used to ZEP frames to (0 = default) */
+  u16_t               zep_dst_udp_port;
+  /** The IP address to sed ZEP frames from (NULL = ANY) */
+  const ip_addr_t    *zep_src_ip_addr;
+  /** The IP address to sed ZEP frames to (NULL = BROADCAST) */
+  const ip_addr_t    *zep_dst_ip_addr;
+  /** If != NULL, the udp pcb is bound to this netif */
+  const struct netif *zep_netif;
+  /** MAC address of the 6LowPAN device */
+  u8_t                addr[6];
+};
+
+err_t zepif_init(struct netif *netif);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_IPV6 && LWIP_UDP */
+
+#endif /* LWIP_HDR_ZEPIF_H */
diff --git a/lib/lwip/netif/ethernet.c b/lib/lwip/netif/ethernet.c
new file mode 100644
index 0000000000..db5c514b45
--- /dev/null
+++ b/lib/lwip/netif/ethernet.c
@@ -0,0 +1,329 @@
+/**
+ * @file
+ * Ethernet common functions
+ *
+ * @defgroup ethernet Ethernet
+ * @ingroup callbackstyle_api
+ */
+
+/*
+ * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
+ * Copyright (c) 2003-2004 Leon Woestenberg <leon.woestenberg@axon.tv>
+ * Copyright (c) 2003-2004 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_ARP || LWIP_ETHERNET
+
+#include "netif/ethernet.h"
+#include "lwip/def.h"
+#include "lwip/stats.h"
+#include "lwip/etharp.h"
+#include "lwip/ip.h"
+#include "lwip/snmp.h"
+
+#include <string.h>
+
+#include "netif/ppp/ppp_opts.h"
+#if PPPOE_SUPPORT
+#include "netif/ppp/pppoe.h"
+#endif /* PPPOE_SUPPORT */
+
+#ifdef LWIP_HOOK_FILENAME
+#include LWIP_HOOK_FILENAME
+#endif
+
+const struct eth_addr ethbroadcast = {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}};
+const struct eth_addr ethzero = {{0, 0, 0, 0, 0, 0}};
+
+/**
+ * @ingroup lwip_nosys
+ * Process received ethernet frames. Using this function instead of directly
+ * calling ip_input and passing ARP frames through etharp in ethernetif_input,
+ * the ARP cache is protected from concurrent access.<br>
+ * Don't call directly, pass to netif_add() and call netif->input().
+ *
+ * @param p the received packet, p->payload pointing to the ethernet header
+ * @param netif the network interface on which the packet was received
+ *
+ * @see LWIP_HOOK_UNKNOWN_ETH_PROTOCOL
+ * @see ETHARP_SUPPORT_VLAN
+ * @see LWIP_HOOK_VLAN_CHECK
+ */
+err_t
+ethernet_input(struct pbuf *p, struct netif *netif)
+{
+  struct eth_hdr *ethhdr;
+  u16_t type;
+#if LWIP_ARP || ETHARP_SUPPORT_VLAN || LWIP_IPV6
+  u16_t next_hdr_offset = SIZEOF_ETH_HDR;
+#endif /* LWIP_ARP || ETHARP_SUPPORT_VLAN */
+
+  LWIP_ASSERT_CORE_LOCKED();
+
+  if (p->len <= SIZEOF_ETH_HDR) {
+    /* a packet with only an ethernet header (or less) is not valid for us */
+    ETHARP_STATS_INC(etharp.proterr);
+    ETHARP_STATS_INC(etharp.drop);
+    MIB2_STATS_NETIF_INC(netif, ifinerrors);
+    goto free_and_return;
+  }
+
+  /* points to packet payload, which starts with an Ethernet header */
+  ethhdr = (struct eth_hdr *)p->payload;
+  LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE,
+              ("ethernet_input: dest:%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F", src:%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F", type:%"X16_F"\n",
+               (unsigned char)ethhdr->dest.addr[0], (unsigned char)ethhdr->dest.addr[1], (unsigned char)ethhdr->dest.addr[2],
+               (unsigned char)ethhdr->dest.addr[3], (unsigned char)ethhdr->dest.addr[4], (unsigned char)ethhdr->dest.addr[5],
+               (unsigned char)ethhdr->src.addr[0],  (unsigned char)ethhdr->src.addr[1],  (unsigned char)ethhdr->src.addr[2],
+               (unsigned char)ethhdr->src.addr[3],  (unsigned char)ethhdr->src.addr[4],  (unsigned char)ethhdr->src.addr[5],
+               lwip_htons(ethhdr->type)));
+
+  type = ethhdr->type;
+#if ETHARP_SUPPORT_VLAN
+  if (type == PP_HTONS(ETHTYPE_VLAN)) {
+    struct eth_vlan_hdr *vlan = (struct eth_vlan_hdr *)(((char *)ethhdr) + SIZEOF_ETH_HDR);
+    next_hdr_offset = SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR;
+    if (p->len <= SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR) {
+      /* a packet with only an ethernet/vlan header (or less) is not valid for us */
+      ETHARP_STATS_INC(etharp.proterr);
+      ETHARP_STATS_INC(etharp.drop);
+      MIB2_STATS_NETIF_INC(netif, ifinerrors);
+      goto free_and_return;
+    }
+#if defined(LWIP_HOOK_VLAN_CHECK) || defined(ETHARP_VLAN_CHECK) || defined(ETHARP_VLAN_CHECK_FN) /* if not, allow all VLANs */
+#ifdef LWIP_HOOK_VLAN_CHECK
+    if (!LWIP_HOOK_VLAN_CHECK(netif, ethhdr, vlan)) {
+#elif defined(ETHARP_VLAN_CHECK_FN)
+    if (!ETHARP_VLAN_CHECK_FN(ethhdr, vlan)) {
+#elif defined(ETHARP_VLAN_CHECK)
+    if (VLAN_ID(vlan) != ETHARP_VLAN_CHECK) {
+#endif
+      /* silently ignore this packet: not for our VLAN */
+      pbuf_free(p);
+      return ERR_OK;
+    }
+#endif /* defined(LWIP_HOOK_VLAN_CHECK) || defined(ETHARP_VLAN_CHECK) || defined(ETHARP_VLAN_CHECK_FN) */
+    type = vlan->tpid;
+  }
+#endif /* ETHARP_SUPPORT_VLAN */
+
+#if LWIP_ARP_FILTER_NETIF
+  netif = LWIP_ARP_FILTER_NETIF_FN(p, netif, lwip_htons(type));
+#endif /* LWIP_ARP_FILTER_NETIF*/
+
+  if (p->if_idx == NETIF_NO_INDEX) {
+    p->if_idx = netif_get_index(netif);
+  }
+
+  if (ethhdr->dest.addr[0] & 1) {
+    /* this might be a multicast or broadcast packet */
+    if (ethhdr->dest.addr[0] == LL_IP4_MULTICAST_ADDR_0) {
+#if LWIP_IPV4
+      if ((ethhdr->dest.addr[1] == LL_IP4_MULTICAST_ADDR_1) &&
+          (ethhdr->dest.addr[2] == LL_IP4_MULTICAST_ADDR_2)) {
+        /* mark the pbuf as link-layer multicast */
+        p->flags |= PBUF_FLAG_LLMCAST;
+      }
+#endif /* LWIP_IPV4 */
+    }
+#if LWIP_IPV6
+    else if ((ethhdr->dest.addr[0] == LL_IP6_MULTICAST_ADDR_0) &&
+             (ethhdr->dest.addr[1] == LL_IP6_MULTICAST_ADDR_1)) {
+      /* mark the pbuf as link-layer multicast */
+      p->flags |= PBUF_FLAG_LLMCAST;
+    }
+#endif /* LWIP_IPV6 */
+    else if (eth_addr_cmp(&ethhdr->dest, &ethbroadcast)) {
+      /* mark the pbuf as link-layer broadcast */
+      p->flags |= PBUF_FLAG_LLBCAST;
+    }
+  }
+
+  switch (type) {
+#if LWIP_IPV4 && LWIP_ARP
+    /* IP packet? */
+    case PP_HTONS(ETHTYPE_IP):
+      if (!(netif->flags & NETIF_FLAG_ETHARP)) {
+        goto free_and_return;
+      }
+      /* skip Ethernet header (min. size checked above) */
+      if (pbuf_remove_header(p, next_hdr_offset)) {
+        LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
+                    ("ethernet_input: IPv4 packet dropped, too short (%"U16_F"/%"U16_F")\n",
+                     p->tot_len, next_hdr_offset));
+        LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("Can't move over header in packet\n"));
+        goto free_and_return;
+      } else {
+        /* pass to IP layer */
+        ip4_input(p, netif);
+      }
+      break;
+
+    case PP_HTONS(ETHTYPE_ARP):
+      if (!(netif->flags & NETIF_FLAG_ETHARP)) {
+        goto free_and_return;
+      }
+      /* skip Ethernet header (min. size checked above) */
+      if (pbuf_remove_header(p, next_hdr_offset)) {
+        LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
+                    ("ethernet_input: ARP response packet dropped, too short (%"U16_F"/%"U16_F")\n",
+                     p->tot_len, next_hdr_offset));
+        LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("Can't move over header in packet\n"));
+        ETHARP_STATS_INC(etharp.lenerr);
+        ETHARP_STATS_INC(etharp.drop);
+        goto free_and_return;
+      } else {
+        /* pass p to ARP module */
+        etharp_input(p, netif);
+      }
+      break;
+#endif /* LWIP_IPV4 && LWIP_ARP */
+#if PPPOE_SUPPORT
+    case PP_HTONS(ETHTYPE_PPPOEDISC): /* PPP Over Ethernet Discovery Stage */
+      pppoe_disc_input(netif, p);
+      break;
+
+    case PP_HTONS(ETHTYPE_PPPOE): /* PPP Over Ethernet Session Stage */
+      pppoe_data_input(netif, p);
+      break;
+#endif /* PPPOE_SUPPORT */
+
+#if LWIP_IPV6
+    case PP_HTONS(ETHTYPE_IPV6): /* IPv6 */
+      /* skip Ethernet header */
+      if ((p->len < next_hdr_offset) || pbuf_remove_header(p, next_hdr_offset)) {
+        LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
+                    ("ethernet_input: IPv6 packet dropped, too short (%"U16_F"/%"U16_F")\n",
+                     p->tot_len, next_hdr_offset));
+        goto free_and_return;
+      } else {
+        /* pass to IPv6 layer */
+        ip6_input(p, netif);
+      }
+      break;
+#endif /* LWIP_IPV6 */
+
+    default:
+#ifdef LWIP_HOOK_UNKNOWN_ETH_PROTOCOL
+      if (LWIP_HOOK_UNKNOWN_ETH_PROTOCOL(p, netif) == ERR_OK) {
+        break;
+      }
+#endif
+      ETHARP_STATS_INC(etharp.proterr);
+      ETHARP_STATS_INC(etharp.drop);
+      MIB2_STATS_NETIF_INC(netif, ifinunknownprotos);
+      goto free_and_return;
+  }
+
+  /* This means the pbuf is freed or consumed,
+     so the caller doesn't have to free it again */
+  return ERR_OK;
+
+free_and_return:
+  pbuf_free(p);
+  return ERR_OK;
+}
+
+/**
+ * @ingroup ethernet
+ * Send an ethernet packet on the network using netif->linkoutput().
+ * The ethernet header is filled in before sending.
+ *
+ * @see LWIP_HOOK_VLAN_SET
+ *
+ * @param netif the lwIP network interface on which to send the packet
+ * @param p the packet to send. pbuf layer must be @ref PBUF_LINK.
+ * @param src the source MAC address to be copied into the ethernet header
+ * @param dst the destination MAC address to be copied into the ethernet header
+ * @param eth_type ethernet type (@ref lwip_ieee_eth_type)
+ * @return ERR_OK if the packet was sent, any other err_t on failure
+ */
+err_t
+ethernet_output(struct netif * netif, struct pbuf * p,
+                const struct eth_addr * src, const struct eth_addr * dst,
+                u16_t eth_type) {
+  struct eth_hdr *ethhdr;
+  u16_t eth_type_be = lwip_htons(eth_type);
+
+#if ETHARP_SUPPORT_VLAN && (defined(LWIP_HOOK_VLAN_SET) || LWIP_VLAN_PCP)
+  s32_t vlan_prio_vid;
+#ifdef LWIP_HOOK_VLAN_SET
+  vlan_prio_vid = LWIP_HOOK_VLAN_SET(netif, p, src, dst, eth_type);
+#elif LWIP_VLAN_PCP
+  vlan_prio_vid = -1;
+  if (netif->hints && (netif->hints->tci >= 0)) {
+    vlan_prio_vid = (u16_t)netif->hints->tci;
+  }
+#endif
+  if (vlan_prio_vid >= 0) {
+    struct eth_vlan_hdr *vlanhdr;
+
+    LWIP_ASSERT("prio_vid must be <= 0xFFFF", vlan_prio_vid <= 0xFFFF);
+
+    if (pbuf_add_header(p, SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR) != 0) {
+      goto pbuf_header_failed;
+    }
+    vlanhdr = (struct eth_vlan_hdr *)(((u8_t *)p->payload) + SIZEOF_ETH_HDR);
+    vlanhdr->tpid     = eth_type_be;
+    vlanhdr->prio_vid = lwip_htons((u16_t)vlan_prio_vid);
+
+    eth_type_be = PP_HTONS(ETHTYPE_VLAN);
+  } else
+#endif /* ETHARP_SUPPORT_VLAN && (defined(LWIP_HOOK_VLAN_SET) || LWIP_VLAN_PCP) */
+  {
+    if (pbuf_add_header(p, SIZEOF_ETH_HDR) != 0) {
+      goto pbuf_header_failed;
+    }
+  }
+
+  LWIP_ASSERT_CORE_LOCKED();
+
+  ethhdr = (struct eth_hdr *)p->payload;
+  ethhdr->type = eth_type_be;
+  SMEMCPY(&ethhdr->dest, dst, ETH_HWADDR_LEN);
+  SMEMCPY(&ethhdr->src,  src, ETH_HWADDR_LEN);
+
+  LWIP_ASSERT("netif->hwaddr_len must be 6 for ethernet_output!",
+              (netif->hwaddr_len == ETH_HWADDR_LEN));
+  LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE,
+              ("ethernet_output: sending packet %p\n", (void *)p));
+
+  /* send the packet */
+  return netif->linkoutput(netif, p);
+
+pbuf_header_failed:
+  LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
+              ("ethernet_output: could not allocate room for header.\n"));
+  LINK_STATS_INC(link.lenerr);
+  return ERR_BUF;
+}
+
+#endif /* LWIP_ARP || LWIP_ETHERNET */
diff --git a/lib/lwip/u-boot/arch/cc.h b/lib/lwip/u-boot/arch/cc.h
new file mode 100644
index 0000000000..31c036dc0c
--- /dev/null
+++ b/lib/lwip/u-boot/arch/cc.h
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2023 Linaro Ltd. <maxim.uvarov@linaro.org> */
+
+#ifndef LWIP_ARCH_CC_H
+#define LWIP_ARCH_CC_H
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <vsprintf.h>
+#include <rand.h>
+
+#define LWIP_ERRNO_INCLUDE <errno.h>
+
+#define LWIP_ERRNO_STDINCLUDE	1
+#define LWIP_NO_UNISTD_H 1
+#define LWIP_TIMEVAL_PRIVATE 1
+
+#ifdef CONFIG_LIB_RAND
+#define LWIP_RAND() ((u32_t)rand())
+#endif
+
+/* different handling for unit test, normally not needed */
+#ifdef LWIP_NOASSERT_ON_ERROR
+#define LWIP_ERROR(message, expression, handler) do { if (!(expression)) { \
+						handler; }} while (0)
+#endif
+
+#define LWIP_DONT_PROVIDE_BYTEORDER_FUNCTIONS
+
+#define LWIP_PLATFORM_ASSERT(x) do {printf("Assertion \"%s\" failed at line %d in %s\n", \
+				    x, __LINE__, __FILE__); } while (0)
+
+#define atoi(str) (int)dectoul(str, NULL)
+#define lwip_strnstr(a, b)  strnstr(a, b)
+
+#define LWIP_ERR_T int
+#define LWIP_CONST_CAST(target_type, val) ((target_type)((uintptr_t)val))
+
+#if defined(CONFIG_SYS_BIG_ENDIAN)
+#define BYTE_ORDER BIG_ENDIAN
+#endif
+
+#endif /* LWIP_ARCH_CC_H */
diff --git a/lib/lwip/u-boot/arch/sys_arch.h b/lib/lwip/u-boot/arch/sys_arch.h
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/lib/lwip/u-boot/limits.h b/lib/lwip/u-boot/limits.h
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/lib/lwip/u-boot/lwipopts.h b/lib/lwip/u-boot/lwipopts.h
new file mode 100644
index 0000000000..aac0a6259f
--- /dev/null
+++ b/lib/lwip/u-boot/lwipopts.h
@@ -0,0 +1,197 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+
+/* Copyright (C) 2023 Linaro Ltd. <maxim.uvarov@linaro.org> */
+
+#ifndef LWIP_UBOOT_LWIPOPTS_H
+#define LWIP_UBOOT_LWIPOPTS_H
+
+#if defined(CONFIG_LWIP_DEBUG)
+#define LWIP_DEBUG 1
+#define LWIP_DBG_MIN_LEVEL              LWIP_DBG_LEVEL_ALL
+#define LWIP_DBG_TYPES_ON               LWIP_DBG_ON
+#define ETHARP_DEBUG                    LWIP_DBG_ON
+#define NETIF_DEBUG                     LWIP_DBG_ON
+#define PBUF_DEBUG                      LWIP_DBG_OFF
+#define API_LIB_DEBUG                   LWIP_DBG_ON
+#define API_MSG_DEBUG                   LWIP_DBG_OFF
+#define SOCKETS_DEBUG                   LWIP_DBG_OFF
+#define ICMP_DEBUG                      LWIP_DBG_OFF
+#define IGMP_DEBUG                      LWIP_DBG_OFF
+#define INET_DEBUG                      LWIP_DBG_OFF
+#define IP_DEBUG                        LWIP_DBG_ON
+#define IP_REASS_DEBUG                  LWIP_DBG_OFF
+#define RAW_DEBUG                       LWIP_DBG_OFF
+#define MEM_DEBUG                       LWIP_DBG_OFF
+#define MEMP_DEBUG                      LWIP_DBG_OFF
+#define SYS_DEBUG                       LWIP_DBG_OFF
+#define TIMERS_DEBUG                    LWIP_DBG_ON
+#define TCP_DEBUG                       LWIP_DBG_OFF
+#define TCP_INPUT_DEBUG                 LWIP_DBG_OFF
+#define TCP_FR_DEBUG                    LWIP_DBG_OFF
+#define TCP_RTO_DEBUG                   LWIP_DBG_OFF
+#define TCP_CWND_DEBUG                  LWIP_DBG_OFF
+#define TCP_WND_DEBUG                   LWIP_DBG_OFF
+#define TCP_OUTPUT_DEBUG                LWIP_DBG_OFF
+#define TCP_RST_DEBUG                   LWIP_DBG_OFF
+#define TCP_QLEN_DEBUG                  LWIP_DBG_OFF
+#define UDP_DEBUG                       LWIP_DBG_OFF
+#define TCPIP_DEBUG                     LWIP_DBG_OFF
+#define SLIP_DEBUG                      LWIP_DBG_OFF
+#define DHCP_DEBUG                      LWIP_DBG_ON
+#define AUTOIP_DEBUG                    LWIP_DBG_ON
+#define DNS_DEBUG                       LWIP_DBG_ON
+#define IP6_DEBUG                       LWIP_DBG_OFF
+#define DHCP6_DEBUG                     LWIP_DBG_OFF
+#else
+#define LWIP_DBG_MIN_LEVEL              LWIP_DBG_LEVEL_ALL
+#define LWIP_DBG_TYPES_ON               LWIP_DBG_OFF
+#define ETHARP_DEBUG                    LWIP_DBG_OFF
+#define NETIF_DEBUG                     LWIP_DBG_OFF
+#define PBUF_DEBUG                      LWIP_DBG_OFF
+#define API_LIB_DEBUG                   LWIP_DBG_OFF
+#define API_MSG_DEBUG                   LWIP_DBG_OFF
+#define SOCKETS_DEBUG                   LWIP_DBG_OFF
+#define ICMP_DEBUG                      LWIP_DBG_OFF
+#define IGMP_DEBUG                      LWIP_DBG_OFF
+#define INET_DEBUG                      LWIP_DBG_OFF
+#define IP_DEBUG                        LWIP_DBG_OFF
+#define IP_REASS_DEBUG                  LWIP_DBG_OFF
+#define RAW_DEBUG                       LWIP_DBG_OFF
+#define MEM_DEBUG                       LWIP_DBG_OFF
+#define MEMP_DEBUG                      LWIP_DBG_OFF
+#define SYS_DEBUG                       LWIP_DBG_OFF
+#define TIMERS_DEBUG                    LWIP_DBG_OFF
+#define TCP_DEBUG                       LWIP_DBG_OFF
+#define TCP_INPUT_DEBUG                 LWIP_DBG_OFF
+#define TCP_FR_DEBUG                    LWIP_DBG_OFF
+#define TCP_RTO_DEBUG                   LWIP_DBG_OFF
+#define TCP_CWND_DEBUG                  LWIP_DBG_OFF
+#define TCP_WND_DEBUG                   LWIP_DBG_OFF
+#define TCP_OUTPUT_DEBUG                LWIP_DBG_OFF
+#define TCP_RST_DEBUG                   LWIP_DBG_OFF
+#define TCP_QLEN_DEBUG                  LWIP_DBG_OFF
+#define UDP_DEBUG                       LWIP_DBG_OFF
+#define TCPIP_DEBUG                     LWIP_DBG_OFF
+#define SLIP_DEBUG                      LWIP_DBG_OFF
+#define DHCP_DEBUG                      LWIP_DBG_OFF
+#define AUTOIP_DEBUG                    LWIP_DBG_OFF
+#define DNS_DEBUG                       LWIP_DBG_OFF
+#define IP6_DEBUG                       LWIP_DBG_OFF
+#define DHCP6_DEBUG                     LWIP_DBG_OFF
+#endif
+#define LWIP_TESTMODE                   0
+
+#if !defined(CONFIG_LWIP_ASSERT)
+#define LWIP_NOASSERT 1
+#define LWIP_ASSERT(message, assertion)
+#endif
+
+#include "lwip/debug.h"
+
+#define SYS_LIGHTWEIGHT_PROT            0
+#define NO_SYS                          1
+
+#define LWIP_IPV4			1
+#define LWIP_IPV6			0
+
+#define MEM_ALIGNMENT                   1
+#define MEM_SIZE                        1600
+
+#define MEMP_NUM_PBUF                   4
+#define MEMP_NUM_RAW_PCB                2
+#define MEMP_NUM_UDP_PCB                4
+#define MEMP_NUM_TCP_PCB                2
+#define MEMP_NUM_TCP_PCB_LISTEN         2
+#define MEMP_NUM_TCP_SEG                16
+#define MEMP_NUM_REASSDATA              1
+#define MEMP_NUM_ARP_QUEUE              2
+#define MEMP_NUM_SYS_TIMEOUT            4
+#define MEMP_NUM_NETBUF                 2
+#define MEMP_NUM_NETCONN               32
+#define MEMP_NUM_TCPIP_MSG_API          8
+#define MEMP_NUM_TCPIP_MSG_INPKT        8
+#define PBUF_POOL_SIZE                  8
+
+#define LWIP_ARP                        1
+#define ARP_TABLE_SIZE                  1
+#define ARP_QUEUEING                    0
+
+#define IP_FORWARD                      0
+#define IP_OPTIONS_ALLOWED              1
+#define IP_REASSEMBLY                   0
+#define IP_FRAG                         0
+#define IP_REASS_MAXAGE                 3
+#define IP_REASS_MAX_PBUFS              4
+#define IP_FRAG_USES_STATIC_BUF         0
+
+#define IP_DEFAULT_TTL                  255
+
+#define LWIP_ICMP                       0
+
+#if defined(CONFIG_PROT_RAW_LWIP)
+#define LWIP_RAW                        1
+#else
+#define LWIP_RAW			0
+#endif
+
+#if defined(CONFIG_PROT_DHCP_LWIP)
+#define LWIP_DHCP                       1
+#define LWIP_DHCP_BOOTP_FILE		1
+#else
+#define LWIP_DHCP			0
+#endif
+
+#define LWIP_DHCP_DOES_ACD_CHECK	0
+
+#define LWIP_AUTOIP                     0
+
+#define LWIP_SNMP                       0
+
+#define LWIP_IGMP                       0
+
+#if defined(CONFIG_PROT_DNS_LWIP)
+#define LWIP_DNS                        1
+#define DNS_TABLE_SIZE                  1
+#else
+#define LWIP_DNS                        0
+#endif
+
+#if defined(CONFIG_PROT_UDP_LWIP)
+#define LWIP_UDP                        1
+#else
+#define LWIP_UDP                        0
+#endif
+
+#if defined(CONFIG_PROT_TCP_LWIP)
+#define LWIP_TCP                        1
+#else
+#define LWIP_TCP                        0
+#endif
+
+#define LWIP_LISTEN_BACKLOG             0
+
+#define PBUF_LINK_HLEN                  14
+#define PBUF_POOL_BUFSIZE               LWIP_MEM_ALIGN_SIZE(TCP_MSS + 40 + PBUF_LINK_HLEN)
+
+#define LWIP_HAVE_LOOPIF                0
+
+#define LWIP_NETCONN                    0
+#define LWIP_DISABLE_MEMP_SANITY_CHECKS 1
+
+#define LWIP_SOCKET                     0
+#define SO_REUSE                        0
+
+#define LWIP_STATS                      0
+
+#define PPP_SUPPORT                     0
+
+#define LWIP_TCPIP_CORE_LOCKING		0
+
+#define LWIP_NETIF_LOOPBACK		0
+
+/* use malloc instead of pool */
+#define MEMP_MEM_MALLOC                 1
+#define MEMP_MEM_INIT			1
+#define MEM_LIBC_MALLOC			1
+
+#endif /* LWIP_UBOOT_LWIPOPTS_H */
diff --git a/net-lwip/Kconfig b/net-lwip/Kconfig
index f8e0481964..87ce51ca8b 100644
--- a/net-lwip/Kconfig
+++ b/net-lwip/Kconfig
@@ -12,4 +12,42 @@ menuconfig NET_LWIP
 	  depend on CONFIG_NET (such as cmd/net.c enabled via CONFIG_CMD_NET).
 	  Therefore the two symbols CONFIG_NET and CONFIG_NET_LWIP are mutually
 	  exclusive.
-	  NOTE: This is currently a placeholder.
+	  NOTE: This currently only builds the lwIP library but does not add
+	  any functionality to U-Boot.
+
+if NET_LWIP
+
+config LWIP_DEBUG
+	bool "Enable debug traces in the lwIP library"
+
+config LWIP_ASSERT
+	bool "Enable assertions in the lwIP library"
+
+config PROT_DHCP_LWIP
+	bool "DHCP support in lwIP"
+	depends on PROT_UDP_LWIP
+	help
+	  Enable support for the DHCP protocol in lwIP.
+
+config PROT_DNS_LWIP
+	bool "DNS support in lwIP"
+	depends on PROT_UDP_LWIP
+	help
+	  Enable support for the DNS protocol in lwIP.
+
+config PROT_RAW_LWIP
+	bool "Raw IP API in lwIP"
+	help
+	  Enable support for the raw IP API in lwIP.
+
+config PROT_TCP_LWIP
+	bool "TCP support in lwIP"
+	help
+	  Enable support for the Transmission Control Protocol in lwIP.
+
+config PROT_UDP_LWIP
+	bool "UDP support in lwIP"
+	help
+	  Enable support for the User Datagram Protocol in lwIP.
+
+endif
