initial commit of lk (little kernel) project
diff --git a/lib/lwip/src/core/dhcp.c b/lib/lwip/src/core/dhcp.c
new file mode 100644
index 0000000..b688afa
--- /dev/null
+++ b/lib/lwip/src/core/dhcp.c
@@ -0,0 +1,1455 @@
+/**
+ * @file
+ *
+ * Dynamic Host Configuration Protocol client
+ */
+
+/*
+ *
+ * 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 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.
+ *
+ * Author: Leon Woestenberg <leon.woestenberg@gmx.net>
+ *
+ * This is a DHCP client for the lwIP TCP/IP stack. It aims to conform
+ * with RFC 2131 and RFC 2132.
+ *
+ * TODO:
+ * - Proper parsing of DHCP messages exploiting file/sname field overloading.
+ * - Add JavaDoc style documentation (API, internals).
+ * - Support for interfaces other than Ethernet (SLIP, PPP, ...)
+ *
+ * Please coordinate changes and requests with Leon Woestenberg
+ * <leon.woestenberg@gmx.net>
+ *
+ * Integration with your code:
+ *
+ * In lwip/dhcp.h
+ * #define DHCP_COARSE_TIMER_SECS (recommended 60 which is a minute)
+ * #define DHCP_FINE_TIMER_MSECS (recommended 500 which equals TCP coarse timer)
+ *
+ * Then have your application call dhcp_coarse_tmr() and
+ * dhcp_fine_tmr() on the defined intervals.
+ *
+ * dhcp_start(struct netif *netif);
+ * starts a DHCP client instance which configures the interface by
+ * obtaining an IP address lease and maintaining it.
+ *
+ * Use dhcp_release(netif) to end the lease and use dhcp_stop(netif)
+ * to remove the DHCP client.
+ *
+ */
+ 
+#include <string.h>
+ 
+#include "lwip/stats.h"
+#include "lwip/mem.h"
+#include "lwip/udp.h"
+#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+#include "lwip/inet.h"
+#include "netif/etharp.h"
+
+#include "lwip/sys.h"
+#include "lwip/opt.h"
+#include "lwip/dhcp.h"
+
+#if LWIP_DHCP /* don't build if not configured for use in lwipopt.h */
+
+/** global transaction identifier, must be
+ *  unique for each DHCP request. We simply increment, starting
+ *  with this value (easy to match with a packet analyzer) */
+static u32_t xid = 0xABCD0000;
+
+/** DHCP client state machine functions */
+static void dhcp_handle_ack(struct netif *netif);
+static void dhcp_handle_nak(struct netif *netif);
+static void dhcp_handle_offer(struct netif *netif);
+
+static err_t dhcp_discover(struct netif *netif);
+static err_t dhcp_select(struct netif *netif);
+static void dhcp_check(struct netif *netif);
+static void dhcp_bind(struct netif *netif);
+static err_t dhcp_decline(struct netif *netif);
+static err_t dhcp_rebind(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, struct ip_addr *addr, u16_t port);
+static err_t dhcp_unfold_reply(struct dhcp *dhcp);
+static u8_t *dhcp_get_option_ptr(struct dhcp *dhcp, u8_t option_type);
+static u8_t dhcp_get_option_byte(u8_t *ptr);
+static u16_t dhcp_get_option_short(u8_t *ptr);
+static u32_t dhcp_get_option_long(u8_t *ptr);
+static void dhcp_free_reply(struct dhcp *dhcp);
+
+/** 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 request, fill in common headers */
+static err_t dhcp_create_request(struct netif *netif);
+/** free a DHCP request */
+static void dhcp_delete_request(struct netif *netif);
+/** add a DHCP option (type, then length in bytes) */
+static void dhcp_option(struct dhcp *dhcp, u8_t option_type, u8_t option_len);
+/** add option values */
+static void dhcp_option_byte(struct dhcp *dhcp, u8_t value);
+static void dhcp_option_short(struct dhcp *dhcp, u16_t value);
+static void dhcp_option_long(struct dhcp *dhcp, u32_t value);
+/** always add the DHCP options trailer to end and pad */
+static void dhcp_option_trailer(struct dhcp *dhcp);
+
+/**
+ * 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 back-off and will end up restarting a fresh DHCP negotiation later.
+ *
+ * @param state pointer to DHCP state structure
+ */
+static void dhcp_handle_nak(struct netif *netif) {
+  struct dhcp *dhcp = netif->dhcp;
+  u16_t msecs = 10 * 1000;
+  LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 3, ("dhcp_handle_nak(netif=%p) %c%c%"U16_F"\n", 
+    (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num));
+  dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
+  LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_handle_nak(): set request timeout %"U16_F" msecs\n", msecs));
+  dhcp_set_state(dhcp, DHCP_BACKING_OFF);
+}
+
+/**
+ * Checks if the offered IP address is already in use.
+ *
+ * It does so by sending an ARP request for the offered address and
+ * entering CHECKING state. If no ARP reply is received within a small
+ * interval, the address is assumed to be free for use by us.
+ */
+static void dhcp_check(struct netif *netif)
+{
+  struct dhcp *dhcp = netif->dhcp;
+  err_t result;
+  u16_t msecs;
+  LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 3, ("dhcp_check(netif=%p) %c%c\n", (void *)netif, (s16_t)netif->name[0],
+    (s16_t)netif->name[1]));
+  /* create an ARP query for the offered IP address, expecting that no host
+     responds, as the IP address should not be in use. */
+  result = etharp_query(netif, &dhcp->offered_ip_addr, NULL);
+  if (result != ERR_OK) {
+    LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 2, ("dhcp_check: could not perform ARP query\n"));
+  }
+  dhcp->tries++;
+  msecs = 500;
+  dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
+  LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_check(): set request timeout %"U16_F" msecs\n", msecs));
+  dhcp_set_state(dhcp, DHCP_CHECKING);
+}
+
+/**
+ * Remember the configuration offered by a DHCP server.
+ *
+ * @param state pointer to DHCP state structure
+ */
+static void dhcp_handle_offer(struct netif *netif)
+{
+  struct dhcp *dhcp = netif->dhcp;
+  /* obtain the server address */
+  u8_t *option_ptr = dhcp_get_option_ptr(dhcp, DHCP_OPTION_SERVER_ID);
+  LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 3, ("dhcp_handle_offer(netif=%p) %c%c%"U16_F"\n",
+    (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num));
+  if (option_ptr != NULL)
+  {
+    dhcp->server_ip_addr.addr = htonl(dhcp_get_option_long(&option_ptr[2]));
+    LWIP_DEBUGF(DHCP_DEBUG | DBG_STATE, ("dhcp_handle_offer(): server 0x%08"X32_F"\n", dhcp->server_ip_addr.addr));
+    /* remember offered address */
+    ip_addr_set(&dhcp->offered_ip_addr, (struct ip_addr *)&dhcp->msg_in->yiaddr);
+    LWIP_DEBUGF(DHCP_DEBUG | DBG_STATE, ("dhcp_handle_offer(): offer for 0x%08"X32_F"\n", dhcp->offered_ip_addr.addr));
+
+    dhcp_select(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 = netif->dhcp;
+  err_t result;
+  u32_t msecs;
+  LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 3, ("dhcp_select(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num));
+
+  /* create and initialize the DHCP message header */
+  result = dhcp_create_request(netif);
+  if (result == ERR_OK)
+  {
+    dhcp_option(dhcp, DHCP_OPTION_MESSAGE_TYPE, DHCP_OPTION_MESSAGE_TYPE_LEN);
+    dhcp_option_byte(dhcp, DHCP_REQUEST);
+
+    dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
+    dhcp_option_short(dhcp, 576);
+
+    /* MUST request the offered IP address */
+    dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4);
+    dhcp_option_long(dhcp, ntohl(dhcp->offered_ip_addr.addr));
+
+    dhcp_option(dhcp, DHCP_OPTION_SERVER_ID, 4);
+    dhcp_option_long(dhcp, ntohl(dhcp->server_ip_addr.addr));
+
+    dhcp_option(dhcp, DHCP_OPTION_PARAMETER_REQUEST_LIST, 4/*num options*/);
+    dhcp_option_byte(dhcp, DHCP_OPTION_SUBNET_MASK);
+    dhcp_option_byte(dhcp, DHCP_OPTION_ROUTER);
+    dhcp_option_byte(dhcp, DHCP_OPTION_BROADCAST);
+    dhcp_option_byte(dhcp, DHCP_OPTION_DNS_SERVER);
+
+    dhcp_option_trailer(dhcp);
+    /* shrink the pbuf to the actual content length */
+    pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len);
+
+    /* TODO: we really should bind to a specific local interface here
+       but we cannot specify an unconfigured netif as it is addressless */
+    udp_bind(dhcp->pcb, IP_ADDR_ANY, DHCP_CLIENT_PORT);
+    /* send broadcast to any DHCP server */
+    udp_connect(dhcp->pcb, IP_ADDR_BROADCAST, DHCP_SERVER_PORT);
+    udp_send(dhcp->pcb, dhcp->p_out);
+    /* reconnect to any (or to server here?!) */
+    udp_connect(dhcp->pcb, IP_ADDR_ANY, DHCP_SERVER_PORT);
+    dhcp_delete_request(netif);
+    LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_select: REQUESTING\n"));
+    dhcp_set_state(dhcp, DHCP_REQUESTING);
+  } else {
+    LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 2, ("dhcp_select: could not allocate DHCP request\n"));
+  }
+  dhcp->tries++;
+  msecs = dhcp->tries < 4 ? dhcp->tries * 1000 : 4 * 1000;
+  dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
+  LWIP_DEBUGF(DHCP_DEBUG | DBG_STATE, ("dhcp_select(): set request timeout %"U32_F" msecs\n", msecs));
+  return result;
+}
+
+/**
+ * The DHCP timer that checks for lease renewal/rebind timeouts.
+ *
+ */
+void dhcp_coarse_tmr()
+{
+  struct netif *netif = netif_list;
+  LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE, ("dhcp_coarse_tmr()\n"));
+  /* iterate through all network interfaces */
+  while (netif != NULL) {
+    /* only act on DHCP configured interfaces */
+    if (netif->dhcp != NULL) {
+      /* timer is active (non zero), and triggers (zeroes) now? */
+      if (netif->dhcp->t2_timeout-- == 1) {
+        LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 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 (netif->dhcp->t1_timeout-- == 1) {
+        LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_coarse_tmr(): t1 timeout\n"));
+        /* this clients' renewal timeout triggered */
+        dhcp_t1_timeout(netif);
+      }
+    }
+    /* proceed to next netif */
+    netif = netif->next;
+  }
+}
+
+/**
+ * DHCP transaction timeout handling
+ *
+ * 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()
+{
+  struct netif *netif = netif_list;
+  /* loop through netif's */
+  while (netif != NULL) {
+    /* only act on DHCP configured interfaces */
+    if (netif->dhcp != NULL) {
+      /* timer is active (non zero), and is about to trigger now */
+      if (netif->dhcp->request_timeout-- == 1) {
+        /* { netif->dhcp->request_timeout == 0 } */
+        LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_fine_tmr(): request timeout\n"));
+        /* this clients' request timeout triggered */
+        dhcp_timeout(netif);
+      }
+    }
+    /* proceed to next network interface */
+    netif = netif->next;
+  }
+}
+
+/**
+ * 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;
+  LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 3, ("dhcp_timeout()\n"));
+  /* back-off period has passed, or server selection timed out */
+  if ((dhcp->state == DHCP_BACKING_OFF) || (dhcp->state == DHCP_SELECTING)) {
+    LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE, ("dhcp_timeout(): restarting discovery\n"));
+    dhcp_discover(netif);
+  /* receiving the requested lease timed out */
+  } else if (dhcp->state == DHCP_REQUESTING) {
+    LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_timeout(): REQUESTING, DHCP request timed out\n"));
+    if (dhcp->tries <= 5) {
+      dhcp_select(netif);
+    } else {
+      LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_timeout(): REQUESTING, releasing, restarting\n"));
+      dhcp_release(netif);
+      dhcp_discover(netif);
+    }
+  /* received no ARP reply for the offered address (which is good) */
+  } else if (dhcp->state == DHCP_CHECKING) {
+    LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_timeout(): CHECKING, ARP request timed out\n"));
+    if (dhcp->tries <= 1) {
+      dhcp_check(netif);
+    /* no ARP replies on the offered address,
+       looks like the IP address is indeed free */
+    } else {
+      /* bind the interface to the offered address */
+      dhcp_bind(netif);
+    }
+  }
+  /* did not get response to renew request? */
+  else if (dhcp->state == DHCP_RENEWING) {
+    LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_timeout(): RENEWING, DHCP request timed out\n"));
+    /* just retry renewal */
+    /* note that the rebind timer will eventually time-out if renew does not work */
+    dhcp_renew(netif);
+  /* did not get response to rebind request? */
+  } else if (dhcp->state == DHCP_REBINDING) {
+    LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_timeout(): REBINDING, DHCP request timed out\n"));
+    if (dhcp->tries <= 8) {
+      dhcp_rebind(netif);
+    } else {
+      LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_timeout(): RELEASING, DISCOVERING\n"));
+      dhcp_release(netif);
+      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;
+  LWIP_DEBUGF(DHCP_DEBUG | DBG_STATE, ("dhcp_t1_timeout()\n"));
+  if ((dhcp->state == DHCP_REQUESTING) || (dhcp->state == DHCP_BOUND) || (dhcp->state == DHCP_RENEWING)) {
+    /* just retry to renew - note that the rebind timer (t2) will
+     * eventually time-out if renew tries fail. */
+    LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_t1_timeout(): must renew\n"));
+    dhcp_renew(netif);
+  }
+}
+
+/**
+ * The rebind period has timed out.
+ *
+ */
+static void dhcp_t2_timeout(struct netif *netif)
+{
+  struct dhcp *dhcp = netif->dhcp;
+  LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_t2_timeout()\n"));
+  if ((dhcp->state == DHCP_REQUESTING) || (dhcp->state == DHCP_BOUND) || (dhcp->state == DHCP_RENEWING)) {
+    /* just retry to rebind */
+    LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_t2_timeout(): must rebind\n"));
+    dhcp_rebind(netif);
+  }
+}
+
+/**
+ *
+ * @param netif the netif under DHCP control
+ */
+static void dhcp_handle_ack(struct netif *netif)
+{
+  struct dhcp *dhcp = netif->dhcp;
+  u8_t *option_ptr;
+  /* clear options we might not get from the ACK */
+  dhcp->offered_sn_mask.addr = 0;
+  dhcp->offered_gw_addr.addr = 0;
+  dhcp->offered_bc_addr.addr = 0;
+
+  /* lease time given? */
+  option_ptr = dhcp_get_option_ptr(dhcp, DHCP_OPTION_LEASE_TIME);
+  if (option_ptr != NULL) {
+    /* remember offered lease time */
+    dhcp->offered_t0_lease = dhcp_get_option_long(option_ptr + 2);
+  }
+  /* renewal period given? */
+  option_ptr = dhcp_get_option_ptr(dhcp, DHCP_OPTION_T1);
+  if (option_ptr != NULL) {
+    /* remember given renewal period */
+    dhcp->offered_t1_renew = dhcp_get_option_long(option_ptr + 2);
+  } else {
+    /* calculate safe periods for renewal */
+    dhcp->offered_t1_renew = dhcp->offered_t0_lease / 2;
+  }
+
+  /* renewal period given? */
+  option_ptr = dhcp_get_option_ptr(dhcp, DHCP_OPTION_T2);
+  if (option_ptr != NULL) {
+    /* remember given rebind period */
+    dhcp->offered_t2_rebind = dhcp_get_option_long(option_ptr + 2);
+  } else {
+    /* calculate safe periods for rebinding */
+    dhcp->offered_t2_rebind = dhcp->offered_t0_lease;
+  }
+
+  /* (y)our internet address */
+  ip_addr_set(&dhcp->offered_ip_addr, &dhcp->msg_in->yiaddr);
+
+/**
+ * Patch #1308
+ * TODO: we must check if the file field is not overloaded by DHCP options!
+ */
+#if 0
+  /* boot server address */
+  ip_addr_set(&dhcp->offered_si_addr, &dhcp->msg_in->siaddr);
+  /* boot file name */
+  if (dhcp->msg_in->file[0]) {
+    dhcp->boot_file_name = mem_malloc(strlen(dhcp->msg_in->file) + 1);
+    strcpy(dhcp->boot_file_name, dhcp->msg_in->file);
+  }
+#endif
+
+  /* subnet mask */
+  option_ptr = dhcp_get_option_ptr(dhcp, DHCP_OPTION_SUBNET_MASK);
+  /* subnet mask given? */
+  if (option_ptr != NULL) {
+    dhcp->offered_sn_mask.addr = htonl(dhcp_get_option_long(&option_ptr[2]));
+  }
+
+  /* gateway router */
+  option_ptr = dhcp_get_option_ptr(dhcp, DHCP_OPTION_ROUTER);
+  if (option_ptr != NULL) {
+    dhcp->offered_gw_addr.addr = htonl(dhcp_get_option_long(&option_ptr[2]));
+  }
+
+  /* broadcast address */
+  option_ptr = dhcp_get_option_ptr(dhcp, DHCP_OPTION_BROADCAST);
+  if (option_ptr != NULL) {
+    dhcp->offered_bc_addr.addr = htonl(dhcp_get_option_long(&option_ptr[2]));
+  }
+  
+  /* DNS servers */
+  option_ptr = dhcp_get_option_ptr(dhcp, DHCP_OPTION_DNS_SERVER);
+  if (option_ptr != NULL) {
+    u8_t n;
+    dhcp->dns_count = dhcp_get_option_byte(&option_ptr[1]);
+    /* limit to at most DHCP_MAX_DNS DNS servers */
+    if (dhcp->dns_count > DHCP_MAX_DNS) dhcp->dns_count = DHCP_MAX_DNS;
+    for (n = 0; n < dhcp->dns_count; n++)
+    {
+      dhcp->offered_dns_addr[n].addr = htonl(dhcp_get_option_long(&option_ptr[2+(n<<2)]));
+    }
+  }
+}
+
+/**
+ * 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 = netif->dhcp;
+  err_t result = ERR_OK;
+
+  LWIP_ASSERT("netif != NULL", netif != NULL);
+  LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_start(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num));
+  netif->flags &= ~NETIF_FLAG_DHCP;
+
+  /* no DHCP client attached yet? */
+  if (dhcp == NULL) {
+    LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE, ("dhcp_start(): starting new DHCP client\n"));
+    dhcp = mem_malloc(sizeof(struct dhcp));
+    if (dhcp == NULL) {
+      LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE, ("dhcp_start(): could not allocate dhcp\n"));
+      return ERR_MEM;
+    }
+    /* store this dhcp client in the netif */
+    netif->dhcp = dhcp;
+    LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE, ("dhcp_start(): allocated dhcp"));
+  /* already has DHCP client attached */
+  } else {
+    LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE | 3, ("dhcp_start(): restarting DHCP configuration\n"));
+  }
+  	
+	/* clear data structure */
+	memset(dhcp, 0, sizeof(struct dhcp));
+  /* allocate UDP PCB */
+	dhcp->pcb = udp_new();
+	if (dhcp->pcb == NULL) {
+	  LWIP_DEBUGF(DHCP_DEBUG  | DBG_TRACE, ("dhcp_start(): could not obtain pcb\n"));
+	  mem_free((void *)dhcp);
+	  netif->dhcp = dhcp = NULL;
+	  return ERR_MEM;
+	}
+	LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE, ("dhcp_start(): starting DHCP configuration\n"));
+  /* (re)start the DHCP negotiation */
+  result = dhcp_discover(netif);
+  if (result != ERR_OK) {
+    /* free resources allocated above */
+    dhcp_stop(netif);
+    return ERR_MEM;
+  }
+  netif->flags |= NETIF_FLAG_DHCP;
+  return result;
+}
+
+/**
+ * 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;
+  err_t result = ERR_OK;
+  dhcp = mem_malloc(sizeof(struct dhcp));
+  if (dhcp == NULL) {
+    LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 2, ("dhcp_inform(): could not allocate dhcp\n"));
+    return;
+  }
+  netif->dhcp = dhcp;
+  memset(dhcp, 0, sizeof(struct dhcp));
+
+  LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE, ("dhcp_inform(): allocated dhcp\n"));
+  dhcp->pcb = udp_new();
+  if (dhcp->pcb == NULL) {
+    LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 2, ("dhcp_inform(): could not obtain pcb"));
+    mem_free((void *)dhcp);
+    return;
+  }
+  LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE, ("dhcp_inform(): created new udp pcb\n"));
+  /* create and initialize the DHCP message header */
+  result = dhcp_create_request(netif);
+  if (result == ERR_OK) {
+
+    dhcp_option(dhcp, DHCP_OPTION_MESSAGE_TYPE, DHCP_OPTION_MESSAGE_TYPE_LEN);
+    dhcp_option_byte(dhcp, DHCP_INFORM);
+
+    dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
+    /* TODO: use netif->mtu ?! */
+    dhcp_option_short(dhcp, 576);
+
+    dhcp_option_trailer(dhcp);
+
+    pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len);
+
+    udp_bind(dhcp->pcb, IP_ADDR_ANY, DHCP_CLIENT_PORT);
+    udp_connect(dhcp->pcb, IP_ADDR_BROADCAST, DHCP_SERVER_PORT);
+    LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_inform: INFORMING\n"));
+    udp_send(dhcp->pcb, dhcp->p_out);
+    udp_connect(dhcp->pcb, IP_ADDR_ANY, DHCP_SERVER_PORT);
+    dhcp_delete_request(netif);
+  } else {
+    LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 2, ("dhcp_inform: could not allocate DHCP request\n"));
+  }
+
+  if (dhcp != NULL)
+  {
+    if (dhcp->pcb != NULL) udp_remove(dhcp->pcb);
+    dhcp->pcb = NULL;
+    mem_free((void *)dhcp);
+    netif->dhcp = NULL;
+  }
+}
+
+#if DHCP_DOES_ARP_CHECK
+/**
+ * Match an ARP reply with the offered IP address.
+ *
+ * @param addr The IP address we received a reply from
+ *
+ */
+void dhcp_arp_reply(struct netif *netif, struct ip_addr *addr)
+{
+  LWIP_ASSERT("netif != NULL", netif != NULL);
+  LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 3, ("dhcp_arp_reply()\n"));
+  /* is a DHCP client doing an ARP check? */
+  if ((netif->dhcp != NULL) && (netif->dhcp->state == DHCP_CHECKING)) {
+    LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_arp_reply(): CHECKING, arp reply for 0x%08"X32_F"\n", addr->addr));
+    /* did a host respond with the address we
+       were offered by the DHCP server? */
+    if (ip_addr_cmp(addr, &netif->dhcp->offered_ip_addr)) {
+      /* we will not accept the offered address */
+      LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE | 1, ("dhcp_arp_reply(): arp reply matched with offered address, declining\n"));
+      dhcp_decline(netif);
+    }
+  }
+}
+
+/**
+ * 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).
+ */
+static err_t dhcp_decline(struct netif *netif)
+{
+  struct dhcp *dhcp = netif->dhcp;
+  err_t result = ERR_OK;
+  u16_t msecs;
+  LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 3, ("dhcp_decline()\n"));
+  dhcp_set_state(dhcp, DHCP_BACKING_OFF);
+  /* create and initialize the DHCP message header */
+  result = dhcp_create_request(netif);
+  if (result == ERR_OK)
+  {
+    dhcp_option(dhcp, DHCP_OPTION_MESSAGE_TYPE, DHCP_OPTION_MESSAGE_TYPE_LEN);
+    dhcp_option_byte(dhcp, DHCP_DECLINE);
+
+    dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
+    dhcp_option_short(dhcp, 576);
+
+    dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4);
+    dhcp_option_long(dhcp, ntohl(dhcp->offered_ip_addr.addr));
+
+    dhcp_option_trailer(dhcp);
+    /* resize pbuf to reflect true size of options */
+    pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len);
+
+    udp_bind(dhcp->pcb, IP_ADDR_ANY, DHCP_CLIENT_PORT);
+    /* @todo: should we really connect here? we are performing sendto() */
+    udp_connect(dhcp->pcb, IP_ADDR_ANY, DHCP_SERVER_PORT);
+    /* per section 4.4.4, broadcast DECLINE messages */
+    udp_sendto(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT);
+    dhcp_delete_request(netif);
+    LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_decline: BACKING OFF\n"));
+  } else {
+    LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 2, ("dhcp_decline: could not allocate DHCP request\n"));
+  }
+  dhcp->tries++;
+  msecs = 10*1000;
+  dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
+   LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE, ("dhcp_decline(): set request timeout %"U16_F" msecs\n", msecs));
+  return result;
+}
+#endif
+
+
+/**
+ * Start the DHCP process, discover a DHCP server.
+ *
+ */
+static err_t dhcp_discover(struct netif *netif)
+{
+  struct dhcp *dhcp = netif->dhcp;
+  err_t result = ERR_OK;
+  u16_t msecs;
+  LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 3, ("dhcp_discover()\n"));
+  ip_addr_set(&dhcp->offered_ip_addr, IP_ADDR_ANY);
+  /* create and initialize the DHCP message header */
+  result = dhcp_create_request(netif);
+  if (result == ERR_OK)
+  {
+    LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE, ("dhcp_discover: making request\n"));
+    dhcp_option(dhcp, DHCP_OPTION_MESSAGE_TYPE, DHCP_OPTION_MESSAGE_TYPE_LEN);
+    dhcp_option_byte(dhcp, DHCP_DISCOVER);
+
+    dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
+    dhcp_option_short(dhcp, 576);
+
+    dhcp_option(dhcp, DHCP_OPTION_PARAMETER_REQUEST_LIST, 4/*num options*/);
+    dhcp_option_byte(dhcp, DHCP_OPTION_SUBNET_MASK);
+    dhcp_option_byte(dhcp, DHCP_OPTION_ROUTER);
+    dhcp_option_byte(dhcp, DHCP_OPTION_BROADCAST);
+    dhcp_option_byte(dhcp, DHCP_OPTION_DNS_SERVER);
+
+    dhcp_option_trailer(dhcp);
+
+    LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE, ("dhcp_discover: realloc()ing\n"));
+    pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len);
+
+    /* set receive callback function with netif as user data */
+    udp_recv(dhcp->pcb, dhcp_recv, netif);
+    udp_bind(dhcp->pcb, IP_ADDR_ANY, DHCP_CLIENT_PORT);
+    udp_connect(dhcp->pcb, IP_ADDR_ANY, DHCP_SERVER_PORT);
+    LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE, ("dhcp_discover: sendto(DISCOVER, IP_ADDR_BROADCAST, DHCP_SERVER_PORT)\n"));
+    udp_sendto(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT);
+    LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE, ("dhcp_discover: deleting()ing\n"));
+    dhcp_delete_request(netif);
+    LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_discover: SELECTING\n"));
+    dhcp_set_state(dhcp, DHCP_SELECTING);
+  } else {
+    LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 2, ("dhcp_discover: could not allocate DHCP request\n"));
+  }
+  dhcp->tries++;
+  msecs = dhcp->tries < 4 ? (dhcp->tries + 1) * 1000 : 10 * 1000;
+  dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
+  LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 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 = netif->dhcp;
+  struct ip_addr sn_mask, gw_addr;
+  LWIP_ASSERT("dhcp_bind: netif != NULL", netif != NULL);
+  LWIP_ASSERT("dhcp_bind: dhcp != NULL", dhcp != NULL);
+  LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 3, ("dhcp_bind(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num));
+
+  /* temporary DHCP lease? */
+  if (dhcp->offered_t1_renew != 0xffffffffUL) {
+    /* set renewal period timer */
+    LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE, ("dhcp_bind(): t1 renewal timer %"U32_F" secs\n", dhcp->offered_t1_renew));
+    dhcp->t1_timeout = (dhcp->offered_t1_renew + DHCP_COARSE_TIMER_SECS / 2) / DHCP_COARSE_TIMER_SECS;
+    if (dhcp->t1_timeout == 0) dhcp->t1_timeout = 1;
+    LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_bind(): set request timeout %"U32_F" msecs\n", dhcp->offered_t1_renew*1000));
+  }
+  /* set renewal period timer */
+  if (dhcp->offered_t2_rebind != 0xffffffffUL) {
+    LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE, ("dhcp_bind(): t2 rebind timer %"U32_F" secs\n", dhcp->offered_t2_rebind));
+    dhcp->t2_timeout = (dhcp->offered_t2_rebind + DHCP_COARSE_TIMER_SECS / 2) / DHCP_COARSE_TIMER_SECS;
+    if (dhcp->t2_timeout == 0) dhcp->t2_timeout = 1;
+    LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_bind(): set request timeout %"U32_F" msecs\n", dhcp->offered_t2_rebind*1000));
+  }
+  /* copy offered network mask */
+  ip_addr_set(&sn_mask, &dhcp->offered_sn_mask);
+
+  /* subnet mask not given? */
+  /* TODO: this is not a valid check. what if the network mask is 0? */
+  if (sn_mask.addr == 0) {
+    /* choose a safe subnet mask given the network class */
+    u8_t first_octet = ip4_addr1(&sn_mask);
+    if (first_octet <= 127) sn_mask.addr = htonl(0xff000000);
+    else if (first_octet >= 192) sn_mask.addr = htonl(0xffffff00);
+    else sn_mask.addr = htonl(0xffff0000);
+  }
+
+  ip_addr_set(&gw_addr, &dhcp->offered_gw_addr);
+  /* gateway address not given? */
+  if (gw_addr.addr == 0) {
+    /* copy network address */
+    gw_addr.addr = (dhcp->offered_ip_addr.addr & sn_mask.addr);
+    /* use first host address on network as gateway */
+    gw_addr.addr |= htonl(0x00000001);
+  }
+
+  LWIP_DEBUGF(DHCP_DEBUG | DBG_STATE, ("dhcp_bind(): IP: 0x%08"X32_F"\n", dhcp->offered_ip_addr.addr));
+  netif_set_ipaddr(netif, &dhcp->offered_ip_addr);
+  LWIP_DEBUGF(DHCP_DEBUG | DBG_STATE, ("dhcp_bind(): SN: 0x%08"X32_F"\n", sn_mask.addr));
+  netif_set_netmask(netif, &sn_mask);
+  LWIP_DEBUGF(DHCP_DEBUG | DBG_STATE, ("dhcp_bind(): GW: 0x%08"X32_F"\n", gw_addr.addr));
+  netif_set_gw(netif, &gw_addr);
+  /* bring the interface up */
+  netif_set_up(netif);
+  /* netif is now bound to DHCP leased address */
+  dhcp_set_state(dhcp, DHCP_BOUND);
+}
+
+/**
+ * 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;
+  err_t result;
+  u16_t msecs;
+  LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 3, ("dhcp_renew()\n"));
+  dhcp_set_state(dhcp, DHCP_RENEWING);
+
+  /* create and initialize the DHCP message header */
+  result = dhcp_create_request(netif);
+  if (result == ERR_OK) {
+
+    dhcp_option(dhcp, DHCP_OPTION_MESSAGE_TYPE, DHCP_OPTION_MESSAGE_TYPE_LEN);
+    dhcp_option_byte(dhcp, DHCP_REQUEST);
+
+    dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
+    /* TODO: use netif->mtu in some way */
+    dhcp_option_short(dhcp, 576);
+
+#if 0
+    dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4);
+    dhcp_option_long(dhcp, ntohl(dhcp->offered_ip_addr.addr));
+#endif
+
+#if 0
+    dhcp_option(dhcp, DHCP_OPTION_SERVER_ID, 4);
+    dhcp_option_long(dhcp, ntohl(dhcp->server_ip_addr.addr));
+#endif
+    /* append DHCP message trailer */
+    dhcp_option_trailer(dhcp);
+
+    pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len);
+
+    udp_bind(dhcp->pcb, IP_ADDR_ANY, DHCP_CLIENT_PORT);
+    udp_connect(dhcp->pcb, &dhcp->server_ip_addr, DHCP_SERVER_PORT);
+    udp_send(dhcp->pcb, dhcp->p_out);
+    dhcp_delete_request(netif);
+
+    LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_renew: RENEWING\n"));
+  } else {
+    LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 2, ("dhcp_renew: could not allocate DHCP request\n"));
+  }
+  dhcp->tries++;
+  /* back-off on retries, but to a maximum of 20 seconds */
+  msecs = dhcp->tries < 10 ? dhcp->tries * 2000 : 20 * 1000;
+  dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
+   LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 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;
+  err_t result;
+  u16_t msecs;
+  LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_rebind()\n"));
+  dhcp_set_state(dhcp, DHCP_REBINDING);
+
+  /* create and initialize the DHCP message header */
+  result = dhcp_create_request(netif);
+  if (result == ERR_OK)
+  {
+
+    dhcp_option(dhcp, DHCP_OPTION_MESSAGE_TYPE, DHCP_OPTION_MESSAGE_TYPE_LEN);
+    dhcp_option_byte(dhcp, DHCP_REQUEST);
+
+    dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
+    dhcp_option_short(dhcp, 576);
+
+#if 0
+    dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4);
+    dhcp_option_long(dhcp, ntohl(dhcp->offered_ip_addr.addr));
+
+    dhcp_option(dhcp, DHCP_OPTION_SERVER_ID, 4);
+    dhcp_option_long(dhcp, ntohl(dhcp->server_ip_addr.addr));
+#endif
+
+    dhcp_option_trailer(dhcp);
+
+    pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len);
+
+    /* set remote IP association to any DHCP server */
+    udp_bind(dhcp->pcb, IP_ADDR_ANY, DHCP_CLIENT_PORT);
+    udp_connect(dhcp->pcb, IP_ADDR_ANY, DHCP_SERVER_PORT);
+    /* broadcast to server */
+    udp_sendto(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT);
+    dhcp_delete_request(netif);
+    LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_rebind: REBINDING\n"));
+  } else {
+    LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 2, ("dhcp_rebind: could not allocate DHCP request\n"));
+  }
+  dhcp->tries++;
+  msecs = dhcp->tries < 10 ? dhcp->tries * 1000 : 10 * 1000;
+  dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
+   LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_rebind(): set request timeout %"U16_F" msecs\n", msecs));
+  return result;
+}
+
+/**
+ * Release a DHCP lease.
+ *
+ * @param netif network interface which must release its lease
+ */
+err_t dhcp_release(struct netif *netif)
+{
+  struct dhcp *dhcp = netif->dhcp;
+  err_t result;
+  u16_t msecs;
+  LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 3, ("dhcp_release()\n"));
+
+  /* idle DHCP client */
+  dhcp_set_state(dhcp, DHCP_OFF);
+  /* clean old DHCP offer */
+  dhcp->server_ip_addr.addr = 0;
+  dhcp->offered_ip_addr.addr = dhcp->offered_sn_mask.addr = 0;
+  dhcp->offered_gw_addr.addr = dhcp->offered_bc_addr.addr = 0;
+  dhcp->offered_t0_lease = dhcp->offered_t1_renew = dhcp->offered_t2_rebind = 0;
+  dhcp->dns_count = 0;
+  
+  /* create and initialize the DHCP message header */
+  result = dhcp_create_request(netif);
+  if (result == ERR_OK) {
+    dhcp_option(dhcp, DHCP_OPTION_MESSAGE_TYPE, DHCP_OPTION_MESSAGE_TYPE_LEN);
+    dhcp_option_byte(dhcp, DHCP_RELEASE);
+
+    dhcp_option_trailer(dhcp);
+
+    pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len);
+
+    udp_bind(dhcp->pcb, IP_ADDR_ANY, DHCP_CLIENT_PORT);
+    udp_connect(dhcp->pcb, &dhcp->server_ip_addr, DHCP_SERVER_PORT);
+    udp_send(dhcp->pcb, dhcp->p_out);
+    dhcp_delete_request(netif);
+    LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_release: RELEASED, DHCP_OFF\n"));
+  } else {
+    LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 2, ("dhcp_release: could not allocate DHCP request\n"));
+  }
+  dhcp->tries++;
+  msecs = dhcp->tries < 10 ? dhcp->tries * 1000 : 10 * 1000;
+  dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
+  LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_release(): set request timeout %"U16_F" msecs\n", msecs));
+  /* bring the interface down */
+  netif_set_down(netif);
+  /* remove IP address from interface */
+  netif_set_ipaddr(netif, IP_ADDR_ANY);
+  netif_set_gw(netif, IP_ADDR_ANY);
+  netif_set_netmask(netif, IP_ADDR_ANY);
+  
+  /* TODO: netif_down(netif); */
+  return result;
+}
+/**
+ * Remove the DHCP client from the interface.
+ *
+ * @param netif The network interface to stop DHCP on
+ */
+void dhcp_stop(struct netif *netif)
+{
+  struct dhcp *dhcp = netif->dhcp;
+  LWIP_ASSERT("dhcp_stop: netif != NULL", netif != NULL);
+
+  LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 3, ("dhcp_stop()\n"));
+  /* netif is DHCP configured? */
+  if (dhcp != NULL)
+  {
+    if (dhcp->pcb != NULL)
+    {
+      udp_remove(dhcp->pcb);
+      dhcp->pcb = NULL;
+    }
+    if (dhcp->p != NULL)
+    {
+      pbuf_free(dhcp->p);
+      dhcp->p = NULL;
+    }
+    /* free unfolded reply */
+    dhcp_free_reply(dhcp);
+    mem_free((void *)dhcp);
+    netif->dhcp = NULL;
+  }
+}
+
+/*
+ * Set the DHCP state of a DHCP client.
+ *
+ * If the state changed, reset the number of tries.
+ *
+ * TODO: we might also want to reset the timeout here?
+ */
+static void dhcp_set_state(struct dhcp *dhcp, u8_t new_state)
+{
+  if (new_state != dhcp->state)
+  {
+    dhcp->state = new_state;
+    dhcp->tries = 0;
+  }
+}
+
+/*
+ * Concatenate an option type and length field to the outgoing
+ * DHCP message.
+ *
+ */
+static void dhcp_option(struct dhcp *dhcp, u8_t option_type, u8_t option_len)
+{
+  LWIP_ASSERT("dhcp_option_short: dhcp->options_out_len + 2 + option_len <= DHCP_OPTIONS_LEN", dhcp->options_out_len + 2 + option_len <= DHCP_OPTIONS_LEN);
+  dhcp->msg_out->options[dhcp->options_out_len++] = option_type;
+  dhcp->msg_out->options[dhcp->options_out_len++] = option_len;
+}
+/*
+ * Concatenate a single byte to the outgoing DHCP message.
+ *
+ */
+static void dhcp_option_byte(struct dhcp *dhcp, u8_t value)
+{
+  LWIP_ASSERT("dhcp_option_short: dhcp->options_out_len < DHCP_OPTIONS_LEN", dhcp->options_out_len < DHCP_OPTIONS_LEN);
+  dhcp->msg_out->options[dhcp->options_out_len++] = value;
+}
+static void dhcp_option_short(struct dhcp *dhcp, u16_t value)
+{
+  LWIP_ASSERT("dhcp_option_short: dhcp->options_out_len + 2 <= DHCP_OPTIONS_LEN", dhcp->options_out_len + 2 <= DHCP_OPTIONS_LEN);
+  dhcp->msg_out->options[dhcp->options_out_len++] = (value & 0xff00U) >> 8;
+  dhcp->msg_out->options[dhcp->options_out_len++] =  value & 0x00ffU;
+}
+static void dhcp_option_long(struct dhcp *dhcp, u32_t value)
+{
+  LWIP_ASSERT("dhcp_option_long: dhcp->options_out_len + 4 <= DHCP_OPTIONS_LEN", dhcp->options_out_len + 4 <= DHCP_OPTIONS_LEN);
+  dhcp->msg_out->options[dhcp->options_out_len++] = (value & 0xff000000UL) >> 24;
+  dhcp->msg_out->options[dhcp->options_out_len++] = (value & 0x00ff0000UL) >> 16;
+  dhcp->msg_out->options[dhcp->options_out_len++] = (value & 0x0000ff00UL) >> 8;
+  dhcp->msg_out->options[dhcp->options_out_len++] = (value & 0x000000ffUL);
+}
+
+/**
+ * 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 conitguous piece of memory, and
+ * use that further on.
+ *
+ */
+static err_t dhcp_unfold_reply(struct dhcp *dhcp)
+{
+  struct pbuf *p = dhcp->p;
+  u8_t *ptr;
+  u16_t i;
+  u16_t j = 0;
+  LWIP_ASSERT("dhcp->p != NULL", dhcp->p != NULL);
+  /* free any left-overs from previous unfolds */
+  dhcp_free_reply(dhcp);
+  /* options present? */
+  if (dhcp->p->tot_len > (sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN))
+  {
+    dhcp->options_in_len = dhcp->p->tot_len - (sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN);
+    dhcp->options_in = mem_malloc(dhcp->options_in_len);
+    if (dhcp->options_in == NULL)
+    {
+      LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 2, ("dhcp_unfold_reply(): could not allocate dhcp->options\n"));
+      return ERR_MEM;
+    }
+  }
+  dhcp->msg_in = mem_malloc(sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN);
+  if (dhcp->msg_in == NULL)
+  {
+    LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 2, ("dhcp_unfold_reply(): could not allocate dhcp->msg_in\n"));
+    mem_free((void *)dhcp->options_in);
+    dhcp->options_in = NULL;
+    return ERR_MEM;
+  }
+
+  ptr = (u8_t *)dhcp->msg_in;
+  /* proceed through struct dhcp_msg */
+  for (i = 0; i < sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN; i++)
+  {
+    *ptr++ = ((u8_t *)p->payload)[j++];
+    /* reached end of pbuf? */
+    if (j == p->len)
+    {
+      /* proceed to next pbuf in chain */
+      p = p->next;
+      j = 0;
+    }
+  }
+  LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE, ("dhcp_unfold_reply(): copied %"U16_F" bytes into dhcp->msg_in[]\n", i));
+  if (dhcp->options_in != NULL) {
+    ptr = (u8_t *)dhcp->options_in;
+    /* proceed through options */
+    for (i = 0; i < dhcp->options_in_len; i++) {
+      *ptr++ = ((u8_t *)p->payload)[j++];
+      /* reached end of pbuf? */
+      if (j == p->len) {
+        /* proceed to next pbuf in chain */
+        p = p->next;
+        j = 0;
+      }
+    }
+    LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE, ("dhcp_unfold_reply(): copied %"U16_F" bytes to dhcp->options_in[]\n", i));
+  }
+  return ERR_OK;
+}
+
+/**
+ * Free the incoming DHCP message including contiguous copy of
+ * its DHCP options.
+ *
+ */
+static void dhcp_free_reply(struct dhcp *dhcp)
+{
+  if (dhcp->msg_in != NULL) {
+    mem_free((void *)dhcp->msg_in);
+    dhcp->msg_in = NULL;
+  }
+  if (dhcp->options_in) {
+    mem_free((void *)dhcp->options_in);
+    dhcp->options_in = NULL;
+    dhcp->options_in_len = 0;
+  }
+  LWIP_DEBUGF(DHCP_DEBUG, ("dhcp_free_reply(): free'd\n"));
+}
+
+
+/**
+ * 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, struct ip_addr *addr, u16_t port)
+{
+  struct netif *netif = (struct netif *)arg;
+  struct dhcp *dhcp = netif->dhcp;
+  struct dhcp_msg *reply_msg = (struct dhcp_msg *)p->payload;
+  u8_t *options_ptr;
+  u8_t msg_type;
+  u8_t i;
+  LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 3, ("dhcp_recv(pbuf = %p) from DHCP server %"U16_F".%"U16_F".%"U16_F".%"U16_F" port %"U16_F"\n", (void*)p,
+    (u16_t)(ntohl(addr->addr) >> 24 & 0xff), (u16_t)(ntohl(addr->addr) >> 16 & 0xff),
+    (u16_t)(ntohl(addr->addr) >>  8 & 0xff), (u16_t)(ntohl(addr->addr) & 0xff), port));
+  LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE, ("pbuf->len = %"U16_F"\n", p->len));
+  LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE, ("pbuf->tot_len = %"U16_F"\n", p->tot_len));
+  /* prevent warnings about unused arguments */
+  (void)pcb; (void)addr; (void)port;
+  dhcp->p = p;
+  /* TODO: check packet length before reading them */
+  if (reply_msg->op != DHCP_BOOTREPLY) {
+    LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 1, ("not a DHCP reply message, but type %"U16_F"\n", (u16_t)reply_msg->op));
+    pbuf_free(p);
+    dhcp->p = NULL;
+    return;
+  }
+  /* iterate through hardware address and match against DHCP message */
+  for (i = 0; i < netif->hwaddr_len; i++) {
+    if (netif->hwaddr[i] != reply_msg->chaddr[i]) {
+      LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 2, ("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]));
+      pbuf_free(p);
+      dhcp->p = NULL;
+      return;
+    }
+  }
+  /* match transaction ID against what we expected */
+  if (ntohl(reply_msg->xid) != dhcp->xid) {
+    LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 2, ("transaction id mismatch\n"));
+    pbuf_free(p);
+    dhcp->p = NULL;
+    return;
+  }
+  /* option fields could be unfold? */
+  if (dhcp_unfold_reply(dhcp) != ERR_OK) {
+    LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 2, ("problem unfolding DHCP message - too short on memory?\n"));
+    pbuf_free(p);
+    dhcp->p = NULL;
+    return;
+  }
+
+  LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE, ("searching DHCP_OPTION_MESSAGE_TYPE\n"));
+  /* obtain pointer to DHCP message type */
+  options_ptr = dhcp_get_option_ptr(dhcp, DHCP_OPTION_MESSAGE_TYPE);
+  if (options_ptr == NULL) {
+    LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 1, ("DHCP_OPTION_MESSAGE_TYPE option not found\n"));
+    pbuf_free(p);
+    dhcp->p = NULL;
+    return;
+  }
+
+  /* read DHCP message type */
+  msg_type = dhcp_get_option_byte(options_ptr + 2);
+  /* message type is DHCP ACK? */
+  if (msg_type == DHCP_ACK) {
+    LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 1, ("DHCP_ACK received\n"));
+    /* in requesting state? */
+    if (dhcp->state == DHCP_REQUESTING) {
+      dhcp_handle_ack(netif);
+      dhcp->request_timeout = 0;
+#if DHCP_DOES_ARP_CHECK
+      /* check if the acknowledged lease address is already in use */
+      dhcp_check(netif);
+#else
+      /* bind interface to the acknowledged lease address */
+      dhcp_bind(netif);
+#endif
+    }
+    /* already bound to the given lease address? */
+    else if ((dhcp->state == DHCP_REBOOTING) || (dhcp->state == DHCP_REBINDING) || (dhcp->state == DHCP_RENEWING)) {
+      dhcp->request_timeout = 0;
+      dhcp_bind(netif);
+    }
+  }
+  /* received a DHCP_NAK in appropriate state? */
+  else if ((msg_type == DHCP_NAK) &&
+    ((dhcp->state == DHCP_REBOOTING) || (dhcp->state == DHCP_REQUESTING) ||
+     (dhcp->state == DHCP_REBINDING) || (dhcp->state == DHCP_RENEWING  ))) {
+    LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 1, ("DHCP_NAK received\n"));
+    dhcp->request_timeout = 0;
+    dhcp_handle_nak(netif);
+  }
+  /* received a DHCP_OFFER in DHCP_SELECTING state? */
+  else if ((msg_type == DHCP_OFFER) && (dhcp->state == DHCP_SELECTING)) {
+    LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 1, ("DHCP_OFFER received in DHCP_SELECTING state\n"));
+    dhcp->request_timeout = 0;
+    /* remember offered lease */
+    dhcp_handle_offer(netif);
+  }
+  pbuf_free(p);
+  dhcp->p = NULL;
+}
+
+
+static err_t dhcp_create_request(struct netif *netif)
+{
+  struct dhcp *dhcp = netif->dhcp;
+  u16_t i;
+  LWIP_ASSERT("dhcp_create_request: dhcp->p_out == NULL", dhcp->p_out == NULL);
+  LWIP_ASSERT("dhcp_create_request: dhcp->msg_out == NULL", dhcp->msg_out == NULL);
+  dhcp->p_out = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct dhcp_msg), PBUF_RAM);
+  if (dhcp->p_out == NULL) {
+    LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 2, ("dhcp_create_request(): could not allocate pbuf\n"));
+    return ERR_MEM;
+  }
+  /* give unique transaction identifier to this request */
+  dhcp->xid = xid++;
+
+  dhcp->msg_out = (struct dhcp_msg *)dhcp->p_out->payload;
+
+  dhcp->msg_out->op = DHCP_BOOTREQUEST;
+  /* TODO: make link layer independent */
+  dhcp->msg_out->htype = DHCP_HTYPE_ETH;
+  /* TODO: make link layer independent */
+  dhcp->msg_out->hlen = DHCP_HLEN_ETH;
+  dhcp->msg_out->hops = 0;
+  dhcp->msg_out->xid = htonl(dhcp->xid);
+  dhcp->msg_out->secs = 0;
+  dhcp->msg_out->flags = 0;
+  dhcp->msg_out->ciaddr.addr = netif->ip_addr.addr;
+  dhcp->msg_out->yiaddr.addr = 0;
+  dhcp->msg_out->siaddr.addr = 0;
+  dhcp->msg_out->giaddr.addr = 0;
+  for (i = 0; i < DHCP_CHADDR_LEN; i++) {
+    /* copy netif hardware address, pad with zeroes */
+    dhcp->msg_out->chaddr[i] = (i < netif->hwaddr_len) ? netif->hwaddr[i] : 0/* pad byte*/;
+  }
+  for (i = 0; i < DHCP_SNAME_LEN; i++) dhcp->msg_out->sname[i] = 0;
+  for (i = 0; i < DHCP_FILE_LEN; i++) dhcp->msg_out->file[i] = 0;
+  dhcp->msg_out->cookie = htonl(0x63825363UL);
+  dhcp->options_out_len = 0;
+  /* fill options field with an incrementing array (for debugging purposes) */
+  for (i = 0; i < DHCP_OPTIONS_LEN; i++) dhcp->msg_out->options[i] = i;
+  return ERR_OK;
+}
+
+static void dhcp_delete_request(struct netif *netif)
+{
+  struct dhcp *dhcp = netif->dhcp;
+  LWIP_ASSERT("dhcp_free_msg: dhcp->p_out != NULL", dhcp->p_out != NULL);
+  LWIP_ASSERT("dhcp_free_msg: dhcp->msg_out != NULL", dhcp->msg_out != NULL);
+  pbuf_free(dhcp->p_out);
+  dhcp->p_out = NULL;
+  dhcp->msg_out = NULL;
+}
+
+/**
+ * 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(struct dhcp *dhcp)
+{
+  LWIP_ASSERT("dhcp_option_trailer: dhcp->msg_out != NULL\n", dhcp->msg_out != NULL);
+  LWIP_ASSERT("dhcp_option_trailer: dhcp->options_out_len < DHCP_OPTIONS_LEN\n", dhcp->options_out_len < DHCP_OPTIONS_LEN);
+  dhcp->msg_out->options[dhcp->options_out_len++] = DHCP_OPTION_END;
+  /* packet is too small, or not 4 byte aligned? */
+  while ((dhcp->options_out_len < DHCP_MIN_OPTIONS_LEN) || (dhcp->options_out_len & 3)) {
+    /* LWIP_DEBUGF(DHCP_DEBUG,("dhcp_option_trailer:dhcp->options_out_len=%"U16_F", DHCP_OPTIONS_LEN=%"U16_F, dhcp->options_out_len, DHCP_OPTIONS_LEN)); */
+    LWIP_ASSERT("dhcp_option_trailer: dhcp->options_out_len < DHCP_OPTIONS_LEN\n", dhcp->options_out_len < DHCP_OPTIONS_LEN);
+    /* add a fill/padding byte */
+    dhcp->msg_out->options[dhcp->options_out_len++] = 0;
+  }
+}
+
+/**
+ * Find the offset of a DHCP option inside the DHCP message.
+ *
+ * @param client DHCP client
+ * @param option_type
+ *
+ * @return a byte offset into the UDP message where the option was found, or
+ * zero if the given option was not found.
+ */
+static u8_t *dhcp_get_option_ptr(struct dhcp *dhcp, u8_t option_type)
+{
+  u8_t overload = DHCP_OVERLOAD_NONE;
+
+  /* options available? */
+  if ((dhcp->options_in != NULL) && (dhcp->options_in_len > 0)) {
+    /* start with options field */
+    u8_t *options = (u8_t *)dhcp->options_in;
+    u16_t offset = 0;
+    /* at least 1 byte to read and no end marker, then at least 3 bytes to read? */
+    while ((offset < dhcp->options_in_len) && (options[offset] != DHCP_OPTION_END)) {
+      /* LWIP_DEBUGF(DHCP_DEBUG, ("msg_offset=%"U16_F", q->len=%"U16_F, msg_offset, q->len)); */
+      /* are the sname and/or file field overloaded with options? */
+      if (options[offset] == DHCP_OPTION_OVERLOAD) {
+        LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 2, ("overloaded message detected\n"));
+        /* skip option type and length */
+        offset += 2;
+        overload = options[offset++];
+      }
+      /* requested option found */
+      else if (options[offset] == option_type) {
+        LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE, ("option found at offset %"U16_F" in options\n", offset));
+        return &options[offset];
+      /* skip option */
+      } else {
+         LWIP_DEBUGF(DHCP_DEBUG, ("skipping option %"U16_F" in options\n", options[offset]));
+        /* skip option type */
+        offset++;
+        /* skip option length, and then length bytes */
+        offset += 1 + options[offset];
+      }
+    }
+    /* is this an overloaded message? */
+    if (overload != DHCP_OVERLOAD_NONE) {
+      u16_t field_len;
+      if (overload == DHCP_OVERLOAD_FILE) {
+        LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 1, ("overloaded file field\n"));
+        options = (u8_t *)&dhcp->msg_in->file;
+        field_len = DHCP_FILE_LEN;
+      } else if (overload == DHCP_OVERLOAD_SNAME) {
+        LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 1, ("overloaded sname field\n"));
+        options = (u8_t *)&dhcp->msg_in->sname;
+        field_len = DHCP_SNAME_LEN;
+      /* TODO: check if else if () is necessary */
+      } else {
+        LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 1, ("overloaded sname and file field\n"));
+        options = (u8_t *)&dhcp->msg_in->sname;
+        field_len = DHCP_FILE_LEN + DHCP_SNAME_LEN;
+      }
+      offset = 0;
+
+      /* at least 1 byte to read and no end marker */
+      while ((offset < field_len) && (options[offset] != DHCP_OPTION_END)) {
+        if (options[offset] == option_type) {
+           LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE, ("option found at offset=%"U16_F"\n", offset));
+          return &options[offset];
+        /* skip option */
+        } else {
+          LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE, ("skipping option %"U16_F"\n", options[offset]));
+          /* skip option type */
+          offset++;
+          offset += 1 + options[offset];
+        }
+      }
+    }
+  }
+  return 0;
+}
+
+/**
+ * Return the byte of DHCP option data.
+ *
+ * @param client DHCP client.
+ * @param ptr pointer obtained by dhcp_get_option_ptr().
+ *
+ * @return byte value at the given address.
+ */
+static u8_t dhcp_get_option_byte(u8_t *ptr)
+{
+  LWIP_DEBUGF(DHCP_DEBUG, ("option byte value=%"U16_F"\n", (u16_t)(*ptr)));
+  return *ptr;
+}
+
+/**
+ * Return the 16-bit value of DHCP option data.
+ *
+ * @param client DHCP client.
+ * @param ptr pointer obtained by dhcp_get_option_ptr().
+ *
+ * @return byte value at the given address.
+ */
+static u16_t dhcp_get_option_short(u8_t *ptr)
+{
+  u16_t value;
+  value = *ptr++ << 8;
+  value |= *ptr;
+  LWIP_DEBUGF(DHCP_DEBUG, ("option short value=%"U16_F"\n", value));
+  return value;
+}
+
+/**
+ * Return the 32-bit value of DHCP option data.
+ *
+ * @param client DHCP client.
+ * @param ptr pointer obtained by dhcp_get_option_ptr().
+ *
+ * @return byte value at the given address.
+ */
+static u32_t dhcp_get_option_long(u8_t *ptr)
+{
+  u32_t value;
+  value = (u32_t)(*ptr++) << 24;
+  value |= (u32_t)(*ptr++) << 16;
+  value |= (u32_t)(*ptr++) << 8;
+  value |= (u32_t)(*ptr++);
+  LWIP_DEBUGF(DHCP_DEBUG, ("option long value=%"U32_F"\n", value));
+  return value;
+}
+
+#endif /* LWIP_DHCP */
diff --git a/lib/lwip/src/core/inet.c b/lib/lwip/src/core/inet.c
new file mode 100644
index 0000000..556cbeb
--- /dev/null
+++ b/lib/lwip/src/core/inet.c
@@ -0,0 +1,525 @@
+/*
+ * 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>
+ *
+ */
+
+
+/* inet.c
+ *
+ * Functions common to all TCP/IP modules, such as the Internet checksum and the
+ * byte order functions.
+ *
+ */
+
+
+#include "lwip/opt.h"
+
+#include "lwip/arch.h"
+
+#include "lwip/def.h"
+#include "lwip/inet.h"
+
+#include "lwip/sys.h"
+
+/* This is a reference implementation 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. You will
+ * need to port it to your architecture and in your sys_arch.h:
+ * 
+ * #define LWIP_CHKSUM <your_checksum_routine> 
+*/
+#ifndef LWIP_CHKSUM
+#define LWIP_CHKSUM lwip_standard_chksum
+
+/**
+ * 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 lenght to 64k
+ * @note host endianess is irrelevant (p3 RFC1071)
+ */
+static u16_t
+lwip_standard_chksum(void *dataptr, u16_t len)
+{
+  u32_t acc;
+  u16_t src;
+  u8_t *octetptr;
+
+  acc = 0;
+  /* dataptr may be at odd or even addresses */
+  octetptr = (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 & 0xffff0000) != 0) {
+    acc = (acc >> 16) + (acc & 0x0000ffffUL);
+  }
+  /* This maybe a little confusing: reorder sum using htons()
+     instead of ntohs() since it has a little less call overhead.
+     The caller must invert bits for Internet sum ! */
+  return htons((u16_t)acc);
+}
+
+#endif
+
+#if 0
+/*
+ * 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
+ */
+
+static u16_t
+lwip_standard_chksum2(void *dataptr, int len)
+{
+  u8_t *pb = dataptr;
+  u16_t *ps, t = 0;
+  u32_t sum = 0;
+  int odd = ((u32_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 = (u16_t *)pb;
+  while (len > 1) {
+    sum += *ps++;
+    len -= 2;
+  }
+
+  /* Consume left-over byte, if any */
+  if (len > 0)
+    ((u8_t *)&t)[0] = *(u8_t *)ps;;
+
+  /* Add end bytes */
+  sum += t;
+
+  /*  Fold 32-bit sum to 16 bits */
+  while (sum >> 16)
+    sum = (sum & 0xffff) + (sum >> 16);
+
+  /* Swap if alignment was odd */
+  if (odd)
+    sum = ((sum & 0xff) << 8) | ((sum & 0xff00) >> 8);
+
+  return sum;
+}
+
+/**
+ * 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.
+ * 
+ * @todo First argument type conflicts with generic checksum routine.
+ * 
+ * by Curt McDowell, Broadcom Corp. December 8th, 2005
+ */
+
+static u16_t
+lwip_standard_chksum4(u8_t *pb, int len)
+{
+  u16_t *ps, t = 0;
+  u32_t *pl;
+  u32_t sum = 0, tmp;
+  /* starts at odd byte address? */
+  int odd = ((u32_t)pb & 1);
+
+  if (odd && len > 0) {
+    ((u8_t *)&t)[1] = *pb++;
+    len--;
+  }
+
+  ps = (u16_t *)pb;
+
+  if (((u32_t)ps & 3) && len > 1) {
+    sum += *ps++;
+    len -= 2;
+  }
+
+  pl = (u32_t *)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 = (sum >> 16) + (sum & 0xffff);
+
+  ps = (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] = *(u8_t *)ps;
+
+  sum += t;                     /* add end bytes */
+
+  while (sum >> 16)             /* combine halves */
+    sum = (sum >> 16) + (sum & 0xffff);
+
+  if (odd)
+    sum = ((sum & 0xff) << 8) | ((sum & 0xff00) >> 8);
+
+  return sum;
+}
+#endif
+
+/* inet_chksum_pseudo:
+ *
+ * Calculates the pseudo Internet checksum used by TCP and UDP for a pbuf chain.
+ */
+
+u16_t
+inet_chksum_pseudo(struct pbuf *p,
+       struct ip_addr *src, struct ip_addr *dest,
+       u8_t proto, u16_t proto_len)
+{
+  u32_t acc;
+  struct pbuf *q;
+  u8_t swapped;
+
+  acc = 0;
+  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));*/
+    while (acc >> 16) {
+      acc = (acc & 0xffffUL) + (acc >> 16);
+    }
+    if (q->len % 2 != 0) {
+      swapped = 1 - swapped;
+      acc = ((acc & 0xff) << 8) | ((acc & 0xff00UL) >> 8);
+    }
+    /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): wrapped lwip_chksum()=%"X32_F" \n", acc));*/
+  }
+
+  if (swapped) {
+    acc = ((acc & 0xff) << 8) | ((acc & 0xff00UL) >> 8);
+  }
+  acc += (src->addr & 0xffffUL);
+  acc += ((src->addr >> 16) & 0xffffUL);
+  acc += (dest->addr & 0xffffUL);
+  acc += ((dest->addr >> 16) & 0xffffUL);
+  acc += (u32_t)htons((u16_t)proto);
+  acc += (u32_t)htons(proto_len);
+
+  while (acc >> 16) {
+    acc = (acc & 0xffffUL) + (acc >> 16);
+  }
+  LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): pbuf chain lwip_chksum()=%"X32_F"\n", acc));
+  return (u16_t)~(acc & 0xffffUL);
+}
+
+/* inet_chksum:
+ *
+ * Calculates the Internet checksum over a portion of memory. Used primarely for IP
+ * and ICMP.
+ */
+
+u16_t
+inet_chksum(void *dataptr, u16_t len)
+{
+  u32_t acc;
+
+  acc = LWIP_CHKSUM(dataptr, len);
+  while (acc >> 16) {
+    acc = (acc & 0xffff) + (acc >> 16);
+  }
+  return (u16_t)~(acc & 0xffff);
+}
+
+u16_t
+inet_chksum_pbuf(struct pbuf *p)
+{
+  u32_t acc;
+  struct pbuf *q;
+  u8_t swapped;
+
+  acc = 0;
+  swapped = 0;
+  for(q = p; q != NULL; q = q->next) {
+    acc += LWIP_CHKSUM(q->payload, q->len);
+    while (acc >> 16) {
+      acc = (acc & 0xffffUL) + (acc >> 16);
+    }
+    if (q->len % 2 != 0) {
+      swapped = 1 - swapped;
+      acc = (acc & 0x00ffUL << 8) | (acc & 0xff00UL >> 8);
+    }
+  }
+
+  if (swapped) {
+    acc = ((acc & 0x00ffUL) << 8) | ((acc & 0xff00UL) >> 8);
+  }
+  return (u16_t)~(acc & 0xffffUL);
+}
+
+/* Here for now until needed in other places in lwIP */
+#ifndef isascii
+#define in_range(c, lo, up)  ((u8_t)c >= lo && (u8_t)c <= up)
+#define isascii(c)           in_range(c, 0x20, 0x7f)
+#define isdigit(c)           in_range(c, '0', '9')
+#define isxdigit(c)          (isdigit(c) || in_range(c, 'a', 'f') || in_range(c, 'A', 'F'))
+#define islower(c)           in_range(c, 'a', 'z')
+#define isspace(c)           (c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || c == '\v')
+#endif		
+		
+
+ /*
+  * Ascii internet address interpretation routine.
+  * The value returned is in network order.
+  */
+
+ /*  */
+ /* inet_addr */
+ u32_t inet_addr(const char *cp)
+ {
+     struct in_addr val;
+
+     if (inet_aton(cp, &val)) {
+         return (val.s_addr);
+     }
+     return (INADDR_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.
+  */
+ /*  */
+ /* inet_aton */
+ s8_t
+ inet_aton(const char *cp, struct in_addr *addr)
+ {
+     u32_t val;
+     s32_t base, n;
+     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, isdigit=decimal.
+          */
+         if (!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 (isdigit(c)) {
+                 val = (val * base) + (s16_t)(c - '0');
+                 c = *++cp;
+             } else if (base == 16 && isxdigit(c)) {
+                 val = (val << 4) |
+                     (s16_t)(c + 10 - (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' && (!isascii(c) || !isspace(c)))
+         return (0);
+     /*
+      * Concoct the address according to
+      * the number of parts specified.
+      */
+     n = pp - parts + 1;
+     switch (n) {
+
+     case 0:
+         return (0);     /* initial nondigit */
+
+     case 1:             /* a -- 32 bits */
+         break;
+
+     case 2:             /* a.b -- 8.24 bits */
+         if (val > 0xffffff)
+             return (0);
+         val |= parts[0] << 24;
+         break;
+
+     case 3:             /* a.b.c -- 8.8.16 bits */
+         if (val > 0xffff)
+             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);
+         val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8);
+         break;
+     }
+     if (addr)
+         addr->s_addr = htonl(val);
+     return (1);
+ }
+
+/* Convert numeric IP address into decimal dotted ASCII representation.
+ * returns ptr to static buffer; not reentrant!
+ */
+char *inet_ntoa(struct in_addr addr)
+{
+  static char str[16];
+  u32_t s_addr = addr.s_addr;
+  char inv[3];
+  char *rp;
+  u8_t *ap;
+  u8_t rem;
+  u8_t n;
+  u8_t i;
+
+  rp = str;
+  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++] = '0' + rem;
+    } while(*ap);
+    while(i--)
+      *rp++ = inv[i];
+    *rp++ = '.';
+    ap++;
+  }
+  *--rp = 0;
+  return str;
+}
+
+
+#ifndef BYTE_ORDER
+#error BYTE_ORDER is not defined
+#endif
+#if BYTE_ORDER == LITTLE_ENDIAN
+
+u16_t
+htons(u16_t n)
+{
+  return ((n & 0xff) << 8) | ((n & 0xff00) >> 8);
+}
+
+u16_t
+ntohs(u16_t n)
+{
+  return htons(n);
+}
+
+u32_t
+htonl(u32_t n)
+{
+  return ((n & 0xff) << 24) |
+    ((n & 0xff00) << 8) |
+    ((n & 0xff0000) >> 8) |
+    ((n & 0xff000000) >> 24);
+}
+
+u32_t
+ntohl(u32_t n)
+{
+  return htonl(n);
+}
+
+#endif /* BYTE_ORDER == LITTLE_ENDIAN */
diff --git a/lib/lwip/src/core/inet6.c b/lib/lwip/src/core/inet6.c
new file mode 100644
index 0000000..c04915b
--- /dev/null
+++ b/lib/lwip/src/core/inet6.c
@@ -0,0 +1,168 @@
+/*
+ * 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>
+ *
+ */
+
+
+/* inet6.c
+ *
+ * Functions common to all TCP/IP modules, such as the Internet checksum and the
+ * byte order functions.
+ *
+ */
+
+
+#include "lwip/opt.h"
+
+#include "lwip/def.h"
+#include "lwip/inet.h"
+
+
+
+/* chksum:
+ *
+ * Sums up all 16 bit words in a memory portion. Also includes any odd byte.
+ * This function is used by the other checksum functions.
+ *
+ * For now, this is not optimized. Must be optimized for the particular processor
+ * arcitecture on which it is to run. Preferebly coded in assembler.
+ */
+
+static u32_t
+chksum(void *dataptr, u16_t len)
+{
+  u16_t *sdataptr = dataptr;
+  u32_t acc;
+  
+  
+  for(acc = 0; len > 1; len -= 2) {
+    acc += *sdataptr++;
+  }
+
+  /* add up any odd byte */
+  if (len == 1) {
+    acc += htons((u16_t)(*(u8_t *)dataptr) << 8);
+  }
+
+  return acc;
+
+}
+
+/* inet_chksum_pseudo:
+ *
+ * Calculates the pseudo Internet checksum used by TCP and UDP for a pbuf chain.
+ */
+
+u16_t
+inet_chksum_pseudo(struct pbuf *p,
+       struct ip_addr *src, struct ip_addr *dest,
+       u8_t proto, u32_t proto_len)
+{
+  u32_t acc;
+  struct pbuf *q;
+  u8_t swapped, i;
+
+  acc = 0;
+  swapped = 0;
+  for(q = p; q != NULL; q = q->next) {    
+    acc += chksum(q->payload, q->len);
+    while (acc >> 16) {
+      acc = (acc & 0xffff) + (acc >> 16);
+    }
+    if (q->len % 2 != 0) {
+      swapped = 1 - swapped;
+      acc = ((acc & 0xff) << 8) | ((acc & 0xff00) >> 8);
+    }
+  }
+
+  if (swapped) {
+    acc = ((acc & 0xff) << 8) | ((acc & 0xff00) >> 8);
+  }
+  
+  for(i = 0; i < 8; i++) {
+    acc += ((u16_t *)src->addr)[i] & 0xffff;
+    acc += ((u16_t *)dest->addr)[i] & 0xffff;
+    while (acc >> 16) {
+      acc = (acc & 0xffff) + (acc >> 16);
+    }
+  }
+  acc += (u16_t)htons((u16_t)proto);
+  acc += ((u16_t *)&proto_len)[0] & 0xffff;
+  acc += ((u16_t *)&proto_len)[1] & 0xffff;
+
+  while (acc >> 16) {
+    acc = (acc & 0xffff) + (acc >> 16);
+  }
+  return ~(acc & 0xffff);
+}
+
+/* inet_chksum:
+ *
+ * Calculates the Internet checksum over a portion of memory. Used primarely for IP
+ * and ICMP.
+ */
+
+u16_t
+inet_chksum(void *dataptr, u16_t len)
+{
+  u32_t acc, sum;
+
+  acc = chksum(dataptr, len);
+  sum = (acc & 0xffff) + (acc >> 16);
+  sum += (sum >> 16);
+  return ~(sum & 0xffff);
+}
+
+u16_t
+inet_chksum_pbuf(struct pbuf *p)
+{
+  u32_t acc;
+  struct pbuf *q;
+  u8_t swapped;
+  
+  acc = 0;
+  swapped = 0;
+  for(q = p; q != NULL; q = q->next) {
+    acc += chksum(q->payload, q->len);
+    while (acc >> 16) {
+      acc = (acc & 0xffff) + (acc >> 16);
+    }    
+    if (q->len % 2 != 0) {
+      swapped = 1 - swapped;
+      acc = (acc & 0xff << 8) | (acc & 0xff00 >> 8);
+    }
+  }
+ 
+  if (swapped) {
+    acc = ((acc & 0xff) << 8) | ((acc & 0xff00) >> 8);
+  }
+  return ~(acc & 0xffff);
+}
+
diff --git a/lib/lwip/src/core/ipv4/icmp.c b/lib/lwip/src/core/ipv4/icmp.c
new file mode 100644
index 0000000..db82014
--- /dev/null
+++ b/lib/lwip/src/core/ipv4/icmp.c
@@ -0,0 +1,202 @@
+/*
+ * 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 <string.h>
+
+#include "lwip/opt.h"
+#include "lwip/icmp.h"
+#include "lwip/inet.h"
+#include "lwip/ip.h"
+#include "lwip/def.h"
+#include "lwip/stats.h"
+#include "lwip/snmp.h"
+
+void
+icmp_input(struct pbuf *p, struct netif *inp)
+{
+  u8_t type;
+  u8_t code;
+  struct icmp_echo_hdr *iecho;
+  struct ip_hdr *iphdr;
+  struct ip_addr tmpaddr;
+  u16_t hlen;
+
+  ICMP_STATS_INC(icmp.recv);
+  snmp_inc_icmpinmsgs();
+
+
+  iphdr = p->payload;
+  hlen = IPH_HL(iphdr) * 4;
+  if (pbuf_header(p, -((s16_t)hlen)) || (p->tot_len < sizeof(u16_t)*2)) {
+    LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: short ICMP (%"U16_F" bytes) received\n", p->tot_len));
+    pbuf_free(p);
+    ICMP_STATS_INC(icmp.lenerr);
+    snmp_inc_icmpinerrors();
+    return;
+  }
+
+  type = *((u8_t *)p->payload);
+  code = *(((u8_t *)p->payload)+1);
+  switch (type) {
+  case ICMP_ECHO:
+    /* broadcast or multicast destination address? */
+    if (ip_addr_isbroadcast(&iphdr->dest, inp) || ip_addr_ismulticast(&iphdr->dest)) {
+      LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: Not echoing to multicast or broadcast pings\n"));
+      ICMP_STATS_INC(icmp.err);
+      pbuf_free(p);
+      return;
+    }
+    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"));
+      pbuf_free(p);
+      ICMP_STATS_INC(icmp.lenerr);
+      snmp_inc_icmpinerrors();
+
+      return;
+    }
+    iecho = p->payload;
+    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);
+      snmp_inc_icmpinerrors();
+      return;
+    }
+    tmpaddr.addr = iphdr->src.addr;
+    iphdr->src.addr = iphdr->dest.addr;
+    iphdr->dest.addr = tmpaddr.addr;
+    ICMPH_TYPE_SET(iecho, ICMP_ER);
+    /* adjust the checksum */
+    if (iecho->chksum >= htons(0xffff - (ICMP_ECHO << 8))) {
+      iecho->chksum += htons(ICMP_ECHO << 8) + 1;
+    } else {
+      iecho->chksum += htons(ICMP_ECHO << 8);
+    }
+    ICMP_STATS_INC(icmp.xmit);
+    /* increase number of messages attempted to send */
+    snmp_inc_icmpoutmsgs();
+    /* increase number of echo replies attempted to send */
+    snmp_inc_icmpoutechoreps();
+
+    pbuf_header(p, hlen);
+    ip_output_if(p, &(iphdr->src), IP_HDRINCL,
+		 IPH_TTL(iphdr), 0, IP_PROTO_ICMP, inp);
+    break;
+  default:
+  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);
+}
+
+void
+icmp_dest_unreach(struct pbuf *p, enum icmp_dur_type t)
+{
+  struct pbuf *q;
+  struct ip_hdr *iphdr;
+  struct icmp_dur_hdr *idur;
+
+  q = pbuf_alloc(PBUF_IP, 8 + IP_HLEN + 8, PBUF_RAM);
+  /* ICMP header + IP header + 8 bytes of data */
+
+  iphdr = p->payload;
+
+  idur = q->payload;
+  ICMPH_TYPE_SET(idur, ICMP_DUR);
+  ICMPH_CODE_SET(idur, t);
+
+  memcpy((u8_t *)q->payload + 8, p->payload, IP_HLEN + 8);
+
+  /* calculate checksum */
+  idur->chksum = 0;
+  idur->chksum = inet_chksum(idur, q->len);
+  ICMP_STATS_INC(icmp.xmit);
+  /* increase number of messages attempted to send */
+  snmp_inc_icmpoutmsgs();
+  /* increase number of destination unreachable messages attempted to send */
+  snmp_inc_icmpoutdestunreachs();
+
+  ip_output(q, NULL, &(iphdr->src),
+	    ICMP_TTL, 0, IP_PROTO_ICMP);
+  pbuf_free(q);
+}
+
+#if IP_FORWARD
+void
+icmp_time_exceeded(struct pbuf *p, enum icmp_te_type t)
+{
+  struct pbuf *q;
+  struct ip_hdr *iphdr;
+  struct icmp_te_hdr *tehdr;
+
+  q = pbuf_alloc(PBUF_IP, 8 + IP_HLEN + 8, PBUF_RAM);
+
+  iphdr = p->payload;
+  LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded from "));
+  ip_addr_debug_print(ICMP_DEBUG, &(iphdr->src));
+  LWIP_DEBUGF(ICMP_DEBUG, (" to "));
+  ip_addr_debug_print(ICMP_DEBUG, &(iphdr->dest));
+  LWIP_DEBUGF(ICMP_DEBUG, ("\n"));
+
+  tehdr = q->payload;
+  ICMPH_TYPE_SET(tehdr, ICMP_TE);
+  ICMPH_CODE_SET(tehdr, t);
+
+  /* copy fields from original packet */
+  memcpy((u8_t *)q->payload + 8, (u8_t *)p->payload, IP_HLEN + 8);
+
+  /* calculate checksum */
+  tehdr->chksum = 0;
+  tehdr->chksum = inet_chksum(tehdr, q->len);
+  ICMP_STATS_INC(icmp.xmit);
+  /* increase number of messages attempted to send */
+  snmp_inc_icmpoutmsgs();
+  /* increase number of destination unreachable messages attempted to send */
+  snmp_inc_icmpouttimeexcds();
+  ip_output(q, NULL, &(iphdr->src),
+	    ICMP_TTL, 0, IP_PROTO_ICMP);
+  pbuf_free(q);
+}
+
+#endif /* IP_FORWARD */
+
+
+
+
+
+
+
diff --git a/lib/lwip/src/core/ipv4/ip.c b/lib/lwip/src/core/ipv4/ip.c
new file mode 100644
index 0000000..4db68c8
--- /dev/null
+++ b/lib/lwip/src/core/ipv4/ip.c
@@ -0,0 +1,508 @@
+/* @file
+ *
+ * This is the IP 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"
+
+#include "lwip/def.h"
+#include "lwip/mem.h"
+#include "lwip/ip.h"
+#include "lwip/ip_frag.h"
+#include "lwip/inet.h"
+#include "lwip/netif.h"
+#include "lwip/icmp.h"
+#include "lwip/raw.h"
+#include "lwip/udp.h"
+#include "lwip/tcp.h"
+
+#include "lwip/stats.h"
+
+#include "arch/perf.h"
+
+#include "lwip/snmp.h"
+#if LWIP_DHCP
+#  include "lwip/dhcp.h"
+#endif /* LWIP_DHCP */
+
+
+/**
+ * Initializes the IP layer.
+ */
+
+void
+ip_init(void)
+{
+  /* no initializations as of yet */
+}
+
+/**
+ * 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.
+ */
+
+struct netif *
+ip_route(struct ip_addr *dest)
+{
+  struct netif *netif;
+
+  /* iterate through netifs */
+  for(netif = netif_list; netif != NULL; netif = netif->next) {
+    /* network mask matches? */
+    if (ip_addr_netcmp(dest, &(netif->ip_addr), &(netif->netmask))) {
+      /* return netif on which to forward IP packet */
+      return netif;
+    }
+  }
+  /* no matching netif found, use default netif */
+  return netif_default;
+}
+#if IP_FORWARD
+
+/**
+ * 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.
+ */
+
+static struct netif *
+ip_forward(struct pbuf *p, struct ip_hdr *iphdr, struct netif *inp)
+{
+  struct netif *netif;
+
+  PERF_START;
+  /* Find network interface where to forward this IP packet to. */
+  netif = ip_route((struct ip_addr *)&(iphdr->dest));
+  if (netif == NULL) {
+    LWIP_DEBUGF(IP_DEBUG, ("ip_forward: no forwarding route for 0x%"X32_F" found\n",
+                      iphdr->dest.addr));
+    snmp_inc_ipnoroutes();
+    return (struct netif *)NULL;
+  }
+  /* Do not forward packets onto the same network interface on which
+   * they arrived. */
+  if (netif == inp) {
+    LWIP_DEBUGF(IP_DEBUG, ("ip_forward: not bouncing packets back on incoming interface.\n"));
+    snmp_inc_ipnoroutes();
+    return (struct netif *)NULL;
+  }
+
+  /* decrement TTL */
+  IPH_TTL_SET(iphdr, IPH_TTL(iphdr) - 1);
+  /* send ICMP if TTL == 0 */
+  if (IPH_TTL(iphdr) == 0) {
+    /* Don't send ICMP messages in response to ICMP messages */
+    if (IPH_PROTO(iphdr) != IP_PROTO_ICMP) {
+      icmp_time_exceeded(p, ICMP_TE_TTL);
+      snmp_inc_icmpouttimeexcds();
+    }
+    return (struct netif *)NULL;
+  }
+
+  /* Incrementally update the IP checksum. */
+  if (IPH_CHKSUM(iphdr) >= htons(0xffff - 0x100)) {
+    IPH_CHKSUM_SET(iphdr, IPH_CHKSUM(iphdr) + htons(0x100) + 1);
+  } else {
+    IPH_CHKSUM_SET(iphdr, IPH_CHKSUM(iphdr) + htons(0x100));
+  }
+
+  LWIP_DEBUGF(IP_DEBUG, ("ip_forward: forwarding packet to 0x%"X32_F"\n",
+                    iphdr->dest.addr));
+
+  IP_STATS_INC(ip.fw);
+  IP_STATS_INC(ip.xmit);
+    snmp_inc_ipforwdatagrams();
+
+  PERF_STOP("ip_forward");
+  /* transmit pbuf on chosen interface */
+  netif->output(netif, p, (struct ip_addr *)&(iphdr->dest));
+  return netif;
+}
+#endif /* IP_FORWARD */
+
+/**
+ * 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.
+ * 
+ * 
+ * 
+ */
+
+err_t
+ip_input(struct pbuf *p, struct netif *inp) {
+  struct ip_hdr *iphdr;
+  struct netif *netif;
+  u16_t iphdrlen;
+
+  IP_STATS_INC(ip.recv);
+  snmp_inc_ipinreceives();
+
+  /* identify the IP header */
+  iphdr = p->payload;
+  if (IPH_V(iphdr) != 4) {
+    LWIP_DEBUGF(IP_DEBUG | 1, ("IP packet dropped due to bad version number %"U16_F"\n", IPH_V(iphdr)));
+    ip_debug_print(p);
+    pbuf_free(p);
+    IP_STATS_INC(ip.err);
+    IP_STATS_INC(ip.drop);
+    snmp_inc_ipunknownprotos();
+    return ERR_OK;
+  }
+  /* obtain IP header length in number of 32-bit words */
+  iphdrlen = IPH_HL(iphdr);
+  /* calculate IP header length in bytes */
+  iphdrlen *= 4;
+
+  /* header length exceeds first pbuf length? */
+  if (iphdrlen > p->len) {
+    LWIP_DEBUGF(IP_DEBUG | 2, ("IP header (len %"U16_F") does not fit in first pbuf (len %"U16_F"), IP packet droppped.\n",
+      iphdrlen, p->len));
+    /* free (drop) packet pbufs */
+    pbuf_free(p);
+    IP_STATS_INC(ip.lenerr);
+    IP_STATS_INC(ip.drop);
+    snmp_inc_ipindiscards();
+    return ERR_OK;
+  }
+
+  /* verify checksum */
+#if CHECKSUM_CHECK_IP
+  if (inet_chksum(iphdr, iphdrlen) != 0) {
+
+    LWIP_DEBUGF(IP_DEBUG | 2, ("Checksum (0x%"X16_F") failed, IP packet dropped.\n", inet_chksum(iphdr, iphdrlen)));
+    ip_debug_print(p);
+    pbuf_free(p);
+    IP_STATS_INC(ip.chkerr);
+    IP_STATS_INC(ip.drop);
+    snmp_inc_ipindiscards();
+    return ERR_OK;
+  }
+#endif
+
+  /* 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, ntohs(IPH_LEN(iphdr)));
+
+  /* match packet against an interface, i.e. is this packet for us? */
+  for (netif = netif_list; netif != NULL; netif = netif->next) {
+
+    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",
+      iphdr->dest.addr, netif->ip_addr.addr,
+      iphdr->dest.addr & netif->netmask.addr,
+      netif->ip_addr.addr & netif->netmask.addr,
+      iphdr->dest.addr & ~(netif->netmask.addr)));
+
+    /* interface is up and configured? */
+    if ((netif_is_up(netif)) && (!ip_addr_isany(&(netif->ip_addr))))
+    {
+      /* unicast to this interface address? */
+      if (ip_addr_cmp(&(iphdr->dest), &(netif->ip_addr)) ||
+         /* or broadcast on this interface network address? */
+         ip_addr_isbroadcast(&(iphdr->dest), netif)) {
+        LWIP_DEBUGF(IP_DEBUG, ("ip_input: packet accepted on interface %c%c\n",
+          netif->name[0], netif->name[1]));
+        /* break out of for loop */
+        break;
+      }
+    }
+  }
+#if LWIP_DHCP
+  /* 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 (netif == NULL) {
+    /* remote port is DHCP server? */
+    if (IPH_PROTO(iphdr) == IP_PROTO_UDP) {
+      LWIP_DEBUGF(IP_DEBUG | DBG_TRACE | 1, ("ip_input: UDP packet to DHCP client port %"U16_F"\n",
+        ntohs(((struct udp_hdr *)((u8_t *)iphdr + iphdrlen))->dest)));
+      if (ntohs(((struct udp_hdr *)((u8_t *)iphdr + iphdrlen))->dest) == DHCP_CLIENT_PORT) {
+        LWIP_DEBUGF(IP_DEBUG | DBG_TRACE | 1, ("ip_input: DHCP packet accepted.\n"));
+        netif = inp;
+      }
+    }
+  }
+#endif /* LWIP_DHCP */
+  /* packet not for us? */
+  if (netif == NULL) {
+    /* packet not for us, route or discard */
+    LWIP_DEBUGF(IP_DEBUG | DBG_TRACE | 1, ("ip_input: packet not for us.\n"));
+#if IP_FORWARD
+    /* non-broadcast packet? */
+    if (!ip_addr_isbroadcast(&(iphdr->dest), inp)) {
+      /* try to forward IP packet on (other) interfaces */
+      ip_forward(p, iphdr, inp);
+    }
+    else
+#endif /* IP_FORWARD */
+    {
+      snmp_inc_ipindiscards();
+    }
+    pbuf_free(p);
+    return ERR_OK;
+  }
+  /* packet consists of multiple fragments? */
+  if ((IPH_OFFSET(iphdr) & 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 ip_reass()\n",
+      ntohs(IPH_ID(iphdr)), p->tot_len, ntohs(IPH_LEN(iphdr)), !!(IPH_OFFSET(iphdr) & htons(IP_MF)), (ntohs(IPH_OFFSET(iphdr)) & IP_OFFMASK)*8));
+    /* reassemble the packet*/
+    p = ip_reass(p);
+    /* packet not fully reassembled yet? */
+    if (p == NULL) {
+      return ERR_OK;
+    }
+    iphdr = p->payload;
+#else /* IP_REASSEMBLY == 0, no packet fragment reassembly code present */
+    pbuf_free(p);
+    LWIP_DEBUGF(IP_DEBUG | 2, ("IP packet dropped since it was fragmented (0x%"X16_F") (while IP_REASSEMBLY == 0).\n",
+      ntohs(IPH_OFFSET(iphdr))));
+    IP_STATS_INC(ip.opterr);
+    IP_STATS_INC(ip.drop);
+    snmp_inc_ipunknownprotos();
+    return ERR_OK;
+#endif /* IP_REASSEMBLY */
+  }
+
+#if IP_OPTIONS == 0 /* no support for IP options in the IP header? */
+  if (iphdrlen > IP_HLEN) {
+    LWIP_DEBUGF(IP_DEBUG | 2, ("IP packet dropped since there were IP options (while IP_OPTIONS == 0).\n"));
+    pbuf_free(p);
+    IP_STATS_INC(ip.opterr);
+    IP_STATS_INC(ip.drop);
+    snmp_inc_ipunknownprotos();
+    return ERR_OK;
+  }
+#endif /* IP_OPTIONS == 0 */
+
+  /* send to upper layers */
+  LWIP_DEBUGF(IP_DEBUG, ("ip_input: \n"));
+  ip_debug_print(p);
+  LWIP_DEBUGF(IP_DEBUG, ("ip_input: p->len %"U16_F" p->tot_len %"U16_F"\n", p->len, p->tot_len));
+
+#if LWIP_RAW
+  /* raw input did not eat the packet? */
+  if (raw_input(p, inp) == 0) {
+#endif /* LWIP_RAW */
+
+  switch (IPH_PROTO(iphdr)) {
+#if LWIP_UDP
+  case IP_PROTO_UDP:
+  case IP_PROTO_UDPLITE:
+    snmp_inc_ipindelivers();
+    udp_input(p, inp);
+    break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+  case IP_PROTO_TCP:
+    snmp_inc_ipindelivers();
+    tcp_input(p, inp);
+    break;
+#endif /* LWIP_TCP */
+  case IP_PROTO_ICMP:
+    snmp_inc_ipindelivers();
+    icmp_input(p, inp);
+    break;
+  default:
+    /* send ICMP destination protocol unreachable unless is was a broadcast */
+    if (!ip_addr_isbroadcast(&(iphdr->dest), inp) &&
+        !ip_addr_ismulticast(&(iphdr->dest))) {
+      p->payload = iphdr;
+      icmp_dest_unreach(p, ICMP_DUR_PROTO);
+    }
+    pbuf_free(p);
+
+    LWIP_DEBUGF(IP_DEBUG | 2, ("Unsupported transport protocol %"U16_F"\n", IPH_PROTO(iphdr)));
+
+    IP_STATS_INC(ip.proterr);
+    IP_STATS_INC(ip.drop);
+    snmp_inc_ipunknownprotos();
+  }
+#if LWIP_RAW
+  } /* LWIP_RAW */
+#endif
+  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.
+ */
+
+err_t
+ip_output_if(struct pbuf *p, struct ip_addr *src, struct ip_addr *dest,
+             u8_t ttl, u8_t tos,
+             u8_t proto, struct netif *netif)
+{
+  struct ip_hdr *iphdr;
+  u16_t ip_id = 0;
+
+  snmp_inc_ipoutrequests();
+
+  if (dest != IP_HDRINCL) {
+    if (pbuf_header(p, IP_HLEN)) {
+      LWIP_DEBUGF(IP_DEBUG | 2, ("ip_output: not enough room for IP header in pbuf\n"));
+
+      IP_STATS_INC(ip.err);
+      snmp_inc_ipoutdiscards();
+      return ERR_BUF;
+    }
+
+    iphdr = p->payload;
+
+    IPH_TTL_SET(iphdr, ttl);
+    IPH_PROTO_SET(iphdr, proto);
+
+    ip_addr_set(&(iphdr->dest), dest);
+
+    IPH_VHLTOS_SET(iphdr, 4, IP_HLEN / 4, tos);
+    IPH_LEN_SET(iphdr, htons(p->tot_len));
+    IPH_OFFSET_SET(iphdr, htons(IP_DF));
+    IPH_ID_SET(iphdr, htons(ip_id));
+    ++ip_id;
+
+    if (ip_addr_isany(src)) {
+      ip_addr_set(&(iphdr->src), &(netif->ip_addr));
+    } else {
+      ip_addr_set(&(iphdr->src), src);
+    }
+
+    IPH_CHKSUM_SET(iphdr, 0);
+#if CHECKSUM_GEN_IP
+    IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN));
+#endif
+  } else {
+    iphdr = p->payload;
+    dest = &(iphdr->dest);
+  }
+
+#if IP_FRAG
+  /* don't fragment if interface has mtu set to 0 [loopif] */
+  if (netif->mtu && (p->tot_len > netif->mtu))
+    return ip_frag(p,netif,dest);
+#endif
+
+  IP_STATS_INC(ip.xmit);
+
+  LWIP_DEBUGF(IP_DEBUG, ("ip_output_if: %c%c%"U16_F"\n", netif->name[0], netif->name[1], netif->num));
+  ip_debug_print(p);
+
+  LWIP_DEBUGF(IP_DEBUG, ("netif->output()"));
+
+  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.
+ */
+
+err_t
+ip_output(struct pbuf *p, struct ip_addr *src, struct ip_addr *dest,
+          u8_t ttl, u8_t tos, u8_t proto)
+{
+  struct netif *netif;
+
+  if ((netif = ip_route(dest)) == NULL) {
+    LWIP_DEBUGF(IP_DEBUG | 2, ("ip_output: No route to 0x%"X32_F"\n", dest->addr));
+
+    IP_STATS_INC(ip.rterr);
+    snmp_inc_ipoutdiscards();
+    return ERR_RTE;
+  }
+
+  return ip_output_if(p, src, dest, ttl, tos, proto, netif);
+}
+
+#if IP_DEBUG
+void
+ip_debug_print(struct pbuf *p)
+{
+  struct ip_hdr *iphdr = p->payload;
+  u8_t *payload;
+
+  payload = (u8_t *)iphdr + IP_HLEN;
+
+  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",
+                    IPH_V(iphdr),
+                    IPH_HL(iphdr),
+                    IPH_TOS(iphdr),
+                    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",
+                    ntohs(IPH_ID(iphdr)),
+                    ntohs(IPH_OFFSET(iphdr)) >> 15 & 1,
+                    ntohs(IPH_OFFSET(iphdr)) >> 14 & 1,
+                    ntohs(IPH_OFFSET(iphdr)) >> 13 & 1,
+                    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",
+                    IPH_TTL(iphdr),
+                    IPH_PROTO(iphdr),
+                    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(&iphdr->src),
+                    ip4_addr2(&iphdr->src),
+                    ip4_addr3(&iphdr->src),
+                    ip4_addr4(&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(&iphdr->dest),
+                    ip4_addr2(&iphdr->dest),
+                    ip4_addr3(&iphdr->dest),
+                    ip4_addr4(&iphdr->dest)));
+  LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
+}
+#endif /* IP_DEBUG */
+
+
+
+
+
+
diff --git a/lib/lwip/src/core/ipv4/ip_addr.c b/lib/lwip/src/core/ipv4/ip_addr.c
new file mode 100644
index 0000000..2af526e
--- /dev/null
+++ b/lib/lwip/src/core/ipv4/ip_addr.c
@@ -0,0 +1,72 @@
+/*
+ * 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/ip_addr.h"
+#include "lwip/inet.h"
+#include "lwip/netif.h"
+
+/* used by IP_ADDR_ANY and IP_ADDR_BROADCAST in ip_addr.h */
+const struct ip_addr ip_addr_any = { 0x00000000UL };
+const struct ip_addr ip_addr_broadcast = { 0xffffffffUL };
+
+/* 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 ip_addr_isbroadcast(struct ip_addr *addr, struct netif *netif)
+{
+  /* all ones (broadcast) or all zeroes (old skool broadcast) */
+  if ((addr->addr == ip_addr_broadcast.addr) ||
+      (addr->addr == ip_addr_any.addr))
+    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->addr == netif->ip_addr.addr)
+    return 0;
+  /*  on the same (sub) network... */
+  else if (ip_addr_netcmp(addr, &(netif->ip_addr), &(netif->netmask))
+         /* ...and host identifier bits are all ones? =>... */
+          && ((addr->addr & ~netif->netmask.addr) ==
+           (ip_addr_broadcast.addr & ~netif->netmask.addr)))
+    /* => network broadcast address */
+    return 1;
+  else
+    return 0;
+}
diff --git a/lib/lwip/src/core/ipv4/ip_frag.c b/lib/lwip/src/core/ipv4/ip_frag.c
new file mode 100644
index 0000000..5a57138
--- /dev/null
+++ b/lib/lwip/src/core/ipv4/ip_frag.c
@@ -0,0 +1,366 @@
+/* @file
+ * 
+ * This is the IP 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> 
+ * original reassembly code by Adam Dunkels <adam@sics.se>
+ * 
+ */
+
+#include <string.h>
+
+#include "lwip/opt.h"
+/* #include "lwip/sys.h" */
+#include "lwip/ip.h"
+#include "lwip/ip_frag.h"
+#include "lwip/netif.h"
+#include "lwip/stats.h"
+
+
+/*
+ * Copy len bytes from offset in pbuf to buffer 
+ *
+ * helper used by both ip_reass and ip_frag
+ */
+static struct pbuf *
+copy_from_pbuf(struct pbuf *p, u16_t * offset,
+           u8_t * buffer, u16_t len)
+{
+  u16_t l;
+
+  p->payload = (u8_t *)p->payload + *offset;
+  p->len -= *offset;
+  while (len) {
+    l = len < p->len ? len : p->len;
+    memcpy(buffer, p->payload, l);
+    buffer += l;
+    len -= l;
+    if (len)
+      p = p->next;
+    else
+      *offset = l;
+  }
+  return p;
+}
+
+#define IP_REASS_BUFSIZE 5760
+#define IP_REASS_MAXAGE 30
+#define IP_REASS_TMO 1000
+
+static u8_t ip_reassbuf[IP_HLEN + IP_REASS_BUFSIZE];
+static u8_t ip_reassbitmap[IP_REASS_BUFSIZE / (8 * 8) + 1];
+static const u8_t bitmap_bits[8] = { 0xff, 0x7f, 0x3f, 0x1f,
+  0x0f, 0x07, 0x03, 0x01
+};
+static u16_t ip_reasslen;
+static u8_t ip_reassflags;
+#define IP_REASS_FLAG_LASTFRAG 0x01
+
+static u8_t ip_reasstmr;
+
+/**
+ * Reassembly timer base function
+ * for both NO_SYS == 0 and 1 (!).
+ *
+ * Should be called every 1000 msec.
+ */
+void
+ip_reass_tmr(void)
+{
+  if (ip_reasstmr > 0) {
+    ip_reasstmr--;
+  }
+}
+
+/**
+ * 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 *
+ip_reass(struct pbuf *p)
+{
+  struct pbuf *q;
+  struct ip_hdr *fraghdr, *iphdr;
+  u16_t offset, len;
+  u16_t i;
+
+  IPFRAG_STATS_INC(ip_frag.recv);
+
+  iphdr = (struct ip_hdr *) ip_reassbuf;
+  fraghdr = (struct ip_hdr *) p->payload;
+  /* If ip_reasstmr is zero, no packet is present in the buffer, so we
+     write the IP header of the fragment into the reassembly
+     buffer. The timer is updated with the maximum age. */
+  if (ip_reasstmr == 0) {
+    LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass: new packet\n"));
+    memcpy(iphdr, fraghdr, IP_HLEN);
+    ip_reasstmr = IP_REASS_MAXAGE;
+    ip_reassflags = 0;
+    /* Clear the bitmap. */
+    memset(ip_reassbitmap, 0, sizeof(ip_reassbitmap));
+  }
+
+  /* Check if the incoming fragment matches the one currently present
+     in the reasembly buffer. If so, we proceed with copying the
+     fragment into the buffer. */
+  if (ip_addr_cmp(&iphdr->src, &fraghdr->src) &&
+      ip_addr_cmp(&iphdr->dest, &fraghdr->dest) &&
+      IPH_ID(iphdr) == IPH_ID(fraghdr)) {
+    LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass: matching previous fragment ID=%"X16_F"\n",
+      ntohs(IPH_ID(fraghdr))));
+    IPFRAG_STATS_INC(ip_frag.cachehit);
+    /* Find out the offset in the reassembly buffer where we should
+       copy the fragment. */
+    len = ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4;
+    offset = (ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8;
+
+    /* If the offset or the offset + fragment length overflows the
+       reassembly buffer, we discard the entire packet. */
+    if (offset > IP_REASS_BUFSIZE || offset + len > IP_REASS_BUFSIZE) {
+      LWIP_DEBUGF(IP_REASS_DEBUG,
+       ("ip_reass: fragment outside of buffer (%"S16_F":%"S16_F"/%"S16_F").\n", offset,
+        offset + len, IP_REASS_BUFSIZE));
+      ip_reasstmr = 0;
+      goto nullreturn;
+    }
+
+    /* Copy the fragment into the reassembly buffer, at the right
+       offset. */
+    LWIP_DEBUGF(IP_REASS_DEBUG,
+     ("ip_reass: copying with offset %"S16_F" into %"S16_F":%"S16_F"\n", offset,
+      IP_HLEN + offset, IP_HLEN + offset + len));
+    i = IPH_HL(fraghdr) * 4;
+    copy_from_pbuf(p, &i, &ip_reassbuf[IP_HLEN + offset], len);
+
+    /* Update the bitmap. */
+    if (offset / (8 * 8) == (offset + len) / (8 * 8)) {
+      LWIP_DEBUGF(IP_REASS_DEBUG,
+       ("ip_reass: updating single byte in bitmap.\n"));
+      /* If the two endpoints are in the same byte, we only update that byte. */
+      LWIP_ASSERT("offset / (8 * 8) < sizeof(ip_reassbitmap)",
+                   offset / (8 * 8) < sizeof(ip_reassbitmap));
+      ip_reassbitmap[offset / (8 * 8)] |=
+        bitmap_bits[(offset / 8) & 7] &
+        ~bitmap_bits[((offset + len) / 8) & 7];
+    } else {
+      /* If the two endpoints are in different bytes, we update the
+         bytes in the endpoints and fill the stuff inbetween with
+         0xff. */
+      LWIP_ASSERT("offset / (8 * 8) < sizeof(ip_reassbitmap)",
+                   offset / (8 * 8) < sizeof(ip_reassbitmap));
+      ip_reassbitmap[offset / (8 * 8)] |= bitmap_bits[(offset / 8) & 7];
+      LWIP_DEBUGF(IP_REASS_DEBUG,
+       ("ip_reass: updating many bytes in bitmap (%"S16_F":%"S16_F").\n",
+        1 + offset / (8 * 8), (offset + len) / (8 * 8)));
+      for (i = 1 + offset / (8 * 8); i < (offset + len) / (8 * 8); ++i) {
+        ip_reassbitmap[i] = 0xff;
+      }
+      LWIP_ASSERT("(offset + len) / (8 * 8) < sizeof(ip_reassbitmap)",
+                   (offset + len) / (8 * 8) < sizeof(ip_reassbitmap));
+      ip_reassbitmap[(offset + len) / (8 * 8)] |=
+        ~bitmap_bits[((offset + len) / 8) & 7];
+    }
+
+    /* If this fragment has the More Fragments flag set to zero, we
+       know that this is the last fragment, so we can calculate the
+       size of the entire packet. We also set the
+       IP_REASS_FLAG_LASTFRAG flag to indicate that we have received
+       the final fragment. */
+
+    if ((ntohs(IPH_OFFSET(fraghdr)) & IP_MF) == 0) {
+      ip_reassflags |= IP_REASS_FLAG_LASTFRAG;
+      ip_reasslen = offset + len;
+      LWIP_DEBUGF(IP_REASS_DEBUG,
+       ("ip_reass: last fragment seen, total len %"S16_F"\n",
+        ip_reasslen));
+    }
+
+    /* Finally, we check if we have a full packet in the buffer. We do
+       this by checking if we have the last fragment and if all bits
+       in the bitmap are set. */
+    if (ip_reassflags & IP_REASS_FLAG_LASTFRAG) {
+      /* Check all bytes up to and including all but the last byte in
+         the bitmap. */
+      LWIP_ASSERT("ip_reasslen / (8 * 8) - 1 < sizeof(ip_reassbitmap)",
+                   ip_reasslen / (8 * 8) - 1 < sizeof(ip_reassbitmap));
+      for (i = 0; i < ip_reasslen / (8 * 8) - 1; ++i) {
+        if (ip_reassbitmap[i] != 0xff) {
+          LWIP_DEBUGF(IP_REASS_DEBUG,
+           ("ip_reass: last fragment seen, bitmap %"S16_F"/%"S16_F" failed (%"X16_F")\n",
+            i, ip_reasslen / (8 * 8) - 1, ip_reassbitmap[i]));
+          goto nullreturn;
+        }
+      }
+      /* Check the last byte in the bitmap. It should contain just the
+         right amount of bits. */
+      LWIP_ASSERT("ip_reasslen / (8 * 8) < sizeof(ip_reassbitmap)",
+                   ip_reasslen / (8 * 8) < sizeof(ip_reassbitmap));
+      if (ip_reassbitmap[ip_reasslen / (8 * 8)] !=
+        (u8_t) ~ bitmap_bits[ip_reasslen / 8 & 7]) {
+         LWIP_DEBUGF(IP_REASS_DEBUG,
+          ("ip_reass: last fragment seen, bitmap %"S16_F" didn't contain %"X16_F" (%"X16_F")\n",
+        ip_reasslen / (8 * 8), ~bitmap_bits[ip_reasslen / 8 & 7],
+        ip_reassbitmap[ip_reasslen / (8 * 8)]));
+        goto nullreturn;
+      }
+
+      /* Pretend to be a "normal" (i.e., not fragmented) IP packet
+         from now on. */
+      ip_reasslen += IP_HLEN;
+
+      IPH_LEN_SET(iphdr, htons(ip_reasslen));
+      IPH_OFFSET_SET(iphdr, 0);
+      IPH_CHKSUM_SET(iphdr, 0);
+      IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN));
+
+      /* If we have come this far, we have a full packet in the
+         buffer, so we allocate a pbuf and copy the packet into it. We
+         also reset the timer. */
+      ip_reasstmr = 0;
+      pbuf_free(p);
+      p = pbuf_alloc(PBUF_LINK, ip_reasslen, PBUF_POOL);
+      if (p != NULL) {
+        i = 0;
+        for (q = p; q != NULL; q = q->next) {
+          /* Copy enough bytes to fill this pbuf in the chain. The
+             available data in the pbuf is given by the q->len variable. */
+          LWIP_DEBUGF(IP_REASS_DEBUG,
+           ("ip_reass: memcpy from %p (%"S16_F") to %p, %"S16_F" bytes\n",
+            (void *)&ip_reassbuf[i], i, q->payload,
+            q->len > ip_reasslen - i ? ip_reasslen - i : q->len));
+          memcpy(q->payload, &ip_reassbuf[i],
+            q->len > ip_reasslen - i ? ip_reasslen - i : q->len);
+          i += q->len;
+        }
+        IPFRAG_STATS_INC(ip_frag.fw);
+      } else {
+        IPFRAG_STATS_INC(ip_frag.memerr);
+      }
+      LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass: p %p\n", (void*)p));
+      return p;
+    }
+  }
+
+nullreturn:
+  IPFRAG_STATS_INC(ip_frag.drop);
+  pbuf_free(p);
+  return NULL;
+}
+
+#define MAX_MTU 1500
+static u8_t buf[MEM_ALIGN_SIZE(MAX_MTU)];
+
+/**
+ * Fragment an IP datagram if too large for the netif.
+ *
+ * Chop the datagram in MTU sized chunks and send them in order
+ * by using a fixed size static memory buffer (PBUF_ROM)
+ */
+err_t 
+ip_frag(struct pbuf *p, struct netif *netif, struct ip_addr *dest)
+{
+  struct pbuf *rambuf;
+  struct pbuf *header;
+  struct ip_hdr *iphdr;
+  u16_t nfb = 0;
+  u16_t left, cop;
+  u16_t mtu = netif->mtu;
+  u16_t ofo, omf;
+  u16_t last;
+  u16_t poff = IP_HLEN;
+  u16_t tmp;
+
+  /* Get a RAM based MTU sized pbuf */
+  rambuf = pbuf_alloc(PBUF_LINK, 0, PBUF_REF);
+  if (rambuf == NULL) {
+    return ERR_MEM;
+  }
+  rambuf->tot_len = rambuf->len = mtu;
+  rambuf->payload = MEM_ALIGN((void *)buf);
+
+  /* Copy the IP header in it */
+  iphdr = rambuf->payload;
+  memcpy(iphdr, p->payload, IP_HLEN);
+
+  /* Save original offset */
+  tmp = ntohs(IPH_OFFSET(iphdr));
+  ofo = tmp & IP_OFFMASK;
+  omf = tmp & IP_MF;
+
+  left = p->tot_len - IP_HLEN;
+
+  while (left) {
+    last = (left <= mtu - IP_HLEN);
+
+    /* Set new offset and MF flag */
+    ofo += nfb;
+    tmp = omf | (IP_OFFMASK & (ofo));
+    if (!last)
+      tmp = tmp | IP_MF;
+    IPH_OFFSET_SET(iphdr, htons(tmp));
+
+    /* Fill this fragment */
+    nfb = (mtu - IP_HLEN) / 8;
+    cop = last ? left : nfb * 8;
+
+    p = copy_from_pbuf(p, &poff, (u8_t *) iphdr + IP_HLEN, cop);
+
+    /* Correct header */
+    IPH_LEN_SET(iphdr, htons(cop + IP_HLEN));
+    IPH_CHKSUM_SET(iphdr, 0);
+    IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN));
+
+    if (last)
+      pbuf_realloc(rambuf, left + IP_HLEN);
+    /* This part is ugly: we alloc a RAM based pbuf for 
+     * the link level header for each chunk and then 
+     * free it.A PBUF_ROM style pbuf for which pbuf_header
+     * worked would make things simpler.
+     */
+    header = pbuf_alloc(PBUF_LINK, 0, PBUF_RAM);
+    if (header != NULL) {
+      pbuf_chain(header, rambuf);
+      netif->output(netif, header, dest);
+      IPFRAG_STATS_INC(ip_frag.xmit);
+      pbuf_free(header);
+    } else {
+      pbuf_free(rambuf);      
+      return ERR_MEM;    
+    }
+    left -= cop;
+  }
+  pbuf_free(rambuf);
+  return ERR_OK;
+}
diff --git a/lib/lwip/src/core/ipv6/README b/lib/lwip/src/core/ipv6/README
new file mode 100644
index 0000000..3620004
--- /dev/null
+++ b/lib/lwip/src/core/ipv6/README
@@ -0,0 +1 @@
+IPv6 support in lwIP is very experimental.
diff --git a/lib/lwip/src/core/ipv6/icmp6.c b/lib/lwip/src/core/ipv6/icmp6.c
new file mode 100644
index 0000000..10b6903
--- /dev/null
+++ b/lib/lwip/src/core/ipv6/icmp6.c
@@ -0,0 +1,184 @@
+/*
+ * 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"
+
+#include "lwip/icmp.h"
+#include "lwip/inet.h"
+#include "lwip/ip.h"
+#include "lwip/def.h"
+
+#include "lwip/stats.h"
+
+
+void
+icmp_input(struct pbuf *p, struct netif *inp)
+{
+  u8_t type;
+  struct icmp_echo_hdr *iecho;
+  struct ip_hdr *iphdr;
+  struct ip_addr tmpaddr;
+
+#ifdef ICMP_STATS
+  ++lwip_stats.icmp.recv;
+#endif /* ICMP_STATS */
+
+  /* TODO: check length before accessing payload! */
+
+  type = ((u8_t *)p->payload)[0];
+
+  switch (type) {
+  case ICMP6_ECHO:
+    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"));
+
+      pbuf_free(p);
+#ifdef ICMP_STATS
+      ++lwip_stats.icmp.lenerr;
+#endif /* ICMP_STATS */
+
+      return;
+    }
+    iecho = p->payload;
+    iphdr = (struct ip_hdr *)((u8_t *)p->payload - IP_HLEN);
+    if (inet_chksum_pbuf(p) != 0) {
+      LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: checksum failed for received ICMP echo (%"X16_F")\n", inet_chksum_pseudo(p, &(iphdr->src), &(iphdr->dest), IP_PROTO_ICMP, p->tot_len)));
+
+#ifdef ICMP_STATS
+      ++lwip_stats.icmp.chkerr;
+#endif /* ICMP_STATS */
+    /*      return;*/
+    }
+    LWIP_DEBUGF(ICMP_DEBUG, ("icmp: p->len %"S16_F" p->tot_len %"S16_F"\n", p->len, p->tot_len));
+    ip_addr_set(&tmpaddr, &(iphdr->src));
+    ip_addr_set(&(iphdr->src), &(iphdr->dest));
+    ip_addr_set(&(iphdr->dest), &tmpaddr);
+    iecho->type = ICMP6_ER;
+    /* adjust the checksum */
+    if (iecho->chksum >= htons(0xffff - (ICMP6_ECHO << 8))) {
+      iecho->chksum += htons(ICMP6_ECHO << 8) + 1;
+    } else {
+      iecho->chksum += htons(ICMP6_ECHO << 8);
+    }
+    LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: checksum failed for received ICMP echo (%"X16_F")\n", inet_chksum_pseudo(p, &(iphdr->src), &(iphdr->dest), IP_PROTO_ICMP, p->tot_len)));
+#ifdef ICMP_STATS
+    ++lwip_stats.icmp.xmit;
+#endif /* ICMP_STATS */
+
+    /*    LWIP_DEBUGF("icmp: p->len %"U16_F" p->tot_len %"U16_F"\n", p->len, p->tot_len);*/
+    ip_output_if (p, &(iphdr->src), IP_HDRINCL,
+     iphdr->hoplim, IP_PROTO_ICMP, inp);
+    break;
+  default:
+    LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ICMP type %"S16_F" not supported.\n", (s16_t)type));
+#ifdef ICMP_STATS
+    ++lwip_stats.icmp.proterr;
+    ++lwip_stats.icmp.drop;
+#endif /* ICMP_STATS */
+  }
+
+  pbuf_free(p);
+}
+
+void
+icmp_dest_unreach(struct pbuf *p, enum icmp_dur_type t)
+{
+  struct pbuf *q;
+  struct ip_hdr *iphdr;
+  struct icmp_dur_hdr *idur;
+
+  q = pbuf_alloc(PBUF_IP, 8 + IP_HLEN + 8, PBUF_RAM);
+  /* ICMP header + IP header + 8 bytes of data */
+
+  iphdr = p->payload;
+
+  idur = q->payload;
+  idur->type = (u8_t)ICMP6_DUR;
+  idur->icode = (u8_t)t;
+
+  memcpy((u8_t *)q->payload + 8, p->payload, IP_HLEN + 8);
+
+  /* calculate checksum */
+  idur->chksum = 0;
+  idur->chksum = inet_chksum(idur, q->len);
+#ifdef ICMP_STATS
+  ++lwip_stats.icmp.xmit;
+#endif /* ICMP_STATS */
+
+  ip_output(q, NULL,
+      (struct ip_addr *)&(iphdr->src), ICMP_TTL, IP_PROTO_ICMP);
+  pbuf_free(q);
+}
+
+void
+icmp_time_exceeded(struct pbuf *p, enum icmp_te_type t)
+{
+  struct pbuf *q;
+  struct ip_hdr *iphdr;
+  struct icmp_te_hdr *tehdr;
+
+  LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded\n"));
+
+  q = pbuf_alloc(PBUF_IP, 8 + IP_HLEN + 8, PBUF_RAM);
+
+  iphdr = p->payload;
+
+  tehdr = q->payload;
+  tehdr->type = (u8_t)ICMP6_TE;
+  tehdr->icode = (u8_t)t;
+
+  /* copy fields from original packet */
+  memcpy((u8_t *)q->payload + 8, (u8_t *)p->payload, IP_HLEN + 8);
+
+  /* calculate checksum */
+  tehdr->chksum = 0;
+  tehdr->chksum = inet_chksum(tehdr, q->len);
+#ifdef ICMP_STATS
+  ++lwip_stats.icmp.xmit;
+#endif /* ICMP_STATS */
+  ip_output(q, NULL,
+      (struct ip_addr *)&(iphdr->src), ICMP_TTL, IP_PROTO_ICMP);
+  pbuf_free(q);
+}
+
+
+
+
+
+
+
+
diff --git a/lib/lwip/src/core/ipv6/ip6.c b/lib/lwip/src/core/ipv6/ip6.c
new file mode 100644
index 0000000..03037c8
--- /dev/null
+++ b/lib/lwip/src/core/ipv6/ip6.c
@@ -0,0 +1,386 @@
+/*
+ * 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>
+ *
+ */
+
+
+
+/* ip.c
+ *
+ * This is the code for the IP layer for IPv6.
+ *
+ */
+
+
+#include "lwip/opt.h"
+
+#include "lwip/def.h"
+#include "lwip/mem.h"
+#include "lwip/ip.h"
+#include "lwip/inet.h"
+#include "lwip/netif.h"
+#include "lwip/icmp.h"
+#include "lwip/udp.h"
+#include "lwip/tcp.h"
+
+#include "lwip/stats.h"
+
+#include "arch/perf.h"
+
+/* ip_init:
+ *
+ * Initializes the IP layer.
+ */
+
+void
+ip_init(void)
+{
+}
+
+/* ip_route:
+ *
+ * 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.
+ */
+
+struct netif *
+ip_route(struct ip_addr *dest)
+{
+  struct netif *netif;
+
+  for(netif = netif_list; netif != NULL; netif = netif->next) {
+    if (ip_addr_netcmp(dest, &(netif->ip_addr), &(netif->netmask))) {
+      return netif;
+    }
+  }
+
+  return netif_default;
+}
+
+/* ip_forward:
+ *
+ * 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.
+ */
+
+static void
+ip_forward(struct pbuf *p, struct ip_hdr *iphdr)
+{
+  struct netif *netif;
+
+  PERF_START;
+
+  if ((netif = ip_route((struct ip_addr *)&(iphdr->dest))) == NULL) {
+
+    LWIP_DEBUGF(IP_DEBUG, ("ip_input: no forwarding route found for "));
+#if IP_DEBUG
+    ip_addr_debug_print(IP_DEBUG, &(iphdr->dest));
+#endif /* IP_DEBUG */
+    LWIP_DEBUGF(IP_DEBUG, ("\n"));
+    pbuf_free(p);
+    return;
+  }
+  /* Decrement TTL and send ICMP if ttl == 0. */
+  if (--iphdr->hoplim == 0) {
+    /* Don't send ICMP messages in response to ICMP messages */
+    if (iphdr->nexthdr != IP_PROTO_ICMP) {
+      icmp_time_exceeded(p, ICMP_TE_TTL);
+    }
+    pbuf_free(p);
+    return;
+  }
+
+  /* Incremental update of the IP checksum. */
+  /*  if (iphdr->chksum >= htons(0xffff - 0x100)) {
+    iphdr->chksum += htons(0x100) + 1;
+  } else {
+    iphdr->chksum += htons(0x100);
+    }*/
+
+
+  LWIP_DEBUGF(IP_DEBUG, ("ip_forward: forwarding packet to "));
+#if IP_DEBUG
+  ip_addr_debug_print(IP_DEBUG, &(iphdr->dest));
+#endif /* IP_DEBUG */
+  LWIP_DEBUGF(IP_DEBUG, ("\n"));
+
+#ifdef IP_STATS
+  ++lwip_stats.ip.fw;
+  ++lwip_stats.ip.xmit;
+#endif /* IP_STATS */
+
+  PERF_STOP("ip_forward");
+
+  netif->output(netif, p, (struct ip_addr *)&(iphdr->dest));
+}
+
+/* ip_input:
+ *
+ * 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.
+ */
+
+void
+ip_input(struct pbuf *p, struct netif *inp) {
+  struct ip_hdr *iphdr;
+  struct netif *netif;
+
+
+  PERF_START;
+
+#if IP_DEBUG
+  ip_debug_print(p);
+#endif /* IP_DEBUG */
+
+
+#ifdef IP_STATS
+  ++lwip_stats.ip.recv;
+#endif /* IP_STATS */
+
+  /* identify the IP header */
+  iphdr = p->payload;
+
+
+  if (iphdr->v != 6) {
+    LWIP_DEBUGF(IP_DEBUG, ("IP packet dropped due to bad version number\n"));
+#if IP_DEBUG
+    ip_debug_print(p);
+#endif /* IP_DEBUG */
+    pbuf_free(p);
+#ifdef IP_STATS
+    ++lwip_stats.ip.err;
+    ++lwip_stats.ip.drop;
+#endif /* IP_STATS */
+    return;
+  }
+
+  /* is this packet for us? */
+  for(netif = netif_list; netif != NULL; netif = netif->next) {
+#if IP_DEBUG
+    LWIP_DEBUGF(IP_DEBUG, ("ip_input: iphdr->dest "));
+    ip_addr_debug_print(IP_DEBUG, &(iphdr->dest));
+    LWIP_DEBUGF(IP_DEBUG, ("netif->ip_addr "));
+    ip_addr_debug_print(IP_DEBUG, &(netif->ip_addr));
+    LWIP_DEBUGF(IP_DEBUG, ("\n"));
+#endif /* IP_DEBUG */
+    if (ip_addr_cmp(&(iphdr->dest), &(netif->ip_addr))) {
+      break;
+    }
+  }
+
+
+  if (netif == NULL) {
+    /* packet not for us, route or discard */
+#ifdef IP_FORWARD
+    ip_forward(p, iphdr);
+#endif
+    pbuf_free(p);
+    return;
+  }
+
+  pbuf_realloc(p, IP_HLEN + ntohs(iphdr->len));
+
+  /* send to upper layers */
+#if IP_DEBUG
+  /*  LWIP_DEBUGF("ip_input: \n");
+  ip_debug_print(p);
+  LWIP_DEBUGF("ip_input: p->len %"U16_F" p->tot_len %"U16_F"\n", p->len, p->tot_len);*/
+#endif /* IP_DEBUG */
+
+
+  pbuf_header(p, -IP_HLEN);
+
+  switch (iphdr->nexthdr) {
+  case IP_PROTO_UDP:
+    udp_input(p);
+    break;
+  case IP_PROTO_TCP:
+    tcp_input(p);
+    break;
+  case IP_PROTO_ICMP:
+    icmp_input(p, inp);
+    break;
+  default:
+    /* send ICMP destination protocol unreachable */
+    icmp_dest_unreach(p, ICMP_DUR_PROTO);
+    pbuf_free(p);
+    LWIP_DEBUGF(IP_DEBUG, ("Unsupported transport protocol %"U16_F"\n",
+          iphdr->nexthdr));
+
+#ifdef IP_STATS
+    ++lwip_stats.ip.proterr;
+    ++lwip_stats.ip.drop;
+#endif /* IP_STATS */
+
+  }
+  PERF_STOP("ip_input");
+}
+
+
+/* ip_output_if:
+ *
+ * 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.
+ */
+
+err_t
+ip_output_if (struct pbuf *p, struct ip_addr *src, struct ip_addr *dest,
+       u8_t ttl,
+       u8_t proto, struct netif *netif)
+{
+  struct ip_hdr *iphdr;
+
+  PERF_START;
+
+  printf("len %"U16_F" tot_len %"U16_F"\n", p->len, p->tot_len);
+  if (pbuf_header(p, IP_HLEN)) {
+    LWIP_DEBUGF(IP_DEBUG, ("ip_output: not enough room for IP header in pbuf\n"));
+#ifdef IP_STATS
+    ++lwip_stats.ip.err;
+#endif /* IP_STATS */
+
+    return ERR_BUF;
+  }
+  printf("len %"U16_F" tot_len %"U16_F"\n", p->len, p->tot_len);
+
+  iphdr = p->payload;
+
+
+  if (dest != IP_HDRINCL) {
+    printf("!IP_HDRLINCL\n");
+    iphdr->hoplim = ttl;
+    iphdr->nexthdr = proto;
+    iphdr->len = htons(p->tot_len - IP_HLEN);
+    ip_addr_set(&(iphdr->dest), dest);
+
+    iphdr->v = 6;
+
+    if (ip_addr_isany(src)) {
+      ip_addr_set(&(iphdr->src), &(netif->ip_addr));
+    } else {
+      ip_addr_set(&(iphdr->src), src);
+    }
+
+  } else {
+    dest = &(iphdr->dest);
+  }
+
+#ifdef IP_STATS
+  ++lwip_stats.ip.xmit;
+#endif /* IP_STATS */
+
+  LWIP_DEBUGF(IP_DEBUG, ("ip_output_if: %c%c (len %"U16_F")\n", netif->name[0], netif->name[1], p->tot_len));
+#if IP_DEBUG
+  ip_debug_print(p);
+#endif /* IP_DEBUG */
+
+  PERF_STOP("ip_output_if");
+  return netif->output(netif, p, dest);
+}
+
+/* ip_output:
+ *
+ * Simple interface to ip_output_if. It finds the outgoing network interface and
+ * calls upon ip_output_if to do the actual work.
+ */
+
+err_t
+ip_output(struct pbuf *p, struct ip_addr *src, struct ip_addr *dest,
+    u8_t ttl, u8_t proto)
+{
+  struct netif *netif;
+  if ((netif = ip_route(dest)) == NULL) {
+    LWIP_DEBUGF(IP_DEBUG, ("ip_output: No route to 0x%"X32_F"\n", dest->addr));
+#ifdef IP_STATS
+    ++lwip_stats.ip.rterr;
+#endif /* IP_STATS */
+    return ERR_RTE;
+  }
+
+  return ip_output_if (p, src, dest, ttl, proto, netif);
+}
+
+#if IP_DEBUG
+void
+ip_debug_print(struct pbuf *p)
+{
+  struct ip_hdr *iphdr = p->payload;
+  u8_t *payload;
+
+  payload = (u8_t *)iphdr + IP_HLEN;
+
+  LWIP_DEBUGF(IP_DEBUG, ("IP header:\n"));
+  LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
+  LWIP_DEBUGF(IP_DEBUG, ("|%2"S16_F" |  %"X16_F"%"X16_F"  |      %"X16_F"%"X16_F"           | (v, traffic class, flow label)\n",
+        iphdr->v,
+        iphdr->tclass1, iphdr->tclass2,
+        iphdr->flow1, iphdr->flow2));
+  LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
+  LWIP_DEBUGF(IP_DEBUG, ("|    %5"U16_F"      | %2"U16_F"  |  %2"U16_F"   | (len, nexthdr, hoplim)\n",
+        ntohs(iphdr->len),
+        iphdr->nexthdr,
+        iphdr->hoplim));
+  LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
+  LWIP_DEBUGF(IP_DEBUG, ("|       %4"X32_F"      |       %4"X32_F"     | (src)\n",
+        ntohl(iphdr->src.addr[0]) >> 16 & 0xffff,
+        ntohl(iphdr->src.addr[0]) & 0xffff));
+  LWIP_DEBUGF(IP_DEBUG, ("|       %4"X32_F"      |       %4"X32_F"     | (src)\n",
+        ntohl(iphdr->src.addr[1]) >> 16 & 0xffff,
+        ntohl(iphdr->src.addr[1]) & 0xffff));
+  LWIP_DEBUGF(IP_DEBUG, ("|       %4"X32_F"      |       %4"X32_F"     | (src)\n",
+        ntohl(iphdr->src.addr[2]) >> 16 & 0xffff,
+        ntohl(iphdr->src.addr[2]) & 0xffff));
+  LWIP_DEBUGF(IP_DEBUG, ("|       %4"X32_F"      |       %4"X32_F"     | (src)\n",
+        ntohl(iphdr->src.addr[3]) >> 16 & 0xffff,
+        ntohl(iphdr->src.addr[3]) & 0xffff));
+  LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
+  LWIP_DEBUGF(IP_DEBUG, ("|       %4"X32_F"      |       %4"X32_F"     | (dest)\n",
+        ntohl(iphdr->dest.addr[0]) >> 16 & 0xffff,
+        ntohl(iphdr->dest.addr[0]) & 0xffff));
+  LWIP_DEBUGF(IP_DEBUG, ("|       %4"X32_F"      |       %4"X32_F"     | (dest)\n",
+        ntohl(iphdr->dest.addr[1]) >> 16 & 0xffff,
+        ntohl(iphdr->dest.addr[1]) & 0xffff));
+  LWIP_DEBUGF(IP_DEBUG, ("|       %4"X32_F"      |       %4"X32_F"     | (dest)\n",
+        ntohl(iphdr->dest.addr[2]) >> 16 & 0xffff,
+        ntohl(iphdr->dest.addr[2]) & 0xffff));
+  LWIP_DEBUGF(IP_DEBUG, ("|       %4"X32_F"      |       %4"X32_F"     | (dest)\n",
+        ntohl(iphdr->dest.addr[3]) >> 16 & 0xffff,
+        ntohl(iphdr->dest.addr[3]) & 0xffff));
+  LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
+}
+#endif /* IP_DEBUG */
+
diff --git a/lib/lwip/src/core/ipv6/ip6_addr.c b/lib/lwip/src/core/ipv6/ip6_addr.c
new file mode 100644
index 0000000..dcb5078
--- /dev/null
+++ b/lib/lwip/src/core/ipv6/ip6_addr.c
@@ -0,0 +1,90 @@
+/*
+ * 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/ip_addr.h"
+#include "lwip/inet.h"
+
+
+u8_t
+ip_addr_netcmp(struct ip_addr *addr1, struct ip_addr *addr2,
+                struct ip_addr *mask)
+{
+  return((addr1->addr[0] & mask->addr[0]) == (addr2->addr[0] & mask->addr[0]) &&
+         (addr1->addr[1] & mask->addr[1]) == (addr2->addr[1] & mask->addr[1]) &&
+         (addr1->addr[2] & mask->addr[2]) == (addr2->addr[2] & mask->addr[2]) &&
+         (addr1->addr[3] & mask->addr[3]) == (addr2->addr[3] & mask->addr[3]));
+        
+}
+
+u8_t
+ip_addr_cmp(struct ip_addr *addr1, struct ip_addr *addr2)
+{
+  return(addr1->addr[0] == addr2->addr[0] &&
+         addr1->addr[1] == addr2->addr[1] &&
+         addr1->addr[2] == addr2->addr[2] &&
+         addr1->addr[3] == addr2->addr[3]);
+}
+
+void
+ip_addr_set(struct ip_addr *dest, struct ip_addr *src)
+{
+  memcpy(dest, src, sizeof(struct ip_addr));
+  /*  dest->addr[0] = src->addr[0];
+  dest->addr[1] = src->addr[1];
+  dest->addr[2] = src->addr[2];
+  dest->addr[3] = src->addr[3];*/
+}
+
+u8_t
+ip_addr_isany(struct ip_addr *addr)
+{
+  if (addr == NULL) return 1;
+  return((addr->addr[0] | addr->addr[1] | addr->addr[2] | addr->addr[3]) == 0);
+}
+
+
+/*#if IP_DEBUG*/
+void
+ip_addr_debug_print(struct ip_addr *addr)
+{
+  printf("%"X32_F":%"X32_F":%"X32_F":%"X32_F":%"X32_F":%"X32_F":%"X32_F":%"X32_F",
+         ntohl(addr->addr[0]) >> 16 & 0xffff,
+         ntohl(addr->addr[0]) & 0xffff,
+         ntohl(addr->addr[1]) >> 16 & 0xffff,
+         ntohl(addr->addr[1]) & 0xffff,
+         ntohl(addr->addr[2]) >> 16 & 0xffff,
+         ntohl(addr->addr[2]) & 0xffff,
+         ntohl(addr->addr[3]) >> 16 & 0xffff,
+         ntohl(addr->addr[3]) & 0xffff);
+}
+/*#endif*/ /* IP_DEBUG */
+
diff --git a/lib/lwip/src/core/mem.c b/lib/lwip/src/core/mem.c
new file mode 100644
index 0000000..b38d8f1
--- /dev/null
+++ b/lib/lwip/src/core/mem.c
@@ -0,0 +1,310 @@
+/** @file
+ *
+ * Dynamic memory manager
+ *
+ */
+
+/* 
+ * 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 <string.h>
+
+#include "lwip/arch.h"
+#include "lwip/opt.h"
+#include "lwip/def.h"
+#include "lwip/mem.h"
+
+#include "lwip/sys.h"
+
+#include "lwip/stats.h"
+
+struct mem {
+  mem_size_t next, prev;
+#if MEM_ALIGNMENT == 1
+  u8_t used;
+#elif MEM_ALIGNMENT == 2
+  u16_t used;
+#elif MEM_ALIGNMENT == 4
+  u32_t used;
+#elif MEM_ALIGNMENT == 8
+  u64_t used;
+#else
+#error "unhandled MEM_ALIGNMENT size"
+#endif /* MEM_ALIGNMENT */
+}; 
+
+static struct mem *ram_end;
+static u8_t ram[MEM_SIZE + sizeof(struct mem) + MEM_ALIGNMENT];
+
+#define MIN_SIZE 12
+#if 0 /* this one does not align correctly for some, resulting in crashes */
+#define SIZEOF_STRUCT_MEM (unsigned int)MEM_ALIGN_SIZE(sizeof(struct mem))
+#else
+#define SIZEOF_STRUCT_MEM (sizeof(struct mem) + \
+                          (((sizeof(struct mem) % MEM_ALIGNMENT) == 0)? 0 : \
+                          (4 - (sizeof(struct mem) % MEM_ALIGNMENT))))
+#endif
+
+static struct mem *lfree;   /* pointer to the lowest free block */
+
+static sys_sem_t mem_sem;
+
+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", mem->next <= MEM_SIZE);
+  
+  nmem = (struct mem *)&ram[mem->next];
+  if (mem != nmem && nmem->used == 0 && (u8_t *)nmem != (u8_t *)ram_end) {
+    if (lfree == nmem) {
+      lfree = mem;
+    }
+    mem->next = nmem->next;
+    ((struct mem *)&ram[nmem->next])->prev = (u8_t *)mem - ram;
+  }
+
+  /* plug hole backward */
+  pmem = (struct mem *)&ram[mem->prev];
+  if (pmem != mem && pmem->used == 0) {
+    if (lfree == mem) {
+      lfree = pmem;
+    }
+    pmem->next = mem->next;
+    ((struct mem *)&ram[mem->next])->prev = (u8_t *)pmem - ram;
+  }
+
+}
+void
+mem_init(void)
+{
+  struct mem *mem;
+
+  memset(ram, 0, MEM_SIZE);
+  mem = (struct mem *)ram;
+  mem->next = MEM_SIZE;
+  mem->prev = 0;
+  mem->used = 0;
+  ram_end = (struct mem *)&ram[MEM_SIZE];
+  ram_end->used = 1;
+  ram_end->next = MEM_SIZE;
+  ram_end->prev = MEM_SIZE;
+
+  mem_sem = sys_sem_new(1);
+
+  lfree = (struct mem *)ram;
+
+#if MEM_STATS
+  lwip_stats.mem.avail = MEM_SIZE;
+#endif /* MEM_STATS */
+}
+void
+mem_free(void *rmem)
+{
+  struct mem *mem;
+
+  if (rmem == NULL) {
+    LWIP_DEBUGF(MEM_DEBUG | DBG_TRACE | 2, ("mem_free(p == NULL) was called.\n"));
+    return;
+  }
+  
+  sys_sem_wait(mem_sem);
+
+  LWIP_ASSERT("mem_free: 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 | 3, ("mem_free: illegal memory\n"));
+#if MEM_STATS
+    ++lwip_stats.mem.err;
+#endif /* MEM_STATS */
+    sys_sem_signal(mem_sem);
+    return;
+  }
+  mem = (struct mem *)((u8_t *)rmem - SIZEOF_STRUCT_MEM);
+
+  LWIP_ASSERT("mem_free: mem->used", mem->used);
+  
+  mem->used = 0;
+
+  if (mem < lfree) {
+    lfree = mem;
+  }
+  
+#if MEM_STATS
+  lwip_stats.mem.used -= mem->next - ((u8_t *)mem - ram);
+  
+#endif /* MEM_STATS */
+  plug_holes(mem);
+  sys_sem_signal(mem_sem);
+}
+void *
+mem_reallocm(void *rmem, mem_size_t newsize)
+{
+  void *nmem;
+  nmem = mem_malloc(newsize);
+  if (nmem == NULL) {
+    return mem_realloc(rmem, newsize);
+  }
+  memcpy(nmem, rmem, newsize);
+  mem_free(rmem);
+  return nmem;
+}
+
+void *
+mem_realloc(void *rmem, mem_size_t newsize)
+{
+  mem_size_t size;
+  mem_size_t ptr, ptr2;
+  struct mem *mem, *mem2;
+
+  /* Expand the size of the allocated memory region so that we can
+     adjust for alignment. */
+  if ((newsize % MEM_ALIGNMENT) != 0) {
+   newsize += MEM_ALIGNMENT - ((newsize + SIZEOF_STRUCT_MEM) % MEM_ALIGNMENT);
+  }
+  
+  if (newsize > MEM_SIZE) {
+    return NULL;
+  }
+  
+  sys_sem_wait(mem_sem);
+  
+  LWIP_ASSERT("mem_realloc: 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 | 3, ("mem_realloc: illegal memory\n"));
+    return rmem;
+  }
+  mem = (struct mem *)((u8_t *)rmem - SIZEOF_STRUCT_MEM);
+
+  ptr = (u8_t *)mem - ram;
+
+  size = mem->next - ptr - SIZEOF_STRUCT_MEM;
+#if MEM_STATS
+  lwip_stats.mem.used -= (size - newsize);
+#endif /* MEM_STATS */
+  
+  if (newsize + SIZEOF_STRUCT_MEM + MIN_SIZE < size) {
+    ptr2 = ptr + SIZEOF_STRUCT_MEM + newsize;
+    mem2 = (struct mem *)&ram[ptr2];
+    mem2->used = 0;
+    mem2->next = mem->next;
+    mem2->prev = ptr;
+    mem->next = ptr2;
+    if (mem2->next != MEM_SIZE) {
+      ((struct mem *)&ram[mem2->next])->prev = ptr2;
+    }
+
+    plug_holes(mem2);
+  }
+  sys_sem_signal(mem_sem);  
+  return rmem;
+}
+void *
+mem_malloc(mem_size_t size)
+{
+  mem_size_t ptr, ptr2;
+  struct mem *mem, *mem2;
+
+  if (size == 0) {
+    return NULL;
+  }
+
+  /* Expand the size of the allocated memory region so that we can
+     adjust for alignment. */
+  if ((size % MEM_ALIGNMENT) != 0) {
+    size += MEM_ALIGNMENT - ((size + SIZEOF_STRUCT_MEM) % MEM_ALIGNMENT);
+  }
+  
+  if (size > MEM_SIZE) {
+    return NULL;
+  }
+  
+  sys_sem_wait(mem_sem);
+
+  for (ptr = (u8_t *)lfree - ram; ptr < MEM_SIZE; ptr = ((struct mem *)&ram[ptr])->next) {
+    mem = (struct mem *)&ram[ptr];
+    if (!mem->used &&
+       mem->next - (ptr + SIZEOF_STRUCT_MEM) >= size + SIZEOF_STRUCT_MEM) {
+      ptr2 = ptr + SIZEOF_STRUCT_MEM + size;
+      mem2 = (struct mem *)&ram[ptr2];
+
+      mem2->prev = ptr;      
+      mem2->next = mem->next;
+      mem->next = ptr2;      
+      if (mem2->next != MEM_SIZE) {
+        ((struct mem *)&ram[mem2->next])->prev = ptr2;
+      }
+      
+      mem2->used = 0;      
+      mem->used = 1;
+#if MEM_STATS
+      lwip_stats.mem.used += (size + SIZEOF_STRUCT_MEM);
+      /*      if (lwip_stats.mem.max < lwip_stats.mem.used) {
+        lwip_stats.mem.max = lwip_stats.mem.used;
+  } */
+      if (lwip_stats.mem.max < ptr2) {
+        lwip_stats.mem.max = ptr2;
+      }      
+#endif /* MEM_STATS */
+
+      if (mem == lfree) {
+  /* Find next free block after mem */
+        while (lfree->used && lfree != ram_end) {
+    lfree = (struct mem *)&ram[lfree->next];
+        }
+        LWIP_ASSERT("mem_malloc: !lfree->used", !lfree->used);
+      }
+      sys_sem_signal(mem_sem);
+      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.",
+       (unsigned long)((u8_t *)mem + SIZEOF_STRUCT_MEM) % MEM_ALIGNMENT == 0);
+      return (u8_t *)mem + SIZEOF_STRUCT_MEM;
+    }    
+  }
+  LWIP_DEBUGF(MEM_DEBUG | 2, ("mem_malloc: could not allocate %"S16_F" bytes\n", (s16_t)size));
+#if MEM_STATS
+  ++lwip_stats.mem.err;
+#endif /* MEM_STATS */  
+  sys_sem_signal(mem_sem);
+  return NULL;
+}
diff --git a/lib/lwip/src/core/memp.c b/lib/lwip/src/core/memp.c
new file mode 100644
index 0000000..c0cfce2
--- /dev/null
+++ b/lib/lwip/src/core/memp.c
@@ -0,0 +1,274 @@
+/*
+ * 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/pbuf.h"
+#include "lwip/udp.h"
+#include "lwip/raw.h"
+#include "lwip/tcp.h"
+#include "lwip/api.h"
+#include "lwip/api_msg.h"
+#include "lwip/tcpip.h"
+
+#include "lwip/sys.h"
+#include "lwip/stats.h"
+
+struct memp {
+  struct memp *next;
+};
+
+
+
+static struct memp *memp_tab[MEMP_MAX];
+
+static const u16_t memp_sizes[MEMP_MAX] = {
+  sizeof(struct pbuf),
+  sizeof(struct raw_pcb),
+  sizeof(struct udp_pcb),
+  sizeof(struct tcp_pcb),
+  sizeof(struct tcp_pcb_listen),
+  sizeof(struct tcp_seg),
+  sizeof(struct netbuf),
+  sizeof(struct netconn),
+  sizeof(struct api_msg),
+  sizeof(struct tcpip_msg),
+  sizeof(struct sys_timeout)
+};
+
+static const u16_t memp_num[MEMP_MAX] = {
+  MEMP_NUM_PBUF,
+  MEMP_NUM_RAW_PCB,
+  MEMP_NUM_UDP_PCB,
+  MEMP_NUM_TCP_PCB,
+  MEMP_NUM_TCP_PCB_LISTEN,
+  MEMP_NUM_TCP_SEG,
+  MEMP_NUM_NETBUF,
+  MEMP_NUM_NETCONN,
+  MEMP_NUM_API_MSG,
+  MEMP_NUM_TCPIP_MSG,
+  MEMP_NUM_SYS_TIMEOUT
+};
+
+static u8_t memp_memory[(MEMP_NUM_PBUF *
+       MEM_ALIGN_SIZE(sizeof(struct pbuf) +
+          sizeof(struct memp)) +
+      MEMP_NUM_RAW_PCB *
+       MEM_ALIGN_SIZE(sizeof(struct raw_pcb) +
+          sizeof(struct memp)) +
+      MEMP_NUM_UDP_PCB *
+       MEM_ALIGN_SIZE(sizeof(struct udp_pcb) +
+          sizeof(struct memp)) +
+      MEMP_NUM_TCP_PCB *
+       MEM_ALIGN_SIZE(sizeof(struct tcp_pcb) +
+          sizeof(struct memp)) +
+      MEMP_NUM_TCP_PCB_LISTEN *
+       MEM_ALIGN_SIZE(sizeof(struct tcp_pcb_listen) +
+          sizeof(struct memp)) +
+      MEMP_NUM_TCP_SEG *
+       MEM_ALIGN_SIZE(sizeof(struct tcp_seg) +
+          sizeof(struct memp)) +
+      MEMP_NUM_NETBUF *
+       MEM_ALIGN_SIZE(sizeof(struct netbuf) +
+          sizeof(struct memp)) +
+      MEMP_NUM_NETCONN *
+       MEM_ALIGN_SIZE(sizeof(struct netconn) +
+          sizeof(struct memp)) +
+      MEMP_NUM_API_MSG *
+       MEM_ALIGN_SIZE(sizeof(struct api_msg) +
+          sizeof(struct memp)) +
+      MEMP_NUM_TCPIP_MSG *
+       MEM_ALIGN_SIZE(sizeof(struct tcpip_msg) +
+          sizeof(struct memp)) +
+      MEMP_NUM_SYS_TIMEOUT *
+       MEM_ALIGN_SIZE(sizeof(struct sys_timeout) +
+          sizeof(struct memp)))];
+
+
+#if !SYS_LIGHTWEIGHT_PROT
+static sys_sem_t mutex;
+#endif
+
+#if MEMP_SANITY_CHECK
+static int
+memp_sanity(void)
+{
+  s16_t i, c;
+  struct memp *m, *n;
+
+  for(i = 0; i < MEMP_MAX; i++) {
+    for(m = memp_tab[i]; m != NULL; m = m->next) {
+      c = 1;
+      for(n = memp_tab[i]; n != NULL; n = n->next) {
+         if (n == m) {
+          --c;
+        }
+        if (c < 0) return 0; /* LW was: abort(); */
+      }
+    }
+  }
+  return 1;
+}
+#endif /* MEMP_SANITY_CHECK*/
+
+void
+memp_init(void)
+{
+  struct memp *m, *memp;
+  u16_t i, j;
+  u16_t size;
+      
+#if MEMP_STATS
+  for(i = 0; i < MEMP_MAX; ++i) {
+    lwip_stats.memp[i].used = lwip_stats.memp[i].max =
+      lwip_stats.memp[i].err = 0;
+    lwip_stats.memp[i].avail = memp_num[i];
+  }
+#endif /* MEMP_STATS */
+
+  memp = (struct memp *)&memp_memory[0];
+  for(i = 0; i < MEMP_MAX; ++i) {
+    size = MEM_ALIGN_SIZE(memp_sizes[i] + sizeof(struct memp));
+    if (memp_num[i] > 0) {
+      memp_tab[i] = memp;
+      m = memp;
+      
+      for(j = 0; j < memp_num[i]; ++j) {
+  m->next = (struct memp *)MEM_ALIGN((u8_t *)m + size);
+  memp = m;
+  m = m->next;
+      }
+      memp->next = NULL;
+      memp = m;
+    } else {
+      memp_tab[i] = NULL;
+    }
+  }
+
+#if !SYS_LIGHTWEIGHT_PROT
+  mutex = sys_sem_new(1);
+#endif
+
+  
+}
+
+void *
+memp_malloc(memp_t type)
+{
+  struct memp *memp;
+  void *mem;
+#if SYS_LIGHTWEIGHT_PROT
+  SYS_ARCH_DECL_PROTECT(old_level);
+#endif
+ 
+  LWIP_ASSERT("memp_malloc: type < MEMP_MAX", type < MEMP_MAX);
+
+#if SYS_LIGHTWEIGHT_PROT
+  SYS_ARCH_PROTECT(old_level);
+#else /* SYS_LIGHTWEIGHT_PROT */  
+  sys_sem_wait(mutex);
+#endif /* SYS_LIGHTWEIGHT_PROT */  
+
+  memp = memp_tab[type];
+  
+  if (memp != NULL) {    
+    memp_tab[type] = memp->next;    
+    memp->next = NULL;
+#if MEMP_STATS
+    ++lwip_stats.memp[type].used;
+    if (lwip_stats.memp[type].used > lwip_stats.memp[type].max) {
+      lwip_stats.memp[type].max = lwip_stats.memp[type].used;
+    }
+#endif /* MEMP_STATS */
+#if SYS_LIGHTWEIGHT_PROT
+    SYS_ARCH_UNPROTECT(old_level);
+#else /* SYS_LIGHTWEIGHT_PROT */
+    sys_sem_signal(mutex);
+#endif /* SYS_LIGHTWEIGHT_PROT */  
+    LWIP_ASSERT("memp_malloc: memp properly aligned",
+     ((mem_ptr_t)MEM_ALIGN((u8_t *)memp + sizeof(struct memp)) % MEM_ALIGNMENT) == 0);
+
+    mem = MEM_ALIGN((u8_t *)memp + sizeof(struct memp));
+    return mem;
+  } else {
+    LWIP_DEBUGF(MEMP_DEBUG | 2, ("memp_malloc: out of memory in pool %"S16_F"\n", type));
+#if MEMP_STATS
+    ++lwip_stats.memp[type].err;
+#endif /* MEMP_STATS */
+#if SYS_LIGHTWEIGHT_PROT
+  SYS_ARCH_UNPROTECT(old_level);
+#else /* SYS_LIGHTWEIGHT_PROT */
+  sys_sem_signal(mutex);
+#endif /* SYS_LIGHTWEIGHT_PROT */  
+    return NULL;
+  }
+}
+
+void
+memp_free(memp_t type, void *mem)
+{
+  struct memp *memp;
+#if SYS_LIGHTWEIGHT_PROT
+  SYS_ARCH_DECL_PROTECT(old_level);
+#endif /* SYS_LIGHTWEIGHT_PROT */  
+
+  if (mem == NULL) {
+    return;
+  }
+  memp = (struct memp *)((u8_t *)mem - sizeof(struct memp));
+
+#if SYS_LIGHTWEIGHT_PROT
+    SYS_ARCH_PROTECT(old_level);
+#else /* SYS_LIGHTWEIGHT_PROT */  
+  sys_sem_wait(mutex);
+#endif /* SYS_LIGHTWEIGHT_PROT */  
+
+#if MEMP_STATS
+  lwip_stats.memp[type].used--; 
+#endif /* MEMP_STATS */
+  
+  memp->next = memp_tab[type]; 
+  memp_tab[type] = memp;
+
+#if MEMP_SANITY_CHECK
+  LWIP_ASSERT("memp sanity", memp_sanity());
+#endif  
+
+#if SYS_LIGHTWEIGHT_PROT
+  SYS_ARCH_UNPROTECT(old_level);
+#else /* SYS_LIGHTWEIGHT_PROT */
+  sys_sem_signal(mutex);
+#endif /* SYS_LIGHTWEIGHT_PROT */  
+}
+
diff --git a/lib/lwip/src/core/netif.c b/lib/lwip/src/core/netif.c
new file mode 100644
index 0000000..3525089
--- /dev/null
+++ b/lib/lwip/src/core/netif.c
@@ -0,0 +1,288 @@
+/**
+ * @file
+ *
+ * lwIP network interface 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>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#include "lwip/def.h"
+#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+#include "lwip/tcp.h"
+
+struct netif *netif_list = NULL;
+struct netif *netif_default = NULL;
+
+/**
+ * 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.
+ *
+ * @return netif, or NULL if failed.
+ */
+struct netif *
+netif_add(struct netif *netif, struct ip_addr *ipaddr, struct ip_addr *netmask,
+  struct ip_addr *gw,
+  void *state,
+  err_t (* init)(struct netif *netif),
+  err_t (* input)(struct pbuf *p, struct netif *netif))
+{
+  static s16_t netifnum = 0;
+  
+#if LWIP_DHCP
+  /* netif not under DHCP control by default */
+  netif->dhcp = NULL;
+#endif
+  /* remember netif specific state information data */
+  netif->state = state;
+  netif->num = netifnum++;
+  netif->input = input;
+
+  netif_set_addr(netif, ipaddr, netmask, gw);
+
+  /* call user specified initialization function for netif */
+  if (init(netif) != ERR_OK) {
+    return NULL;
+  }
+
+  /* add this netif to the list */
+  netif->next = netif_list;
+  netif_list = netif;
+  LWIP_DEBUGF(NETIF_DEBUG, ("netif: added interface %c%c IP addr ",
+    netif->name[0], netif->name[1]));
+  ip_addr_debug_print(NETIF_DEBUG, ipaddr);
+  LWIP_DEBUGF(NETIF_DEBUG, (" netmask "));
+  ip_addr_debug_print(NETIF_DEBUG, netmask);
+  LWIP_DEBUGF(NETIF_DEBUG, (" gw "));
+  ip_addr_debug_print(NETIF_DEBUG, gw);
+  LWIP_DEBUGF(NETIF_DEBUG, ("\n"));
+  return netif;
+}
+
+void
+netif_set_addr(struct netif *netif,struct ip_addr *ipaddr, struct ip_addr *netmask,
+    struct ip_addr *gw)
+{
+  netif_set_ipaddr(netif, ipaddr);
+  netif_set_netmask(netif, netmask);
+  netif_set_gw(netif, gw);
+}
+
+void netif_remove(struct netif * netif)
+{
+  if ( netif == NULL ) return;
+
+  /*  is it the first netif? */
+  if (netif_list == netif) {
+    netif_list = netif->next;
+  }
+  else {
+    /*  look for netif further down the list */
+    struct netif * tmpNetif;
+    for (tmpNetif = netif_list; tmpNetif != NULL; tmpNetif = tmpNetif->next) {
+      if (tmpNetif->next == netif) {
+        tmpNetif->next = netif->next;
+        break;
+        }
+    }
+    if (tmpNetif == NULL)
+      return; /*  we didn't find any netif today */
+  }
+  /* this netif is default? */
+  if (netif_default == netif)
+    /* reset default netif */
+    netif_default = NULL;
+  LWIP_DEBUGF( NETIF_DEBUG, ("netif_remove: removed netif\n") );
+}
+
+struct netif *
+netif_find(char *name)
+{
+  struct netif *netif;
+  u8_t num;
+
+  if (name == NULL) {
+    return NULL;
+  }
+
+  num = name[2] - '0';
+
+  for(netif = netif_list; netif != NULL; netif = netif->next) {
+    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;
+}
+
+void
+netif_set_ipaddr(struct netif *netif, struct ip_addr *ipaddr)
+{
+  /* TODO: Handling of obsolete pcbs */
+  /* See:  http://mail.gnu.org/archive/html/lwip-users/2003-03/msg00118.html */
+#if LWIP_TCP
+  struct tcp_pcb *pcb;
+  struct tcp_pcb_listen *lpcb;
+
+  /* address is actually being changed? */
+  if ((ip_addr_cmp(ipaddr, &(netif->ip_addr))) == 0)
+  {
+    /* extern struct tcp_pcb *tcp_active_pcbs; defined by tcp.h */
+    LWIP_DEBUGF(NETIF_DEBUG | 1, ("netif_set_ipaddr: netif address being changed\n"));
+    pcb = tcp_active_pcbs;
+    while (pcb != NULL) {
+      /* PCB bound to current local interface address? */
+      if (ip_addr_cmp(&(pcb->local_ip), &(netif->ip_addr))) {
+        /* this connection must be aborted */
+        struct tcp_pcb *next = pcb->next;
+        LWIP_DEBUGF(NETIF_DEBUG | 1, ("netif_set_ipaddr: aborting TCP pcb %p\n", (void *)pcb));
+        tcp_abort(pcb);
+        pcb = next;
+      } else {
+        pcb = pcb->next;
+      }
+    }
+    for (lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) {
+      /* PCB bound to current local interface address? */
+      if (ip_addr_cmp(&(lpcb->local_ip), &(netif->ip_addr))) {
+        /* The PCB is listening to the old ipaddr and
+         * is set to listen to the new one instead */
+        ip_addr_set(&(lpcb->local_ip), ipaddr);
+      }
+    }
+  }
+#endif
+  ip_addr_set(&(netif->ip_addr), ipaddr);
+#if 0 /* only allowed for Ethernet interfaces TODO: how can we check? */
+  /** For Ethernet network interfaces, we would like to send a
+   *  "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.
+   */ 
+  etharp_query(netif, ipaddr, NULL);
+#endif
+  LWIP_DEBUGF(NETIF_DEBUG | DBG_TRACE | DBG_STATE | 3, ("netif: IP address of interface %c%c set to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+    netif->name[0], netif->name[1],
+    ip4_addr1(&netif->ip_addr),
+    ip4_addr2(&netif->ip_addr),
+    ip4_addr3(&netif->ip_addr),
+    ip4_addr4(&netif->ip_addr)));
+}
+
+void
+netif_set_gw(struct netif *netif, struct ip_addr *gw)
+{
+  ip_addr_set(&(netif->gw), gw);
+  LWIP_DEBUGF(NETIF_DEBUG | DBG_TRACE | DBG_STATE | 3, ("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(&netif->gw),
+    ip4_addr2(&netif->gw),
+    ip4_addr3(&netif->gw),
+    ip4_addr4(&netif->gw)));
+}
+
+void
+netif_set_netmask(struct netif *netif, struct ip_addr *netmask)
+{
+  ip_addr_set(&(netif->netmask), netmask);
+  LWIP_DEBUGF(NETIF_DEBUG | DBG_TRACE | DBG_STATE | 3, ("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(&netif->netmask),
+    ip4_addr2(&netif->netmask),
+    ip4_addr3(&netif->netmask),
+    ip4_addr4(&netif->netmask)));
+}
+
+void
+netif_set_default(struct netif *netif)
+{
+  netif_default = netif;
+  LWIP_DEBUGF(NETIF_DEBUG, ("netif: setting default interface %c%c\n",
+           netif ? netif->name[0] : '\'', netif ? netif->name[1] : '\''));
+}
+
+/**
+ * Bring an interface up, available for processing
+ * traffic.
+ * 
+ * @note: Enabling DHCP on a down interface will make it come
+ * up once configured.
+ * 
+ * @see dhcp_start()
+ */ 
+void netif_set_up(struct netif *netif)
+{
+  netif->flags |= NETIF_FLAG_UP;
+}
+
+/**
+ * Ask if an interface is up
+ */ 
+u8_t netif_is_up(struct netif *netif)
+{
+  return (netif->flags & NETIF_FLAG_UP)?1:0;
+}
+
+/**
+ * Bring an interface down, disabling any traffic processing.
+ *
+ * @note: Enabling DHCP on a down interface will make it come
+ * up once configured.
+ * 
+ * @see dhcp_start()
+ */ 
+void netif_set_down(struct netif *netif)
+{
+  netif->flags &= ~NETIF_FLAG_UP;
+}
+
+void
+netif_init(void)
+{
+  netif_list = netif_default = NULL;
+}
+
diff --git a/lib/lwip/src/core/pbuf.c b/lib/lwip/src/core/pbuf.c
new file mode 100644
index 0000000..2ece4b0
--- /dev/null
+++ b/lib/lwip/src/core/pbuf.c
@@ -0,0 +1,957 @@
+/**
+ * @file
+ * Packet buffer management
+ *
+ * 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, queues are only
+ * supported in a limited section of lwIP, this is the etharp queueing
+ * code. Outside of this section no packet queues are supported yet.
+ * 
+ * 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).
+ */
+
+/*
+ * 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 <string.h>
+
+#include "lwip/opt.h"
+#include "lwip/stats.h"
+#include "lwip/def.h"
+#include "lwip/mem.h"
+#include "lwip/memp.h"
+#include "lwip/pbuf.h"
+#include "lwip/sys.h"
+#include "arch/perf.h"
+
+static u8_t pbuf_pool_memory[MEM_ALIGNMENT - 1 + PBUF_POOL_SIZE * MEM_ALIGN_SIZE(PBUF_POOL_BUFSIZE + sizeof(struct pbuf))];
+
+#if !SYS_LIGHTWEIGHT_PROT
+static volatile u8_t pbuf_pool_free_lock, pbuf_pool_alloc_lock;
+static sys_sem_t pbuf_pool_free_sem;
+#endif
+
+static struct pbuf *pbuf_pool = NULL;
+
+/**
+ * Initializes the pbuf module.
+ *
+ * A large part of memory is allocated for holding the pool of pbufs.
+ * The size of the individual pbufs in the pool is given by the size
+ * parameter, and the number of pbufs in the pool by the num parameter.
+ *
+ * After the memory has been allocated, the pbufs are set up. The
+ * ->next pointer in each pbuf is set up to point to the next pbuf in
+ * the pool.
+ *
+ */
+void
+pbuf_init(void)
+{
+  struct pbuf *p, *q = NULL;
+  u16_t i;
+
+  pbuf_pool = (struct pbuf *)MEM_ALIGN(pbuf_pool_memory);
+
+#if PBUF_STATS
+  lwip_stats.pbuf.avail = PBUF_POOL_SIZE;
+#endif /* PBUF_STATS */
+
+  /* Set up ->next pointers to link the pbufs of the pool together */
+  p = pbuf_pool;
+
+  for(i = 0; i < PBUF_POOL_SIZE; ++i) {
+    p->next = (struct pbuf *)((u8_t *)p + PBUF_POOL_BUFSIZE + sizeof(struct pbuf));
+    p->len = p->tot_len = PBUF_POOL_BUFSIZE;
+    p->payload = MEM_ALIGN((void *)((u8_t *)p + sizeof(struct pbuf)));
+    p->flags = PBUF_FLAG_POOL;
+    q = p;
+    p = p->next;
+  }
+
+  /* The ->next pointer of last pbuf is NULL to indicate that there
+     are no more pbufs in the pool */
+  q->next = NULL;
+
+#if !SYS_LIGHTWEIGHT_PROT
+  pbuf_pool_alloc_lock = 0;
+  pbuf_pool_free_lock = 0;
+  pbuf_pool_free_sem = sys_sem_new(1);
+#endif
+}
+
+/**
+ * @internal only called from pbuf_alloc()
+ */
+static struct pbuf *
+pbuf_pool_alloc(void)
+{
+  struct pbuf *p = NULL;
+
+  SYS_ARCH_DECL_PROTECT(old_level);
+  SYS_ARCH_PROTECT(old_level);
+
+#if !SYS_LIGHTWEIGHT_PROT
+  /* Next, check the actual pbuf pool, but if the pool is locked, we
+     pretend to be out of buffers and return NULL. */
+  if (pbuf_pool_free_lock) {
+#if PBUF_STATS
+    ++lwip_stats.pbuf.alloc_locked;
+#endif /* PBUF_STATS */
+    return NULL;
+  }
+  pbuf_pool_alloc_lock = 1;
+  if (!pbuf_pool_free_lock) {
+#endif /* SYS_LIGHTWEIGHT_PROT */
+    p = pbuf_pool;
+    if (p) {
+      pbuf_pool = p->next;
+    }
+#if !SYS_LIGHTWEIGHT_PROT
+#if PBUF_STATS
+  } else {
+    ++lwip_stats.pbuf.alloc_locked;
+#endif /* PBUF_STATS */
+  }
+  pbuf_pool_alloc_lock = 0;
+#endif /* SYS_LIGHTWEIGHT_PROT */
+
+#if PBUF_STATS
+  if (p != NULL) {
+    ++lwip_stats.pbuf.used;
+    if (lwip_stats.pbuf.used > lwip_stats.pbuf.max) {
+      lwip_stats.pbuf.max = lwip_stats.pbuf.used;
+    }
+  }
+#endif /* PBUF_STATS */
+
+  SYS_ARCH_UNPROTECT(old_level);
+  return p;
+}
+
+
+/**
+ * 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 flag 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 l, u16_t length, pbuf_flag flag)
+{
+  struct pbuf *p, *q, *r;
+  u16_t offset;
+  s32_t rem_len; /* remaining length */
+  LWIP_DEBUGF(PBUF_DEBUG | DBG_TRACE | 3, ("pbuf_alloc(length=%"U16_F")\n", length));
+
+  /* determine header offset */
+  offset = 0;
+  switch (l) {
+  case PBUF_TRANSPORT:
+    /* add room for transport (often TCP) layer header */
+    offset += PBUF_TRANSPORT_HLEN;
+    /* FALLTHROUGH */
+  case PBUF_IP:
+    /* add room for IP layer header */
+    offset += PBUF_IP_HLEN;
+    /* FALLTHROUGH */
+  case PBUF_LINK:
+    /* add room for link layer header */
+    offset += PBUF_LINK_HLEN;
+    break;
+  case PBUF_RAW:
+    break;
+  default:
+    LWIP_ASSERT("pbuf_alloc: bad pbuf layer", 0);
+    return NULL;
+  }
+
+  switch (flag) {
+  case PBUF_POOL:
+    /* allocate head of pbuf chain into p */
+    p = pbuf_pool_alloc();
+    LWIP_DEBUGF(PBUF_DEBUG | DBG_TRACE | 3, ("pbuf_alloc: allocated pbuf %p\n", (void *)p));
+    if (p == NULL) {
+#if PBUF_STATS
+      ++lwip_stats.pbuf.err;
+#endif /* PBUF_STATS */
+      return NULL;
+    }
+    p->next = NULL;
+
+    /* make the payload pointer point 'offset' bytes into pbuf data memory */
+    p->payload = MEM_ALIGN((void *)((u8_t *)p + (sizeof(struct pbuf) + offset)));
+    LWIP_ASSERT("pbuf_alloc: pbuf p->payload properly aligned",
+            ((mem_ptr_t)p->payload % MEM_ALIGNMENT) == 0);
+    /* the total length of the pbuf chain is the requested size */
+    p->tot_len = length;
+    /* set the length of the first pbuf in the chain */
+    p->len = length > PBUF_POOL_BUFSIZE - offset? PBUF_POOL_BUFSIZE - offset: length;
+    /* set reference count (needed here in case we fail) */
+    p->ref = 1;
+
+    /* now allocate the tail of the pbuf chain */
+
+    /* remember first pbuf for linkage in next iteration */
+    r = p;
+    /* remaining length to be allocated */
+    rem_len = length - p->len;
+    /* any remaining pbufs to be allocated? */
+    while (rem_len > 0) {
+      q = pbuf_pool_alloc();
+      if (q == NULL) {
+       LWIP_DEBUGF(PBUF_DEBUG | 2, ("pbuf_alloc: Out of pbufs in pool.\n"));
+#if PBUF_STATS
+        ++lwip_stats.pbuf.err;
+#endif /* PBUF_STATS */
+        /* free chain so far allocated */
+        pbuf_free(p);
+        /* bail out unsuccesfully */
+        return NULL;
+      }
+      q->next = NULL;
+      /* make previous pbuf point to this pbuf */
+      r->next = q;
+      /* set total length of this pbuf and next in chain */
+      q->tot_len = rem_len;
+      /* this pbuf length is pool size, unless smaller sized tail */
+      q->len = rem_len > PBUF_POOL_BUFSIZE? PBUF_POOL_BUFSIZE: rem_len;
+      q->payload = (void *)((u8_t *)q + sizeof(struct pbuf));
+      LWIP_ASSERT("pbuf_alloc: pbuf q->payload properly aligned",
+              ((mem_ptr_t)q->payload % MEM_ALIGNMENT) == 0);
+      q->ref = 1;
+      /* calculate remaining length to be allocated */
+      rem_len -= q->len;
+      /* remember this pbuf for linkage in next iteration */
+      r = q;
+    }
+    /* end of chain */
+    /*r->next = NULL;*/
+
+    break;
+  case PBUF_RAM:
+    /* If pbuf is to be allocated in RAM, allocate memory for it. */
+    p = mem_malloc(MEM_ALIGN_SIZE(sizeof(struct pbuf) + offset) + MEM_ALIGN_SIZE(length));
+    if (p == NULL) {
+      return NULL;
+    }
+    /* Set up internal structure of the pbuf. */
+    p->payload = MEM_ALIGN((void *)((u8_t *)p + sizeof(struct pbuf) + offset));
+    p->len = p->tot_len = length;
+    p->next = NULL;
+    p->flags = PBUF_FLAG_RAM;
+
+    LWIP_ASSERT("pbuf_alloc: pbuf->payload properly aligned",
+           ((mem_ptr_t)p->payload % MEM_ALIGNMENT) == 0);
+    break;
+  /* pbuf references existing (non-volatile static constant) ROM payload? */
+  case PBUF_ROM:
+  /* pbuf references existing (externally allocated) RAM payload? */
+  case PBUF_REF:
+    /* only allocate memory for the pbuf structure */
+    p = memp_malloc(MEMP_PBUF);
+    if (p == NULL) {
+      LWIP_DEBUGF(PBUF_DEBUG | DBG_TRACE | 2, ("pbuf_alloc: Could not allocate MEMP_PBUF for PBUF_%s.\n", flag == PBUF_ROM?"ROM":"REF"));
+      return NULL;
+    }
+    /* caller must set this field properly, afterwards */
+    p->payload = NULL;
+    p->len = p->tot_len = length;
+    p->next = NULL;
+    p->flags = (flag == PBUF_ROM? PBUF_FLAG_ROM: PBUF_FLAG_REF);
+    break;
+  default:
+    LWIP_ASSERT("pbuf_alloc: erroneous flag", 0);
+    return NULL;
+  }
+  /* set reference count */
+  p->ref = 1;
+  LWIP_DEBUGF(PBUF_DEBUG | DBG_TRACE | 3, ("pbuf_alloc(length=%"U16_F") == %p\n", length, (void *)p));
+  return p;
+}
+
+
+#if PBUF_STATS
+#define DEC_PBUF_STATS do { --lwip_stats.pbuf.used; } while (0)
+#else /* PBUF_STATS */
+#define DEC_PBUF_STATS
+#endif /* PBUF_STATS */
+
+#define PBUF_POOL_FAST_FREE(p)  do {                                    \
+                                  p->next = pbuf_pool;                  \
+                                  pbuf_pool = p;                        \
+                                  DEC_PBUF_STATS;                       \
+                                } while (0)
+
+#if SYS_LIGHTWEIGHT_PROT
+#define PBUF_POOL_FREE(p)  do {                                         \
+                                SYS_ARCH_DECL_PROTECT(old_level);       \
+                                SYS_ARCH_PROTECT(old_level);            \
+                                PBUF_POOL_FAST_FREE(p);                 \
+                                SYS_ARCH_UNPROTECT(old_level);          \
+                               } while (0)
+#else /* SYS_LIGHTWEIGHT_PROT */
+#define PBUF_POOL_FREE(p)  do {                                         \
+                             sys_sem_wait(pbuf_pool_free_sem);          \
+                             PBUF_POOL_FAST_FREE(p);                    \
+                             sys_sem_signal(pbuf_pool_free_sem);        \
+                           } while (0)
+#endif /* SYS_LIGHTWEIGHT_PROT */
+
+/**
+ * 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.
+ *
+ * @bug Cannot grow the size of a pbuf (chain) (yet).
+ */
+void
+pbuf_realloc(struct pbuf *p, u16_t new_len)
+{
+  struct pbuf *q;
+  u16_t rem_len; /* remaining length */
+  s16_t grow;
+
+  LWIP_ASSERT("pbuf_realloc: sane p->flags", p->flags == PBUF_FLAG_POOL ||
+              p->flags == PBUF_FLAG_ROM ||
+              p->flags == PBUF_FLAG_RAM ||
+              p->flags == PBUF_FLAG_REF);
+
+  /* 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) */
+  grow = new_len - p->tot_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 -= q->len;
+    /* decrease total length indicator */
+    q->tot_len += grow;
+    /* proceed to next pbuf in chain */
+    q = q->next;
+  }
+  /* 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 ((q->flags == PBUF_FLAG_RAM) && (rem_len != q->len)) {
+    /* reallocate and adjust the length of the pbuf that will be split */
+    mem_realloc(q, (u8_t *)q->payload - (u8_t *)q + rem_len);
+  }
+  /* 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 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 hdr_size_inc 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 hdr_size_inc is 0, this function does nothing and returns succesful.
+ *
+ * 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)
+{
+  void *payload;
+
+  LWIP_ASSERT("p != NULL", p != NULL);
+  if ((header_size_increment == 0) || (p == NULL)) return 0;
+ 
+  /* remember current payload pointer */
+  payload = p->payload;
+
+  /* pbuf types containing payloads? */
+  if (p->flags == PBUF_FLAG_RAM || p->flags == PBUF_FLAG_POOL) {
+    /* set new payload pointer */
+    p->payload = (u8_t *)p->payload - header_size_increment;
+    /* boundary check fails? */
+    if ((u8_t *)p->payload < (u8_t *)p + sizeof(struct pbuf)) {
+      LWIP_DEBUGF( PBUF_DEBUG | 2, ("pbuf_header: failed as %p < %p (not enough space for new header size)\n",
+        (void *)p->payload,
+        (void *)(p + 1)));\
+      /* restore old payload pointer */
+      p->payload = payload;
+      /* bail out unsuccesfully */
+      return 1;
+    }
+  /* pbuf types refering to external payloads? */
+  } else if (p->flags == PBUF_FLAG_REF || p->flags == PBUF_FLAG_ROM) {
+    /* hide a header in the payload? */
+    if ((header_size_increment < 0) && (header_size_increment - p->len <= 0)) {
+      /* increase payload pointer */
+      p->payload = (u8_t *)p->payload - header_size_increment;
+    } else {
+      /* cannot expand payload to front (yet!)
+       * bail out unsuccesfully */
+      return 1;
+    }
+  }
+  /* modify pbuf length fields */
+  p->len += header_size_increment;
+  p->tot_len += header_size_increment;
+
+  LWIP_DEBUGF( PBUF_DEBUG, ("pbuf_header: old %p new %p (%"S16_F")\n",
+    (void *)payload, (void *)p->payload, header_size_increment));
+
+  return 0;
+}
+
+/**
+ * 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 pbuf The pbuf (chain) to be dereferenced.
+ *
+ * @return the number of pbufs that were de-allocated
+ * from the head of the chain.
+ *
+ * @note MUST NOT be called on a packet queue (Not verified to work yet).
+ * @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)
+{
+  struct pbuf *q;
+  u8_t count;
+  SYS_ARCH_DECL_PROTECT(old_level);
+
+  LWIP_ASSERT("p != NULL", p != NULL);
+  /* if assertions are disabled, proceed with debug output */
+  if (p == NULL) {
+    LWIP_DEBUGF(PBUF_DEBUG | DBG_TRACE | 2, ("pbuf_free(p == NULL) was called.\n"));
+    return 0;
+  }
+  LWIP_DEBUGF(PBUF_DEBUG | DBG_TRACE | 3, ("pbuf_free(%p)\n", (void *)p));
+
+  PERF_START;
+
+  LWIP_ASSERT("pbuf_free: sane flags",
+    p->flags == PBUF_FLAG_RAM || p->flags == PBUF_FLAG_ROM ||
+    p->flags == PBUF_FLAG_REF || p->flags == PBUF_FLAG_POOL);
+
+  count = 0;
+  /* Since decrementing ref cannot be guaranteed to be a single machine operation
+   * we must protect it. Also, the later test of ref must be protected.
+   */
+  SYS_ARCH_PROTECT(old_level);
+  /* de-allocate all consecutive pbufs from the head of the chain that
+   * obtain a zero reference count after decrementing*/
+  while (p != NULL) {
+    /* 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) */
+    p->ref--;
+    /* this pbuf is no longer referenced to? */
+    if (p->ref == 0) {
+      /* remember next pbuf in chain for next iteration */
+      q = p->next;
+      LWIP_DEBUGF( PBUF_DEBUG | 2, ("pbuf_free: deallocating %p\n", (void *)p));
+      /* is this a pbuf from the pool? */
+      if (p->flags == PBUF_FLAG_POOL) {
+        p->len = p->tot_len = PBUF_POOL_BUFSIZE;
+        p->payload = (void *)((u8_t *)p + sizeof(struct pbuf));
+        PBUF_POOL_FREE(p);
+      /* is this a ROM or RAM referencing pbuf? */
+      } else if (p->flags == PBUF_FLAG_ROM || p->flags == PBUF_FLAG_REF) {
+        memp_free(MEMP_PBUF, p);
+      /* p->flags == PBUF_FLAG_RAM */
+      } else {
+        mem_free(p);
+      }
+      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 | 2, ("pbuf_free: %p has ref %"U16_F", ending here.\n", (void *)p, (u16_t)p->ref));
+      /* stop walking through the chain */
+      p = NULL;
+    }
+  }
+  SYS_ARCH_UNPROTECT(old_level);
+  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
+ */
+
+u8_t
+pbuf_clen(struct pbuf *p)
+{
+  u8_t len;
+
+  len = 0;
+  while (p != NULL) {
+    ++len;
+    p = p->next;
+  }
+  return len;
+}
+
+/**
+ * Increment the reference count of the pbuf.
+ *
+ * @param p pbuf to increase reference counter of
+ *
+ */
+void
+pbuf_ref(struct pbuf *p)
+{
+  SYS_ARCH_DECL_PROTECT(old_level);
+  /* pbuf given? */
+  if (p != NULL) {
+    SYS_ARCH_PROTECT(old_level);
+    ++(p->ref);
+    SYS_ARCH_UNPROTECT(old_level);
+  }
+}
+
+/**
+ * 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.
+ * 
+ * @see pbuf_chain()
+ */
+
+void
+pbuf_cat(struct pbuf *h, struct pbuf *t)
+{
+  struct pbuf *p;
+
+  LWIP_ASSERT("h != NULL (programmer violates API)", h != NULL);
+  LWIP_ASSERT("t != NULL (programmer violates API)", t != NULL);
+  if ((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 += 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 += 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.
+   */
+}
+
+/**
+ * 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 | DBG_FRESH | 2, ("pbuf_chain: %p references %p\n", (void *)h, (void *)t));
+}
+
+/* For packet queueing. Note that queued packets MUST be dequeued first
+ * using pbuf_dequeue() before calling other pbuf_() functions. */
+#if ARP_QUEUEING
+/**
+ * Add a packet to the end of a queue.
+ *
+ * @param q pointer to first packet on the queue
+ * @param n packet to be queued
+ *
+ * Both packets MUST be given, and must be different.
+ */
+void
+pbuf_queue(struct pbuf *p, struct pbuf *n)
+{
+#if PBUF_DEBUG /* remember head of queue */
+  struct pbuf *q = p;
+#endif
+  /* programmer stupidity checks */
+  LWIP_ASSERT("p == NULL in pbuf_queue: this indicates a programmer error\n", p != NULL);
+  LWIP_ASSERT("n == NULL in pbuf_queue: this indicates a programmer error\n", n != NULL);
+  LWIP_ASSERT("p == n in pbuf_queue: this indicates a programmer error\n", p != n);
+  if ((p == NULL) || (n == NULL) || (p == n)){
+    LWIP_DEBUGF(PBUF_DEBUG | DBG_HALT | 3, ("pbuf_queue: programmer argument error\n"));
+    return;
+  }
+
+  /* iterate through all packets on queue */
+  while (p->next != NULL) {
+/* be very picky about pbuf chain correctness */
+#if PBUF_DEBUG
+    /* iterate through all pbufs in packet */
+    while (p->tot_len != p->len) {
+      /* make sure invariant condition holds */
+      LWIP_ASSERT("p->len < p->tot_len", p->len < p->tot_len);
+      /* make sure each packet is complete */
+      LWIP_ASSERT("p->next != NULL", p->next != NULL);
+      p = p->next;
+      /* { p->tot_len == p->len => p is last pbuf of a packet } */
+    }
+    /* { p is last pbuf of a packet } */
+    /* proceed to next packet on queue */
+#endif
+    /* proceed to next pbuf */
+    if (p->next != NULL) p = p->next;
+  }
+  /* { p->tot_len == p->len and p->next == NULL } ==>
+   * { p is last pbuf of last packet on queue } */
+  /* chain last pbuf of queue with n */
+  p->next = n;
+  /* n is now referenced to by the (packet p in the) queue */
+  pbuf_ref(n);
+#if PBUF_DEBUG
+  LWIP_DEBUGF(PBUF_DEBUG | DBG_FRESH | 2,
+    ("pbuf_queue: newly queued packet %p sits after packet %p in queue %p\n",
+    (void *)n, (void *)p, (void *)q));
+#endif
+}
+
+/**
+ * Remove a packet from the head of a queue.
+ *
+ * The caller MUST reference the remainder of the queue (as returned). The
+ * caller MUST NOT call pbuf_ref() as it implicitly takes over the reference
+ * from p.
+ * 
+ * @param p pointer to first packet on the queue which will be dequeued.
+ * @return first packet on the remaining queue (NULL if no further packets).
+ *
+ */
+struct pbuf *
+pbuf_dequeue(struct pbuf *p)
+{
+  struct pbuf *q;
+  LWIP_ASSERT("p != NULL", p != NULL);
+
+  /* iterate through all pbufs in packet p */
+  while (p->tot_len != p->len) {
+    /* make sure invariant condition holds */
+    LWIP_ASSERT("p->len < p->tot_len", p->len < p->tot_len);
+    /* make sure each packet is complete */
+    LWIP_ASSERT("p->next != NULL", p->next != NULL);
+    p = p->next;
+  }
+  /* { p->tot_len == p->len } => p is the last pbuf of the first packet */
+  /* remember next packet on queue in q */
+  q = p->next;
+  /* dequeue packet p from queue */
+  p->next = NULL;
+  /* any next packet on queue? */
+  if (q != NULL) {
+    /* although q is no longer referenced by p, it MUST be referenced by
+     * the caller, who is maintaining this packet queue. So, we do not call
+     * pbuf_free(q) here, resulting in an implicit pbuf_ref(q) for the caller. */
+    LWIP_DEBUGF(PBUF_DEBUG | DBG_FRESH | 2, ("pbuf_dequeue: first remaining packet on queue is %p\n", (void *)q));
+  } else {
+    LWIP_DEBUGF(PBUF_DEBUG | DBG_FRESH | 2, ("pbuf_dequeue: no further packets on queue\n"));
+  }
+  return q;
+}
+#endif
+
+/**
+ *
+ * Create PBUF_POOL (or PBUF_RAM) copies of PBUF_REF pbufs.
+ *
+ * Used to queue packets on behalf of the lwIP stack, such as
+ * ARP based queueing.
+ *
+ * Go through a pbuf chain and replace any PBUF_REF buffers
+ * with PBUF_POOL (or PBUF_RAM) pbufs, each taking a copy of
+ * the referenced data.
+ *
+ * @note You MUST explicitly use p = pbuf_take(p);
+ * The pbuf you give as argument, may have been replaced
+ * by a (differently located) copy through pbuf_take()!
+ *
+ * @note Any replaced pbufs will be freed through pbuf_free().
+ * This may deallocate them if they become no longer referenced.
+ *
+ * @param p Head of pbuf chain to process
+ *
+ * @return Pointer to head of pbuf chain
+ */
+struct pbuf *
+pbuf_take(struct pbuf *p)
+{
+  struct pbuf *q , *prev, *head;
+  LWIP_ASSERT("pbuf_take: p != NULL\n", p != NULL);
+  LWIP_DEBUGF(PBUF_DEBUG | DBG_TRACE | 3, ("pbuf_take(%p)\n", (void*)p));
+
+  prev = NULL;
+  head = p;
+  /* iterate through pbuf chain */
+  do
+  {
+    /* pbuf is of type PBUF_REF? */
+    if (p->flags == PBUF_FLAG_REF) {
+      LWIP_DEBUGF(PBUF_DEBUG | DBG_TRACE, ("pbuf_take: encountered PBUF_REF %p\n", (void *)p));
+      /* allocate a pbuf (w/ payload) fully in RAM */
+      /* PBUF_POOL buffers are faster if we can use them */
+      if (p->len <= PBUF_POOL_BUFSIZE) {
+        q = pbuf_alloc(PBUF_RAW, p->len, PBUF_POOL);
+        if (q == NULL) {
+          LWIP_DEBUGF(PBUF_DEBUG | DBG_TRACE | 2, ("pbuf_take: Could not allocate PBUF_POOL\n"));
+        }
+      } else {
+        /* no replacement pbuf yet */
+        q = NULL;
+        LWIP_DEBUGF(PBUF_DEBUG | DBG_TRACE | 2, ("pbuf_take: PBUF_POOL too small to replace PBUF_REF\n"));
+      }
+      /* no (large enough) PBUF_POOL was available? retry with PBUF_RAM */
+      if (q == NULL) {
+        q = pbuf_alloc(PBUF_RAW, p->len, PBUF_RAM);
+        if (q == NULL) {
+          LWIP_DEBUGF(PBUF_DEBUG | DBG_TRACE | 2, ("pbuf_take: Could not allocate PBUF_RAM\n"));
+        }
+      }
+      /* replacement pbuf could be allocated? */
+      if (q != NULL)
+      {
+        /* copy p to q */
+        /* copy successor */
+        q->next = p->next;
+        /* remove linkage from original pbuf */
+        p->next = NULL;
+        /* remove linkage to original pbuf */
+        if (prev != NULL) {
+          /* prev->next == p at this point */
+          LWIP_ASSERT("prev->next == p", prev->next == p);
+          /* break chain and insert new pbuf instead */
+          prev->next = q;
+        /* prev == NULL, so we replaced the head pbuf of the chain */
+        } else {
+          head = q;
+        }
+        /* copy pbuf payload */
+        memcpy(q->payload, p->payload, p->len);
+        q->tot_len = p->tot_len;
+        q->len = p->len;
+        /* in case p was the first pbuf, it is no longer refered to by
+         * our caller, as the caller MUST do p = pbuf_take(p);
+         * in case p was not the first pbuf, it is no longer refered to
+         * by prev. we can safely free the pbuf here.
+         * (note that we have set p->next to NULL already so that
+         * we will not free the rest of the chain by accident.)
+         */
+        pbuf_free(p);
+        /* do not copy ref, since someone else might be using the old buffer */
+        LWIP_DEBUGF(PBUF_DEBUG, ("pbuf_take: replaced PBUF_REF %p with %p\n", (void *)p, (void *)q));
+        p = q;
+      } else {
+        /* deallocate chain */
+        pbuf_free(head);
+        LWIP_DEBUGF(PBUF_DEBUG | 2, ("pbuf_take: failed to allocate replacement pbuf for %p\n", (void *)p));
+        return NULL;
+      }
+    /* p->flags != PBUF_FLAG_REF */
+    } else {
+      LWIP_DEBUGF(PBUF_DEBUG | DBG_TRACE | 1, ("pbuf_take: skipping pbuf not of type PBUF_REF\n"));
+    }
+    /* remember this pbuf */
+    prev = p;
+    /* proceed to next pbuf in original chain */
+    p = p->next;
+  } while (p);
+  LWIP_DEBUGF(PBUF_DEBUG | DBG_TRACE | 1, ("pbuf_take: end of chain reached.\n"));
+
+  return head;
+}
+
+/**
+ * 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 = 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 | DBG_STATE, ("pbuf_dechain: unreferencing %p\n", (void *)q));
+    tail_gone = pbuf_free(q);
+    if (tail_gone > 0) {
+      LWIP_DEBUGF(PBUF_DEBUG | DBG_STATE,
+                  ("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);
+}
diff --git a/lib/lwip/src/core/raw.c b/lib/lwip/src/core/raw.c
new file mode 100644
index 0000000..3019980
--- /dev/null
+++ b/lib/lwip/src/core/raw.c
@@ -0,0 +1,326 @@
+/**
+ * @file
+ * 
+ * Implementation of raw protocol PCBs for low-level handling of
+ * different types of protocols besides (or overriding) those
+ * already available in lwIP.
+ *
+ */
+/*
+ * 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 <string.h>
+
+#include "lwip/opt.h"
+
+#include "lwip/def.h"
+#include "lwip/memp.h"
+#include "lwip/inet.h"
+#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+#include "lwip/raw.h"
+
+#include "lwip/stats.h"
+
+#include "arch/perf.h"
+#include "lwip/snmp.h"
+
+#if LWIP_RAW
+
+/** The list of RAW PCBs */
+static struct raw_pcb *raw_pcbs = NULL;
+
+void
+raw_init(void)
+{
+  raw_pcbs = NULL;
+}
+
+/**
+ * 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 pbuf pbuf to be demultiplexed to a RAW PCB.
+ * @param netif 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).
+ *
+ */
+u8_t
+raw_input(struct pbuf *p, struct netif *inp)
+{
+  struct raw_pcb *pcb;
+  struct ip_hdr *iphdr;
+  s16_t proto;
+  u8_t eaten = 0;
+
+  iphdr = p->payload;
+  proto = IPH_PROTO(iphdr);
+
+  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 ((eaten == 0) && (pcb != NULL)) {
+    if (pcb->protocol == proto) {
+      /* receive callback function available? */
+      if (pcb->recv != NULL) {
+        /* the receive callback function did not eat the packet? */
+        if (pcb->recv(pcb->recv_arg, pcb, p, &(iphdr->src)) != 0)
+        {
+          /* receive function ate the packet */
+          p = NULL;
+          eaten = 1;
+        }
+      }
+      /* no receive callback function was set for this raw PCB */
+      /* drop the packet */
+    }
+    pcb = pcb->next;
+  }
+  return eaten;
+}
+
+/**
+ * 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 IP_ADDR_ANY to
+ * bind to all local interfaces.
+ *
+ * @return lwIP error code.
+ * - ERR_OK. Successful. No error occured.
+ * - 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, struct ip_addr *ipaddr)
+{
+  ip_addr_set(&pcb->local_ip, ipaddr);
+  return ERR_OK;
+}
+
+/**
+ * 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, struct ip_addr *ipaddr)
+{
+  ip_addr_set(&pcb->remote_ip, ipaddr);
+  return ERR_OK;
+}
+
+
+/**
+ * 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.
+ * 
+ * @return non-zero if the packet was free()d, zero if the packet remains
+ * available for others.
+ */
+void
+raw_recv(struct raw_pcb *pcb,
+         u8_t (* recv)(void *arg, struct raw_pcb *upcb, struct pbuf *p,
+                      struct ip_addr *addr),
+         void *recv_arg)
+{
+  /* remember recv() callback and user data */
+  pcb->recv = recv;
+  pcb->recv_arg = recv_arg;
+}
+
+/**
+ * Send the raw IP packet to the given address. Note that actually you cannot
+ * modify the IP headers (this is inconsistent with the receive callback where
+ * you actually get the IP headers), you can only specify the IP payload here.
+ * It requires some more changes in lwIP. (there will be a raw_send() function
+ * then.)
+ *
+ * @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, struct ip_addr *ipaddr)
+{
+  err_t err;
+  struct netif *netif;
+  struct ip_addr *src_ip;
+  struct pbuf *q; /* q will be sent down the stack */
+  
+  LWIP_DEBUGF(RAW_DEBUG | DBG_TRACE | 3, ("raw_sendto\n"));
+  
+  /* not enough space to add an IP header to first pbuf in given p chain? */
+  if (pbuf_header(p, IP_HLEN)) {
+    /* 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 | DBG_TRACE | 2, ("raw_sendto: could not allocate header\n"));
+      return ERR_MEM;
+    }
+    /* 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;
+    pbuf_header(q, -IP_HLEN);
+  }
+  
+  if ((netif = ip_route(ipaddr)) == NULL) {
+    LWIP_DEBUGF(RAW_DEBUG | 1, ("raw_sendto: No route to 0x%"X32_F"\n", ipaddr->addr));
+#if RAW_STATS
+    /*    ++lwip_stats.raw.rterr;*/
+#endif /* RAW_STATS */
+    /* free any temporary header pbuf allocated by pbuf_header() */
+    if (q != p) {
+      pbuf_free(q);
+    }
+    return ERR_RTE;
+  }
+
+  if (ip_addr_isany(&pcb->local_ip)) {
+    /* use outgoing network interface IP address as source address */
+    src_ip = &(netif->ip_addr);
+  } else {
+    /* use RAW PCB local IP address as source address */
+    src_ip = &(pcb->local_ip);
+  }
+
+  err = ip_output_if (q, src_ip, ipaddr, pcb->ttl, pcb->tos, pcb->protocol, netif);
+
+  /* did we chain a header earlier? */
+  if (q != p) {
+    /* free the header */
+    pbuf_free(q);
+  }
+  return err;
+}
+
+/**
+ * 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
+ * @param ipaddr the destination address of the IP packet
+ *
+ */
+err_t
+raw_send(struct raw_pcb *pcb, struct pbuf *p)
+{
+  return raw_sendto(pcb, p, &pcb->remote_ip);
+}
+
+/**
+ * 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;
+  /* 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;
+    }
+  }
+  memp_free(MEMP_RAW_PCB, pcb);
+}
+
+/**
+ * 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(u16_t proto) {
+  struct raw_pcb *pcb;
+
+  LWIP_DEBUGF(RAW_DEBUG | DBG_TRACE | 3, ("raw_new\n"));
+
+  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;
+    pcb->next = raw_pcbs;
+    raw_pcbs = pcb;
+  }
+  return pcb;
+}
+
+#endif /* LWIP_RAW */
diff --git a/lib/lwip/src/core/stats.c b/lib/lwip/src/core/stats.c
new file mode 100644
index 0000000..c94623f
--- /dev/null
+++ b/lib/lwip/src/core/stats.c
@@ -0,0 +1,115 @@
+/*
+ * 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 <string.h>
+
+#include "lwip/opt.h"
+
+#include "lwip/def.h"
+
+#include "lwip/stats.h"
+#include "lwip/mem.h"
+
+
+#if LWIP_STATS
+struct stats_ lwip_stats;
+
+void
+stats_init(void)
+{
+  memset(&lwip_stats, 0, sizeof(struct stats_));
+}
+#if LWIP_STATS_DISPLAY
+void
+stats_display_proto(struct stats_proto *proto, char *name)
+{
+  LWIP_PLATFORM_DIAG(("\n%s\n\t", name));
+  LWIP_PLATFORM_DIAG(("xmit: %"S16_F"\n\t", proto->xmit)); 
+  LWIP_PLATFORM_DIAG(("rexmit: %"S16_F"\n\t", proto->rexmit)); 
+  LWIP_PLATFORM_DIAG(("recv: %"S16_F"\n\t", proto->recv)); 
+  LWIP_PLATFORM_DIAG(("fw: %"S16_F"\n\t", proto->fw)); 
+  LWIP_PLATFORM_DIAG(("drop: %"S16_F"\n\t", proto->drop)); 
+  LWIP_PLATFORM_DIAG(("chkerr: %"S16_F"\n\t", proto->chkerr)); 
+  LWIP_PLATFORM_DIAG(("lenerr: %"S16_F"\n\t", proto->lenerr)); 
+  LWIP_PLATFORM_DIAG(("memerr: %"S16_F"\n\t", proto->memerr)); 
+  LWIP_PLATFORM_DIAG(("rterr: %"S16_F"\n\t", proto->rterr)); 
+  LWIP_PLATFORM_DIAG(("proterr: %"S16_F"\n\t", proto->proterr)); 
+  LWIP_PLATFORM_DIAG(("opterr: %"S16_F"\n\t", proto->opterr)); 
+  LWIP_PLATFORM_DIAG(("err: %"S16_F"\n\t", proto->err)); 
+  LWIP_PLATFORM_DIAG(("cachehit: %"S16_F"\n", proto->cachehit)); 
+}
+
+void
+stats_display_pbuf(struct stats_pbuf *pbuf)
+{
+  LWIP_PLATFORM_DIAG(("\nPBUF\n\t"));
+  LWIP_PLATFORM_DIAG(("avail: %"S16_F"\n\t", pbuf->avail)); 
+  LWIP_PLATFORM_DIAG(("used: %"S16_F"\n\t", pbuf->used)); 
+  LWIP_PLATFORM_DIAG(("max: %"S16_F"\n\t", pbuf->max)); 
+  LWIP_PLATFORM_DIAG(("err: %"S16_F"\n\t", pbuf->err)); 
+  LWIP_PLATFORM_DIAG(("alloc_locked: %"S16_F"\n\t", pbuf->alloc_locked)); 
+  LWIP_PLATFORM_DIAG(("refresh_locked: %"S16_F"\n", pbuf->refresh_locked)); 
+}
+
+void
+stats_display_mem(struct stats_mem *mem, char *name)
+{
+  LWIP_PLATFORM_DIAG(("\n MEM %s\n\t", name));
+  LWIP_PLATFORM_DIAG(("avail: %"S16_F"\n\t", mem->avail)); 
+  LWIP_PLATFORM_DIAG(("used: %"S16_F"\n\t", mem->used)); 
+  LWIP_PLATFORM_DIAG(("max: %"S16_F"\n\t", mem->max)); 
+  LWIP_PLATFORM_DIAG(("err: %"S16_F"\n", mem->err));
+  
+}
+
+void
+stats_display(void)
+{
+  s16_t i;
+  char * memp_names[] = {"PBUF", "RAW_PCB", "UDP_PCB", "TCP_PCB", "TCP_PCB_LISTEN",
+	  		"TCP_SEG", "NETBUF", "NETCONN", "API_MSG", "TCP_MSG", "TIMEOUT"};
+  stats_display_proto(&lwip_stats.link, "LINK");
+  stats_display_proto(&lwip_stats.ip_frag, "IP_FRAG");
+  stats_display_proto(&lwip_stats.ip, "IP");
+  stats_display_proto(&lwip_stats.icmp, "ICMP");
+  stats_display_proto(&lwip_stats.udp, "UDP");
+  stats_display_proto(&lwip_stats.tcp, "TCP");
+  stats_display_pbuf(&lwip_stats.pbuf);
+  stats_display_mem(&lwip_stats.mem, "HEAP");
+  for (i = 0; i < MEMP_MAX; i++) {
+    stats_display_mem(&lwip_stats.memp[i], memp_names[i]);
+  }
+	
+}
+#endif /* LWIP_STATS_DISPLAY */
+#endif /* LWIP_STATS */
+
diff --git a/lib/lwip/src/core/sys.c b/lib/lwip/src/core/sys.c
new file mode 100644
index 0000000..a7dbf34
--- /dev/null
+++ b/lib/lwip/src/core/sys.c
@@ -0,0 +1,294 @@
+/*
+ * 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/sys.h"
+#include "lwip/opt.h"
+#include "lwip/def.h"
+#include "lwip/memp.h"
+
+#if (NO_SYS == 0)
+
+struct sswt_cb
+{
+    s16_t timeflag;
+    sys_sem_t *psem;
+};
+
+
+
+void
+sys_mbox_fetch(sys_mbox_t mbox, void **msg)
+{
+  u32_t time;
+  struct sys_timeouts *timeouts;
+  struct sys_timeout *tmptimeout;
+  sys_timeout_handler h;
+  void *arg;
+
+
+ again:
+  timeouts = sys_arch_timeouts();
+
+  if (!timeouts || !timeouts->next) {
+    sys_arch_mbox_fetch(mbox, msg, 0);
+  } else {
+    if (timeouts->next->time > 0) {
+      time = sys_arch_mbox_fetch(mbox, msg, timeouts->next->time);
+    } else {
+      time = SYS_ARCH_TIMEOUT;
+    }
+
+    if (time == SYS_ARCH_TIMEOUT) {
+      /* If time == SYS_ARCH_TIMEOUT, a timeout occured before a message
+   could be fetched. We should now call the timeout handler and
+   deallocate the memory allocated for the timeout. */
+      tmptimeout = timeouts->next;
+      timeouts->next = tmptimeout->next;
+      h = tmptimeout->h;
+      arg = tmptimeout->arg;
+      memp_free(MEMP_SYS_TIMEOUT, tmptimeout);
+      if (h != NULL) {
+        LWIP_DEBUGF(SYS_DEBUG, ("smf calling h=%p(%p)\n", (void *)h, (void *)arg));
+      	h(arg);
+      }
+
+      /* We try again to fetch a message from the mbox. */
+      goto again;
+    } else {
+      /* If time != SYS_ARCH_TIMEOUT, a message was received before the timeout
+   occured. The time variable is set to the number of
+   milliseconds we waited for the message. */
+      if (time <= timeouts->next->time) {
+  timeouts->next->time -= time;
+      } else {
+  timeouts->next->time = 0;
+      }
+    }
+
+  }
+}
+
+void
+sys_sem_wait(sys_sem_t sem)
+{
+  u32_t time;
+  struct sys_timeouts *timeouts;
+  struct sys_timeout *tmptimeout;
+  sys_timeout_handler h;
+  void *arg;
+
+  /*  while (sys_arch_sem_wait(sem, 1000) == 0);
+      return;*/
+
+ again:
+
+  timeouts = sys_arch_timeouts();
+
+  if (!timeouts || !timeouts->next) {
+    sys_arch_sem_wait(sem, 0);
+  } else {
+    if (timeouts->next->time > 0) {
+      time = sys_arch_sem_wait(sem, timeouts->next->time);
+    } else {
+      time = SYS_ARCH_TIMEOUT;
+    }
+
+    if (time == SYS_ARCH_TIMEOUT) {
+      /* If time == SYS_ARCH_TIMEOUT, a timeout occured before a message
+   could be fetched. We should now call the timeout handler and
+   deallocate the memory allocated for the timeout. */
+      tmptimeout = timeouts->next;
+      timeouts->next = tmptimeout->next;
+      h = tmptimeout->h;
+      arg = tmptimeout->arg;
+      memp_free(MEMP_SYS_TIMEOUT, tmptimeout);
+      if (h != NULL) {
+        LWIP_DEBUGF(SYS_DEBUG, ("ssw h=%p(%p)\n", (void *)h, (void *)arg));
+        h(arg);
+      }
+
+
+      /* We try again to fetch a message from the mbox. */
+      goto again;
+    } else {
+      /* If time != SYS_ARCH_TIMEOUT, a message was received before the timeout
+   occured. The time variable is set to the number of
+   milliseconds we waited for the message. */
+      if (time <= timeouts->next->time) {
+  timeouts->next->time -= time;
+      } else {
+  timeouts->next->time = 0;
+      }
+    }
+
+  }
+}
+
+void
+sys_timeout(u32_t msecs, sys_timeout_handler h, void *arg)
+{
+  struct sys_timeouts *timeouts;
+  struct sys_timeout *timeout, *t;
+
+  timeout = memp_malloc(MEMP_SYS_TIMEOUT);
+  if (timeout == NULL) {
+    return;
+  }
+  timeout->next = NULL;
+  timeout->h = h;
+  timeout->arg = arg;
+  timeout->time = msecs;
+
+  timeouts = sys_arch_timeouts();
+
+  LWIP_DEBUGF(SYS_DEBUG, ("sys_timeout: %p msecs=%"U32_F" h=%p arg=%p\n",
+    (void *)timeout, msecs, (void *)h, (void *)arg));
+
+  LWIP_ASSERT("sys_timeout: timeouts != NULL", timeouts != NULL);
+
+  if (timeouts->next == NULL) {
+    timeouts->next = timeout;
+    return;
+  }
+
+  if (timeouts->next->time > msecs) {
+    timeouts->next->time -= msecs;
+    timeout->next = timeouts->next;
+    timeouts->next = timeout;
+  } else {
+    for(t = timeouts->next; t != NULL; t = t->next) {
+      timeout->time -= t->time;
+      if (t->next == NULL || t->next->time > timeout->time) {
+        if (t->next != NULL) {
+          t->next->time -= timeout->time;
+        }
+        timeout->next = t->next;
+        t->next = timeout;
+        break;
+      }
+    }
+  }
+
+}
+
+/* Go through timeout list (for this task only) and remove the first matching entry,
+   even though the timeout has not triggered yet.
+*/
+
+void
+sys_untimeout(sys_timeout_handler h, void *arg)
+{
+    struct sys_timeouts *timeouts;
+    struct sys_timeout *prev_t, *t;
+
+    timeouts = sys_arch_timeouts();
+
+    if (timeouts->next == NULL)
+        return;
+
+    for (t = timeouts->next, prev_t = NULL; t != NULL; prev_t = t, t = t->next)
+    {
+        if ((t->h == h) && (t->arg == arg))
+        {
+            /* We have a match */
+            /* Unlink from previous in list */
+            if (prev_t == NULL)
+                timeouts->next = t->next;
+            else
+                prev_t->next = t->next;
+            /* If not the last one, add time of this one back to next */
+            if (t->next != NULL)
+                t->next->time += t->time;
+            memp_free(MEMP_SYS_TIMEOUT, t);
+            return;
+        }
+    }
+    return;
+}
+
+
+
+
+
+static void
+sswt_handler(void *arg)
+{
+    struct sswt_cb *sswt_cb = (struct sswt_cb *) arg;
+
+    /* Timeout. Set flag to TRUE and signal semaphore */
+    sswt_cb->timeflag = 1;
+    sys_sem_signal(*(sswt_cb->psem));
+}
+
+/* Wait for a semaphore with timeout (specified in ms) */
+/* timeout = 0: wait forever */
+/* Returns 0 on timeout. 1 otherwise */
+
+int
+sys_sem_wait_timeout(sys_sem_t sem, u32_t timeout)
+{
+    struct sswt_cb sswt_cb;
+
+    sswt_cb.psem = &sem;
+    sswt_cb.timeflag = 0;
+
+    /* If timeout is zero, then just wait forever */
+    if (timeout > 0)
+        /* Create a timer and pass it the address of our flag */
+        sys_timeout(timeout, sswt_handler, &sswt_cb);
+    sys_sem_wait(sem);
+    /* Was it a timeout? */
+    if (sswt_cb.timeflag)
+    {
+        /* timeout */
+        return 0;
+    } else {
+        /* Not a timeout. Remove timeout entry */
+        sys_untimeout(sswt_handler, &sswt_cb);
+        return 1;
+    }
+
+}
+
+
+void
+sys_msleep(u32_t ms)
+{
+  sys_sem_t delaysem = sys_sem_new(0);
+
+  sys_sem_wait_timeout(delaysem, ms);
+
+  sys_sem_free(delaysem);
+}
+
+
+#endif /* NO_SYS */
diff --git a/lib/lwip/src/core/tcp.c b/lib/lwip/src/core/tcp.c
new file mode 100644
index 0000000..41a9edb
--- /dev/null
+++ b/lib/lwip/src/core/tcp.c
@@ -0,0 +1,1171 @@
+/**
+ * @file
+ *
+ * Transmission Control Protocol for IP
+ *
+ * This file contains common functions for the TCP implementation, such as functinos
+ * 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.
+ *
+ */
+
+/*
+ * 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 <string.h>
+
+#include "lwip/opt.h"
+#include "lwip/def.h"
+#include "lwip/mem.h"
+#include "lwip/memp.h"
+
+#include "lwip/tcp.h"
+#if LWIP_TCP
+
+/* Incremented every coarse grained timer shot
+   (typically every 500 ms, determined by TCP_COARSE_TIMEOUT). */
+u32_t tcp_ticks;
+const u8_t tcp_backoff[13] =
+    { 1, 2, 3, 4, 5, 6, 7, 7, 7, 7, 7, 7, 7};
+
+/* The TCP PCB lists. */
+
+/** 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;
+
+struct tcp_pcb *tcp_tmp_pcb;
+
+static u8_t tcp_timer;
+static u16_t tcp_new_port(void);
+
+/**
+ * Initializes the TCP layer.
+ */
+void
+tcp_init(void)
+{
+  /* Clear globals. */
+  tcp_listen_pcbs.listen_pcbs = NULL;
+  tcp_active_pcbs = NULL;
+  tcp_tw_pcbs = NULL;
+  tcp_tmp_pcb = NULL;
+  
+  /* initialize timer */
+  tcp_ticks = 0;
+  tcp_timer = 0;
+  
+}
+
+/**
+ * Called periodically to dispatch TCP timers.
+ *
+ */
+void
+tcp_tmr(void)
+{
+  /* Call tcp_fasttmr() every 250 ms */
+  tcp_fasttmr();
+
+  if (++tcp_timer & 1) {
+    /* Call tcp_tmr() every 500 ms, i.e., every other timer
+       tcp_tmr() is called. */
+    tcp_slowtmr();
+  }
+}
+
+/**
+ * Closes the connection held by the PCB.
+ *
+ */
+err_t
+tcp_close(struct tcp_pcb *pcb)
+{
+  err_t err;
+
+#if TCP_DEBUG
+  LWIP_DEBUGF(TCP_DEBUG, ("tcp_close: closing in state "));
+  tcp_debug_print_state(pcb->state);
+  LWIP_DEBUGF(TCP_DEBUG, ("\n"));
+#endif /* TCP_DEBUG */
+  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. */
+    err = ERR_OK;
+    memp_free(MEMP_TCP_PCB, pcb);
+    pcb = NULL;
+    break;
+  case LISTEN:
+    err = ERR_OK;
+    tcp_pcb_remove((struct tcp_pcb **)&tcp_listen_pcbs.pcbs, pcb);
+    memp_free(MEMP_TCP_PCB_LISTEN, pcb);
+    pcb = NULL;
+    break;
+  case SYN_SENT:
+    err = ERR_OK;
+    tcp_pcb_remove(&tcp_active_pcbs, pcb);
+    memp_free(MEMP_TCP_PCB, pcb);
+    pcb = NULL;
+    break;
+  case SYN_RCVD:
+  case ESTABLISHED:
+    err = tcp_send_ctrl(pcb, TCP_FIN);
+    if (err == ERR_OK) {
+      pcb->state = FIN_WAIT_1;
+    }
+    break;
+  case CLOSE_WAIT:
+    err = tcp_send_ctrl(pcb, TCP_FIN);
+    if (err == ERR_OK) {
+      pcb->state = LAST_ACK;
+    }
+    break;
+  default:
+    /* Has already been closed, do nothing. */
+    err = ERR_OK;
+    pcb = NULL;
+    break;
+  }
+
+  if (pcb != NULL && err == ERR_OK) {
+    err = tcp_output(pcb);
+  }
+  return err;
+}
+
+/**
+ * Aborts a connection by sending a RST to the remote host and deletes
+ * the local protocol control block. This is done when a connection is
+ * killed because of shortage of memory.
+ *
+ */
+void
+tcp_abort(struct tcp_pcb *pcb)
+{
+  u32_t seqno, ackno;
+  u16_t remote_port, local_port;
+  struct ip_addr remote_ip, local_ip;
+#if LWIP_CALLBACK_API  
+  void (* errf)(void *arg, err_t err);
+#endif /* LWIP_CALLBACK_API */
+  void *errf_arg;
+
+  
+  /* 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);
+    memp_free(MEMP_TCP_PCB, pcb);
+  } else {
+    seqno = pcb->snd_nxt;
+    ackno = pcb->rcv_nxt;
+    ip_addr_set(&local_ip, &(pcb->local_ip));
+    ip_addr_set(&remote_ip, &(pcb->remote_ip));
+    local_port = pcb->local_port;
+    remote_port = pcb->remote_port;
+#if LWIP_CALLBACK_API
+    errf = pcb->errf;
+#endif /* LWIP_CALLBACK_API */
+    errf_arg = pcb->callback_arg;
+    tcp_pcb_remove(&tcp_active_pcbs, 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 */
+    memp_free(MEMP_TCP_PCB, pcb);
+    TCP_EVENT_ERR(errf, errf_arg, ERR_ABRT);
+    LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_abort: sending RST\n"));
+    tcp_rst(seqno, ackno, &local_ip, &remote_ip, local_port, remote_port);
+  }
+}
+
+/**
+ * Binds the connection to a local portnumber and IP address. If the
+ * IP address is not given (i.e., ipaddr == NULL), the IP address of
+ * the outgoing network interface is used instead.
+ *
+ */
+
+err_t
+tcp_bind(struct tcp_pcb *pcb, struct ip_addr *ipaddr, u16_t port)
+{
+  struct tcp_pcb *cpcb;
+
+  if (port == 0) {
+    port = tcp_new_port();
+  }
+  /* Check if the address already is in use. */
+  for(cpcb = (struct tcp_pcb *)tcp_listen_pcbs.pcbs;
+      cpcb != NULL; cpcb = cpcb->next) {
+    if (cpcb->local_port == port) {
+      if (ip_addr_isany(&(cpcb->local_ip)) ||
+        ip_addr_isany(ipaddr) ||
+        ip_addr_cmp(&(cpcb->local_ip), ipaddr)) {
+          return ERR_USE;
+      }
+    }
+  }
+  for(cpcb = tcp_active_pcbs;
+      cpcb != NULL; cpcb = cpcb->next) {
+    if (cpcb->local_port == port) {
+      if (ip_addr_isany(&(cpcb->local_ip)) ||
+   ip_addr_isany(ipaddr) ||
+   ip_addr_cmp(&(cpcb->local_ip), ipaddr)) {
+  return ERR_USE;
+      }
+    }
+  }
+
+  if (!ip_addr_isany(ipaddr)) {
+    pcb->local_ip = *ipaddr;
+  }
+  pcb->local_port = port;
+  LWIP_DEBUGF(TCP_DEBUG, ("tcp_bind: bind to port %"U16_F"\n", port));
+  return ERR_OK;
+}
+#if LWIP_CALLBACK_API
+static err_t
+tcp_accept_null(void *arg, struct tcp_pcb *pcb, err_t err)
+{
+  (void)arg;
+  (void)pcb;
+  (void)err;
+
+  return ERR_ABRT;
+}
+#endif /* LWIP_CALLBACK_API */
+
+/**
+ * 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.
+ *
+ */
+struct tcp_pcb *
+tcp_listen(struct tcp_pcb *pcb)
+{
+  struct tcp_pcb_listen *lpcb;
+
+  /* already listening? */
+  if (pcb->state == LISTEN) {
+    return pcb;
+  }
+  lpcb = memp_malloc(MEMP_TCP_PCB_LISTEN);
+  if (lpcb == NULL) {
+    return NULL;
+  }
+  lpcb->callback_arg = pcb->callback_arg;
+  lpcb->local_port = pcb->local_port;
+  lpcb->state = LISTEN;
+  lpcb->so_options = pcb->so_options;
+  lpcb->so_options |= SOF_ACCEPTCONN;
+  lpcb->ttl = pcb->ttl;
+  lpcb->tos = pcb->tos;
+  ip_addr_set(&lpcb->local_ip, &pcb->local_ip);
+  memp_free(MEMP_TCP_PCB, pcb);
+#if LWIP_CALLBACK_API
+  lpcb->accept = tcp_accept_null;
+#endif /* LWIP_CALLBACK_API */
+  TCP_REG(&tcp_listen_pcbs.listen_pcbs, lpcb);
+  return (struct tcp_pcb *)lpcb;
+}
+
+/**
+ * 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.
+ *
+ */
+void
+tcp_recved(struct tcp_pcb *pcb, u16_t len)
+{
+  if ((u32_t)pcb->rcv_wnd + len > TCP_WND) {
+    pcb->rcv_wnd = TCP_WND;
+  } else {
+    pcb->rcv_wnd += len;
+  }
+  if (!(pcb->flags & TF_ACK_DELAY) &&
+     !(pcb->flags & TF_ACK_NOW)) {
+    /*
+     * We send an ACK here (if one is not already pending, hence
+     * the above tests) as tcp_recved() implies that the application
+     * has processed some data, and so we can open the receiver's
+     * window to allow more to be transmitted.  This could result in
+     * two ACKs being sent for each received packet in some limited cases
+     * (where the application is only receiving data, and is slow to
+     * process it) but it is necessary to guarantee that the sender can
+     * continue to transmit.
+     */
+    tcp_ack(pcb);
+  } 
+  else if (pcb->flags & TF_ACK_DELAY && pcb->rcv_wnd >= TCP_WND/2) {
+    /* If we can send a window update such that there is a full
+     * segment available in the window, do so now.  This is sort of
+     * nagle-like in its goals, and tries to hit a compromise between
+     * sending acks each time the window is updated, and only sending
+     * window updates when a timer expires.  The "threshold" used
+     * above (currently TCP_WND/2) can be tuned to be more or less
+     * aggressive  */
+    tcp_ack_now(pcb);
+  }
+
+  LWIP_DEBUGF(TCP_DEBUG, ("tcp_recved: recveived %"U16_F" bytes, wnd %"U16_F" (%"U16_F").\n",
+         len, pcb->rcv_wnd, TCP_WND - pcb->rcv_wnd));
+}
+
+/**
+ * A nastly hack featuring 'goto' statements that allocates a
+ * new TCP local port.
+ */
+static u16_t
+tcp_new_port(void)
+{
+  struct tcp_pcb *pcb;
+#ifndef TCP_LOCAL_PORT_RANGE_START
+#define TCP_LOCAL_PORT_RANGE_START 4096
+#define TCP_LOCAL_PORT_RANGE_END   0x7fff
+#endif
+  static u16_t port = TCP_LOCAL_PORT_RANGE_START;
+  
+ again:
+  if (++port > TCP_LOCAL_PORT_RANGE_END) {
+    port = TCP_LOCAL_PORT_RANGE_START;
+  }
+  
+  for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
+    if (pcb->local_port == port) {
+      goto again;
+    }
+  }
+  for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) {
+    if (pcb->local_port == port) {
+      goto again;
+    }
+  }
+  for(pcb = (struct tcp_pcb *)tcp_listen_pcbs.pcbs; pcb != NULL; pcb = pcb->next) {
+    if (pcb->local_port == port) {
+      goto again;
+    }
+  }
+  return port;
+}
+
+/**
+ * Connects to another host. The function given as the "connected"
+ * argument will be called when the connection has been established.
+ *
+ */
+err_t
+tcp_connect(struct tcp_pcb *pcb, struct ip_addr *ipaddr, u16_t port,
+      err_t (* connected)(void *arg, struct tcp_pcb *tpcb, err_t err))
+{
+  u32_t optdata;
+  err_t ret;
+  u32_t iss;
+
+  LWIP_DEBUGF(TCP_DEBUG, ("tcp_connect to port %"U16_F"\n", port));
+  if (ipaddr != NULL) {
+    pcb->remote_ip = *ipaddr;
+  } else {
+    return ERR_VAL;
+  }
+  pcb->remote_port = port;
+  if (pcb->local_port == 0) {
+    pcb->local_port = tcp_new_port();
+  }
+  iss = tcp_next_iss();
+  pcb->rcv_nxt = 0;
+  pcb->snd_nxt = iss;
+  pcb->lastack = iss - 1;
+  pcb->snd_lbb = iss - 1;
+  pcb->rcv_wnd = TCP_WND;
+  pcb->snd_wnd = TCP_WND;
+  pcb->mss = TCP_MSS;
+  pcb->cwnd = 1;
+  pcb->ssthresh = pcb->mss * 10;
+  pcb->state = SYN_SENT;
+#if LWIP_CALLBACK_API  
+  pcb->connected = connected;
+#endif /* LWIP_CALLBACK_API */  
+  TCP_REG(&tcp_active_pcbs, pcb);
+  
+  /* Build an MSS option */
+  optdata = htonl(((u32_t)2 << 24) | 
+      ((u32_t)4 << 16) | 
+      (((u32_t)pcb->mss / 256) << 8) |
+      (pcb->mss & 255));
+
+  ret = tcp_enqueue(pcb, NULL, 0, TCP_SYN, 0, (u8_t *)&optdata, 4);
+  if (ret == ERR_OK) { 
+    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.
+ */
+void
+tcp_slowtmr(void)
+{
+  struct tcp_pcb *pcb, *pcb2, *prev;
+  u32_t eff_wnd;
+  u8_t pcb_remove;      /* flag if a PCB should be removed */
+  err_t err;
+
+  err = ERR_OK;
+
+  ++tcp_ticks;
+
+  /* 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\n", pcb->state != CLOSED);
+    LWIP_ASSERT("tcp_slowtmr: active pcb->state != LISTEN\n", pcb->state != LISTEN);
+    LWIP_ASSERT("tcp_slowtmr: active pcb->state != TIME-WAIT\n", pcb->state != TIME_WAIT);
+
+    pcb_remove = 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 {
+      ++pcb->rtime;
+      if (pcb->unacked != NULL && pcb->rtime >= pcb->rto) {
+
+        /* Time for a retransmission. */
+        LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_slowtmr: rtime %"U16_F" pcb->rto %"U16_F"\n",
+          pcb->rtime, pcb->rto));
+
+        /* Double retransmission time-out unless we are trying to
+         * connect to somebody (i.e., we are in SYN_SENT). */
+        if (pcb->state != SYN_SENT) {
+          pcb->rto = ((pcb->sa >> 3) + pcb->sv) << tcp_backoff[pcb->nrtx];
+        }
+        /* Reduce congestion window and ssthresh. */
+        eff_wnd = LWIP_MIN(pcb->cwnd, pcb->snd_wnd);
+        pcb->ssthresh = eff_wnd >> 1;
+        if (pcb->ssthresh < pcb->mss) {
+          pcb->ssthresh = pcb->mss * 2;
+        }
+        pcb->cwnd = pcb->mss;
+        LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_slowtmr: cwnd %"U16_F" ssthresh %"U16_F"\n",
+                                pcb->cwnd, pcb->ssthresh));
+ 
+        /* The following needs to be called AFTER cwnd is set to one mss - STJ */
+        tcp_rexmit_rto(pcb);
+     }
+    }
+    /* Check if this PCB has stayed too long in FIN-WAIT-2 */
+    if (pcb->state == FIN_WAIT_2) {
+      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((pcb->so_options & SOF_KEEPALIVE) && ((pcb->state == ESTABLISHED) || (pcb->state == CLOSE_WAIT))) {
+      if((u32_t)(tcp_ticks - pcb->tmr) > (pcb->keepalive + TCP_MAXIDLE) / TCP_SLOW_INTERVAL)  {
+         LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: KEEPALIVE timeout. Aborting connection to %"U16_F".%"U16_F".%"U16_F".%"U16_F".\n",
+                                 ip4_addr1(&pcb->remote_ip), ip4_addr2(&pcb->remote_ip),
+                                 ip4_addr3(&pcb->remote_ip), ip4_addr4(&pcb->remote_ip)));
+
+         tcp_abort(pcb);
+      }
+      else if((u32_t)(tcp_ticks - pcb->tmr) > (pcb->keepalive + pcb->keep_cnt * TCP_KEEPINTVL) / TCP_SLOW_INTERVAL) {
+         tcp_keepalive(pcb);
+         pcb->keep_cnt++;
+      }
+   }
+
+    /* 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 &&
+       (u32_t)tcp_ticks - pcb->tmr >=
+       pcb->rto * TCP_OOSEQ_TIMEOUT) {
+      tcp_segs_free(pcb->ooseq);
+      pcb->ooseq = NULL;
+      LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_slowtmr: dropping OOSEQ queued data\n"));
+    }
+#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) {
+      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;
+      }
+
+      TCP_EVENT_ERR(pcb->errf, pcb->callback_arg, ERR_ABRT);
+
+      pcb2 = pcb->next;
+      memp_free(MEMP_TCP_PCB, pcb);
+      pcb = pcb2;
+    } else {
+
+      /* We check if we should poll the connection. */
+      ++pcb->polltmr;
+      if (pcb->polltmr >= pcb->pollinterval) {
+        pcb->polltmr = 0;
+        LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: polling application\n"));
+        TCP_EVENT_POLL(pcb, err);
+        if (err == ERR_OK) {
+          tcp_output(pcb);
+        }
+      }
+      
+      prev = pcb;
+      pcb = pcb->next;
+    }
+  }
+
+  
+  /* 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) {
+      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->next;
+      memp_free(MEMP_TCP_PCB, pcb);
+      pcb = pcb2;
+    } else {
+      prev = pcb;
+      pcb = pcb->next;
+    }
+  }
+}
+
+/**
+ * Is called every TCP_FAST_INTERVAL (250 ms) and sends delayed ACKs.
+ */
+void
+tcp_fasttmr(void)
+{
+  struct tcp_pcb *pcb;
+
+  /* send delayed ACKs */  
+  for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
+    if (pcb->flags & TF_ACK_DELAY) {
+      LWIP_DEBUGF(TCP_DEBUG, ("tcp_fasttmr: delayed ACK\n"));
+      tcp_ack_now(pcb);
+      pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW);
+    }
+  }
+}
+
+/**
+ * Deallocates a list of TCP segments (tcp_seg structures).
+ *
+ */
+u8_t
+tcp_segs_free(struct tcp_seg *seg)
+{
+  u8_t count = 0;
+  struct tcp_seg *next;
+  while (seg != NULL) {
+    next = seg->next;
+    count += tcp_seg_free(seg);
+    seg = next;
+  }
+  return count;
+}
+
+/**
+ * Frees a TCP segment.
+ *
+ */
+u8_t
+tcp_seg_free(struct tcp_seg *seg)
+{
+  u8_t count = 0;
+  
+  if (seg != NULL) {
+    if (seg->p != NULL) {
+      count = pbuf_free(seg->p);
+#if TCP_DEBUG
+      seg->p = NULL;
+#endif /* TCP_DEBUG */
+    }
+    memp_free(MEMP_TCP_SEG, seg);
+  }
+  return count;
+}
+
+/**
+ * Sets the priority of a connection.
+ *
+ */
+void
+tcp_setprio(struct tcp_pcb *pcb, u8_t prio)
+{
+  pcb->prio = prio;
+}
+#if TCP_QUEUE_OOSEQ
+
+/**
+ * Returns a copy of the given TCP segment.
+ *
+ */ 
+struct tcp_seg *
+tcp_seg_copy(struct tcp_seg *seg)
+{
+  struct tcp_seg *cseg;
+
+  cseg = memp_malloc(MEMP_TCP_SEG);
+  if (cseg == NULL) {
+    return NULL;
+  }
+  memcpy((u8_t *)cseg, (const u8_t *)seg, sizeof(struct tcp_seg)); 
+  pbuf_ref(cseg->p);
+  return cseg;
+}
+#endif
+
+#if LWIP_CALLBACK_API
+static err_t
+tcp_recv_null(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
+{
+  arg = arg;
+  if (p != NULL) {
+    pbuf_free(p);
+  } else if (err == ERR_OK) {
+    return tcp_close(pcb);
+  }
+  return ERR_OK;
+}
+#endif /* LWIP_CALLBACK_API */
+
+static void
+tcp_kill_prio(u8_t prio)
+{
+  struct tcp_pcb *pcb, *inactive;
+  u32_t inactivity;
+  u8_t mprio;
+
+
+  mprio = TCP_PRIO_MAX;
+  
+  /* We kill the oldest active connection that has lower priority than
+     prio. */
+  inactivity = 0;
+  inactive = NULL;
+  for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
+    if (pcb->prio <= prio &&
+       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);
+  }      
+}
+
+
+static void
+tcp_kill_timewait(void)
+{
+  struct tcp_pcb *pcb, *inactive;
+  u32_t inactivity;
+
+  inactivity = 0;
+  inactive = NULL;
+  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);
+  }      
+}
+
+
+
+struct tcp_pcb *
+tcp_alloc(u8_t prio)
+{
+  struct tcp_pcb *pcb;
+  u32_t iss;
+  
+  pcb = memp_malloc(MEMP_TCP_PCB);
+  if (pcb == NULL) {
+    /* Try killing oldest connection in TIME-WAIT. */
+    LWIP_DEBUGF(TCP_DEBUG, ("tcp_alloc: killing off oldest TIME-WAIT connection\n"));
+    tcp_kill_timewait();
+    pcb = memp_malloc(MEMP_TCP_PCB);
+    if (pcb == NULL) {
+      tcp_kill_prio(prio);    
+      pcb = memp_malloc(MEMP_TCP_PCB);
+    }
+  }
+  if (pcb != NULL) {
+    memset(pcb, 0, sizeof(struct tcp_pcb));
+    pcb->prio = TCP_PRIO_NORMAL;
+    pcb->snd_buf = TCP_SND_BUF;
+    pcb->snd_queuelen = 0;
+    pcb->rcv_wnd = TCP_WND;
+    pcb->tos = 0;
+    pcb->ttl = TCP_TTL;
+    pcb->mss = TCP_MSS;
+    pcb->rto = 3000 / TCP_SLOW_INTERVAL;
+    pcb->sa = 0;
+    pcb->sv = 3000 / TCP_SLOW_INTERVAL;
+    pcb->rtime = 0;
+    pcb->cwnd = 1;
+    iss = tcp_next_iss();
+    pcb->snd_wl2 = iss;
+    pcb->snd_nxt = iss;
+    pcb->snd_max = iss;
+    pcb->lastack = iss;
+    pcb->snd_lbb = iss;   
+    pcb->tmr = tcp_ticks;
+
+    pcb->polltmr = 0;
+
+#if LWIP_CALLBACK_API
+    pcb->recv = tcp_recv_null;
+#endif /* LWIP_CALLBACK_API */  
+    
+    /* Init KEEPALIVE timer */
+    pcb->keepalive = TCP_KEEPDEFAULT;
+    pcb->keep_cnt = 0;
+  }
+  return pcb;
+}
+
+/**
+ * Creates a new TCP protocol control block but doesn't place it on
+ * any of the TCP PCB lists.
+ *
+ * @internal: Maybe there should be a idle TCP PCB list where these
+ * PCBs are put on. We can then implement port reservation using
+ * tcp_bind(). Currently, we lack this (BSD socket type of) feature.
+ */
+
+struct tcp_pcb *
+tcp_new(void)
+{
+  return tcp_alloc(TCP_PRIO_NORMAL);
+}
+
+/*
+ * tcp_arg():
+ *
+ * Used to specify the argument that should be passed callback
+ * functions.
+ *
+ */ 
+
+void
+tcp_arg(struct tcp_pcb *pcb, void *arg)
+{  
+  pcb->callback_arg = arg;
+}
+#if LWIP_CALLBACK_API
+
+/**
+ * Used to specify the function that should be called when a TCP
+ * connection receives data.
+ *
+ */ 
+void
+tcp_recv(struct tcp_pcb *pcb,
+   err_t (* recv)(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err))
+{
+  pcb->recv = recv;
+}
+
+/**
+ * Used to specify the function that should be called when TCP data
+ * has been successfully delivered to the remote host.
+ *
+ */ 
+
+void
+tcp_sent(struct tcp_pcb *pcb,
+   err_t (* sent)(void *arg, struct tcp_pcb *tpcb, u16_t len))
+{
+  pcb->sent = sent;
+}
+
+/**
+ * Used to specify the function that should be called when a fatal error
+ * has occured on the connection.
+ *
+ */ 
+void
+tcp_err(struct tcp_pcb *pcb,
+   void (* errf)(void *arg, err_t err))
+{
+  pcb->errf = errf;
+}
+
+/**
+ * Used for specifying the function that should be called when a
+ * LISTENing connection has been connected to another host.
+ *
+ */ 
+void
+tcp_accept(struct tcp_pcb *pcb,
+     err_t (* accept)(void *arg, struct tcp_pcb *newpcb, err_t err))
+{
+  ((struct tcp_pcb_listen *)pcb)->accept = accept;
+}
+#endif /* LWIP_CALLBACK_API */
+
+
+/**
+ * Used to specify the function that should be called periodically
+ * from TCP. The interval is specified in terms of the TCP coarse
+ * timer interval, which is called twice a second.
+ *
+ */ 
+void
+tcp_poll(struct tcp_pcb *pcb,
+   err_t (* poll)(void *arg, struct tcp_pcb *tpcb), u8_t interval)
+{
+#if LWIP_CALLBACK_API
+  pcb->poll = poll;
+#endif /* LWIP_CALLBACK_API */  
+  pcb->pollinterval = interval;
+}
+
+/**
+ * Purges a TCP PCB. Removes any buffered data and frees the buffer memory.
+ *
+ */
+void
+tcp_pcb_purge(struct tcp_pcb *pcb)
+{
+  if (pcb->state != CLOSED &&
+     pcb->state != TIME_WAIT &&
+     pcb->state != LISTEN) {
+
+    LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge\n"));
+    
+    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 /* LW */
+    if (pcb->ooseq != NULL) {    
+      LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: data left on ->ooseq\n"));
+    }
+    
+    tcp_segs_free(pcb->ooseq);
+    pcb->ooseq = NULL;
+#endif /* TCP_QUEUE_OOSEQ */
+    tcp_segs_free(pcb->unsent);
+    tcp_segs_free(pcb->unacked);
+    pcb->unacked = pcb->unsent = NULL;
+  }
+}
+
+/**
+ * Purges the PCB and removes it from a PCB list. Any delayed ACKs are sent first.
+ *
+ */
+void
+tcp_pcb_remove(struct tcp_pcb **pcblist, struct tcp_pcb *pcb)
+{
+  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) {
+    pcb->flags |= TF_ACK_NOW;
+    tcp_output(pcb);
+  }  
+  pcb->state = CLOSED;
+
+  LWIP_ASSERT("tcp_pcb_remove: tcp_pcbs_sane()", tcp_pcbs_sane());
+}
+
+/**
+ * Calculates a new initial sequence number for new connections.
+ *
+ */
+u32_t
+tcp_next_iss(void)
+{
+  static u32_t iss = 6510;
+  
+  iss += tcp_ticks;       /* XXX */
+  return iss;
+}
+
+#if TCP_DEBUG || TCP_INPUT_DEBUG || TCP_OUTPUT_DEBUG
+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",
+         ntohs(tcphdr->src), ntohs(tcphdr->dest)));
+  LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n"));
+  LWIP_DEBUGF(TCP_DEBUG, ("|           %010"U32_F"          | (seq no)\n",
+          ntohl(tcphdr->seqno)));
+  LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n"));
+  LWIP_DEBUGF(TCP_DEBUG, ("|           %010"U32_F"          | (ack no)\n",
+         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),
+         TCPH_FLAGS(tcphdr) >> 5 & 1,
+         TCPH_FLAGS(tcphdr) >> 4 & 1,
+         TCPH_FLAGS(tcphdr) >> 3 & 1,
+         TCPH_FLAGS(tcphdr) >> 2 & 1,
+         TCPH_FLAGS(tcphdr) >> 1 & 1,
+         TCPH_FLAGS(tcphdr) & 1,
+         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",
+         ntohs(tcphdr->chksum), ntohs(tcphdr->urgp)));
+  LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n"));
+}
+
+void
+tcp_debug_print_state(enum tcp_state s)
+{
+  LWIP_DEBUGF(TCP_DEBUG, ("State: "));
+  switch (s) {
+  case CLOSED:
+    LWIP_DEBUGF(TCP_DEBUG, ("CLOSED\n"));
+    break;
+ case LISTEN:
+   LWIP_DEBUGF(TCP_DEBUG, ("LISTEN\n"));
+   break;
+  case SYN_SENT:
+    LWIP_DEBUGF(TCP_DEBUG, ("SYN_SENT\n"));
+    break;
+  case SYN_RCVD:
+    LWIP_DEBUGF(TCP_DEBUG, ("SYN_RCVD\n"));
+    break;
+  case ESTABLISHED:
+    LWIP_DEBUGF(TCP_DEBUG, ("ESTABLISHED\n"));
+    break;
+  case FIN_WAIT_1:
+    LWIP_DEBUGF(TCP_DEBUG, ("FIN_WAIT_1\n"));
+    break;
+  case FIN_WAIT_2:
+    LWIP_DEBUGF(TCP_DEBUG, ("FIN_WAIT_2\n"));
+    break;
+  case CLOSE_WAIT:
+    LWIP_DEBUGF(TCP_DEBUG, ("CLOSE_WAIT\n"));
+    break;
+  case CLOSING:
+    LWIP_DEBUGF(TCP_DEBUG, ("CLOSING\n"));
+    break;
+  case LAST_ACK:
+    LWIP_DEBUGF(TCP_DEBUG, ("LAST_ACK\n"));
+    break;
+  case TIME_WAIT:
+    LWIP_DEBUGF(TCP_DEBUG, ("TIME_WAIT\n"));
+   break;
+  }
+}
+
+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 "));
+  }
+}
+
+void
+tcp_debug_print_pcbs(void)
+{
+  struct tcp_pcb *pcb;
+  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(pcb = (struct tcp_pcb *)tcp_listen_pcbs.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, ("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);
+  }    
+}
+
+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 */
+#endif /* LWIP_TCP */
+
+
+
+
+
+
+
+
+
+
diff --git a/lib/lwip/src/core/tcp_in.c b/lib/lwip/src/core/tcp_in.c
new file mode 100644
index 0000000..212f9c4
--- /dev/null
+++ b/lib/lwip/src/core/tcp_in.c
@@ -0,0 +1,1199 @@
+/**
+ * @file
+ *
+ * Transmission Control Protocol, incoming traffic
+ *
+ * The input processing functions of TCP.
+ *
+ * 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/def.h"
+#include "lwip/opt.h"
+
+#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+#include "lwip/mem.h"
+#include "lwip/memp.h"
+
+#include "lwip/inet.h"
+#include "lwip/tcp.h"
+
+#include "lwip/stats.h"
+
+#include "arch/perf.h"
+#if LWIP_TCP
+/* 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 struct ip_hdr *iphdr;
+static u32_t seqno, ackno;
+static u8_t flags;
+static u16_t tcplen;
+
+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 err_t tcp_listen_input(struct tcp_pcb_listen *pcb);
+static err_t tcp_timewait_input(struct tcp_pcb *pcb);
+
+
+/* tcp_input:
+ *
+ * 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()).
+ */
+
+void
+tcp_input(struct pbuf *p, struct netif *inp)
+{
+  struct tcp_pcb *pcb, *prev;
+  struct tcp_pcb_listen *lpcb;
+  u8_t hdrlen;
+  err_t err;
+
+  PERF_START;
+
+  TCP_STATS_INC(tcp.recv);
+
+  iphdr = p->payload;
+  tcphdr = (struct tcp_hdr *)((u8_t *)p->payload + IPH_HL(iphdr) * 4);
+
+#if TCP_INPUT_DEBUG
+  tcp_debug_print(tcphdr);
+#endif
+
+  /* remove header from payload */
+  if (pbuf_header(p, -((s16_t)(IPH_HL(iphdr) * 4))) || (p->tot_len < sizeof(struct tcp_hdr))) {
+    /* 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);
+    TCP_STATS_INC(tcp.drop);
+    pbuf_free(p);
+    return;
+  }
+
+  /* Don't even process incoming broadcasts/multicasts. */
+  if (ip_addr_isbroadcast(&(iphdr->dest), inp) ||
+      ip_addr_ismulticast(&(iphdr->dest))) {
+    pbuf_free(p);
+    return;
+  }
+
+#if CHECKSUM_CHECK_TCP
+  /* Verify TCP checksum. */
+  if (inet_chksum_pseudo(p, (struct ip_addr *)&(iphdr->src),
+      (struct ip_addr *)&(iphdr->dest),
+      IP_PROTO_TCP, p->tot_len) != 0) {
+      LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packet discarded due to failing checksum 0x%04"X16_F"\n",
+        inet_chksum_pseudo(p, (struct ip_addr *)&(iphdr->src), (struct ip_addr *)&(iphdr->dest),
+      IP_PROTO_TCP, p->tot_len)));
+#if TCP_DEBUG
+    tcp_debug_print(tcphdr);
+#endif /* TCP_DEBUG */
+    TCP_STATS_INC(tcp.chkerr);
+    TCP_STATS_INC(tcp.drop);
+
+    pbuf_free(p);
+    return;
+  }
+#endif
+
+  /* Move the payload pointer in the pbuf so that it points to the
+     TCP data instead of the TCP header. */
+  hdrlen = TCPH_HDRLEN(tcphdr);
+  pbuf_header(p, -(hdrlen * 4));
+
+  /* Convert fields in TCP header to host byte order. */
+  tcphdr->src = ntohs(tcphdr->src);
+  tcphdr->dest = ntohs(tcphdr->dest);
+  seqno = tcphdr->seqno = ntohl(tcphdr->seqno);
+  ackno = tcphdr->ackno = ntohl(tcphdr->ackno);
+  tcphdr->wnd = ntohs(tcphdr->wnd);
+
+  flags = TCPH_FLAGS(tcphdr) & TCP_FLAGS;
+  tcplen = p->tot_len + ((flags & TCP_FIN || flags & TCP_SYN)? 1: 0);
+
+  /* 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);
+    if (pcb->remote_port == tcphdr->src &&
+       pcb->local_port == tcphdr->dest &&
+       ip_addr_cmp(&(pcb->remote_ip), &(iphdr->src)) &&
+       ip_addr_cmp(&(pcb->local_ip), &(iphdr->dest))) {
+
+      /* 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;
+      }
+      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);
+      if (pcb->remote_port == tcphdr->src &&
+   pcb->local_port == tcphdr->dest &&
+   ip_addr_cmp(&(pcb->remote_ip), &(iphdr->src)) &&
+         ip_addr_cmp(&(pcb->local_ip), &(iphdr->dest))) {
+  /* 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"));
+  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) {
+      if ((ip_addr_isany(&(lpcb->local_ip)) ||
+    ip_addr_cmp(&(lpcb->local_ip), &(iphdr->dest))) &&
+   lpcb->local_port == tcphdr->dest) {
+  /* 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;
+  }
+
+  LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packed for LISTENing connection.\n"));
+  tcp_listen_input(lpcb);
+  pbuf_free(p);
+  return;
+      }
+      prev = (struct tcp_pcb *)lpcb;
+    }
+  }
+
+#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 */
+
+
+  if (pcb != NULL) {
+    /* The incoming segment belongs to a connection. */
+#if TCP_INPUT_DEBUG
+#if TCP_DEBUG
+    tcp_debug_print_state(pcb->state);
+#endif /* TCP_DEBUG */
+#endif /* TCP_INPUT_DEBUG */
+
+    /* Set up a tcp_seg structure. */
+    inseg.next = NULL;
+    inseg.len = p->tot_len;
+    inseg.dataptr = p->payload;
+    inseg.p = p;
+    inseg.tcphdr = tcphdr;
+
+    recv_data = NULL;
+    recv_flags = 0;
+
+    tcp_input_pcb = pcb;
+    err = tcp_process(pcb);
+    tcp_input_pcb = NULL;
+    /* 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->errf, pcb->callback_arg, ERR_RST);
+  tcp_pcb_remove(&tcp_active_pcbs, pcb);
+  memp_free(MEMP_TCP_PCB, pcb);
+      } else if (recv_flags & TF_CLOSED) {
+  /* The connection has been closed and we will deallocate the
+     PCB. */
+  tcp_pcb_remove(&tcp_active_pcbs, pcb);
+  memp_free(MEMP_TCP_PCB, 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 (pcb->acked > 0) {
+    TCP_EVENT_SENT(pcb, pcb->acked, err);
+  }
+
+  if (recv_data != NULL) {
+    /* Notify application that data has been received. */
+    TCP_EVENT_RECV(pcb, recv_data, ERR_OK, err);
+  }
+
+  /* If a FIN segment was received, we call the callback
+     function with a NULL buffer to indicate EOF. */
+  if (recv_flags & TF_GOT_FIN) {
+    TCP_EVENT_RECV(pcb, NULL, ERR_OK, err);
+  }
+  /* If there were no errors, we try to send something out. */
+  if (err == ERR_OK) {
+    tcp_output(pcb);
+  }
+      }
+    }
+
+
+    /* We deallocate the incoming pbuf. If it was buffered by the
+       application, the application should have called pbuf_ref() to
+       increase the reference counter in the pbuf. If so, the buffer
+       isn't actually deallocated by the call to pbuf_free(), only the
+       reference count is decreased. */
+    if (inseg.p != NULL) pbuf_free(inseg.p);
+#if TCP_INPUT_DEBUG
+#if TCP_DEBUG
+    tcp_debug_print_state(pcb->state);
+#endif /* TCP_DEBUG */
+#endif /* TCP_INPUT_DEBUG */
+      
+  } 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(ackno, seqno + tcplen,
+        &(iphdr->dest), &(iphdr->src),
+        tcphdr->dest, tcphdr->src);
+    }
+    pbuf_free(p);
+  }
+
+  LWIP_ASSERT("tcp_input: tcp_pcbs_sane()", tcp_pcbs_sane());
+  PERF_STOP("tcp_input");
+}
+
+/* tcp_listen_input():
+ *
+ * Called by tcp_input() when a segment arrives for a listening
+ * connection.
+ */
+
+static err_t
+tcp_listen_input(struct tcp_pcb_listen *pcb)
+{
+  struct tcp_pcb *npcb;
+  u32_t optdata;
+
+  /* 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(ackno + 1, seqno + tcplen,
+      &(iphdr->dest), &(iphdr->src),
+      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));
+    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) {
+      LWIP_DEBUGF(TCP_DEBUG, ("tcp_listen_input: could not allocate PCB\n"));
+      TCP_STATS_INC(tcp.memerr);
+      return ERR_MEM;
+    }
+    /* Set up the new PCB. */
+    ip_addr_set(&(npcb->local_ip), &(iphdr->dest));
+    npcb->local_port = pcb->local_port;
+    ip_addr_set(&(npcb->remote_ip), &(iphdr->src));
+    npcb->remote_port = tcphdr->src;
+    npcb->state = SYN_RCVD;
+    npcb->rcv_nxt = seqno + 1;
+    npcb->snd_wnd = tcphdr->wnd;
+    npcb->ssthresh = npcb->snd_wnd;
+    npcb->snd_wl1 = seqno - 1;/* initialise to seqno-1 to force window update */
+    npcb->callback_arg = pcb->callback_arg;
+#if LWIP_CALLBACK_API
+    npcb->accept = pcb->accept;
+#endif /* LWIP_CALLBACK_API */
+    /* inherit socket options */
+    npcb->so_options = pcb->so_options & (SOF_DEBUG|SOF_DONTROUTE|SOF_KEEPALIVE|SOF_OOBINLINE|SOF_LINGER);
+    /* Register the new PCB so that we can begin receiving segments
+       for it. */
+    TCP_REG(&tcp_active_pcbs, npcb);
+
+    /* Parse any options in the SYN. */
+    tcp_parseopt(npcb);
+
+    /* Build an MSS option. */
+    optdata = htonl(((u32_t)2 << 24) |
+        ((u32_t)4 << 16) |
+        (((u32_t)npcb->mss / 256) << 8) |
+        (npcb->mss & 255));
+    /* Send a SYN|ACK together with the MSS option. */
+    tcp_enqueue(npcb, NULL, 0, TCP_SYN | TCP_ACK, 0, (u8_t *)&optdata, 4);
+    return tcp_output(npcb);
+  }
+  return ERR_OK;
+}
+
+/* tcp_timewait_input():
+ *
+ * Called by tcp_input() when a segment arrives for a connection in
+ * TIME_WAIT.
+ */
+
+static err_t
+tcp_timewait_input(struct tcp_pcb *pcb)
+{
+  if (TCP_SEQ_GT(seqno + tcplen, pcb->rcv_nxt)) {
+    pcb->rcv_nxt = seqno + tcplen;
+  }
+  if (tcplen > 0) {
+    tcp_ack_now(pcb);
+  }
+  return tcp_output(pcb);
+}
+
+/* tcp_process
+ *
+ * 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.
+ */
+
+static err_t
+tcp_process(struct tcp_pcb *pcb)
+{
+  struct tcp_seg *rseg;
+  u8_t acceptable = 0;
+  err_t err;
+
+
+  err = ERR_OK;
+
+  /* Process incoming RST segments. */
+  if (flags & TCP_RST) {
+    /* First, determine if the reset is acceptable. */
+    if (pcb->state == SYN_SENT) {
+      if (ackno == pcb->snd_nxt) {
+        acceptable = 1;
+      }
+    } else {
+      /*if (TCP_SEQ_GEQ(seqno, pcb->rcv_nxt) &&
+          TCP_SEQ_LEQ(seqno, pcb->rcv_nxt + pcb->rcv_wnd)) {
+      */
+      if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, pcb->rcv_nxt+pcb->rcv_wnd)) {
+        acceptable = 1;
+      }
+    }
+
+    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;
+      pcb->flags &= ~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;
+    }
+  }
+
+  /* Update the PCB (in)activity timer. */
+  pcb->tmr = tcp_ticks;
+  pcb->keep_cnt = 0;
+
+  /* Do different things depending on the TCP state. */
+  switch (pcb->state) {
+  case SYN_SENT:
+    LWIP_DEBUGF(TCP_INPUT_DEBUG, ("SYN-SENT: ackno %"U32_F" pcb->snd_nxt %"U32_F" unacked %"U32_F"\n", ackno,
+     pcb->snd_nxt, ntohl(pcb->unacked->tcphdr->seqno)));
+    /* received SYN ACK with expected sequence number? */
+    if ((flags & TCP_ACK) && (flags & TCP_SYN)
+        && ackno == ntohl(pcb->unacked->tcphdr->seqno) + 1) {
+      pcb->snd_buf++;
+      pcb->rcv_nxt = seqno + 1;
+      pcb->lastack = ackno;
+      pcb->snd_wnd = tcphdr->wnd;
+      pcb->snd_wl1 = seqno - 1; /* initialise to seqno - 1 to force window update */
+      pcb->state = ESTABLISHED;
+      pcb->cwnd = pcb->mss;
+      --pcb->snd_queuelen;
+      LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_process: SYN-SENT --queuelen %"U16_F"\n", (u16_t)pcb->snd_queuelen));
+      rseg = pcb->unacked;
+      pcb->unacked = rseg->next;
+      tcp_seg_free(rseg);
+
+      /* Parse any options in the SYNACK. */
+      tcp_parseopt(pcb);
+
+      /* Call the user specified function to call when sucessfully
+       * connected. */
+      TCP_EVENT_CONNECTED(pcb, ERR_OK, err);
+      tcp_ack(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(ackno, seqno + tcplen, &(iphdr->dest), &(iphdr->src),
+        tcphdr->dest, tcphdr->src);
+    }
+    break;
+  case SYN_RCVD:
+    if (flags & TCP_ACK &&
+       !(flags & TCP_RST)) {
+      /* 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
+        LWIP_ASSERT("pcb->accept != NULL", pcb->accept != NULL);
+#endif
+        /* Call the accept function. */
+        TCP_EVENT_ACCEPT(pcb, ERR_OK, err);
+        if (err != ERR_OK) {
+          /* If the accept function returns with an error, we abort
+           * the connection. */
+          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);
+        pcb->cwnd = pcb->mss;
+      }
+      /* incorrect ACK number */
+      else {
+        /* send RST */
+        tcp_rst(ackno, seqno + tcplen, &(iphdr->dest), &(iphdr->src),
+          tcphdr->dest, tcphdr->src);
+      }
+    }
+    break;
+  case CLOSE_WAIT:
+    /* FALLTHROUGH */
+  case ESTABLISHED:
+    tcp_receive(pcb);
+    if (flags & TCP_FIN) {
+      tcp_ack_now(pcb);
+      pcb->state = CLOSE_WAIT;
+    }
+    break;
+  case FIN_WAIT_1:
+    tcp_receive(pcb);
+    if (flags & TCP_FIN) {
+      if (flags & TCP_ACK && ackno == pcb->snd_nxt) {
+        LWIP_DEBUGF(TCP_DEBUG,
+          ("TCP connection closed %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
+        tcp_ack_now(pcb);
+        tcp_pcb_purge(pcb);
+        TCP_RMV(&tcp_active_pcbs, 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->state = FIN_WAIT_2;
+    }
+    break;
+  case FIN_WAIT_2:
+    tcp_receive(pcb);
+    if (flags & TCP_FIN) {
+      LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
+      tcp_ack_now(pcb);
+      tcp_pcb_purge(pcb);
+      TCP_RMV(&tcp_active_pcbs, 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) {
+      LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
+      tcp_ack_now(pcb);
+      tcp_pcb_purge(pcb);
+      TCP_RMV(&tcp_active_pcbs, 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) {
+      LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
+      pcb->state = CLOSED;
+      recv_flags = TF_CLOSED;
+    }
+    break;
+  default:
+    break;
+  }
+  return ERR_OK;
+}
+
+/* tcp_receive:
+ *
+ * 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, is 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
+ * i 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.
+ */
+
+static void
+tcp_receive(struct tcp_pcb *pcb)
+{
+  struct tcp_seg *next;
+#if TCP_QUEUE_OOSEQ
+  struct tcp_seg *prev, *cseg;
+#endif
+  struct pbuf *p;
+  s32_t off;
+  s16_t m;
+  u32_t right_wnd_edge;
+  u16_t new_tot_len;
+
+
+  if (flags & TCP_ACK) {
+    right_wnd_edge = pcb->snd_wnd + pcb->snd_wl1;
+
+    /* 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 && tcphdr->wnd > pcb->snd_wnd)) {
+      pcb->snd_wnd = tcphdr->wnd;
+      pcb->snd_wl1 = seqno;
+      pcb->snd_wl2 = ackno;
+      LWIP_DEBUGF(TCP_WND_DEBUG, ("tcp_receive: window update %"U32_F"\n", pcb->snd_wnd));
+#if TCP_WND_DEBUG
+    } else {
+      if (pcb->snd_wnd != tcphdr->wnd) {
+        LWIP_DEBUGF(TCP_WND_DEBUG, ("tcp_receive: no window update lastack %"U32_F" snd_max %"U32_F" ackno %"U32_F" wl1 %"U32_F" seqno %"U32_F" wl2 %"U32_F"\n",
+                               pcb->lastack, pcb->snd_max, ackno, pcb->snd_wl1, seqno, pcb->snd_wl2));
+      }
+#endif /* TCP_WND_DEBUG */
+    }
+
+
+    if (pcb->lastack == ackno) {
+      pcb->acked = 0;
+
+      if (pcb->snd_wl1 + pcb->snd_wnd == right_wnd_edge){
+        ++pcb->dupacks;
+        if (pcb->dupacks >= 3 && pcb->unacked != NULL) {
+          if (!(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,
+                                       ntohl(pcb->unacked->tcphdr->seqno)));
+            tcp_rexmit(pcb);
+            /* Set ssthresh to max (FlightSize / 2, 2*SMSS) */
+            /*pcb->ssthresh = LWIP_MAX((pcb->snd_max -
+                                      pcb->lastack) / 2,
+                                      2 * pcb->mss);*/
+            /* Set ssthresh to half of the minimum of the currenct cwnd and the advertised window */
+            if(pcb->cwnd > pcb->snd_wnd)
+              pcb->ssthresh = pcb->snd_wnd / 2;
+            else
+              pcb->ssthresh = pcb->cwnd / 2;
+
+            pcb->cwnd = pcb->ssthresh + 3 * pcb->mss;
+            pcb->flags |= TF_INFR;
+          } else {
+            /* Inflate the congestion window, but not if it means that
+               the value overflows. */
+            if ((u16_t)(pcb->cwnd + pcb->mss) > pcb->cwnd) {
+              pcb->cwnd += pcb->mss;
+            }
+          }
+        }
+      } else {
+        LWIP_DEBUGF(TCP_FR_DEBUG, ("tcp_receive: dupack averted %"U32_F" %"U32_F"\n",
+                                   pcb->snd_wl1 + pcb->snd_wnd, right_wnd_edge));
+      }
+    } else
+      /*if (TCP_SEQ_LT(pcb->lastack, ackno) &&
+        TCP_SEQ_LEQ(ackno, pcb->snd_max)) { */
+      if(TCP_SEQ_BETWEEN(ackno, pcb->lastack+1, pcb->snd_max)){
+      /* We come here when the ACK acknowledges new data. */
+      
+      /* 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) {
+        pcb->flags &= ~TF_INFR;
+        pcb->cwnd = pcb->ssthresh;
+      }
+
+      /* Reset the number of retransmissions. */
+      pcb->nrtx = 0;
+
+      /* Reset the retransmission time-out. */
+      pcb->rto = (pcb->sa >> 3) + pcb->sv;
+
+      /* Update the send buffer space. */
+      pcb->acked = ackno - pcb->lastack;
+
+      pcb->snd_buf += pcb->acked;
+
+      /* 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) {
+          if ((u16_t)(pcb->cwnd + pcb->mss) > pcb->cwnd) {
+            pcb->cwnd += pcb->mss;
+          }
+          LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_receive: slow start cwnd %"U16_F"\n", pcb->cwnd));
+        } else {
+          u16_t new_cwnd = (pcb->cwnd + pcb->mss * pcb->mss / pcb->cwnd);
+          if (new_cwnd > pcb->cwnd) {
+            pcb->cwnd = new_cwnd;
+          }
+          LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_receive: congestion avoidance cwnd %"U16_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?
+                                    ntohl(pcb->unacked->tcphdr->seqno): 0,
+                                    pcb->unacked != NULL?
+                                    ntohl(pcb->unacked->tcphdr->seqno) + TCP_TCPLEN(pcb->unacked): 0));
+
+      /* Remove segment from the unacknowledged list if the incoming
+         ACK acknowlegdes them. */
+      while (pcb->unacked != NULL &&
+             TCP_SEQ_LEQ(ntohl(pcb->unacked->tcphdr->seqno) +
+                         TCP_TCPLEN(pcb->unacked), ackno)) {
+        LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: removing %"U32_F":%"U32_F" from pcb->unacked\n",
+                                      ntohl(pcb->unacked->tcphdr->seqno),
+                                      ntohl(pcb->unacked->tcphdr->seqno) +
+                                      TCP_TCPLEN(pcb->unacked)));
+
+        next = pcb->unacked;
+        pcb->unacked = pcb->unacked->next;
+
+        LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_receive: queuelen %"U16_F" ... ", (u16_t)pcb->snd_queuelen));
+        pcb->snd_queuelen -= pbuf_clen(next->p);
+        tcp_seg_free(next);
+
+        LWIP_DEBUGF(TCP_QLEN_DEBUG, ("%"U16_F" (after freeing unacked)\n", (u16_t)pcb->snd_queuelen));
+        if (pcb->snd_queuelen != 0) {
+          LWIP_ASSERT("tcp_receive: valid queue length", pcb->unacked != NULL ||
+                      pcb->unsent != NULL);
+        }
+      }
+      pcb->polltmr = 0;
+    }
+
+    /* 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. */
+    while (pcb->unsent != NULL &&
+           /*TCP_SEQ_LEQ(ntohl(pcb->unsent->tcphdr->seqno) + TCP_TCPLEN(pcb->unsent), ackno) &&
+             TCP_SEQ_LEQ(ackno, pcb->snd_max)*/
+           TCP_SEQ_BETWEEN(ackno, ntohl(pcb->unsent->tcphdr->seqno) + TCP_TCPLEN(pcb->unsent), pcb->snd_max)
+           ) {
+      LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: removing %"U32_F":%"U32_F" from pcb->unsent\n",
+                                    ntohl(pcb->unsent->tcphdr->seqno), ntohl(pcb->unsent->tcphdr->seqno) +
+                                    TCP_TCPLEN(pcb->unsent)));
+
+      next = pcb->unsent;
+      pcb->unsent = pcb->unsent->next;
+      LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_receive: queuelen %"U16_F" ... ", (u16_t)pcb->snd_queuelen));
+      pcb->snd_queuelen -= pbuf_clen(next->p);
+      tcp_seg_free(next);
+      LWIP_DEBUGF(TCP_QLEN_DEBUG, ("%"U16_F" (after freeing unsent)\n", (u16_t)pcb->snd_queuelen));
+      if (pcb->snd_queuelen != 0) {
+        LWIP_ASSERT("tcp_receive: valid queue length",
+          pcb->unacked != NULL || pcb->unsent != NULL);
+      }
+
+      if (pcb->unsent != NULL) {
+        pcb->snd_nxt = htonl(pcb->unsent->tcphdr->seqno);
+      }
+    }
+    /* End of ACK for new data processing. */
+
+    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)) {
+      m = tcp_ticks - pcb->rttest;
+
+      LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: experienced rtt %"U16_F" ticks (%"U16_F" msec).\n",
+                                  m, m * TCP_SLOW_INTERVAL));
+
+      /* This is taken directly from VJs original code in his paper */
+      m = m - (pcb->sa >> 3);
+      pcb->sa += m;
+      if (m < 0) {
+        m = -m;
+      }
+      m = m - (pcb->sv >> 2);
+      pcb->sv += m;
+      pcb->rto = (pcb->sa >> 3) + pcb->sv;
+
+      LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: RTO %"U16_F" (%"U16_F" miliseconds)\n",
+                                  pcb->rto, pcb->rto * TCP_SLOW_INTERVAL));
+
+      pcb->rttest = 0;
+    }
+  }
+
+  /* If the incoming segment contains data, we must process it
+     further. */
+  if (tcplen > 0) {
+    /* 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.*/
+      
+      off = pcb->rcv_nxt - seqno;
+      p = inseg.p;
+      if (inseg.p->len < off) {
+        new_tot_len = inseg.p->tot_len - off;
+        while (p->len < off) {
+          off -= p->len;
+          /* KJM following line changed (with addition of new_tot_len var)
+             to fix bug #9076
+             inseg.p->tot_len -= p->len; */
+          p->tot_len = new_tot_len;
+          p->len = 0;
+          p = p->next;
+        }
+        pbuf_header(p, -off);
+      } else {
+        pbuf_header(inseg.p, -off);
+      }
+      /* KJM following line changed to use p->payload rather than inseg->p->payload
+         to fix bug #9076 */
+      inseg.dataptr = p->payload;
+      inseg.len -= pcb->rcv_nxt - seqno;
+      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_GEQ(seqno, pcb->rcv_nxt) &&
+      TCP_SEQ_LT(seqno, pcb->rcv_nxt + pcb->rcv_wnd)) {*/
+    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. */
+#if TCP_QUEUE_OOSEQ
+        if (pcb->ooseq != NULL &&
+            TCP_SEQ_LEQ(pcb->ooseq->tcphdr->seqno, seqno + inseg.len)) {
+          /* We have to trim the second edge of the incoming
+             segment. */
+          inseg.len = pcb->ooseq->tcphdr->seqno - seqno;
+          pbuf_realloc(inseg.p, inseg.len);
+        }
+#endif /* TCP_QUEUE_OOSEQ */
+
+        tcplen = TCP_TCPLEN(&inseg);
+
+        /* First received FIN will be ACKed +1, on any successive (duplicate)
+         * FINs we are already in CLOSE_WAIT and have already done +1.
+         */
+        if (pcb->state != CLOSE_WAIT) {
+          pcb->rcv_nxt += tcplen;
+        }
+
+        /* Update the receiver's (our) window. */
+        if (pcb->rcv_wnd < tcplen) {
+          pcb->rcv_wnd = 0;
+        } else {
+          pcb->rcv_wnd -= tcplen;
+        }
+
+        /* 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
+           is now in sequence. */
+        while (pcb->ooseq != NULL &&
+               pcb->ooseq->tcphdr->seqno == pcb->rcv_nxt) {
+
+          cseg = pcb->ooseq;
+          seqno = pcb->ooseq->tcphdr->seqno;
+
+          pcb->rcv_nxt += TCP_TCPLEN(cseg);
+          if (pcb->rcv_wnd < TCP_TCPLEN(cseg)) {
+            pcb->rcv_wnd = 0;
+          } else {
+            pcb->rcv_wnd -= TCP_TCPLEN(cseg);
+          }
+          if (cseg->p->tot_len > 0) {
+            /* Chain this pbuf onto the pbuf that we will pass 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;
+          }
+
+
+          pcb->ooseq = cseg->next;
+          tcp_seg_free(cseg);
+        }
+#endif /* TCP_QUEUE_OOSEQ */
+
+
+        /* Acknowledge the segment(s). */
+        tcp_ack(pcb);
+
+      } else {
+        /* We get here if the incoming segment is out-of-sequence. */
+        tcp_ack_now(pcb);
+#if TCP_QUEUE_OOSEQ
+        /* We queue the segment on the ->ooseq queue. */
+        if (pcb->ooseq == NULL) {
+          pcb->ooseq = tcp_seg_copy(&inseg);
+        } 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. */
+
+          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 the old segment with the new
+                   one. */
+                cseg = tcp_seg_copy(&inseg);
+                if (cseg != NULL) {
+                  cseg->next = next->next;
+                  if (prev != NULL) {
+                    prev->next = cseg;
+                  } else {
+                    pcb->ooseq = cseg;
+                  }
+                }
+                break;
+              } else {
+                /* Either the lenghts 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. */
+
+                  if (TCP_SEQ_GT(seqno + inseg.len, next->tcphdr->seqno)) {
+                    /* We need to trim the incoming segment. */
+                    inseg.len = next->tcphdr->seqno - seqno;
+                    pbuf_realloc(inseg.p, inseg.len);
+                  }
+                  cseg = tcp_seg_copy(&inseg);
+                  if (cseg != NULL) {
+                    cseg->next = next;
+                    pcb->ooseq = cseg;
+                  }
+                  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 and insert the
+                   incoming segment and trim the previous segment, if
+                   needed. */
+                if (TCP_SEQ_GT(seqno + inseg.len, next->tcphdr->seqno)) {
+                  /* We need to trim the incoming segment. */
+                  inseg.len = next->tcphdr->seqno - seqno;
+                  pbuf_realloc(inseg.p, inseg.len);
+                }
+
+                cseg = tcp_seg_copy(&inseg);
+                if (cseg != NULL) {
+                  cseg->next = next;
+                  prev->next = cseg;
+                  if (TCP_SEQ_GT(prev->tcphdr->seqno + prev->len, seqno)) {
+                    /* We need to trim the prev segment. */
+                    prev->len = seqno - prev->tcphdr->seqno;
+                    pbuf_realloc(prev->p, prev->len);
+                  }
+                }
+                break;
+              }
+              /* 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)) {
+                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 = seqno - next->tcphdr->seqno;
+                    pbuf_realloc(next->p, next->len);
+                  }
+                }
+                break;
+              }
+            }
+            prev = next;
+          }
+        }
+#endif /* TCP_QUEUE_OOSEQ */
+
+      }
+    } else {
+      /*if (TCP_SEQ_GT(pcb->rcv_nxt, seqno) ||
+        TCP_SEQ_GEQ(seqno, pcb->rcv_nxt + pcb->rcv_wnd)) {*/
+      if(!TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, pcb->rcv_nxt + pcb->rcv_wnd-1)){
+        tcp_ack_now(pcb);
+      }
+    }
+  } else {
+    /* Segments with length 0 is taken care of here. Segments that
+       fall out of the window are ACKed. */
+    /*if (TCP_SEQ_GT(pcb->rcv_nxt, seqno) ||
+      TCP_SEQ_GEQ(seqno, pcb->rcv_nxt + pcb->rcv_wnd)) {*/
+    if(!TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, pcb->rcv_nxt + pcb->rcv_wnd-1)){
+      tcp_ack_now(pcb);
+    }
+  }
+}
+
+/*
+ * tcp_parseopt:
+ *
+ * Parses the options contained in the incoming segment. (Code taken
+ * from uIP with only small changes.)
+ *
+ */
+
+static void
+tcp_parseopt(struct tcp_pcb *pcb)
+{
+  u8_t c;
+  u8_t *opts, opt;
+  u16_t mss;
+
+  opts = (u8_t *)tcphdr + TCP_HLEN;
+
+  /* Parse the TCP MSS option, if present. */
+  if(TCPH_HDRLEN(tcphdr) > 0x5) {
+    for(c = 0; c < (TCPH_HDRLEN(tcphdr) - 5) << 2 ;) {
+      opt = opts[c];
+      if (opt == 0x00) {
+        /* End of options. */
+  break;
+      } else if (opt == 0x01) {
+        ++c;
+        /* NOP option. */
+      } else if (opt == 0x02 &&
+        opts[c + 1] == 0x04) {
+        /* An MSS option with the right option length. */
+        mss = (opts[c + 2] << 8) | opts[c + 3];
+        pcb->mss = mss > TCP_MSS? TCP_MSS: mss;
+
+        /* And we are done processing options. */
+        break;
+      } else {
+  if (opts[c + 1] == 0) {
+          /* If the length field is zero, the options are malformed
+             and we don't process them further. */
+          break;
+        }
+        /* All other options have a length field, so that we easily
+           can skip past them. */
+        c += opts[c + 1];
+      }
+    }
+  }
+}
+#endif /* LWIP_TCP */
+
+
diff --git a/lib/lwip/src/core/tcp_out.c b/lib/lwip/src/core/tcp_out.c
new file mode 100644
index 0000000..62982bd
--- /dev/null
+++ b/lib/lwip/src/core/tcp_out.c
@@ -0,0 +1,721 @@
+/**
+ * @file
+ *
+ * Transmission Control Protocol, outgoing traffic
+ *
+ * The output functions of TCP.
+ *
+ */
+
+/*
+ * 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 <string.h>
+
+#include "lwip/def.h"
+#include "lwip/opt.h"
+#include "lwip/mem.h"
+#include "lwip/memp.h"
+#include "lwip/sys.h"
+#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+#include "lwip/inet.h"
+#include "lwip/tcp.h"
+#include "lwip/stats.h"
+
+#if LWIP_TCP
+
+/* Forward declarations.*/
+static void tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb);
+
+err_t
+tcp_send_ctrl(struct tcp_pcb *pcb, u8_t flags)
+{
+  /* no data, no length, flags, copy=1, no optdata, no optdatalen */
+  return tcp_enqueue(pcb, NULL, 0, flags, 1, NULL, 0);
+}
+
+/**
+ * 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().
+ * 
+ * @arg pcb Protocol control block of the TCP connection to enqueue data for. 
+ * 
+ * @see tcp_write()
+ */
+
+err_t
+tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t copy)
+{
+  LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_write(pcb=%p, arg=%p, len=%"U16_F", copy=%"U16_F")\n", (void *)pcb,
+    arg, len, (u16_t)copy));
+  /* connection is in valid state for data transmission? */
+  if (pcb->state == ESTABLISHED ||
+     pcb->state == CLOSE_WAIT ||
+     pcb->state == SYN_SENT ||
+     pcb->state == SYN_RCVD) {
+    if (len > 0) {
+      return tcp_enqueue(pcb, (void *)arg, len, 0, copy, NULL, 0);
+    }
+    return ERR_OK;
+  } else {
+    LWIP_DEBUGF(TCP_OUTPUT_DEBUG | DBG_STATE | 3, ("tcp_write() called in invalid state\n"));
+    return ERR_CONN;
+  }
+}
+
+/**
+ * Enqueue either data or TCP options (but not both) for tranmission
+ * 
+ * 
+ * 
+ * @arg pcb Protocol control block for the TCP connection to enqueue data for.
+ * @arg arg Pointer to the data to be enqueued for sending.
+ * @arg len Data length in bytes
+ * @arg flags
+ * @arg copy 1 if data must be copied, 0 if data is non-volatile and can be
+ * referenced.
+ * @arg optdata
+ * @arg optlen
+ */
+err_t
+tcp_enqueue(struct tcp_pcb *pcb, void *arg, u16_t len,
+  u8_t flags, u8_t copy,
+  u8_t *optdata, u8_t optlen)
+{
+  struct pbuf *p;
+  struct tcp_seg *seg, *useg, *queue;
+  u32_t left, seqno;
+  u16_t seglen;
+  void *ptr;
+  u8_t queuelen;
+
+  LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_enqueue(pcb=%p, arg=%p, len=%"U16_F", flags=%"X16_F", copy=%"U16_F")\n",
+    (void *)pcb, arg, len, (u16_t)flags, (u16_t)copy));
+  LWIP_ASSERT("tcp_enqueue: len == 0 || optlen == 0 (programmer violates API)",
+      len == 0 || optlen == 0);
+  LWIP_ASSERT("tcp_enqueue: arg == NULL || optdata == NULL (programmer violates API)",
+      arg == NULL || optdata == NULL);
+  /* fail on too much data */
+  if (len > pcb->snd_buf) {
+    LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_enqueue: too much data (len=%"U16_F" > snd_buf=%"U16_F")\n", len, pcb->snd_buf));
+    return ERR_MEM;
+  }
+  left = len;
+  ptr = arg;
+
+  /* seqno will be the sequence number of the first segment enqueued
+   * by the call to this function. */
+  seqno = pcb->snd_lbb;
+
+  LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue: queuelen: %"U16_F"\n", (u16_t)pcb->snd_queuelen));
+
+  /* If total number of pbufs on the unsent/unacked queues exceeds the
+   * configured maximum, return an error */
+  queuelen = pcb->snd_queuelen;
+  if (queuelen >= TCP_SND_QUEUELEN) {
+    LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_enqueue: too long queue %"U16_F" (max %"U16_F")\n", queuelen, TCP_SND_QUEUELEN));
+    TCP_STATS_INC(tcp.memerr);
+    return ERR_MEM;
+  }
+  if (queuelen != 0) {
+    LWIP_ASSERT("tcp_enqueue: pbufs on queue => at least one queue non-empty",
+      pcb->unacked != NULL || pcb->unsent != NULL);
+  } else {
+    LWIP_ASSERT("tcp_enqueue: no pbufs on queue => both queues empty",
+      pcb->unacked == NULL && pcb->unsent == NULL);
+  }
+
+  /* First, break up the data into segments and tuck them together in
+   * the local "queue" variable. */
+  useg = queue = seg = NULL;
+  seglen = 0;
+  while (queue == NULL || left > 0) {
+
+    /* The segment length should be the MSS if the data to be enqueued
+     * is larger than the MSS. */
+    seglen = left > pcb->mss? pcb->mss: left;
+
+    /* Allocate memory for tcp_seg, and fill in fields. */
+    seg = memp_malloc(MEMP_TCP_SEG);
+    if (seg == NULL) {
+      LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_enqueue: could not allocate memory for tcp_seg\n"));
+      goto memerr;
+    }
+    seg->next = NULL;
+    seg->p = NULL;
+
+    /* first segment of to-be-queued data? */
+    if (queue == NULL) {
+      queue = seg;
+    }
+    /* subsequent segments of to-be-queued data */
+    else {
+      /* Attach the segment to the end of the queued segments */
+      LWIP_ASSERT("useg != NULL", useg != NULL);
+      useg->next = seg;
+    }
+    /* remember last segment of to-be-queued data for next iteration */
+    useg = seg;
+
+    /* If copy is set, memory should be allocated
+     * and data copied into pbuf, otherwise data comes from
+     * ROM or other static memory, and need not be copied. If
+     * optdata is != NULL, we have options instead of data. */
+     
+    /* options? */
+    if (optdata != NULL) {
+      if ((seg->p = pbuf_alloc(PBUF_TRANSPORT, optlen, PBUF_RAM)) == NULL) {
+        goto memerr;
+      }
+      ++queuelen;
+      seg->dataptr = seg->p->payload;
+    }
+    /* copy from volatile memory? */
+    else if (copy) {
+      if ((seg->p = pbuf_alloc(PBUF_TRANSPORT, seglen, PBUF_RAM)) == NULL) {
+        LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_enqueue : could not allocate memory for pbuf copy size %"U16_F"\n", seglen));
+        goto memerr;
+      }
+      ++queuelen;
+      if (arg != NULL) {
+        memcpy(seg->p->payload, ptr, seglen);
+      }
+      seg->dataptr = seg->p->payload;
+    }
+    /* do not copy data */
+    else {
+      /* 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.
+       */
+      if ((p = pbuf_alloc(PBUF_TRANSPORT, seglen, PBUF_ROM)) == NULL) {
+        LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_enqueue: could not allocate memory for zero-copy pbuf\n"));
+        goto memerr;
+      }
+      ++queuelen;
+      /* reference the non-volatile payload data */
+      p->payload = ptr;
+      seg->dataptr = ptr;
+
+      /* Second, allocate a pbuf for the headers. */
+      if ((seg->p = pbuf_alloc(PBUF_TRANSPORT, 0, PBUF_RAM)) == NULL) {
+        /* If allocation fails, we have to deallocate the data pbuf as
+         * well. */
+        pbuf_free(p);
+        LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_enqueue: could not allocate memory for header pbuf\n"));
+        goto memerr;
+      }
+      ++queuelen;
+
+      /* Concatenate the headers and data pbufs together. */
+      pbuf_cat(seg->p/*header*/, p/*data*/);
+      p = NULL;
+    }
+
+    /* Now that there are more segments queued, we check again if the
+    length of the queue exceeds the configured maximum. */
+    if (queuelen > TCP_SND_QUEUELEN) {
+      LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_enqueue: queue too long %"U16_F" (%"U16_F")\n", queuelen, TCP_SND_QUEUELEN));
+      goto memerr;
+    }
+
+    seg->len = seglen;
+
+    /* build TCP header */
+    if (pbuf_header(seg->p, TCP_HLEN)) {
+      LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_enqueue: no room for TCP header in pbuf.\n"));
+      TCP_STATS_INC(tcp.err);
+      goto memerr;
+    }
+    seg->tcphdr = seg->p->payload;
+    seg->tcphdr->src = htons(pcb->local_port);
+    seg->tcphdr->dest = htons(pcb->remote_port);
+    seg->tcphdr->seqno = htonl(seqno);
+    seg->tcphdr->urgp = 0;
+    TCPH_FLAGS_SET(seg->tcphdr, flags);
+    /* don't fill in tcphdr->ackno and tcphdr->wnd until later */
+
+    /* Copy the options into the header, if they are present. */
+    if (optdata == NULL) {
+      TCPH_HDRLEN_SET(seg->tcphdr, 5);
+    }
+    else {
+      TCPH_HDRLEN_SET(seg->tcphdr, (5 + optlen / 4));
+      /* Copy options into data portion of segment.
+       Options can thus only be sent in non data carrying
+       segments such as SYN|ACK. */
+      memcpy(seg->dataptr, optdata, optlen);
+    }
+    LWIP_DEBUGF(TCP_OUTPUT_DEBUG | DBG_TRACE, ("tcp_enqueue: queueing %"U32_F":%"U32_F" (0x%"X16_F")\n",
+      ntohl(seg->tcphdr->seqno),
+      ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg),
+      (u16_t)flags));
+
+    left -= seglen;
+    seqno += seglen;
+    ptr = (void *)((u8_t *)ptr + seglen);
+  }
+
+  /* Now that the data to be enqueued has been broken up into TCP
+  segments in the queue variable, we add them to the end of the
+  pcb->unsent queue. */
+  if (pcb->unsent == NULL) {
+    useg = NULL;
+  }
+  else {
+    for (useg = pcb->unsent; useg->next != NULL; useg = useg->next);
+  }
+  /* { useg is last segment on the unsent queue, NULL if list is empty } */
+
+  /* If there is room in the last pbuf on the unsent queue,
+  chain the first pbuf on the queue together with that. */
+  if (useg != NULL &&
+    TCP_TCPLEN(useg) != 0 &&
+    !(TCPH_FLAGS(useg->tcphdr) & (TCP_SYN | TCP_FIN)) &&
+    !(flags & (TCP_SYN | TCP_FIN)) &&
+    /* fit within max seg size */
+    useg->len + queue->len <= pcb->mss) {
+    /* Remove TCP header from first segment of our to-be-queued list */
+    pbuf_header(queue->p, -TCP_HLEN);
+    pbuf_cat(useg->p, queue->p);
+    useg->len += queue->len;
+    useg->next = queue->next;
+
+    LWIP_DEBUGF(TCP_OUTPUT_DEBUG | DBG_TRACE | DBG_STATE, ("tcp_enqueue: chaining segments, new len %"U16_F"\n", useg->len));
+    if (seg == queue) {
+      seg = NULL;
+    }
+    memp_free(MEMP_TCP_SEG, queue);
+  }
+  else {
+    /* empty list */
+    if (useg == NULL) {
+      /* initialize list with this segment */
+      pcb->unsent = queue;
+    }
+    /* enqueue segment */
+    else {
+      useg->next = queue;
+    }
+  }
+  if ((flags & TCP_SYN) || (flags & TCP_FIN)) {
+    ++len;
+  }
+  pcb->snd_lbb += len;
+
+  pcb->snd_buf -= len;
+
+  /* update number of segments on the queues */
+  pcb->snd_queuelen = queuelen;
+  LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue: %"S16_F" (after enqueued)\n", pcb->snd_queuelen));
+  if (pcb->snd_queuelen != 0) {
+    LWIP_ASSERT("tcp_enqueue: valid queue length",
+      pcb->unacked != NULL || pcb->unsent != NULL);
+  }
+
+  /* Set the PSH flag in the last segment that we enqueued, but only
+  if the segment has data (indicated by seglen > 0). */
+  if (seg != NULL && seglen > 0 && seg->tcphdr != NULL) {
+    TCPH_SET_FLAG(seg->tcphdr, TCP_PSH);
+  }
+
+  return ERR_OK;
+memerr:
+  TCP_STATS_INC(tcp.memerr);
+
+  if (queue != NULL) {
+    tcp_segs_free(queue);
+  }
+  if (pcb->snd_queuelen != 0) {
+    LWIP_ASSERT("tcp_enqueue: valid queue length", pcb->unacked != NULL ||
+      pcb->unsent != NULL);
+  }
+  LWIP_DEBUGF(TCP_QLEN_DEBUG | DBG_STATE, ("tcp_enqueue: %"S16_F" (with mem err)\n", pcb->snd_queuelen));
+  return ERR_MEM;
+}
+
+/* find out what we can send and send it */
+err_t
+tcp_output(struct tcp_pcb *pcb)
+{
+  struct pbuf *p;
+  struct tcp_hdr *tcphdr;
+  struct tcp_seg *seg, *useg;
+  u32_t wnd;
+#if TCP_CWND_DEBUG
+  s16_t i = 0;
+#endif /* TCP_CWND_DEBUG */
+
+  /* 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;
+
+  /* useg should point to last segment on unacked queue */
+  useg = pcb->unacked;
+  if (useg != NULL) {
+    for (; useg->next != NULL; useg = useg->next);
+  }                                                                             
+   
+  /* If the TF_ACK_NOW flag is set and no data will be sent (either
+   * because the ->unsent queue is empty or because the window does
+   * not allow it), construct an empty ACK segment and send it.
+   *
+   * If data is to be sent, we will just piggyback the ACK (see below).
+   */
+  if (pcb->flags & TF_ACK_NOW &&
+     (seg == NULL ||
+      ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len > wnd)) {
+    p = pbuf_alloc(PBUF_IP, TCP_HLEN, PBUF_RAM);
+    if (p == NULL) {
+      LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: (ACK) could not allocate pbuf\n"));
+      return ERR_BUF;
+    }
+    LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: sending ACK for %"U32_F"\n", pcb->rcv_nxt));
+    /* remove ACK flags from the PCB, as we send an empty ACK now */
+    pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW);
+
+    tcphdr = p->payload;
+    tcphdr->src = htons(pcb->local_port);
+    tcphdr->dest = htons(pcb->remote_port);
+    tcphdr->seqno = htonl(pcb->snd_nxt);
+    tcphdr->ackno = htonl(pcb->rcv_nxt);
+    TCPH_FLAGS_SET(tcphdr, TCP_ACK);
+    tcphdr->wnd = htons(pcb->rcv_wnd);
+    tcphdr->urgp = 0;
+    TCPH_HDRLEN_SET(tcphdr, 5);
+
+    tcphdr->chksum = 0;
+#if CHECKSUM_GEN_TCP
+    tcphdr->chksum = inet_chksum_pseudo(p, &(pcb->local_ip), &(pcb->remote_ip),
+          IP_PROTO_TCP, p->tot_len);
+#endif
+    ip_output(p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, pcb->tos,
+        IP_PROTO_TCP);
+    pbuf_free(p);
+
+    return ERR_OK;
+  }
+
+#if TCP_OUTPUT_DEBUG
+  if (seg == NULL) {
+    LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: nothing to send (%p)\n", (void*)pcb->unsent));
+  }
+#endif /* TCP_OUTPUT_DEBUG */
+#if TCP_CWND_DEBUG
+  if (seg == NULL) {
+    LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_output: snd_wnd %"U32_F", cwnd %"U16_F", wnd %"U32_F", seg == NULL, ack %"U32_F"\n",
+                            pcb->snd_wnd, pcb->cwnd, wnd,
+                            pcb->lastack));
+  } else {
+    LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_output: snd_wnd %"U32_F", cwnd %"U16_F", wnd %"U32_F", effwnd %"U32_F", seq %"U32_F", ack %"U32_F"\n",
+                            pcb->snd_wnd, pcb->cwnd, wnd,
+                            ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len,
+                            ntohl(seg->tcphdr->seqno), pcb->lastack));
+  }
+#endif /* TCP_CWND_DEBUG */
+  /* data available and window allows it to be sent? */
+  while (seg != NULL &&
+  ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len <= wnd) {
+#if TCP_CWND_DEBUG
+    LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_output: snd_wnd %"U32_F", cwnd %"U16_F", wnd %"U32_F", effwnd %"U32_F", seq %"U32_F", ack %"U32_F", i %"S16_F"\n",
+                            pcb->snd_wnd, pcb->cwnd, wnd,
+                            ntohl(seg->tcphdr->seqno) + seg->len -
+                            pcb->lastack,
+                            ntohl(seg->tcphdr->seqno), pcb->lastack, i));
+    ++i;
+#endif /* TCP_CWND_DEBUG */
+
+    pcb->unsent = seg->next;
+
+    if (pcb->state != SYN_SENT) {
+      TCPH_SET_FLAG(seg->tcphdr, TCP_ACK);
+      pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW);
+    }
+
+    tcp_output_segment(seg, pcb);
+    pcb->snd_nxt = ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg);
+    if (TCP_SEQ_LT(pcb->snd_max, pcb->snd_nxt)) {
+      pcb->snd_max = pcb->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 at the head. We need to check for
+         * this case. -STJ Jul 27, 2004 */
+        if (TCP_SEQ_LT(ntohl(seg->tcphdr->seqno), ntohl(useg->tcphdr->seqno))){
+          /* add segment to head of unacked list */
+          seg->next = pcb->unacked;
+          pcb->unacked = 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;
+  }
+  return ERR_OK;
+}
+
+/**
+ * Actually send a TCP segment over IP
+ */
+static void
+tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb)
+{
+  u16_t len;
+  struct netif *netif;
+
+  /* The TCP header has already been constructed, but the ackno and
+   wnd fields remain. */
+  seg->tcphdr->ackno = htonl(pcb->rcv_nxt);
+
+  /* silly window avoidance */
+  if (pcb->rcv_wnd < pcb->mss) {
+    seg->tcphdr->wnd = 0;
+  } else {
+    /* advertise our receive window size in this TCP segment */
+    seg->tcphdr->wnd = htons(pcb->rcv_wnd);
+  }
+
+  /* If we don't have a local IP address, we get one by
+     calling ip_route(). */
+  if (ip_addr_isany(&(pcb->local_ip))) {
+    netif = ip_route(&(pcb->remote_ip));
+    if (netif == NULL) {
+      return;
+    }
+    ip_addr_set(&(pcb->local_ip), &(netif->ip_addr));
+  }
+
+  pcb->rtime = 0;
+
+  if (pcb->rttest == 0) {
+    pcb->rttest = tcp_ticks;
+    pcb->rtseq = 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",
+          htonl(seg->tcphdr->seqno), htonl(seg->tcphdr->seqno) +
+          seg->len));
+
+  len = (u16_t)((u8_t *)seg->tcphdr - (u8_t *)seg->p->payload);
+
+  seg->p->len -= len;
+  seg->p->tot_len -= len;
+
+  seg->p->payload = seg->tcphdr;
+
+  seg->tcphdr->chksum = 0;
+#if CHECKSUM_GEN_TCP
+  seg->tcphdr->chksum = inet_chksum_pseudo(seg->p,
+             &(pcb->local_ip),
+             &(pcb->remote_ip),
+             IP_PROTO_TCP, seg->p->tot_len);
+#endif
+  TCP_STATS_INC(tcp.xmit);
+
+  ip_output(seg->p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, pcb->tos,
+      IP_PROTO_TCP);
+}
+
+void
+tcp_rst(u32_t seqno, u32_t ackno,
+  struct ip_addr *local_ip, struct ip_addr *remote_ip,
+  u16_t local_port, u16_t remote_port)
+{
+  struct pbuf *p;
+  struct tcp_hdr *tcphdr;
+  p = pbuf_alloc(PBUF_IP, TCP_HLEN, PBUF_RAM);
+  if (p == NULL) {
+      LWIP_DEBUGF(TCP_DEBUG, ("tcp_rst: could not allocate memory for pbuf\n"));
+      return;
+  }
+
+  tcphdr = p->payload;
+  tcphdr->src = htons(local_port);
+  tcphdr->dest = htons(remote_port);
+  tcphdr->seqno = htonl(seqno);
+  tcphdr->ackno = htonl(ackno);
+  TCPH_FLAGS_SET(tcphdr, TCP_RST | TCP_ACK);
+  tcphdr->wnd = htons(TCP_WND);
+  tcphdr->urgp = 0;
+  TCPH_HDRLEN_SET(tcphdr, 5);
+
+  tcphdr->chksum = 0;
+#if CHECKSUM_GEN_TCP
+  tcphdr->chksum = inet_chksum_pseudo(p, local_ip, remote_ip,
+              IP_PROTO_TCP, p->tot_len);
+#endif
+  TCP_STATS_INC(tcp.xmit);
+   /* Send output with hardcoded TTL since we have no access to the pcb */
+  ip_output(p, local_ip, remote_ip, TCP_TTL, 0, IP_PROTO_TCP);
+  pbuf_free(p);
+  LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_rst: seqno %"U32_F" ackno %"U32_F".\n", seqno, ackno));
+}
+
+/* requeue all unacked segments for retransmission */
+void
+tcp_rexmit_rto(struct tcp_pcb *pcb)
+{
+  struct tcp_seg *seg;
+
+  if (pcb->unacked == NULL) {
+    return;
+  }
+
+  /* Move all unacked segments to the head of the unsent queue */
+  for (seg = pcb->unacked; seg->next != NULL; seg = seg->next);
+  /* concatenate unsent queue after unacked queue */
+  seg->next = pcb->unsent;
+  /* unsent queue is the concatenated queue (of unacked, unsent) */
+  pcb->unsent = pcb->unacked;
+  /* unacked queue is now empty */
+  pcb->unacked = NULL;
+
+  pcb->snd_nxt = ntohl(pcb->unsent->tcphdr->seqno);
+  /* increment number of retransmissions */
+  ++pcb->nrtx;
+
+  /* Don't take any RTT measurements after retransmitting. */
+  pcb->rttest = 0;
+
+  /* Do the actual retransmission */
+  tcp_output(pcb);
+}
+
+void
+tcp_rexmit(struct tcp_pcb *pcb)
+{
+  struct tcp_seg *seg;
+
+  if (pcb->unacked == NULL) {
+    return;
+  }
+
+  /* Move the first unacked segment to the unsent queue */
+  seg = pcb->unacked->next;
+  pcb->unacked->next = pcb->unsent;
+  pcb->unsent = pcb->unacked;
+  pcb->unacked = seg;
+
+  pcb->snd_nxt = ntohl(pcb->unsent->tcphdr->seqno);
+
+  ++pcb->nrtx;
+
+  /* Don't take any rtt measurements after retransmitting. */
+  pcb->rttest = 0;
+
+  /* Do the actual retransmission. */
+  tcp_output(pcb);
+
+}
+
+
+void
+tcp_keepalive(struct tcp_pcb *pcb)
+{
+   struct pbuf *p;
+   struct tcp_hdr *tcphdr;
+
+   LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: sending KEEPALIVE probe to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+                           ip4_addr1(&pcb->remote_ip), ip4_addr2(&pcb->remote_ip),
+                           ip4_addr3(&pcb->remote_ip), ip4_addr4(&pcb->remote_ip)));
+
+   LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: tcp_ticks %"U32_F"   pcb->tmr %"U32_F"  pcb->keep_cnt %"U16_F"\n", tcp_ticks, pcb->tmr, pcb->keep_cnt));
+   
+   p = pbuf_alloc(PBUF_IP, TCP_HLEN, PBUF_RAM);
+
+   if(p == NULL) {
+      LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: could not allocate memory for pbuf\n"));
+      return;
+   }
+
+   tcphdr = p->payload;
+   tcphdr->src = htons(pcb->local_port);
+   tcphdr->dest = htons(pcb->remote_port);
+   tcphdr->seqno = htonl(pcb->snd_nxt - 1);
+   tcphdr->ackno = htonl(pcb->rcv_nxt);
+   tcphdr->wnd = htons(pcb->rcv_wnd);
+   tcphdr->urgp = 0;
+   TCPH_HDRLEN_SET(tcphdr, 5);
+   
+   tcphdr->chksum = 0;
+#if CHECKSUM_GEN_TCP
+   tcphdr->chksum = inet_chksum_pseudo(p, &pcb->local_ip, &pcb->remote_ip, IP_PROTO_TCP, p->tot_len);
+#endif
+  TCP_STATS_INC(tcp.xmit);
+
+   /* Send output to IP */
+  ip_output(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP);
+
+  pbuf_free(p);
+
+  LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_keepalive: seqno %"U32_F" ackno %"U32_F".\n", pcb->snd_nxt - 1, pcb->rcv_nxt));
+}
+
+#endif /* LWIP_TCP */
+
+
+
+
+
+
+
+
+
diff --git a/lib/lwip/src/core/udp.c b/lib/lwip/src/core/udp.c
new file mode 100644
index 0000000..d1e0eac
--- /dev/null
+++ b/lib/lwip/src/core/udp.c
@@ -0,0 +1,655 @@
+/**
+ * @file
+ * User Datagram Protocol 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>
+ *
+ */
+
+
+/* udp.c
+ *
+ * The code for the User Datagram Protocol UDP.
+ *
+ */
+
+#include <string.h>
+
+#include "lwip/opt.h"
+
+#include "lwip/def.h"
+#include "lwip/memp.h"
+#include "lwip/inet.h"
+#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+#include "lwip/udp.h"
+#include "lwip/icmp.h"
+
+#include "lwip/stats.h"
+
+#include "arch/perf.h"
+#include "lwip/snmp.h"
+
+/* The list of UDP PCBs */
+#if LWIP_UDP
+/* was static, but we may want to access this from a socket layer */
+struct udp_pcb *udp_pcbs = NULL;
+
+static struct udp_pcb *pcb_cache = NULL;
+
+void
+udp_init(void)
+{
+  udp_pcbs = pcb_cache = NULL;
+}
+
+/**
+ * Process an incoming UDP datagram.
+ *
+ * Given an incoming UDP datagram (as a chain of pbufs) this function
+ * finds a corresponding UDP PCB and
+ *
+ * @param pbuf pbuf to be demultiplexed to a UDP PCB.
+ * @param netif 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;
+  struct udp_pcb *uncon_pcb;
+  struct ip_hdr *iphdr;
+  u16_t src, dest;
+  u8_t local_match;
+
+  PERF_START;
+
+  UDP_STATS_INC(udp.recv);
+
+  iphdr = p->payload;
+
+  if (pbuf_header(p, -((s16_t)(UDP_HLEN + IPH_HL(iphdr) * 4)))) {
+    /* 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);
+    snmp_inc_udpinerrors();
+    pbuf_free(p);
+    goto end;
+  }
+
+  udphdr = (struct udp_hdr *)((u8_t *)p->payload - UDP_HLEN);
+
+  LWIP_DEBUGF(UDP_DEBUG, ("udp_input: received datagram of length %"U16_F"\n", p->tot_len));
+
+  src = ntohs(udphdr->src);
+  dest = ntohs(udphdr->dest);
+
+  udp_debug_print(udphdr);
+
+  /* print the UDP source and destination */
+  LWIP_DEBUGF(UDP_DEBUG, ("udp (%"U16_F".%"U16_F".%"U16_F".%"U16_F", %"U16_F") <-- (%"U16_F".%"U16_F".%"U16_F".%"U16_F", %"U16_F")\n",
+    ip4_addr1(&iphdr->dest), ip4_addr2(&iphdr->dest),
+    ip4_addr3(&iphdr->dest), ip4_addr4(&iphdr->dest), ntohs(udphdr->dest),
+    ip4_addr1(&iphdr->src), ip4_addr2(&iphdr->src),
+    ip4_addr3(&iphdr->src), ip4_addr4(&iphdr->src), ntohs(udphdr->src)));
+
+  local_match = 0;
+  uncon_pcb = NULL;
+  /* Iterate through the UDP pcb list for a matching pcb */
+  for (pcb = udp_pcbs; pcb != NULL; pcb = pcb->next) {
+    /* print the PCB local and remote address */
+    LWIP_DEBUGF(UDP_DEBUG, ("pcb (%"U16_F".%"U16_F".%"U16_F".%"U16_F", %"U16_F") --- (%"U16_F".%"U16_F".%"U16_F".%"U16_F", %"U16_F")\n",
+      ip4_addr1(&pcb->local_ip), ip4_addr2(&pcb->local_ip),
+      ip4_addr3(&pcb->local_ip), ip4_addr4(&pcb->local_ip), pcb->local_port,
+      ip4_addr1(&pcb->remote_ip), ip4_addr2(&pcb->remote_ip),
+      ip4_addr3(&pcb->remote_ip), ip4_addr4(&pcb->remote_ip), pcb->remote_port));
+
+    /* compare PCB local addr+port to UDP destination addr+port */
+    if ((pcb->local_port == dest) &&
+       (ip_addr_isany(&pcb->local_ip) ||
+        ip_addr_cmp(&(pcb->local_ip), &(iphdr->dest)))) {
+       local_match = 1;
+       if ((uncon_pcb == NULL) && 
+           ((pcb->flags & UDP_FLAGS_CONNECTED) == 0)) {
+         /* the first unconnected matching PCB */     
+         uncon_pcb = pcb;
+       }
+    }
+    /* compare PCB remote addr+port to UDP source addr+port */
+    if ((local_match != 0) &&
+       (pcb->remote_port == src) &&
+       (ip_addr_isany(&pcb->remote_ip) ||
+       ip_addr_cmp(&(pcb->remote_ip), &(iphdr->src)))) {
+       /* the first fully matching PCB */
+      break;
+    }
+  }
+  /* 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  || ip_addr_cmp(&inp->ip_addr, &iphdr->dest))
+    {
+    LWIP_DEBUGF(UDP_DEBUG | DBG_TRACE, ("udp_input: calculating checksum\n"));
+    pbuf_header(p, UDP_HLEN);
+#ifdef IPv6
+    if (iphdr->nexthdr == IP_PROTO_UDPLITE) {
+#else
+    if (IPH_PROTO(iphdr) == IP_PROTO_UDPLITE) {
+#endif /* IPv4 */
+      /* Do the UDP Lite checksum */
+#if CHECKSUM_CHECK_UDP
+      if (inet_chksum_pseudo(p, (struct ip_addr *)&(iphdr->src),
+         (struct ip_addr *)&(iphdr->dest),
+         IP_PROTO_UDPLITE, ntohs(udphdr->len)) != 0) {
+  LWIP_DEBUGF(UDP_DEBUG | 2, ("udp_input: UDP Lite datagram discarded due to failing checksum\n"));
+  UDP_STATS_INC(udp.chkerr);
+  UDP_STATS_INC(udp.drop);
+  snmp_inc_udpinerrors();
+  pbuf_free(p);
+  goto end;
+      }
+#endif
+    } else {
+#if CHECKSUM_CHECK_UDP
+      if (udphdr->chksum != 0) {
+  if (inet_chksum_pseudo(p, (struct ip_addr *)&(iphdr->src),
+       (struct ip_addr *)&(iphdr->dest),
+        IP_PROTO_UDP, p->tot_len) != 0) {
+    LWIP_DEBUGF(UDP_DEBUG | 2, ("udp_input: UDP datagram discarded due to failing checksum\n"));
+
+    UDP_STATS_INC(udp.chkerr);
+    UDP_STATS_INC(udp.drop);
+    snmp_inc_udpinerrors();
+    pbuf_free(p);
+    goto end;
+  }
+      }
+#endif
+    }
+    pbuf_header(p, -UDP_HLEN);
+    if (pcb != NULL) {
+      snmp_inc_udpindatagrams();
+      /* callback */
+      if (pcb->recv != NULL)
+      {
+        pcb->recv(pcb->recv_arg, pcb, p, &(iphdr->src), src);
+      }
+        } else {
+      LWIP_DEBUGF(UDP_DEBUG | DBG_TRACE, ("udp_input: not for us.\n"));
+
+      /* No match was found, send ICMP destination port unreachable unless
+      destination address was broadcast/multicast. */
+
+      if (!ip_addr_isbroadcast(&iphdr->dest, inp) &&
+          !ip_addr_ismulticast(&iphdr->dest)) {
+
+  /* adjust pbuf pointer */
+  p->payload = iphdr;
+  icmp_dest_unreach(p, ICMP_DUR_PORT);
+      }
+      UDP_STATS_INC(udp.proterr);
+      UDP_STATS_INC(udp.drop);
+    snmp_inc_udpnoports();
+      pbuf_free(p);
+    }
+  } else {
+    pbuf_free(p);
+  }
+  end:
+
+  PERF_STOP("udp_input");
+}
+
+/**
+ * Send data to a specified address using UDP.
+ *
+ * @param pcb UDP PCB used to send the data.
+ * @param pbuf chain of pbuf's to be sent.
+ * @param dst_ip Destination IP address.
+ * @param dst_port Destination UDP port.
+ *
+ * If the PCB already has a remote address association, it will
+ * be restored after the data is sent.
+ * 
+ * @return lwIP error code.
+ * - ERR_OK. Successful. No error occured.
+ * - ERR_MEM. Out of memory.
+ * - ERR_RTE. Could not find route to destination address.
+ *
+ * @see udp_disconnect() udp_send()
+ */
+err_t
+udp_sendto(struct udp_pcb *pcb, struct pbuf *p,
+  struct ip_addr *dst_ip, u16_t dst_port)
+{
+  err_t err;
+  /* temporary space for current PCB remote address */
+  struct ip_addr pcb_remote_ip;
+  u16_t pcb_remote_port;
+  /* remember current remote peer address of PCB */
+  pcb_remote_ip.addr = pcb->remote_ip.addr;
+  pcb_remote_port = pcb->remote_port;
+  /* copy packet destination address to PCB remote peer address */
+  pcb->remote_ip.addr = dst_ip->addr;
+  pcb->remote_port = dst_port;
+  /* send to the packet destination address */
+  err = udp_send(pcb, p);
+  /* restore PCB remote peer address */
+  pcb->remote_ip.addr = pcb_remote_ip.addr;
+  pcb->remote_port = pcb_remote_port;
+  return err;
+}
+
+/**
+ * Send data using UDP.
+ *
+ * @param pcb UDP PCB used to send the data.
+ * @param pbuf chain of pbuf's to be sent.
+ *
+ * @return lwIP error code.
+ * - ERR_OK. Successful. No error occured.
+ * - ERR_MEM. Out of memory.
+ * - ERR_RTE. Could not find route to destination address.
+ *
+ * @see udp_disconnect() udp_sendto()
+ */
+err_t
+udp_send(struct udp_pcb *pcb, struct pbuf *p)
+{
+  struct udp_hdr *udphdr;
+  struct netif *netif;
+  struct ip_addr *src_ip;
+  err_t err;
+  struct pbuf *q; /* q will be sent down the stack */
+
+  LWIP_DEBUGF(UDP_DEBUG | DBG_TRACE | 3, ("udp_send\n"));
+
+  /* if the PCB is not yet bound to a port, bind it here */
+  if (pcb->local_port == 0) {
+    LWIP_DEBUGF(UDP_DEBUG | DBG_TRACE | 2, ("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 | DBG_TRACE | 2, ("udp_send: forced port bind failed\n"));
+      return err;
+    }
+  }
+  /* find the outgoing network interface for this packet */
+  netif = ip_route(&(pcb->remote_ip));
+  /* no outgoing network interface could be found? */
+  if (netif == NULL) {
+    LWIP_DEBUGF(UDP_DEBUG | 1, ("udp_send: No route to 0x%"X32_F"\n", pcb->remote_ip.addr));
+    UDP_STATS_INC(udp.rterr);
+    return ERR_RTE;
+  }
+
+  /* not enough space to add an UDP header to first pbuf in given p chain? */
+  if (pbuf_header(p, UDP_HLEN)) {
+    /* allocate header in a seperate 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 | DBG_TRACE | 2, ("udp_send: could not allocate header\n"));
+      return ERR_MEM;
+    }
+    /* chain header q in front of given pbuf p */
+    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));
+  /* adding a header within p succeeded */
+  } else {
+    /* first pbuf q equals given pbuf */
+    q = p;
+    LWIP_DEBUGF(UDP_DEBUG, ("udp_send: added header in given pbuf %p\n", (void *)p));
+  }
+  /* { q now represents the packet to be sent } */
+  udphdr = q->payload;
+  udphdr->src = htons(pcb->local_port);
+  udphdr->dest = htons(pcb->remote_port);
+  /* in UDP, 0 checksum means 'no checksum' */
+  udphdr->chksum = 0x0000; 
+
+  /* PCB local address is IP_ANY_ADDR? */
+  if (ip_addr_isany(&pcb->local_ip)) {
+    /* use outgoing network interface IP address as source address */
+    src_ip = &(netif->ip_addr);
+  } else {
+    /* use UDP PCB local IP address as source address */
+    src_ip = &(pcb->local_ip);
+  }
+
+  LWIP_DEBUGF(UDP_DEBUG, ("udp_send: sending datagram of length %"U16_F"\n", q->tot_len));
+
+  /* UDP Lite protocol? */
+  if (pcb->flags & UDP_FLAGS_UDPLITE) {
+    LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP LITE packet length %"U16_F"\n", q->tot_len));
+    /* set UDP message length in UDP header */
+    udphdr->len = htons(pcb->chksum_len);
+    /* calculate checksum */
+#if CHECKSUM_GEN_UDP
+    udphdr->chksum = inet_chksum_pseudo(q, src_ip, &(pcb->remote_ip),
+          IP_PROTO_UDP, pcb->chksum_len);
+    /* chksum zero must become 0xffff, as zero means 'no checksum' */
+    if (udphdr->chksum == 0x0000) udphdr->chksum = 0xffff;
+#else
+    udphdr->chksum = 0x0000;
+#endif
+    /* output to IP */
+    LWIP_DEBUGF(UDP_DEBUG, ("udp_send: ip_output_if (,,,,IP_PROTO_UDPLITE,)\n"));
+    err = ip_output_if (q, src_ip, &pcb->remote_ip, pcb->ttl, pcb->tos, IP_PROTO_UDPLITE, netif);    
+  /* UDP */
+  } else {
+    LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP packet length %"U16_F"\n", q->tot_len));
+    udphdr->len = htons(q->tot_len);
+    /* calculate checksum */
+#if CHECKSUM_GEN_UDP
+    if ((pcb->flags & UDP_FLAGS_NOCHKSUM) == 0) {
+      udphdr->chksum = inet_chksum_pseudo(q, src_ip, &pcb->remote_ip, IP_PROTO_UDP, q->tot_len);
+      /* chksum zero must become 0xffff, as zero means 'no checksum' */
+      if (udphdr->chksum == 0x0000) udphdr->chksum = 0xffff;
+    }
+#else
+    udphdr->chksum = 0x0000;
+#endif
+    LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP checksum 0x%04"X16_F"\n", udphdr->chksum));
+    LWIP_DEBUGF(UDP_DEBUG, ("udp_send: ip_output_if (,,,,IP_PROTO_UDP,)\n"));
+    /* output to IP */
+    err = ip_output_if(q, src_ip, &pcb->remote_ip, pcb->ttl, pcb->tos, IP_PROTO_UDP, netif);    
+  }
+  /* TODO: must this be increased even if error occured? */
+  snmp_inc_udpoutdatagrams();
+
+  /* did we chain a seperate 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;
+}
+
+/**
+ * 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_ADDR_ANY to
+ * bind to all local interfaces.
+ * @param port local UDP port to bind with.
+ *
+ * @return lwIP error code.
+ * - ERR_OK. Successful. No error occured.
+ * - 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, struct ip_addr *ipaddr, u16_t port)
+{
+  struct udp_pcb *ipcb;
+  u8_t rebind;
+
+  LWIP_DEBUGF(UDP_DEBUG | DBG_TRACE | 3, ("udp_bind(ipaddr = "));
+  ip_addr_debug_print(UDP_DEBUG, ipaddr);
+  LWIP_DEBUGF(UDP_DEBUG | DBG_TRACE | 3, (", 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) {
+      /* pcb may occur at most once in active list */
+      LWIP_ASSERT("rebind == 0", rebind == 0);
+      /* pcb already in list, just rebind */
+      rebind = 1;
+    }
+
+/* this code does not allow upper layer to share a UDP port for
+   listening to broadcast or multicast traffic (See SO_REUSE_ADDR and
+   SO_REUSE_PORT under *BSD). TODO: See where it fits instead, OR
+   combine with implementation of UDP PCB flags. Leon Woestenberg. */
+#ifdef LWIP_UDP_TODO
+    /* port matches that of PCB in list? */
+    else if ((ipcb->local_port == port) &&
+       /* IP address matches, or one is IP_ADDR_ANY? */
+       (ip_addr_isany(&(ipcb->local_ip)) ||
+       ip_addr_isany(ipaddr) ||
+       ip_addr_cmp(&(ipcb->local_ip), ipaddr))) {
+      /* 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;
+    }
+#endif
+
+  }
+
+  ip_addr_set(&pcb->local_ip, ipaddr);
+  /* no port specified? */
+  if (port == 0) {
+#ifndef UDP_LOCAL_PORT_RANGE_START
+#define UDP_LOCAL_PORT_RANGE_START 4096
+#define UDP_LOCAL_PORT_RANGE_END   0x7fff
+#endif
+    port = UDP_LOCAL_PORT_RANGE_START;
+    ipcb = udp_pcbs;
+    while ((ipcb != NULL) && (port != UDP_LOCAL_PORT_RANGE_END)) {
+      if (ipcb->local_port == port) {
+        port++;
+        ipcb = udp_pcbs;
+      } else
+        ipcb = ipcb->next;
+    }
+    if (ipcb != NULL) {
+      /* no more ports available in local range */
+      LWIP_DEBUGF(UDP_DEBUG, ("udp_bind: out of free UDP ports\n"));
+      return ERR_USE;
+    }
+  }
+  pcb->local_port = port;
+  /* 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 | DBG_TRACE | DBG_STATE, ("udp_bind: bound to %"U16_F".%"U16_F".%"U16_F".%"U16_F", port %"U16_F"\n",
+   (u16_t)(ntohl(pcb->local_ip.addr) >> 24 & 0xff),
+   (u16_t)(ntohl(pcb->local_ip.addr) >> 16 & 0xff),
+   (u16_t)(ntohl(pcb->local_ip.addr) >> 8 & 0xff),
+   (u16_t)(ntohl(pcb->local_ip.addr) & 0xff), pcb->local_port));
+  return ERR_OK;
+}
+/**
+ * Connect an UDP PCB.
+ *
+ * This will associate the UDP PCB with the remote address.
+ *
+ * @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
+ *
+ * @see udp_disconnect()
+ */
+err_t
+udp_connect(struct udp_pcb *pcb, struct ip_addr *ipaddr, u16_t port)
+{
+  struct udp_pcb *ipcb;
+
+  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(&pcb->remote_ip, ipaddr);
+  pcb->remote_port = port;
+  pcb->flags |= UDP_FLAGS_CONNECTED;
+/** TODO: this functionality belongs in upper layers */
+#ifdef LWIP_UDP_TODO
+  /* Nail down local IP for netconn_addr()/getsockname() */
+  if (ip_addr_isany(&pcb->local_ip) && !ip_addr_isany(&pcb->remote_ip)) {
+    struct netif *netif;
+
+    if ((netif = ip_route(&(pcb->remote_ip))) == NULL) {
+      LWIP_DEBUGF(UDP_DEBUG, ("udp_connect: No route to 0x%lx\n", pcb->remote_ip.addr));
+        UDP_STATS_INC(udp.rterr);
+      return ERR_RTE;
+    }
+    /** TODO: this will bind the udp pcb locally, to the interface which
+        is used to route output packets to the remote address. However, we
+        might want to accept incoming packets on any interface! */
+    pcb->local_ip = netif->ip_addr;
+  } else if (ip_addr_isany(&pcb->remote_ip)) {
+    pcb->local_ip.addr = 0;
+  }
+#endif
+  LWIP_DEBUGF(UDP_DEBUG | DBG_TRACE | DBG_STATE, ("udp_connect: connected to %"U16_F".%"U16_F".%"U16_F".%"U16_F",port %"U16_F"\n",
+   (u16_t)(ntohl(pcb->remote_ip.addr) >> 24 & 0xff),
+   (u16_t)(ntohl(pcb->remote_ip.addr) >> 16 & 0xff),
+   (u16_t)(ntohl(pcb->remote_ip.addr) >> 8 & 0xff),
+   (u16_t)(ntohl(pcb->remote_ip.addr) & 0xff), 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;
+}
+
+void
+udp_disconnect(struct udp_pcb *pcb)
+{
+  /* reset remote address association */
+  ip_addr_set(&pcb->remote_ip, IP_ADDR_ANY);
+  pcb->remote_port = 0;
+  /* mark PCB as unconnected */
+  pcb->flags &= ~UDP_FLAGS_CONNECTED;
+}
+
+void
+udp_recv(struct udp_pcb *pcb,
+   void (* recv)(void *arg, struct udp_pcb *upcb, struct pbuf *p,
+           struct ip_addr *addr, u16_t port),
+   void *recv_arg)
+{
+  /* remember recv() callback and user data */
+  pcb->recv = recv;
+  pcb->recv_arg = recv_arg;
+}
+/**
+ * Remove an UDP 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;
+  /* 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;
+    }
+  }
+  memp_free(MEMP_UDP_PCB, pcb);
+}
+/**
+ * Create a 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;
+  pcb = memp_malloc(MEMP_UDP_PCB);
+  /* could allocate UDP PCB? */
+  if (pcb != NULL) {
+    /* initialize PCB to all zeroes */
+    memset(pcb, 0, sizeof(struct udp_pcb));
+    pcb->ttl = UDP_TTL;
+  }
+  
+  
+  return pcb;
+}
+
+#if UDP_DEBUG
+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",
+         ntohs(udphdr->src), ntohs(udphdr->dest)));
+  LWIP_DEBUGF(UDP_DEBUG, ("+-------------------------------+\n"));
+  LWIP_DEBUGF(UDP_DEBUG, ("|     %5"U16_F"     |     0x%04"X16_F"    | (len, chksum)\n",
+         ntohs(udphdr->len), ntohs(udphdr->chksum)));
+  LWIP_DEBUGF(UDP_DEBUG, ("+-------------------------------+\n"));
+}
+#endif /* UDP_DEBUG */
+
+#endif /* LWIP_UDP */
+
+
+
+
+
+
+
+
+