TelenkovDmitry 7 年之前
父節點
當前提交
0d0f2b0fef
共有 100 個文件被更改,包括 39058 次插入36302 次删除
  1. 1 1
      config/common_config.h
  2. 14 13
      modules/Ethernet/lwipopts.h
  3. 11 9
      modules/Ethernet/netconf.c
  4. 2 2
      modules/Ethernet/private_mib.c
  5. 15 15
      modules/Ethernet/snmp_api.c
  6. 256 249
      modules/Ethernet/sntp.c
  7. 56 56
      modules/Ethernet/sntp.h
  8. 6 2
      modules/Ethernet/trap_api.c
  9. 1 1
      modules/Ethernet/udp_netsetting.c
  10. 3 2
      modules/HTTP_Server/web_params_api.c
  11. 1 1
      modules/parameters.c
  12. 62 80
      projects/iar/bt-670x.ewp
  13. 3 1
      thirdparty/FreeRadius/radius_config.h
  14. 1 1
      thirdparty/lwip/port/FreeRTOS/ethernetif.c
  15. 476 586
      thirdparty/lwip/port/FreeRTOS/sys_arch.c
  16. 0 1
      thirdparty/lwip/port/arch/bpstruct.h
  17. 0 1
      thirdparty/lwip/port/arch/epstruct.h
  18. 0 40
      thirdparty/lwip/port/arch/perf.h
  19. 404 189
      thirdparty/lwip/src/api/api_lib.c
  20. 1947 1565
      thirdparty/lwip/src/api/api_msg.c
  21. 115 75
      thirdparty/lwip/src/api/err.c
  22. 246 245
      thirdparty/lwip/src/api/netbuf.c
  23. 413 353
      thirdparty/lwip/src/api/netdb.c
  24. 221 160
      thirdparty/lwip/src/api/netifapi.c
  25. 2827 2374
      thirdparty/lwip/src/api/sockets.c
  26. 518 511
      thirdparty/lwip/src/api/tcpip.c
  27. 二進制
      thirdparty/lwip/src/core/.DS_Store
  28. 222 108
      thirdparty/lwip/src/core/def.c
  29. 1573 970
      thirdparty/lwip/src/core/dns.c
  30. 609 450
      thirdparty/lwip/src/core/inet_chksum.c
  31. 385 332
      thirdparty/lwip/src/core/init.c
  32. 124 0
      thirdparty/lwip/src/core/ip.c
  33. 527 528
      thirdparty/lwip/src/core/ipv4/autoip.c
  34. 1950 1770
      thirdparty/lwip/src/core/ipv4/dhcp.c
  35. 1206 1399
      thirdparty/lwip/src/core/ipv4/etharp.c
  36. 397 355
      thirdparty/lwip/src/core/ipv4/icmp.c
  37. 800 805
      thirdparty/lwip/src/core/ipv4/igmp.c
  38. 1086 924
      thirdparty/lwip/src/core/ipv4/ip4.c
  39. 331 312
      thirdparty/lwip/src/core/ipv4/ip4_addr.c
  40. 831 863
      thirdparty/lwip/src/core/ipv4/ip4_frag.c
  41. 0 1
      thirdparty/lwip/src/core/ipv6/README
  42. 50 0
      thirdparty/lwip/src/core/ipv6/dhcp6.c
  43. 118 0
      thirdparty/lwip/src/core/ipv6/ethip6.c
  44. 350 179
      thirdparty/lwip/src/core/ipv6/icmp6.c
  45. 53 163
      thirdparty/lwip/src/core/ipv6/inet6.c
  46. 1122 397
      thirdparty/lwip/src/core/ipv6/ip6.c
  47. 292 72
      thirdparty/lwip/src/core/ipv6/ip6_addr.c
  48. 805 0
      thirdparty/lwip/src/core/ipv6/ip6_frag.c
  49. 588 0
      thirdparty/lwip/src/core/ipv6/mld6.c
  50. 2102 0
      thirdparty/lwip/src/core/ipv6/nd6.c
  51. 777 659
      thirdparty/lwip/src/core/mem.c
  52. 496 470
      thirdparty/lwip/src/core/memp.c
  53. 1265 774
      thirdparty/lwip/src/core/netif.c
  54. 1442 1179
      thirdparty/lwip/src/core/pbuf.c
  55. 521 350
      thirdparty/lwip/src/core/raw.c
  56. 0 657
      thirdparty/lwip/src/core/snmp/asn1_dec.c
  57. 0 611
      thirdparty/lwip/src/core/snmp/asn1_enc.c
  58. 0 4146
      thirdparty/lwip/src/core/snmp/mib2.c
  59. 0 1174
      thirdparty/lwip/src/core/snmp/mib_structs.c
  60. 0 1491
      thirdparty/lwip/src/core/snmp/msg_in.c
  61. 0 674
      thirdparty/lwip/src/core/snmp/msg_out.c
  62. 169 176
      thirdparty/lwip/src/core/stats.c
  63. 106 68
      thirdparty/lwip/src/core/sys.c
  64. 2164 1742
      thirdparty/lwip/src/core/tcp.c
  65. 1818 1619
      thirdparty/lwip/src/core/tcp_in.c
  66. 1671 1485
      thirdparty/lwip/src/core/tcp_out.c
  67. 433 487
      thirdparty/lwip/src/core/timeouts.c
  68. 1191 1013
      thirdparty/lwip/src/core/udp.c
  69. 二進制
      thirdparty/lwip/src/include/ipv4/.DS_Store
  70. 0 118
      thirdparty/lwip/src/include/ipv4/lwip/autoip.h
  71. 0 107
      thirdparty/lwip/src/include/ipv4/lwip/inet.h
  72. 0 223
      thirdparty/lwip/src/include/ipv4/lwip/ip.h
  73. 0 130
      thirdparty/lwip/src/include/ipv6/lwip/ip.h
  74. 0 97
      thirdparty/lwip/src/include/ipv6/lwip/ip_addr.h
  75. 400 297
      thirdparty/lwip/src/include/lwip/api.h
  76. 0 177
      thirdparty/lwip/src/include/lwip/api_msg.h
  77. 103 0
      thirdparty/lwip/src/include/lwip/apps/fs.h
  78. 236 0
      thirdparty/lwip/src/include/lwip/apps/httpd.h
  79. 323 0
      thirdparty/lwip/src/include/lwip/apps/httpd_opts.h
  80. 84 0
      thirdparty/lwip/src/include/lwip/apps/lwiperf.h
  81. 69 0
      thirdparty/lwip/src/include/lwip/apps/mdns.h
  82. 74 0
      thirdparty/lwip/src/include/lwip/apps/mdns_opts.h
  83. 66 0
      thirdparty/lwip/src/include/lwip/apps/mdns_priv.h
  84. 244 0
      thirdparty/lwip/src/include/lwip/apps/mqtt.h
  85. 103 0
      thirdparty/lwip/src/include/lwip/apps/mqtt_opts.h
  86. 43 0
      thirdparty/lwip/src/include/lwip/apps/netbiosns.h
  87. 59 0
      thirdparty/lwip/src/include/lwip/apps/netbiosns_opts.h
  88. 128 0
      thirdparty/lwip/src/include/lwip/apps/snmp.h
  89. 364 0
      thirdparty/lwip/src/include/lwip/apps/snmp_core.h
  90. 78 0
      thirdparty/lwip/src/include/lwip/apps/snmp_mib2.h
  91. 293 0
      thirdparty/lwip/src/include/lwip/apps/snmp_opts.h
  92. 113 0
      thirdparty/lwip/src/include/lwip/apps/snmp_scalar.h
  93. 134 0
      thirdparty/lwip/src/include/lwip/apps/snmp_table.h
  94. 114 0
      thirdparty/lwip/src/include/lwip/apps/snmp_threadsync.h
  95. 90 0
      thirdparty/lwip/src/include/lwip/apps/snmpv3.h
  96. 76 0
      thirdparty/lwip/src/include/lwip/apps/sntp.h
  97. 173 0
      thirdparty/lwip/src/include/lwip/apps/sntp_opts.h
  98. 105 0
      thirdparty/lwip/src/include/lwip/apps/tftp_opts.h
  99. 94 0
      thirdparty/lwip/src/include/lwip/apps/tftp_server.h
  100. 319 217
      thirdparty/lwip/src/include/lwip/arch.h

+ 1 - 1
config/common_config.h

@@ -81,7 +81,7 @@
 /**
   * @brief  Отладочный порт USART и консоль
   */
-//#define USART_DEBUG_ENABLE
+#define USART_DEBUG_ENABLE
 
 #define SYSTEMTICK_PERIOD_MS  1
 

+ 14 - 13
modules/Ethernet/lwipopts.h

@@ -129,13 +129,12 @@ a lot of data that needs to be copied, this should be set high. */
    turning this on does currently not work. */
 #define LWIP_DHCP               1
 
-
 // ---------- SNMP options ----------
-#define LWIP_SNMP                       1
-#define SNMP_CONCURRENT_REQUESTS        1
-#define SNMP_TRAP_DESTINATIONS          1
-#define SNMP_PRIVATE_MIB                1
-#define SNMP_SAFE_REQUESTS              1
+//#define LWIP_SNMP                       1
+//#define SNMP_CONCURRENT_REQUESTS        1
+//#define SNMP_TRAP_DESTINATIONS          1
+//#define SNMP_PRIVATE_MIB                1
+//#define SNMP_SAFE_REQUESTS              1
      
 
 /* ---------- UDP options ---------- */
@@ -147,6 +146,7 @@ a lot of data that needs to be copied, this should be set high. */
 #define LWIP_STATS 0
 #define LWIP_PROVIDE_ERRNO 1
 
+#define LWIP_NETIF_LINK_CALLBACK        0
 
 /*
    --------------------------------------
@@ -217,21 +217,22 @@ The STM32F4x7 allows computing and verifying the IP, UDP, TCP and ICMP checksums
    -----------------------------------
 */
 
-//#define LWIP_DEBUG
+#define LWIP_DEBUG
 
 #define TCP_DEBUG                       LWIP_DBG_OFF
 #define ETHARP_DEBUG                    LWIP_DBG_OFF
-#define PBUF_DEBUG                      LWIP_DBG_ON
+#define PBUF_DEBUG                      LWIP_DBG_OFF
 #define IP_DEBUG                        LWIP_DBG_OFF
 #define TCPIP_DEBUG                     LWIP_DBG_OFF
 #define DHCP_DEBUG                      LWIP_DBG_OFF
 #define UDP_DEBUG                       LWIP_DBG_OFF
 #define SOCKETS_DEBUG                   LWIP_DBG_OFF
-#define MEMP_DEBUG                      LWIP_DBG_ON
-#define PING_DEBUG                      LWIP_DBG_OFF
-#define SNMP_MSG_DEBUG                  LWIP_DBG_ON
-#define SNMP_MIB_DEBUG                  LWIP_DBG_ON
-
+#define MEMP_DEBUG                      LWIP_DBG_OFF
+#define PING_DEBUG                      LWIP_DBG_ON
+#define SNMP_MSG_DEBUG                  LWIP_DBG_OFF
+#define SNMP_MIB_DEBUG                  LWIP_DBG_OFF
+#define TIMERS_DEBUG                    LWIP_DBG_ON
+#define ICMP_DEBUG                      LWIP_DBG_ON
 
 /*
    ---------------------------------

+ 11 - 9
modules/Ethernet/netconf.c

@@ -57,9 +57,9 @@ bool dhcp = false;
 
 void LwIP_Init(void)
 {
-  struct ip_addr ipaddr;
-  struct ip_addr netmask;
-  struct ip_addr gw;
+  struct ip4_addr ipaddr;
+  struct ip4_addr netmask;
+  struct ip4_addr gw;
   char str[20];
 
   tcpip_init( NULL, NULL );	
@@ -152,12 +152,13 @@ void LwIP_Init(void)
   */
 void LwIP_DHCP_task(void * pvParameters)
 {
-  struct ip_addr ipaddr;
-  struct ip_addr netmask;
-  struct ip_addr gw;
+  struct ip4_addr ipaddr;
+  struct ip4_addr netmask;
+  struct ip4_addr gw;
   uint8_t DHCP_state;  
   DHCP_state = DHCP_START;
-
+  static uint8_t dhcpTry = 0;
+  
   for (;;)
   {
     switch (DHCP_state)
@@ -225,7 +226,8 @@ void LwIP_DHCP_task(void * pvParameters)
         else
         {
           /* DHCP timeout */
-          if (xnetif.dhcp->tries > MAX_DHCP_TRIES)
+          //if (xnetif.dhcp->tries > MAX_DHCP_TRIES)
+          if (dhcpTry++ > MAX_DHCP_TRIES)
           {
             DHCP_state = DHCP_TIMEOUT;
 
@@ -266,7 +268,7 @@ void LwIP_DHCP_task(void * pvParameters)
       default: break;
     }
 
-    vTaskDelay(250);
+    vTaskDelay(2500);
   }   
 }
 

+ 2 - 2
modules/Ethernet/private_mib.c

@@ -1,5 +1,5 @@
 #include "stm32f4xx.h"  
-
+#if 0
 #include "private_mib.h"
 #include "lwip/snmp.h"
 #include "lwip/snmp_msg.h"
@@ -1125,4 +1125,4 @@ void vSendTrapCallback( void * parameters )
    }  
 }
  
- 
+#endif 

+ 15 - 15
modules/Ethernet/snmp_api.c

@@ -32,9 +32,9 @@
 #include "lwip/sys.h"
 #include "lwip/udp.h"
 
-#include "snmp.h"
-#include "snmp_msg.h"
-#include "private_mib.h"
+//#include "snmp.h"
+//#include "snmp_msg.h"
+//#include "private_mib.h"
 
 #include "FreeRTOS.h"
 #include "task.h"
@@ -80,7 +80,7 @@ void SNMP_SysUpTimeTask(void *arg)
   while(1)
   {
 	vTaskDelayUntil( &xLastWakeTime, xFrequency );
-    snmp_inc_sysuptime();
+    //snmp_inc_sysuptime();
   }
 }
 
@@ -120,7 +120,7 @@ void snmp_trap_tread(void *arg)
   * @retval 
   */
 void SNMP_AgentInit(void)
-{
+{/*
   SNMP_SetObjDescr();
   SNMP_SetReadCommunity(sSettings.sSnmp.readCommunity);
   SNMP_SetWriteCommunity(sSettings.sSnmp.writeCommunity);
@@ -135,7 +135,7 @@ void SNMP_AgentInit(void)
 	
   snmp_init();
   udp_init();
-  
+*/  
   SNMP_TrapQueue = xQueueCreate(SNMP_TRAP_QUEUE_SIZE, sizeof(uint8_t));
 }
 
@@ -182,7 +182,7 @@ void SNMP_SetObjDescr(void)
   strcat(sSettings.sSnmp.sysDescr, UPS.model);
   
   len = strlen(sSettings.sSnmp.sysDescr);
-  snmp_set_sysdesr((u8_t*)sSettings.sSnmp.sysDescr, &len);
+  //snmp_set_sysdesr((u8_t*)sSettings.sSnmp.sysDescr, &len);
 }
 
 /**
@@ -210,7 +210,7 @@ void SNMP_SetSysContact(char *con)
   static uint8_t len;
   
   len = strlen(con);
-  snmp_set_syscontact((u8_t*)con, &len);
+  //snmp_set_syscontact((u8_t*)con, &len);
 }
 
 /**
@@ -222,7 +222,7 @@ void SNMP_SetSysName(char *name)
   static uint8_t len;
   
   len = strlen(name);
-  snmp_set_sysname((u8_t*)name, &len);
+  //snmp_set_sysname((u8_t*)name, &len);
 }
 
 /**
@@ -234,7 +234,7 @@ void SNMP_SetSysLocation(char *loc)
   static uint8_t len;
   
   len = strlen(loc);
-  snmp_set_syslocation((u8_t*)loc, &len);
+  //snmp_set_syslocation((u8_t*)loc, &len);
 }
 
 /**
@@ -242,11 +242,11 @@ void SNMP_SetSysLocation(char *loc)
   * @retval 
   */
 void SNMP_SetManagerIP(char *ip)
-{
+{/*
   static ip_addr_t trap_addr;
   
   ipaddr_aton(ip, &trap_addr);
-  snmp_trap_dst_ip_set(0, &trap_addr);
+  snmp_trap_dst_ip_set(0, &trap_addr);*/
 }
 
 /**
@@ -255,9 +255,9 @@ void SNMP_SetManagerIP(char *ip)
   */
 void SNMP_SetObjID(void)
 {
-  static struct snmp_obj_id my_object_id = {9, {1, 3, 6, 1, 4, 1, 41752, 911, 3}};
+  //static struct snmp_obj_id my_object_id = {9, {1, 3, 6, 1, 4, 1, 41752, 911, 3}};
   
-  snmp_set_sysobjid(&my_object_id);
+  //snmp_set_sysobjid(&my_object_id);
 }
 
 /**
@@ -266,7 +266,7 @@ void SNMP_SetObjID(void)
   */
 void SNMP_SetTrapOnOff(uint8_t state)
 {
-  snmp_trap_dst_enable(0, (u8_t)state);
+  //snmp_trap_dst_enable(0, (u8_t)state);
 }
 
 

+ 256 - 249
modules/Ethernet/sntp.c

@@ -1,249 +1,256 @@
-#include "sntp.h"
-#include "rtc.h"
-#include "settings_api.h"
-
-#include <string.h>
-#include <time.h>
-
-#ifdef PRINTF_STDLIB
-#include <stdio.h>
-#endif
-#ifdef PRINTF_CUSTOM
-#include "tinystdio.h"
-#endif
-
-
-#define SENDFAIL_TIMEOUT 5000 /* 5 seconds */
-#define SENT_TIMEOUT 60000 /* 1 minute */
-#define BADREPLY_TIMEOUT 60000 /* 1 minute */
-#define VALID_TIMEOUT (8 * 3600000) /* 8 hours */
-
-/* number of seconds between 1900 and 1970 */
-#define DIFF_SEC_1900_1970         (2208988800UL)
-
-struct sntp_packet
-{
-  uint8_t status;
-  uint8_t stratum;
-  uint8_t ppoll;
-  uint8_t precision;
-  uint32_t distance;
-  uint32_t dispersion;
-  uint32_t refid;
-  uint64_t reftime;
-  uint64_t org;
-  uint64_t rec;
-  uint64_t xmt;
-};
-
-static unsigned int timeout;
-static struct udp_pcb* upcb;
-static struct ip_addr server;
-static int port = 123;
-
-/**
-  * @brief  Общая структура настроек
-  */
-extern SETTINGS_t sSettings;
-
-/**
-  * @brief  Разовая синхронизация времени при старте контроллера
-  */
-extern TaskHandle_t xHandleSntpOnceSinhro;
-
-/**
-  * @brief  Синхронизация времени единоразово при включении контроллера
-  * @retval 
-  */
-void vTaskOnceSynchro(void *arg)
-{
-  for (;;) 
-  {
-	if (sSettings.sSNTP.sntpEnable)
-	{  
-      vTaskDelay(7000);
-  	  SNTP_Poll();
-	  //printf("Once time sinhro\n\r");
-	  vTaskDelete(xHandleSntpOnceSinhro);
-	}
-	else
-	  vTaskDelete(xHandleSntpOnceSinhro);
-  }
-}
-
-/**
-  * @brief  Периодическая синхронизация времени.
-  *         Выполняется раз в сутки с 0 часов.
-  * @retval 
-  */
-void vTaskPeriodicSynchro(void *arg)
-{
-  TM_RTC_t data;
-  
-  static uint8_t fSinhro = 0;
-  
-  for (;;) 
-  {
-	vTaskDelay(10000);
-	if (sSettings.sSNTP.sntpEnable)
-	{
-	  TM_RTC_GetDateTime(&data, TM_RTC_Format_BIN);
-	  
-	  /* Если пришло время синхронизации */
-	  if ((data.hours == 0) && (fSinhro == 0))
-	  {
-		SNTP_Poll();
-	    fSinhro = 1;
-		//printf("Periodic time sinhro\n\r");
-	  }
-	  
-	  if (data.hours > 1)
-	    fSinhro = 0;
-	}
-   	
-  }
-}
-
-/**
-  * @brief  Отладочный таск. Выводим время в консоль.
-  * @retval 
-  */
-void vTaskSntp(void *arg)
-{
-  TM_RTC_t data;
-  
-  for (;;)
-  {
-    vTaskDelay(1000);
-	TM_RTC_GetDateTime(&data, TM_RTC_Format_BIN);
-	printf("%d.%d.%d %d:%d:%d \n\r", data.date,data.month,data.year,data.hours,data.minutes,data.seconds);
-  }
-}
-
-/**
-  * @brief  Инициализация SNTP.
-  * @retval 
-  */
-void SNTP_Init(void)
-{
-  
-  
-  SNTP_SetServerAddr(sSettings.sSNTP.ip);
-  SNTP_Enable(sSettings.sSNTP.sntpEnable);
-}
-
-static void recv(void *arg, struct udp_pcb *upcb, struct pbuf *p, 
-				 struct ip_addr *addr, u16_t port)
-{
-  time_t t;
-  int utcSec = 0;
-  TM_RTC_t data;
-  
-  if (p->len == sizeof(struct sntp_packet))
-  {
-    int i;
-    struct sntp_packet aligned;
-    //myassert(p->len == p->tot_len); /* don't accept chained pbuf */
-    memcpy(&aligned, p->payload, sizeof(aligned));
-    i = (aligned.status >> 3) & 7;
-    
-	if ((i < 1) || (i > 4)) /* SNTP version 1..4 */
-      goto out;
-
-    i = aligned.status & 7;
-
-	if ((i != 4) && (i != 5)) /* mode 4 or 5: server or broadcast */
-      goto out;
-
-	if (aligned.xmt == 0)
-      goto out;
-	
-	utcSec = (int)(3600.0*sSettings.sSNTP.timeZone);
-	t = (ntohl(aligned.xmt) - 2208988800 + utcSec );
-	
-	TM_RTC_SetDataTimeUnix((uint32_t)t);
-	
-	/* Сохраним время последней синхронизации */
-	TM_RTC_GetDateTime(&data, TM_RTC_Format_BIN);
-		
-	memset(sSettings.sSNTP.data, 0, sizeof(sSettings.sSNTP.data));
-	
-	sprintf(sSettings.sSNTP.data, "%02d.%02d.%02d %02d:%02d:%02d", 
-			data.date, data.month, data.year,
-			data.hours, data.minutes, data.seconds);
-	
-    timeout = VALID_TIMEOUT;
-    
-  }
-  out:
-    pbuf_free(p);
-}
-
-void SNTP_Enable(bool enable)
-{
-  if (enable)
-  {
-    if (upcb == 0)
-    {
-      err_t ret;
-      upcb = udp_new();
-      if (upcb != 0)
-	  {
-		ret = udp_bind(upcb, IP_ADDR_ANY, port);
-
-	    if (ret != ERR_OK)
-        {
-          udp_remove(upcb);
-          upcb = 0;
-        }
-        else
-        {
-          udp_recv(upcb, recv, 0);
-        }
-        timeout = 0;
-	  }
-    }
-  }
-  else if (upcb != 0)
-  {
-    udp_remove(upcb);
-    upcb = 0;
-  }
-}
-
-bool SNTP_IsEnabled(void)
-{
-  return upcb != 0;
-}
-
-void SNTP_SetServerAddr(char *addr)
-{
-  server.addr = ipaddr_addr(addr);
-}
-
-int sntp_getserverport(void)
-{
-  return port;
-}
-
-static void send_request(void)
-{
-  struct sntp_packet packet;
-  struct pbuf* psend;
-  memset(&packet, 0, sizeof(packet));
-  packet.status = (3 << 3) /* SNTP vesion 3 */ | (3 << 0); /* Mode: client */
-  psend = pbuf_alloc(PBUF_RAW, sizeof(packet), PBUF_REF);
-  
-  if (psend != 0)
-  {
-    psend->payload = &packet;
-    timeout = (udp_sendto(upcb, psend, &server, port) == ERR_OK) ? SENT_TIMEOUT : SENDFAIL_TIMEOUT;
-    pbuf_free(psend);
-  }
-}
-
-void SNTP_Poll(void)
-{
-  if (upcb)
-    send_request();
-}
+//#include "sntp.h"
+#include "rtc.h"
+#include "settings_api.h"
+
+#include <string.h>
+#include <time.h>
+
+#ifdef PRINTF_STDLIB
+#include <stdio.h>
+#endif
+#ifdef PRINTF_CUSTOM
+#include "tinystdio.h"
+#endif
+
+
+#define SENDFAIL_TIMEOUT 5000 /* 5 seconds */
+#define SENT_TIMEOUT 60000 /* 1 minute */
+#define BADREPLY_TIMEOUT 60000 /* 1 minute */
+#define VALID_TIMEOUT (8 * 3600000) /* 8 hours */
+
+/* number of seconds between 1900 and 1970 */
+#define DIFF_SEC_1900_1970         (2208988800UL)
+
+struct sntp_packet
+{
+  uint8_t status;
+  uint8_t stratum;
+  uint8_t ppoll;
+  uint8_t precision;
+  uint32_t distance;
+  uint32_t dispersion;
+  uint32_t refid;
+  uint64_t reftime;
+  uint64_t org;
+  uint64_t rec;
+  uint64_t xmt;
+};
+/*
+static unsigned int timeout;
+static struct udp_pcb* upcb;
+static struct ip_addr server;
+static int port = 123;
+*/
+/**
+  * @brief  Общая структура настроек
+  */
+extern SETTINGS_t sSettings;
+
+/**
+  * @brief  Разовая синхронизация времени при старте контроллера
+  */
+//extern TaskHandle_t xHandleSntpOnceSinhro;
+
+/**
+  * @brief  Синхронизация времени единоразово при включении контроллера
+  * @retval 
+  */
+void vTaskOnceSynchro(void *arg)
+{
+  for (;;) 
+  {
+	if (sSettings.sSNTP.sntpEnable)
+	{  
+      vTaskDelay(7000);
+  	  //SNTP_Poll();
+	  //printf("Once time sinhro\n\r");
+	  //vTaskDelete(xHandleSntpOnceSinhro);
+	}
+	else
+    {
+      vTaskDelay(7000);
+    }  
+	  //vTaskDelete(xHandleSntpOnceSinhro);
+  }
+}
+
+/**
+  * @brief  Периодическая синхронизация времени.
+  *         Выполняется раз в сутки с 0 часов.
+  * @retval 
+  */
+void vTaskPeriodicSynchro(void *arg)
+{
+  TM_RTC_t data;
+  
+  static uint8_t fSinhro = 0;
+  
+  for (;;) 
+  {
+	vTaskDelay(10000);
+#if 0
+	if (sSettings.sSNTP.sntpEnable)
+	{
+	  TM_RTC_GetDateTime(&data, TM_RTC_Format_BIN);
+	  
+	  /* Если пришло время синхронизации */
+	  if ((data.hours == 0) && (fSinhro == 0))
+	  {
+		SNTP_Poll();
+	    fSinhro = 1;
+		//printf("Periodic time sinhro\n\r");
+	  }
+	  
+	  if (data.hours > 1)
+	    fSinhro = 0;
+	}
+#endif   	
+  }
+}
+
+/**
+  * @brief  Отладочный таск. Выводим время в консоль.
+  * @retval 
+  */
+void vTaskSntp(void *arg)
+{
+  TM_RTC_t data;
+  
+  for (;;)
+  {
+    vTaskDelay(1000);
+	TM_RTC_GetDateTime(&data, TM_RTC_Format_BIN);
+	printf("%d.%d.%d %d:%d:%d \n\r", data.date,data.month,data.year,data.hours,data.minutes,data.seconds);
+  }
+}
+
+/**
+  * @brief  Инициализация SNTP.
+  * @retval 
+  */
+void SNTP_Init(void)
+{
+  
+  
+  //SNTP_SetServerAddr(sSettings.sSNTP.ip);
+  //SNTP_Enable(sSettings.sSNTP.sntpEnable);
+}
+#if 0 
+static void recv(void *arg, struct udp_pcb *upcb, struct pbuf *p, 
+				 struct ip_addr *addr, u16_t port)
+{
+  time_t t;
+  int utcSec = 0;
+  TM_RTC_t data;
+  
+  if (p->len == sizeof(struct sntp_packet))
+  {
+    int i;
+    struct sntp_packet aligned;
+    //myassert(p->len == p->tot_len); /* don't accept chained pbuf */
+    memcpy(&aligned, p->payload, sizeof(aligned));
+    i = (aligned.status >> 3) & 7;
+    
+	if ((i < 1) || (i > 4)) /* SNTP version 1..4 */
+      goto out;
+
+    i = aligned.status & 7;
+
+	if ((i != 4) && (i != 5)) /* mode 4 or 5: server or broadcast */
+      goto out;
+
+	if (aligned.xmt == 0)
+      goto out;
+	
+	utcSec = (int)(3600.0*sSettings.sSNTP.timeZone);
+	t = (ntohl(aligned.xmt) - 2208988800 + utcSec );
+	
+	TM_RTC_SetDataTimeUnix((uint32_t)t);
+	
+	/* Сохраним время последней синхронизации */
+	TM_RTC_GetDateTime(&data, TM_RTC_Format_BIN);
+		
+	memset(sSettings.sSNTP.data, 0, sizeof(sSettings.sSNTP.data));
+	
+	sprintf(sSettings.sSNTP.data, "%02d.%02d.%02d %02d:%02d:%02d", 
+			data.date, data.month, data.year,
+			data.hours, data.minutes, data.seconds);
+	
+    timeout = VALID_TIMEOUT;
+    
+  }
+  out:
+    pbuf_free(p);
+}
+#endif 
+void SNTP_Enable(bool enable)
+{ /*
+  if (enable)
+  {
+    if (upcb == 0)
+    {
+      err_t ret;
+      upcb = udp_new();
+      if (upcb != 0)
+	  {
+		ret = udp_bind(upcb, IP_ADDR_ANY, port);
+
+	    if (ret != ERR_OK)
+        {
+          udp_remove(upcb);
+          upcb = 0;
+        }
+        else
+        {
+          udp_recv(upcb, recv, 0);
+        }
+        timeout = 0;
+	  }
+    }
+  }
+  else if (upcb != 0)
+  {
+    udp_remove(upcb);
+    upcb = 0;
+  }
+*/  
+}
+
+bool SNTP_IsEnabled(void)
+{
+  //return upcb != 0;
+}
+
+void SNTP_SetServerAddr(char *addr)
+{
+  //server.addr = ipaddr_addr(addr);
+}
+
+int sntp_getserverport(void)
+{
+  //return port;
+}
+
+static void send_request(void)
+{
+#if 0  
+  struct sntp_packet packet;
+  struct pbuf* psend;
+  memset(&packet, 0, sizeof(packet));
+  packet.status = (3 << 3) /* SNTP vesion 3 */ | (3 << 0); /* Mode: client */
+  psend = pbuf_alloc(PBUF_RAW, sizeof(packet), PBUF_REF);
+  
+  if (psend != 0)
+  {
+    psend->payload = &packet;
+    timeout = (udp_sendto(upcb, psend, &server, port) == ERR_OK) ? SENT_TIMEOUT : SENDFAIL_TIMEOUT;
+    pbuf_free(psend);
+  }
+#endif  
+}
+
+void SNTP_Poll(void)
+{ /*
+  if (upcb)
+    send_request();*/
+}

+ 56 - 56
modules/Ethernet/sntp.h

@@ -1,56 +1,56 @@
-#ifndef __SNTP_H__
-#define __SNTP_H__
-
-#include "lwip/udp.h"
-#include "lwip/def.h"
-#include "lwip/timers.h"
-#include "lwip/udp.h"
-#include "lwip/dns.h"
-#include "lwip/ip_addr.h"
-#include "lwip/pbuf.h"
-
-#include "stdbool.h"
-
-/**
-  * @brief  Синхронизация времени единоразово при включении контроллера
-  */
-void vTaskOnceSynchro(void *arg);
-
-/**
-  * @brief  Периодическая синхронизация времени.
-  *         Выполняется раз в сутки с 0 часов.
-  */
-void vTaskPeriodicSynchro(void *arg);
-
-void vTaskSntp(void *arg);
-
-/**
-  * @brief  Инициализация SNTP.
-  * @retval 
-  */
-void SNTP_Init(void);
-
-void SNTP_Enable(bool enable);
-
-bool SNTP_IsEnabled(void);
-
-void SNTP_SetServerAddr(char *addr);
-int sntp_getserverport(void);
-void SNTP_Poll(void);
-
-
-
-/*
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-void sntp_init(void);
-void sntp_stop(void);
-
-#ifdef __cplusplus
-}
-#endif
-*/
-
-#endif /* __SNTP_H__ */
+#ifndef __SNTP_H__
+#define __SNTP_H__
+/*
+#include "lwip/udp.h"
+#include "lwip/def.h"
+#include "lwip/timers.h"
+#include "lwip/udp.h"
+#include "lwip/dns.h"
+#include "lwip/ip_addr.h"
+#include "lwip/pbuf.h"
+*/
+#include "stdbool.h"
+
+/**
+  * @brief  Синхронизация времени единоразово при включении контроллера
+  */
+void vTaskOnceSynchro(void *arg);
+
+/**
+  * @brief  Периодическая синхронизация времени.
+  *         Выполняется раз в сутки с 0 часов.
+  */
+void vTaskPeriodicSynchro(void *arg);
+
+void vTaskSntp(void *arg);
+
+/**
+  * @brief  Инициализация SNTP.
+  * @retval 
+  */
+void SNTP_Init(void);
+
+void SNTP_Enable(bool enable);
+
+bool SNTP_IsEnabled(void);
+
+void SNTP_SetServerAddr(char *addr);
+int sntp_getserverport(void);
+void SNTP_Poll(void);
+
+
+
+/*
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void sntp_init(void);
+void sntp_stop(void);
+
+#ifdef __cplusplus
+}
+#endif
+*/
+
+#endif /* __SNTP_H__ */

+ 6 - 2
modules/Ethernet/trap_api.c

@@ -14,12 +14,12 @@
 #include "trap_api.h"
 #include "parameters.h"
 #include "settings_api.h"
-
+/*
 #include "lwip/snmp.h"
 #include "lwip/snmp_msg.h"
 #include "lwip/snmp_asn1.h"
 #include "lwip/snmp_structs.h"
-
+*/
 #ifdef PRINTF_STDLIB
 #include <stdio.h>
 #endif
@@ -172,6 +172,7 @@ void SNMP_InitTrapsBase(void)
   */
 bool SNMP_SendMessageTrap(TRAP_t trap, char* str, uint8_t len)
 {
+/*  
   const uint8_t maxMsgLen = 30;
   char msg[30];
   struct snmp_varbind *vb;
@@ -204,6 +205,7 @@ bool SNMP_SendMessageTrap(TRAP_t trap, char* str, uint8_t len)
   }
   else
 	return false;
+*/  
 }
 
 /**
@@ -212,6 +214,7 @@ bool SNMP_SendMessageTrap(TRAP_t trap, char* str, uint8_t len)
   */
 bool SNMP_SendVarbindTrap(TRAP_t *trap)
 {
+/*  
   char msg[255];
   uint8_t len = 0;
   struct snmp_varbind *vb;
@@ -238,6 +241,7 @@ bool SNMP_SendVarbindTrap(TRAP_t *trap)
   else {
         return false;
   }
+*/  
 }  
 
 

+ 1 - 1
modules/Ethernet/udp_netsetting.c

@@ -215,7 +215,7 @@ bool http_server_serve(struct netconn *conn)
     u16_t buflen;
     bool flag = false;
 
-    netconn_set_recvtimeout(conn, RCV_TIMEOUT);
+    //netconn_set_recvtimeout(conn, RCV_TIMEOUT);
     res = netconn_recv(conn, &inbuf);
 
   //  DBG printf("recv failed %d\n", res);

+ 3 - 2
modules/HTTP_Server/web_params_api.c

@@ -18,7 +18,7 @@
 #include "trap_api.h"
 #include "settings_api.h"
 #include "common_config.h"
-#include "snmp.h"
+//#include "snmp.h"
 #include "log.h"
 #include "hal.h"
 
@@ -395,7 +395,7 @@ void HTTP_GetUpsHistoryPage(char* buf, uint32_t pageNumber)
   * @brief  Возвращает uptime, freq, dutycicle
   */
 void HTTP_GetProgonParams(char *buf)
-{
+{/*
   char str[20];
   u32_t tick;  
   
@@ -409,6 +409,7 @@ void HTTP_GetProgonParams(char *buf)
   strncat(buf, str, strlen(str));
 
   strncat(buf, "\"}", 2);
+*/  
 }
 
 

+ 1 - 1
modules/parameters.c

@@ -337,7 +337,7 @@ void GetWorkTimeStr(char *str, uint8_t *len)
   uint8_t  hour;
   uint8_t  min;
 
-  snmp_get_sysuptime(&tick);
+  //snmp_get_sysuptime(&tick);
 
   day = tick/8640000;
   

+ 62 - 80
projects/iar/bt-670x.ewp

@@ -339,12 +339,12 @@
           <state>$PROJ_DIR$\..\..\modules\radius</state>
           <state>$PROJ_DIR$\..\..\thirdparty\FreeRTOS\include</state>
           <state>$PROJ_DIR$\..\..\thirdparty\FreeRTOS\portable\IAR\ARM_CM4F</state>
-          <state>$PROJ_DIR$\..\..\thirdparty\lwip\src\include</state>
+          <state>$PROJ_DIR$\..\..\thirdparty\lwip\src\include\</state>
           <state>$PROJ_DIR$\..\..\thirdparty\lwip\src\include\lwip</state>
-          <state>$PROJ_DIR$\..\..\thirdparty\lwip\port\FreeRTOS</state>
-          <state>$PROJ_DIR$\..\..\thirdparty\lwip\src\include\ipv4</state>
-          <state>$PROJ_DIR$\..\..\thirdparty\lwip\port</state>
-          <state>$PROJ_DIR$\..\..\thirdparty\lwip\arch</state>
+          <state>$PROJ_DIR$\..\..\thirdparty\lwip\src\include\netif\ppp\</state>
+          <state>$PROJ_DIR$\..\..\thirdparty\lwip\src\include\lwip\apps\</state>
+          <state>$PROJ_DIR$\..\..\thirdparty\lwip\system</state>
+          <state>$PROJ_DIR$\..\..\thirdparty\lwip\system\OS</state>
           <state>$PROJ_DIR$\..\..\thirdparty\PolarSSL\include\polarssl</state>
           <state>$PROJ_DIR$\..\..\thirdparty\PolarSSL\include</state>
           <state>$PROJ_DIR$\..\..\thirdparty\FreeRadius</state>
@@ -2343,13 +2343,10 @@
             <name>$PROJ_DIR$\..\..\thirdparty\lwip\src\core\ipv4\autoip.c</name>
           </file>
           <file>
-            <name>$PROJ_DIR$\..\..\thirdparty\lwip\src\core\def.c</name>
+            <name>$PROJ_DIR$\..\..\thirdparty\lwip\src\core\ipv4\dhcp.c</name>
           </file>
           <file>
-            <name>$PROJ_DIR$\..\..\thirdparty\lwip\src\core\dhcp.c</name>
-          </file>
-          <file>
-            <name>$PROJ_DIR$\..\..\thirdparty\lwip\src\core\dns.c</name>
+            <name>$PROJ_DIR$\..\..\thirdparty\lwip\src\core\ipv4\etharp.c</name>
           </file>
           <file>
             <name>$PROJ_DIR$\..\..\thirdparty\lwip\src\core\ipv4\icmp.c</name>
@@ -2358,86 +2355,71 @@
             <name>$PROJ_DIR$\..\..\thirdparty\lwip\src\core\ipv4\igmp.c</name>
           </file>
           <file>
-            <name>$PROJ_DIR$\..\..\thirdparty\lwip\src\core\ipv4\inet.c</name>
-          </file>
-          <file>
-            <name>$PROJ_DIR$\..\..\thirdparty\lwip\src\core\ipv4\inet_chksum.c</name>
-          </file>
-          <file>
-            <name>$PROJ_DIR$\..\..\thirdparty\lwip\src\core\init.c</name>
-          </file>
-          <file>
-            <name>$PROJ_DIR$\..\..\thirdparty\lwip\src\core\ipv4\ip.c</name>
-          </file>
-          <file>
-            <name>$PROJ_DIR$\..\..\thirdparty\lwip\src\core\ipv4\ip_addr.c</name>
-          </file>
-          <file>
-            <name>$PROJ_DIR$\..\..\thirdparty\lwip\src\core\ipv4\ip_frag.c</name>
-          </file>
-          <file>
-            <name>$PROJ_DIR$\..\..\thirdparty\lwip\src\core\mem.c</name>
-          </file>
-          <file>
-            <name>$PROJ_DIR$\..\..\thirdparty\lwip\src\core\memp.c</name>
-          </file>
-          <file>
-            <name>$PROJ_DIR$\..\..\thirdparty\lwip\src\core\netif.c</name>
-          </file>
-          <file>
-            <name>$PROJ_DIR$\..\..\thirdparty\lwip\src\core\pbuf.c</name>
-          </file>
-          <file>
-            <name>$PROJ_DIR$\..\..\thirdparty\lwip\src\core\raw.c</name>
-          </file>
-          <file>
-            <name>$PROJ_DIR$\..\..\thirdparty\lwip\src\core\stats.c</name>
-          </file>
-          <file>
-            <name>$PROJ_DIR$\..\..\thirdparty\lwip\src\core\sys.c</name>
-          </file>
-          <file>
-            <name>$PROJ_DIR$\..\..\thirdparty\lwip\src\core\tcp.c</name>
-          </file>
-          <file>
-            <name>$PROJ_DIR$\..\..\thirdparty\lwip\src\core\tcp_in.c</name>
-          </file>
-          <file>
-            <name>$PROJ_DIR$\..\..\thirdparty\lwip\src\core\tcp_out.c</name>
-          </file>
-          <file>
-            <name>$PROJ_DIR$\..\..\thirdparty\lwip\src\core\timers.c</name>
-          </file>
-          <file>
-            <name>$PROJ_DIR$\..\..\thirdparty\lwip\src\core\udp.c</name>
-          </file>
-        </group>
-        <group>
-          <name>snmp</name>
-          <file>
-            <name>$PROJ_DIR$\..\..\thirdparty\lwip\src\core\snmp\asn1_dec.c</name>
-          </file>
-          <file>
-            <name>$PROJ_DIR$\..\..\thirdparty\lwip\src\core\snmp\asn1_enc.c</name>
-          </file>
-          <file>
-            <name>$PROJ_DIR$\..\..\thirdparty\lwip\src\core\snmp\mib2.c</name>
-          </file>
-          <file>
-            <name>$PROJ_DIR$\..\..\thirdparty\lwip\src\core\snmp\mib_structs.c</name>
+            <name>$PROJ_DIR$\..\..\thirdparty\lwip\src\core\ipv4\ip4.c</name>
           </file>
           <file>
-            <name>$PROJ_DIR$\..\..\thirdparty\lwip\src\core\snmp\msg_in.c</name>
+            <name>$PROJ_DIR$\..\..\thirdparty\lwip\src\core\ipv4\ip4_addr.c</name>
           </file>
           <file>
-            <name>$PROJ_DIR$\..\..\thirdparty\lwip\src\core\snmp\msg_out.c</name>
+            <name>$PROJ_DIR$\..\..\thirdparty\lwip\src\core\ipv4\ip4_frag.c</name>
           </file>
         </group>
+        <file>
+          <name>$PROJ_DIR$\..\..\thirdparty\lwip\src\core\def.c</name>
+        </file>
+        <file>
+          <name>$PROJ_DIR$\..\..\thirdparty\lwip\src\core\dns.c</name>
+        </file>
+        <file>
+          <name>$PROJ_DIR$\..\..\thirdparty\lwip\src\core\inet_chksum.c</name>
+        </file>
+        <file>
+          <name>$PROJ_DIR$\..\..\thirdparty\lwip\src\core\init.c</name>
+        </file>
+        <file>
+          <name>$PROJ_DIR$\..\..\thirdparty\lwip\src\core\ip.c</name>
+        </file>
+        <file>
+          <name>$PROJ_DIR$\..\..\thirdparty\lwip\src\core\mem.c</name>
+        </file>
+        <file>
+          <name>$PROJ_DIR$\..\..\thirdparty\lwip\src\core\memp.c</name>
+        </file>
+        <file>
+          <name>$PROJ_DIR$\..\..\thirdparty\lwip\src\core\netif.c</name>
+        </file>
+        <file>
+          <name>$PROJ_DIR$\..\..\thirdparty\lwip\src\core\pbuf.c</name>
+        </file>
+        <file>
+          <name>$PROJ_DIR$\..\..\thirdparty\lwip\src\core\raw.c</name>
+        </file>
+        <file>
+          <name>$PROJ_DIR$\..\..\thirdparty\lwip\src\core\stats.c</name>
+        </file>
+        <file>
+          <name>$PROJ_DIR$\..\..\thirdparty\lwip\src\core\sys.c</name>
+        </file>
+        <file>
+          <name>$PROJ_DIR$\..\..\thirdparty\lwip\src\core\tcp.c</name>
+        </file>
+        <file>
+          <name>$PROJ_DIR$\..\..\thirdparty\lwip\src\core\tcp_in.c</name>
+        </file>
+        <file>
+          <name>$PROJ_DIR$\..\..\thirdparty\lwip\src\core\tcp_out.c</name>
+        </file>
+        <file>
+          <name>$PROJ_DIR$\..\..\thirdparty\lwip\src\core\timeouts.c</name>
+        </file>
+        <file>
+          <name>$PROJ_DIR$\..\..\thirdparty\lwip\src\core\udp.c</name>
+        </file>
       </group>
       <group>
         <name>netif</name>
         <file>
-          <name>$PROJ_DIR$\..\..\thirdparty\lwip\src\netif\etharp.c</name>
+          <name>$PROJ_DIR$\..\..\thirdparty\lwip\src\netif\ethernet.c</name>
         </file>
         <file>
           <name>$PROJ_DIR$\..\..\thirdparty\lwip\src\netif\slipif.c</name>

+ 3 - 1
thirdparty/FreeRadius/radius_config.h

@@ -17,6 +17,8 @@
 #include <stdint.h>
 #include <string.h>
 
+#include "sockets.h"
+
 #define RC_LOG
 #define STDC_HEADERS
 #define HAVE_RAND       //  Рандом генератор
@@ -50,7 +52,7 @@ struct __kernel_sockaddr_storage {
 
 // -----------------------------------------------------------------------------
 
-typedef __kernel_sa_family_t	sa_family_t;
+//typedef __kernel_sa_family_t	sa_family_t;
 
 // -----------------------------------------------------------------------------
 /*

+ 1 - 1
thirdparty/lwip/port/FreeRTOS/ethernetif.c

@@ -51,7 +51,7 @@
 #include "netif/etharp.h"
 #include "err.h"
 #include "ethernetif.h"
-#include "lwip/timers.h"
+//#include "lwip/timers.h"
 
 #include "common_config.h"
 #include "settings_api.h"

+ 476 - 586
thirdparty/lwip/port/FreeRTOS/sys_arch.c

@@ -1,586 +1,476 @@
-/*
- * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * This file is part of the lwIP TCP/IP stack.
- *
- * Author: Adam Dunkels <adam@sics.se>
- *
- */
-
-//*****************************************************************************
-//
-// Include OS functionality.
-//
-//*****************************************************************************
-
-/* ------------------------ System architecture includes ----------------------------- */
-#include "arch/sys_arch.h"
-
-/* ------------------------ lwIP includes --------------------------------- */
-#include "lwip/opt.h"
-
-#include "lwip/debug.h"
-#include "lwip/def.h"
-#include "lwip/sys.h"
-#include "lwip/mem.h"
-#include "lwip/stats.h"
-
-#define LWIP_PLATFORM_DIAG(x)   printf(message)
-
-/* Very crude mechanism used to determine if the critical section handling
-functions are being called from an interrupt context or not.  This relies on
-the interrupt handler setting this variable manually. */
-portBASE_TYPE xInsideISR = pdFALSE;
-
-/*---------------------------------------------------------------------------*
- * Routine:  sys_mbox_new
- *---------------------------------------------------------------------------*
- * Description:
- *      Creates a new mailbox
- * Inputs:
- *      int size                -- Size of elements in the mailbox
- * Outputs:
- *      sys_mbox_t              -- Handle to new mailbox
- *---------------------------------------------------------------------------*/
-err_t sys_mbox_new( sys_mbox_t *pxMailBox, int iSize )
-{
-err_t xReturn = ERR_MEM;
-
-	*pxMailBox = xQueueCreate( iSize, sizeof( void * ) );
-
-	if( *pxMailBox != NULL )
-	{
-		xReturn = ERR_OK;
-		SYS_STATS_INC_USED( mbox );
-	}
-
-	return xReturn;
-}
-
-
-/*---------------------------------------------------------------------------*
- * Routine:  sys_mbox_free
- *---------------------------------------------------------------------------*
- * Description:
- *      Deallocates a mailbox. If there are messages still present in the
- *      mailbox when the mailbox is deallocated, it is an indication of a
- *      programming error in lwIP and the developer should be notified.
- * Inputs:
- *      sys_mbox_t mbox         -- Handle of mailbox
- * Outputs:
- *      sys_mbox_t              -- Handle to new mailbox
- *---------------------------------------------------------------------------*/
-void sys_mbox_free( sys_mbox_t *pxMailBox )
-{
-unsigned long ulMessagesWaiting;
-
-	ulMessagesWaiting = uxQueueMessagesWaiting( *pxMailBox );
-	configASSERT( ( ulMessagesWaiting == 0 ) );
-
-	#if SYS_STATS
-	{
-		if( ulMessagesWaiting != 0UL )
-		{
-			SYS_STATS_INC( mbox.err );
-		}
-
-		SYS_STATS_DEC( mbox.used );
-	}
-	#endif /* SYS_STATS */
-
-	vQueueDelete( *pxMailBox );
-}
-
-/*---------------------------------------------------------------------------*
- * Routine:  sys_mbox_post
- *---------------------------------------------------------------------------*
- * Description:
- *      Post the "msg" to the mailbox.
- * Inputs:
- *      sys_mbox_t mbox         -- Handle of mailbox
- *      void *data              -- Pointer to data to post
- *---------------------------------------------------------------------------*/
-void sys_mbox_post( sys_mbox_t *pxMailBox, void *pxMessageToPost )
-{
-	while( xQueueSendToBack( *pxMailBox, &pxMessageToPost, portMAX_DELAY ) != pdTRUE );
-}
-
-/*---------------------------------------------------------------------------*
- * Routine:  sys_mbox_trypost
- *---------------------------------------------------------------------------*
- * Description:
- *      Try to post the "msg" to the mailbox.  Returns immediately with
- *      error if cannot.
- * Inputs:
- *      sys_mbox_t mbox         -- Handle of mailbox
- *      void *msg               -- Pointer to data to post
- * Outputs:
- *      err_t                   -- ERR_OK if message posted, else ERR_MEM
- *                                  if not.
- *---------------------------------------------------------------------------*/
-err_t sys_mbox_trypost( sys_mbox_t *pxMailBox, void *pxMessageToPost )
-{
-err_t xReturn;
-portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
-
-	if( xInsideISR != pdFALSE )
-	{
-		xReturn = xQueueSendFromISR( *pxMailBox, &pxMessageToPost, &xHigherPriorityTaskWoken );
-	}
-	else
-	{
-		xReturn = xQueueSend( *pxMailBox, &pxMessageToPost, ( TickType_t ) 0 );
-	}
-
-	if( xReturn == pdPASS )
-	{
-		xReturn = ERR_OK;
-	}
-	else
-	{
-		/* The queue was already full. */
-		xReturn = ERR_MEM;
-		SYS_STATS_INC( mbox.err );
-	}
-
-	return xReturn;
-}
-
-/*---------------------------------------------------------------------------*
- * Routine:  sys_arch_mbox_fetch
- *---------------------------------------------------------------------------*
- * Description:
- *      Blocks the thread until a message arrives in the mailbox, but does
- *      not block the thread longer than "timeout" milliseconds (similar to
- *      the sys_arch_sem_wait() function). The "msg" argument is a result
- *      parameter that is set by the function (i.e., by doing "*msg =
- *      ptr"). The "msg" parameter maybe NULL to indicate that the message
- *      should be dropped.
- *
- *      The return values are the same as for the sys_arch_sem_wait() function:
- *      Number of milliseconds spent waiting or SYS_ARCH_TIMEOUT if there was a
- *      timeout.
- *
- *      Note that a function with a similar name, sys_mbox_fetch(), is
- *      implemented by lwIP.
- * Inputs:
- *      sys_mbox_t mbox         -- Handle of mailbox
- *      void **msg              -- Pointer to pointer to msg received
- *      u32_t timeout           -- Number of milliseconds until timeout
- * Outputs:
- *      u32_t                   -- SYS_ARCH_TIMEOUT if timeout, else number
- *                                  of milliseconds until received.
- *---------------------------------------------------------------------------*/
-u32_t sys_arch_mbox_fetch( sys_mbox_t *pxMailBox, void **ppvBuffer, u32_t ulTimeOut )
-{
-void *pvDummy;
-TickType_t xStartTime, xEndTime, xElapsed;
-unsigned long ulReturn;
-
-	xStartTime = xTaskGetTickCount();
-
-	if( NULL == ppvBuffer )
-	{
-		ppvBuffer = &pvDummy;
-	}
-
-	if( ulTimeOut != 0UL )
-	{
-		configASSERT( xInsideISR == ( portBASE_TYPE ) 0 );
-
-		if( pdTRUE == xQueueReceive( *pxMailBox, &( *ppvBuffer ), ulTimeOut/ portTICK_PERIOD_MS ) )
-		{
-			xEndTime = xTaskGetTickCount();
-			xElapsed = ( xEndTime - xStartTime ) * portTICK_PERIOD_MS;
-
-			ulReturn = xElapsed;
-		}
-		else
-		{
-			/* Timed out. */
-			*ppvBuffer = NULL;
-			ulReturn = SYS_ARCH_TIMEOUT;
-		}
-	}
-	else
-	{
-		while( pdTRUE != xQueueReceive( *pxMailBox, &( *ppvBuffer ), portMAX_DELAY ) );
-		xEndTime = xTaskGetTickCount();
-		xElapsed = ( xEndTime - xStartTime ) * portTICK_PERIOD_MS;
-
-		if( xElapsed == 0UL )
-		{
-			xElapsed = 1UL;
-		}
-
-		ulReturn = xElapsed;
-	}
-
-	return ulReturn;
-}
-
-/*---------------------------------------------------------------------------*
- * Routine:  sys_arch_mbox_tryfetch
- *---------------------------------------------------------------------------*
- * Description:
- *      Similar to sys_arch_mbox_fetch, but if message is not ready
- *      immediately, we'll return with SYS_MBOX_EMPTY.  On success, 0 is
- *      returned.
- * Inputs:
- *      sys_mbox_t mbox         -- Handle of mailbox
- *      void **msg              -- Pointer to pointer to msg received
- * Outputs:
- *      u32_t                   -- SYS_MBOX_EMPTY if no messages.  Otherwise,
- *                                  return ERR_OK.
- *---------------------------------------------------------------------------*/
-u32_t sys_arch_mbox_tryfetch( sys_mbox_t *pxMailBox, void **ppvBuffer )
-{
-void *pvDummy;
-unsigned long ulReturn;
-long lResult;
-portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
-
-	if( ppvBuffer== NULL )
-	{
-		ppvBuffer = &pvDummy;
-	}
-
-	if( xInsideISR != pdFALSE )
-	{
-		lResult = xQueueReceiveFromISR( *pxMailBox, &( *ppvBuffer ), &xHigherPriorityTaskWoken );
-	}
-	else
-	{
-		lResult = xQueueReceive( *pxMailBox, &( *ppvBuffer ), 0UL );
-	}
-
-	if( lResult == pdPASS )
-	{
-		ulReturn = ERR_OK;
-	}
-	else
-	{
-		ulReturn = SYS_MBOX_EMPTY;
-	}
-
-	return ulReturn;
-}
-
-/*---------------------------------------------------------------------------*
- * Routine:  sys_sem_new
- *---------------------------------------------------------------------------*
- * Description:
- *      Creates and returns a new semaphore. The "ucCount" argument specifies
- *      the initial state of the semaphore.
- *      NOTE: Currently this routine only creates counts of 1 or 0
- * Inputs:
- *      sys_mbox_t mbox         -- Handle of mailbox
- *      u8_t ucCount              -- Initial ucCount of semaphore (1 or 0)
- * Outputs:
- *      sys_sem_t               -- Created semaphore or 0 if could not create.
- *---------------------------------------------------------------------------*/
-err_t sys_sem_new( sys_sem_t *pxSemaphore, u8_t ucCount )
-{
-err_t xReturn = ERR_MEM;
-
-	vSemaphoreCreateBinary( ( *pxSemaphore ) );
-
-	if( *pxSemaphore != NULL )
-	{
-		if( ucCount == 0U )
-		{
-			xSemaphoreTake( *pxSemaphore, 1UL );
-		}
-
-		xReturn = ERR_OK;
-		SYS_STATS_INC_USED( sem );
-	}
-	else
-	{
-		SYS_STATS_INC( sem.err );
-	}
-
-	return xReturn;
-}
-
-/*---------------------------------------------------------------------------*
- * Routine:  sys_arch_sem_wait
- *---------------------------------------------------------------------------*
- * Description:
- *      Blocks the thread while waiting for the semaphore to be
- *      signaled. If the "timeout" argument is non-zero, the thread should
- *      only be blocked for the specified time (measured in
- *      milliseconds).
- *
- *      If the timeout argument is non-zero, the return value is the number of
- *      milliseconds spent waiting for the semaphore to be signaled. If the
- *      semaphore wasn't signaled within the specified time, the return value is
- *      SYS_ARCH_TIMEOUT. If the thread didn't have to wait for the semaphore
- *      (i.e., it was already signaled), the function may return zero.
- *
- *      Notice that lwIP implements a function with a similar name,
- *      sys_sem_wait(), that uses the sys_arch_sem_wait() function.
- * Inputs:
- *      sys_sem_t sem           -- Semaphore to wait on
- *      u32_t timeout           -- Number of milliseconds until timeout
- * Outputs:
- *      u32_t                   -- Time elapsed or SYS_ARCH_TIMEOUT.
- *---------------------------------------------------------------------------*/
-u32_t sys_arch_sem_wait( sys_sem_t *pxSemaphore, u32_t ulTimeout )
-{
-TickType_t xStartTime, xEndTime, xElapsed;
-unsigned long ulReturn;
-
-	xStartTime = xTaskGetTickCount();
-
-	if( ulTimeout != 0UL )
-	{
-		if( xSemaphoreTake( *pxSemaphore, ulTimeout / portTICK_PERIOD_MS ) == pdTRUE )
-		{
-			xEndTime = xTaskGetTickCount();
-			xElapsed = (xEndTime - xStartTime) * portTICK_PERIOD_MS;
-			ulReturn = xElapsed;
-		}
-		else
-		{
-			ulReturn = SYS_ARCH_TIMEOUT;
-		}
-	}
-	else
-	{
-		while( xSemaphoreTake( *pxSemaphore, portMAX_DELAY ) != pdTRUE );
-		xEndTime = xTaskGetTickCount();
-		xElapsed = ( xEndTime - xStartTime ) * portTICK_PERIOD_MS;
-
-		if( xElapsed == 0UL )
-		{
-			xElapsed = 1UL;
-		}
-
-		ulReturn = xElapsed;
-	}
-
-	return ulReturn;
-}
-
-/** Create a new mutex
- * @param mutex pointer to the mutex to create
- * @return a new mutex */
-err_t sys_mutex_new( sys_mutex_t *pxMutex )
-{
-err_t xReturn = ERR_MEM;
-
-	*pxMutex = xSemaphoreCreateMutex();
-
-	if( *pxMutex != NULL )
-	{
-		xReturn = ERR_OK;
-		SYS_STATS_INC_USED( mutex );
-	}
-	else
-	{
-		SYS_STATS_INC( mutex.err );
-	}
-
-	return xReturn;
-}
-
-/** Lock a mutex
- * @param mutex the mutex to lock */
-void sys_mutex_lock( sys_mutex_t *pxMutex )
-{
-	while( xSemaphoreTake( *pxMutex, portMAX_DELAY ) != pdPASS );
-}
-
-/** Unlock a mutex
- * @param mutex the mutex to unlock */
-void sys_mutex_unlock(sys_mutex_t *pxMutex )
-{
-	xSemaphoreGive( *pxMutex );
-}
-
-
-/** Delete a semaphore
- * @param mutex the mutex to delete */
-void sys_mutex_free( sys_mutex_t *pxMutex )
-{
-	SYS_STATS_DEC( mutex.used );
-	vQueueDelete( *pxMutex );
-}
-
-
-/*---------------------------------------------------------------------------*
- * Routine:  sys_sem_signal
- *---------------------------------------------------------------------------*
- * Description:
- *      Signals (releases) a semaphore
- * Inputs:
- *      sys_sem_t sem           -- Semaphore to signal
- *---------------------------------------------------------------------------*/
-void sys_sem_signal( sys_sem_t *pxSemaphore )
-{
-portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
-
-	if( xInsideISR != pdFALSE )
-	{
-		xSemaphoreGiveFromISR( *pxSemaphore, &xHigherPriorityTaskWoken );
-	}
-	else
-	{
-		xSemaphoreGive( *pxSemaphore );
-	}
-}
-
-/*---------------------------------------------------------------------------*
- * Routine:  sys_sem_free
- *---------------------------------------------------------------------------*
- * Description:
- *      Deallocates a semaphore
- * Inputs:
- *      sys_sem_t sem           -- Semaphore to free
- *---------------------------------------------------------------------------*/
-void sys_sem_free( sys_sem_t *pxSemaphore )
-{
-	SYS_STATS_DEC(sem.used);
-	vQueueDelete( *pxSemaphore );
-}
-
-/*---------------------------------------------------------------------------*
- * Routine:  sys_init
- *---------------------------------------------------------------------------*
- * Description:
- *      Initialize sys arch
- *---------------------------------------------------------------------------*/
-void sys_init(void)
-{
-}
-
-u32_t sys_now(void)
-{
-	return xTaskGetTickCount();
-}
-
-/*---------------------------------------------------------------------------*
- * Routine:  sys_thread_new
- *---------------------------------------------------------------------------*
- * Description:
- *      Starts a new thread with priority "prio" that will begin its
- *      execution in the function "thread()". The "arg" argument will be
- *      passed as an argument to the thread() function. The id of the new
- *      thread is returned. Both the id and the priority are system
- *      dependent.
- * Inputs:
- *      char *name              -- Name of thread
- *      void (* thread)(void *arg) -- Pointer to function to run.
- *      void *arg               -- Argument passed into function
- *      int stacksize           -- Required stack amount in bytes
- *      int prio                -- Thread priority
- * Outputs:
- *      sys_thread_t            -- Pointer to per-thread timeouts.
- *---------------------------------------------------------------------------*/
-sys_thread_t sys_thread_new( const char *pcName, void( *pxThread )( void *pvParameters ), void *pvArg, int iStackSize, int iPriority )
-{
-TaskHandle_t xCreatedTask;
-portBASE_TYPE xResult;
-sys_thread_t xReturn;
-
-	xResult = xTaskCreate( pxThread, pcName, iStackSize, pvArg, iPriority, &xCreatedTask );
-
-	if( xResult == pdPASS )
-	{
-		xReturn = xCreatedTask;
-	}
-	else
-	{
-		xReturn = NULL;
-	}
-
-	return xReturn;
-}
-
-/*---------------------------------------------------------------------------*
- * Routine:  sys_arch_protect
- *---------------------------------------------------------------------------*
- * Description:
- *      This optional function does a "fast" critical region protection and
- *      returns the previous protection level. This function is only called
- *      during very short critical regions. An embedded system which supports
- *      ISR-based drivers might want to implement this function by disabling
- *      interrupts. Task-based systems might want to implement this by using
- *      a mutex or disabling tasking. This function should support recursive
- *      calls from the same task or interrupt. In other words,
- *      sys_arch_protect() could be called while already protected. In
- *      that case the return value indicates that it is already protected.
- *
- *      sys_arch_protect() is only required if your port is supporting an
- *      operating system.
- * Outputs:
- *      sys_prot_t              -- Previous protection level (not used here)
- *---------------------------------------------------------------------------*/
-sys_prot_t sys_arch_protect( void )
-{
-	if( xInsideISR == pdFALSE )
-	{
-		taskENTER_CRITICAL();
-	}
-	return ( sys_prot_t ) 1;
-}
-
-/*---------------------------------------------------------------------------*
- * Routine:  sys_arch_unprotect
- *---------------------------------------------------------------------------*
- * Description:
- *      This optional function does a "fast" set of critical region
- *      protection to the value specified by pval. See the documentation for
- *      sys_arch_protect() for more information. This function is only
- *      required if your port is supporting an operating system.
- * Inputs:
- *      sys_prot_t              -- Previous protection level (not used here)
- *---------------------------------------------------------------------------*/
-void sys_arch_unprotect( sys_prot_t xValue )
-{
-	(void) xValue;
-	if( xInsideISR == pdFALSE )
-	{
-		taskEXIT_CRITICAL();
-	}
-}
-
-/*
- * Prints an assertion messages and aborts execution.
- */
-void sys_assert( const char *pcMessage )
-{
-	(void) pcMessage;
-
-	for (;;)
-	{
-	}
-}
-/*-------------------------------------------------------------------------*
- * End of File:  sys_arch.c
- *-------------------------------------------------------------------------*/
-
+/*
+ * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+/* lwIP includes. */
+#include "lwip/debug.h"
+#include "lwip/def.h"
+#include "lwip/sys.h"
+#include "lwip/mem.h"
+#include "lwip/stats.h"
+#include "FreeRTOS.h"
+#include "task.h"
+
+
+xTaskHandle xTaskGetCurrentTaskHandle( void ) PRIVILEGED_FUNCTION;
+
+/* This is the number of threads that can be started with sys_thread_new() */
+#define SYS_THREAD_MAX 6
+
+static u16_t s_nextthread = 0;
+
+
+/*-----------------------------------------------------------------------------------*/
+//  Creates an empty mailbox.
+err_t sys_mbox_new(sys_mbox_t *mbox, int size)
+{
+	(void ) size;
+	
+	*mbox = xQueueCreate( archMESG_QUEUE_LENGTH, sizeof( void * ) );
+
+#if SYS_STATS
+      ++lwip_stats.sys.mbox.used;
+      if (lwip_stats.sys.mbox.max < lwip_stats.sys.mbox.used) {
+         lwip_stats.sys.mbox.max = lwip_stats.sys.mbox.used;
+	  }
+#endif /* SYS_STATS */
+ if (*mbox == NULL)
+  return ERR_MEM;
+ 
+ return ERR_OK;
+}
+
+/*-----------------------------------------------------------------------------------*/
+/*
+  Deallocates a mailbox. If there are messages still present in the
+  mailbox when the mailbox is deallocated, it is an indication of a
+  programming error in lwIP and the developer should be notified.
+*/
+void sys_mbox_free(sys_mbox_t *mbox)
+{
+	if( uxQueueMessagesWaiting( *mbox ) )
+	{
+		/* Line for breakpoint.  Should never break here! */
+		portNOP();
+#if SYS_STATS
+	    lwip_stats.sys.mbox.err++;
+#endif /* SYS_STATS */
+			
+		// TODO notify the user of failure.
+	}
+
+	vQueueDelete( *mbox );
+
+#if SYS_STATS
+     --lwip_stats.sys.mbox.used;
+#endif /* SYS_STATS */
+}
+
+/*-----------------------------------------------------------------------------------*/
+//   Posts the "msg" to the mailbox.
+void sys_mbox_post(sys_mbox_t *mbox, void *data)
+{
+	while ( xQueueSendToBack(*mbox, &data, portMAX_DELAY ) != pdTRUE ){}
+}
+
+
+/*-----------------------------------------------------------------------------------*/
+//   Try to post the "msg" to the mailbox.
+err_t sys_mbox_trypost(sys_mbox_t *mbox, void *msg)
+{
+err_t result;
+
+   if ( xQueueSend( *mbox, &msg, 0 ) == pdPASS )
+   {
+      result = ERR_OK;
+   }
+   else {
+      // could not post, queue must be full
+      result = ERR_MEM;
+			
+#if SYS_STATS
+      lwip_stats.sys.mbox.err++;
+#endif /* SYS_STATS */
+			
+   }
+
+   return result;
+}
+
+/*-----------------------------------------------------------------------------------*/
+/*
+  Blocks the thread until a message arrives in the mailbox, but does
+  not block the thread longer than "timeout" milliseconds (similar to
+  the sys_arch_sem_wait() function). The "msg" argument is a result
+  parameter that is set by the function (i.e., by doing "*msg =
+  ptr"). The "msg" parameter maybe NULL to indicate that the message
+  should be dropped.
+
+  The return values are the same as for the sys_arch_sem_wait() function:
+  Number of milliseconds spent waiting or SYS_ARCH_TIMEOUT if there was a
+  timeout.
+
+  Note that a function with a similar name, sys_mbox_fetch(), is
+  implemented by lwIP.
+*/
+u32_t sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout)
+{
+void *dummyptr;
+portTickType StartTime, EndTime, Elapsed;
+
+	StartTime = xTaskGetTickCount();
+
+	if ( msg == NULL )
+	{
+		msg = &dummyptr;
+	}
+		
+	if ( timeout != 0 )
+	{
+		if ( pdTRUE == xQueueReceive( *mbox, &(*msg), timeout / portTICK_RATE_MS ) )
+		{
+			EndTime = xTaskGetTickCount();
+			Elapsed = (EndTime - StartTime) * portTICK_RATE_MS;
+			
+			return ( Elapsed );
+		}
+		else // timed out blocking for message
+		{
+			*msg = NULL;
+			
+			return SYS_ARCH_TIMEOUT;
+		}
+	}
+	else // block forever for a message.
+	{
+		while( pdTRUE != xQueueReceive( *mbox, &(*msg), portMAX_DELAY ) ){} // time is arbitrary
+		EndTime = xTaskGetTickCount();
+		Elapsed = (EndTime - StartTime) * portTICK_RATE_MS;
+		
+		return ( Elapsed ); // return time blocked TODO test	
+	}
+}
+
+/*-----------------------------------------------------------------------------------*/
+/*
+  Similar to sys_arch_mbox_fetch, but if message is not ready immediately, we'll
+  return with SYS_MBOX_EMPTY.  On success, 0 is returned.
+*/
+u32_t sys_arch_mbox_tryfetch(sys_mbox_t *mbox, void **msg)
+{
+void *dummyptr;
+
+	if ( msg == NULL )
+	{
+		msg = &dummyptr;
+	}
+
+   if ( pdTRUE == xQueueReceive( *mbox, &(*msg), 0 ) )
+   {
+      return ERR_OK;
+   }
+   else
+   {
+      return SYS_MBOX_EMPTY;
+   }
+}
+/*----------------------------------------------------------------------------------*/
+int sys_mbox_valid(sys_mbox_t *mbox)          
+{      
+  if (*mbox == SYS_MBOX_NULL) 
+    return 0;
+  else
+    return 1;
+}                                             
+/*-----------------------------------------------------------------------------------*/                                              
+void sys_mbox_set_invalid(sys_mbox_t *mbox)   
+{                                             
+  *mbox = SYS_MBOX_NULL;                      
+}                                             
+
+/*-----------------------------------------------------------------------------------*/
+//  Creates a new semaphore. The "count" argument specifies
+//  the initial state of the semaphore.
+err_t sys_sem_new(sys_sem_t *sem, u8_t count)
+{
+	vSemaphoreCreateBinary(*sem );
+	if(*sem == NULL)
+	{
+#if SYS_STATS
+      ++lwip_stats.sys.sem.err;
+#endif /* SYS_STATS */	
+		return ERR_MEM;
+	}
+	
+	if(count == 0)	// Means it can't be taken
+	{
+		xSemaphoreTake(*sem,1);
+	}
+
+#if SYS_STATS
+	++lwip_stats.sys.sem.used;
+ 	if (lwip_stats.sys.sem.max < lwip_stats.sys.sem.used) {
+		lwip_stats.sys.sem.max = lwip_stats.sys.sem.used;
+	}
+#endif /* SYS_STATS */
+		
+	return ERR_OK;
+}
+
+/*-----------------------------------------------------------------------------------*/
+/*
+  Blocks the thread while waiting for the semaphore to be
+  signaled. If the "timeout" argument is non-zero, the thread should
+  only be blocked for the specified time (measured in
+  milliseconds).
+
+  If the timeout argument is non-zero, the return value is the number of
+  milliseconds spent waiting for the semaphore to be signaled. If the
+  semaphore wasn't signaled within the specified time, the return value is
+  SYS_ARCH_TIMEOUT. If the thread didn't have to wait for the semaphore
+  (i.e., it was already signaled), the function may return zero.
+
+  Notice that lwIP implements a function with a similar name,
+  sys_sem_wait(), that uses the sys_arch_sem_wait() function.
+*/
+u32_t sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout)
+{
+portTickType StartTime, EndTime, Elapsed;
+
+	StartTime = xTaskGetTickCount();
+
+	if(	timeout != 0)
+	{
+		if( xSemaphoreTake( *sem, timeout / portTICK_RATE_MS ) == pdTRUE )
+		{
+			EndTime = xTaskGetTickCount();
+			Elapsed = (EndTime - StartTime) * portTICK_RATE_MS;
+			
+			return (Elapsed); // return time blocked TODO test	
+		}
+		else
+		{
+			return SYS_ARCH_TIMEOUT;
+		}
+	}
+	else // must block without a timeout
+	{
+		while( xSemaphoreTake(*sem, portMAX_DELAY) != pdTRUE){}
+		EndTime = xTaskGetTickCount();
+		Elapsed = (EndTime - StartTime) * portTICK_RATE_MS;
+
+		return ( Elapsed ); // return time blocked	
+		
+	}
+}
+
+/*-----------------------------------------------------------------------------------*/
+// Signals a semaphore
+void sys_sem_signal(sys_sem_t *sem)
+{
+	xSemaphoreGive(*sem);
+}
+
+/*-----------------------------------------------------------------------------------*/
+// Deallocates a semaphore
+void sys_sem_free(sys_sem_t *sem)
+{
+#if SYS_STATS
+      --lwip_stats.sys.sem.used;
+#endif /* SYS_STATS */
+			
+	vQueueDelete(*sem);
+}
+/*-----------------------------------------------------------------------------------*/
+int sys_sem_valid(sys_sem_t *sem)                                               
+{
+  if (*sem == SYS_SEM_NULL)
+    return 0;
+  else
+    return 1;                                       
+}
+
+/*-----------------------------------------------------------------------------------*/                                                                                                                                                                
+void sys_sem_set_invalid(sys_sem_t *sem)                                        
+{                                                                               
+  *sem = SYS_SEM_NULL;                                                          
+} 
+
+/*-----------------------------------------------------------------------------------*/
+// Initialize sys arch
+void sys_init(void)
+{
+	// keep track of how many threads have been created
+	s_nextthread = 0;
+}
+/*-----------------------------------------------------------------------------------*/
+                                      /* Mutexes*/
+/*-----------------------------------------------------------------------------------*/
+/*-----------------------------------------------------------------------------------*/
+#if LWIP_COMPAT_MUTEX == 0
+/* Create a new mutex*/
+err_t sys_mutex_new(sys_mutex_t *mutex) {
+
+  *mutex = xSemaphoreCreateMutex();
+		if(*mutex == NULL)
+	{
+#if SYS_STATS
+      ++lwip_stats.sys.mutex.err;
+#endif /* SYS_STATS */	
+		return ERR_MEM;
+	}
+
+#if SYS_STATS
+	++lwip_stats.sys.mutex.used;
+ 	if (lwip_stats.sys.mutex.max < lwip_stats.sys.mutex.used) {
+		lwip_stats.sys.mutex.max = lwip_stats.sys.mutex.used;
+	}
+#endif /* SYS_STATS */
+        return ERR_OK;
+}
+/*-----------------------------------------------------------------------------------*/
+/* Deallocate a mutex*/
+void sys_mutex_free(sys_mutex_t *mutex)
+{
+#if SYS_STATS
+      --lwip_stats.sys.mutex.used;
+#endif /* SYS_STATS */
+			
+	vQueueDelete(*mutex);
+}
+/*-----------------------------------------------------------------------------------*/
+/* Lock a mutex*/
+void sys_mutex_lock(sys_mutex_t *mutex)
+{
+	sys_arch_sem_wait(mutex, 0);
+}
+
+/*-----------------------------------------------------------------------------------*/
+/* Unlock a mutex*/
+void sys_mutex_unlock(sys_mutex_t *mutex)
+{
+	xSemaphoreGive(*mutex);
+}
+#endif /*LWIP_COMPAT_MUTEX*/
+/*-----------------------------------------------------------------------------------*/
+// TODO
+/*-----------------------------------------------------------------------------------*/
+/*
+  Starts a new thread with priority "prio" that will begin its execution in the
+  function "thread()". The "arg" argument will be passed as an argument to the
+  thread() function. The id of the new thread is returned. Both the id and
+  the priority are system dependent.
+*/
+sys_thread_t sys_thread_new(const char *name, lwip_thread_fn thread , void *arg, int stacksize, int prio)
+{
+xTaskHandle CreatedTask;
+int result;
+
+   if ( s_nextthread < SYS_THREAD_MAX )
+   {
+      result = xTaskCreate( thread, name, stacksize, arg, prio, &CreatedTask );
+
+	   // For each task created, store the task handle (pid) in the timers array.
+	   // This scheme doesn't allow for threads to be deleted
+	   //s_timeoutlist[s_nextthread++].pid = CreatedTask;
+
+	   if(result == pdPASS)
+	   {
+		   return CreatedTask;
+	   }
+	   else
+	   {
+		   return NULL;
+	   }
+   }
+   else
+   {
+      return NULL;
+   }
+}
+
+/*
+  This optional function does a "fast" critical region protection and returns
+  the previous protection level. This function is only called during very short
+  critical regions. An embedded system which supports ISR-based drivers might
+  want to implement this function by disabling interrupts. Task-based systems
+  might want to implement this by using a mutex or disabling tasking. This
+  function should support recursive calls from the same task or interrupt. In
+  other words, sys_arch_protect() could be called while already protected. In
+  that case the return value indicates that it is already protected.
+
+  sys_arch_protect() is only required if your port is supporting an operating
+  system.
+*/
+sys_prot_t sys_arch_protect(void)
+{
+	vPortEnterCritical();
+	return 1;
+}
+
+/*
+  This optional function does a "fast" set of critical region protection to the
+  value specified by pval. See the documentation for sys_arch_protect() for
+  more information. This function is only required if your port is supporting
+  an operating system.
+*/
+void sys_arch_unprotect(sys_prot_t pval)
+{
+	( void ) pval;
+	vPortExitCritical();
+}
+
+/*
+ * Prints an assertion messages and aborts execution.
+ */
+void sys_assert( const char *msg )
+{	
+	( void ) msg;
+	/*FSL:only needed for debugging
+	printf(msg);
+	printf("\n\r");
+	*/
+    vPortEnterCritical(  );
+    for(;;)
+    ;
+}
+
+u32_t sys_jiffies(void)
+{
+  portTickType    xTicks = xTaskGetTickCount();
+
+  return (u32_t)(xTaskGetTickCount()*portTICK_RATE_MS);
+}
+
+u32_t sys_now(void)
+{
+  return xTaskGetTickCount();
+}

+ 0 - 1
thirdparty/lwip/port/arch/bpstruct.h

@@ -1 +0,0 @@
-#pragma pack(push,1)

+ 0 - 1
thirdparty/lwip/port/arch/epstruct.h

@@ -1 +0,0 @@
-#pragma pack(pop)

+ 0 - 40
thirdparty/lwip/port/arch/perf.h

@@ -1,40 +0,0 @@
-/*
- * Copyright (c) 2001, Swedish Institute of Computer Science.
- * All rights reserved. 
- *
- * Redistribution and use in source and binary forms, with or without 
- * modification, are permitted provided that the following conditions 
- * are met: 
- * 1. Redistributions of source code must retain the above copyright 
- *    notice, this list of conditions and the following disclaimer. 
- * 2. Redistributions in binary form must reproduce the above copyright 
- *    notice, this list of conditions and the following disclaimer in the 
- *    documentation and/or other materials provided with the distribution. 
- * 3. Neither the name of the Institute nor the names of its contributors 
- *    may be used to endorse or promote products derived from this software 
- *    without specific prior written permission. 
- *
- * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
- * SUCH DAMAGE. 
- *
- * This file is part of the lwIP TCP/IP stack.
- * 
- * Author: Adam Dunkels <adam@sics.se>
- *
- */
-#ifndef __PERF_H__
-#define __PERF_H__
-
-#define PERF_START    /* null definition */
-#define PERF_STOP(x)  /* null definition */
-
-#endif /* __PERF_H__ */

+ 404 - 189
thirdparty/lwip/src/api/api_lib.c

@@ -1,14 +1,31 @@
 /**
  * @file
  * Sequential API External module
- *
+ * 
+ * @defgroup netconn Netconn API
+ * @ingroup sequential_api
+ * Thread-safe, to be called from non-TCPIP threads only.
+ * TX/RX handling based on @ref netbuf (containing @ref pbuf)
+ * to avoid copying data around.
+ * 
+ * @defgroup netconn_common Common functions
+ * @ingroup netconn
+ * For use with TCP and UDP
+ * 
+ * @defgroup netconn_tcp TCP only
+ * @ingroup netconn
+ * TCP only functions
+ * 
+ * @defgroup netconn_udp UDP only
+ * @ingroup netconn
+ * UDP only functions
  */
- 
+
 /*
  * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
- * All rights reserved. 
- * 
- * Redistribution and use in source and binary forms, with or without modification, 
+ * 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,
@@ -17,23 +34,22 @@
  *    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. 
+ *    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 
+ * 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>
  *
+ * Author: Adam Dunkels <adam@sics.se>
  */
 
 /* This is the part of the API that is linked with
@@ -44,16 +60,55 @@
 #if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */
 
 #include "lwip/api.h"
-#include "lwip/tcpip.h"
 #include "lwip/memp.h"
 
 #include "lwip/ip.h"
 #include "lwip/raw.h"
 #include "lwip/udp.h"
-#include "lwip/tcp.h"
+#include "lwip/priv/api_msg.h"
+#include "lwip/priv/tcp_priv.h"
+#include "lwip/priv/tcpip_priv.h"
 
 #include <string.h>
 
+#define API_MSG_VAR_REF(name)               API_VAR_REF(name)
+#define API_MSG_VAR_DECLARE(name)           API_VAR_DECLARE(struct api_msg, name)
+#define API_MSG_VAR_ALLOC(name)             API_VAR_ALLOC(struct api_msg, MEMP_API_MSG, name, ERR_MEM)
+#define API_MSG_VAR_ALLOC_RETURN_NULL(name) API_VAR_ALLOC(struct api_msg, MEMP_API_MSG, name, NULL)
+#define API_MSG_VAR_FREE(name)              API_VAR_FREE(MEMP_API_MSG, name)
+
+static err_t netconn_close_shutdown(struct netconn *conn, u8_t how);
+
+/**
+ * Call the lower part of a netconn_* function
+ * This function is then running in the thread context
+ * of tcpip_thread and has exclusive access to lwIP core code.
+ *
+ * @param fn function to call
+ * @param apimsg a struct containing the function to call and its parameters
+ * @return ERR_OK if the function was called, another err_t if not
+ */
+static err_t
+netconn_apimsg(tcpip_callback_fn fn, struct api_msg *apimsg)
+{
+  err_t err;
+
+#ifdef LWIP_DEBUG
+  /* catch functions that don't set err */
+  apimsg->err = ERR_VAL;
+#endif /* LWIP_DEBUG */
+
+#if LWIP_NETCONN_SEM_PER_THREAD
+  apimsg->op_completed_sem = LWIP_NETCONN_THREAD_SEM_GET();
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
+
+  err = tcpip_send_msg_wait_sem(fn, apimsg, LWIP_API_MSG_SEM(apimsg));
+  if (err == ERR_OK) {
+    return apimsg->err;
+  }
+  return err;
+}
+
 /**
  * Create a new netconn (of a specific type) that has a callback function.
  * The corresponding pcb is also created.
@@ -68,30 +123,38 @@ struct netconn*
 netconn_new_with_proto_and_callback(enum netconn_type t, u8_t proto, netconn_callback callback)
 {
   struct netconn *conn;
-  struct api_msg msg;
+  API_MSG_VAR_DECLARE(msg);
+  API_MSG_VAR_ALLOC_RETURN_NULL(msg);
 
   conn = netconn_alloc(t, callback);
   if (conn != NULL) {
-    msg.function = do_newconn;
-    msg.msg.msg.n.proto = proto;
-    msg.msg.conn = conn;
-    if (TCPIP_APIMSG(&msg) != ERR_OK) {
+    err_t err;
+
+    API_MSG_VAR_REF(msg).msg.n.proto = proto;
+    API_MSG_VAR_REF(msg).conn = conn;
+    err = netconn_apimsg(lwip_netconn_do_newconn, &API_MSG_VAR_REF(msg));
+    if (err != ERR_OK) {
       LWIP_ASSERT("freeing conn without freeing pcb", conn->pcb.tcp == NULL);
-      LWIP_ASSERT("conn has no op_completed", sys_sem_valid(&conn->op_completed));
       LWIP_ASSERT("conn has no recvmbox", sys_mbox_valid(&conn->recvmbox));
 #if LWIP_TCP
       LWIP_ASSERT("conn->acceptmbox shouldn't exist", !sys_mbox_valid(&conn->acceptmbox));
 #endif /* LWIP_TCP */
+#if !LWIP_NETCONN_SEM_PER_THREAD
+      LWIP_ASSERT("conn has no op_completed", sys_sem_valid(&conn->op_completed));
       sys_sem_free(&conn->op_completed);
+#endif /* !LWIP_NETCONN_SEM_PER_THREAD */
       sys_mbox_free(&conn->recvmbox);
       memp_free(MEMP_NETCONN, conn);
+      API_MSG_VAR_FREE(msg);
       return NULL;
     }
   }
+  API_MSG_VAR_FREE(msg);
   return conn;
 }
 
 /**
+ * @ingroup netconn_common
  * Close a netconn 'connection' and free its resources.
  * UDP and RAW connection are completely closed, TCP pcbs might still be in a waitstate
  * after this returns.
@@ -102,20 +165,34 @@ netconn_new_with_proto_and_callback(enum netconn_type t, u8_t proto, netconn_cal
 err_t
 netconn_delete(struct netconn *conn)
 {
-  struct api_msg msg;
+  err_t err;
+  API_MSG_VAR_DECLARE(msg);
 
   /* No ASSERT here because possible to get a (conn == NULL) if we got an accept error */
   if (conn == NULL) {
     return ERR_OK;
   }
 
-  msg.function = do_delconn;
-  msg.msg.conn = conn;
-  tcpip_apimsg(&msg);
+  API_MSG_VAR_ALLOC(msg);
+  API_MSG_VAR_REF(msg).conn = conn;
+#if LWIP_SO_SNDTIMEO || LWIP_SO_LINGER
+  /* get the time we started, which is later compared to
+     sys_now() + conn->send_timeout */
+  API_MSG_VAR_REF(msg).msg.sd.time_started = sys_now();
+#else /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
+#if LWIP_TCP
+  API_MSG_VAR_REF(msg).msg.sd.polls_left =
+    ((LWIP_TCP_CLOSE_TIMEOUT_MS_DEFAULT + TCP_SLOW_INTERVAL - 1) / TCP_SLOW_INTERVAL) + 1;
+#endif /* LWIP_TCP */
+#endif /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
+  err = netconn_apimsg(lwip_netconn_do_delconn, &API_MSG_VAR_REF(msg));
+  API_MSG_VAR_FREE(msg);
 
-  netconn_free(conn);
+  if (err != ERR_OK) {
+    return err;
+  }
 
-  /* don't care for return value of do_delconn since it only calls void functions */
+  netconn_free(conn);
 
   return ERR_OK;
 }
@@ -134,53 +211,66 @@ netconn_delete(struct netconn *conn)
 err_t
 netconn_getaddr(struct netconn *conn, ip_addr_t *addr, u16_t *port, u8_t local)
 {
-  struct api_msg msg;
+  API_MSG_VAR_DECLARE(msg);
   err_t err;
 
   LWIP_ERROR("netconn_getaddr: invalid conn", (conn != NULL), return ERR_ARG;);
   LWIP_ERROR("netconn_getaddr: invalid addr", (addr != NULL), return ERR_ARG;);
   LWIP_ERROR("netconn_getaddr: invalid port", (port != NULL), return ERR_ARG;);
 
-  msg.function = do_getaddr;
-  msg.msg.conn = conn;
-  msg.msg.msg.ad.ipaddr = addr;
-  msg.msg.msg.ad.port = port;
-  msg.msg.msg.ad.local = local;
-  err = TCPIP_APIMSG(&msg);
+  API_MSG_VAR_ALLOC(msg);
+  API_MSG_VAR_REF(msg).conn = conn;
+  API_MSG_VAR_REF(msg).msg.ad.local = local;
+#if LWIP_MPU_COMPATIBLE
+  err = netconn_apimsg(lwip_netconn_do_getaddr, &API_MSG_VAR_REF(msg));
+  *addr = msg->msg.ad.ipaddr;
+  *port = msg->msg.ad.port;
+#else /* LWIP_MPU_COMPATIBLE */
+  msg.msg.ad.ipaddr = addr;
+  msg.msg.ad.port = port;
+  err = netconn_apimsg(lwip_netconn_do_getaddr, &msg);
+#endif /* LWIP_MPU_COMPATIBLE */
+  API_MSG_VAR_FREE(msg);
 
-  NETCONN_SET_SAFE_ERR(conn, err);
   return err;
 }
 
 /**
+ * @ingroup netconn_common
  * Bind a netconn to a specific local IP address and port.
  * Binding one netconn twice might not always be checked correctly!
  *
  * @param conn the netconn to bind
- * @param addr the local IP address to bind the netconn to (use IP_ADDR_ANY
- *             to bind to all addresses)
+ * @param addr the local IP address to bind the netconn to 
+ *             (use IP4_ADDR_ANY/IP6_ADDR_ANY to bind to all addresses)
  * @param port the local port to bind the netconn to (not used for RAW)
  * @return ERR_OK if bound, any other err_t on failure
  */
 err_t
-netconn_bind(struct netconn *conn, ip_addr_t *addr, u16_t port)
+netconn_bind(struct netconn *conn, const ip_addr_t *addr, u16_t port)
 {
-  struct api_msg msg;
+  API_MSG_VAR_DECLARE(msg);
   err_t err;
-
+  
   LWIP_ERROR("netconn_bind: invalid conn", (conn != NULL), return ERR_ARG;);
 
-  msg.function = do_bind;
-  msg.msg.conn = conn;
-  msg.msg.msg.bc.ipaddr = addr;
-  msg.msg.msg.bc.port = port;
-  err = TCPIP_APIMSG(&msg);
+  /* Don't propagate NULL pointer (IP_ADDR_ANY alias) to subsequent functions */
+  if (addr == NULL) {
+    addr = IP4_ADDR_ANY;
+  }
+
+  API_MSG_VAR_ALLOC(msg);
+  API_MSG_VAR_REF(msg).conn = conn;
+  API_MSG_VAR_REF(msg).msg.bc.ipaddr = API_MSG_VAR_REF(addr);
+  API_MSG_VAR_REF(msg).msg.bc.port = port;
+  err = netconn_apimsg(lwip_netconn_do_bind, &API_MSG_VAR_REF(msg));
+  API_MSG_VAR_FREE(msg);
 
-  NETCONN_SET_SAFE_ERR(conn, err);
   return err;
 }
 
 /**
+ * @ingroup netconn_common
  * Connect a netconn to a specific remote IP address and port.
  *
  * @param conn the netconn to connect
@@ -189,47 +279,53 @@ netconn_bind(struct netconn *conn, ip_addr_t *addr, u16_t port)
  * @return ERR_OK if connected, return value of tcp_/udp_/raw_connect otherwise
  */
 err_t
-netconn_connect(struct netconn *conn, ip_addr_t *addr, u16_t port)
+netconn_connect(struct netconn *conn, const ip_addr_t *addr, u16_t port)
 {
-  struct api_msg msg;
+  API_MSG_VAR_DECLARE(msg);
   err_t err;
 
   LWIP_ERROR("netconn_connect: invalid conn", (conn != NULL), return ERR_ARG;);
 
-  msg.function = do_connect;
-  msg.msg.conn = conn;
-  msg.msg.msg.bc.ipaddr = addr;
-  msg.msg.msg.bc.port = port;
-  /* This is the only function which need to not block tcpip_thread */
-  err = tcpip_apimsg(&msg);
+  /* Don't propagate NULL pointer (IP_ADDR_ANY alias) to subsequent functions */
+  if (addr == NULL) {
+    addr = IP4_ADDR_ANY;
+  }
+
+  API_MSG_VAR_ALLOC(msg);
+  API_MSG_VAR_REF(msg).conn = conn;
+  API_MSG_VAR_REF(msg).msg.bc.ipaddr = API_MSG_VAR_REF(addr);
+  API_MSG_VAR_REF(msg).msg.bc.port = port;
+  err = netconn_apimsg(lwip_netconn_do_connect, &API_MSG_VAR_REF(msg));
+  API_MSG_VAR_FREE(msg);
 
-  NETCONN_SET_SAFE_ERR(conn, err);
   return err;
 }
 
 /**
+ * @ingroup netconn_udp
  * Disconnect a netconn from its current peer (only valid for UDP netconns).
  *
  * @param conn the netconn to disconnect
- * @return TODO: return value is not set here...
+ * @return See @ref err_t
  */
 err_t
 netconn_disconnect(struct netconn *conn)
 {
-  struct api_msg msg;
+  API_MSG_VAR_DECLARE(msg);
   err_t err;
 
   LWIP_ERROR("netconn_disconnect: invalid conn", (conn != NULL), return ERR_ARG;);
 
-  msg.function = do_disconnect;
-  msg.msg.conn = conn;
-  err = TCPIP_APIMSG(&msg);
+  API_MSG_VAR_ALLOC(msg);
+  API_MSG_VAR_REF(msg).conn = conn;
+  err = netconn_apimsg(lwip_netconn_do_disconnect, &API_MSG_VAR_REF(msg));
+  API_MSG_VAR_FREE(msg);
 
-  NETCONN_SET_SAFE_ERR(conn, err);
   return err;
 }
 
 /**
+ * @ingroup netconn_tcp
  * Set a TCP netconn into listen mode
  *
  * @param conn the tcp netconn to set to listen mode
@@ -241,7 +337,7 @@ err_t
 netconn_listen_with_backlog(struct netconn *conn, u8_t backlog)
 {
 #if LWIP_TCP
-  struct api_msg msg;
+  API_MSG_VAR_DECLARE(msg);
   err_t err;
 
   /* This does no harm. If TCP_LISTEN_BACKLOG is off, backlog is unused. */
@@ -249,14 +345,14 @@ netconn_listen_with_backlog(struct netconn *conn, u8_t backlog)
 
   LWIP_ERROR("netconn_listen: invalid conn", (conn != NULL), return ERR_ARG;);
 
-  msg.function = do_listen;
-  msg.msg.conn = conn;
+  API_MSG_VAR_ALLOC(msg);
+  API_MSG_VAR_REF(msg).conn = conn;
 #if TCP_LISTEN_BACKLOG
-  msg.msg.msg.lb.backlog = backlog;
+  API_MSG_VAR_REF(msg).msg.lb.backlog = backlog;
 #endif /* TCP_LISTEN_BACKLOG */
-  err = TCPIP_APIMSG(&msg);
+  err = netconn_apimsg(lwip_netconn_do_listen, &API_MSG_VAR_REF(msg));
+  API_MSG_VAR_FREE(msg);
 
-  NETCONN_SET_SAFE_ERR(conn, err);
   return err;
 #else /* LWIP_TCP */
   LWIP_UNUSED_ARG(conn);
@@ -266,6 +362,7 @@ netconn_listen_with_backlog(struct netconn *conn, u8_t backlog)
 }
 
 /**
+ * @ingroup netconn_tcp
  * Accept a new connection on a TCP listening netconn.
  *
  * @param conn the TCP listen netconn
@@ -277,16 +374,16 @@ err_t
 netconn_accept(struct netconn *conn, struct netconn **new_conn)
 {
 #if LWIP_TCP
+  void *accept_ptr;
   struct netconn *newconn;
   err_t err;
 #if TCP_LISTEN_BACKLOG
-  struct api_msg msg;
+  API_MSG_VAR_DECLARE(msg);
 #endif /* TCP_LISTEN_BACKLOG */
 
   LWIP_ERROR("netconn_accept: invalid pointer",    (new_conn != NULL),                  return ERR_ARG;);
   *new_conn = NULL;
   LWIP_ERROR("netconn_accept: invalid conn",       (conn != NULL),                      return ERR_ARG;);
-  LWIP_ERROR("netconn_accept: invalid acceptmbox", sys_mbox_valid(&conn->acceptmbox),   return ERR_ARG;);
 
   err = conn->last_err;
   if (ERR_IS_FATAL(err)) {
@@ -294,29 +391,53 @@ netconn_accept(struct netconn *conn, struct netconn **new_conn)
        waiting on acceptmbox forever! */
     return err;
   }
+  if (!sys_mbox_valid(&conn->acceptmbox)) {
+    return ERR_CLSD;
+  }
+
+#if TCP_LISTEN_BACKLOG
+  API_MSG_VAR_ALLOC(msg);
+#endif /* TCP_LISTEN_BACKLOG */
 
 #if LWIP_SO_RCVTIMEO
-  if (sys_arch_mbox_fetch(&conn->acceptmbox, (void **)&newconn, conn->recv_timeout) == SYS_ARCH_TIMEOUT) {
-    NETCONN_SET_SAFE_ERR(conn, ERR_TIMEOUT);
+  if (sys_arch_mbox_fetch(&conn->acceptmbox, &accept_ptr, conn->recv_timeout) == SYS_ARCH_TIMEOUT) {
+#if TCP_LISTEN_BACKLOG
+    API_MSG_VAR_FREE(msg);
+#endif /* TCP_LISTEN_BACKLOG */
     return ERR_TIMEOUT;
   }
 #else
-  sys_arch_mbox_fetch(&conn->acceptmbox, (void **)&newconn, 0);
+  sys_arch_mbox_fetch(&conn->acceptmbox, &accept_ptr, 0);
 #endif /* LWIP_SO_RCVTIMEO*/
+  newconn = (struct netconn *)accept_ptr;
   /* Register event with callback */
   API_EVENT(conn, NETCONN_EVT_RCVMINUS, 0);
 
+  if (accept_ptr == &netconn_aborted) {
+    /* a connection has been aborted: out of pcbs or out of netconns during accept */
+    /* @todo: set netconn error, but this would be fatal and thus block further accepts */
+#if TCP_LISTEN_BACKLOG
+    API_MSG_VAR_FREE(msg);
+#endif /* TCP_LISTEN_BACKLOG */
+    return ERR_ABRT;
+  }
   if (newconn == NULL) {
     /* connection has been aborted */
-    NETCONN_SET_SAFE_ERR(conn, ERR_ABRT);
-    return ERR_ABRT;
+    /* in this special case, we set the netconn error from application thread, as
+       on a ready-to-accept listening netconn, there should not be anything running
+       in tcpip_thread */
+    NETCONN_SET_SAFE_ERR(conn, ERR_CLSD);
+#if TCP_LISTEN_BACKLOG
+    API_MSG_VAR_FREE(msg);
+#endif /* TCP_LISTEN_BACKLOG */
+    return ERR_CLSD;
   }
 #if TCP_LISTEN_BACKLOG
   /* Let the stack know that we have accepted the connection. */
-  msg.function = do_recv;
-  msg.msg.conn = conn;
-  /* don't care for the return value of do_recv */
-  TCPIP_APIMSG(&msg);
+  API_MSG_VAR_REF(msg).conn = newconn;
+  /* don't care for the return value of lwip_netconn_do_recv */
+  netconn_apimsg(lwip_netconn_do_accepted, &API_MSG_VAR_REF(msg));
+  API_MSG_VAR_FREE(msg);
 #endif /* TCP_LISTEN_BACKLOG */
 
   *new_conn = newconn;
@@ -330,6 +451,7 @@ netconn_accept(struct netconn *conn, struct netconn **new_conn)
 }
 
 /**
+ * @ingroup netconn_common
  * Receive data: actual implementation that doesn't care whether pbuf or netbuf
  * is received
  *
@@ -345,13 +467,27 @@ netconn_recv_data(struct netconn *conn, void **new_buf)
   u16_t len;
   err_t err;
 #if LWIP_TCP
-  struct api_msg msg;
+  API_MSG_VAR_DECLARE(msg);
+#if LWIP_MPU_COMPATIBLE
+  msg = NULL;
+#endif
 #endif /* LWIP_TCP */
 
   LWIP_ERROR("netconn_recv: invalid pointer", (new_buf != NULL), return ERR_ARG;);
   *new_buf = NULL;
   LWIP_ERROR("netconn_recv: invalid conn",    (conn != NULL),    return ERR_ARG;);
-  LWIP_ERROR("netconn_accept: invalid recvmbox", sys_mbox_valid(&conn->recvmbox), return ERR_CONN;);
+#if LWIP_TCP
+#if (LWIP_UDP || LWIP_RAW)
+  if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP)
+#endif /* (LWIP_UDP || LWIP_RAW) */
+  {
+    if (!sys_mbox_valid(&conn->recvmbox)) {
+      /* This happens when calling this function after receiving FIN */
+      return sys_mbox_valid(&conn->acceptmbox) ? ERR_CONN : ERR_CLSD;
+    }
+  }
+#endif /* LWIP_TCP */
+  LWIP_ERROR("netconn_recv: invalid recvmbox", sys_mbox_valid(&conn->recvmbox), return ERR_CONN;);
 
   err = conn->last_err;
   if (ERR_IS_FATAL(err)) {
@@ -361,10 +497,25 @@ netconn_recv_data(struct netconn *conn, void **new_buf)
        before the fatal error occurred - is that a problem? */
     return err;
   }
+#if LWIP_TCP
+#if (LWIP_UDP || LWIP_RAW)
+  if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP)
+#endif /* (LWIP_UDP || LWIP_RAW) */
+  {
+    API_MSG_VAR_ALLOC(msg);
+  }
+#endif /* LWIP_TCP */
 
 #if LWIP_SO_RCVTIMEO
   if (sys_arch_mbox_fetch(&conn->recvmbox, &buf, conn->recv_timeout) == SYS_ARCH_TIMEOUT) {
-    NETCONN_SET_SAFE_ERR(conn, ERR_TIMEOUT);
+#if LWIP_TCP
+#if (LWIP_UDP || LWIP_RAW)
+    if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP)
+#endif /* (LWIP_UDP || LWIP_RAW) */
+    {
+      API_MSG_VAR_FREE(msg);
+    }
+#endif /* LWIP_TCP */
     return ERR_TIMEOUT;
   }
 #else
@@ -373,29 +524,33 @@ netconn_recv_data(struct netconn *conn, void **new_buf)
 
 #if LWIP_TCP
 #if (LWIP_UDP || LWIP_RAW)
-  if (conn->type == NETCONN_TCP)
+  if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP)
 #endif /* (LWIP_UDP || LWIP_RAW) */
   {
-    if (!netconn_get_noautorecved(conn) || (buf == NULL)) {
-      /* Let the stack know that we have taken the data. */
-      /* TODO: Speedup: Don't block and wait for the answer here
-         (to prevent multiple thread-switches). */
-      msg.function = do_recv;
-      msg.msg.conn = conn;
-      if (buf != NULL) {
-        msg.msg.msg.r.len = ((struct pbuf *)buf)->tot_len;
-      } else {
-        msg.msg.msg.r.len = 1;
-      }
-      /* don't care for the return value of do_recv */
-      TCPIP_APIMSG(&msg);
+    /* Let the stack know that we have taken the data. */
+    /* @todo: Speedup: Don't block and wait for the answer here
+       (to prevent multiple thread-switches). */
+    API_MSG_VAR_REF(msg).conn = conn;
+    if (buf != NULL) {
+      API_MSG_VAR_REF(msg).msg.r.len = ((struct pbuf *)buf)->tot_len;
+    } else {
+      API_MSG_VAR_REF(msg).msg.r.len = 1;
     }
 
+    /* don't care for the return value of lwip_netconn_do_recv */
+    netconn_apimsg(lwip_netconn_do_recv, &API_MSG_VAR_REF(msg));
+    API_MSG_VAR_FREE(msg);
+
     /* If we are closed, we indicate that we no longer wish to use the socket */
     if (buf == NULL) {
       API_EVENT(conn, NETCONN_EVT_RCVMINUS, 0);
-      /* Avoid to lose any previous error code */
-      NETCONN_SET_SAFE_ERR(conn, ERR_CLSD);
+      if (conn->pcb.ip == NULL) {
+        /* race condition: RST during recv */
+        return conn->last_err == ERR_OK ? ERR_RST : conn->last_err;
+      }
+      /* RX side is closed, so deallocate the recvmbox */
+      netconn_close_shutdown(conn, NETCONN_SHUT_RD);
+      /* Don' store ERR_CLSD as conn->err since we are only half-closed */
       return ERR_CLSD;
     }
     len = ((struct pbuf *)buf)->tot_len;
@@ -425,6 +580,7 @@ netconn_recv_data(struct netconn *conn, void **new_buf)
 }
 
 /**
+ * @ingroup netconn_tcp
  * Receive data (in form of a pbuf) from a TCP netconn
  *
  * @param conn the netconn from which to receive data
@@ -437,12 +593,13 @@ err_t
 netconn_recv_tcp_pbuf(struct netconn *conn, struct pbuf **new_buf)
 {
   LWIP_ERROR("netconn_recv: invalid conn", (conn != NULL) &&
-             netconn_type(conn) == NETCONN_TCP, return ERR_ARG;);
+             NETCONNTYPE_GROUP(netconn_type(conn)) == NETCONN_TCP, return ERR_ARG;);
 
   return netconn_recv_data(conn, (void **)new_buf);
 }
 
 /**
+ * @ingroup netconn_common
  * Receive data (in form of a netbuf containing a packet buffer) from a netconn
  *
  * @param conn the netconn from which to receive data
@@ -461,11 +618,10 @@ netconn_recv(struct netconn *conn, struct netbuf **new_buf)
   LWIP_ERROR("netconn_recv: invalid pointer", (new_buf != NULL), return ERR_ARG;);
   *new_buf = NULL;
   LWIP_ERROR("netconn_recv: invalid conn",    (conn != NULL),    return ERR_ARG;);
-  LWIP_ERROR("netconn_accept: invalid recvmbox", sys_mbox_valid(&conn->recvmbox), return ERR_CONN;);
 
 #if LWIP_TCP
 #if (LWIP_UDP || LWIP_RAW)
-  if (conn->type == NETCONN_TCP)
+  if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP)
 #endif /* (LWIP_UDP || LWIP_RAW) */
   {
     struct pbuf *p = NULL;
@@ -473,7 +629,6 @@ netconn_recv(struct netconn *conn, struct netbuf **new_buf)
 
     buf = (struct netbuf *)memp_malloc(MEMP_NETBUF);
     if (buf == NULL) {
-      NETCONN_SET_SAFE_ERR(conn, ERR_MEM);
       return ERR_MEM;
     }
 
@@ -487,7 +642,7 @@ netconn_recv(struct netconn *conn, struct netbuf **new_buf)
     buf->p = p;
     buf->ptr = p;
     buf->port = 0;
-    ip_addr_set_any(&buf->addr);
+    ip_addr_set_zero(&buf->addr);
     *new_buf = buf;
     /* don't set conn->last_err: it's only ERR_OK, anyway */
     return ERR_OK;
@@ -504,38 +659,7 @@ netconn_recv(struct netconn *conn, struct netbuf **new_buf)
 }
 
 /**
- * TCP: update the receive window: by calling this, the application
- * tells the stack that it has processed data and is able to accept
- * new data.
- * ATTENTION: use with care, this is mainly used for sockets!
- * Can only be used when calling netconn_set_noautorecved(conn, 1) before.
- *
- * @param conn the netconn for which to update the receive window
- * @param length amount of data processed (ATTENTION: this must be accurate!)
- */
-void
-netconn_recved(struct netconn *conn, u32_t length)
-{
-#if LWIP_TCP
-  if ((conn != NULL) && (conn->type == NETCONN_TCP) &&
-      (netconn_get_noautorecved(conn))) {
-    struct api_msg msg;
-    /* Let the stack know that we have taken the data. */
-    /* TODO: Speedup: Don't block and wait for the answer here
-       (to prevent multiple thread-switches). */
-    msg.function = do_recv;
-    msg.msg.conn = conn;
-    msg.msg.msg.r.len = length;
-    /* don't care for the return value of do_recv */
-    TCPIP_APIMSG(&msg);
-  }
-#else /* LWIP_TCP */
-  LWIP_UNUSED_ARG(conn);
-  LWIP_UNUSED_ARG(length);
-#endif /* LWIP_TCP */
-}
-
-/**
+ * @ingroup netconn_udp
  * Send data (in form of a netbuf) to a specific remote IP address and port.
  * Only to be used for UDP and RAW netconns (not TCP).
  *
@@ -546,7 +670,7 @@ netconn_recved(struct netconn *conn, u32_t length)
  * @return ERR_OK if data was sent, any other err_t on error
  */
 err_t
-netconn_sendto(struct netconn *conn, struct netbuf *buf, ip_addr_t *addr, u16_t port)
+netconn_sendto(struct netconn *conn, struct netbuf *buf, const ip_addr_t *addr, u16_t port)
 {
   if (buf != NULL) {
     ip_addr_set(&buf->addr, addr);
@@ -557,6 +681,7 @@ netconn_sendto(struct netconn *conn, struct netbuf *buf, ip_addr_t *addr, u16_t
 }
 
 /**
+ * @ingroup netconn_udp
  * Send data over a UDP or RAW netconn (that is already connected).
  *
  * @param conn the UDP or RAW netconn over which to send data
@@ -566,22 +691,23 @@ netconn_sendto(struct netconn *conn, struct netbuf *buf, ip_addr_t *addr, u16_t
 err_t
 netconn_send(struct netconn *conn, struct netbuf *buf)
 {
-  struct api_msg msg;
+  API_MSG_VAR_DECLARE(msg);
   err_t err;
 
   LWIP_ERROR("netconn_send: invalid conn",  (conn != NULL), return ERR_ARG;);
 
   LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_send: sending %"U16_F" bytes\n", buf->p->tot_len));
-  msg.function = do_send;
-  msg.msg.conn = conn;
-  msg.msg.msg.b = buf;
-  err = TCPIP_APIMSG(&msg);
+  API_MSG_VAR_ALLOC(msg);
+  API_MSG_VAR_REF(msg).conn = conn;
+  API_MSG_VAR_REF(msg).msg.b = buf;
+  err = netconn_apimsg(lwip_netconn_do_send, &API_MSG_VAR_REF(msg));
+  API_MSG_VAR_FREE(msg);
 
-  NETCONN_SET_SAFE_ERR(conn, err);
   return err;
 }
 
 /**
+ * @ingroup netconn_tcp
  * Send data over a TCP netconn.
  *
  * @param conn the TCP netconn over which to send data
@@ -590,7 +716,7 @@ netconn_send(struct netconn *conn, struct netbuf *buf)
  * @param apiflags combination of following flags :
  * - NETCONN_COPY: data will be copied into memory belonging to the stack
  * - NETCONN_MORE: for TCP connection, PSH flag will be set on last segment sent
- * - NETCONN_DONTBLOCK: only write the data if all dat can be written at once
+ * - NETCONN_DONTBLOCK: only write the data if all data can be written at once
  * @param bytes_written pointer to a location that receives the number of written bytes
  * @return ERR_OK if data was sent, any other err_t on error
  */
@@ -598,12 +724,12 @@ err_t
 netconn_write_partly(struct netconn *conn, const void *dataptr, size_t size,
                      u8_t apiflags, size_t *bytes_written)
 {
-  struct api_msg msg;
+  API_MSG_VAR_DECLARE(msg);
   err_t err;
   u8_t dontblock;
 
   LWIP_ERROR("netconn_write: invalid conn",  (conn != NULL), return ERR_ARG;);
-  LWIP_ERROR("netconn_write: invalid conn->type",  (conn->type == NETCONN_TCP), return ERR_VAL;);
+  LWIP_ERROR("netconn_write: invalid conn->type",  (NETCONNTYPE_GROUP(conn->type)== NETCONN_TCP), return ERR_VAL;);
   if (size == 0) {
     return ERR_OK;
   }
@@ -614,26 +740,26 @@ netconn_write_partly(struct netconn *conn, const void *dataptr, size_t size,
     return ERR_VAL;
   }
 
+  API_MSG_VAR_ALLOC(msg);
   /* non-blocking write sends as much  */
-  msg.function = do_write;
-  msg.msg.conn = conn;
-  msg.msg.msg.w.dataptr = dataptr;
-  msg.msg.msg.w.apiflags = apiflags;
-  msg.msg.msg.w.len = size;
+  API_MSG_VAR_REF(msg).conn = conn;
+  API_MSG_VAR_REF(msg).msg.w.dataptr = dataptr;
+  API_MSG_VAR_REF(msg).msg.w.apiflags = apiflags;
+  API_MSG_VAR_REF(msg).msg.w.len = size;
 #if LWIP_SO_SNDTIMEO
   if (conn->send_timeout != 0) {
     /* get the time we started, which is later compared to
         sys_now() + conn->send_timeout */
-    msg.msg.msg.w.time_started = sys_now();
+    API_MSG_VAR_REF(msg).msg.w.time_started = sys_now();
   } else {
-    msg.msg.msg.w.time_started = 0;
+    API_MSG_VAR_REF(msg).msg.w.time_started = 0;
   }
 #endif /* LWIP_SO_SNDTIMEO */
 
   /* For locking the core: this _can_ be delayed on low memory/low send buffer,
      but if it is, this is done inside api_msg.c:do_write(), so we can use the
      non-blocking version here. */
-  err = TCPIP_APIMSG(&msg);
+  err = netconn_apimsg(lwip_netconn_do_write, &API_MSG_VAR_REF(msg));
   if ((err == ERR_OK) && (bytes_written != NULL)) {
     if (dontblock
 #if LWIP_SO_SNDTIMEO
@@ -641,19 +767,20 @@ netconn_write_partly(struct netconn *conn, const void *dataptr, size_t size,
 #endif /* LWIP_SO_SNDTIMEO */
        ) {
       /* nonblocking write: maybe the data has been sent partly */
-      *bytes_written = msg.msg.msg.w.len;
+      *bytes_written = API_MSG_VAR_REF(msg).msg.w.len;
     } else {
       /* blocking call succeeded: all data has been sent if it */
       *bytes_written = size;
     }
   }
+  API_MSG_VAR_FREE(msg);
 
-  NETCONN_SET_SAFE_ERR(conn, err);
   return err;
 }
 
 /**
- * Close ot shutdown a TCP netconn (doesn't delete it).
+ * @ingroup netconn_tcp
+ * Close or shutdown a TCP netconn (doesn't delete it).
  *
  * @param conn the TCP netconn to close or shutdown
  * @param how fully close or only shutdown one side?
@@ -662,24 +789,34 @@ netconn_write_partly(struct netconn *conn, const void *dataptr, size_t size,
 static err_t
 netconn_close_shutdown(struct netconn *conn, u8_t how)
 {
-  struct api_msg msg;
+  API_MSG_VAR_DECLARE(msg);
   err_t err;
+  LWIP_UNUSED_ARG(how);
 
   LWIP_ERROR("netconn_close: invalid conn",  (conn != NULL), return ERR_ARG;);
 
-  msg.function = do_close;
-  msg.msg.conn = conn;
+  API_MSG_VAR_ALLOC(msg);
+  API_MSG_VAR_REF(msg).conn = conn;
+#if LWIP_TCP
   /* shutting down both ends is the same as closing */
-  msg.msg.msg.sd.shut = how;
-  /* because of the LWIP_TCPIP_CORE_LOCKING implementation of do_close,
-     don't use TCPIP_APIMSG here */
-  err = tcpip_apimsg(&msg);
+  API_MSG_VAR_REF(msg).msg.sd.shut = how;
+#if LWIP_SO_SNDTIMEO || LWIP_SO_LINGER
+  /* get the time we started, which is later compared to
+     sys_now() + conn->send_timeout */
+  API_MSG_VAR_REF(msg).msg.sd.time_started = sys_now();
+#else /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
+  API_MSG_VAR_REF(msg).msg.sd.polls_left =
+    ((LWIP_TCP_CLOSE_TIMEOUT_MS_DEFAULT + TCP_SLOW_INTERVAL - 1) / TCP_SLOW_INTERVAL) + 1;
+#endif /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
+#endif /* LWIP_TCP */
+  err = netconn_apimsg(lwip_netconn_do_close, &API_MSG_VAR_REF(msg));
+  API_MSG_VAR_FREE(msg);
 
-  NETCONN_SET_SAFE_ERR(conn, err);
   return err;
 }
 
 /**
+ * @ingroup netconn_tcp
  * Close a TCP netconn (doesn't delete it).
  *
  * @param conn the TCP netconn to close
@@ -693,9 +830,12 @@ netconn_close(struct netconn *conn)
 }
 
 /**
+ * @ingroup netconn_tcp
  * Shut down one or both sides of a TCP netconn (doesn't delete it).
  *
  * @param conn the TCP netconn to shut down
+ * @param shut_rx shut down the RX side (no more read possible after this)
+ * @param shut_tx shut down the TX side (no more write possible after this)
  * @return ERR_OK if the netconn was closed, any other err_t on error
  */
 err_t
@@ -704,8 +844,9 @@ netconn_shutdown(struct netconn *conn, u8_t shut_rx, u8_t shut_tx)
   return netconn_close_shutdown(conn, (shut_rx ? NETCONN_SHUT_RD : 0) | (shut_tx ? NETCONN_SHUT_WR : 0));
 }
 
-#if LWIP_IGMP
+#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD)
 /**
+ * @ingroup netconn_udp
  * Join multicast groups for UDP netconns.
  *
  * @param conn the UDP netconn for which to change multicast addresses
@@ -717,64 +858,138 @@ netconn_shutdown(struct netconn *conn, u8_t shut_rx, u8_t shut_tx)
  */
 err_t
 netconn_join_leave_group(struct netconn *conn,
-                         ip_addr_t *multiaddr,
-                         ip_addr_t *netif_addr,
+                         const ip_addr_t *multiaddr,
+                         const ip_addr_t *netif_addr,
                          enum netconn_igmp join_or_leave)
 {
-  struct api_msg msg;
+  API_MSG_VAR_DECLARE(msg);
   err_t err;
 
   LWIP_ERROR("netconn_join_leave_group: invalid conn",  (conn != NULL), return ERR_ARG;);
 
-  msg.function = do_join_leave_group;
-  msg.msg.conn = conn;
-  msg.msg.msg.jl.multiaddr = multiaddr;
-  msg.msg.msg.jl.netif_addr = netif_addr;
-  msg.msg.msg.jl.join_or_leave = join_or_leave;
-  err = TCPIP_APIMSG(&msg);
+  API_MSG_VAR_ALLOC(msg);
+
+  /* Don't propagate NULL pointer (IP_ADDR_ANY alias) to subsequent functions */
+  if (multiaddr == NULL) {
+    multiaddr = IP4_ADDR_ANY;
+  }
+  if (netif_addr == NULL) {
+    netif_addr = IP4_ADDR_ANY;
+  }
+
+  API_MSG_VAR_REF(msg).conn = conn;
+  API_MSG_VAR_REF(msg).msg.jl.multiaddr = API_MSG_VAR_REF(multiaddr);
+  API_MSG_VAR_REF(msg).msg.jl.netif_addr = API_MSG_VAR_REF(netif_addr);
+  API_MSG_VAR_REF(msg).msg.jl.join_or_leave = join_or_leave;
+  err = netconn_apimsg(lwip_netconn_do_join_leave_group, &API_MSG_VAR_REF(msg));
+  API_MSG_VAR_FREE(msg);
 
-  NETCONN_SET_SAFE_ERR(conn, err);
   return err;
 }
-#endif /* LWIP_IGMP */
+#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */
 
 #if LWIP_DNS
 /**
+ * @ingroup netconn_common
  * Execute a DNS query, only one IP address is returned
  *
  * @param name a string representation of the DNS host name to query
  * @param addr a preallocated ip_addr_t where to store the resolved IP address
+ * @param dns_addrtype IP address type (IPv4 / IPv6)
  * @return ERR_OK: resolving succeeded
  *         ERR_MEM: memory error, try again later
  *         ERR_ARG: dns client not initialized or invalid hostname
  *         ERR_VAL: dns server response was invalid
  */
+#if LWIP_IPV4 && LWIP_IPV6
+err_t
+netconn_gethostbyname_addrtype(const char *name, ip_addr_t *addr, u8_t dns_addrtype)
+#else
 err_t
 netconn_gethostbyname(const char *name, ip_addr_t *addr)
+#endif
 {
-  struct dns_api_msg msg;
-  err_t err;
+  API_VAR_DECLARE(struct dns_api_msg, msg);
+#if !LWIP_MPU_COMPATIBLE
   sys_sem_t sem;
+#endif /* LWIP_MPU_COMPATIBLE */
+  err_t err;
 
   LWIP_ERROR("netconn_gethostbyname: invalid name", (name != NULL), return ERR_ARG;);
   LWIP_ERROR("netconn_gethostbyname: invalid addr", (addr != NULL), return ERR_ARG;);
+#if LWIP_MPU_COMPATIBLE
+  if (strlen(name) >= DNS_MAX_NAME_LENGTH) {
+    return ERR_ARG;
+  }
+#endif
 
-  err = sys_sem_new(&sem, 0);
+  API_VAR_ALLOC(struct dns_api_msg, MEMP_DNS_API_MSG, msg, ERR_MEM);
+#if LWIP_MPU_COMPATIBLE
+  strncpy(API_VAR_REF(msg).name, name, DNS_MAX_NAME_LENGTH-1);
+  API_VAR_REF(msg).name[DNS_MAX_NAME_LENGTH-1] = 0;
+#else /* LWIP_MPU_COMPATIBLE */
+  msg.err = &err;
+  msg.sem = &sem;
+  API_VAR_REF(msg).addr = API_VAR_REF(addr);
+  API_VAR_REF(msg).name = name;
+#endif /* LWIP_MPU_COMPATIBLE */
+#if LWIP_IPV4 && LWIP_IPV6
+  API_VAR_REF(msg).dns_addrtype = dns_addrtype;
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+#if LWIP_NETCONN_SEM_PER_THREAD
+  API_VAR_REF(msg).sem = LWIP_NETCONN_THREAD_SEM_GET();
+#else /* LWIP_NETCONN_SEM_PER_THREAD*/
+  err = sys_sem_new(API_EXPR_REF(API_VAR_REF(msg).sem), 0);
   if (err != ERR_OK) {
+    API_VAR_FREE(MEMP_DNS_API_MSG, msg);
     return err;
   }
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
 
-  msg.name = name;
-  msg.addr = addr;
-  msg.err = &err;
-  msg.sem = &sem;
+  err = tcpip_callback(lwip_netconn_do_gethostbyname, &API_VAR_REF(msg));
+  if (err != ERR_OK) {
+#if !LWIP_NETCONN_SEM_PER_THREAD
+    sys_sem_free(API_EXPR_REF(API_VAR_REF(msg).sem));
+#endif /* !LWIP_NETCONN_SEM_PER_THREAD */
+    API_VAR_FREE(MEMP_DNS_API_MSG, msg);
+    return err;
+  }
+  sys_sem_wait(API_EXPR_REF_SEM(API_VAR_REF(msg).sem));
+#if !LWIP_NETCONN_SEM_PER_THREAD
+  sys_sem_free(API_EXPR_REF(API_VAR_REF(msg).sem));
+#endif /* !LWIP_NETCONN_SEM_PER_THREAD */
 
-  tcpip_callback(do_gethostbyname, &msg);
-  sys_sem_wait(&sem);
-  sys_sem_free(&sem);
+#if LWIP_MPU_COMPATIBLE
+  *addr = msg->addr;
+  err = msg->err;
+#endif /* LWIP_MPU_COMPATIBLE */
 
+  API_VAR_FREE(MEMP_DNS_API_MSG, msg);
   return err;
 }
 #endif /* LWIP_DNS*/
 
+#if LWIP_NETCONN_SEM_PER_THREAD
+void
+netconn_thread_init(void)
+{
+  sys_sem_t *sem = LWIP_NETCONN_THREAD_SEM_GET();
+  if ((sem == NULL) || !sys_sem_valid(sem)) {
+    /* call alloc only once */
+    LWIP_NETCONN_THREAD_SEM_ALLOC();
+    LWIP_ASSERT("LWIP_NETCONN_THREAD_SEM_ALLOC() failed", sys_sem_valid(LWIP_NETCONN_THREAD_SEM_GET()));
+  }
+}
+
+void
+netconn_thread_cleanup(void)
+{
+  sys_sem_t *sem = LWIP_NETCONN_THREAD_SEM_GET();
+  if ((sem != NULL) && sys_sem_valid(sem)) {
+    /* call free only once */
+    LWIP_NETCONN_THREAD_SEM_FREE();
+  }
+}
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
+
 #endif /* LWIP_NETCONN */

+ 1947 - 1565
thirdparty/lwip/src/api/api_msg.c

@@ -1,1565 +1,1947 @@
-/**
- * @file
- * Sequential API Internal module
- *
- */
-
-/*
- * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
- * All rights reserved. 
- * 
- * Redistribution and use in source and binary forms, with or without modification, 
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission. 
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
- * OF SUCH DAMAGE.
- *
- * This file is part of the lwIP TCP/IP stack.
- * 
- * Author: Adam Dunkels <adam@sics.se>
- *
- */
-
-#include "lwip/opt.h"
-
-#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */
-
-#include "lwip/api_msg.h"
-
-#include "lwip/ip.h"
-#include "lwip/udp.h"
-#include "lwip/tcp.h"
-#include "lwip/raw.h"
-
-#include "lwip/memp.h"
-#include "lwip/tcpip.h"
-#include "lwip/igmp.h"
-#include "lwip/dns.h"
-
-#include <string.h>
-
-#define SET_NONBLOCKING_CONNECT(conn, val)  do { if(val) { \
-  (conn)->flags |= NETCONN_FLAG_IN_NONBLOCKING_CONNECT; \
-} else { \
-  (conn)->flags &= ~ NETCONN_FLAG_IN_NONBLOCKING_CONNECT; }} while(0)
-#define IN_NONBLOCKING_CONNECT(conn) (((conn)->flags & NETCONN_FLAG_IN_NONBLOCKING_CONNECT) != 0)
-
-/* forward declarations */
-#if LWIP_TCP
-static err_t do_writemore(struct netconn *conn);
-static void do_close_internal(struct netconn *conn);
-#endif
-
-#if LWIP_RAW
-/**
- * Receive callback function for RAW netconns.
- * Doesn't 'eat' the packet, only references it and sends it to
- * conn->recvmbox
- *
- * @see raw.h (struct raw_pcb.recv) for parameters and return value
- */
-static u8_t
-recv_raw(void *arg, struct raw_pcb *pcb, struct pbuf *p,
-    ip_addr_t *addr)
-{
-  struct pbuf *q;
-  struct netbuf *buf;
-  struct netconn *conn;
-
-  LWIP_UNUSED_ARG(addr);
-  conn = (struct netconn *)arg;
-
-  if ((conn != NULL) && sys_mbox_valid(&conn->recvmbox)) {
-#if LWIP_SO_RCVBUF
-    int recv_avail;
-    SYS_ARCH_GET(conn->recv_avail, recv_avail);
-    if ((recv_avail + (int)(p->tot_len)) > conn->recv_bufsize) {
-      return 0;
-    }
-#endif /* LWIP_SO_RCVBUF */
-    /* copy the whole packet into new pbufs */
-    q = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM);
-    if(q != NULL) {
-      if (pbuf_copy(q, p) != ERR_OK) {
-        pbuf_free(q);
-        q = NULL;
-      }
-    }
-
-    if (q != NULL) {
-      u16_t len;
-      buf = (struct netbuf *)memp_malloc(MEMP_NETBUF);
-      if (buf == NULL) {
-        pbuf_free(q);
-        return 0;
-      }
-
-      buf->p = q;
-      buf->ptr = q;
-      ip_addr_copy(buf->addr, *ip_current_src_addr());
-      buf->port = pcb->protocol;
-
-      len = q->tot_len;
-      if (sys_mbox_trypost(&conn->recvmbox, buf) != ERR_OK) {
-        netbuf_delete(buf);
-        return 0;
-      } else {
-#if LWIP_SO_RCVBUF
-        SYS_ARCH_INC(conn->recv_avail, len);
-#endif /* LWIP_SO_RCVBUF */
-        /* Register event with callback */
-        API_EVENT(conn, NETCONN_EVT_RCVPLUS, len);
-      }
-    }
-  }
-
-  return 0; /* do not eat the packet */
-}
-#endif /* LWIP_RAW*/
-
-#if LWIP_UDP
-/**
- * Receive callback function for UDP netconns.
- * Posts the packet to conn->recvmbox or deletes it on memory error.
- *
- * @see udp.h (struct udp_pcb.recv) for parameters
- */
-static void
-recv_udp(void *arg, struct udp_pcb *pcb, struct pbuf *p,
-   ip_addr_t *addr, u16_t port)
-{
-  struct netbuf *buf;
-  struct netconn *conn;
-  u16_t len;
-#if LWIP_SO_RCVBUF
-  int recv_avail;
-#endif /* LWIP_SO_RCVBUF */
-
-  LWIP_UNUSED_ARG(pcb); /* only used for asserts... */
-  LWIP_ASSERT("recv_udp must have a pcb argument", pcb != NULL);
-  LWIP_ASSERT("recv_udp must have an argument", arg != NULL);
-  conn = (struct netconn *)arg;
-  LWIP_ASSERT("recv_udp: recv for wrong pcb!", conn->pcb.udp == pcb);
-
-#if LWIP_SO_RCVBUF
-  SYS_ARCH_GET(conn->recv_avail, recv_avail);
-  if ((conn == NULL) || !sys_mbox_valid(&conn->recvmbox) ||
-      ((recv_avail + (int)(p->tot_len)) > conn->recv_bufsize)) {
-#else  /* LWIP_SO_RCVBUF */
-  if ((conn == NULL) || !sys_mbox_valid(&conn->recvmbox)) {
-#endif /* LWIP_SO_RCVBUF */
-    pbuf_free(p);
-    return;
-  }
-
-  buf = (struct netbuf *)memp_malloc(MEMP_NETBUF);
-  if (buf == NULL) {
-    pbuf_free(p);
-    return;
-  } else {
-    buf->p = p;
-    buf->ptr = p;
-    ip_addr_set(&buf->addr, addr);
-    buf->port = port;
-#if LWIP_NETBUF_RECVINFO
-    {
-      const struct ip_hdr* iphdr = ip_current_header();
-      /* get the UDP header - always in the first pbuf, ensured by udp_input */
-      const struct udp_hdr* udphdr = (void*)(((char*)iphdr) + IPH_LEN(iphdr));
-#if LWIP_CHECKSUM_ON_COPY
-      buf->flags = NETBUF_FLAG_DESTADDR;
-#endif /* LWIP_CHECKSUM_ON_COPY */
-      ip_addr_set(&buf->toaddr, ip_current_dest_addr());
-      buf->toport_chksum = udphdr->dest;
-    }
-#endif /* LWIP_NETBUF_RECVINFO */
-  }
-
-  len = p->tot_len;
-  if (sys_mbox_trypost(&conn->recvmbox, buf) != ERR_OK) {
-    netbuf_delete(buf);
-    return;
-  } else {
-#if LWIP_SO_RCVBUF
-    SYS_ARCH_INC(conn->recv_avail, len);
-#endif /* LWIP_SO_RCVBUF */
-    /* Register event with callback */
-    API_EVENT(conn, NETCONN_EVT_RCVPLUS, len);
-  }
-}
-#endif /* LWIP_UDP */
-
-#if LWIP_TCP
-/**
- * Receive callback function for TCP netconns.
- * Posts the packet to conn->recvmbox, but doesn't delete it on errors.
- *
- * @see tcp.h (struct tcp_pcb.recv) for parameters and return value
- */
-static err_t
-recv_tcp(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
-{
-  struct netconn *conn;
-  u16_t len;
-
-  LWIP_UNUSED_ARG(pcb);
-  LWIP_ASSERT("recv_tcp must have a pcb argument", pcb != NULL);
-  LWIP_ASSERT("recv_tcp must have an argument", arg != NULL);
-  conn = (struct netconn *)arg;
-  LWIP_ASSERT("recv_tcp: recv for wrong pcb!", conn->pcb.tcp == pcb);
-
-  if (conn == NULL) {
-    return ERR_VAL;
-  }
-  if (!sys_mbox_valid(&conn->recvmbox)) {
-    /* recvmbox already deleted */
-    if (p != NULL) {
-      tcp_recved(pcb, p->tot_len);
-      pbuf_free(p);
-    }
-    return ERR_OK;
-  }
-  /* Unlike for UDP or RAW pcbs, don't check for available space
-     using recv_avail since that could break the connection
-     (data is already ACKed) */
-
-  /* don't overwrite fatal errors! */
-  NETCONN_SET_SAFE_ERR(conn, err);
-
-  if (p != NULL) {
-    len = p->tot_len;
-  } else {
-    len = 0;
-  }
-
-  if (sys_mbox_trypost(&conn->recvmbox, p) != ERR_OK) {
-    /* don't deallocate p: it is presented to us later again from tcp_fasttmr! */
-    return ERR_MEM;
-  } else {
-#if LWIP_SO_RCVBUF
-    SYS_ARCH_INC(conn->recv_avail, len);
-#endif /* LWIP_SO_RCVBUF */
-    /* Register event with callback */
-    API_EVENT(conn, NETCONN_EVT_RCVPLUS, len);
-  }
-
-  return ERR_OK;
-}
-
-/**
- * Poll callback function for TCP netconns.
- * Wakes up an application thread that waits for a connection to close
- * or data to be sent. The application thread then takes the
- * appropriate action to go on.
- *
- * Signals the conn->sem.
- * netconn_close waits for conn->sem if closing failed.
- *
- * @see tcp.h (struct tcp_pcb.poll) for parameters and return value
- */
-static err_t
-poll_tcp(void *arg, struct tcp_pcb *pcb)
-{
-  struct netconn *conn = (struct netconn *)arg;
-
-  LWIP_UNUSED_ARG(pcb);
-  LWIP_ASSERT("conn != NULL", (conn != NULL));
-
-  if (conn->state == NETCONN_WRITE) {
-    do_writemore(conn);
-  } else if (conn->state == NETCONN_CLOSE) {
-    do_close_internal(conn);
-  }
-  /* @todo: implement connect timeout here? */
-
-  /* Did a nonblocking write fail before? Then check available write-space. */
-  if (conn->flags & NETCONN_FLAG_CHECK_WRITESPACE) {
-    /* If the queued byte- or pbuf-count drops below the configured low-water limit,
-       let select mark this pcb as writable again. */
-    if ((conn->pcb.tcp != NULL) && (tcp_sndbuf(conn->pcb.tcp) > TCP_SNDLOWAT) &&
-      (tcp_sndqueuelen(conn->pcb.tcp) < TCP_SNDQUEUELOWAT)) {
-      conn->flags &= ~NETCONN_FLAG_CHECK_WRITESPACE;
-      API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0);
-    }
-  }
-
-  return ERR_OK;
-}
-
-/**
- * Sent callback function for TCP netconns.
- * Signals the conn->sem and calls API_EVENT.
- * netconn_write waits for conn->sem if send buffer is low.
- *
- * @see tcp.h (struct tcp_pcb.sent) for parameters and return value
- */
-static err_t
-sent_tcp(void *arg, struct tcp_pcb *pcb, u16_t len)
-{
-  struct netconn *conn = (struct netconn *)arg;
-
-  LWIP_UNUSED_ARG(pcb);
-  LWIP_ASSERT("conn != NULL", (conn != NULL));
-
-  if (conn->state == NETCONN_WRITE) {
-    do_writemore(conn);
-  } else if (conn->state == NETCONN_CLOSE) {
-    do_close_internal(conn);
-  }
-
-  if (conn) {
-    /* If the queued byte- or pbuf-count drops below the configured low-water limit,
-       let select mark this pcb as writable again. */
-    if ((conn->pcb.tcp != NULL) && (tcp_sndbuf(conn->pcb.tcp) > TCP_SNDLOWAT) &&
-      (tcp_sndqueuelen(conn->pcb.tcp) < TCP_SNDQUEUELOWAT)) {
-      conn->flags &= ~NETCONN_FLAG_CHECK_WRITESPACE;
-      API_EVENT(conn, NETCONN_EVT_SENDPLUS, len);
-    }
-  }
-  
-  return ERR_OK;
-}
-
-/**
- * Error callback function for TCP netconns.
- * Signals conn->sem, posts to all conn mboxes and calls API_EVENT.
- * The application thread has then to decide what to do.
- *
- * @see tcp.h (struct tcp_pcb.err) for parameters
- */
-static void
-err_tcp(void *arg, err_t err)
-{
-  struct netconn *conn;
-  enum netconn_state old_state;
-  SYS_ARCH_DECL_PROTECT(lev);
-
-  conn = (struct netconn *)arg;
-  LWIP_ASSERT("conn != NULL", (conn != NULL));
-
-  conn->pcb.tcp = NULL;
-
-  /* no check since this is always fatal! */
-  SYS_ARCH_PROTECT(lev);
-  conn->last_err = err;
-  SYS_ARCH_UNPROTECT(lev);
-
-  /* reset conn->state now before waking up other threads */
-  old_state = conn->state;
-  conn->state = NETCONN_NONE;
-
-  /* Notify the user layer about a connection error. Used to signal
-     select. */
-  API_EVENT(conn, NETCONN_EVT_ERROR, 0);
-  /* Try to release selects pending on 'read' or 'write', too.
-     They will get an error if they actually try to read or write. */
-  API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
-  API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0);
-
-  /* pass NULL-message to recvmbox to wake up pending recv */
-  if (sys_mbox_valid(&conn->recvmbox)) {
-    /* use trypost to prevent deadlock */
-    sys_mbox_trypost(&conn->recvmbox, NULL);
-  }
-  /* pass NULL-message to acceptmbox to wake up pending accept */
-  if (sys_mbox_valid(&conn->acceptmbox)) {
-    /* use trypost to preven deadlock */
-    sys_mbox_trypost(&conn->acceptmbox, NULL);
-  }
-
-  if ((old_state == NETCONN_WRITE) || (old_state == NETCONN_CLOSE) ||
-      (old_state == NETCONN_CONNECT)) {
-    /* calling do_writemore/do_close_internal is not necessary
-       since the pcb has already been deleted! */
-    int was_nonblocking_connect = IN_NONBLOCKING_CONNECT(conn);
-    SET_NONBLOCKING_CONNECT(conn, 0);
-
-    if (!was_nonblocking_connect) {
-      /* set error return code */
-      LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL);
-      conn->current_msg->err = err;
-      conn->current_msg = NULL;
-      /* wake up the waiting task */
-      sys_sem_signal(&conn->op_completed);
-    }
-  } else {
-    LWIP_ASSERT("conn->current_msg == NULL", conn->current_msg == NULL);
-  }
-}
-
-/**
- * Setup a tcp_pcb with the correct callback function pointers
- * and their arguments.
- *
- * @param conn the TCP netconn to setup
- */
-static void
-setup_tcp(struct netconn *conn)
-{
-  struct tcp_pcb *pcb;
-
-  pcb = conn->pcb.tcp;
-  tcp_arg(pcb, conn);
-  tcp_recv(pcb, recv_tcp);
-  tcp_sent(pcb, sent_tcp);
-  tcp_poll(pcb, poll_tcp, 4);
-  tcp_err(pcb, err_tcp);
-}
-
-/**
- * Accept callback function for TCP netconns.
- * Allocates a new netconn and posts that to conn->acceptmbox.
- *
- * @see tcp.h (struct tcp_pcb_listen.accept) for parameters and return value
- */
-static err_t
-accept_function(void *arg, struct tcp_pcb *newpcb, err_t err)
-{
-  struct netconn *newconn;
-  struct netconn *conn = (struct netconn *)arg;
-
-  LWIP_DEBUGF(API_MSG_DEBUG, ("accept_function: newpcb->tate: %s\n", tcp_debug_state_str(newpcb->state)));
-
-  if (!sys_mbox_valid(&conn->acceptmbox)) {
-    LWIP_DEBUGF(API_MSG_DEBUG, ("accept_function: acceptmbox already deleted\n"));
-    return ERR_VAL;
-  }
-
-  /* We have to set the callback here even though
-   * the new socket is unknown. conn->socket is marked as -1. */
-  newconn = netconn_alloc(conn->type, conn->callback);
-  if (newconn == NULL) {
-    return ERR_MEM;
-  }
-  newconn->pcb.tcp = newpcb;
-  setup_tcp(newconn);
-  /* no protection: when creating the pcb, the netconn is not yet known
-     to the application thread */
-  newconn->last_err = err;
-
-  if (sys_mbox_trypost(&conn->acceptmbox, newconn) != ERR_OK) {
-    /* When returning != ERR_OK, the pcb is aborted in tcp_process(),
-       so do nothing here! */
-    /* remove all references to this netconn from the pcb */
-    struct tcp_pcb* pcb = newconn->pcb.tcp;
-    tcp_arg(pcb, NULL);
-    tcp_recv(pcb, NULL);
-    tcp_sent(pcb, NULL);
-    tcp_poll(pcb, NULL, 4);
-    tcp_err(pcb, NULL);
-    /* remove reference from to the pcb from this netconn */
-    newconn->pcb.tcp = NULL;
-    /* no need to drain since we know the recvmbox is empty. */
-    sys_mbox_free(&newconn->recvmbox);
-    sys_mbox_set_invalid(&newconn->recvmbox);
-    netconn_free(newconn);
-    return ERR_MEM;
-  } else {
-    /* Register event with callback */
-    API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
-  }
-
-  return ERR_OK;
-}
-#endif /* LWIP_TCP */
-
-/**
- * Create a new pcb of a specific type.
- * Called from do_newconn().
- *
- * @param msg the api_msg_msg describing the connection type
- * @return msg->conn->err, but the return value is currently ignored
- */
-static void
-pcb_new(struct api_msg_msg *msg)
-{
-  LWIP_ASSERT("pcb_new: pcb already allocated", msg->conn->pcb.tcp == NULL);
-
-  /* Allocate a PCB for this connection */
-  switch(NETCONNTYPE_GROUP(msg->conn->type)) {
-#if LWIP_RAW
-  case NETCONN_RAW:
-    msg->conn->pcb.raw = raw_new(msg->msg.n.proto);
-    if(msg->conn->pcb.raw == NULL) {
-      msg->err = ERR_MEM;
-      break;
-    }
-    raw_recv(msg->conn->pcb.raw, recv_raw, msg->conn);
-    break;
-#endif /* LWIP_RAW */
-#if LWIP_UDP
-  case NETCONN_UDP:
-    msg->conn->pcb.udp = udp_new();
-    if(msg->conn->pcb.udp == NULL) {
-      msg->err = ERR_MEM;
-      break;
-    }
-#if LWIP_UDPLITE
-    if (msg->conn->type==NETCONN_UDPLITE) {
-      udp_setflags(msg->conn->pcb.udp, UDP_FLAGS_UDPLITE);
-    }
-#endif /* LWIP_UDPLITE */
-    if (msg->conn->type==NETCONN_UDPNOCHKSUM) {
-      udp_setflags(msg->conn->pcb.udp, UDP_FLAGS_NOCHKSUM);
-    }
-    udp_recv(msg->conn->pcb.udp, recv_udp, msg->conn);
-    break;
-#endif /* LWIP_UDP */
-#if LWIP_TCP
-  case NETCONN_TCP:
-    msg->conn->pcb.tcp = tcp_new();
-    if(msg->conn->pcb.tcp == NULL) {
-      msg->err = ERR_MEM;
-      break;
-    }
-    setup_tcp(msg->conn);
-    break;
-#endif /* LWIP_TCP */
-  default:
-    /* Unsupported netconn type, e.g. protocol disabled */
-    msg->err = ERR_VAL;
-    break;
-  }
-}
-
-/**
- * Create a new pcb of a specific type inside a netconn.
- * Called from netconn_new_with_proto_and_callback.
- *
- * @param msg the api_msg_msg describing the connection type
- */
-void
-do_newconn(struct api_msg_msg *msg)
-{
-  msg->err = ERR_OK;
-  if(msg->conn->pcb.tcp == NULL) {
-    pcb_new(msg);
-  }
-  /* Else? This "new" connection already has a PCB allocated. */
-  /* Is this an error condition? Should it be deleted? */
-  /* We currently just are happy and return. */
-
-  TCPIP_APIMSG_ACK(msg);
-}
-
-/**
- * Create a new netconn (of a specific type) that has a callback function.
- * The corresponding pcb is NOT created!
- *
- * @param t the type of 'connection' to create (@see enum netconn_type)
- * @param proto the IP protocol for RAW IP pcbs
- * @param callback a function to call on status changes (RX available, TX'ed)
- * @return a newly allocated struct netconn or
- *         NULL on memory error
- */
-struct netconn*
-netconn_alloc(enum netconn_type t, netconn_callback callback)
-{
-  struct netconn *conn;
-  int size;
-
-  conn = (struct netconn *)memp_malloc(MEMP_NETCONN);
-  if (conn == NULL) {
-    return NULL;
-  }
-
-  conn->last_err = ERR_OK;
-  conn->type = t;
-  conn->pcb.tcp = NULL;
-
-#if (DEFAULT_RAW_RECVMBOX_SIZE == DEFAULT_UDP_RECVMBOX_SIZE) && \
-    (DEFAULT_RAW_RECVMBOX_SIZE == DEFAULT_TCP_RECVMBOX_SIZE)
-  size = DEFAULT_RAW_RECVMBOX_SIZE;
-#else
-  switch(NETCONNTYPE_GROUP(t)) {
-#if LWIP_RAW
-  case NETCONN_RAW:
-    size = DEFAULT_RAW_RECVMBOX_SIZE;
-    break;
-#endif /* LWIP_RAW */
-#if LWIP_UDP
-  case NETCONN_UDP:
-    size = DEFAULT_UDP_RECVMBOX_SIZE;
-    break;
-#endif /* LWIP_UDP */
-#if LWIP_TCP
-  case NETCONN_TCP:
-    size = DEFAULT_TCP_RECVMBOX_SIZE;
-    break;
-#endif /* LWIP_TCP */
-  default:
-    LWIP_ASSERT("netconn_alloc: undefined netconn_type", 0);
-    goto free_and_return;
-  }
-#endif
-
-  if (sys_sem_new(&conn->op_completed, 0) != ERR_OK) {
-    goto free_and_return;
-  }
-  if (sys_mbox_new(&conn->recvmbox, size) != ERR_OK) {
-    sys_sem_free(&conn->op_completed);
-    goto free_and_return;
-  }
-
-#if LWIP_TCP
-  sys_mbox_set_invalid(&conn->acceptmbox);
-#endif
-  conn->state        = NETCONN_NONE;
-#if LWIP_SOCKET
-  /* initialize socket to -1 since 0 is a valid socket */
-  conn->socket       = -1;
-#endif /* LWIP_SOCKET */
-  conn->callback     = callback;
-#if LWIP_TCP
-  conn->current_msg  = NULL;
-  conn->write_offset = 0;
-#endif /* LWIP_TCP */
-#if LWIP_SO_SNDTIMEO
-  conn->send_timeout = 0;
-#endif /* LWIP_SO_SNDTIMEO */
-#if LWIP_SO_RCVTIMEO
-  conn->recv_timeout = 0;
-#endif /* LWIP_SO_RCVTIMEO */
-#if LWIP_SO_RCVBUF
-  conn->recv_bufsize = RECV_BUFSIZE_DEFAULT;
-  conn->recv_avail   = 0;
-#endif /* LWIP_SO_RCVBUF */
-  conn->flags = 0;
-  return conn;
-free_and_return:
-  memp_free(MEMP_NETCONN, conn);
-  return NULL;
-}
-
-/**
- * Delete a netconn and all its resources.
- * The pcb is NOT freed (since we might not be in the right thread context do this).
- *
- * @param conn the netconn to free
- */
-void
-netconn_free(struct netconn *conn)
-{
-  LWIP_ASSERT("PCB must be deallocated outside this function", conn->pcb.tcp == NULL);
-  LWIP_ASSERT("recvmbox must be deallocated before calling this function",
-    !sys_mbox_valid(&conn->recvmbox));
-#if LWIP_TCP
-  LWIP_ASSERT("acceptmbox must be deallocated before calling this function",
-    !sys_mbox_valid(&conn->acceptmbox));
-#endif /* LWIP_TCP */
-
-  sys_sem_free(&conn->op_completed);
-  sys_sem_set_invalid(&conn->op_completed);
-
-  memp_free(MEMP_NETCONN, conn);
-}
-
-/**
- * Delete rcvmbox and acceptmbox of a netconn and free the left-over data in
- * these mboxes
- *
- * @param conn the netconn to free
- * @bytes_drained bytes drained from recvmbox
- * @accepts_drained pending connections drained from acceptmbox
- */
-static void
-netconn_drain(struct netconn *conn)
-{
-  void *mem;
-#if LWIP_TCP
-  struct pbuf *p;
-#endif /* LWIP_TCP */
-
-  /* This runs in tcpip_thread, so we don't need to lock against rx packets */
-
-  /* Delete and drain the recvmbox. */
-  if (sys_mbox_valid(&conn->recvmbox)) {
-    while (sys_mbox_tryfetch(&conn->recvmbox, &mem) != SYS_MBOX_EMPTY) {
-#if LWIP_TCP
-      if (conn->type == NETCONN_TCP) {
-        if(mem != NULL) {
-          p = (struct pbuf*)mem;
-          /* pcb might be set to NULL already by err_tcp() */
-          if (conn->pcb.tcp != NULL) {
-            tcp_recved(conn->pcb.tcp, p->tot_len);
-          }
-          pbuf_free(p);
-        }
-      } else
-#endif /* LWIP_TCP */
-      {
-        netbuf_delete((struct netbuf *)mem);
-      }
-    }
-    sys_mbox_free(&conn->recvmbox);
-    sys_mbox_set_invalid(&conn->recvmbox);
-  }
-
-  /* Delete and drain the acceptmbox. */
-#if LWIP_TCP
-  if (sys_mbox_valid(&conn->acceptmbox)) {
-    while (sys_mbox_tryfetch(&conn->acceptmbox, &mem) != SYS_MBOX_EMPTY) {
-      struct netconn *newconn = (struct netconn *)mem;
-      /* Only tcp pcbs have an acceptmbox, so no need to check conn->type */
-      /* pcb might be set to NULL already by err_tcp() */
-      if (conn->pcb.tcp != NULL) {
-        tcp_accepted(conn->pcb.tcp);
-      }
-      /* drain recvmbox */
-      netconn_drain(newconn);
-      if (newconn->pcb.tcp != NULL) {
-        tcp_abort(newconn->pcb.tcp);
-        newconn->pcb.tcp = NULL;
-      }
-      netconn_free(newconn);
-    }
-    sys_mbox_free(&conn->acceptmbox);
-    sys_mbox_set_invalid(&conn->acceptmbox);
-  }
-#endif /* LWIP_TCP */
-}
-
-#if LWIP_TCP
-/**
- * Internal helper function to close a TCP netconn: since this sometimes
- * doesn't work at the first attempt, this function is called from multiple
- * places.
- *
- * @param conn the TCP netconn to close
- */
-static void
-do_close_internal(struct netconn *conn)
-{
-  err_t err;
-  u8_t shut, shut_rx, shut_tx, close;
-
-  LWIP_ASSERT("invalid conn", (conn != NULL));
-  LWIP_ASSERT("this is for tcp netconns only", (conn->type == NETCONN_TCP));
-  LWIP_ASSERT("conn must be in state NETCONN_CLOSE", (conn->state == NETCONN_CLOSE));
-  LWIP_ASSERT("pcb already closed", (conn->pcb.tcp != NULL));
-  LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL);
-
-  shut = conn->current_msg->msg.sd.shut;
-  shut_rx = shut & NETCONN_SHUT_RD;
-  shut_tx = shut & NETCONN_SHUT_WR;
-  /* shutting down both ends is the same as closing */
-  close = shut == NETCONN_SHUT_RDWR;
-
-  /* Set back some callback pointers */
-  if (close) {
-    tcp_arg(conn->pcb.tcp, NULL);
-  }
-  if (conn->pcb.tcp->state == LISTEN) {
-    tcp_accept(conn->pcb.tcp, NULL);
-  } else {
-    /* some callbacks have to be reset if tcp_close is not successful */
-    if (shut_rx) {
-      tcp_recv(conn->pcb.tcp, NULL);
-      tcp_accept(conn->pcb.tcp, NULL);
-    }
-    if (shut_tx) {
-      tcp_sent(conn->pcb.tcp, NULL);
-    }
-    if (close) {
-      tcp_poll(conn->pcb.tcp, NULL, 4);
-      tcp_err(conn->pcb.tcp, NULL);
-    }
-  }
-  /* Try to close the connection */
-  if (close) {
-    err = tcp_close(conn->pcb.tcp);
-  } else {
-    err = tcp_shutdown(conn->pcb.tcp, shut_rx, shut_tx);
-  }
-  if (err == ERR_OK) {
-    /* Closing succeeded */
-    conn->current_msg->err = ERR_OK;
-    conn->current_msg = NULL;
-    conn->state = NETCONN_NONE;
-    if (close) {
-      /* Set back some callback pointers as conn is going away */
-      conn->pcb.tcp = NULL;
-      /* Trigger select() in socket layer. Make sure everybody notices activity
-       on the connection, error first! */
-      API_EVENT(conn, NETCONN_EVT_ERROR, 0);
-    }
-    if (shut_rx) {
-      API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
-    }
-    if (shut_tx) {
-      API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0);
-    }
-    /* wake up the application task */
-    sys_sem_signal(&conn->op_completed);
-  } else {
-    /* Closing failed, restore some of the callbacks */
-    /* Closing of listen pcb will never fail! */
-    LWIP_ASSERT("Closing a listen pcb may not fail!", (conn->pcb.tcp->state != LISTEN));
-    tcp_sent(conn->pcb.tcp, sent_tcp);
-    tcp_poll(conn->pcb.tcp, poll_tcp, 4);
-    tcp_err(conn->pcb.tcp, err_tcp);
-    tcp_arg(conn->pcb.tcp, conn);
-    /* don't restore recv callback: we don't want to receive any more data */
-  }
-  /* If closing didn't succeed, we get called again either
-     from poll_tcp or from sent_tcp */
-}
-#endif /* LWIP_TCP */
-
-/**
- * Delete the pcb inside a netconn.
- * Called from netconn_delete.
- *
- * @param msg the api_msg_msg pointing to the connection
- */
-void
-do_delconn(struct api_msg_msg *msg)
-{
-  /* @todo TCP: abort running write/connect? */
- if ((msg->conn->state != NETCONN_NONE) &&
-     (msg->conn->state != NETCONN_LISTEN) &&
-     (msg->conn->state != NETCONN_CONNECT)) {
-    /* this only happens for TCP netconns */
-    LWIP_ASSERT("msg->conn->type == NETCONN_TCP", msg->conn->type == NETCONN_TCP);
-    msg->err = ERR_INPROGRESS;
-  } else {
-    LWIP_ASSERT("blocking connect in progress",
-      (msg->conn->state != NETCONN_CONNECT) || IN_NONBLOCKING_CONNECT(msg->conn));
-    /* Drain and delete mboxes */
-    netconn_drain(msg->conn);
-
-    if (msg->conn->pcb.tcp != NULL) {
-
-      switch (NETCONNTYPE_GROUP(msg->conn->type)) {
-#if LWIP_RAW
-      case NETCONN_RAW:
-        raw_remove(msg->conn->pcb.raw);
-        break;
-#endif /* LWIP_RAW */
-#if LWIP_UDP
-      case NETCONN_UDP:
-        msg->conn->pcb.udp->recv_arg = NULL;
-        udp_remove(msg->conn->pcb.udp);
-        break;
-#endif /* LWIP_UDP */
-#if LWIP_TCP
-      case NETCONN_TCP:
-        LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL &&
-          msg->conn->write_offset == 0);
-        msg->conn->state = NETCONN_CLOSE;
-        msg->msg.sd.shut = NETCONN_SHUT_RDWR;
-        msg->conn->current_msg = msg;
-        do_close_internal(msg->conn);
-        /* API_EVENT is called inside do_close_internal, before releasing
-           the application thread, so we can return at this point! */
-        return;
-#endif /* LWIP_TCP */
-      default:
-        break;
-      }
-      msg->conn->pcb.tcp = NULL;
-    }
-    /* tcp netconns don't come here! */
-
-    /* @todo: this lets select make the socket readable and writable,
-       which is wrong! errfd instead? */
-    API_EVENT(msg->conn, NETCONN_EVT_RCVPLUS, 0);
-    API_EVENT(msg->conn, NETCONN_EVT_SENDPLUS, 0);
-  }
-  if (sys_sem_valid(&msg->conn->op_completed)) {
-    sys_sem_signal(&msg->conn->op_completed);
-  }
-}
-
-/**
- * Bind a pcb contained in a netconn
- * Called from netconn_bind.
- *
- * @param msg the api_msg_msg pointing to the connection and containing
- *            the IP address and port to bind to
- */
-void
-do_bind(struct api_msg_msg *msg)
-{
-  if (ERR_IS_FATAL(msg->conn->last_err)) {
-    msg->err = msg->conn->last_err;
-  } else {
-    msg->err = ERR_VAL;
-    if (msg->conn->pcb.tcp != NULL) {
-      switch (NETCONNTYPE_GROUP(msg->conn->type)) {
-#if LWIP_RAW
-      case NETCONN_RAW:
-        msg->err = raw_bind(msg->conn->pcb.raw, msg->msg.bc.ipaddr);
-        break;
-#endif /* LWIP_RAW */
-#if LWIP_UDP
-      case NETCONN_UDP:
-        msg->err = udp_bind(msg->conn->pcb.udp, msg->msg.bc.ipaddr, msg->msg.bc.port);
-        break;
-#endif /* LWIP_UDP */
-#if LWIP_TCP
-      case NETCONN_TCP:
-        msg->err = tcp_bind(msg->conn->pcb.tcp, msg->msg.bc.ipaddr, msg->msg.bc.port);
-        break;
-#endif /* LWIP_TCP */
-      default:
-        break;
-      }
-    }
-  }
-  TCPIP_APIMSG_ACK(msg);
-}
-
-#if LWIP_TCP
-/**
- * TCP callback function if a connection (opened by tcp_connect/do_connect) has
- * been established (or reset by the remote host).
- *
- * @see tcp.h (struct tcp_pcb.connected) for parameters and return values
- */
-static err_t
-do_connected(void *arg, struct tcp_pcb *pcb, err_t err)
-{
-  struct netconn *conn;
-  int was_blocking;
-
-  LWIP_UNUSED_ARG(pcb);
-
-  conn = (struct netconn *)arg;
-
-  if (conn == NULL) {
-    return ERR_VAL;
-  }
-
-  LWIP_ASSERT("conn->state == NETCONN_CONNECT", conn->state == NETCONN_CONNECT);
-  LWIP_ASSERT("(conn->current_msg != NULL) || conn->in_non_blocking_connect",
-    (conn->current_msg != NULL) || IN_NONBLOCKING_CONNECT(conn));
-
-  if (conn->current_msg != NULL) {
-    conn->current_msg->err = err;
-  }
-  if ((conn->type == NETCONN_TCP) && (err == ERR_OK)) {
-    setup_tcp(conn);
-  }
-  was_blocking = !IN_NONBLOCKING_CONNECT(conn);
-  SET_NONBLOCKING_CONNECT(conn, 0);
-  conn->current_msg = NULL;
-  conn->state = NETCONN_NONE;
-  if (!was_blocking) {
-    NETCONN_SET_SAFE_ERR(conn, ERR_OK);
-  }
-  API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0);
-
-  if (was_blocking) {
-    sys_sem_signal(&conn->op_completed);
-  }
-  return ERR_OK;
-}
-#endif /* LWIP_TCP */
-
-/**
- * Connect a pcb contained inside a netconn
- * Called from netconn_connect.
- *
- * @param msg the api_msg_msg pointing to the connection and containing
- *            the IP address and port to connect to
- */
-void
-do_connect(struct api_msg_msg *msg)
-{
-  if (msg->conn->pcb.tcp == NULL) {
-    /* This may happen when calling netconn_connect() a second time */
-    msg->err = ERR_CLSD;
-  } else {
-    switch (NETCONNTYPE_GROUP(msg->conn->type)) {
-#if LWIP_RAW
-  case NETCONN_RAW:
-    msg->err = raw_connect(msg->conn->pcb.raw, msg->msg.bc.ipaddr);
-    break;
-#endif /* LWIP_RAW */
-#if LWIP_UDP
-  case NETCONN_UDP:
-    msg->err = udp_connect(msg->conn->pcb.udp, msg->msg.bc.ipaddr, msg->msg.bc.port);
-    break;
-#endif /* LWIP_UDP */
-#if LWIP_TCP
-  case NETCONN_TCP:
-    /* Prevent connect while doing any other action. */
-    if (msg->conn->state != NETCONN_NONE) {
-      msg->err = ERR_ISCONN;
-    } else {
-      setup_tcp(msg->conn);
-      msg->err = tcp_connect(msg->conn->pcb.tcp, msg->msg.bc.ipaddr,
-        msg->msg.bc.port, do_connected);
-      if (msg->err == ERR_OK) {
-        u8_t non_blocking = netconn_is_nonblocking(msg->conn);
-        msg->conn->state = NETCONN_CONNECT;
-        SET_NONBLOCKING_CONNECT(msg->conn, non_blocking);
-        if (non_blocking) {
-          msg->err = ERR_INPROGRESS;
-        } else {
-          msg->conn->current_msg = msg;
-          /* sys_sem_signal() is called from do_connected (or err_tcp()),
-          * when the connection is established! */
-          return;
-        }
-      }
-    }
-    break;
-#endif /* LWIP_TCP */
-  default:
-    LWIP_ERROR("Invalid netconn type", 0, do{ msg->err = ERR_VAL; }while(0));
-    break;
-    }
-  }
-  sys_sem_signal(&msg->conn->op_completed);
-}
-
-/**
- * Connect a pcb contained inside a netconn
- * Only used for UDP netconns.
- * Called from netconn_disconnect.
- *
- * @param msg the api_msg_msg pointing to the connection to disconnect
- */
-void
-do_disconnect(struct api_msg_msg *msg)
-{
-#if LWIP_UDP
-  if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_UDP) {
-    udp_disconnect(msg->conn->pcb.udp);
-    msg->err = ERR_OK;
-  } else
-#endif /* LWIP_UDP */
-  {
-    msg->err = ERR_VAL;
-  }
-  TCPIP_APIMSG_ACK(msg);
-}
-
-#if LWIP_TCP
-/**
- * Set a TCP pcb contained in a netconn into listen mode
- * Called from netconn_listen.
- *
- * @param msg the api_msg_msg pointing to the connection
- */
-void
-do_listen(struct api_msg_msg *msg)
-{
-  if (ERR_IS_FATAL(msg->conn->last_err)) {
-    msg->err = msg->conn->last_err;
-  } else {
-    msg->err = ERR_CONN;
-    if (msg->conn->pcb.tcp != NULL) {
-      if (msg->conn->type == NETCONN_TCP) {
-        if (msg->conn->state == NETCONN_NONE) {
-#if TCP_LISTEN_BACKLOG
-          struct tcp_pcb* lpcb = tcp_listen_with_backlog(msg->conn->pcb.tcp, msg->msg.lb.backlog);
-#else  /* TCP_LISTEN_BACKLOG */
-          struct tcp_pcb* lpcb = tcp_listen(msg->conn->pcb.tcp);
-#endif /* TCP_LISTEN_BACKLOG */
-          if (lpcb == NULL) {
-            /* in this case, the old pcb is still allocated */
-            msg->err = ERR_MEM;
-          } else {
-            /* delete the recvmbox and allocate the acceptmbox */
-            if (sys_mbox_valid(&msg->conn->recvmbox)) {
-              /** @todo: should we drain the recvmbox here? */
-              sys_mbox_free(&msg->conn->recvmbox);
-              sys_mbox_set_invalid(&msg->conn->recvmbox);
-            }
-            msg->err = ERR_OK;
-            if (!sys_mbox_valid(&msg->conn->acceptmbox)) {
-              msg->err = sys_mbox_new(&msg->conn->acceptmbox, DEFAULT_ACCEPTMBOX_SIZE);
-            }
-            if (msg->err == ERR_OK) {
-              msg->conn->state = NETCONN_LISTEN;
-              msg->conn->pcb.tcp = lpcb;
-              tcp_arg(msg->conn->pcb.tcp, msg->conn);
-              tcp_accept(msg->conn->pcb.tcp, accept_function);
-            } else {
-              /* since the old pcb is already deallocated, free lpcb now */
-              tcp_close(lpcb);
-              msg->conn->pcb.tcp = NULL;
-            }
-          }
-        }
-      } else {
-        msg->err = ERR_ARG;
-      }
-    }
-  }
-  TCPIP_APIMSG_ACK(msg);
-}
-#endif /* LWIP_TCP */
-
-/**
- * Send some data on a RAW or UDP pcb contained in a netconn
- * Called from netconn_send
- *
- * @param msg the api_msg_msg pointing to the connection
- */
-void
-do_send(struct api_msg_msg *msg)
-{
-  if (ERR_IS_FATAL(msg->conn->last_err)) {
-    msg->err = msg->conn->last_err;
-  } else {
-    msg->err = ERR_CONN;
-    if (msg->conn->pcb.tcp != NULL) {
-      switch (NETCONNTYPE_GROUP(msg->conn->type)) {
-#if LWIP_RAW
-      case NETCONN_RAW:
-        if (ip_addr_isany(&msg->msg.b->addr)) {
-          msg->err = raw_send(msg->conn->pcb.raw, msg->msg.b->p);
-        } else {
-          msg->err = raw_sendto(msg->conn->pcb.raw, msg->msg.b->p, &msg->msg.b->addr);
-        }
-        break;
-#endif
-#if LWIP_UDP
-      case NETCONN_UDP:
-#if LWIP_CHECKSUM_ON_COPY
-        if (ip_addr_isany(&msg->msg.b->addr)) {
-          msg->err = udp_send_chksum(msg->conn->pcb.udp, msg->msg.b->p,
-            msg->msg.b->flags & NETBUF_FLAG_CHKSUM, msg->msg.b->toport_chksum);
-        } else {
-          msg->err = udp_sendto_chksum(msg->conn->pcb.udp, msg->msg.b->p,
-            &msg->msg.b->addr, msg->msg.b->port,
-            msg->msg.b->flags & NETBUF_FLAG_CHKSUM, msg->msg.b->toport_chksum);
-        }
-#else /* LWIP_CHECKSUM_ON_COPY */
-        if (ip_addr_isany(&msg->msg.b->addr)) {
-          msg->err = udp_send(msg->conn->pcb.udp, msg->msg.b->p);
-        } else {
-          msg->err = udp_sendto(msg->conn->pcb.udp, msg->msg.b->p, &msg->msg.b->addr, msg->msg.b->port);
-        }
-#endif /* LWIP_CHECKSUM_ON_COPY */
-        break;
-#endif /* LWIP_UDP */
-      default:
-        break;
-      }
-    }
-  }
-  TCPIP_APIMSG_ACK(msg);
-}
-
-#if LWIP_TCP
-/**
- * Indicate data has been received from a TCP pcb contained in a netconn
- * Called from netconn_recv
- *
- * @param msg the api_msg_msg pointing to the connection
- */
-void
-do_recv(struct api_msg_msg *msg)
-{
-  msg->err = ERR_OK;
-  if (msg->conn->pcb.tcp != NULL) {
-    if (msg->conn->type == NETCONN_TCP) {
-#if TCP_LISTEN_BACKLOG
-      if (msg->conn->pcb.tcp->state == LISTEN) {
-        tcp_accepted(msg->conn->pcb.tcp);
-      } else
-#endif /* TCP_LISTEN_BACKLOG */
-      {
-        u32_t remaining = msg->msg.r.len;
-        do {
-          u16_t recved = (remaining > 0xffff) ? 0xffff : (u16_t)remaining;
-          tcp_recved(msg->conn->pcb.tcp, recved);
-          remaining -= recved;
-        }while(remaining != 0);
-      }
-    }
-  }
-  TCPIP_APIMSG_ACK(msg);
-}
-
-/**
- * See if more data needs to be written from a previous call to netconn_write.
- * Called initially from do_write. If the first call can't send all data
- * (because of low memory or empty send-buffer), this function is called again
- * from sent_tcp() or poll_tcp() to send more data. If all data is sent, the
- * blocking application thread (waiting in netconn_write) is released.
- *
- * @param conn netconn (that is currently in state NETCONN_WRITE) to process
- * @return ERR_OK
- *         ERR_MEM if LWIP_TCPIP_CORE_LOCKING=1 and sending hasn't yet finished
- */
-static err_t
-do_writemore(struct netconn *conn)
-{
-  err_t err;
-  void *dataptr;
-  u16_t len, available;
-  u8_t write_finished = 0;
-  size_t diff;
-  u8_t dontblock = netconn_is_nonblocking(conn) ||
-       (conn->current_msg->msg.w.apiflags & NETCONN_DONTBLOCK);
-  u8_t apiflags = conn->current_msg->msg.w.apiflags;
-
-  LWIP_ASSERT("conn != NULL", conn != NULL);
-  LWIP_ASSERT("conn->state == NETCONN_WRITE", (conn->state == NETCONN_WRITE));
-  LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL);
-  LWIP_ASSERT("conn->pcb.tcp != NULL", conn->pcb.tcp != NULL);
-  LWIP_ASSERT("conn->write_offset < conn->current_msg->msg.w.len",
-    conn->write_offset < conn->current_msg->msg.w.len);
-
-#if LWIP_SO_SNDTIMEO
-  if ((conn->send_timeout != 0) &&
-      ((s32_t)(sys_now() - conn->current_msg->msg.w.time_started) >= conn->send_timeout)) {
-    write_finished = 1;
-    if (conn->write_offset == 0) {
-      /* nothing has been written */
-      err = ERR_WOULDBLOCK;
-      conn->current_msg->msg.w.len = 0;
-    } else {
-      /* partial write */
-      err = ERR_OK;
-      conn->current_msg->msg.w.len = conn->write_offset;
-    }
-  } else
-#endif /* LWIP_SO_SNDTIMEO */
-  {
-    dataptr = (u8_t*)conn->current_msg->msg.w.dataptr + conn->write_offset;
-    diff = conn->current_msg->msg.w.len - conn->write_offset;
-    if (diff > 0xffffUL) { /* max_u16_t */
-      len = 0xffff;
-#if LWIP_TCPIP_CORE_LOCKING
-      conn->flags |= NETCONN_FLAG_WRITE_DELAYED;
-#endif
-      apiflags |= TCP_WRITE_FLAG_MORE;
-    } else {
-      len = (u16_t)diff;
-    }
-    available = tcp_sndbuf(conn->pcb.tcp);
-    if (available < len) {
-      /* don't try to write more than sendbuf */
-      len = available;
-      if (dontblock){ 
-        if (!len) {
-          err = ERR_WOULDBLOCK;
-          goto err_mem;
-        }
-      } else {
-#if LWIP_TCPIP_CORE_LOCKING
-        conn->flags |= NETCONN_FLAG_WRITE_DELAYED;
-#endif
-        apiflags |= TCP_WRITE_FLAG_MORE;
-      }
-    }
-    LWIP_ASSERT("do_writemore: invalid length!", ((conn->write_offset + len) <= conn->current_msg->msg.w.len));
-    err = tcp_write(conn->pcb.tcp, dataptr, len, apiflags);
-    /* if OK or memory error, check available space */
-    if ((err == ERR_OK) || (err == ERR_MEM)) {
-err_mem:
-      if (dontblock && (len < conn->current_msg->msg.w.len)) {
-        /* non-blocking write did not write everything: mark the pcb non-writable
-           and let poll_tcp check writable space to mark the pcb writable again */
-        API_EVENT(conn, NETCONN_EVT_SENDMINUS, len);
-        conn->flags |= NETCONN_FLAG_CHECK_WRITESPACE;
-      } else if ((tcp_sndbuf(conn->pcb.tcp) <= TCP_SNDLOWAT) ||
-                 (tcp_sndqueuelen(conn->pcb.tcp) >= TCP_SNDQUEUELOWAT)) {
-        /* The queued byte- or pbuf-count exceeds the configured low-water limit,
-           let select mark this pcb as non-writable. */
-        API_EVENT(conn, NETCONN_EVT_SENDMINUS, len);
-      }
-    }
-
-    if (err == ERR_OK) {
-      conn->write_offset += len;
-      if ((conn->write_offset == conn->current_msg->msg.w.len) || dontblock) {
-        /* return sent length */
-        conn->current_msg->msg.w.len = conn->write_offset;
-        /* everything was written */
-        write_finished = 1;
-        conn->write_offset = 0;
-      }
-      tcp_output(conn->pcb.tcp);
-    } else if ((err == ERR_MEM) && !dontblock) {
-      /* If ERR_MEM, we wait for sent_tcp or poll_tcp to be called
-         we do NOT return to the application thread, since ERR_MEM is
-         only a temporary error! */
-
-      /* tcp_write returned ERR_MEM, try tcp_output anyway */
-      tcp_output(conn->pcb.tcp);
-
-#if LWIP_TCPIP_CORE_LOCKING
-      conn->flags |= NETCONN_FLAG_WRITE_DELAYED;
-#endif
-    } else {
-      /* On errors != ERR_MEM, we don't try writing any more but return
-         the error to the application thread. */
-      write_finished = 1;
-      conn->current_msg->msg.w.len = 0;
-    }
-  }
-  if (write_finished) {
-    /* everything was written: set back connection state
-       and back to application task */
-    conn->current_msg->err = err;
-    conn->current_msg = NULL;
-    conn->state = NETCONN_NONE;
-#if LWIP_TCPIP_CORE_LOCKING
-    if ((conn->flags & NETCONN_FLAG_WRITE_DELAYED) != 0)
-#endif
-    {
-      sys_sem_signal(&conn->op_completed);
-    }
-  }
-#if LWIP_TCPIP_CORE_LOCKING
-  else
-    return ERR_MEM;
-#endif
-  return ERR_OK;
-}
-#endif /* LWIP_TCP */
-
-/**
- * Send some data on a TCP pcb contained in a netconn
- * Called from netconn_write
- *
- * @param msg the api_msg_msg pointing to the connection
- */
-void
-do_write(struct api_msg_msg *msg)
-{
-  if (ERR_IS_FATAL(msg->conn->last_err)) {
-    msg->err = msg->conn->last_err;
-  } else {
-    if (msg->conn->type == NETCONN_TCP) {
-#if LWIP_TCP
-      if (msg->conn->state != NETCONN_NONE) {
-        /* netconn is connecting, closing or in blocking write */
-        msg->err = ERR_INPROGRESS;
-      } else if (msg->conn->pcb.tcp != NULL) {
-        msg->conn->state = NETCONN_WRITE;
-        /* set all the variables used by do_writemore */
-        LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL &&
-          msg->conn->write_offset == 0);
-        LWIP_ASSERT("msg->msg.w.len != 0", msg->msg.w.len != 0);
-        msg->conn->current_msg = msg;
-        msg->conn->write_offset = 0;
-#if LWIP_TCPIP_CORE_LOCKING
-        msg->conn->flags &= ~NETCONN_FLAG_WRITE_DELAYED;
-        if (do_writemore(msg->conn) != ERR_OK) {
-          LWIP_ASSERT("state!", msg->conn->state == NETCONN_WRITE);
-          UNLOCK_TCPIP_CORE();
-          sys_arch_sem_wait(&msg->conn->op_completed, 0);
-          LOCK_TCPIP_CORE();
-          LWIP_ASSERT("state!", msg->conn->state == NETCONN_NONE);
-        }
-#else /* LWIP_TCPIP_CORE_LOCKING */
-        do_writemore(msg->conn);
-#endif /* LWIP_TCPIP_CORE_LOCKING */
-        /* for both cases: if do_writemore was called, don't ACK the APIMSG
-           since do_writemore ACKs it! */
-        return;
-      } else {
-        msg->err = ERR_CONN;
-      }
-#else /* LWIP_TCP */
-      msg->err = ERR_VAL;
-#endif /* LWIP_TCP */
-#if (LWIP_UDP || LWIP_RAW)
-    } else {
-      msg->err = ERR_VAL;
-#endif /* (LWIP_UDP || LWIP_RAW) */
-    }
-  }
-  TCPIP_APIMSG_ACK(msg);
-}
-
-/**
- * Return a connection's local or remote address
- * Called from netconn_getaddr
- *
- * @param msg the api_msg_msg pointing to the connection
- */
-void
-do_getaddr(struct api_msg_msg *msg)
-{
-  if (msg->conn->pcb.ip != NULL) {
-    *(msg->msg.ad.ipaddr) = (msg->msg.ad.local ? msg->conn->pcb.ip->local_ip :
-                             msg->conn->pcb.ip->remote_ip);
-
-    msg->err = ERR_OK;
-    switch (NETCONNTYPE_GROUP(msg->conn->type)) {
-#if LWIP_RAW
-    case NETCONN_RAW:
-      if (msg->msg.ad.local) {
-        *(msg->msg.ad.port) = msg->conn->pcb.raw->protocol;
-      } else {
-        /* return an error as connecting is only a helper for upper layers */
-        msg->err = ERR_CONN;
-      }
-      break;
-#endif /* LWIP_RAW */
-#if LWIP_UDP
-    case NETCONN_UDP:
-      if (msg->msg.ad.local) {
-        *(msg->msg.ad.port) = msg->conn->pcb.udp->local_port;
-      } else {
-        if ((msg->conn->pcb.udp->flags & UDP_FLAGS_CONNECTED) == 0) {
-          msg->err = ERR_CONN;
-        } else {
-          *(msg->msg.ad.port) = msg->conn->pcb.udp->remote_port;
-        }
-      }
-      break;
-#endif /* LWIP_UDP */
-#if LWIP_TCP
-    case NETCONN_TCP:
-      *(msg->msg.ad.port) = (msg->msg.ad.local?msg->conn->pcb.tcp->local_port:msg->conn->pcb.tcp->remote_port);
-      break;
-#endif /* LWIP_TCP */
-    default:
-      LWIP_ASSERT("invalid netconn_type", 0);
-      break;
-    }
-  } else {
-    msg->err = ERR_CONN;
-  }
-  TCPIP_APIMSG_ACK(msg);
-}
-
-/**
- * Close a TCP pcb contained in a netconn
- * Called from netconn_close
- *
- * @param msg the api_msg_msg pointing to the connection
- */
-void
-do_close(struct api_msg_msg *msg)
-{
-#if LWIP_TCP
-  /* @todo: abort running write/connect? */
-  if ((msg->conn->state != NETCONN_NONE) && (msg->conn->state != NETCONN_LISTEN)) {
-    /* this only happens for TCP netconns */
-    LWIP_ASSERT("msg->conn->type == NETCONN_TCP", msg->conn->type == NETCONN_TCP);
-    msg->err = ERR_INPROGRESS;
-  } else if ((msg->conn->pcb.tcp != NULL) && (msg->conn->type == NETCONN_TCP)) {
-    if ((msg->msg.sd.shut != NETCONN_SHUT_RDWR) && (msg->conn->state == NETCONN_LISTEN)) {
-      /* LISTEN doesn't support half shutdown */
-      msg->err = ERR_CONN;
-    } else {
-      if (msg->msg.sd.shut & NETCONN_SHUT_RD) {
-        /* Drain and delete mboxes */
-        netconn_drain(msg->conn);
-      }
-      LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL &&
-        msg->conn->write_offset == 0);
-      msg->conn->state = NETCONN_CLOSE;
-      msg->conn->current_msg = msg;
-      do_close_internal(msg->conn);
-      /* for tcp netconns, do_close_internal ACKs the message */
-      return;
-    }
-  } else
-#endif /* LWIP_TCP */
-  {
-    msg->err = ERR_VAL;
-  }
-  sys_sem_signal(&msg->conn->op_completed);
-}
-
-#if LWIP_IGMP
-/**
- * Join multicast groups for UDP netconns.
- * Called from netconn_join_leave_group
- *
- * @param msg the api_msg_msg pointing to the connection
- */
-void
-do_join_leave_group(struct api_msg_msg *msg)
-{ 
-  if (ERR_IS_FATAL(msg->conn->last_err)) {
-    msg->err = msg->conn->last_err;
-  } else {
-    if (msg->conn->pcb.tcp != NULL) {
-      if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_UDP) {
-#if LWIP_UDP
-        if (msg->msg.jl.join_or_leave == NETCONN_JOIN) {
-          msg->err = igmp_joingroup(msg->msg.jl.netif_addr, msg->msg.jl.multiaddr);
-        } else {
-          msg->err = igmp_leavegroup(msg->msg.jl.netif_addr, msg->msg.jl.multiaddr);
-        }
-#endif /* LWIP_UDP */
-#if (LWIP_TCP || LWIP_RAW)
-      } else {
-        msg->err = ERR_VAL;
-#endif /* (LWIP_TCP || LWIP_RAW) */
-      }
-    } else {
-      msg->err = ERR_CONN;
-    }
-  }
-  TCPIP_APIMSG_ACK(msg);
-}
-#endif /* LWIP_IGMP */
-
-#if LWIP_DNS
-/**
- * Callback function that is called when DNS name is resolved
- * (or on timeout). A waiting application thread is waked up by
- * signaling the semaphore.
- */
-static void
-do_dns_found(const char *name, ip_addr_t *ipaddr, void *arg)
-{
-  struct dns_api_msg *msg = (struct dns_api_msg*)arg;
-
-  LWIP_ASSERT("DNS response for wrong host name", strcmp(msg->name, name) == 0);
-  LWIP_UNUSED_ARG(name);
-
-  if (ipaddr == NULL) {
-    /* timeout or memory error */
-    *msg->err = ERR_VAL;
-  } else {
-    /* address was resolved */
-    *msg->err = ERR_OK;
-    *msg->addr = *ipaddr;
-  }
-  /* wake up the application task waiting in netconn_gethostbyname */
-  sys_sem_signal(msg->sem);
-}
-
-/**
- * Execute a DNS query
- * Called from netconn_gethostbyname
- *
- * @param arg the dns_api_msg pointing to the query
- */
-void
-do_gethostbyname(void *arg)
-{
-  struct dns_api_msg *msg = (struct dns_api_msg*)arg;
-
-  *msg->err = dns_gethostbyname(msg->name, msg->addr, do_dns_found, msg);
-  if (*msg->err != ERR_INPROGRESS) {
-    /* on error or immediate success, wake up the application
-     * task waiting in netconn_gethostbyname */
-    sys_sem_signal(msg->sem);
-  }
-}
-#endif /* LWIP_DNS */
-
-#endif /* LWIP_NETCONN */
+/**
+ * @file
+ * Sequential API Internal module
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/priv/api_msg.h"
+
+#include "lwip/ip.h"
+#include "lwip/ip_addr.h"
+#include "lwip/udp.h"
+#include "lwip/tcp.h"
+#include "lwip/raw.h"
+
+#include "lwip/memp.h"
+#include "lwip/igmp.h"
+#include "lwip/dns.h"
+#include "lwip/mld6.h"
+#include "lwip/priv/tcpip_priv.h"
+
+#include <string.h>
+
+/* netconns are polled once per second (e.g. continue write on memory error) */
+#define NETCONN_TCP_POLL_INTERVAL 2
+
+#define SET_NONBLOCKING_CONNECT(conn, val)  do { if (val) { \
+  (conn)->flags |= NETCONN_FLAG_IN_NONBLOCKING_CONNECT; \
+} else { \
+  (conn)->flags &= ~ NETCONN_FLAG_IN_NONBLOCKING_CONNECT; }} while(0)
+#define IN_NONBLOCKING_CONNECT(conn) (((conn)->flags & NETCONN_FLAG_IN_NONBLOCKING_CONNECT) != 0)
+
+/* forward declarations */
+#if LWIP_TCP
+#if LWIP_TCPIP_CORE_LOCKING
+#define WRITE_DELAYED         , 1
+#define WRITE_DELAYED_PARAM   , u8_t delayed
+#else /* LWIP_TCPIP_CORE_LOCKING */
+#define WRITE_DELAYED
+#define WRITE_DELAYED_PARAM
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+static err_t lwip_netconn_do_writemore(struct netconn *conn  WRITE_DELAYED_PARAM);
+static err_t lwip_netconn_do_close_internal(struct netconn *conn  WRITE_DELAYED_PARAM);
+#endif
+
+#if LWIP_TCPIP_CORE_LOCKING
+#define TCPIP_APIMSG_ACK(m)   NETCONN_SET_SAFE_ERR((m)->conn, (m)->err)
+#else /* LWIP_TCPIP_CORE_LOCKING */
+#define TCPIP_APIMSG_ACK(m)   do { NETCONN_SET_SAFE_ERR((m)->conn, (m)->err); sys_sem_signal(LWIP_API_MSG_SEM(m)); } while(0)
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+
+#if LWIP_TCP
+u8_t netconn_aborted;
+#endif /* LWIP_TCP */
+
+#if LWIP_RAW
+/**
+ * Receive callback function for RAW netconns.
+ * Doesn't 'eat' the packet, only copies it and sends it to
+ * conn->recvmbox
+ *
+ * @see raw.h (struct raw_pcb.recv) for parameters and return value
+ */
+static u8_t
+recv_raw(void *arg, struct raw_pcb *pcb, struct pbuf *p,
+    const ip_addr_t *addr)
+{
+  struct pbuf *q;
+  struct netbuf *buf;
+  struct netconn *conn;
+
+  LWIP_UNUSED_ARG(addr);
+  conn = (struct netconn *)arg;
+
+  if ((conn != NULL) && sys_mbox_valid(&conn->recvmbox)) {
+#if LWIP_SO_RCVBUF
+    int recv_avail;
+    SYS_ARCH_GET(conn->recv_avail, recv_avail);
+    if ((recv_avail + (int)(p->tot_len)) > conn->recv_bufsize) {
+      return 0;
+    }
+#endif /* LWIP_SO_RCVBUF */
+    /* copy the whole packet into new pbufs */
+    q = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM);
+    if (q != NULL) {
+      if (pbuf_copy(q, p) != ERR_OK) {
+        pbuf_free(q);
+        q = NULL;
+      }
+    }
+
+    if (q != NULL) {
+      u16_t len;
+      buf = (struct netbuf *)memp_malloc(MEMP_NETBUF);
+      if (buf == NULL) {
+        pbuf_free(q);
+        return 0;
+      }
+
+      buf->p = q;
+      buf->ptr = q;
+      ip_addr_copy(buf->addr, *ip_current_src_addr());
+      buf->port = pcb->protocol;
+
+      len = q->tot_len;
+      if (sys_mbox_trypost(&conn->recvmbox, buf) != ERR_OK) {
+        netbuf_delete(buf);
+        return 0;
+      } else {
+#if LWIP_SO_RCVBUF
+        SYS_ARCH_INC(conn->recv_avail, len);
+#endif /* LWIP_SO_RCVBUF */
+        /* Register event with callback */
+        API_EVENT(conn, NETCONN_EVT_RCVPLUS, len);
+      }
+    }
+  }
+
+  return 0; /* do not eat the packet */
+}
+#endif /* LWIP_RAW*/
+
+#if LWIP_UDP
+/**
+ * Receive callback function for UDP netconns.
+ * Posts the packet to conn->recvmbox or deletes it on memory error.
+ *
+ * @see udp.h (struct udp_pcb.recv) for parameters
+ */
+static void
+recv_udp(void *arg, struct udp_pcb *pcb, struct pbuf *p,
+   const ip_addr_t *addr, u16_t port)
+{
+  struct netbuf *buf;
+  struct netconn *conn;
+  u16_t len;
+#if LWIP_SO_RCVBUF
+  int recv_avail;
+#endif /* LWIP_SO_RCVBUF */
+
+  LWIP_UNUSED_ARG(pcb); /* only used for asserts... */
+  LWIP_ASSERT("recv_udp must have a pcb argument", pcb != NULL);
+  LWIP_ASSERT("recv_udp must have an argument", arg != NULL);
+  conn = (struct netconn *)arg;
+  LWIP_ASSERT("recv_udp: recv for wrong pcb!", conn->pcb.udp == pcb);
+
+#if LWIP_SO_RCVBUF
+  SYS_ARCH_GET(conn->recv_avail, recv_avail);
+  if ((conn == NULL) || !sys_mbox_valid(&conn->recvmbox) ||
+      ((recv_avail + (int)(p->tot_len)) > conn->recv_bufsize)) {
+#else  /* LWIP_SO_RCVBUF */
+  if ((conn == NULL) || !sys_mbox_valid(&conn->recvmbox)) {
+#endif /* LWIP_SO_RCVBUF */
+    pbuf_free(p);
+    return;
+  }
+
+  buf = (struct netbuf *)memp_malloc(MEMP_NETBUF);
+  if (buf == NULL) {
+    pbuf_free(p);
+    return;
+  } else {
+    buf->p = p;
+    buf->ptr = p;
+    ip_addr_set(&buf->addr, addr);
+    buf->port = port;
+#if LWIP_NETBUF_RECVINFO
+    {
+      /* get the UDP header - always in the first pbuf, ensured by udp_input */
+      const struct udp_hdr* udphdr = (const struct udp_hdr*)ip_next_header_ptr();
+#if LWIP_CHECKSUM_ON_COPY
+      buf->flags = NETBUF_FLAG_DESTADDR;
+#endif /* LWIP_CHECKSUM_ON_COPY */
+      ip_addr_set(&buf->toaddr, ip_current_dest_addr());
+      buf->toport_chksum = udphdr->dest;
+    }
+#endif /* LWIP_NETBUF_RECVINFO */
+  }
+
+  len = p->tot_len;
+  if (sys_mbox_trypost(&conn->recvmbox, buf) != ERR_OK) {
+    netbuf_delete(buf);
+    return;
+  } else {
+#if LWIP_SO_RCVBUF
+    SYS_ARCH_INC(conn->recv_avail, len);
+#endif /* LWIP_SO_RCVBUF */
+    /* Register event with callback */
+    API_EVENT(conn, NETCONN_EVT_RCVPLUS, len);
+  }
+}
+#endif /* LWIP_UDP */
+
+#if LWIP_TCP
+/**
+ * Receive callback function for TCP netconns.
+ * Posts the packet to conn->recvmbox, but doesn't delete it on errors.
+ *
+ * @see tcp.h (struct tcp_pcb.recv) for parameters and return value
+ */
+static err_t
+recv_tcp(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
+{
+  struct netconn *conn;
+  u16_t len;
+
+  LWIP_UNUSED_ARG(pcb);
+  LWIP_ASSERT("recv_tcp must have a pcb argument", pcb != NULL);
+  LWIP_ASSERT("recv_tcp must have an argument", arg != NULL);
+  conn = (struct netconn *)arg;
+
+  if (conn == NULL) {
+    return ERR_VAL;
+  }
+  LWIP_ASSERT("recv_tcp: recv for wrong pcb!", conn->pcb.tcp == pcb);
+
+  if (!sys_mbox_valid(&conn->recvmbox)) {
+    /* recvmbox already deleted */
+    if (p != NULL) {
+      tcp_recved(pcb, p->tot_len);
+      pbuf_free(p);
+    }
+    return ERR_OK;
+  }
+  /* Unlike for UDP or RAW pcbs, don't check for available space
+     using recv_avail since that could break the connection
+     (data is already ACKed) */
+
+  /* don't overwrite fatal errors! */
+  if (err != ERR_OK) {
+    NETCONN_SET_SAFE_ERR(conn, err);
+  }
+
+  if (p != NULL) {
+    len = p->tot_len;
+  } else {
+    len = 0;
+  }
+
+  if (sys_mbox_trypost(&conn->recvmbox, p) != ERR_OK) {
+    /* don't deallocate p: it is presented to us later again from tcp_fasttmr! */
+    return ERR_MEM;
+  } else {
+#if LWIP_SO_RCVBUF
+    SYS_ARCH_INC(conn->recv_avail, len);
+#endif /* LWIP_SO_RCVBUF */
+    /* Register event with callback */
+    API_EVENT(conn, NETCONN_EVT_RCVPLUS, len);
+  }
+
+  return ERR_OK;
+}
+
+/**
+ * Poll callback function for TCP netconns.
+ * Wakes up an application thread that waits for a connection to close
+ * or data to be sent. The application thread then takes the
+ * appropriate action to go on.
+ *
+ * Signals the conn->sem.
+ * netconn_close waits for conn->sem if closing failed.
+ *
+ * @see tcp.h (struct tcp_pcb.poll) for parameters and return value
+ */
+static err_t
+poll_tcp(void *arg, struct tcp_pcb *pcb)
+{
+  struct netconn *conn = (struct netconn *)arg;
+
+  LWIP_UNUSED_ARG(pcb);
+  LWIP_ASSERT("conn != NULL", (conn != NULL));
+
+  if (conn->state == NETCONN_WRITE) {
+    lwip_netconn_do_writemore(conn  WRITE_DELAYED);
+  } else if (conn->state == NETCONN_CLOSE) {
+#if !LWIP_SO_SNDTIMEO && !LWIP_SO_LINGER
+    if (conn->current_msg && conn->current_msg->msg.sd.polls_left) {
+      conn->current_msg->msg.sd.polls_left--;
+    }
+#endif /* !LWIP_SO_SNDTIMEO && !LWIP_SO_LINGER */
+    lwip_netconn_do_close_internal(conn  WRITE_DELAYED);
+  }
+  /* @todo: implement connect timeout here? */
+
+  /* Did a nonblocking write fail before? Then check available write-space. */
+  if (conn->flags & NETCONN_FLAG_CHECK_WRITESPACE) {
+    /* If the queued byte- or pbuf-count drops below the configured low-water limit,
+       let select mark this pcb as writable again. */
+    if ((conn->pcb.tcp != NULL) && (tcp_sndbuf(conn->pcb.tcp) > TCP_SNDLOWAT) &&
+      (tcp_sndqueuelen(conn->pcb.tcp) < TCP_SNDQUEUELOWAT)) {
+      conn->flags &= ~NETCONN_FLAG_CHECK_WRITESPACE;
+      API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0);
+    }
+  }
+
+  return ERR_OK;
+}
+
+/**
+ * Sent callback function for TCP netconns.
+ * Signals the conn->sem and calls API_EVENT.
+ * netconn_write waits for conn->sem if send buffer is low.
+ *
+ * @see tcp.h (struct tcp_pcb.sent) for parameters and return value
+ */
+static err_t
+sent_tcp(void *arg, struct tcp_pcb *pcb, u16_t len)
+{
+  struct netconn *conn = (struct netconn *)arg;
+
+  LWIP_UNUSED_ARG(pcb);
+  LWIP_ASSERT("conn != NULL", (conn != NULL));
+
+  if (conn) {
+    if (conn->state == NETCONN_WRITE) {
+      lwip_netconn_do_writemore(conn  WRITE_DELAYED);
+    } else if (conn->state == NETCONN_CLOSE) {
+      lwip_netconn_do_close_internal(conn  WRITE_DELAYED);
+    }
+
+    /* If the queued byte- or pbuf-count drops below the configured low-water limit,
+       let select mark this pcb as writable again. */
+    if ((conn->pcb.tcp != NULL) && (tcp_sndbuf(conn->pcb.tcp) > TCP_SNDLOWAT) &&
+      (tcp_sndqueuelen(conn->pcb.tcp) < TCP_SNDQUEUELOWAT)) {
+      conn->flags &= ~NETCONN_FLAG_CHECK_WRITESPACE;
+      API_EVENT(conn, NETCONN_EVT_SENDPLUS, len);
+    }
+  }
+
+  return ERR_OK;
+}
+
+/**
+ * Error callback function for TCP netconns.
+ * Signals conn->sem, posts to all conn mboxes and calls API_EVENT.
+ * The application thread has then to decide what to do.
+ *
+ * @see tcp.h (struct tcp_pcb.err) for parameters
+ */
+static void
+err_tcp(void *arg, err_t err)
+{
+  struct netconn *conn;
+  enum netconn_state old_state;
+
+  conn = (struct netconn *)arg;
+  LWIP_ASSERT("conn != NULL", (conn != NULL));
+
+  conn->pcb.tcp = NULL;
+
+  /* reset conn->state now before waking up other threads */
+  old_state = conn->state;
+  conn->state = NETCONN_NONE;
+
+  if (old_state == NETCONN_CLOSE) {
+    /* RST during close: let close return success & dealloc the netconn */
+    err = ERR_OK;
+    NETCONN_SET_SAFE_ERR(conn, ERR_OK);
+  } else {
+    /* no check since this is always fatal! */
+    SYS_ARCH_SET(conn->last_err, err);
+  }
+
+  /* @todo: the type of NETCONN_EVT created should depend on 'old_state' */
+
+  /* Notify the user layer about a connection error. Used to signal select. */
+  API_EVENT(conn, NETCONN_EVT_ERROR, 0);
+  /* Try to release selects pending on 'read' or 'write', too.
+     They will get an error if they actually try to read or write. */
+  API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
+  API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0);
+
+  /* pass NULL-message to recvmbox to wake up pending recv */
+  if (sys_mbox_valid(&conn->recvmbox)) {
+    /* use trypost to prevent deadlock */
+    sys_mbox_trypost(&conn->recvmbox, NULL);
+  }
+  /* pass NULL-message to acceptmbox to wake up pending accept */
+  if (sys_mbox_valid(&conn->acceptmbox)) {
+    /* use trypost to preven deadlock */
+    sys_mbox_trypost(&conn->acceptmbox, NULL);
+  }
+
+  if ((old_state == NETCONN_WRITE) || (old_state == NETCONN_CLOSE) ||
+      (old_state == NETCONN_CONNECT)) {
+    /* calling lwip_netconn_do_writemore/lwip_netconn_do_close_internal is not necessary
+       since the pcb has already been deleted! */
+    int was_nonblocking_connect = IN_NONBLOCKING_CONNECT(conn);
+    SET_NONBLOCKING_CONNECT(conn, 0);
+
+    if (!was_nonblocking_connect) {
+      sys_sem_t* op_completed_sem;
+      /* set error return code */
+      LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL);
+      conn->current_msg->err = err;
+      op_completed_sem = LWIP_API_MSG_SEM(conn->current_msg);
+      LWIP_ASSERT("inavlid op_completed_sem", sys_sem_valid(op_completed_sem));
+      conn->current_msg = NULL;
+      /* wake up the waiting task */
+      NETCONN_SET_SAFE_ERR(conn, err);
+      sys_sem_signal(op_completed_sem);
+    }
+  } else {
+    LWIP_ASSERT("conn->current_msg == NULL", conn->current_msg == NULL);
+  }
+}
+
+/**
+ * Setup a tcp_pcb with the correct callback function pointers
+ * and their arguments.
+ *
+ * @param conn the TCP netconn to setup
+ */
+static void
+setup_tcp(struct netconn *conn)
+{
+  struct tcp_pcb *pcb;
+
+  pcb = conn->pcb.tcp;
+  tcp_arg(pcb, conn);
+  tcp_recv(pcb, recv_tcp);
+  tcp_sent(pcb, sent_tcp);
+  tcp_poll(pcb, poll_tcp, NETCONN_TCP_POLL_INTERVAL);
+  tcp_err(pcb, err_tcp);
+}
+
+/**
+ * Accept callback function for TCP netconns.
+ * Allocates a new netconn and posts that to conn->acceptmbox.
+ *
+ * @see tcp.h (struct tcp_pcb_listen.accept) for parameters and return value
+ */
+static err_t
+accept_function(void *arg, struct tcp_pcb *newpcb, err_t err)
+{
+  struct netconn *newconn;
+  struct netconn *conn = (struct netconn *)arg;
+
+  LWIP_DEBUGF(API_MSG_DEBUG, ("accept_function: newpcb->tate: %s\n", tcp_debug_state_str(newpcb->state)));
+
+  if (conn == NULL) {
+    return ERR_VAL;
+  }
+  if (!sys_mbox_valid(&conn->acceptmbox)) {
+    LWIP_DEBUGF(API_MSG_DEBUG, ("accept_function: acceptmbox already deleted\n"));
+    return ERR_VAL;
+  }
+
+  if (newpcb == NULL) {
+    /* out-of-pcbs during connect: pass on this error to the application */
+    if (sys_mbox_trypost(&conn->acceptmbox, &netconn_aborted) == ERR_OK) {
+      /* Register event with callback */
+      API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
+    }
+    return ERR_VAL;
+  }
+
+  /* We have to set the callback here even though
+   * the new socket is unknown. newconn->socket is marked as -1. */
+  newconn = netconn_alloc(conn->type, conn->callback);
+  if (newconn == NULL) {
+    /* outof netconns: pass on this error to the application */
+    if (sys_mbox_trypost(&conn->acceptmbox, &netconn_aborted) == ERR_OK) {
+      /* Register event with callback */
+      API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
+    }
+    return ERR_MEM;
+  }
+  newconn->pcb.tcp = newpcb;
+  setup_tcp(newconn);
+  /* no protection: when creating the pcb, the netconn is not yet known
+     to the application thread */
+  newconn->last_err = err;
+
+  /* handle backlog counter */
+  tcp_backlog_delayed(newpcb);
+
+  if (sys_mbox_trypost(&conn->acceptmbox, newconn) != ERR_OK) {
+    /* When returning != ERR_OK, the pcb is aborted in tcp_process(),
+       so do nothing here! */
+    /* remove all references to this netconn from the pcb */
+    struct tcp_pcb* pcb = newconn->pcb.tcp;
+    tcp_arg(pcb, NULL);
+    tcp_recv(pcb, NULL);
+    tcp_sent(pcb, NULL);
+    tcp_poll(pcb, NULL, 0);
+    tcp_err(pcb, NULL);
+    /* remove reference from to the pcb from this netconn */
+    newconn->pcb.tcp = NULL;
+    /* no need to drain since we know the recvmbox is empty. */
+    sys_mbox_free(&newconn->recvmbox);
+    sys_mbox_set_invalid(&newconn->recvmbox);
+    netconn_free(newconn);
+    return ERR_MEM;
+  } else {
+    /* Register event with callback */
+    API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
+  }
+
+  return ERR_OK;
+}
+#endif /* LWIP_TCP */
+
+/**
+ * Create a new pcb of a specific type.
+ * Called from lwip_netconn_do_newconn().
+ *
+ * @param msg the api_msg_msg describing the connection type
+ */
+static void
+pcb_new(struct api_msg *msg)
+{
+  enum lwip_ip_addr_type iptype = IPADDR_TYPE_V4;
+
+  LWIP_ASSERT("pcb_new: pcb already allocated", msg->conn->pcb.tcp == NULL);
+ 
+#if LWIP_IPV6 && LWIP_IPV4
+  /* IPv6: Dual-stack by default, unless netconn_set_ipv6only() is called */
+  if(NETCONNTYPE_ISIPV6(netconn_type(msg->conn))) {
+    iptype = IPADDR_TYPE_ANY;
+  }
+#endif
+  
+  /* Allocate a PCB for this connection */
+  switch(NETCONNTYPE_GROUP(msg->conn->type)) {
+#if LWIP_RAW
+  case NETCONN_RAW:
+    msg->conn->pcb.raw = raw_new_ip_type(iptype, msg->msg.n.proto);
+    if (msg->conn->pcb.raw != NULL) {
+#if LWIP_IPV6
+      /* ICMPv6 packets should always have checksum calculated by the stack as per RFC 3542 chapter 3.1 */
+      if (NETCONNTYPE_ISIPV6(msg->conn->type) && msg->conn->pcb.raw->protocol == IP6_NEXTH_ICMP6) {
+        msg->conn->pcb.raw->chksum_reqd = 1;
+        msg->conn->pcb.raw->chksum_offset = 2;
+      }
+#endif /* LWIP_IPV6 */
+      raw_recv(msg->conn->pcb.raw, recv_raw, msg->conn);
+    }
+    break;
+#endif /* LWIP_RAW */
+#if LWIP_UDP
+  case NETCONN_UDP:
+    msg->conn->pcb.udp = udp_new_ip_type(iptype);
+    if (msg->conn->pcb.udp != NULL) {
+#if LWIP_UDPLITE
+      if (NETCONNTYPE_ISUDPLITE(msg->conn->type)) {
+        udp_setflags(msg->conn->pcb.udp, UDP_FLAGS_UDPLITE);
+      }
+#endif /* LWIP_UDPLITE */
+      if (NETCONNTYPE_ISUDPNOCHKSUM(msg->conn->type)) {
+        udp_setflags(msg->conn->pcb.udp, UDP_FLAGS_NOCHKSUM);
+      }
+      udp_recv(msg->conn->pcb.udp, recv_udp, msg->conn);
+    }
+    break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+  case NETCONN_TCP:
+    msg->conn->pcb.tcp = tcp_new_ip_type(iptype);
+    if (msg->conn->pcb.tcp != NULL) {
+      setup_tcp(msg->conn);
+    }
+    break;
+#endif /* LWIP_TCP */
+  default:
+    /* Unsupported netconn type, e.g. protocol disabled */
+    msg->err = ERR_VAL;
+    return;
+  }
+  if (msg->conn->pcb.ip == NULL) {
+    msg->err = ERR_MEM;
+  }
+}
+
+/**
+ * Create a new pcb of a specific type inside a netconn.
+ * Called from netconn_new_with_proto_and_callback.
+ *
+ * @param m the api_msg_msg describing the connection type
+ */
+void
+lwip_netconn_do_newconn(void *m)
+{
+  struct api_msg *msg = (struct api_msg*)m;
+
+  msg->err = ERR_OK;
+  if (msg->conn->pcb.tcp == NULL) {
+    pcb_new(msg);
+  }
+  /* Else? This "new" connection already has a PCB allocated. */
+  /* Is this an error condition? Should it be deleted? */
+  /* We currently just are happy and return. */
+
+  TCPIP_APIMSG_ACK(msg);
+}
+
+/**
+ * Create a new netconn (of a specific type) that has a callback function.
+ * The corresponding pcb is NOT created!
+ *
+ * @param t the type of 'connection' to create (@see enum netconn_type)
+ * @param callback a function to call on status changes (RX available, TX'ed)
+ * @return a newly allocated struct netconn or
+ *         NULL on memory error
+ */
+struct netconn*
+netconn_alloc(enum netconn_type t, netconn_callback callback)
+{
+  struct netconn *conn;
+  int size;
+
+  conn = (struct netconn *)memp_malloc(MEMP_NETCONN);
+  if (conn == NULL) {
+    return NULL;
+  }
+
+  conn->last_err = ERR_OK;
+  conn->type = t;
+  conn->pcb.tcp = NULL;
+
+  /* If all sizes are the same, every compiler should optimize this switch to nothing */
+  switch(NETCONNTYPE_GROUP(t)) {
+#if LWIP_RAW
+  case NETCONN_RAW:
+    size = DEFAULT_RAW_RECVMBOX_SIZE;
+    break;
+#endif /* LWIP_RAW */
+#if LWIP_UDP
+  case NETCONN_UDP:
+    size = DEFAULT_UDP_RECVMBOX_SIZE;
+    break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+  case NETCONN_TCP:
+    size = DEFAULT_TCP_RECVMBOX_SIZE;
+    break;
+#endif /* LWIP_TCP */
+  default:
+    LWIP_ASSERT("netconn_alloc: undefined netconn_type", 0);
+    goto free_and_return;
+  }
+
+  if (sys_mbox_new(&conn->recvmbox, size) != ERR_OK) {
+    goto free_and_return;
+  }
+#if !LWIP_NETCONN_SEM_PER_THREAD
+  if (sys_sem_new(&conn->op_completed, 0) != ERR_OK) {
+    sys_mbox_free(&conn->recvmbox);
+    goto free_and_return;
+  }
+#endif
+
+#if LWIP_TCP
+  sys_mbox_set_invalid(&conn->acceptmbox);
+#endif
+  conn->state        = NETCONN_NONE;
+#if LWIP_SOCKET
+  /* initialize socket to -1 since 0 is a valid socket */
+  conn->socket       = -1;
+#endif /* LWIP_SOCKET */
+  conn->callback     = callback;
+#if LWIP_TCP
+  conn->current_msg  = NULL;
+  conn->write_offset = 0;
+#endif /* LWIP_TCP */
+#if LWIP_SO_SNDTIMEO
+  conn->send_timeout = 0;
+#endif /* LWIP_SO_SNDTIMEO */
+#if LWIP_SO_RCVTIMEO
+  conn->recv_timeout = 0;
+#endif /* LWIP_SO_RCVTIMEO */
+#if LWIP_SO_RCVBUF
+  conn->recv_bufsize = RECV_BUFSIZE_DEFAULT;
+  conn->recv_avail   = 0;
+#endif /* LWIP_SO_RCVBUF */
+#if LWIP_SO_LINGER
+  conn->linger = -1;
+#endif /* LWIP_SO_LINGER */
+  conn->flags = 0;
+  return conn;
+free_and_return:
+  memp_free(MEMP_NETCONN, conn);
+  return NULL;
+}
+
+/**
+ * Delete a netconn and all its resources.
+ * The pcb is NOT freed (since we might not be in the right thread context do this).
+ *
+ * @param conn the netconn to free
+ */
+void
+netconn_free(struct netconn *conn)
+{
+  LWIP_ASSERT("PCB must be deallocated outside this function", conn->pcb.tcp == NULL);
+  LWIP_ASSERT("recvmbox must be deallocated before calling this function",
+    !sys_mbox_valid(&conn->recvmbox));
+#if LWIP_TCP
+  LWIP_ASSERT("acceptmbox must be deallocated before calling this function",
+    !sys_mbox_valid(&conn->acceptmbox));
+#endif /* LWIP_TCP */
+
+#if !LWIP_NETCONN_SEM_PER_THREAD
+  sys_sem_free(&conn->op_completed);
+  sys_sem_set_invalid(&conn->op_completed);
+#endif
+
+  memp_free(MEMP_NETCONN, conn);
+}
+
+/**
+ * Delete rcvmbox and acceptmbox of a netconn and free the left-over data in
+ * these mboxes
+ *
+ * @param conn the netconn to free
+ * @bytes_drained bytes drained from recvmbox
+ * @accepts_drained pending connections drained from acceptmbox
+ */
+static void
+netconn_drain(struct netconn *conn)
+{
+  void *mem;
+#if LWIP_TCP
+  struct pbuf *p;
+#endif /* LWIP_TCP */
+
+  /* This runs in tcpip_thread, so we don't need to lock against rx packets */
+
+  /* Delete and drain the recvmbox. */
+  if (sys_mbox_valid(&conn->recvmbox)) {
+    while (sys_mbox_tryfetch(&conn->recvmbox, &mem) != SYS_MBOX_EMPTY) {
+#if LWIP_TCP
+      if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP) {
+        if (mem != NULL) {
+          p = (struct pbuf*)mem;
+          /* pcb might be set to NULL already by err_tcp() */
+          if (conn->pcb.tcp != NULL) {
+            tcp_recved(conn->pcb.tcp, p->tot_len);
+          }
+          pbuf_free(p);
+        }
+      } else
+#endif /* LWIP_TCP */
+      {
+        netbuf_delete((struct netbuf *)mem);
+      }
+    }
+    sys_mbox_free(&conn->recvmbox);
+    sys_mbox_set_invalid(&conn->recvmbox);
+  }
+
+  /* Delete and drain the acceptmbox. */
+#if LWIP_TCP
+  if (sys_mbox_valid(&conn->acceptmbox)) {
+    while (sys_mbox_tryfetch(&conn->acceptmbox, &mem) != SYS_MBOX_EMPTY) {
+      if (mem != &netconn_aborted) {
+        struct netconn *newconn = (struct netconn *)mem;
+        /* Only tcp pcbs have an acceptmbox, so no need to check conn->type */
+        /* pcb might be set to NULL already by err_tcp() */
+        /* drain recvmbox */
+        netconn_drain(newconn);
+        if (newconn->pcb.tcp != NULL) {
+          tcp_abort(newconn->pcb.tcp);
+          newconn->pcb.tcp = NULL;
+        }
+        netconn_free(newconn);
+      }
+    }
+    sys_mbox_free(&conn->acceptmbox);
+    sys_mbox_set_invalid(&conn->acceptmbox);
+  }
+#endif /* LWIP_TCP */
+}
+
+#if LWIP_TCP
+/**
+ * Internal helper function to close a TCP netconn: since this sometimes
+ * doesn't work at the first attempt, this function is called from multiple
+ * places.
+ *
+ * @param conn the TCP netconn to close
+ */
+static err_t
+lwip_netconn_do_close_internal(struct netconn *conn  WRITE_DELAYED_PARAM)
+{
+  err_t err;
+  u8_t shut, shut_rx, shut_tx, close;
+  u8_t close_finished = 0;
+  struct tcp_pcb* tpcb;
+#if LWIP_SO_LINGER
+  u8_t linger_wait_required = 0;
+#endif /* LWIP_SO_LINGER */
+
+  LWIP_ASSERT("invalid conn", (conn != NULL));
+  LWIP_ASSERT("this is for tcp netconns only", (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP));
+  LWIP_ASSERT("conn must be in state NETCONN_CLOSE", (conn->state == NETCONN_CLOSE));
+  LWIP_ASSERT("pcb already closed", (conn->pcb.tcp != NULL));
+  LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL);
+
+  tpcb = conn->pcb.tcp;
+  shut = conn->current_msg->msg.sd.shut;
+  shut_rx = shut & NETCONN_SHUT_RD;
+  shut_tx = shut & NETCONN_SHUT_WR;
+  /* shutting down both ends is the same as closing
+     (also if RD or WR side was shut down before already) */
+  if (shut == NETCONN_SHUT_RDWR) {
+    close = 1;
+  } else if (shut_rx &&
+             ((tpcb->state == FIN_WAIT_1) ||
+              (tpcb->state == FIN_WAIT_2) ||
+              (tpcb->state == CLOSING))) {
+    close = 1;
+  } else if (shut_tx && ((tpcb->flags & TF_RXCLOSED) != 0)) {
+    close = 1;
+  } else {
+    close = 0;
+  }
+
+  /* Set back some callback pointers */
+  if (close) {
+    tcp_arg(tpcb, NULL);
+  }
+  if (tpcb->state == LISTEN) {
+    tcp_accept(tpcb, NULL);
+  } else {
+    /* some callbacks have to be reset if tcp_close is not successful */
+    if (shut_rx) {
+      tcp_recv(tpcb, NULL);
+      tcp_accept(tpcb, NULL);
+    }
+    if (shut_tx) {
+      tcp_sent(tpcb, NULL);
+    }
+    if (close) {
+      tcp_poll(tpcb, NULL, 0);
+      tcp_err(tpcb, NULL);
+    }
+  }
+  /* Try to close the connection */
+  if (close) {
+#if LWIP_SO_LINGER
+    /* check linger possibilites before calling tcp_close */
+    err = ERR_OK;
+    /* linger enabled/required at all? (i.e. is there untransmitted data left?) */
+    if ((conn->linger >= 0) && (conn->pcb.tcp->unsent || conn->pcb.tcp->unacked)) {
+      if ((conn->linger == 0)) {
+        /* data left but linger prevents waiting */
+        tcp_abort(tpcb);
+        tpcb = NULL;
+      } else if (conn->linger > 0) {
+        /* data left and linger says we should wait */
+        if (netconn_is_nonblocking(conn)) {
+          /* data left on a nonblocking netconn -> cannot linger */
+          err = ERR_WOULDBLOCK;
+        } else if ((s32_t)(sys_now() - conn->current_msg->msg.sd.time_started) >=
+          (conn->linger * 1000)) {
+          /* data left but linger timeout has expired (this happens on further
+             calls to this function through poll_tcp */
+          tcp_abort(tpcb);
+          tpcb = NULL;
+        } else {
+          /* data left -> need to wait for ACK after successful close */
+          linger_wait_required = 1;
+        }
+      }
+    }
+    if ((err == ERR_OK) && (tpcb != NULL))
+#endif /* LWIP_SO_LINGER */
+    {
+      err = tcp_close(tpcb);
+    }
+  } else {
+    err = tcp_shutdown(tpcb, shut_rx, shut_tx);
+  }
+  if (err == ERR_OK) {
+    close_finished = 1;
+#if LWIP_SO_LINGER
+    if (linger_wait_required) {
+      /* wait for ACK of all unsent/unacked data by just getting called again */
+      close_finished = 0;
+      err = ERR_INPROGRESS;
+    }
+#endif /* LWIP_SO_LINGER */
+  } else {
+    if (err == ERR_MEM) {
+      /* Closing failed because of memory shortage, try again later. Even for
+         nonblocking netconns, we have to wait since no standard socket application
+         is prepared for close failing because of resource shortage.
+         Check the timeout: this is kind of an lwip addition to the standard sockets:
+         we wait for some time when failing to allocate a segment for the FIN */
+#if LWIP_SO_SNDTIMEO || LWIP_SO_LINGER
+      s32_t close_timeout = LWIP_TCP_CLOSE_TIMEOUT_MS_DEFAULT;
+#if LWIP_SO_SNDTIMEO
+      if (conn->send_timeout > 0) {
+        close_timeout = conn->send_timeout;
+      }
+#endif /* LWIP_SO_SNDTIMEO */
+#if LWIP_SO_LINGER
+      if (conn->linger >= 0) {
+        /* use linger timeout (seconds) */
+        close_timeout = conn->linger * 1000U;
+      }
+#endif
+      if ((s32_t)(sys_now() - conn->current_msg->msg.sd.time_started) >= close_timeout) {
+#else /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
+      if (conn->current_msg->msg.sd.polls_left == 0) {
+#endif /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
+        close_finished = 1;
+        if (close) {
+          /* in this case, we want to RST the connection */
+          tcp_abort(tpcb);
+          err = ERR_OK;
+        }
+      }
+    } else {
+      /* Closing failed for a non-memory error: give up */
+      close_finished = 1;
+    }
+  }
+  if (close_finished) {
+    /* Closing done (succeeded, non-memory error, nonblocking error or timeout) */
+    sys_sem_t* op_completed_sem = LWIP_API_MSG_SEM(conn->current_msg);
+    conn->current_msg->err = err;
+    conn->current_msg = NULL;
+    conn->state = NETCONN_NONE;
+    if (err == ERR_OK) {
+      if (close) {
+        /* Set back some callback pointers as conn is going away */
+        conn->pcb.tcp = NULL;
+        /* Trigger select() in socket layer. Make sure everybody notices activity
+         on the connection, error first! */
+        API_EVENT(conn, NETCONN_EVT_ERROR, 0);
+      }
+      if (shut_rx) {
+        API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
+      }
+      if (shut_tx) {
+        API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0);
+      }
+    }
+    NETCONN_SET_SAFE_ERR(conn, err);
+#if LWIP_TCPIP_CORE_LOCKING
+    if (delayed)
+#endif
+    {
+      /* wake up the application task */
+      sys_sem_signal(op_completed_sem);
+    }
+    return ERR_OK;
+  }
+  if (!close_finished) {
+    /* Closing failed and we want to wait: restore some of the callbacks */
+    /* Closing of listen pcb will never fail! */
+    LWIP_ASSERT("Closing a listen pcb may not fail!", (tpcb->state != LISTEN));
+    if (shut_tx) {
+      tcp_sent(tpcb, sent_tcp);
+    }
+    /* when waiting for close, set up poll interval to 500ms */
+    tcp_poll(tpcb, poll_tcp, 1);
+    tcp_err(tpcb, err_tcp);
+    tcp_arg(tpcb, conn);
+    /* don't restore recv callback: we don't want to receive any more data */
+  }
+  /* If closing didn't succeed, we get called again either
+     from poll_tcp or from sent_tcp */
+  LWIP_ASSERT("err != ERR_OK", err != ERR_OK);
+  return err;
+}
+#endif /* LWIP_TCP */
+
+/**
+ * Delete the pcb inside a netconn.
+ * Called from netconn_delete.
+ *
+ * @param m the api_msg_msg pointing to the connection
+ */
+void
+lwip_netconn_do_delconn(void *m)
+{
+  struct api_msg *msg = (struct api_msg*)m;
+
+  enum netconn_state state = msg->conn->state;
+  LWIP_ASSERT("netconn state error", /* this only happens for TCP netconns */
+    (state == NETCONN_NONE) || (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP));
+#if LWIP_NETCONN_FULLDUPLEX
+  /* In full duplex mode, blocking write/connect is aborted with ERR_CLSD */
+  if (state != NETCONN_NONE) {
+    if ((state == NETCONN_WRITE) ||
+        ((state == NETCONN_CONNECT) && !IN_NONBLOCKING_CONNECT(msg->conn))) {
+      /* close requested, abort running write/connect */
+      sys_sem_t* op_completed_sem;
+      LWIP_ASSERT("msg->conn->current_msg != NULL", msg->conn->current_msg != NULL);
+      op_completed_sem = LWIP_API_MSG_SEM(msg->conn->current_msg);
+      msg->conn->current_msg->err = ERR_CLSD;
+      msg->conn->current_msg = NULL;
+      msg->conn->write_offset = 0;
+      msg->conn->state = NETCONN_NONE;
+      NETCONN_SET_SAFE_ERR(msg->conn, ERR_CLSD);
+      sys_sem_signal(op_completed_sem);
+    }
+  }
+#else /* LWIP_NETCONN_FULLDUPLEX */
+  if (((state != NETCONN_NONE) &&
+       (state != NETCONN_LISTEN) &&
+       (state != NETCONN_CONNECT)) ||
+      ((state == NETCONN_CONNECT) && !IN_NONBLOCKING_CONNECT(msg->conn))) {
+    /* This means either a blocking write or blocking connect is running
+       (nonblocking write returns and sets state to NONE) */
+    msg->err = ERR_INPROGRESS;
+  } else
+#endif /* LWIP_NETCONN_FULLDUPLEX */
+  {
+    LWIP_ASSERT("blocking connect in progress",
+      (state != NETCONN_CONNECT) || IN_NONBLOCKING_CONNECT(msg->conn));
+    msg->err = ERR_OK;
+    /* Drain and delete mboxes */
+    netconn_drain(msg->conn);
+
+    if (msg->conn->pcb.tcp != NULL) {
+
+      switch (NETCONNTYPE_GROUP(msg->conn->type)) {
+#if LWIP_RAW
+      case NETCONN_RAW:
+        raw_remove(msg->conn->pcb.raw);
+        break;
+#endif /* LWIP_RAW */
+#if LWIP_UDP
+      case NETCONN_UDP:
+        msg->conn->pcb.udp->recv_arg = NULL;
+        udp_remove(msg->conn->pcb.udp);
+        break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+      case NETCONN_TCP:
+        LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL &&
+          msg->conn->write_offset == 0);
+        msg->conn->state = NETCONN_CLOSE;
+        msg->msg.sd.shut = NETCONN_SHUT_RDWR;
+        msg->conn->current_msg = msg;
+#if LWIP_TCPIP_CORE_LOCKING
+        if (lwip_netconn_do_close_internal(msg->conn, 0) != ERR_OK) {
+          LWIP_ASSERT("state!", msg->conn->state == NETCONN_CLOSE);
+          UNLOCK_TCPIP_CORE();
+          sys_arch_sem_wait(LWIP_API_MSG_SEM(msg), 0);
+          LOCK_TCPIP_CORE();
+          LWIP_ASSERT("state!", msg->conn->state == NETCONN_NONE);
+        }
+#else /* LWIP_TCPIP_CORE_LOCKING */
+        lwip_netconn_do_close_internal(msg->conn);
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+        /* API_EVENT is called inside lwip_netconn_do_close_internal, before releasing
+           the application thread, so we can return at this point! */
+        return;
+#endif /* LWIP_TCP */
+      default:
+        break;
+      }
+      msg->conn->pcb.tcp = NULL;
+    }
+    /* tcp netconns don't come here! */
+
+    /* @todo: this lets select make the socket readable and writable,
+       which is wrong! errfd instead? */
+    API_EVENT(msg->conn, NETCONN_EVT_RCVPLUS, 0);
+    API_EVENT(msg->conn, NETCONN_EVT_SENDPLUS, 0);
+  }
+  if (sys_sem_valid(LWIP_API_MSG_SEM(msg))) {
+    TCPIP_APIMSG_ACK(msg);
+  }
+}
+
+/**
+ * Bind a pcb contained in a netconn
+ * Called from netconn_bind.
+ *
+ * @param m the api_msg_msg pointing to the connection and containing
+ *          the IP address and port to bind to
+ */
+void
+lwip_netconn_do_bind(void *m)
+{
+  struct api_msg *msg = (struct api_msg*)m;
+
+  if (ERR_IS_FATAL(msg->conn->last_err)) {
+    msg->err = msg->conn->last_err;
+  } else {
+    msg->err = ERR_VAL;
+    if (msg->conn->pcb.tcp != NULL) {
+      switch (NETCONNTYPE_GROUP(msg->conn->type)) {
+#if LWIP_RAW
+      case NETCONN_RAW:
+        msg->err = raw_bind(msg->conn->pcb.raw, API_EXPR_REF(msg->msg.bc.ipaddr));
+        break;
+#endif /* LWIP_RAW */
+#if LWIP_UDP
+      case NETCONN_UDP:
+        msg->err = udp_bind(msg->conn->pcb.udp, API_EXPR_REF(msg->msg.bc.ipaddr), msg->msg.bc.port);
+        break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+      case NETCONN_TCP:
+        msg->err = tcp_bind(msg->conn->pcb.tcp, API_EXPR_REF(msg->msg.bc.ipaddr), msg->msg.bc.port);
+        break;
+#endif /* LWIP_TCP */
+      default:
+        break;
+      }
+    }
+  }
+  TCPIP_APIMSG_ACK(msg);
+}
+
+#if LWIP_TCP
+/**
+ * TCP callback function if a connection (opened by tcp_connect/lwip_netconn_do_connect) has
+ * been established (or reset by the remote host).
+ *
+ * @see tcp.h (struct tcp_pcb.connected) for parameters and return values
+ */
+static err_t
+lwip_netconn_do_connected(void *arg, struct tcp_pcb *pcb, err_t err)
+{
+  struct netconn *conn;
+  int was_blocking;
+  sys_sem_t* op_completed_sem = NULL;
+
+  LWIP_UNUSED_ARG(pcb);
+
+  conn = (struct netconn *)arg;
+
+  if (conn == NULL) {
+    return ERR_VAL;
+  }
+
+  LWIP_ASSERT("conn->state == NETCONN_CONNECT", conn->state == NETCONN_CONNECT);
+  LWIP_ASSERT("(conn->current_msg != NULL) || conn->in_non_blocking_connect",
+    (conn->current_msg != NULL) || IN_NONBLOCKING_CONNECT(conn));
+
+  if (conn->current_msg != NULL) {
+    conn->current_msg->err = err;
+    op_completed_sem = LWIP_API_MSG_SEM(conn->current_msg);
+  }
+  if ((NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP) && (err == ERR_OK)) {
+    setup_tcp(conn);
+  }
+  was_blocking = !IN_NONBLOCKING_CONNECT(conn);
+  SET_NONBLOCKING_CONNECT(conn, 0);
+  LWIP_ASSERT("blocking connect state error",
+    (was_blocking && op_completed_sem != NULL) ||
+    (!was_blocking && op_completed_sem == NULL));
+  conn->current_msg = NULL;
+  conn->state = NETCONN_NONE;
+  NETCONN_SET_SAFE_ERR(conn, ERR_OK);
+  API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0);
+
+  if (was_blocking) {
+    sys_sem_signal(op_completed_sem);
+  }
+  return ERR_OK;
+}
+#endif /* LWIP_TCP */
+
+/**
+ * Connect a pcb contained inside a netconn
+ * Called from netconn_connect.
+ *
+ * @param m the api_msg_msg pointing to the connection and containing
+ *          the IP address and port to connect to
+ */
+void
+lwip_netconn_do_connect(void *m)
+{
+  struct api_msg *msg = (struct api_msg*)m;
+
+  if (msg->conn->pcb.tcp == NULL) {
+    /* This may happen when calling netconn_connect() a second time */
+    msg->err = ERR_CLSD;
+  } else {
+    switch (NETCONNTYPE_GROUP(msg->conn->type)) {
+#if LWIP_RAW
+    case NETCONN_RAW:
+      msg->err = raw_connect(msg->conn->pcb.raw, API_EXPR_REF(msg->msg.bc.ipaddr));
+      break;
+#endif /* LWIP_RAW */
+#if LWIP_UDP
+    case NETCONN_UDP:
+      msg->err = udp_connect(msg->conn->pcb.udp, API_EXPR_REF(msg->msg.bc.ipaddr), msg->msg.bc.port);
+      break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+    case NETCONN_TCP:
+      /* Prevent connect while doing any other action. */
+      if (msg->conn->state == NETCONN_CONNECT) {
+        msg->err = ERR_ALREADY;
+      } else if (msg->conn->state != NETCONN_NONE) {
+        msg->err = ERR_ISCONN;
+      } else {
+        setup_tcp(msg->conn);
+        msg->err = tcp_connect(msg->conn->pcb.tcp, API_EXPR_REF(msg->msg.bc.ipaddr),
+          msg->msg.bc.port, lwip_netconn_do_connected);
+        if (msg->err == ERR_OK) {
+          u8_t non_blocking = netconn_is_nonblocking(msg->conn);
+          msg->conn->state = NETCONN_CONNECT;
+          SET_NONBLOCKING_CONNECT(msg->conn, non_blocking);
+          if (non_blocking) {
+            msg->err = ERR_INPROGRESS;
+          } else {
+            msg->conn->current_msg = msg;
+            /* sys_sem_signal() is called from lwip_netconn_do_connected (or err_tcp()),
+               when the connection is established! */
+#if LWIP_TCPIP_CORE_LOCKING
+            LWIP_ASSERT("state!", msg->conn->state == NETCONN_CONNECT);
+            UNLOCK_TCPIP_CORE();
+            sys_arch_sem_wait(LWIP_API_MSG_SEM(msg), 0);
+            LOCK_TCPIP_CORE();
+            LWIP_ASSERT("state!", msg->conn->state != NETCONN_CONNECT);
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+            return;
+          }
+        }
+      }
+      break;
+#endif /* LWIP_TCP */
+    default:
+      LWIP_ERROR("Invalid netconn type", 0, do{ msg->err = ERR_VAL; }while(0));
+      break;
+    }
+  }
+  /* For all other protocols, netconn_connect() calls TCPIP_APIMSG(),
+     so use TCPIP_APIMSG_ACK() here. */
+  TCPIP_APIMSG_ACK(msg);
+}
+
+/**
+ * Disconnect a pcb contained inside a netconn
+ * Only used for UDP netconns.
+ * Called from netconn_disconnect.
+ *
+ * @param m the api_msg_msg pointing to the connection to disconnect
+ */
+void
+lwip_netconn_do_disconnect(void *m)
+{
+  struct api_msg *msg = (struct api_msg*)m;
+
+#if LWIP_UDP
+  if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_UDP) {
+    udp_disconnect(msg->conn->pcb.udp);
+    msg->err = ERR_OK;
+  } else
+#endif /* LWIP_UDP */
+  {
+    msg->err = ERR_VAL;
+  }
+  TCPIP_APIMSG_ACK(msg);
+}
+
+#if LWIP_TCP
+/**
+ * Set a TCP pcb contained in a netconn into listen mode
+ * Called from netconn_listen.
+ *
+ * @param m the api_msg_msg pointing to the connection
+ */
+void
+lwip_netconn_do_listen(void *m)
+{
+  struct api_msg *msg = (struct api_msg*)m;
+
+  if (ERR_IS_FATAL(msg->conn->last_err)) {
+    msg->err = msg->conn->last_err;
+  } else {
+    msg->err = ERR_CONN;
+    if (msg->conn->pcb.tcp != NULL) {
+      if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) {
+        if (msg->conn->state == NETCONN_NONE) {
+          struct tcp_pcb* lpcb;
+          if (msg->conn->pcb.tcp->state != CLOSED) {
+            /* connection is not closed, cannot listen */
+            msg->err = ERR_VAL;
+          } else {
+            err_t err;
+            u8_t backlog;
+#if TCP_LISTEN_BACKLOG
+            backlog = msg->msg.lb.backlog;
+#else  /* TCP_LISTEN_BACKLOG */
+            backlog = TCP_DEFAULT_LISTEN_BACKLOG;
+#endif /* TCP_LISTEN_BACKLOG */
+#if LWIP_IPV4 && LWIP_IPV6
+            /* "Socket API like" dual-stack support: If IP to listen to is IP6_ADDR_ANY,
+             * and NETCONN_FLAG_IPV6_V6ONLY is NOT set, use IP_ANY_TYPE to listen
+             */
+            if (ip_addr_cmp(&msg->conn->pcb.ip->local_ip, IP6_ADDR_ANY) &&
+                (netconn_get_ipv6only(msg->conn) == 0)) {
+              /* change PCB type to IPADDR_TYPE_ANY */
+              IP_SET_TYPE_VAL(msg->conn->pcb.tcp->local_ip,  IPADDR_TYPE_ANY);
+              IP_SET_TYPE_VAL(msg->conn->pcb.tcp->remote_ip, IPADDR_TYPE_ANY);
+            }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
+            lpcb = tcp_listen_with_backlog_and_err(msg->conn->pcb.tcp, backlog, &err);
+
+            if (lpcb == NULL) {
+              /* in this case, the old pcb is still allocated */
+              msg->err = err;
+            } else {
+              /* delete the recvmbox and allocate the acceptmbox */
+              if (sys_mbox_valid(&msg->conn->recvmbox)) {
+                /** @todo: should we drain the recvmbox here? */
+                sys_mbox_free(&msg->conn->recvmbox);
+                sys_mbox_set_invalid(&msg->conn->recvmbox);
+              }
+              msg->err = ERR_OK;
+              if (!sys_mbox_valid(&msg->conn->acceptmbox)) {
+                msg->err = sys_mbox_new(&msg->conn->acceptmbox, DEFAULT_ACCEPTMBOX_SIZE);
+              }
+              if (msg->err == ERR_OK) {
+                msg->conn->state = NETCONN_LISTEN;
+                msg->conn->pcb.tcp = lpcb;
+                tcp_arg(msg->conn->pcb.tcp, msg->conn);
+                tcp_accept(msg->conn->pcb.tcp, accept_function);
+              } else {
+                /* since the old pcb is already deallocated, free lpcb now */
+                tcp_close(lpcb);
+                msg->conn->pcb.tcp = NULL;
+              }
+            }
+          }
+        } else if (msg->conn->state == NETCONN_LISTEN) {
+          /* already listening, allow updating of the backlog */
+          msg->err = ERR_OK;
+          tcp_backlog_set(msg->conn->pcb.tcp, msg->msg.lb.backlog);
+        }
+      } else {
+        msg->err = ERR_ARG;
+      }
+    }
+  }
+  TCPIP_APIMSG_ACK(msg);
+}
+#endif /* LWIP_TCP */
+
+/**
+ * Send some data on a RAW or UDP pcb contained in a netconn
+ * Called from netconn_send
+ *
+ * @param m the api_msg_msg pointing to the connection
+ */
+void
+lwip_netconn_do_send(void *m)
+{
+  struct api_msg *msg = (struct api_msg*)m;
+
+  if (ERR_IS_FATAL(msg->conn->last_err)) {
+    msg->err = msg->conn->last_err;
+  } else {
+    msg->err = ERR_CONN;
+    if (msg->conn->pcb.tcp != NULL) {
+      switch (NETCONNTYPE_GROUP(msg->conn->type)) {
+#if LWIP_RAW
+      case NETCONN_RAW:
+        if (ip_addr_isany(&msg->msg.b->addr) || IP_IS_ANY_TYPE_VAL(msg->msg.b->addr)) {
+          msg->err = raw_send(msg->conn->pcb.raw, msg->msg.b->p);
+        } else {
+          msg->err = raw_sendto(msg->conn->pcb.raw, msg->msg.b->p, &msg->msg.b->addr);
+        }
+        break;
+#endif
+#if LWIP_UDP
+      case NETCONN_UDP:
+#if LWIP_CHECKSUM_ON_COPY
+        if (ip_addr_isany(&msg->msg.b->addr) || IP_IS_ANY_TYPE_VAL(msg->msg.b->addr)) {
+          msg->err = udp_send_chksum(msg->conn->pcb.udp, msg->msg.b->p,
+            msg->msg.b->flags & NETBUF_FLAG_CHKSUM, msg->msg.b->toport_chksum);
+        } else {
+          msg->err = udp_sendto_chksum(msg->conn->pcb.udp, msg->msg.b->p,
+            &msg->msg.b->addr, msg->msg.b->port,
+            msg->msg.b->flags & NETBUF_FLAG_CHKSUM, msg->msg.b->toport_chksum);
+        }
+#else /* LWIP_CHECKSUM_ON_COPY */
+        if (ip_addr_isany_val(msg->msg.b->addr) || IP_IS_ANY_TYPE_VAL(msg->msg.b->addr)) {
+          msg->err = udp_send(msg->conn->pcb.udp, msg->msg.b->p);
+        } else {
+          msg->err = udp_sendto(msg->conn->pcb.udp, msg->msg.b->p, &msg->msg.b->addr, msg->msg.b->port);
+        }
+#endif /* LWIP_CHECKSUM_ON_COPY */
+        break;
+#endif /* LWIP_UDP */
+      default:
+        break;
+      }
+    }
+  }
+  TCPIP_APIMSG_ACK(msg);
+}
+
+#if LWIP_TCP
+/**
+ * Indicate data has been received from a TCP pcb contained in a netconn
+ * Called from netconn_recv
+ *
+ * @param m the api_msg_msg pointing to the connection
+ */
+void
+lwip_netconn_do_recv(void *m)
+{
+  struct api_msg *msg = (struct api_msg*)m;
+
+  msg->err = ERR_OK;
+  if (msg->conn->pcb.tcp != NULL) {
+    if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) {
+      u32_t remaining = msg->msg.r.len;
+      do {
+        u16_t recved = (remaining > 0xffff) ? 0xffff : (u16_t)remaining;
+        tcp_recved(msg->conn->pcb.tcp, recved);
+        remaining -= recved;
+      } while (remaining != 0);
+    }
+  }
+  TCPIP_APIMSG_ACK(msg);
+}
+
+#if TCP_LISTEN_BACKLOG
+/** Indicate that a TCP pcb has been accepted
+ * Called from netconn_accept
+ *
+ * @param m the api_msg_msg pointing to the connection
+ */
+void
+lwip_netconn_do_accepted(void *m)
+{
+  struct api_msg *msg = (struct api_msg*)m;
+
+  msg->err = ERR_OK;
+  if (msg->conn->pcb.tcp != NULL) {
+    if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) {
+      tcp_backlog_accepted(msg->conn->pcb.tcp);
+    }
+  }
+  TCPIP_APIMSG_ACK(msg);
+}
+#endif /* TCP_LISTEN_BACKLOG */
+
+/**
+ * See if more data needs to be written from a previous call to netconn_write.
+ * Called initially from lwip_netconn_do_write. If the first call can't send all data
+ * (because of low memory or empty send-buffer), this function is called again
+ * from sent_tcp() or poll_tcp() to send more data. If all data is sent, the
+ * blocking application thread (waiting in netconn_write) is released.
+ *
+ * @param conn netconn (that is currently in state NETCONN_WRITE) to process
+ * @return ERR_OK
+ *         ERR_MEM if LWIP_TCPIP_CORE_LOCKING=1 and sending hasn't yet finished
+ */
+static err_t
+lwip_netconn_do_writemore(struct netconn *conn  WRITE_DELAYED_PARAM)
+{
+  err_t err;
+  const void *dataptr;
+  u16_t len, available;
+  u8_t write_finished = 0;
+  size_t diff;
+  u8_t dontblock;
+  u8_t apiflags;
+
+  LWIP_ASSERT("conn != NULL", conn != NULL);
+  LWIP_ASSERT("conn->state == NETCONN_WRITE", (conn->state == NETCONN_WRITE));
+  LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL);
+  LWIP_ASSERT("conn->pcb.tcp != NULL", conn->pcb.tcp != NULL);
+  LWIP_ASSERT("conn->write_offset < conn->current_msg->msg.w.len",
+    conn->write_offset < conn->current_msg->msg.w.len);
+
+  apiflags = conn->current_msg->msg.w.apiflags;
+  dontblock = netconn_is_nonblocking(conn) || (apiflags & NETCONN_DONTBLOCK);
+
+#if LWIP_SO_SNDTIMEO
+  if ((conn->send_timeout != 0) &&
+      ((s32_t)(sys_now() - conn->current_msg->msg.w.time_started) >= conn->send_timeout)) {
+    write_finished = 1;
+    if (conn->write_offset == 0) {
+      /* nothing has been written */
+      err = ERR_WOULDBLOCK;
+      conn->current_msg->msg.w.len = 0;
+    } else {
+      /* partial write */
+      err = ERR_OK;
+      conn->current_msg->msg.w.len = conn->write_offset;
+      conn->write_offset = 0;
+    }
+  } else
+#endif /* LWIP_SO_SNDTIMEO */
+  {
+    dataptr = (const u8_t*)conn->current_msg->msg.w.dataptr + conn->write_offset;
+    diff = conn->current_msg->msg.w.len - conn->write_offset;
+    if (diff > 0xffffUL) { /* max_u16_t */
+      len = 0xffff;
+      apiflags |= TCP_WRITE_FLAG_MORE;
+    } else {
+      len = (u16_t)diff;
+    }
+    available = tcp_sndbuf(conn->pcb.tcp);
+    if (available < len) {
+      /* don't try to write more than sendbuf */
+      len = available;
+      if (dontblock) {
+        if (!len) {
+          err = ERR_WOULDBLOCK;
+          goto err_mem;
+        }
+      } else {
+        apiflags |= TCP_WRITE_FLAG_MORE;
+      }
+    }
+    LWIP_ASSERT("lwip_netconn_do_writemore: invalid length!", ((conn->write_offset + len) <= conn->current_msg->msg.w.len));
+    err = tcp_write(conn->pcb.tcp, dataptr, len, apiflags);
+    /* if OK or memory error, check available space */
+    if ((err == ERR_OK) || (err == ERR_MEM)) {
+err_mem:
+      if (dontblock && (len < conn->current_msg->msg.w.len)) {
+        /* non-blocking write did not write everything: mark the pcb non-writable
+           and let poll_tcp check writable space to mark the pcb writable again */
+        API_EVENT(conn, NETCONN_EVT_SENDMINUS, len);
+        conn->flags |= NETCONN_FLAG_CHECK_WRITESPACE;
+      } else if ((tcp_sndbuf(conn->pcb.tcp) <= TCP_SNDLOWAT) ||
+                 (tcp_sndqueuelen(conn->pcb.tcp) >= TCP_SNDQUEUELOWAT)) {
+        /* The queued byte- or pbuf-count exceeds the configured low-water limit,
+           let select mark this pcb as non-writable. */
+        API_EVENT(conn, NETCONN_EVT_SENDMINUS, len);
+      }
+    }
+
+    if (err == ERR_OK) {
+      err_t out_err;
+      conn->write_offset += len;
+      if ((conn->write_offset == conn->current_msg->msg.w.len) || dontblock) {
+        /* return sent length */
+        conn->current_msg->msg.w.len = conn->write_offset;
+        /* everything was written */
+        write_finished = 1;
+      }
+      out_err = tcp_output(conn->pcb.tcp);
+      if (ERR_IS_FATAL(out_err) || (out_err == ERR_RTE)) {
+        /* If tcp_output fails with fatal error or no route is found,
+           don't try writing any more but return the error
+           to the application thread. */
+        err = out_err;
+        write_finished = 1;
+        conn->current_msg->msg.w.len = 0;
+      }
+    } else if (err == ERR_MEM) {
+      /* If ERR_MEM, we wait for sent_tcp or poll_tcp to be called.
+         For blocking sockets, we do NOT return to the application
+         thread, since ERR_MEM is only a temporary error! Non-blocking
+         will remain non-writable until sent_tcp/poll_tcp is called */
+
+      /* tcp_write returned ERR_MEM, try tcp_output anyway */
+      err_t out_err = tcp_output(conn->pcb.tcp);
+      if (ERR_IS_FATAL(out_err) || (out_err == ERR_RTE)) {
+        /* If tcp_output fails with fatal error or no route is found,
+           don't try writing any more but return the error
+           to the application thread. */
+        err = out_err;
+        write_finished = 1;
+        conn->current_msg->msg.w.len = 0;
+      } else if (dontblock) {
+        /* non-blocking write is done on ERR_MEM */
+        err = ERR_WOULDBLOCK;
+        write_finished = 1;
+        conn->current_msg->msg.w.len = 0;
+      }
+    } else {
+      /* On errors != ERR_MEM, we don't try writing any more but return
+         the error to the application thread. */
+      write_finished = 1;
+      conn->current_msg->msg.w.len = 0;
+    }
+  }
+  if (write_finished) {
+    /* everything was written: set back connection state
+       and back to application task */
+    sys_sem_t* op_completed_sem = LWIP_API_MSG_SEM(conn->current_msg);
+    conn->current_msg->err = err;
+    conn->current_msg = NULL;
+    conn->write_offset = 0;
+    conn->state = NETCONN_NONE;
+    NETCONN_SET_SAFE_ERR(conn, err);
+#if LWIP_TCPIP_CORE_LOCKING
+    if (delayed)
+#endif
+    {
+      sys_sem_signal(op_completed_sem);
+    }
+  }
+#if LWIP_TCPIP_CORE_LOCKING
+  else {
+    return ERR_MEM;
+  }
+#endif
+  return ERR_OK;
+}
+#endif /* LWIP_TCP */
+
+/**
+ * Send some data on a TCP pcb contained in a netconn
+ * Called from netconn_write
+ *
+ * @param m the api_msg_msg pointing to the connection
+ */
+void
+lwip_netconn_do_write(void *m)
+{
+  struct api_msg *msg = (struct api_msg*)m;
+
+  if (ERR_IS_FATAL(msg->conn->last_err)) {
+    msg->err = msg->conn->last_err;
+  } else {
+    if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) {
+#if LWIP_TCP
+      if (msg->conn->state != NETCONN_NONE) {
+        /* netconn is connecting, closing or in blocking write */
+        msg->err = ERR_INPROGRESS;
+      } else if (msg->conn->pcb.tcp != NULL) {
+        msg->conn->state = NETCONN_WRITE;
+        /* set all the variables used by lwip_netconn_do_writemore */
+        LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL &&
+          msg->conn->write_offset == 0);
+        LWIP_ASSERT("msg->msg.w.len != 0", msg->msg.w.len != 0);
+        msg->conn->current_msg = msg;
+        msg->conn->write_offset = 0;
+#if LWIP_TCPIP_CORE_LOCKING
+        if (lwip_netconn_do_writemore(msg->conn, 0) != ERR_OK) {
+          LWIP_ASSERT("state!", msg->conn->state == NETCONN_WRITE);
+          UNLOCK_TCPIP_CORE();
+          sys_arch_sem_wait(LWIP_API_MSG_SEM(msg), 0);
+          LOCK_TCPIP_CORE();
+          LWIP_ASSERT("state!", msg->conn->state != NETCONN_WRITE);
+        }
+#else /* LWIP_TCPIP_CORE_LOCKING */
+        lwip_netconn_do_writemore(msg->conn);
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+        /* for both cases: if lwip_netconn_do_writemore was called, don't ACK the APIMSG
+           since lwip_netconn_do_writemore ACKs it! */
+        return;
+      } else {
+        msg->err = ERR_CONN;
+      }
+#else /* LWIP_TCP */
+      msg->err = ERR_VAL;
+#endif /* LWIP_TCP */
+#if (LWIP_UDP || LWIP_RAW)
+    } else {
+      msg->err = ERR_VAL;
+#endif /* (LWIP_UDP || LWIP_RAW) */
+    }
+  }
+  TCPIP_APIMSG_ACK(msg);
+}
+
+/**
+ * Return a connection's local or remote address
+ * Called from netconn_getaddr
+ *
+ * @param m the api_msg_msg pointing to the connection
+ */
+void
+lwip_netconn_do_getaddr(void *m)
+{
+  struct api_msg *msg = (struct api_msg*)m;
+
+  if (msg->conn->pcb.ip != NULL) {
+    if (msg->msg.ad.local) {
+      ip_addr_copy(API_EXPR_DEREF(msg->msg.ad.ipaddr),
+        msg->conn->pcb.ip->local_ip);
+    } else {
+      ip_addr_copy(API_EXPR_DEREF(msg->msg.ad.ipaddr),
+        msg->conn->pcb.ip->remote_ip);
+    }
+
+    msg->err = ERR_OK;
+    switch (NETCONNTYPE_GROUP(msg->conn->type)) {
+#if LWIP_RAW
+    case NETCONN_RAW:
+      if (msg->msg.ad.local) {
+        API_EXPR_DEREF(msg->msg.ad.port) = msg->conn->pcb.raw->protocol;
+      } else {
+        /* return an error as connecting is only a helper for upper layers */
+        msg->err = ERR_CONN;
+      }
+      break;
+#endif /* LWIP_RAW */
+#if LWIP_UDP
+    case NETCONN_UDP:
+      if (msg->msg.ad.local) {
+        API_EXPR_DEREF(msg->msg.ad.port) = msg->conn->pcb.udp->local_port;
+      } else {
+        if ((msg->conn->pcb.udp->flags & UDP_FLAGS_CONNECTED) == 0) {
+          msg->err = ERR_CONN;
+        } else {
+          API_EXPR_DEREF(msg->msg.ad.port) = msg->conn->pcb.udp->remote_port;
+        }
+      }
+      break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+    case NETCONN_TCP:
+      if ((msg->msg.ad.local == 0) &&
+          ((msg->conn->pcb.tcp->state == CLOSED) || (msg->conn->pcb.tcp->state == LISTEN))) {
+        /* pcb is not connected and remote name is requested */
+        msg->err = ERR_CONN;
+      } else {
+        API_EXPR_DEREF(msg->msg.ad.port) = (msg->msg.ad.local ? msg->conn->pcb.tcp->local_port : msg->conn->pcb.tcp->remote_port);
+      }
+      break;
+#endif /* LWIP_TCP */
+    default:
+      LWIP_ASSERT("invalid netconn_type", 0);
+      break;
+    }
+  } else {
+    msg->err = ERR_CONN;
+  }
+  TCPIP_APIMSG_ACK(msg);
+}
+
+/**
+ * Close or half-shutdown a TCP pcb contained in a netconn
+ * Called from netconn_close
+ * In contrast to closing sockets, the netconn is not deallocated.
+ *
+ * @param m the api_msg_msg pointing to the connection
+ */
+void
+lwip_netconn_do_close(void *m)
+{
+  struct api_msg *msg = (struct api_msg*)m;
+
+#if LWIP_TCP
+  enum netconn_state state = msg->conn->state;
+  /* First check if this is a TCP netconn and if it is in a correct state
+      (LISTEN doesn't support half shutdown) */
+  if ((msg->conn->pcb.tcp != NULL) &&
+      (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) &&
+      ((msg->msg.sd.shut == NETCONN_SHUT_RDWR) || (state != NETCONN_LISTEN))) {
+    /* Check if we are in a connected state */
+    if (state == NETCONN_CONNECT) {
+      /* TCP connect in progress: cannot shutdown */
+      msg->err = ERR_CONN;
+    } else if (state == NETCONN_WRITE) {
+#if LWIP_NETCONN_FULLDUPLEX
+      if (msg->msg.sd.shut & NETCONN_SHUT_WR) {
+        /* close requested, abort running write */
+        sys_sem_t* write_completed_sem;
+        LWIP_ASSERT("msg->conn->current_msg != NULL", msg->conn->current_msg != NULL);
+        write_completed_sem = LWIP_API_MSG_SEM(msg->conn->current_msg);
+        msg->conn->current_msg->err = ERR_CLSD;
+        msg->conn->current_msg = NULL;
+        msg->conn->write_offset = 0;
+        msg->conn->state = NETCONN_NONE;
+        state = NETCONN_NONE;
+        NETCONN_SET_SAFE_ERR(msg->conn, ERR_CLSD);
+        sys_sem_signal(write_completed_sem);
+      } else {
+        LWIP_ASSERT("msg->msg.sd.shut == NETCONN_SHUT_RD", msg->msg.sd.shut == NETCONN_SHUT_RD);
+        /* In this case, let the write continue and do not interfere with
+           conn->current_msg or conn->state! */
+        msg->err = tcp_shutdown(msg->conn->pcb.tcp, 1, 0);
+      }
+    }
+    if (state == NETCONN_NONE) {
+#else /* LWIP_NETCONN_FULLDUPLEX */
+      msg->err = ERR_INPROGRESS;
+    } else {
+#endif /* LWIP_NETCONN_FULLDUPLEX */
+      if (msg->msg.sd.shut & NETCONN_SHUT_RD) {
+        /* Drain and delete mboxes */
+        netconn_drain(msg->conn);
+      }
+      LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL &&
+        msg->conn->write_offset == 0);
+      msg->conn->state = NETCONN_CLOSE;
+      msg->conn->current_msg = msg;
+#if LWIP_TCPIP_CORE_LOCKING
+      if (lwip_netconn_do_close_internal(msg->conn, 0) != ERR_OK) {
+        LWIP_ASSERT("state!", msg->conn->state == NETCONN_CLOSE);
+        UNLOCK_TCPIP_CORE();
+        sys_arch_sem_wait(LWIP_API_MSG_SEM(msg), 0);
+        LOCK_TCPIP_CORE();
+        LWIP_ASSERT("state!", msg->conn->state == NETCONN_NONE);
+      }
+#else /* LWIP_TCPIP_CORE_LOCKING */
+      lwip_netconn_do_close_internal(msg->conn);
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+      /* for tcp netconns, lwip_netconn_do_close_internal ACKs the message */
+      return;
+    }
+  } else
+#endif /* LWIP_TCP */
+  {
+    msg->err = ERR_CONN;
+  }
+  TCPIP_APIMSG_ACK(msg);
+}
+
+#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD)
+/**
+ * Join multicast groups for UDP netconns.
+ * Called from netconn_join_leave_group
+ *
+ * @param m the api_msg_msg pointing to the connection
+ */
+void
+lwip_netconn_do_join_leave_group(void *m)
+{
+  struct api_msg *msg = (struct api_msg*)m;
+
+  if (ERR_IS_FATAL(msg->conn->last_err)) {
+    msg->err = msg->conn->last_err;
+  } else {
+    if (msg->conn->pcb.tcp != NULL) {
+      if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_UDP) {
+#if LWIP_UDP
+#if LWIP_IPV6 && LWIP_IPV6_MLD
+        if (NETCONNTYPE_ISIPV6(msg->conn->type)) {
+          if (msg->msg.jl.join_or_leave == NETCONN_JOIN) {
+            msg->err = mld6_joingroup(ip_2_ip6(API_EXPR_REF(msg->msg.jl.netif_addr)),
+              ip_2_ip6(API_EXPR_REF(msg->msg.jl.multiaddr)));
+          } else {
+            msg->err = mld6_leavegroup(ip_2_ip6(API_EXPR_REF(msg->msg.jl.netif_addr)),
+              ip_2_ip6(API_EXPR_REF(msg->msg.jl.multiaddr)));
+          }
+        }
+        else
+#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
+        {
+#if LWIP_IGMP
+          if (msg->msg.jl.join_or_leave == NETCONN_JOIN) {
+            msg->err = igmp_joingroup(ip_2_ip4(API_EXPR_REF(msg->msg.jl.netif_addr)),
+              ip_2_ip4(API_EXPR_REF(msg->msg.jl.multiaddr)));
+          } else {
+            msg->err = igmp_leavegroup(ip_2_ip4(API_EXPR_REF(msg->msg.jl.netif_addr)),
+              ip_2_ip4(API_EXPR_REF(msg->msg.jl.multiaddr)));
+          }
+#endif /* LWIP_IGMP */
+        }
+#endif /* LWIP_UDP */
+#if (LWIP_TCP || LWIP_RAW)
+      } else {
+        msg->err = ERR_VAL;
+#endif /* (LWIP_TCP || LWIP_RAW) */
+      }
+    } else {
+      msg->err = ERR_CONN;
+    }
+  }
+  TCPIP_APIMSG_ACK(msg);
+}
+#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */
+
+#if LWIP_DNS
+/**
+ * Callback function that is called when DNS name is resolved
+ * (or on timeout). A waiting application thread is waked up by
+ * signaling the semaphore.
+ */
+static void
+lwip_netconn_do_dns_found(const char *name, const ip_addr_t *ipaddr, void *arg)
+{
+  struct dns_api_msg *msg = (struct dns_api_msg*)arg;
+
+  /* we trust the internal implementation to be correct :-) */
+  LWIP_UNUSED_ARG(name);
+
+  if (ipaddr == NULL) {
+    /* timeout or memory error */
+    API_EXPR_DEREF(msg->err) = ERR_VAL;
+  } else {
+    /* address was resolved */
+    API_EXPR_DEREF(msg->err) = ERR_OK;
+    API_EXPR_DEREF(msg->addr) = *ipaddr;
+  }
+  /* wake up the application task waiting in netconn_gethostbyname */
+  sys_sem_signal(API_EXPR_REF_SEM(msg->sem));
+}
+
+/**
+ * Execute a DNS query
+ * Called from netconn_gethostbyname
+ *
+ * @param arg the dns_api_msg pointing to the query
+ */
+void
+lwip_netconn_do_gethostbyname(void *arg)
+{
+  struct dns_api_msg *msg = (struct dns_api_msg*)arg;
+  u8_t addrtype =
+#if LWIP_IPV4 && LWIP_IPV6
+    msg->dns_addrtype;
+#else
+    LWIP_DNS_ADDRTYPE_DEFAULT;
+#endif
+
+  API_EXPR_DEREF(msg->err) = dns_gethostbyname_addrtype(msg->name,
+    API_EXPR_REF(msg->addr), lwip_netconn_do_dns_found, msg, addrtype);
+  if (API_EXPR_DEREF(msg->err) != ERR_INPROGRESS) {
+    /* on error or immediate success, wake up the application
+     * task waiting in netconn_gethostbyname */
+    sys_sem_signal(API_EXPR_REF_SEM(msg->sem));
+  }
+}
+#endif /* LWIP_DNS */
+
+#endif /* LWIP_NETCONN */

+ 115 - 75
thirdparty/lwip/src/api/err.c

@@ -1,75 +1,115 @@
-/**
- * @file
- * Error Management module
- *
- */
-
-/*
- * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
- * All rights reserved. 
- * 
- * Redistribution and use in source and binary forms, with or without modification, 
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission. 
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
- * OF SUCH DAMAGE.
- *
- * This file is part of the lwIP TCP/IP stack.
- * 
- * Author: Adam Dunkels <adam@sics.se>
- *
- */
-
-#include "lwip/err.h"
-
-#ifdef LWIP_DEBUG
-
-static const char *err_strerr[] = {
-           "Ok.",                    /* ERR_OK          0  */
-           "Out of memory error.",   /* ERR_MEM        -1  */
-           "Buffer error.",          /* ERR_BUF        -2  */
-           "Timeout.",               /* ERR_TIMEOUT    -3  */
-           "Routing problem.",       /* ERR_RTE        -4  */
-           "Operation in progress.", /* ERR_INPROGRESS -5  */
-           "Illegal value.",         /* ERR_VAL        -6  */
-           "Operation would block.", /* ERR_WOULDBLOCK -7  */
-           "Address in use.",        /* ERR_USE        -8  */
-           "Already connected.",     /* ERR_ISCONN     -9  */
-           "Connection aborted.",    /* ERR_ABRT       -10 */
-           "Connection reset.",      /* ERR_RST        -11 */
-           "Connection closed.",     /* ERR_CLSD       -12 */
-           "Not connected.",         /* ERR_CONN       -13 */
-           "Illegal argument.",      /* ERR_ARG        -14 */
-           "Low-level netif error.", /* ERR_IF         -15 */
-};
-
-/**
- * Convert an lwip internal error to a string representation.
- *
- * @param err an lwip internal err_t
- * @return a string representation for err
- */
-const char *
-lwip_strerr(err_t err)
-{
-  return err_strerr[-err];
-
-}
-
-#endif /* LWIP_DEBUG */
+/**
+ * @file
+ * Error Management module
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/err.h"
+#include "lwip/def.h"
+#include "lwip/sys.h"
+
+#include "lwip/errno.h"
+
+#if !NO_SYS
+/** Table to quickly map an lwIP error (err_t) to a socket error
+  * by using -err as an index */
+static const int err_to_errno_table[] = {
+  0,             /* ERR_OK          0      No error, everything OK. */
+  ENOMEM,        /* ERR_MEM        -1      Out of memory error.     */
+  ENOBUFS,       /* ERR_BUF        -2      Buffer error.            */
+  EWOULDBLOCK,   /* ERR_TIMEOUT    -3      Timeout                  */
+  EHOSTUNREACH,  /* ERR_RTE        -4      Routing problem.         */
+  EINPROGRESS,   /* ERR_INPROGRESS -5      Operation in progress    */
+  EINVAL,        /* ERR_VAL        -6      Illegal value.           */
+  EWOULDBLOCK,   /* ERR_WOULDBLOCK -7      Operation would block.   */
+  EADDRINUSE,    /* ERR_USE        -8      Address in use.          */
+  EALREADY,      /* ERR_ALREADY    -9      Already connecting.      */
+  EISCONN,       /* ERR_ISCONN     -10     Conn already established.*/
+  ENOTCONN,      /* ERR_CONN       -11     Not connected.           */
+  -1,            /* ERR_IF         -12     Low-level netif error    */
+  ECONNABORTED,  /* ERR_ABRT       -13     Connection aborted.      */
+  ECONNRESET,    /* ERR_RST        -14     Connection reset.        */
+  ENOTCONN,      /* ERR_CLSD       -15     Connection closed.       */
+  EIO            /* ERR_ARG        -16     Illegal argument.        */
+};
+
+int
+err_to_errno(err_t err)
+{
+  if ((err > 0) || (-err >= (err_t)LWIP_ARRAYSIZE(err_to_errno_table))) {
+    return EIO;
+  }
+  return err_to_errno_table[-err];
+}
+#endif /* !NO_SYS */
+
+#ifdef LWIP_DEBUG
+
+static const char *err_strerr[] = {
+           "Ok.",                    /* ERR_OK          0  */
+           "Out of memory error.",   /* ERR_MEM        -1  */
+           "Buffer error.",          /* ERR_BUF        -2  */
+           "Timeout.",               /* ERR_TIMEOUT    -3  */
+           "Routing problem.",       /* ERR_RTE        -4  */
+           "Operation in progress.", /* ERR_INPROGRESS -5  */
+           "Illegal value.",         /* ERR_VAL        -6  */
+           "Operation would block.", /* ERR_WOULDBLOCK -7  */
+           "Address in use.",        /* ERR_USE        -8  */
+           "Already connecting.",    /* ERR_ALREADY    -9  */
+           "Already connected.",     /* ERR_ISCONN     -10 */
+           "Not connected.",         /* ERR_CONN       -11 */
+           "Low-level netif error.", /* ERR_IF         -12 */
+           "Connection aborted.",    /* ERR_ABRT       -13 */
+           "Connection reset.",      /* ERR_RST        -14 */
+           "Connection closed.",     /* ERR_CLSD       -15 */
+           "Illegal argument."       /* ERR_ARG        -16 */
+};
+
+/**
+ * Convert an lwip internal error to a string representation.
+ *
+ * @param err an lwip internal err_t
+ * @return a string representation for err
+ */
+const char *
+lwip_strerr(err_t err)
+{
+  if ((err > 0) || (-err >= (err_t)LWIP_ARRAYSIZE(err_strerr))) {
+    return "Unknown error.";
+  }
+  return err_strerr[-err];
+}
+
+#endif /* LWIP_DEBUG */

+ 246 - 245
thirdparty/lwip/src/api/netbuf.c

@@ -1,245 +1,246 @@
-/**
- * @file
- * Network buffer management
- *
- */
- 
-/*
- * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
- * All rights reserved. 
- * 
- * Redistribution and use in source and binary forms, with or without modification, 
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission. 
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
- * OF SUCH DAMAGE.
- *
- * This file is part of the lwIP TCP/IP stack.
- * 
- * Author: Adam Dunkels <adam@sics.se>
- *
- */
-
-#include "lwip/opt.h"
-
-#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */
-
-#include "lwip/netbuf.h"
-#include "lwip/memp.h"
-
-#include <string.h>
-
-/**
- * Create (allocate) and initialize a new netbuf.
- * The netbuf doesn't yet contain a packet buffer!
- *
- * @return a pointer to a new netbuf
- *         NULL on lack of memory
- */
-struct
-netbuf *netbuf_new(void)
-{
-  struct netbuf *buf;
-
-  buf = (struct netbuf *)memp_malloc(MEMP_NETBUF);
-  if (buf != NULL) {
-    buf->p = NULL;
-    buf->ptr = NULL;
-    ip_addr_set_any(&buf->addr);
-    buf->port = 0;
-#if LWIP_NETBUF_RECVINFO || LWIP_CHECKSUM_ON_COPY
-#if LWIP_CHECKSUM_ON_COPY
-    buf->flags = 0;
-#endif /* LWIP_CHECKSUM_ON_COPY */
-    buf->toport_chksum = 0;
-#if LWIP_NETBUF_RECVINFO
-    ip_addr_set_any(&buf->toaddr);
-#endif /* LWIP_NETBUF_RECVINFO */
-#endif /* LWIP_NETBUF_RECVINFO || LWIP_CHECKSUM_ON_COPY */
-    return buf;
-  } else {
-    return NULL;
-  }
-}
-
-/**
- * Deallocate a netbuf allocated by netbuf_new().
- *
- * @param buf pointer to a netbuf allocated by netbuf_new()
- */
-void
-netbuf_delete(struct netbuf *buf)
-{
-  if (buf != NULL) {
-    if (buf->p != NULL) {
-      pbuf_free(buf->p);
-      buf->p = buf->ptr = NULL;
-    }
-    memp_free(MEMP_NETBUF, buf);
-  }
-}
-
-/**
- * Allocate memory for a packet buffer for a given netbuf.
- *
- * @param buf the netbuf for which to allocate a packet buffer
- * @param size the size of the packet buffer to allocate
- * @return pointer to the allocated memory
- *         NULL if no memory could be allocated
- */
-void *
-netbuf_alloc(struct netbuf *buf, u16_t size)
-{
-  LWIP_ERROR("netbuf_alloc: invalid buf", (buf != NULL), return NULL;);
-
-  /* Deallocate any previously allocated memory. */
-  if (buf->p != NULL) {
-    pbuf_free(buf->p);
-  }
-  buf->p = pbuf_alloc(PBUF_TRANSPORT, size, PBUF_RAM);
-  if (buf->p == NULL) {
-     return NULL;
-  }
-  LWIP_ASSERT("check that first pbuf can hold size",
-             (buf->p->len >= size));
-  buf->ptr = buf->p;
-  return buf->p->payload;
-}
-
-/**
- * Free the packet buffer included in a netbuf
- *
- * @param buf pointer to the netbuf which contains the packet buffer to free
- */
-void
-netbuf_free(struct netbuf *buf)
-{
-  LWIP_ERROR("netbuf_free: invalid buf", (buf != NULL), return;);
-  if (buf->p != NULL) {
-    pbuf_free(buf->p);
-  }
-  buf->p = buf->ptr = NULL;
-}
-
-/**
- * Let a netbuf reference existing (non-volatile) data.
- *
- * @param buf netbuf which should reference the data
- * @param dataptr pointer to the data to reference
- * @param size size of the data
- * @return ERR_OK if data is referenced
- *         ERR_MEM if data couldn't be referenced due to lack of memory
- */
-err_t
-netbuf_ref(struct netbuf *buf, const void *dataptr, u16_t size)
-{
-  LWIP_ERROR("netbuf_ref: invalid buf", (buf != NULL), return ERR_ARG;);
-  if (buf->p != NULL) {
-    pbuf_free(buf->p);
-  }
-  buf->p = pbuf_alloc(PBUF_TRANSPORT, 0, PBUF_REF);
-  if (buf->p == NULL) {
-    buf->ptr = NULL;
-    return ERR_MEM;
-  }
-  buf->p->payload = (void*)dataptr;
-  buf->p->len = buf->p->tot_len = size;
-  buf->ptr = buf->p;
-  return ERR_OK;
-}
-
-/**
- * Chain one netbuf to another (@see pbuf_chain)
- *
- * @param head the first netbuf
- * @param tail netbuf to chain after head, freed by this function, may not be reference after returning
- */
-void
-netbuf_chain(struct netbuf *head, struct netbuf *tail)
-{
-  LWIP_ERROR("netbuf_ref: invalid head", (head != NULL), return;);
-  LWIP_ERROR("netbuf_chain: invalid tail", (tail != NULL), return;);
-  pbuf_cat(head->p, tail->p);
-  head->ptr = head->p;
-  memp_free(MEMP_NETBUF, tail);
-}
-
-/**
- * Get the data pointer and length of the data inside a netbuf.
- *
- * @param buf netbuf to get the data from
- * @param dataptr pointer to a void pointer where to store the data pointer
- * @param len pointer to an u16_t where the length of the data is stored
- * @return ERR_OK if the information was retreived,
- *         ERR_BUF on error.
- */
-err_t
-netbuf_data(struct netbuf *buf, void **dataptr, u16_t *len)
-{
-  LWIP_ERROR("netbuf_data: invalid buf", (buf != NULL), return ERR_ARG;);
-  LWIP_ERROR("netbuf_data: invalid dataptr", (dataptr != NULL), return ERR_ARG;);
-  LWIP_ERROR("netbuf_data: invalid len", (len != NULL), return ERR_ARG;);
-
-  if (buf->ptr == NULL) {
-    return ERR_BUF;
-  }
-  *dataptr = buf->ptr->payload;
-  *len = buf->ptr->len;
-  return ERR_OK;
-}
-
-/**
- * Move the current data pointer of a packet buffer contained in a netbuf
- * to the next part.
- * The packet buffer itself is not modified.
- *
- * @param buf the netbuf to modify
- * @return -1 if there is no next part
- *         1  if moved to the next part but now there is no next part
- *         0  if moved to the next part and there are still more parts
- */
-s8_t
-netbuf_next(struct netbuf *buf)
-{
-  LWIP_ERROR("netbuf_free: invalid buf", (buf != NULL), return -1;);
-  if (buf->ptr->next == NULL) {
-    return -1;
-  }
-  buf->ptr = buf->ptr->next;
-  if (buf->ptr->next == NULL) {
-    return 1;
-  }
-  return 0;
-}
-
-/**
- * Move the current data pointer of a packet buffer contained in a netbuf
- * to the beginning of the packet.
- * The packet buffer itself is not modified.
- *
- * @param buf the netbuf to modify
- */
-void
-netbuf_first(struct netbuf *buf)
-{
-  LWIP_ERROR("netbuf_free: invalid buf", (buf != NULL), return;);
-  buf->ptr = buf->p;
-}
-
-#endif /* LWIP_NETCONN */
+/**
+ * @file
+ * Network buffer management
+ *
+ * @defgroup netbuf Network buffers
+ * @ingroup netconn
+ * Network buffer descriptor for @ref netconn. Based on @ref pbuf internally
+ * to avoid copying data around.\n
+ * Buffers must not be shared accross multiple threads, all functions except
+ * netbuf_new() and netbuf_delete() are not thread-safe.
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/netbuf.h"
+#include "lwip/memp.h"
+
+#include <string.h>
+
+/**
+ * @ingroup netbuf
+ * Create (allocate) and initialize a new netbuf.
+ * The netbuf doesn't yet contain a packet buffer!
+ *
+ * @return a pointer to a new netbuf
+ *         NULL on lack of memory
+ */
+struct
+netbuf *netbuf_new(void)
+{
+  struct netbuf *buf;
+
+  buf = (struct netbuf *)memp_malloc(MEMP_NETBUF);
+  if (buf != NULL) {
+    memset(buf, 0, sizeof(struct netbuf));
+  }
+  return buf;
+}
+
+/**
+ * @ingroup netbuf
+ * Deallocate a netbuf allocated by netbuf_new().
+ *
+ * @param buf pointer to a netbuf allocated by netbuf_new()
+ */
+void
+netbuf_delete(struct netbuf *buf)
+{
+  if (buf != NULL) {
+    if (buf->p != NULL) {
+      pbuf_free(buf->p);
+      buf->p = buf->ptr = NULL;
+    }
+    memp_free(MEMP_NETBUF, buf);
+  }
+}
+
+/**
+ * @ingroup netbuf
+ * Allocate memory for a packet buffer for a given netbuf.
+ *
+ * @param buf the netbuf for which to allocate a packet buffer
+ * @param size the size of the packet buffer to allocate
+ * @return pointer to the allocated memory
+ *         NULL if no memory could be allocated
+ */
+void *
+netbuf_alloc(struct netbuf *buf, u16_t size)
+{
+  LWIP_ERROR("netbuf_alloc: invalid buf", (buf != NULL), return NULL;);
+
+  /* Deallocate any previously allocated memory. */
+  if (buf->p != NULL) {
+    pbuf_free(buf->p);
+  }
+  buf->p = pbuf_alloc(PBUF_TRANSPORT, size, PBUF_RAM);
+  if (buf->p == NULL) {
+     return NULL;
+  }
+  LWIP_ASSERT("check that first pbuf can hold size",
+             (buf->p->len >= size));
+  buf->ptr = buf->p;
+  return buf->p->payload;
+}
+
+/**
+ * @ingroup netbuf
+ * Free the packet buffer included in a netbuf
+ *
+ * @param buf pointer to the netbuf which contains the packet buffer to free
+ */
+void
+netbuf_free(struct netbuf *buf)
+{
+  LWIP_ERROR("netbuf_free: invalid buf", (buf != NULL), return;);
+  if (buf->p != NULL) {
+    pbuf_free(buf->p);
+  }
+  buf->p = buf->ptr = NULL;
+}
+
+/**
+ * @ingroup netbuf
+ * Let a netbuf reference existing (non-volatile) data.
+ *
+ * @param buf netbuf which should reference the data
+ * @param dataptr pointer to the data to reference
+ * @param size size of the data
+ * @return ERR_OK if data is referenced
+ *         ERR_MEM if data couldn't be referenced due to lack of memory
+ */
+err_t
+netbuf_ref(struct netbuf *buf, const void *dataptr, u16_t size)
+{
+  LWIP_ERROR("netbuf_ref: invalid buf", (buf != NULL), return ERR_ARG;);
+  if (buf->p != NULL) {
+    pbuf_free(buf->p);
+  }
+  buf->p = pbuf_alloc(PBUF_TRANSPORT, 0, PBUF_REF);
+  if (buf->p == NULL) {
+    buf->ptr = NULL;
+    return ERR_MEM;
+  }
+  ((struct pbuf_rom*)buf->p)->payload = dataptr;
+  buf->p->len = buf->p->tot_len = size;
+  buf->ptr = buf->p;
+  return ERR_OK;
+}
+
+/**
+ * @ingroup netbuf
+ * Chain one netbuf to another (@see pbuf_chain)
+ *
+ * @param head the first netbuf
+ * @param tail netbuf to chain after head, freed by this function, may not be reference after returning
+ */
+void
+netbuf_chain(struct netbuf *head, struct netbuf *tail)
+{
+  LWIP_ERROR("netbuf_chain: invalid head", (head != NULL), return;);
+  LWIP_ERROR("netbuf_chain: invalid tail", (tail != NULL), return;);
+  pbuf_cat(head->p, tail->p);
+  head->ptr = head->p;
+  memp_free(MEMP_NETBUF, tail);
+}
+
+/**
+ * @ingroup netbuf
+ * Get the data pointer and length of the data inside a netbuf.
+ *
+ * @param buf netbuf to get the data from
+ * @param dataptr pointer to a void pointer where to store the data pointer
+ * @param len pointer to an u16_t where the length of the data is stored
+ * @return ERR_OK if the information was retrieved,
+ *         ERR_BUF on error.
+ */
+err_t
+netbuf_data(struct netbuf *buf, void **dataptr, u16_t *len)
+{
+  LWIP_ERROR("netbuf_data: invalid buf", (buf != NULL), return ERR_ARG;);
+  LWIP_ERROR("netbuf_data: invalid dataptr", (dataptr != NULL), return ERR_ARG;);
+  LWIP_ERROR("netbuf_data: invalid len", (len != NULL), return ERR_ARG;);
+
+  if (buf->ptr == NULL) {
+    return ERR_BUF;
+  }
+  *dataptr = buf->ptr->payload;
+  *len = buf->ptr->len;
+  return ERR_OK;
+}
+
+/**
+ * @ingroup netbuf
+ * Move the current data pointer of a packet buffer contained in a netbuf
+ * to the next part.
+ * The packet buffer itself is not modified.
+ *
+ * @param buf the netbuf to modify
+ * @return -1 if there is no next part
+ *         1  if moved to the next part but now there is no next part
+ *         0  if moved to the next part and there are still more parts
+ */
+s8_t
+netbuf_next(struct netbuf *buf)
+{
+  LWIP_ERROR("netbuf_next: invalid buf", (buf != NULL), return -1;);
+  if (buf->ptr->next == NULL) {
+    return -1;
+  }
+  buf->ptr = buf->ptr->next;
+  if (buf->ptr->next == NULL) {
+    return 1;
+  }
+  return 0;
+}
+
+/**
+ * @ingroup netbuf
+ * Move the current data pointer of a packet buffer contained in a netbuf
+ * to the beginning of the packet.
+ * The packet buffer itself is not modified.
+ *
+ * @param buf the netbuf to modify
+ */
+void
+netbuf_first(struct netbuf *buf)
+{
+  LWIP_ERROR("netbuf_first: invalid buf", (buf != NULL), return;);
+  buf->ptr = buf->p;
+}
+
+#endif /* LWIP_NETCONN */

+ 413 - 353
thirdparty/lwip/src/api/netdb.c

@@ -1,353 +1,413 @@
-/**
- * @file
- * API functions for name resolving
- *
- */
-
-/*
- * Redistribution and use in source and binary forms, with or without modification, 
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission. 
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
- * OF SUCH DAMAGE.
- *
- * This file is part of the lwIP TCP/IP stack.
- * 
- * Author: Simon Goldschmidt
- *
- */
-
-#include "lwip/netdb.h"
-
-#if LWIP_DNS && LWIP_SOCKET
-
-#include "lwip/err.h"
-#include "lwip/mem.h"
-#include "lwip/memp.h"
-#include "lwip/ip_addr.h"
-#include "lwip/api.h"
-#include "lwip/dns.h"
-
-#include <string.h>
-#include <stdlib.h>
-
-/** helper struct for gethostbyname_r to access the char* buffer */
-struct gethostbyname_r_helper {
-  ip_addr_t *addr_list[2];
-  ip_addr_t addr;
-  char *aliases;
-};
-
-/** h_errno is exported in netdb.h for access by applications. */
-#if LWIP_DNS_API_DECLARE_H_ERRNO
-int h_errno;
-#endif /* LWIP_DNS_API_DECLARE_H_ERRNO */
-
-/** define "hostent" variables storage: 0 if we use a static (but unprotected)
- * set of variables for lwip_gethostbyname, 1 if we use a local storage */
-#ifndef LWIP_DNS_API_HOSTENT_STORAGE
-#define LWIP_DNS_API_HOSTENT_STORAGE 0
-#endif
-
-/** define "hostent" variables storage */
-#if LWIP_DNS_API_HOSTENT_STORAGE
-#define HOSTENT_STORAGE
-#else
-#define HOSTENT_STORAGE static
-#endif /* LWIP_DNS_API_STATIC_HOSTENT */
-
-/**
- * Returns an entry containing addresses of address family AF_INET
- * for the host with name name.
- * Due to dns_gethostbyname limitations, only one address is returned.
- *
- * @param name the hostname to resolve
- * @return an entry containing addresses of address family AF_INET
- *         for the host with name name
- */
-struct hostent*
-lwip_gethostbyname(const char *name)
-{
-  err_t err;
-  ip_addr_t addr;
-
-  /* buffer variables for lwip_gethostbyname() */
-  HOSTENT_STORAGE struct hostent s_hostent;
-  HOSTENT_STORAGE char *s_aliases;
-  HOSTENT_STORAGE ip_addr_t s_hostent_addr;
-  HOSTENT_STORAGE ip_addr_t *s_phostent_addr[2];
-
-  /* query host IP address */
-  err = netconn_gethostbyname(name, &addr);
-  if (err != ERR_OK) {
-    LWIP_DEBUGF(DNS_DEBUG, ("lwip_gethostbyname(%s) failed, err=%d\n", name, err));
-    h_errno = HOST_NOT_FOUND;
-    return NULL;
-  }
-
-  /* fill hostent */
-  s_hostent_addr = addr;
-  s_phostent_addr[0] = &s_hostent_addr;
-  s_phostent_addr[1] = NULL;
-  s_hostent.h_name = (char*)name;
-  s_hostent.h_aliases = &s_aliases;
-  s_hostent.h_addrtype = AF_INET;
-  s_hostent.h_length = sizeof(ip_addr_t);
-  s_hostent.h_addr_list = (char**)&s_phostent_addr;
-
-#if DNS_DEBUG
-  /* dump hostent */
-  LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_name           == %s\n", s_hostent.h_name));
-  LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_aliases        == %p\n", s_hostent.h_aliases));
-  if (s_hostent.h_aliases != NULL) {
-    u8_t idx;
-    for ( idx=0; s_hostent.h_aliases[idx]; idx++) {
-      LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_aliases[%i]->   == %p\n", idx, s_hostent.h_aliases[idx]));
-      LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_aliases[%i]->   == %s\n", idx, s_hostent.h_aliases[idx]));
-    }
-  }
-  LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addrtype       == %d\n", s_hostent.h_addrtype));
-  LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_length         == %d\n", s_hostent.h_length));
-  LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addr_list      == %p\n", s_hostent.h_addr_list));
-  if (s_hostent.h_addr_list != NULL) {
-    u8_t idx;
-    for ( idx=0; s_hostent.h_addr_list[idx]; idx++) {
-      LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addr_list[%i]   == %p\n", idx, s_hostent.h_addr_list[idx]));
-      LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addr_list[%i]-> == %s\n", idx, ip_ntoa((ip_addr_t*)s_hostent.h_addr_list[idx])));
-    }
-  }
-#endif /* DNS_DEBUG */
-
-#if LWIP_DNS_API_HOSTENT_STORAGE
-  /* this function should return the "per-thread" hostent after copy from s_hostent */
-  return sys_thread_hostent(&s_hostent);
-#else
-  return &s_hostent;
-#endif /* LWIP_DNS_API_HOSTENT_STORAGE */
-}
-
-/**
- * Thread-safe variant of lwip_gethostbyname: instead of using a static
- * buffer, this function takes buffer and errno pointers as arguments
- * and uses these for the result.
- *
- * @param name the hostname to resolve
- * @param ret pre-allocated struct where to store the result
- * @param buf pre-allocated buffer where to store additional data
- * @param buflen the size of buf
- * @param result pointer to a hostent pointer that is set to ret on success
- *               and set to zero on error
- * @param h_errnop pointer to an int where to store errors (instead of modifying
- *                 the global h_errno)
- * @return 0 on success, non-zero on error, additional error information
- *         is stored in *h_errnop instead of h_errno to be thread-safe
- */
-int
-lwip_gethostbyname_r(const char *name, struct hostent *ret, char *buf,
-                size_t buflen, struct hostent **result, int *h_errnop)
-{
-  err_t err;
-  struct gethostbyname_r_helper *h;
-  char *hostname;
-  size_t namelen;
-  int lh_errno;
-
-  if (h_errnop == NULL) {
-    /* ensure h_errnop is never NULL */
-    h_errnop = &lh_errno;
-  }
-
-  if (result == NULL) {
-    /* not all arguments given */
-    *h_errnop = EINVAL;
-    return -1;
-  }
-  /* first thing to do: set *result to nothing */
-  *result = NULL;
-  if ((name == NULL) || (ret == NULL) || (buf == NULL)) {
-    /* not all arguments given */
-    *h_errnop = EINVAL;
-    return -1;
-  }
-
-  namelen = strlen(name);
-  if (buflen < (sizeof(struct gethostbyname_r_helper) + namelen + 1 + (MEM_ALIGNMENT - 1))) {
-    /* buf can't hold the data needed + a copy of name */
-    *h_errnop = ERANGE;
-    return -1;
-  }
-
-  h = (struct gethostbyname_r_helper*)LWIP_MEM_ALIGN(buf);
-  hostname = ((char*)h) + sizeof(struct gethostbyname_r_helper);
-
-  /* query host IP address */
-  err = netconn_gethostbyname(name, &h->addr);
-  if (err != ERR_OK) {
-    LWIP_DEBUGF(DNS_DEBUG, ("lwip_gethostbyname(%s) failed, err=%d\n", name, err));
-    *h_errnop = HOST_NOT_FOUND;
-    return -1;
-  }
-
-  /* copy the hostname into buf */
-  MEMCPY(hostname, name, namelen);
-  hostname[namelen] = 0;
-
-  /* fill hostent */
-  h->addr_list[0] = &h->addr;
-  h->addr_list[1] = NULL;
-  h->aliases = NULL;
-  ret->h_name = hostname;
-  ret->h_aliases = &h->aliases;
-  ret->h_addrtype = AF_INET;
-  ret->h_length = sizeof(ip_addr_t);
-  ret->h_addr_list = (char**)&h->addr_list;
-
-  /* set result != NULL */
-  *result = ret;
-
-  /* return success */
-  return 0;
-}
-
-/**
- * Frees one or more addrinfo structures returned by getaddrinfo(), along with
- * any additional storage associated with those structures. If the ai_next field
- * of the structure is not null, the entire list of structures is freed.
- *
- * @param ai struct addrinfo to free
- */
-void
-lwip_freeaddrinfo(struct addrinfo *ai)
-{
-  struct addrinfo *next;
-
-  while (ai != NULL) {
-    next = ai->ai_next;
-    memp_free(MEMP_NETDB, ai);
-    ai = next;
-  }
-}
-
-/**
- * Translates the name of a service location (for example, a host name) and/or
- * a service name and returns a set of socket addresses and associated
- * information to be used in creating a socket with which to address the
- * specified service.
- * Memory for the result is allocated internally and must be freed by calling
- * lwip_freeaddrinfo()!
- *
- * Due to a limitation in dns_gethostbyname, only the first address of a
- * host is returned.
- * Also, service names are not supported (only port numbers)!
- *
- * @param nodename descriptive name or address string of the host
- *                 (may be NULL -> local address)
- * @param servname port number as string of NULL 
- * @param hints structure containing input values that set socktype and protocol
- * @param res pointer to a pointer where to store the result (set to NULL on failure)
- * @return 0 on success, non-zero on failure
- */
-int
-lwip_getaddrinfo(const char *nodename, const char *servname,
-       const struct addrinfo *hints, struct addrinfo **res)
-{
-  err_t err;
-  ip_addr_t addr;
-  struct addrinfo *ai;
-  struct sockaddr_in *sa = NULL;
-  int port_nr = 0;
-  size_t total_size;
-  size_t namelen = 0;
-
-  if (res == NULL) {
-    return EAI_FAIL;
-  }
-  *res = NULL;
-  if ((nodename == NULL) && (servname == NULL)) {
-    return EAI_NONAME;
-  }
-
-  if (servname != NULL) {
-    /* service name specified: convert to port number
-     * @todo?: currently, only ASCII integers (port numbers) are supported! */
-    port_nr = atoi(servname);
-    if ((port_nr <= 0) || (port_nr > 0xffff)) {
-      return EAI_SERVICE;
-    }
-  }
-
-  if (nodename != NULL) {
-    /* service location specified, try to resolve */
-    err = netconn_gethostbyname(nodename, &addr);
-    if (err != ERR_OK) {
-      return EAI_FAIL;
-    }
-  } else {
-    /* service location specified, use loopback address */
-    ip_addr_set_loopback(&addr);
-  }
-
-  total_size = sizeof(struct addrinfo) + sizeof(struct sockaddr_in);
-  if (nodename != NULL) {
-    namelen = strlen(nodename);
-    LWIP_ASSERT("namelen is too long", (namelen + 1) <= (mem_size_t)-1);
-    total_size += namelen + 1;
-  }
-  /* If this fails, please report to lwip-devel! :-) */
-  LWIP_ASSERT("total_size <= NETDB_ELEM_SIZE: please report this!",
-    total_size <= NETDB_ELEM_SIZE);
-  ai = (struct addrinfo *)memp_malloc(MEMP_NETDB);
-  if (ai == NULL) {
-    goto memerr;
-  }
-  memset(ai, 0, total_size);
-  sa = (struct sockaddr_in*)((u8_t*)ai + sizeof(struct addrinfo));
-  /* set up sockaddr */
-  inet_addr_from_ipaddr(&sa->sin_addr, &addr);
-  sa->sin_family = AF_INET;
-  sa->sin_len = sizeof(struct sockaddr_in);
-  sa->sin_port = htons((u16_t)port_nr);
-
-  /* set up addrinfo */
-  ai->ai_family = AF_INET;
-  if (hints != NULL) {
-    /* copy socktype & protocol from hints if specified */
-    ai->ai_socktype = hints->ai_socktype;
-    ai->ai_protocol = hints->ai_protocol;
-  }
-  if (nodename != NULL) {
-    /* copy nodename to canonname if specified */
-    ai->ai_canonname = ((char*)ai + sizeof(struct addrinfo) + sizeof(struct sockaddr_in));
-    MEMCPY(ai->ai_canonname, nodename, namelen);
-    ai->ai_canonname[namelen] = 0;
-  }
-  ai->ai_addrlen = sizeof(struct sockaddr_in);
-  ai->ai_addr = (struct sockaddr*)sa;
-
-  *res = ai;
-
-  return 0;
-memerr:
-  if (ai != NULL) {
-    memp_free(MEMP_NETDB, ai);
-  }
-  return EAI_MEMORY;
-}
-
-#endif /* LWIP_DNS && LWIP_SOCKET */
+/**
+ * @file
+ * API functions for name resolving
+ *
+ * @defgroup netdbapi NETDB API
+ * @ingroup socket
+ */
+
+/*
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt
+ *
+ */
+
+#include "lwip/netdb.h"
+
+#if LWIP_DNS && LWIP_SOCKET
+
+#include "lwip/err.h"
+#include "lwip/mem.h"
+#include "lwip/memp.h"
+#include "lwip/ip_addr.h"
+#include "lwip/api.h"
+#include "lwip/dns.h"
+
+#include <string.h> /* memset */
+#include <stdlib.h> /* atoi */
+
+/** helper struct for gethostbyname_r to access the char* buffer */
+struct gethostbyname_r_helper {
+  ip_addr_t *addr_list[2];
+  ip_addr_t addr;
+  char *aliases;
+};
+
+/** h_errno is exported in netdb.h for access by applications. */
+#if LWIP_DNS_API_DECLARE_H_ERRNO
+int h_errno;
+#endif /* LWIP_DNS_API_DECLARE_H_ERRNO */
+
+/** define "hostent" variables storage: 0 if we use a static (but unprotected)
+ * set of variables for lwip_gethostbyname, 1 if we use a local storage */
+#ifndef LWIP_DNS_API_HOSTENT_STORAGE
+#define LWIP_DNS_API_HOSTENT_STORAGE 0
+#endif
+
+/** define "hostent" variables storage */
+#if LWIP_DNS_API_HOSTENT_STORAGE
+#define HOSTENT_STORAGE
+#else
+#define HOSTENT_STORAGE static
+#endif /* LWIP_DNS_API_STATIC_HOSTENT */
+
+/**
+ * Returns an entry containing addresses of address family AF_INET
+ * for the host with name name.
+ * Due to dns_gethostbyname limitations, only one address is returned.
+ *
+ * @param name the hostname to resolve
+ * @return an entry containing addresses of address family AF_INET
+ *         for the host with name name
+ */
+struct hostent*
+lwip_gethostbyname(const char *name)
+{
+  err_t err;
+  ip_addr_t addr;
+
+  /* buffer variables for lwip_gethostbyname() */
+  HOSTENT_STORAGE struct hostent s_hostent;
+  HOSTENT_STORAGE char *s_aliases;
+  HOSTENT_STORAGE ip_addr_t s_hostent_addr;
+  HOSTENT_STORAGE ip_addr_t *s_phostent_addr[2];
+  HOSTENT_STORAGE char s_hostname[DNS_MAX_NAME_LENGTH + 1];
+
+  /* query host IP address */
+  err = netconn_gethostbyname(name, &addr);
+  if (err != ERR_OK) {
+    LWIP_DEBUGF(DNS_DEBUG, ("lwip_gethostbyname(%s) failed, err=%d\n", name, err));
+    h_errno = HOST_NOT_FOUND;
+    return NULL;
+  }
+
+  /* fill hostent */
+  s_hostent_addr = addr;
+  s_phostent_addr[0] = &s_hostent_addr;
+  s_phostent_addr[1] = NULL;
+  strncpy(s_hostname, name, DNS_MAX_NAME_LENGTH);
+  s_hostname[DNS_MAX_NAME_LENGTH] = 0;
+  s_hostent.h_name = s_hostname;
+  s_aliases = NULL;
+  s_hostent.h_aliases = &s_aliases;
+  s_hostent.h_addrtype = AF_INET;
+  s_hostent.h_length = sizeof(ip_addr_t);
+  s_hostent.h_addr_list = (char**)&s_phostent_addr;
+
+#if DNS_DEBUG
+  /* dump hostent */
+  LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_name           == %s\n", s_hostent.h_name));
+  LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_aliases        == %p\n", (void*)s_hostent.h_aliases));
+  /* h_aliases are always empty */
+  LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addrtype       == %d\n", s_hostent.h_addrtype));
+  LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_length         == %d\n", s_hostent.h_length));
+  LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addr_list      == %p\n", (void*)s_hostent.h_addr_list));
+  if (s_hostent.h_addr_list != NULL) {
+    u8_t idx;
+    for (idx=0; s_hostent.h_addr_list[idx]; idx++) {
+      LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addr_list[%i]   == %p\n", idx, s_hostent.h_addr_list[idx]));
+      LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addr_list[%i]-> == %s\n", idx, ipaddr_ntoa((ip_addr_t*)s_hostent.h_addr_list[idx])));
+    }
+  }
+#endif /* DNS_DEBUG */
+
+#if LWIP_DNS_API_HOSTENT_STORAGE
+  /* this function should return the "per-thread" hostent after copy from s_hostent */
+  return sys_thread_hostent(&s_hostent);
+#else
+  return &s_hostent;
+#endif /* LWIP_DNS_API_HOSTENT_STORAGE */
+}
+
+/**
+ * Thread-safe variant of lwip_gethostbyname: instead of using a static
+ * buffer, this function takes buffer and errno pointers as arguments
+ * and uses these for the result.
+ *
+ * @param name the hostname to resolve
+ * @param ret pre-allocated struct where to store the result
+ * @param buf pre-allocated buffer where to store additional data
+ * @param buflen the size of buf
+ * @param result pointer to a hostent pointer that is set to ret on success
+ *               and set to zero on error
+ * @param h_errnop pointer to an int where to store errors (instead of modifying
+ *                 the global h_errno)
+ * @return 0 on success, non-zero on error, additional error information
+ *         is stored in *h_errnop instead of h_errno to be thread-safe
+ */
+int
+lwip_gethostbyname_r(const char *name, struct hostent *ret, char *buf,
+                size_t buflen, struct hostent **result, int *h_errnop)
+{
+  err_t err;
+  struct gethostbyname_r_helper *h;
+  char *hostname;
+  size_t namelen;
+  int lh_errno;
+
+  if (h_errnop == NULL) {
+    /* ensure h_errnop is never NULL */
+    h_errnop = &lh_errno;
+  }
+
+  if (result == NULL) {
+    /* not all arguments given */
+    *h_errnop = EINVAL;
+    return -1;
+  }
+  /* first thing to do: set *result to nothing */
+  *result = NULL;
+  if ((name == NULL) || (ret == NULL) || (buf == NULL)) {
+    /* not all arguments given */
+    *h_errnop = EINVAL;
+    return -1;
+  }
+
+  namelen = strlen(name);
+  if (buflen < (sizeof(struct gethostbyname_r_helper) + namelen + 1 + (MEM_ALIGNMENT - 1))) {
+    /* buf can't hold the data needed + a copy of name */
+    *h_errnop = ERANGE;
+    return -1;
+  }
+
+  h = (struct gethostbyname_r_helper*)LWIP_MEM_ALIGN(buf);
+  hostname = ((char*)h) + sizeof(struct gethostbyname_r_helper);
+
+  /* query host IP address */
+  err = netconn_gethostbyname(name, &h->addr);
+  if (err != ERR_OK) {
+    LWIP_DEBUGF(DNS_DEBUG, ("lwip_gethostbyname(%s) failed, err=%d\n", name, err));
+    *h_errnop = HOST_NOT_FOUND;
+    return -1;
+  }
+
+  /* copy the hostname into buf */
+  MEMCPY(hostname, name, namelen);
+  hostname[namelen] = 0;
+
+  /* fill hostent */
+  h->addr_list[0] = &h->addr;
+  h->addr_list[1] = NULL;
+  h->aliases = NULL;
+  ret->h_name = hostname;
+  ret->h_aliases = &h->aliases;
+  ret->h_addrtype = AF_INET;
+  ret->h_length = sizeof(ip_addr_t);
+  ret->h_addr_list = (char**)&h->addr_list;
+
+  /* set result != NULL */
+  *result = ret;
+
+  /* return success */
+  return 0;
+}
+
+/**
+ * Frees one or more addrinfo structures returned by getaddrinfo(), along with
+ * any additional storage associated with those structures. If the ai_next field
+ * of the structure is not null, the entire list of structures is freed.
+ *
+ * @param ai struct addrinfo to free
+ */
+void
+lwip_freeaddrinfo(struct addrinfo *ai)
+{
+  struct addrinfo *next;
+
+  while (ai != NULL) {
+    next = ai->ai_next;
+    memp_free(MEMP_NETDB, ai);
+    ai = next;
+  }
+}
+
+/**
+ * Translates the name of a service location (for example, a host name) and/or
+ * a service name and returns a set of socket addresses and associated
+ * information to be used in creating a socket with which to address the
+ * specified service.
+ * Memory for the result is allocated internally and must be freed by calling
+ * lwip_freeaddrinfo()!
+ *
+ * Due to a limitation in dns_gethostbyname, only the first address of a
+ * host is returned.
+ * Also, service names are not supported (only port numbers)!
+ *
+ * @param nodename descriptive name or address string of the host
+ *                 (may be NULL -> local address)
+ * @param servname port number as string of NULL
+ * @param hints structure containing input values that set socktype and protocol
+ * @param res pointer to a pointer where to store the result (set to NULL on failure)
+ * @return 0 on success, non-zero on failure
+ *
+ * @todo: implement AI_V4MAPPED, AI_ADDRCONFIG
+ */
+int
+lwip_getaddrinfo(const char *nodename, const char *servname,
+       const struct addrinfo *hints, struct addrinfo **res)
+{
+  err_t err;
+  ip_addr_t addr;
+  struct addrinfo *ai;
+  struct sockaddr_storage *sa = NULL;
+  int port_nr = 0;
+  size_t total_size;
+  size_t namelen = 0;
+  int ai_family;
+
+  if (res == NULL) {
+    return EAI_FAIL;
+  }
+  *res = NULL;
+  if ((nodename == NULL) && (servname == NULL)) {
+    return EAI_NONAME;
+  }
+
+  if (hints != NULL) {
+    ai_family = hints->ai_family;
+    if ((ai_family != AF_UNSPEC)
+#if LWIP_IPV4
+      && (ai_family != AF_INET)
+#endif /* LWIP_IPV4 */
+#if LWIP_IPV6
+      && (ai_family != AF_INET6)
+#endif /* LWIP_IPV6 */
+      ) {
+      return EAI_FAMILY;
+    }
+  } else {
+    ai_family = AF_UNSPEC;
+  }
+
+  if (servname != NULL) {
+    /* service name specified: convert to port number
+     * @todo?: currently, only ASCII integers (port numbers) are supported (AI_NUMERICSERV)! */
+    port_nr = atoi(servname);
+    if ((port_nr <= 0) || (port_nr > 0xffff)) {
+      return EAI_SERVICE;
+    }
+  }
+
+  if (nodename != NULL) {
+    /* service location specified, try to resolve */
+    if ((hints != NULL) && (hints->ai_flags & AI_NUMERICHOST)) {
+      /* no DNS lookup, just parse for an address string */
+      if (!ipaddr_aton(nodename, &addr)) {
+        return EAI_NONAME;
+      }
+#if LWIP_IPV4 && LWIP_IPV6
+      if ((IP_IS_V6_VAL(addr) && ai_family == AF_INET) ||
+          (IP_IS_V4_VAL(addr) && ai_family == AF_INET6)) {
+        return EAI_NONAME;
+      }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+    } else {
+#if LWIP_IPV4 && LWIP_IPV6
+      /* AF_UNSPEC: prefer IPv4 */
+      u8_t type = NETCONN_DNS_IPV4_IPV6;
+      if (ai_family == AF_INET) {
+        type = NETCONN_DNS_IPV4;
+      } else if (ai_family == AF_INET6) {
+        type = NETCONN_DNS_IPV6;
+      }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+      err = netconn_gethostbyname_addrtype(nodename, &addr, type);
+      if (err != ERR_OK) {
+        return EAI_FAIL;
+      }
+    }
+  } else {
+    /* service location specified, use loopback address */
+    if ((hints != NULL) && (hints->ai_flags & AI_PASSIVE)) {
+      ip_addr_set_any(ai_family == AF_INET6, &addr);
+    } else {
+      ip_addr_set_loopback(ai_family == AF_INET6, &addr);
+    }
+  }
+
+  total_size = sizeof(struct addrinfo) + sizeof(struct sockaddr_storage);
+  if (nodename != NULL) {
+    namelen = strlen(nodename);
+    if (namelen > DNS_MAX_NAME_LENGTH) {
+      /* invalid name length */
+      return EAI_FAIL;
+    }
+    LWIP_ASSERT("namelen is too long", total_size + namelen + 1 > total_size);
+    total_size += namelen + 1;
+  }
+  /* If this fails, please report to lwip-devel! :-) */
+  LWIP_ASSERT("total_size <= NETDB_ELEM_SIZE: please report this!",
+    total_size <= NETDB_ELEM_SIZE);
+  ai = (struct addrinfo *)memp_malloc(MEMP_NETDB);
+  if (ai == NULL) {
+    return EAI_MEMORY;
+  }
+  memset(ai, 0, total_size);
+  /* cast through void* to get rid of alignment warnings */
+  sa = (struct sockaddr_storage *)(void*)((u8_t*)ai + sizeof(struct addrinfo));
+  if (IP_IS_V6_VAL(addr)) {
+#if LWIP_IPV6
+    struct sockaddr_in6 *sa6 = (struct sockaddr_in6*)sa;
+    /* set up sockaddr */
+    inet6_addr_from_ip6addr(&sa6->sin6_addr, ip_2_ip6(&addr));
+    sa6->sin6_family = AF_INET6;
+    sa6->sin6_len = sizeof(struct sockaddr_in6);
+    sa6->sin6_port = lwip_htons((u16_t)port_nr);
+    ai->ai_family = AF_INET6;
+#endif /* LWIP_IPV6 */
+  } else {
+#if LWIP_IPV4
+    struct sockaddr_in *sa4 = (struct sockaddr_in*)sa;
+    /* set up sockaddr */
+    inet_addr_from_ip4addr(&sa4->sin_addr, ip_2_ip4(&addr));
+    sa4->sin_family = AF_INET;
+    sa4->sin_len = sizeof(struct sockaddr_in);
+    sa4->sin_port = lwip_htons((u16_t)port_nr);
+    ai->ai_family = AF_INET;
+#endif /* LWIP_IPV4 */
+  }
+
+  /* set up addrinfo */
+  if (hints != NULL) {
+    /* copy socktype & protocol from hints if specified */
+    ai->ai_socktype = hints->ai_socktype;
+    ai->ai_protocol = hints->ai_protocol;
+  }
+  if (nodename != NULL) {
+    /* copy nodename to canonname if specified */
+    ai->ai_canonname = ((char*)ai + sizeof(struct addrinfo) + sizeof(struct sockaddr_storage));
+    MEMCPY(ai->ai_canonname, nodename, namelen);
+    ai->ai_canonname[namelen] = 0;
+  }
+  ai->ai_addrlen = sizeof(struct sockaddr_storage);
+  ai->ai_addr = (struct sockaddr*)sa;
+
+  *res = ai;
+
+  return 0;
+}
+
+#endif /* LWIP_DNS && LWIP_SOCKET */

+ 221 - 160
thirdparty/lwip/src/api/netifapi.c

@@ -1,160 +1,221 @@
-/**
- * @file
- * Network Interface Sequential API module
- *
- */
-
-/*
- * Redistribution and use in source and binary forms, with or without modification, 
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission. 
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
- * OF SUCH DAMAGE.
- *
- * This file is part of the lwIP TCP/IP stack.
- * 
- */
-
-#include "lwip/opt.h"
-
-#if LWIP_NETIF_API /* don't build if not configured for use in lwipopts.h */
-
-#include "lwip/netifapi.h"
-#include "lwip/tcpip.h"
-
-/**
- * Call netif_add() inside the tcpip_thread context.
- */
-void
-do_netifapi_netif_add(struct netifapi_msg_msg *msg)
-{
-  if (!netif_add( msg->netif,
-                  msg->msg.add.ipaddr,
-                  msg->msg.add.netmask,
-                  msg->msg.add.gw,
-                  msg->msg.add.state,
-                  msg->msg.add.init,
-                  msg->msg.add.input)) {
-    msg->err = ERR_IF;
-  } else {
-    msg->err = ERR_OK;
-  }
-  TCPIP_NETIFAPI_ACK(msg);
-}
-
-/**
- * Call netif_set_addr() inside the tcpip_thread context.
- */
-void
-do_netifapi_netif_set_addr(struct netifapi_msg_msg *msg)
-{
-  netif_set_addr( msg->netif,
-                  msg->msg.add.ipaddr,
-                  msg->msg.add.netmask,
-                  msg->msg.add.gw);
-  msg->err = ERR_OK;
-  TCPIP_NETIFAPI_ACK(msg);
-}
-
-/**
- * Call the "errtfunc" (or the "voidfunc" if "errtfunc" is NULL) inside the
- * tcpip_thread context.
- */
-void
-do_netifapi_netif_common(struct netifapi_msg_msg *msg)
-{
-  if (msg->msg.common.errtfunc != NULL) {
-    msg->err = msg->msg.common.errtfunc(msg->netif);
-  } else {
-    msg->err = ERR_OK;
-    msg->msg.common.voidfunc(msg->netif);
-  }
-  TCPIP_NETIFAPI_ACK(msg);
-}
-
-/**
- * Call netif_add() in a thread-safe way by running that function inside the
- * tcpip_thread context.
- *
- * @note for params @see netif_add()
- */
-err_t
-netifapi_netif_add(struct netif *netif,
-                   ip_addr_t *ipaddr,
-                   ip_addr_t *netmask,
-                   ip_addr_t *gw,
-                   void *state,
-                   netif_init_fn init,
-                   netif_input_fn input)
-{
-  struct netifapi_msg msg;
-  msg.function = do_netifapi_netif_add;
-  msg.msg.netif = netif;
-  msg.msg.msg.add.ipaddr  = ipaddr;
-  msg.msg.msg.add.netmask = netmask;
-  msg.msg.msg.add.gw      = gw;
-  msg.msg.msg.add.state   = state;
-  msg.msg.msg.add.init    = init;
-  msg.msg.msg.add.input   = input;
-  TCPIP_NETIFAPI(&msg);
-  return msg.msg.err;
-}
-
-/**
- * Call netif_set_addr() in a thread-safe way by running that function inside the
- * tcpip_thread context.
- *
- * @note for params @see netif_set_addr()
- */
-err_t
-netifapi_netif_set_addr(struct netif *netif,
-                        ip_addr_t *ipaddr,
-                        ip_addr_t *netmask,
-                        ip_addr_t *gw)
-{
-  struct netifapi_msg msg;
-  msg.function = do_netifapi_netif_set_addr;
-  msg.msg.netif = netif;
-  msg.msg.msg.add.ipaddr  = ipaddr;
-  msg.msg.msg.add.netmask = netmask;
-  msg.msg.msg.add.gw      = gw;
-  TCPIP_NETIFAPI(&msg);
-  return msg.msg.err;
-}
-
-/**
- * call the "errtfunc" (or the "voidfunc" if "errtfunc" is NULL) in a thread-safe
- * way by running that function inside the tcpip_thread context.
- *
- * @note use only for functions where there is only "netif" parameter.
- */
-err_t
-netifapi_netif_common(struct netif *netif, netifapi_void_fn voidfunc,
-                       netifapi_errt_fn errtfunc)
-{
-  struct netifapi_msg msg;
-  msg.function = do_netifapi_netif_common;
-  msg.msg.netif = netif;
-  msg.msg.msg.common.voidfunc = voidfunc;
-  msg.msg.msg.common.errtfunc = errtfunc;
-  TCPIP_NETIFAPI(&msg);
-  return msg.msg.err;
-}
-
-#endif /* LWIP_NETIF_API */
+/**
+ * @file
+ * Network Interface Sequential API module
+ *
+ * @defgroup netifapi NETIF API
+ * @ingroup sequential_api
+ * Thread-safe functions to be called from non-TCPIP threads
+ * 
+ * @defgroup netifapi_netif NETIF related
+ * @ingroup netifapi
+ * To be called from non-TCPIP threads 
+ */
+
+/*
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_NETIF_API /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/netifapi.h"
+#include "lwip/memp.h"
+#include "lwip/priv/tcpip_priv.h"
+
+#define NETIFAPI_VAR_REF(name)      API_VAR_REF(name)
+#define NETIFAPI_VAR_DECLARE(name)  API_VAR_DECLARE(struct netifapi_msg, name)
+#define NETIFAPI_VAR_ALLOC(name)    API_VAR_ALLOC(struct netifapi_msg, MEMP_NETIFAPI_MSG, name, ERR_MEM)
+#define NETIFAPI_VAR_FREE(name)     API_VAR_FREE(MEMP_NETIFAPI_MSG, name)
+
+/**
+ * Call netif_add() inside the tcpip_thread context.
+ */
+static err_t
+netifapi_do_netif_add(struct tcpip_api_call_data *m)
+{
+  /* cast through void* to silence alignment warnings. 
+   * We know it works because the structs have been instantiated as struct netifapi_msg */
+  struct netifapi_msg *msg = (struct netifapi_msg*)(void*)m;
+  
+  if (!netif_add( msg->netif,
+#if LWIP_IPV4
+                  API_EXPR_REF(msg->msg.add.ipaddr),
+                  API_EXPR_REF(msg->msg.add.netmask),
+                  API_EXPR_REF(msg->msg.add.gw),
+#endif /* LWIP_IPV4 */
+                  msg->msg.add.state,
+                  msg->msg.add.init,
+                  msg->msg.add.input)) {
+    return ERR_IF;
+  } else {
+    return ERR_OK;
+  }
+}
+
+#if LWIP_IPV4
+/**
+ * Call netif_set_addr() inside the tcpip_thread context.
+ */
+static err_t
+netifapi_do_netif_set_addr(struct tcpip_api_call_data *m)
+{
+  /* cast through void* to silence alignment warnings. 
+   * We know it works because the structs have been instantiated as struct netifapi_msg */
+  struct netifapi_msg *msg = (struct netifapi_msg*)(void*)m;
+
+  netif_set_addr( msg->netif,
+                  API_EXPR_REF(msg->msg.add.ipaddr),
+                  API_EXPR_REF(msg->msg.add.netmask),
+                  API_EXPR_REF(msg->msg.add.gw));
+  return ERR_OK;
+}
+#endif /* LWIP_IPV4 */
+
+/**
+ * Call the "errtfunc" (or the "voidfunc" if "errtfunc" is NULL) inside the
+ * tcpip_thread context.
+ */
+static err_t
+netifapi_do_netif_common(struct tcpip_api_call_data *m)
+{
+  /* cast through void* to silence alignment warnings. 
+   * We know it works because the structs have been instantiated as struct netifapi_msg */
+  struct netifapi_msg *msg = (struct netifapi_msg*)(void*)m;
+
+  if (msg->msg.common.errtfunc != NULL) {
+    return msg->msg.common.errtfunc(msg->netif);
+  } else {
+    msg->msg.common.voidfunc(msg->netif);
+    return ERR_OK;
+  }
+}
+
+/**
+ * @ingroup netifapi_netif
+ * Call netif_add() in a thread-safe way by running that function inside the
+ * tcpip_thread context.
+ *
+ * @note for params @see netif_add()
+ */
+err_t
+netifapi_netif_add(struct netif *netif,
+#if LWIP_IPV4
+                   const ip4_addr_t *ipaddr, const ip4_addr_t *netmask, const ip4_addr_t *gw,
+#endif /* LWIP_IPV4 */
+                   void *state, netif_init_fn init, netif_input_fn input)
+{
+  err_t err;
+  NETIFAPI_VAR_DECLARE(msg);
+  NETIFAPI_VAR_ALLOC(msg);
+
+#if LWIP_IPV4
+  if (ipaddr == NULL) {
+    ipaddr = IP4_ADDR_ANY4;
+  }
+  if (netmask == NULL) {
+    netmask = IP4_ADDR_ANY4;
+  }
+  if (gw == NULL) {
+    gw = IP4_ADDR_ANY4;
+  }
+#endif /* LWIP_IPV4 */
+
+  NETIFAPI_VAR_REF(msg).netif = netif;
+#if LWIP_IPV4
+  NETIFAPI_VAR_REF(msg).msg.add.ipaddr  = NETIFAPI_VAR_REF(ipaddr);
+  NETIFAPI_VAR_REF(msg).msg.add.netmask = NETIFAPI_VAR_REF(netmask);
+  NETIFAPI_VAR_REF(msg).msg.add.gw      = NETIFAPI_VAR_REF(gw);
+#endif /* LWIP_IPV4 */
+  NETIFAPI_VAR_REF(msg).msg.add.state   = state;
+  NETIFAPI_VAR_REF(msg).msg.add.init    = init;
+  NETIFAPI_VAR_REF(msg).msg.add.input   = input;
+  err = tcpip_api_call(netifapi_do_netif_add, &API_VAR_REF(msg).call);
+  NETIFAPI_VAR_FREE(msg);
+  return err;
+}
+
+#if LWIP_IPV4
+/**
+ * @ingroup netifapi_netif
+ * Call netif_set_addr() in a thread-safe way by running that function inside the
+ * tcpip_thread context.
+ *
+ * @note for params @see netif_set_addr()
+ */
+err_t
+netifapi_netif_set_addr(struct netif *netif,
+                        const ip4_addr_t *ipaddr,
+                        const ip4_addr_t *netmask,
+                        const ip4_addr_t *gw)
+{
+  err_t err;
+  NETIFAPI_VAR_DECLARE(msg);
+  NETIFAPI_VAR_ALLOC(msg);
+
+  if (ipaddr == NULL) {
+    ipaddr = IP4_ADDR_ANY4;
+  }
+  if (netmask == NULL) {
+    netmask = IP4_ADDR_ANY4;
+  }
+  if (gw == NULL) {
+    gw = IP4_ADDR_ANY4;
+  }
+
+  NETIFAPI_VAR_REF(msg).netif = netif;
+  NETIFAPI_VAR_REF(msg).msg.add.ipaddr  = NETIFAPI_VAR_REF(ipaddr);
+  NETIFAPI_VAR_REF(msg).msg.add.netmask = NETIFAPI_VAR_REF(netmask);
+  NETIFAPI_VAR_REF(msg).msg.add.gw      = NETIFAPI_VAR_REF(gw);
+  err = tcpip_api_call(netifapi_do_netif_set_addr, &API_VAR_REF(msg).call);
+  NETIFAPI_VAR_FREE(msg);
+  return err;
+}
+#endif /* LWIP_IPV4 */
+
+/**
+ * call the "errtfunc" (or the "voidfunc" if "errtfunc" is NULL) in a thread-safe
+ * way by running that function inside the tcpip_thread context.
+ *
+ * @note use only for functions where there is only "netif" parameter.
+ */
+err_t
+netifapi_netif_common(struct netif *netif, netifapi_void_fn voidfunc,
+                       netifapi_errt_fn errtfunc)
+{
+  err_t err;
+  NETIFAPI_VAR_DECLARE(msg);
+  NETIFAPI_VAR_ALLOC(msg);
+
+  NETIFAPI_VAR_REF(msg).netif = netif;
+  NETIFAPI_VAR_REF(msg).msg.common.voidfunc = voidfunc;
+  NETIFAPI_VAR_REF(msg).msg.common.errtfunc = errtfunc;
+  err = tcpip_api_call(netifapi_do_netif_common, &API_VAR_REF(msg).call);
+  NETIFAPI_VAR_FREE(msg);
+  return err;
+}
+
+#endif /* LWIP_NETIF_API */

+ 2827 - 2374
thirdparty/lwip/src/api/sockets.c

@@ -1,2374 +1,2827 @@
-/**
- * @file
- * Sockets BSD-Like API module
- *
- */
-
-/*
- * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * This file is part of the lwIP TCP/IP stack.
- *
- * Author: Adam Dunkels <adam@sics.se>
- *
- * Improved by Marc Boucher <marc@mbsi.ca> and David Haas <dhaas@alum.rpi.edu>
- *
- */
-
-#include "lwip/opt.h"
-
-#if LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */
-
-#include "lwip/sockets.h"
-#include "lwip/api.h"
-#include "lwip/sys.h"
-#include "lwip/igmp.h"
-#include "lwip/inet.h"
-#include "lwip/tcp.h"
-#include "lwip/raw.h"
-#include "lwip/udp.h"
-#include "lwip/tcpip.h"
-#include "lwip/pbuf.h"
-#if LWIP_CHECKSUM_ON_COPY
-#include "lwip/inet_chksum.h"
-#endif
-
-#include <string.h>
-
-#define NUM_SOCKETS MEMP_NUM_NETCONN
-
-/** Contains all internal pointers and states used for a socket */
-struct lwip_sock {
-  /** sockets currently are built on netconns, each socket has one netconn */
-  struct netconn *conn;
-  /** data that was left from the previous read */
-  void *lastdata;
-  /** offset in the data that was left from the previous read */
-  u16_t lastoffset;
-  /** number of times data was received, set by event_callback(),
-      tested by the receive and select functions */
-  s16_t rcvevent;
-  /** number of times data was ACKed (free send buffer), set by event_callback(),
-      tested by select */
-  u16_t sendevent;
-  /** error happened for this socket, set by event_callback(), tested by select */
-  u16_t errevent; 
-  /** last error that occurred on this socket */
-  int err;
-  /** counter of how many threads are waiting for this socket using select */
-  int select_waiting;
-};
-
-/** Description for a task waiting in select */
-struct lwip_select_cb {
-  /** Pointer to the next waiting task */
-  struct lwip_select_cb *next;
-  /** Pointer to the previous waiting task */
-  struct lwip_select_cb *prev;
-  /** readset passed to select */
-  fd_set *readset;
-  /** writeset passed to select */
-  fd_set *writeset;
-  /** unimplemented: exceptset passed to select */
-  fd_set *exceptset;
-  /** don't signal the same semaphore twice: set to 1 when signalled */
-  int sem_signalled;
-  /** semaphore to wake up a task waiting for select */
-  sys_sem_t sem;
-};
-
-/** This struct is used to pass data to the set/getsockopt_internal
- * functions running in tcpip_thread context (only a void* is allowed) */
-struct lwip_setgetsockopt_data {
-  /** socket struct for which to change options */
-  struct lwip_sock *sock;
-#ifdef LWIP_DEBUG
-  /** socket index for which to change options */
-  int s;
-#endif /* LWIP_DEBUG */
-  /** level of the option to process */
-  int level;
-  /** name of the option to process */
-  int optname;
-  /** set: value to set the option to
-    * get: value of the option is stored here */
-  void *optval;
-  /** size of *optval */
-  socklen_t *optlen;
-  /** if an error occures, it is temporarily stored here */
-  err_t err;
-};
-
-/** The global array of available sockets */
-static struct lwip_sock sockets[NUM_SOCKETS];
-/** The global list of tasks waiting for select */
-static struct lwip_select_cb *select_cb_list;
-/** This counter is increased from lwip_select when the list is chagned
-    and checked in event_callback to see if it has changed. */
-static volatile int select_cb_ctr;
-
-/** Table to quickly map an lwIP error (err_t) to a socket error
-  * by using -err as an index */
-static const int err_to_errno_table[] = {
-  0,             /* ERR_OK          0      No error, everything OK. */
-  ENOMEM,        /* ERR_MEM        -1      Out of memory error.     */
-  ENOBUFS,       /* ERR_BUF        -2      Buffer error.            */
-  EWOULDBLOCK,   /* ERR_TIMEOUT    -3      Timeout                  */
-  EHOSTUNREACH,  /* ERR_RTE        -4      Routing problem.         */
-  EINPROGRESS,   /* ERR_INPROGRESS -5      Operation in progress    */
-  EINVAL,        /* ERR_VAL        -6      Illegal value.           */
-  EWOULDBLOCK,   /* ERR_WOULDBLOCK -7      Operation would block.   */
-  EADDRINUSE,    /* ERR_USE        -8      Address in use.          */
-  EALREADY,      /* ERR_ISCONN     -9      Already connected.       */
-  ECONNABORTED,  /* ERR_ABRT       -10     Connection aborted.      */
-  ECONNRESET,    /* ERR_RST        -11     Connection reset.        */
-  ENOTCONN,      /* ERR_CLSD       -12     Connection closed.       */
-  ENOTCONN,      /* ERR_CONN       -13     Not connected.           */
-  EIO,           /* ERR_ARG        -14     Illegal argument.        */
-  -1,            /* ERR_IF         -15     Low-level netif error    */
-};
-
-#define ERR_TO_ERRNO_TABLE_SIZE \
-  (sizeof(err_to_errno_table)/sizeof(err_to_errno_table[0]))
-
-#define err_to_errno(err) \
-  ((unsigned)(-(err)) < ERR_TO_ERRNO_TABLE_SIZE ? \
-    err_to_errno_table[-(err)] : EIO)
-
-#ifdef ERRNO
-#ifndef set_errno
-#define set_errno(err) errno = (err)
-#endif
-#else /* ERRNO */
-#define set_errno(err)
-#endif /* ERRNO */
-
-#define sock_set_errno(sk, e) do { \
-  sk->err = (e); \
-  set_errno(sk->err); \
-} while (0)
-
-/* Forward delcaration of some functions */
-static void event_callback(struct netconn *conn, enum netconn_evt evt, u16_t len);
-static void lwip_getsockopt_internal(void *arg);
-static void lwip_setsockopt_internal(void *arg);
-
-/**
- * Initialize this module. This function has to be called before any other
- * functions in this module!
- */
-void
-lwip_socket_init(void)
-{
-}
-
-/**
- * Map a externally used socket index to the internal socket representation.
- *
- * @param s externally used socket index
- * @return struct lwip_sock for the socket or NULL if not found
- */
-static struct lwip_sock *
-get_socket(int s)
-{
-  struct lwip_sock *sock;
-
-  if ((s < 0) || (s >= NUM_SOCKETS)) {
-    LWIP_DEBUGF(SOCKETS_DEBUG, ("get_socket(%d): invalid\n", s));
-    set_errno(EBADF);
-    return NULL;
-  }
-
-  sock = &sockets[s];
-
-  if (!sock->conn) {
-    LWIP_DEBUGF(SOCKETS_DEBUG, ("get_socket(%d): not active\n", s));
-    set_errno(EBADF);
-    return NULL;
-  }
-
-  return sock;
-}
-
-/**
- * Same as get_socket but doesn't set errno
- *
- * @param s externally used socket index
- * @return struct lwip_sock for the socket or NULL if not found
- */
-static struct lwip_sock *
-tryget_socket(int s)
-{
-  if ((s < 0) || (s >= NUM_SOCKETS)) {
-    return NULL;
-  }
-  if (!sockets[s].conn) {
-    return NULL;
-  }
-  return &sockets[s];
-}
-
-/**
- * Allocate a new socket for a given netconn.
- *
- * @param newconn the netconn for which to allocate a socket
- * @param accepted 1 if socket has been created by accept(),
- *                 0 if socket has been created by socket()
- * @return the index of the new socket; -1 on error
- */
-static int
-alloc_socket(struct netconn *newconn, int accepted)
-{
-  int i;
-  SYS_ARCH_DECL_PROTECT(lev);
-
-  /* allocate a new socket identifier */
-  for (i = 0; i < NUM_SOCKETS; ++i) {
-    /* Protect socket array */
-    SYS_ARCH_PROTECT(lev);
-    if (!sockets[i].conn) {
-      sockets[i].conn       = newconn;
-      /* The socket is not yet known to anyone, so no need to protect
-         after having marked it as used. */
-      SYS_ARCH_UNPROTECT(lev);
-      sockets[i].lastdata   = NULL;
-      sockets[i].lastoffset = 0;
-      sockets[i].rcvevent   = 0;
-      /* TCP sendbuf is empty, but the socket is not yet writable until connected
-       * (unless it has been created by accept()). */
-      sockets[i].sendevent  = (newconn->type == NETCONN_TCP ? (accepted != 0) : 1);
-      sockets[i].errevent   = 0;
-      sockets[i].err        = 0;
-      sockets[i].select_waiting = 0;
-      return i;
-    }
-    SYS_ARCH_UNPROTECT(lev);
-  }
-  return -1;
-}
-
-/** Free a socket. The socket's netconn must have been
- * delete before!
- *
- * @param sock the socket to free
- * @param is_tcp != 0 for TCP sockets, used to free lastdata
- */
-static void
-free_socket(struct lwip_sock *sock, int is_tcp)
-{
-  void *lastdata;
-  SYS_ARCH_DECL_PROTECT(lev);
-
-  lastdata         = sock->lastdata;
-  sock->lastdata   = NULL;
-  sock->lastoffset = 0;
-  sock->err        = 0;
-
-  /* Protect socket array */
-  SYS_ARCH_PROTECT(lev);
-  sock->conn       = NULL;
-  SYS_ARCH_UNPROTECT(lev);
-  /* don't use 'sock' after this line, as another task might have allocated it */
-
-  if (lastdata != NULL) {
-    if (is_tcp) {
-      pbuf_free((struct pbuf *)lastdata);
-    } else {
-      netbuf_delete((struct netbuf *)lastdata);
-    }
-  }
-}
-
-/* Below this, the well-known socket functions are implemented.
- * Use google.com or opengroup.org to get a good description :-)
- *
- * Exceptions are documented!
- */
-
-int
-lwip_accept(int s, struct sockaddr *addr, socklen_t *addrlen)
-{
-  struct lwip_sock *sock, *nsock;
-  struct netconn *newconn;
-  ip_addr_t naddr;
-  u16_t port;
-  int newsock;
-  struct sockaddr_in sin;
-  err_t err;
-  SYS_ARCH_DECL_PROTECT(lev);
-
-  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d)...\n", s));
-  sock = get_socket(s);
-  if (!sock) {
-    return -1;
-  }
-
-  if (netconn_is_nonblocking(sock->conn) && (sock->rcvevent <= 0)) {
-    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d): returning EWOULDBLOCK\n", s));
-    sock_set_errno(sock, EWOULDBLOCK);
-    return -1;
-  }
-
-  /* wait for a new connection */
-  err = netconn_accept(sock->conn, &newconn);
-  if (err != ERR_OK) {
-    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d): netconn_acept failed, err=%d\n", s, err));
-    if (netconn_type(sock->conn) != NETCONN_TCP) {
-      sock_set_errno(sock, EOPNOTSUPP);
-      return EOPNOTSUPP;
-    }
-    sock_set_errno(sock, err_to_errno(err));
-    return -1;
-  }
-  LWIP_ASSERT("newconn != NULL", newconn != NULL);
-  /* Prevent automatic window updates, we do this on our own! */
-  netconn_set_noautorecved(newconn, 1);
-
-  /* get the IP address and port of the remote host */
-  err = netconn_peer(newconn, &naddr, &port);
-  if (err != ERR_OK) {
-    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d): netconn_peer failed, err=%d\n", s, err));
-    netconn_delete(newconn);
-    sock_set_errno(sock, err_to_errno(err));
-    return -1;
-  }
-
-  /* Note that POSIX only requires us to check addr is non-NULL. addrlen must
-   * not be NULL if addr is valid.
-   */
-  if (NULL != addr) {
-    LWIP_ASSERT("addr valid but addrlen NULL", addrlen != NULL);
-    memset(&sin, 0, sizeof(sin));
-    sin.sin_len = sizeof(sin);
-    sin.sin_family = AF_INET;
-    sin.sin_port = htons(port);
-    inet_addr_from_ipaddr(&sin.sin_addr, &naddr);
-
-    if (*addrlen > sizeof(sin))
-      *addrlen = sizeof(sin);
-
-    MEMCPY(addr, &sin, *addrlen);
-  }
-
-  newsock = alloc_socket(newconn, 1);
-  if (newsock == -1) {
-    netconn_delete(newconn);
-    sock_set_errno(sock, ENFILE);
-    return -1;
-  }
-  LWIP_ASSERT("invalid socket index", (newsock >= 0) && (newsock < NUM_SOCKETS));
-  LWIP_ASSERT("newconn->callback == event_callback", newconn->callback == event_callback);
-  nsock = &sockets[newsock];
-
-  /* See event_callback: If data comes in right away after an accept, even
-   * though the server task might not have created a new socket yet.
-   * In that case, newconn->socket is counted down (newconn->socket--),
-   * so nsock->rcvevent is >= 1 here!
-   */
-  SYS_ARCH_PROTECT(lev);
-  nsock->rcvevent += (s16_t)(-1 - newconn->socket);
-  newconn->socket = newsock;
-  SYS_ARCH_UNPROTECT(lev);
-
-  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d) returning new sock=%d addr=", s, newsock));
-  ip_addr_debug_print(SOCKETS_DEBUG, &naddr);
-  LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F"\n", port));
-
-  sock_set_errno(sock, 0);
-  return newsock;
-}
-
-int
-lwip_bind(int s, const struct sockaddr *name, socklen_t namelen)
-{
-  struct lwip_sock *sock;
-  ip_addr_t local_addr;
-  u16_t local_port;
-  err_t err;
-  const struct sockaddr_in *name_in;
-
-  sock = get_socket(s);
-  if (!sock) {
-    return -1;
-  }
-
-  /* check size, familiy and alignment of 'name' */
-  LWIP_ERROR("lwip_bind: invalid address", ((namelen == sizeof(struct sockaddr_in)) &&
-             ((name->sa_family) == AF_INET) && ((((mem_ptr_t)name) % 4) == 0)),
-             sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;);
-  name_in = (const struct sockaddr_in *)(void*)name;
-
-  inet_addr_to_ipaddr(&local_addr, &name_in->sin_addr);
-  local_port = name_in->sin_port;
-
-  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d, addr=", s));
-  ip_addr_debug_print(SOCKETS_DEBUG, &local_addr);
-  LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", ntohs(local_port)));
-
-  err = netconn_bind(sock->conn, &local_addr, ntohs(local_port));
-
-  if (err != ERR_OK) {
-    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d) failed, err=%d\n", s, err));
-    sock_set_errno(sock, err_to_errno(err));
-    return -1;
-  }
-
-  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d) succeeded\n", s));
-  sock_set_errno(sock, 0);
-  return 0;
-}
-
-int
-lwip_close(int s)
-{
-  struct lwip_sock *sock;
-  int is_tcp = 0;
-
-  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_close(%d)\n", s));
-
-  sock = get_socket(s);
-  if (!sock) {
-    return -1;
-  }
-
-  if(sock->conn != NULL) {
-    is_tcp = netconn_type(sock->conn) == NETCONN_TCP;
-  } else {
-    LWIP_ASSERT("sock->lastdata == NULL", sock->lastdata == NULL);
-  }
-
-  netconn_delete(sock->conn);
-
-  free_socket(sock, is_tcp);
-  set_errno(0);
-  return 0;
-}
-
-int
-lwip_connect(int s, const struct sockaddr *name, socklen_t namelen)
-{
-  struct lwip_sock *sock;
-  err_t err;
-  const struct sockaddr_in *name_in;
-
-  sock = get_socket(s);
-  if (!sock) {
-    return -1;
-  }
-
-  /* check size, familiy and alignment of 'name' */
-  LWIP_ERROR("lwip_connect: invalid address", ((namelen == sizeof(struct sockaddr_in)) &&
-             ((name->sa_family) == AF_INET) && ((((mem_ptr_t)name) % 4) == 0)),
-             sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;);
-  name_in = (const struct sockaddr_in *)(void*)name;
-
-  if (name_in->sin_family == AF_UNSPEC) {
-    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d, AF_UNSPEC)\n", s));
-    err = netconn_disconnect(sock->conn);
-  } else {
-    ip_addr_t remote_addr;
-    u16_t remote_port;
-
-    inet_addr_to_ipaddr(&remote_addr, &name_in->sin_addr);
-    remote_port = name_in->sin_port;
-
-    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d, addr=", s));
-    ip_addr_debug_print(SOCKETS_DEBUG, &remote_addr);
-    LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", ntohs(remote_port)));
-
-    err = netconn_connect(sock->conn, &remote_addr, ntohs(remote_port));
-  }
-
-  if (err != ERR_OK) {
-    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d) failed, err=%d\n", s, err));
-    sock_set_errno(sock, err_to_errno(err));
-    return -1;
-  }
-
-  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d) succeeded\n", s));
-  sock_set_errno(sock, 0);
-  return 0;
-}
-
-/**
- * Set a socket into listen mode.
- * The socket may not have been used for another connection previously.
- *
- * @param s the socket to set to listening mode
- * @param backlog (ATTENTION: needs TCP_LISTEN_BACKLOG=1)
- * @return 0 on success, non-zero on failure
- */
-int
-lwip_listen(int s, int backlog)
-{
-  struct lwip_sock *sock;
-  err_t err;
-
-  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_listen(%d, backlog=%d)\n", s, backlog));
-
-  sock = get_socket(s);
-  if (!sock) {
-    return -1;
-  }
-
-  /* limit the "backlog" parameter to fit in an u8_t */
-  backlog = LWIP_MIN(LWIP_MAX(backlog, 0), 0xff);
-
-  err = netconn_listen_with_backlog(sock->conn, (u8_t)backlog);
-
-  if (err != ERR_OK) {
-    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_listen(%d) failed, err=%d\n", s, err));
-    if (netconn_type(sock->conn) != NETCONN_TCP) {
-      sock_set_errno(sock, EOPNOTSUPP);
-      return EOPNOTSUPP;
-    }
-    sock_set_errno(sock, err_to_errno(err));
-    return -1;
-  }
-
-  sock_set_errno(sock, 0);
-  return 0;
-}
-
-int
-lwip_recvfrom(int s, void *mem, size_t len, int flags,
-        struct sockaddr *from, socklen_t *fromlen)
-{
-  struct lwip_sock *sock;
-  void             *buf = NULL;
-  struct pbuf      *p;
-  u16_t            buflen, copylen;
-  int              off = 0;
-  ip_addr_t        *addr;
-  u16_t            port;
-  u8_t             done = 0;
-  err_t            err;
-
-  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d, %p, %"SZT_F", 0x%x, ..)\n", s, mem, len, flags));
-  sock = get_socket(s);
-  if (!sock) {
-    return -1;
-  }
-
-  do {
-    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: top while sock->lastdata=%p\n", sock->lastdata));
-    /* Check if there is data left from the last recv operation. */
-    if (sock->lastdata) {
-      buf = sock->lastdata;
-    } else {
-      /* If this is non-blocking call, then check first */
-      if (((flags & MSG_DONTWAIT) || netconn_is_nonblocking(sock->conn)) && 
-          (sock->rcvevent <= 0)) {
-        if (off > 0) {
-          /* update receive window */
-          netconn_recved(sock->conn, (u32_t)off);
-          /* already received data, return that */
-          sock_set_errno(sock, 0);
-          return off;
-        }
-        LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): returning EWOULDBLOCK\n", s));
-        sock_set_errno(sock, EWOULDBLOCK);
-        return -1;
-      }
-
-      /* No data was left from the previous operation, so we try to get
-         some from the network. */
-      if (netconn_type(sock->conn) == NETCONN_TCP) {
-        err = netconn_recv_tcp_pbuf(sock->conn, (struct pbuf **)&buf);
-      } else {
-        err = netconn_recv(sock->conn, (struct netbuf **)&buf);
-      }
-      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: netconn_recv err=%d, netbuf=%p\n",
-        err, buf));
-
-      if (err != ERR_OK) {
-        if (off > 0) {
-          /* update receive window */
-          netconn_recved(sock->conn, (u32_t)off);
-          /* already received data, return that */
-          sock_set_errno(sock, 0);
-          return off;
-        }
-        /* We should really do some error checking here. */
-        LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): buf == NULL, error is \"%s\"!\n",
-          s, lwip_strerr(err)));
-        sock_set_errno(sock, err_to_errno(err));
-        if (err == ERR_CLSD) {
-          return 0;
-        } else {
-          return -1;
-        }
-      }
-      LWIP_ASSERT("buf != NULL", buf != NULL);
-      sock->lastdata = buf;
-    }
-
-    if (netconn_type(sock->conn) == NETCONN_TCP) {
-      p = (struct pbuf *)buf;
-    } else {
-      p = ((struct netbuf *)buf)->p;
-    }
-    buflen = p->tot_len;
-    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: buflen=%"U16_F" len=%"SZT_F" off=%d sock->lastoffset=%"U16_F"\n",
-      buflen, len, off, sock->lastoffset));
-
-    buflen -= sock->lastoffset;
-
-    if (len > buflen) {
-      copylen = buflen;
-    } else {
-      copylen = (u16_t)len;
-    }
-
-    /* copy the contents of the received buffer into
-    the supplied memory pointer mem */
-    pbuf_copy_partial(p, (u8_t*)mem + off, copylen, sock->lastoffset);
-
-    off += copylen;
-
-    if (netconn_type(sock->conn) == NETCONN_TCP) {
-      LWIP_ASSERT("invalid copylen, len would underflow", len >= copylen);
-      len -= copylen;
-      if ( (len <= 0) || 
-           (p->flags & PBUF_FLAG_PUSH) || 
-           (sock->rcvevent <= 0) || 
-           ((flags & MSG_PEEK)!=0)) {
-        done = 1;
-      }
-    } else {
-      done = 1;
-    }
-
-    /* Check to see from where the data was.*/
-    if (done) {
-      ip_addr_t fromaddr;
-      if (from && fromlen) {
-        struct sockaddr_in sin;
-
-        if (netconn_type(sock->conn) == NETCONN_TCP) {
-          addr = &fromaddr;
-          netconn_getaddr(sock->conn, addr, &port, 0);
-        } else {
-          addr = netbuf_fromaddr((struct netbuf *)buf);
-          port = netbuf_fromport((struct netbuf *)buf);
-        }
-
-        memset(&sin, 0, sizeof(sin));
-        sin.sin_len = sizeof(sin);
-        sin.sin_family = AF_INET;
-        sin.sin_port = htons(port);
-        inet_addr_from_ipaddr(&sin.sin_addr, addr);
-
-        if (*fromlen > sizeof(sin)) {
-          *fromlen = sizeof(sin);
-        }
-
-        MEMCPY(from, &sin, *fromlen);
-
-        LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): addr=", s));
-        ip_addr_debug_print(SOCKETS_DEBUG, addr);
-        LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F" len=%d\n", port, off));
-      } else {
-#if SOCKETS_DEBUG
-        if (netconn_type(sock->conn) == NETCONN_TCP) {
-          addr = &fromaddr;
-          netconn_getaddr(sock->conn, addr, &port, 0);
-        } else {
-          addr = netbuf_fromaddr((struct netbuf *)buf);
-          port = netbuf_fromport((struct netbuf *)buf);
-        }
-
-        LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): addr=", s));
-        ip_addr_debug_print(SOCKETS_DEBUG, addr);
-        LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F" len=%d\n", port, off));
-#endif /*  SOCKETS_DEBUG */
-      }
-    }
-
-    /* If we don't peek the incoming message... */
-    if ((flags & MSG_PEEK) == 0) {
-      /* If this is a TCP socket, check if there is data left in the
-         buffer. If so, it should be saved in the sock structure for next
-         time around. */
-      if ((netconn_type(sock->conn) == NETCONN_TCP) && (buflen - copylen > 0)) {
-        sock->lastdata = buf;
-        sock->lastoffset += copylen;
-        LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: lastdata now netbuf=%p\n", buf));
-      } else {
-        sock->lastdata = NULL;
-        sock->lastoffset = 0;
-        LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: deleting netbuf=%p\n", buf));
-        if (netconn_type(sock->conn) == NETCONN_TCP) {
-          pbuf_free((struct pbuf *)buf);
-        } else {
-          netbuf_delete((struct netbuf *)buf);
-        }
-      }
-    }
-  } while (!done);
-
-  if (off > 0) {
-    /* update receive window */
-    netconn_recved(sock->conn, (u32_t)off);
-  }
-  sock_set_errno(sock, 0);
-  return off;
-}
-
-int
-lwip_read(int s, void *mem, size_t len)
-{
-  return lwip_recvfrom(s, mem, len, 0, NULL, NULL);
-}
-
-int
-lwip_recv(int s, void *mem, size_t len, int flags)
-{
-  return lwip_recvfrom(s, mem, len, flags, NULL, NULL);
-}
-
-int
-lwip_send(int s, const void *data, size_t size, int flags)
-{
-  struct lwip_sock *sock;
-  err_t err;
-  u8_t write_flags;
-  size_t written;
-
-  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_send(%d, data=%p, size=%"SZT_F", flags=0x%x)\n",
-                              s, data, size, flags));
-
-  sock = get_socket(s);
-  if (!sock) {
-    return -1;
-  }
-
-  if (sock->conn->type != NETCONN_TCP) {
-#if (LWIP_UDP || LWIP_RAW)
-    return lwip_sendto(s, data, size, flags, NULL, 0);
-#else /* (LWIP_UDP || LWIP_RAW) */
-    sock_set_errno(sock, err_to_errno(ERR_ARG));
-    return -1;
-#endif /* (LWIP_UDP || LWIP_RAW) */
-  }
-
-  write_flags = NETCONN_COPY |
-    ((flags & MSG_MORE)     ? NETCONN_MORE      : 0) |
-    ((flags & MSG_DONTWAIT) ? NETCONN_DONTBLOCK : 0);
-  written = 0;
-  err = netconn_write_partly(sock->conn, data, size, write_flags, &written);
-
-  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_send(%d) err=%d written=%"SZT_F"\n", s, err, written));
-  sock_set_errno(sock, err_to_errno(err));
-  return (err == ERR_OK ? (int)written : -1);
-}
-
-int
-lwip_sendto(int s, const void *data, size_t size, int flags,
-       const struct sockaddr *to, socklen_t tolen)
-{
-  struct lwip_sock *sock;
-  err_t err;
-  u16_t short_size;
-  const struct sockaddr_in *to_in;
-  u16_t remote_port;
-#if !LWIP_TCPIP_CORE_LOCKING
-  struct netbuf buf;
-#endif
-
-  sock = get_socket(s);
-  if (!sock) {
-    return -1;
-  }
-
-  if (sock->conn->type == NETCONN_TCP) {
-#if LWIP_TCP
-    return lwip_send(s, data, size, flags);
-#else /* LWIP_TCP */
-    LWIP_UNUSED_ARG(flags);
-    sock_set_errno(sock, err_to_errno(ERR_ARG));
-    return -1;
-#endif /* LWIP_TCP */
-  }
-
-  /* @todo: split into multiple sendto's? */
-  LWIP_ASSERT("lwip_sendto: size must fit in u16_t", size <= 0xffff);
-  short_size = (u16_t)size;
-  LWIP_ERROR("lwip_sendto: invalid address", (((to == NULL) && (tolen == 0)) ||
-             ((tolen == sizeof(struct sockaddr_in)) &&
-             ((to->sa_family) == AF_INET) && ((((mem_ptr_t)to) % 4) == 0))),
-             sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;);
-  to_in = (const struct sockaddr_in *)(void*)to;
-
-#if LWIP_TCPIP_CORE_LOCKING
-  /* Should only be consider like a sample or a simple way to experiment this option (no check of "to" field...) */
-  {
-    struct pbuf* p;
-    ip_addr_t *remote_addr;
-
-#if LWIP_NETIF_TX_SINGLE_PBUF
-    p = pbuf_alloc(PBUF_TRANSPORT, short_size, PBUF_RAM);
-    if (p != NULL) {
-#if LWIP_CHECKSUM_ON_COPY
-      u16_t chksum = 0;
-      if (sock->conn->type != NETCONN_RAW) {
-        chksum = LWIP_CHKSUM_COPY(p->payload, data, short_size);
-      } else
-#endif /* LWIP_CHECKSUM_ON_COPY */
-      MEMCPY(p->payload, data, size);
-#else /* LWIP_NETIF_TX_SINGLE_PBUF */
-    p = pbuf_alloc(PBUF_TRANSPORT, short_size, PBUF_REF);
-    if (p != NULL) {
-      p->payload = (void*)data;
-#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
-
-      if (to_in != NULL) {
-        inet_addr_to_ipaddr_p(remote_addr, &to_in->sin_addr);
-        remote_port = ntohs(to_in->sin_port);
-      } else {
-        remote_addr = &sock->conn->pcb.ip->remote_ip;
-#if LWIP_UDP
-        if (NETCONNTYPE_GROUP(sock->conn->type) == NETCONN_UDP) {
-          remote_port = sock->conn->pcb.udp->remote_port;
-        } else
-#endif /* LWIP_UDP */
-        {
-          remote_port = 0;
-        }
-      }
-
-      LOCK_TCPIP_CORE();
-      if (netconn_type(sock->conn) == NETCONN_RAW) {
-#if LWIP_RAW
-        err = sock->conn->last_err = raw_sendto(sock->conn->pcb.raw, p, remote_addr);
-#else /* LWIP_RAW */
-        err = ERR_ARG;
-#endif /* LWIP_RAW */
-      }
-#if LWIP_UDP && LWIP_RAW
-      else
-#endif /* LWIP_UDP && LWIP_RAW */
-      {
-#if LWIP_UDP
-#if LWIP_CHECKSUM_ON_COPY && LWIP_NETIF_TX_SINGLE_PBUF
-        err = sock->conn->last_err = udp_sendto_chksum(sock->conn->pcb.udp, p,
-          remote_addr, remote_port, 1, chksum);
-#else /* LWIP_CHECKSUM_ON_COPY && LWIP_NETIF_TX_SINGLE_PBUF */
-        err = sock->conn->last_err = udp_sendto(sock->conn->pcb.udp, p,
-          remote_addr, remote_port);
-#endif /* LWIP_CHECKSUM_ON_COPY && LWIP_NETIF_TX_SINGLE_PBUF */
-#else /* LWIP_UDP */
-        err = ERR_ARG;
-#endif /* LWIP_UDP */
-      }
-      UNLOCK_TCPIP_CORE();
-      
-      pbuf_free(p);
-    } else {
-      err = ERR_MEM;
-    }
-  }
-#else /* LWIP_TCPIP_CORE_LOCKING */
-  /* initialize a buffer */
-  buf.p = buf.ptr = NULL;
-#if LWIP_CHECKSUM_ON_COPY
-  buf.flags = 0;
-#endif /* LWIP_CHECKSUM_ON_COPY */
-  if (to) {
-    inet_addr_to_ipaddr(&buf.addr, &to_in->sin_addr);
-    remote_port           = ntohs(to_in->sin_port);
-    netbuf_fromport(&buf) = remote_port;
-  } else {
-    remote_port           = 0;
-    ip_addr_set_any(&buf.addr);
-    netbuf_fromport(&buf) = 0;
-  }
-
-  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_sendto(%d, data=%p, short_size=%"U16_F", flags=0x%x to=",
-              s, data, short_size, flags));
-  ip_addr_debug_print(SOCKETS_DEBUG, &buf.addr);
-  LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F"\n", remote_port));
-
-  /* make the buffer point to the data that should be sent */
-#if LWIP_NETIF_TX_SINGLE_PBUF
-  /* Allocate a new netbuf and copy the data into it. */
-  if (netbuf_alloc(&buf, short_size) == NULL) {
-    err = ERR_MEM;
-  } else {
-#if LWIP_CHECKSUM_ON_COPY
-    if (sock->conn->type != NETCONN_RAW) {
-      u16_t chksum = LWIP_CHKSUM_COPY(buf.p->payload, data, short_size);
-      netbuf_set_chksum(&buf, chksum);
-      err = ERR_OK;
-    } else
-#endif /* LWIP_CHECKSUM_ON_COPY */
-    {
-      err = netbuf_take(&buf, data, short_size);
-    }
-  }
-#else /* LWIP_NETIF_TX_SINGLE_PBUF */
-  err = netbuf_ref(&buf, data, short_size);
-#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
-  if (err == ERR_OK) {
-    /* send the data */
-    err = netconn_send(sock->conn, &buf);
-  }
-
-  /* deallocated the buffer */
-  netbuf_free(&buf);
-#endif /* LWIP_TCPIP_CORE_LOCKING */
-  sock_set_errno(sock, err_to_errno(err));
-  return (err == ERR_OK ? short_size : -1);
-}
-
-int
-lwip_socket(int domain, int type, int protocol)
-{
-  struct netconn *conn;
-  int i;
-
-  LWIP_UNUSED_ARG(domain);
-
-  /* create a netconn */
-  switch (type) {
-  case SOCK_RAW:
-    conn = netconn_new_with_proto_and_callback(NETCONN_RAW, (u8_t)protocol, event_callback);
-    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_RAW, %d) = ",
-                                 domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol));
-    break;
-  case SOCK_DGRAM:
-    conn = netconn_new_with_callback( (protocol == IPPROTO_UDPLITE) ?
-                 NETCONN_UDPLITE : NETCONN_UDP, event_callback);
-    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_DGRAM, %d) = ",
-                                 domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol));
-    break;
-  case SOCK_STREAM:
-    conn = netconn_new_with_callback(NETCONN_TCP, event_callback);
-    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_STREAM, %d) = ",
-                                 domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol));
-    if (conn != NULL) {
-      /* Prevent automatic window updates, we do this on our own! */
-      netconn_set_noautorecved(conn, 1);
-    }
-    break;
-  default:
-    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%d, %d/UNKNOWN, %d) = -1\n",
-                                 domain, type, protocol));
-    set_errno(EINVAL);
-    return -1;
-  }
-
-  if (!conn) {
-    LWIP_DEBUGF(SOCKETS_DEBUG, ("-1 / ENOBUFS (could not create netconn)\n"));
-    set_errno(ENOBUFS);
-    return -1;
-  }
-
-  i = alloc_socket(conn, 0);
-
-  if (i == -1) {
-    netconn_delete(conn);
-    set_errno(ENFILE);
-    return -1;
-  }
-  conn->socket = i;
-  LWIP_DEBUGF(SOCKETS_DEBUG, ("%d\n", i));
-  set_errno(0);
-  return i;
-}
-
-int
-lwip_write(int s, const void *data, size_t size)
-{
-  return lwip_send(s, data, size, 0);
-}
-
-/**
- * Go through the readset and writeset lists and see which socket of the sockets
- * set in the sets has events. On return, readset, writeset and exceptset have
- * the sockets enabled that had events.
- *
- * exceptset is not used for now!!!
- *
- * @param maxfdp1 the highest socket index in the sets
- * @param readset_in:    set of sockets to check for read events
- * @param writeset_in:   set of sockets to check for write events
- * @param exceptset_in:  set of sockets to check for error events
- * @param readset_out:   set of sockets that had read events
- * @param writeset_out:  set of sockets that had write events
- * @param exceptset_out: set os sockets that had error events
- * @return number of sockets that had events (read/write/exception) (>= 0)
- */
-static int
-lwip_selscan(int maxfdp1, fd_set *readset_in, fd_set *writeset_in, fd_set *exceptset_in,
-             fd_set *readset_out, fd_set *writeset_out, fd_set *exceptset_out)
-{
-  int i, nready = 0;
-  fd_set lreadset, lwriteset, lexceptset;
-  struct lwip_sock *sock;
-  SYS_ARCH_DECL_PROTECT(lev);
-
-  FD_ZERO(&lreadset);
-  FD_ZERO(&lwriteset);
-  FD_ZERO(&lexceptset);
-
-  /* Go through each socket in each list to count number of sockets which
-     currently match */
-  for(i = 0; i < maxfdp1; i++) {
-    void* lastdata = NULL;
-    s16_t rcvevent = 0;
-    u16_t sendevent = 0;
-    u16_t errevent = 0;
-    /* First get the socket's status (protected)... */
-    SYS_ARCH_PROTECT(lev);
-    sock = tryget_socket(i);
-    if (sock != NULL) {
-      lastdata = sock->lastdata;
-      rcvevent = sock->rcvevent;
-      sendevent = sock->sendevent;
-      errevent = sock->errevent;
-    }
-    SYS_ARCH_UNPROTECT(lev);
-    /* ... then examine it: */
-    /* See if netconn of this socket is ready for read */
-    if (readset_in && FD_ISSET(i, readset_in) && ((lastdata != NULL) || (rcvevent > 0))) {
-      FD_SET(i, &lreadset);
-      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_selscan: fd=%d ready for reading\n", i));
-      nready++;
-    }
-    /* See if netconn of this socket is ready for write */
-    if (writeset_in && FD_ISSET(i, writeset_in) && (sendevent != 0)) {
-      FD_SET(i, &lwriteset);
-      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_selscan: fd=%d ready for writing\n", i));
-      nready++;
-    }
-    /* See if netconn of this socket had an error */
-    if (exceptset_in && FD_ISSET(i, exceptset_in) && (errevent != 0)) {
-      FD_SET(i, &lexceptset);
-      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_selscan: fd=%d ready for exception\n", i));
-      nready++;
-    }
-  }
-  /* copy local sets to the ones provided as arguments */
-  *readset_out = lreadset;
-  *writeset_out = lwriteset;
-  *exceptset_out = lexceptset;
-
-  LWIP_ASSERT("nready >= 0", nready >= 0);
-  return nready;
-}
-
-/**
- * Processing exceptset is not yet implemented.
- */
-int
-lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset,
-            struct timeval *timeout)
-{
-  u32_t waitres = 0;
-  int nready;
-  fd_set lreadset, lwriteset, lexceptset;
-  u32_t msectimeout;
-  struct lwip_select_cb select_cb;
-  err_t err;
-  int i;
-  SYS_ARCH_DECL_PROTECT(lev);
-
-  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select(%d, %p, %p, %p, tvsec=%"S32_F" tvusec=%"S32_F")\n",
-                  maxfdp1, (void *)readset, (void *) writeset, (void *) exceptset,
-                  timeout ? (s32_t)timeout->tv_sec : (s32_t)-1,
-                  timeout ? (s32_t)timeout->tv_usec : (s32_t)-1));
-
-  /* Go through each socket in each list to count number of sockets which
-     currently match */
-  nready = lwip_selscan(maxfdp1, readset, writeset, exceptset, &lreadset, &lwriteset, &lexceptset);
-
-  /* If we don't have any current events, then suspend if we are supposed to */
-  if (!nready) {
-    if (timeout && timeout->tv_sec == 0 && timeout->tv_usec == 0) {
-      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: no timeout, returning 0\n"));
-      /* This is OK as the local fdsets are empty and nready is zero,
-         or we would have returned earlier. */
-      goto return_copy_fdsets;
-    }
-
-    /* None ready: add our semaphore to list:
-       We don't actually need any dynamic memory. Our entry on the
-       list is only valid while we are in this function, so it's ok
-       to use local variables. */
-
-    select_cb.next = NULL;
-    select_cb.prev = NULL;
-    select_cb.readset = readset;
-    select_cb.writeset = writeset;
-    select_cb.exceptset = exceptset;
-    select_cb.sem_signalled = 0;
-    err = sys_sem_new(&select_cb.sem, 0);
-    if (err != ERR_OK) {
-      /* failed to create semaphore */
-      set_errno(ENOMEM);
-      return -1;
-    }
-
-    /* Protect the select_cb_list */
-    SYS_ARCH_PROTECT(lev);
-
-    /* Put this select_cb on top of list */
-    select_cb.next = select_cb_list;
-    if (select_cb_list != NULL) {
-      select_cb_list->prev = &select_cb;
-    }
-    select_cb_list = &select_cb;
-    /* Increasing this counter tells even_callback that the list has changed. */
-    select_cb_ctr++;
-
-    /* Now we can safely unprotect */
-    SYS_ARCH_UNPROTECT(lev);
-
-    /* Increase select_waiting for each socket we are interested in */
-    for(i = 0; i < maxfdp1; i++) {
-      if ((readset && FD_ISSET(i, readset)) ||
-          (writeset && FD_ISSET(i, writeset)) ||
-          (exceptset && FD_ISSET(i, exceptset))) {
-        struct lwip_sock *sock = tryget_socket(i);
-        LWIP_ASSERT("sock != NULL", sock != NULL);
-        SYS_ARCH_PROTECT(lev);
-        sock->select_waiting++;
-        LWIP_ASSERT("sock->select_waiting > 0", sock->select_waiting > 0);
-        SYS_ARCH_UNPROTECT(lev);
-      }
-    }
-
-    /* Call lwip_selscan again: there could have been events between
-       the last scan (whithout us on the list) and putting us on the list! */
-    nready = lwip_selscan(maxfdp1, readset, writeset, exceptset, &lreadset, &lwriteset, &lexceptset);
-    if (!nready) {
-      /* Still none ready, just wait to be woken */
-      if (timeout == 0) {
-        /* Wait forever */
-        msectimeout = 0;
-      } else {
-        msectimeout =  ((timeout->tv_sec * 1000) + ((timeout->tv_usec + 500)/1000));
-        if (msectimeout == 0) {
-          /* Wait 1ms at least (0 means wait forever) */
-          msectimeout = 1;
-        }
-      }
-
-      waitres = sys_arch_sem_wait(&select_cb.sem, msectimeout);
-    }
-    /* Increase select_waiting for each socket we are interested in */
-    for(i = 0; i < maxfdp1; i++) {
-      if ((readset && FD_ISSET(i, readset)) ||
-          (writeset && FD_ISSET(i, writeset)) ||
-          (exceptset && FD_ISSET(i, exceptset))) {
-        struct lwip_sock *sock = tryget_socket(i);
-        LWIP_ASSERT("sock != NULL", sock != NULL);
-        SYS_ARCH_PROTECT(lev);
-        sock->select_waiting--;
-        LWIP_ASSERT("sock->select_waiting >= 0", sock->select_waiting >= 0);
-        SYS_ARCH_UNPROTECT(lev);
-      }
-    }
-    /* Take us off the list */
-    SYS_ARCH_PROTECT(lev);
-    if (select_cb.next != NULL) {
-      select_cb.next->prev = select_cb.prev;
-    }
-    if (select_cb_list == &select_cb) {
-      LWIP_ASSERT("select_cb.prev == NULL", select_cb.prev == NULL);
-      select_cb_list = select_cb.next;
-    } else {
-      LWIP_ASSERT("select_cb.prev != NULL", select_cb.prev != NULL);
-      select_cb.prev->next = select_cb.next;
-    }
-    /* Increasing this counter tells even_callback that the list has changed. */
-    select_cb_ctr++;
-    SYS_ARCH_UNPROTECT(lev);
-
-    sys_sem_free(&select_cb.sem);
-    if (waitres == SYS_ARCH_TIMEOUT)  {
-      /* Timeout */
-      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: timeout expired\n"));
-      /* This is OK as the local fdsets are empty and nready is zero,
-         or we would have returned earlier. */
-      goto return_copy_fdsets;
-    }
-
-    /* See what's set */
-    nready = lwip_selscan(maxfdp1, readset, writeset, exceptset, &lreadset, &lwriteset, &lexceptset);
-  }
-
-  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: nready=%d\n", nready));
-return_copy_fdsets:
-  set_errno(0);
-  if (readset) {
-    *readset = lreadset;
-  }
-  if (writeset) {
-    *writeset = lwriteset;
-  }
-  if (exceptset) {
-    *exceptset = lexceptset;
-  }
-
-
-  return nready;
-}
-
-/**
- * Callback registered in the netconn layer for each socket-netconn.
- * Processes recvevent (data available) and wakes up tasks waiting for select.
- */
-static void
-event_callback(struct netconn *conn, enum netconn_evt evt, u16_t len)
-{
-  int s;
-  struct lwip_sock *sock;
-  struct lwip_select_cb *scb;
-  int last_select_cb_ctr;
-  SYS_ARCH_DECL_PROTECT(lev);
-
-  LWIP_UNUSED_ARG(len);
-
-  /* Get socket */
-  if (conn) {
-    s = conn->socket;
-    if (s < 0) {
-      /* Data comes in right away after an accept, even though
-       * the server task might not have created a new socket yet.
-       * Just count down (or up) if that's the case and we
-       * will use the data later. Note that only receive events
-       * can happen before the new socket is set up. */
-      SYS_ARCH_PROTECT(lev);
-      if (conn->socket < 0) {
-        if (evt == NETCONN_EVT_RCVPLUS) {
-          conn->socket--;
-        }
-        SYS_ARCH_UNPROTECT(lev);
-        return;
-      }
-      s = conn->socket;
-      SYS_ARCH_UNPROTECT(lev);
-    }
-
-    sock = get_socket(s);
-    if (!sock) {
-      return;
-    }
-  } else {
-    return;
-  }
-
-  SYS_ARCH_PROTECT(lev);
-  /* Set event as required */
-  switch (evt) {
-    case NETCONN_EVT_RCVPLUS:
-      sock->rcvevent++;
-      break;
-    case NETCONN_EVT_RCVMINUS:
-      sock->rcvevent--;
-      break;
-    case NETCONN_EVT_SENDPLUS:
-      sock->sendevent = 1;
-      break;
-    case NETCONN_EVT_SENDMINUS:
-      sock->sendevent = 0;
-      break;
-    case NETCONN_EVT_ERROR:
-      sock->errevent = 1;
-      break;
-    default:
-      LWIP_ASSERT("unknown event", 0);
-      break;
-  }
-
-  if (sock->select_waiting == 0) {
-    /* noone is waiting for this socket, no need to check select_cb_list */
-    SYS_ARCH_UNPROTECT(lev);
-    return;
-  }
-
-  /* Now decide if anyone is waiting for this socket */
-  /* NOTE: This code goes through the select_cb_list list multiple times
-     ONLY IF a select was actually waiting. We go through the list the number
-     of waiting select calls + 1. This list is expected to be small. */
-
-  /* At this point, SYS_ARCH is still protected! */
-again:
-  for (scb = select_cb_list; scb != NULL; scb = scb->next) {
-    if (scb->sem_signalled == 0) {
-      /* semaphore not signalled yet */
-      int do_signal = 0;
-      /* Test this select call for our socket */
-      if (sock->rcvevent > 0) {
-        if (scb->readset && FD_ISSET(s, scb->readset)) {
-          do_signal = 1;
-        }
-      }
-      if (sock->sendevent != 0) {
-        if (!do_signal && scb->writeset && FD_ISSET(s, scb->writeset)) {
-          do_signal = 1;
-        }
-      }
-      if (sock->errevent != 0) {
-        if (!do_signal && scb->exceptset && FD_ISSET(s, scb->exceptset)) {
-          do_signal = 1;
-        }
-      }
-      if (do_signal) {
-        scb->sem_signalled = 1;
-        /* Don't call SYS_ARCH_UNPROTECT() before signaling the semaphore, as this might
-           lead to the select thread taking itself off the list, invalidagin the semaphore. */
-        sys_sem_signal(&scb->sem);
-      }
-    }
-    /* unlock interrupts with each step */
-    last_select_cb_ctr = select_cb_ctr;
-    SYS_ARCH_UNPROTECT(lev);
-    /* this makes sure interrupt protection time is short */
-    SYS_ARCH_PROTECT(lev);
-    if (last_select_cb_ctr != select_cb_ctr) {
-      /* someone has changed select_cb_list, restart at the beginning */
-      goto again;
-    }
-  }
-  SYS_ARCH_UNPROTECT(lev);
-}
-
-/**
- * Unimplemented: Close one end of a full-duplex connection.
- * Currently, the full connection is closed.
- */
-int
-lwip_shutdown(int s, int how)
-{
-  struct lwip_sock *sock;
-  err_t err;
-  u8_t shut_rx = 0, shut_tx = 0;
-
-  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_shutdown(%d, how=%d)\n", s, how));
-
-  sock = get_socket(s);
-  if (!sock) {
-    return -1;
-  }
-
-  if (sock->conn != NULL) {
-    if (netconn_type(sock->conn) != NETCONN_TCP) {
-      sock_set_errno(sock, EOPNOTSUPP);
-      return EOPNOTSUPP;
-    }
-  } else {
-    sock_set_errno(sock, ENOTCONN);
-    return ENOTCONN;
-  }
-
-  if (how == SHUT_RD) {
-    shut_rx = 1;
-  } else if (how == SHUT_WR) {
-    shut_tx = 1;
-  } else if(how == SHUT_RDWR) {
-    shut_rx = 1;
-    shut_tx = 1;
-  } else {
-    sock_set_errno(sock, EINVAL);
-    return EINVAL;
-  }
-  err = netconn_shutdown(sock->conn, shut_rx, shut_tx);
-
-  sock_set_errno(sock, err_to_errno(err));
-  return (err == ERR_OK ? 0 : -1);
-}
-
-static int
-lwip_getaddrname(int s, struct sockaddr *name, socklen_t *namelen, u8_t local)
-{
-  struct lwip_sock *sock;
-  struct sockaddr_in sin;
-  ip_addr_t naddr;
-
-  sock = get_socket(s);
-  if (!sock) {
-    return -1;
-  }
-
-  memset(&sin, 0, sizeof(sin));
-  sin.sin_len = sizeof(sin);
-  sin.sin_family = AF_INET;
-
-  /* get the IP address and port */
-  netconn_getaddr(sock->conn, &naddr, &sin.sin_port, local);
-
-  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getaddrname(%d, addr=", s));
-  ip_addr_debug_print(SOCKETS_DEBUG, &naddr);
-  LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", sin.sin_port));
-
-  sin.sin_port = htons(sin.sin_port);
-  inet_addr_from_ipaddr(&sin.sin_addr, &naddr);
-
-  if (*namelen > sizeof(sin)) {
-    *namelen = sizeof(sin);
-  }
-
-  MEMCPY(name, &sin, *namelen);
-  sock_set_errno(sock, 0);
-  return 0;
-}
-
-int
-lwip_getpeername(int s, struct sockaddr *name, socklen_t *namelen)
-{
-  return lwip_getaddrname(s, name, namelen, 0);
-}
-
-int
-lwip_getsockname(int s, struct sockaddr *name, socklen_t *namelen)
-{
-  return lwip_getaddrname(s, name, namelen, 1);
-}
-
-int
-lwip_getsockopt(int s, int level, int optname, void *optval, socklen_t *optlen)
-{
-  err_t err = ERR_OK;
-  struct lwip_sock *sock = get_socket(s);
-  struct lwip_setgetsockopt_data data;
-
-  if (!sock) {
-    return -1;
-  }
-
-  if ((NULL == optval) || (NULL == optlen)) {
-    sock_set_errno(sock, EFAULT);
-    return -1;
-  }
-
-  /* Do length and type checks for the various options first, to keep it readable. */
-  switch (level) {
-   
-/* Level: SOL_SOCKET */
-  case SOL_SOCKET:
-    switch (optname) {
-       
-    case SO_ACCEPTCONN:
-    case SO_BROADCAST:
-    /* UNIMPL case SO_DEBUG: */
-    /* UNIMPL case SO_DONTROUTE: */
-    case SO_ERROR:
-    case SO_KEEPALIVE:
-    /* UNIMPL case SO_CONTIMEO: */
-#if LWIP_SO_SNDTIMEO
-    case SO_SNDTIMEO:
-#endif /* LWIP_SO_SNDTIMEO */
-#if LWIP_SO_RCVTIMEO
-    case SO_RCVTIMEO:
-#endif /* LWIP_SO_RCVTIMEO */
-#if LWIP_SO_RCVBUF
-    case SO_RCVBUF:
-#endif /* LWIP_SO_RCVBUF */
-    /* UNIMPL case SO_OOBINLINE: */
-    /* UNIMPL case SO_SNDBUF: */
-    /* UNIMPL case SO_RCVLOWAT: */
-    /* UNIMPL case SO_SNDLOWAT: */
-#if SO_REUSE
-    case SO_REUSEADDR:
-    case SO_REUSEPORT:
-#endif /* SO_REUSE */
-    case SO_TYPE:
-    /* UNIMPL case SO_USELOOPBACK: */
-      if (*optlen < sizeof(int)) {
-        err = EINVAL;
-      }
-      break;
-
-    case SO_NO_CHECK:
-      if (*optlen < sizeof(int)) {
-        err = EINVAL;
-      }
-#if LWIP_UDP
-      if ((sock->conn->type != NETCONN_UDP) ||
-          ((udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_UDPLITE) != 0)) {
-        /* this flag is only available for UDP, not for UDP lite */
-        err = EAFNOSUPPORT;
-      }
-#endif /* LWIP_UDP */
-      break;
-
-    default:
-      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, UNIMPL: optname=0x%x, ..)\n",
-                                  s, optname));
-      err = ENOPROTOOPT;
-    }  /* switch (optname) */
-    break;
-                     
-/* Level: IPPROTO_IP */
-  case IPPROTO_IP:
-    switch (optname) {
-    /* UNIMPL case IP_HDRINCL: */
-    /* UNIMPL case IP_RCVDSTADDR: */
-    /* UNIMPL case IP_RCVIF: */
-    case IP_TTL:
-    case IP_TOS:
-      if (*optlen < sizeof(int)) {
-        err = EINVAL;
-      }
-      break;
-#if LWIP_IGMP
-    case IP_MULTICAST_TTL:
-      if (*optlen < sizeof(u8_t)) {
-        err = EINVAL;
-      }
-      break;
-    case IP_MULTICAST_IF:
-      if (*optlen < sizeof(struct in_addr)) {
-        err = EINVAL;
-      }
-      break;
-    case IP_MULTICAST_LOOP:
-      if (*optlen < sizeof(u8_t)) {
-        err = EINVAL;
-      }
-      if (NETCONNTYPE_GROUP(sock->conn->type) != NETCONN_UDP) {
-        err = EAFNOSUPPORT;
-      }
-      break;
-#endif /* LWIP_IGMP */
-
-    default:
-      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, UNIMPL: optname=0x%x, ..)\n",
-                                  s, optname));
-      err = ENOPROTOOPT;
-    }  /* switch (optname) */
-    break;
-         
-#if LWIP_TCP
-/* Level: IPPROTO_TCP */
-  case IPPROTO_TCP:
-    if (*optlen < sizeof(int)) {
-      err = EINVAL;
-      break;
-    }
-    
-    /* If this is no TCP socket, ignore any options. */
-    if (sock->conn->type != NETCONN_TCP)
-      return 0;
-
-    switch (optname) {
-    case TCP_NODELAY:
-    case TCP_KEEPALIVE:
-#if LWIP_TCP_KEEPALIVE
-    case TCP_KEEPIDLE:
-    case TCP_KEEPINTVL:
-    case TCP_KEEPCNT:
-#endif /* LWIP_TCP_KEEPALIVE */
-      break;
-       
-    default:
-      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, UNIMPL: optname=0x%x, ..)\n",
-                                  s, optname));
-      err = ENOPROTOOPT;
-    }  /* switch (optname) */
-    break;
-#endif /* LWIP_TCP */
-#if LWIP_UDP && LWIP_UDPLITE
-/* Level: IPPROTO_UDPLITE */
-  case IPPROTO_UDPLITE:
-    if (*optlen < sizeof(int)) {
-      err = EINVAL;
-      break;
-    }
-    
-    /* If this is no UDP lite socket, ignore any options. */
-    if (sock->conn->type != NETCONN_UDPLITE) {
-      return 0;
-    }
-
-    switch (optname) {
-    case UDPLITE_SEND_CSCOV:
-    case UDPLITE_RECV_CSCOV:
-      break;
-       
-    default:
-      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_UDPLITE, UNIMPL: optname=0x%x, ..)\n",
-                                  s, optname));
-      err = ENOPROTOOPT;
-    }  /* switch (optname) */
-    break;
-#endif /* LWIP_UDP && LWIP_UDPLITE*/
-/* UNDEFINED LEVEL */
-  default:
-      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, level=0x%x, UNIMPL: optname=0x%x, ..)\n",
-                                  s, level, optname));
-      err = ENOPROTOOPT;
-  }  /* switch */
-
-   
-  if (err != ERR_OK) {
-    sock_set_errno(sock, err);
-    return -1;
-  }
-
-  /* Now do the actual option processing */
-  data.sock = sock;
-#ifdef LWIP_DEBUG
-  data.s = s;
-#endif /* LWIP_DEBUG */
-  data.level = level;
-  data.optname = optname;
-  data.optval = optval;
-  data.optlen = optlen;
-  data.err = err;
-  tcpip_callback(lwip_getsockopt_internal, &data);
-  sys_arch_sem_wait(&sock->conn->op_completed, 0);
-  /* maybe lwip_getsockopt_internal has changed err */
-  err = data.err;
-
-  sock_set_errno(sock, err);
-  return err ? -1 : 0;
-}
-
-static void
-lwip_getsockopt_internal(void *arg)
-{
-  struct lwip_sock *sock;
-#ifdef LWIP_DEBUG
-  int s;
-#endif /* LWIP_DEBUG */
-  int level, optname;
-  void *optval;
-  struct lwip_setgetsockopt_data *data;
-
-  LWIP_ASSERT("arg != NULL", arg != NULL);
-
-  data = (struct lwip_setgetsockopt_data*)arg;
-  sock = data->sock;
-#ifdef LWIP_DEBUG
-  s = data->s;
-#endif /* LWIP_DEBUG */
-  level = data->level;
-  optname = data->optname;
-  optval = data->optval;
-
-  switch (level) {
-
-/* Level: SOL_SOCKET */
-  case SOL_SOCKET:
-    switch (optname) {
-
-    /* The option flags */
-    case SO_ACCEPTCONN:
-    case SO_BROADCAST:
-    /* UNIMPL case SO_DEBUG: */
-    /* UNIMPL case SO_DONTROUTE: */
-    case SO_KEEPALIVE:
-    /* UNIMPL case SO_OOBINCLUDE: */
-#if SO_REUSE
-    case SO_REUSEADDR:
-    case SO_REUSEPORT:
-#endif /* SO_REUSE */
-    /*case SO_USELOOPBACK: UNIMPL */
-      *(int*)optval = ip_get_option(sock->conn->pcb.ip, optname);
-      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, optname=0x%x, ..) = %s\n",
-                                  s, optname, (*(int*)optval?"on":"off")));
-      break;
-
-    case SO_TYPE:
-      switch (NETCONNTYPE_GROUP(sock->conn->type)) {
-      case NETCONN_RAW:
-        *(int*)optval = SOCK_RAW;
-        break;
-      case NETCONN_TCP:
-        *(int*)optval = SOCK_STREAM;
-        break;
-      case NETCONN_UDP:
-        *(int*)optval = SOCK_DGRAM;
-        break;
-      default: /* unrecognized socket type */
-        *(int*)optval = sock->conn->type;
-        LWIP_DEBUGF(SOCKETS_DEBUG,
-                    ("lwip_getsockopt(%d, SOL_SOCKET, SO_TYPE): unrecognized socket type %d\n",
-                    s, *(int *)optval));
-      }  /* switch (sock->conn->type) */
-      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, SO_TYPE) = %d\n",
-                  s, *(int *)optval));
-      break;
-
-    case SO_ERROR:
-      /* only overwrite ERR_OK or tempoary errors */
-      if ((sock->err == 0) || (sock->err == EINPROGRESS)) {
-        sock_set_errno(sock, err_to_errno(sock->conn->last_err));
-      } 
-      *(int *)optval = sock->err;
-      sock->err = 0;
-      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, SO_ERROR) = %d\n",
-                  s, *(int *)optval));
-      break;
-
-#if LWIP_SO_SNDTIMEO
-    case SO_SNDTIMEO:
-      *(int *)optval = netconn_get_sendtimeout(sock->conn);
-      break;
-#endif /* LWIP_SO_SNDTIMEO */
-#if LWIP_SO_RCVTIMEO
-    case SO_RCVTIMEO:
-      *(int *)optval = netconn_get_recvtimeout(sock->conn);
-      break;
-#endif /* LWIP_SO_RCVTIMEO */
-#if LWIP_SO_RCVBUF
-    case SO_RCVBUF:
-      *(int *)optval = netconn_get_recvbufsize(sock->conn);
-      break;
-#endif /* LWIP_SO_RCVBUF */
-#if LWIP_UDP
-    case SO_NO_CHECK:
-      *(int*)optval = (udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_NOCHKSUM) ? 1 : 0;
-      break;
-#endif /* LWIP_UDP*/
-    default:
-      LWIP_ASSERT("unhandled optname", 0);
-      break;
-    }  /* switch (optname) */
-    break;
-
-/* Level: IPPROTO_IP */
-  case IPPROTO_IP:
-    switch (optname) {
-    case IP_TTL:
-      *(int*)optval = sock->conn->pcb.ip->ttl;
-      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_TTL) = %d\n",
-                  s, *(int *)optval));
-      break;
-    case IP_TOS:
-      *(int*)optval = sock->conn->pcb.ip->tos;
-      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_TOS) = %d\n",
-                  s, *(int *)optval));
-      break;
-#if LWIP_IGMP
-    case IP_MULTICAST_TTL:
-      *(u8_t*)optval = sock->conn->pcb.ip->ttl;
-      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_TTL) = %d\n",
-                  s, *(int *)optval));
-      break;
-    case IP_MULTICAST_IF:
-      inet_addr_from_ipaddr((struct in_addr*)optval, &sock->conn->pcb.udp->multicast_ip);
-      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_IF) = 0x%"X32_F"\n",
-                  s, *(u32_t *)optval));
-      break;
-    case IP_MULTICAST_LOOP:
-      if ((sock->conn->pcb.udp->flags & UDP_FLAGS_MULTICAST_LOOP) != 0) {
-        *(u8_t*)optval = 1;
-      } else {
-        *(u8_t*)optval = 0;
-      }
-      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_LOOP) = %d\n",
-                  s, *(int *)optval));
-      break;
-#endif /* LWIP_IGMP */
-    default:
-      LWIP_ASSERT("unhandled optname", 0);
-      break;
-    }  /* switch (optname) */
-    break;
-
-#if LWIP_TCP
-/* Level: IPPROTO_TCP */
-  case IPPROTO_TCP:
-    switch (optname) {
-    case TCP_NODELAY:
-      *(int*)optval = tcp_nagle_disabled(sock->conn->pcb.tcp);
-      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, TCP_NODELAY) = %s\n",
-                  s, (*(int*)optval)?"on":"off") );
-      break;
-    case TCP_KEEPALIVE:
-      *(int*)optval = (int)sock->conn->pcb.tcp->keep_idle;
-      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, TCP_KEEPALIVE) = %d\n",
-                  s, *(int *)optval));
-      break;
-
-#if LWIP_TCP_KEEPALIVE
-    case TCP_KEEPIDLE:
-      *(int*)optval = (int)(sock->conn->pcb.tcp->keep_idle/1000);
-      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, TCP_KEEPIDLE) = %d\n",
-                  s, *(int *)optval));
-      break;
-    case TCP_KEEPINTVL:
-      *(int*)optval = (int)(sock->conn->pcb.tcp->keep_intvl/1000);
-      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, TCP_KEEPINTVL) = %d\n",
-                  s, *(int *)optval));
-      break;
-    case TCP_KEEPCNT:
-      *(int*)optval = (int)sock->conn->pcb.tcp->keep_cnt;
-      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, TCP_KEEPCNT) = %d\n",
-                  s, *(int *)optval));
-      break;
-#endif /* LWIP_TCP_KEEPALIVE */
-    default:
-      LWIP_ASSERT("unhandled optname", 0);
-      break;
-    }  /* switch (optname) */
-    break;
-#endif /* LWIP_TCP */
-#if LWIP_UDP && LWIP_UDPLITE
-  /* Level: IPPROTO_UDPLITE */
-  case IPPROTO_UDPLITE:
-    switch (optname) {
-    case UDPLITE_SEND_CSCOV:
-      *(int*)optval = sock->conn->pcb.udp->chksum_len_tx;
-      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_UDPLITE, UDPLITE_SEND_CSCOV) = %d\n",
-                  s, (*(int*)optval)) );
-      break;
-    case UDPLITE_RECV_CSCOV:
-      *(int*)optval = sock->conn->pcb.udp->chksum_len_rx;
-      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_UDPLITE, UDPLITE_RECV_CSCOV) = %d\n",
-                  s, (*(int*)optval)) );
-      break;
-    default:
-      LWIP_ASSERT("unhandled optname", 0);
-      break;
-    }  /* switch (optname) */
-    break;
-#endif /* LWIP_UDP */
-  default:
-    LWIP_ASSERT("unhandled level", 0);
-    break;
-  } /* switch (level) */
-  sys_sem_signal(&sock->conn->op_completed);
-}
-
-int
-lwip_setsockopt(int s, int level, int optname, const void *optval, socklen_t optlen)
-{
-  struct lwip_sock *sock = get_socket(s);
-  err_t err = ERR_OK;
-  struct lwip_setgetsockopt_data data;
-
-  if (!sock) {
-    return -1;
-  }
-
-  if (NULL == optval) {
-    sock_set_errno(sock, EFAULT);
-    return -1;
-  }
-
-  /* Do length and type checks for the various options first, to keep it readable. */
-  switch (level) {
-
-/* Level: SOL_SOCKET */
-  case SOL_SOCKET:
-    switch (optname) {
-
-    case SO_BROADCAST:
-    /* UNIMPL case SO_DEBUG: */
-    /* UNIMPL case SO_DONTROUTE: */
-    case SO_KEEPALIVE:
-    /* UNIMPL case case SO_CONTIMEO: */
-#if LWIP_SO_SNDTIMEO
-    case SO_SNDTIMEO:
-#endif /* LWIP_SO_SNDTIMEO */
-#if LWIP_SO_RCVTIMEO
-    case SO_RCVTIMEO:
-#endif /* LWIP_SO_RCVTIMEO */
-#if LWIP_SO_RCVBUF
-    case SO_RCVBUF:
-#endif /* LWIP_SO_RCVBUF */
-    /* UNIMPL case SO_OOBINLINE: */
-    /* UNIMPL case SO_SNDBUF: */
-    /* UNIMPL case SO_RCVLOWAT: */
-    /* UNIMPL case SO_SNDLOWAT: */
-#if SO_REUSE
-    case SO_REUSEADDR:
-    case SO_REUSEPORT:
-#endif /* SO_REUSE */
-    /* UNIMPL case SO_USELOOPBACK: */
-      if (optlen < sizeof(int)) {
-        err = EINVAL;
-      }
-      break;
-    case SO_NO_CHECK:
-      if (optlen < sizeof(int)) {
-        err = EINVAL;
-      }
-#if LWIP_UDP
-      if ((sock->conn->type != NETCONN_UDP) ||
-          ((udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_UDPLITE) != 0)) {
-        /* this flag is only available for UDP, not for UDP lite */
-        err = EAFNOSUPPORT;
-      }
-#endif /* LWIP_UDP */
-      break;
-    default:
-      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, SOL_SOCKET, UNIMPL: optname=0x%x, ..)\n",
-                  s, optname));
-      err = ENOPROTOOPT;
-    }  /* switch (optname) */
-    break;
-
-/* Level: IPPROTO_IP */
-  case IPPROTO_IP:
-    switch (optname) {
-    /* UNIMPL case IP_HDRINCL: */
-    /* UNIMPL case IP_RCVDSTADDR: */
-    /* UNIMPL case IP_RCVIF: */
-    case IP_TTL:
-    case IP_TOS:
-      if (optlen < sizeof(int)) {
-        err = EINVAL;
-      }
-      break;
-#if LWIP_IGMP
-    case IP_MULTICAST_TTL:
-      if (optlen < sizeof(u8_t)) {
-        err = EINVAL;
-      }
-      if (NETCONNTYPE_GROUP(sock->conn->type) != NETCONN_UDP) {
-        err = EAFNOSUPPORT;
-      }
-      break;
-    case IP_MULTICAST_IF:
-      if (optlen < sizeof(struct in_addr)) {
-        err = EINVAL;
-      }
-      if (NETCONNTYPE_GROUP(sock->conn->type) != NETCONN_UDP) {
-        err = EAFNOSUPPORT;
-      }
-      break;
-    case IP_MULTICAST_LOOP:
-      if (optlen < sizeof(u8_t)) {
-        err = EINVAL;
-      }
-      if (NETCONNTYPE_GROUP(sock->conn->type) != NETCONN_UDP) {
-        err = EAFNOSUPPORT;
-      }
-      break;
-    case IP_ADD_MEMBERSHIP:
-    case IP_DROP_MEMBERSHIP:
-      if (optlen < sizeof(struct ip_mreq)) {
-        err = EINVAL;
-      }
-      if (NETCONNTYPE_GROUP(sock->conn->type) != NETCONN_UDP) {
-        err = EAFNOSUPPORT;
-      }
-      break;
-#endif /* LWIP_IGMP */
-      default:
-        LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, UNIMPL: optname=0x%x, ..)\n",
-                    s, optname));
-        err = ENOPROTOOPT;
-    }  /* switch (optname) */
-    break;
-
-#if LWIP_TCP
-/* Level: IPPROTO_TCP */
-  case IPPROTO_TCP:
-    if (optlen < sizeof(int)) {
-      err = EINVAL;
-      break;
-    }
-
-    /* If this is no TCP socket, ignore any options. */
-    if (sock->conn->type != NETCONN_TCP)
-      return 0;
-
-    switch (optname) {
-    case TCP_NODELAY:
-    case TCP_KEEPALIVE:
-#if LWIP_TCP_KEEPALIVE
-    case TCP_KEEPIDLE:
-    case TCP_KEEPINTVL:
-    case TCP_KEEPCNT:
-#endif /* LWIP_TCP_KEEPALIVE */
-      break;
-
-    default:
-      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, UNIMPL: optname=0x%x, ..)\n",
-                  s, optname));
-      err = ENOPROTOOPT;
-    }  /* switch (optname) */
-    break;
-#endif /* LWIP_TCP */
-#if LWIP_UDP && LWIP_UDPLITE
-/* Level: IPPROTO_UDPLITE */
-  case IPPROTO_UDPLITE:
-    if (optlen < sizeof(int)) {
-      err = EINVAL;
-      break;
-    }
-
-    /* If this is no UDP lite socket, ignore any options. */
-    if (sock->conn->type != NETCONN_UDPLITE)
-      return 0;
-
-    switch (optname) {
-    case UDPLITE_SEND_CSCOV:
-    case UDPLITE_RECV_CSCOV:
-      break;
-
-    default:
-      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UNIMPL: optname=0x%x, ..)\n",
-                  s, optname));
-      err = ENOPROTOOPT;
-    }  /* switch (optname) */
-    break;
-#endif /* LWIP_UDP && LWIP_UDPLITE */
-/* UNDEFINED LEVEL */
-  default:
-    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, level=0x%x, UNIMPL: optname=0x%x, ..)\n",
-                s, level, optname));
-    err = ENOPROTOOPT;
-  }  /* switch (level) */
-
-
-  if (err != ERR_OK) {
-    sock_set_errno(sock, err);
-    return -1;
-  }
-
-
-  /* Now do the actual option processing */
-  data.sock = sock;
-#ifdef LWIP_DEBUG
-  data.s = s;
-#endif /* LWIP_DEBUG */
-  data.level = level;
-  data.optname = optname;
-  data.optval = (void*)optval;
-  data.optlen = &optlen;
-  data.err = err;
-  tcpip_callback(lwip_setsockopt_internal, &data);
-  sys_arch_sem_wait(&sock->conn->op_completed, 0);
-  /* maybe lwip_setsockopt_internal has changed err */
-  err = data.err;
-
-  sock_set_errno(sock, err);
-  return err ? -1 : 0;
-}
-
-static void
-lwip_setsockopt_internal(void *arg)
-{
-  struct lwip_sock *sock;
-#ifdef LWIP_DEBUG
-  int s;
-#endif /* LWIP_DEBUG */
-  int level, optname;
-  const void *optval;
-  struct lwip_setgetsockopt_data *data;
-
-  LWIP_ASSERT("arg != NULL", arg != NULL);
-
-  data = (struct lwip_setgetsockopt_data*)arg;
-  sock = data->sock;
-#ifdef LWIP_DEBUG
-  s = data->s;
-#endif /* LWIP_DEBUG */
-  level = data->level;
-  optname = data->optname;
-  optval = data->optval;
-
-  switch (level) {
-
-/* Level: SOL_SOCKET */
-  case SOL_SOCKET:
-    switch (optname) {
-
-    /* The option flags */
-    case SO_BROADCAST:
-    /* UNIMPL case SO_DEBUG: */
-    /* UNIMPL case SO_DONTROUTE: */
-    case SO_KEEPALIVE:
-    /* UNIMPL case SO_OOBINCLUDE: */
-#if SO_REUSE
-    case SO_REUSEADDR:
-    case SO_REUSEPORT:
-#endif /* SO_REUSE */
-    /* UNIMPL case SO_USELOOPBACK: */
-      if (*(int*)optval) {
-        ip_set_option(sock->conn->pcb.ip, optname);
-      } else {
-        ip_reset_option(sock->conn->pcb.ip, optname);
-      }
-      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, SOL_SOCKET, optname=0x%x, ..) -> %s\n",
-                  s, optname, (*(int*)optval?"on":"off")));
-      break;
-#if LWIP_SO_SNDTIMEO
-    case SO_SNDTIMEO:
-      netconn_set_sendtimeout(sock->conn, (s32_t)*(int*)optval);
-      break;
-#endif /* LWIP_SO_SNDTIMEO */
-#if LWIP_SO_RCVTIMEO
-    case SO_RCVTIMEO:
-      netconn_set_recvtimeout(sock->conn, *(int*)optval);
-      break;
-#endif /* LWIP_SO_RCVTIMEO */
-#if LWIP_SO_RCVBUF
-    case SO_RCVBUF:
-      netconn_set_recvbufsize(sock->conn, *(int*)optval);
-      break;
-#endif /* LWIP_SO_RCVBUF */
-#if LWIP_UDP
-    case SO_NO_CHECK:
-      if (*(int*)optval) {
-        udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) | UDP_FLAGS_NOCHKSUM);
-      } else {
-        udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) & ~UDP_FLAGS_NOCHKSUM);
-      }
-      break;
-#endif /* LWIP_UDP */
-    default:
-      LWIP_ASSERT("unhandled optname", 0);
-      break;
-    }  /* switch (optname) */
-    break;
-
-/* Level: IPPROTO_IP */
-  case IPPROTO_IP:
-    switch (optname) {
-    case IP_TTL:
-      sock->conn->pcb.ip->ttl = (u8_t)(*(int*)optval);
-      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, IP_TTL, ..) -> %d\n",
-                  s, sock->conn->pcb.ip->ttl));
-      break;
-    case IP_TOS:
-      sock->conn->pcb.ip->tos = (u8_t)(*(int*)optval);
-      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, IP_TOS, ..)-> %d\n",
-                  s, sock->conn->pcb.ip->tos));
-      break;
-#if LWIP_IGMP
-    case IP_MULTICAST_TTL:
-      sock->conn->pcb.udp->ttl = (u8_t)(*(u8_t*)optval);
-      break;
-    case IP_MULTICAST_IF:
-      inet_addr_to_ipaddr(&sock->conn->pcb.udp->multicast_ip, (struct in_addr*)optval);
-      break;
-    case IP_MULTICAST_LOOP:
-      if (*(u8_t*)optval) {
-        udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) | UDP_FLAGS_MULTICAST_LOOP);
-      } else {
-        udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) & ~UDP_FLAGS_MULTICAST_LOOP);
-      }
-      break;
-    case IP_ADD_MEMBERSHIP:
-    case IP_DROP_MEMBERSHIP:
-      {
-        /* If this is a TCP or a RAW socket, ignore these options. */
-        struct ip_mreq *imr = (struct ip_mreq *)optval;
-        ip_addr_t if_addr;
-        ip_addr_t multi_addr;
-        inet_addr_to_ipaddr(&if_addr, &imr->imr_interface);
-        inet_addr_to_ipaddr(&multi_addr, &imr->imr_multiaddr);
-        if(optname == IP_ADD_MEMBERSHIP){
-          data->err = igmp_joingroup(&if_addr, &multi_addr);
-        } else {
-          data->err = igmp_leavegroup(&if_addr, &multi_addr);
-        }
-        if(data->err != ERR_OK) {
-          data->err = EADDRNOTAVAIL;
-        }
-      }
-      break;
-#endif /* LWIP_IGMP */
-    default:
-      LWIP_ASSERT("unhandled optname", 0);
-      break;
-    }  /* switch (optname) */
-    break;
-
-#if LWIP_TCP
-/* Level: IPPROTO_TCP */
-  case IPPROTO_TCP:
-    switch (optname) {
-    case TCP_NODELAY:
-      if (*(int*)optval) {
-        tcp_nagle_disable(sock->conn->pcb.tcp);
-      } else {
-        tcp_nagle_enable(sock->conn->pcb.tcp);
-      }
-      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_NODELAY) -> %s\n",
-                  s, (*(int *)optval)?"on":"off") );
-      break;
-    case TCP_KEEPALIVE:
-      sock->conn->pcb.tcp->keep_idle = (u32_t)(*(int*)optval);
-      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPALIVE) -> %"U32_F"\n",
-                  s, sock->conn->pcb.tcp->keep_idle));
-      break;
-
-#if LWIP_TCP_KEEPALIVE
-    case TCP_KEEPIDLE:
-      sock->conn->pcb.tcp->keep_idle = 1000*(u32_t)(*(int*)optval);
-      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPIDLE) -> %"U32_F"\n",
-                  s, sock->conn->pcb.tcp->keep_idle));
-      break;
-    case TCP_KEEPINTVL:
-      sock->conn->pcb.tcp->keep_intvl = 1000*(u32_t)(*(int*)optval);
-      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPINTVL) -> %"U32_F"\n",
-                  s, sock->conn->pcb.tcp->keep_intvl));
-      break;
-    case TCP_KEEPCNT:
-      sock->conn->pcb.tcp->keep_cnt = (u32_t)(*(int*)optval);
-      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPCNT) -> %"U32_F"\n",
-                  s, sock->conn->pcb.tcp->keep_cnt));
-      break;
-#endif /* LWIP_TCP_KEEPALIVE */
-    default:
-      LWIP_ASSERT("unhandled optname", 0);
-      break;
-    }  /* switch (optname) */
-    break;
-#endif /* LWIP_TCP*/
-#if LWIP_UDP && LWIP_UDPLITE
-  /* Level: IPPROTO_UDPLITE */
-  case IPPROTO_UDPLITE:
-    switch (optname) {
-    case UDPLITE_SEND_CSCOV:
-      if ((*(int*)optval != 0) && ((*(int*)optval < 8) || (*(int*)optval > 0xffff))) {
-        /* don't allow illegal values! */
-        sock->conn->pcb.udp->chksum_len_tx = 8;
-      } else {
-        sock->conn->pcb.udp->chksum_len_tx = (u16_t)*(int*)optval;
-      }
-      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UDPLITE_SEND_CSCOV) -> %d\n",
-                  s, (*(int*)optval)) );
-      break;
-    case UDPLITE_RECV_CSCOV:
-      if ((*(int*)optval != 0) && ((*(int*)optval < 8) || (*(int*)optval > 0xffff))) {
-        /* don't allow illegal values! */
-        sock->conn->pcb.udp->chksum_len_rx = 8;
-      } else {
-        sock->conn->pcb.udp->chksum_len_rx = (u16_t)*(int*)optval;
-      }
-      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UDPLITE_RECV_CSCOV) -> %d\n",
-                  s, (*(int*)optval)) );
-      break;
-    default:
-      LWIP_ASSERT("unhandled optname", 0);
-      break;
-    }  /* switch (optname) */
-    break;
-#endif /* LWIP_UDP */
-  default:
-    LWIP_ASSERT("unhandled level", 0);
-    break;
-  }  /* switch (level) */
-  sys_sem_signal(&sock->conn->op_completed);
-}
-
-int
-lwip_ioctl(int s, long cmd, void *argp)
-{
-  struct lwip_sock *sock = get_socket(s);
-  u8_t val;
-#if LWIP_SO_RCVBUF
-  u16_t buflen = 0;
-  s16_t recv_avail;
-#endif /* LWIP_SO_RCVBUF */
-
-  if (!sock) {
-    return -1;
-  }
-
-  switch (cmd) {
-#if LWIP_SO_RCVBUF
-  case FIONREAD:
-    if (!argp) {
-      sock_set_errno(sock, EINVAL);
-      return -1;
-    }
-
-    SYS_ARCH_GET(sock->conn->recv_avail, recv_avail);
-    if (recv_avail < 0) {
-      recv_avail = 0;
-    }
-    *((u16_t*)argp) = (u16_t)recv_avail;
-
-    /* Check if there is data left from the last recv operation. /maq 041215 */
-    if (sock->lastdata) {
-      struct pbuf *p = (struct pbuf *)sock->lastdata;
-      if (netconn_type(sock->conn) != NETCONN_TCP) {
-        p = ((struct netbuf *)p)->p;
-      }
-      buflen = p->tot_len;
-      buflen -= sock->lastoffset;
-
-      *((u16_t*)argp) += buflen;
-    }
-
-    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, FIONREAD, %p) = %"U16_F"\n", s, argp, *((u16_t*)argp)));
-    sock_set_errno(sock, 0);
-    return 0;
-#endif /* LWIP_SO_RCVBUF */
-
-  case FIONBIO:
-    val = 0;
-    if (argp && *(u32_t*)argp) {
-      val = 1;
-    }
-    netconn_set_nonblocking(sock->conn, val);
-    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, FIONBIO, %d)\n", s, val));
-    sock_set_errno(sock, 0);
-    return 0;
-
-  default:
-    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, UNIMPL: 0x%lx, %p)\n", s, cmd, argp));
-    sock_set_errno(sock, ENOSYS); /* not yet implemented */
-    return -1;
-  } /* switch (cmd) */
-}
-
-/** A minimal implementation of fcntl.
- * Currently only the commands F_GETFL and F_SETFL are implemented.
- * Only the flag O_NONBLOCK is implemented.
- */
-int
-lwip_fcntl(int s, int cmd, int val)
-{
-  struct lwip_sock *sock = get_socket(s);
-  int ret = -1;
-
-  if (!sock || !sock->conn) {
-    return -1;
-  }
-
-  switch (cmd) {
-  case F_GETFL:
-    ret = netconn_is_nonblocking(sock->conn) ? O_NONBLOCK : 0;
-    break;
-  case F_SETFL:
-    if ((val & ~O_NONBLOCK) == 0) {
-      /* only O_NONBLOCK, all other bits are zero */
-      netconn_set_nonblocking(sock->conn, val & O_NONBLOCK);
-      ret = 0;
-    }
-    break;
-  default:
-    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_fcntl(%d, UNIMPL: %d, %d)\n", s, cmd, val));
-    break;
-  }
-  return ret;
-}
-
-#endif /* LWIP_SOCKET */
+/**
+ * @file
+ * Sockets BSD-Like API module
+ *
+ * @defgroup socket Socket API
+ * @ingroup sequential_api
+ * BSD-style socket API.\n
+ * Thread-safe, to be called from non-TCPIP threads only.\n
+ * Can be activated by defining @ref LWIP_SOCKET to 1.\n
+ * Header is in posix/sys/socket.h\b
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ * Improved by Marc Boucher <marc@mbsi.ca> and David Haas <dhaas@alum.rpi.edu>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/sockets.h"
+#include "lwip/api.h"
+#include "lwip/sys.h"
+#include "lwip/igmp.h"
+#include "lwip/inet.h"
+#include "lwip/tcp.h"
+#include "lwip/raw.h"
+#include "lwip/udp.h"
+#include "lwip/memp.h"
+#include "lwip/pbuf.h"
+#include "lwip/priv/tcpip_priv.h"
+#if LWIP_CHECKSUM_ON_COPY
+#include "lwip/inet_chksum.h"
+#endif
+
+#include <string.h>
+
+/* If the netconn API is not required publicly, then we include the necessary
+   files here to get the implementation */
+#if !LWIP_NETCONN
+#undef LWIP_NETCONN
+#define LWIP_NETCONN 1
+#include "api_msg.c"
+#include "api_lib.c"
+#include "netbuf.c"
+#undef LWIP_NETCONN
+#define LWIP_NETCONN 0
+#endif
+
+#if LWIP_IPV4
+#define IP4ADDR_PORT_TO_SOCKADDR(sin, ipaddr, port) do { \
+      (sin)->sin_len = sizeof(struct sockaddr_in); \
+      (sin)->sin_family = AF_INET; \
+      (sin)->sin_port = lwip_htons((port)); \
+      inet_addr_from_ip4addr(&(sin)->sin_addr, ipaddr); \
+      memset((sin)->sin_zero, 0, SIN_ZERO_LEN); }while(0)
+#define SOCKADDR4_TO_IP4ADDR_PORT(sin, ipaddr, port) do { \
+    inet_addr_to_ip4addr(ip_2_ip4(ipaddr), &((sin)->sin_addr)); \
+    (port) = lwip_ntohs((sin)->sin_port); }while(0)
+#endif /* LWIP_IPV4 */
+
+#if LWIP_IPV6
+#define IP6ADDR_PORT_TO_SOCKADDR(sin6, ipaddr, port) do { \
+      (sin6)->sin6_len = sizeof(struct sockaddr_in6); \
+      (sin6)->sin6_family = AF_INET6; \
+      (sin6)->sin6_port = lwip_htons((port)); \
+      (sin6)->sin6_flowinfo = 0; \
+      inet6_addr_from_ip6addr(&(sin6)->sin6_addr, ipaddr); \
+      (sin6)->sin6_scope_id = 0; }while(0)
+#define SOCKADDR6_TO_IP6ADDR_PORT(sin6, ipaddr, port) do { \
+    inet6_addr_to_ip6addr(ip_2_ip6(ipaddr), &((sin6)->sin6_addr)); \
+    (port) = lwip_ntohs((sin6)->sin6_port); }while(0)
+#endif /* LWIP_IPV6 */
+
+#if LWIP_IPV4 && LWIP_IPV6
+static void sockaddr_to_ipaddr_port(const struct sockaddr* sockaddr, ip_addr_t* ipaddr, u16_t* port);
+
+#define IS_SOCK_ADDR_LEN_VALID(namelen)  (((namelen) == sizeof(struct sockaddr_in)) || \
+                                         ((namelen) == sizeof(struct sockaddr_in6)))
+#define IS_SOCK_ADDR_TYPE_VALID(name)    (((name)->sa_family == AF_INET) || \
+                                         ((name)->sa_family == AF_INET6))
+#define SOCK_ADDR_TYPE_MATCH(name, sock) \
+       ((((name)->sa_family == AF_INET) && !(NETCONNTYPE_ISIPV6((sock)->conn->type))) || \
+       (((name)->sa_family == AF_INET6) && (NETCONNTYPE_ISIPV6((sock)->conn->type))))
+#define IPADDR_PORT_TO_SOCKADDR(sockaddr, ipaddr, port) do { \
+    if (IP_IS_V6(ipaddr)) { \
+      IP6ADDR_PORT_TO_SOCKADDR((struct sockaddr_in6*)(void*)(sockaddr), ip_2_ip6(ipaddr), port); \
+    } else { \
+      IP4ADDR_PORT_TO_SOCKADDR((struct sockaddr_in*)(void*)(sockaddr), ip_2_ip4(ipaddr), port); \
+    } } while(0)
+#define SOCKADDR_TO_IPADDR_PORT(sockaddr, ipaddr, port) sockaddr_to_ipaddr_port(sockaddr, ipaddr, &(port))
+#define DOMAIN_TO_NETCONN_TYPE(domain, type) (((domain) == AF_INET) ? \
+  (type) : (enum netconn_type)((type) | NETCONN_TYPE_IPV6))
+#elif LWIP_IPV6 /* LWIP_IPV4 && LWIP_IPV6 */
+#define IS_SOCK_ADDR_LEN_VALID(namelen)  ((namelen) == sizeof(struct sockaddr_in6))
+#define IS_SOCK_ADDR_TYPE_VALID(name)    ((name)->sa_family == AF_INET6)
+#define SOCK_ADDR_TYPE_MATCH(name, sock) 1
+#define IPADDR_PORT_TO_SOCKADDR(sockaddr, ipaddr, port) \
+        IP6ADDR_PORT_TO_SOCKADDR((struct sockaddr_in6*)(void*)(sockaddr), ip_2_ip6(ipaddr), port)
+#define SOCKADDR_TO_IPADDR_PORT(sockaddr, ipaddr, port) \
+        SOCKADDR6_TO_IP6ADDR_PORT((const struct sockaddr_in6*)(const void*)(sockaddr), ipaddr, port)
+#define DOMAIN_TO_NETCONN_TYPE(domain, netconn_type) (netconn_type)
+#else /*-> LWIP_IPV4: LWIP_IPV4 && LWIP_IPV6 */
+#define IS_SOCK_ADDR_LEN_VALID(namelen)  ((namelen) == sizeof(struct sockaddr_in))
+#define IS_SOCK_ADDR_TYPE_VALID(name)    ((name)->sa_family == AF_INET)
+#define SOCK_ADDR_TYPE_MATCH(name, sock) 1
+#define IPADDR_PORT_TO_SOCKADDR(sockaddr, ipaddr, port) \
+        IP4ADDR_PORT_TO_SOCKADDR((struct sockaddr_in*)(void*)(sockaddr), ip_2_ip4(ipaddr), port)
+#define SOCKADDR_TO_IPADDR_PORT(sockaddr, ipaddr, port) \
+        SOCKADDR4_TO_IP4ADDR_PORT((const struct sockaddr_in*)(const void*)(sockaddr), ipaddr, port)
+#define DOMAIN_TO_NETCONN_TYPE(domain, netconn_type) (netconn_type)
+#endif /* LWIP_IPV6 */
+
+#define IS_SOCK_ADDR_TYPE_VALID_OR_UNSPEC(name)    (((name)->sa_family == AF_UNSPEC) || \
+                                                    IS_SOCK_ADDR_TYPE_VALID(name))
+#define SOCK_ADDR_TYPE_MATCH_OR_UNSPEC(name, sock) (((name)->sa_family == AF_UNSPEC) || \
+                                                    SOCK_ADDR_TYPE_MATCH(name, sock))
+#define IS_SOCK_ADDR_ALIGNED(name)      ((((mem_ptr_t)(name)) % 4) == 0)
+
+
+#define LWIP_SOCKOPT_CHECK_OPTLEN(optlen, opttype) do { if ((optlen) < sizeof(opttype)) { return EINVAL; }}while(0)
+#define LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, opttype) do { \
+  LWIP_SOCKOPT_CHECK_OPTLEN(optlen, opttype); \
+  if ((sock)->conn == NULL) { return EINVAL; } }while(0)
+#define LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, opttype) do { \
+  LWIP_SOCKOPT_CHECK_OPTLEN(optlen, opttype); \
+  if (((sock)->conn == NULL) || ((sock)->conn->pcb.tcp == NULL)) { return EINVAL; } }while(0)
+#define LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, opttype, netconntype) do { \
+  LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, opttype); \
+  if (NETCONNTYPE_GROUP(netconn_type((sock)->conn)) != netconntype) { return ENOPROTOOPT; } }while(0)
+
+
+#define LWIP_SETGETSOCKOPT_DATA_VAR_REF(name)     API_VAR_REF(name)
+#define LWIP_SETGETSOCKOPT_DATA_VAR_DECLARE(name) API_VAR_DECLARE(struct lwip_setgetsockopt_data, name)
+#define LWIP_SETGETSOCKOPT_DATA_VAR_FREE(name)    API_VAR_FREE(MEMP_SOCKET_SETGETSOCKOPT_DATA, name)
+#if LWIP_MPU_COMPATIBLE
+#define LWIP_SETGETSOCKOPT_DATA_VAR_ALLOC(name, sock) do { \
+  name = (struct lwip_setgetsockopt_data *)memp_malloc(MEMP_SOCKET_SETGETSOCKOPT_DATA); \
+  if (name == NULL) { \
+    sock_set_errno(sock, ENOMEM); \
+    return -1; \
+  } }while(0)
+#else /* LWIP_MPU_COMPATIBLE */
+#define LWIP_SETGETSOCKOPT_DATA_VAR_ALLOC(name, sock)
+#endif /* LWIP_MPU_COMPATIBLE */
+
+#if LWIP_SO_SNDRCVTIMEO_NONSTANDARD
+#define LWIP_SO_SNDRCVTIMEO_OPTTYPE int
+#define LWIP_SO_SNDRCVTIMEO_SET(optval, val) (*(int *)(optval) = (val))
+#define LWIP_SO_SNDRCVTIMEO_GET_MS(optval)   ((s32_t)*(const int*)(optval))
+#else
+#define LWIP_SO_SNDRCVTIMEO_OPTTYPE struct timeval
+#define LWIP_SO_SNDRCVTIMEO_SET(optval, val)  do { \
+  s32_t loc = (val); \
+  ((struct timeval *)(optval))->tv_sec = (loc) / 1000U; \
+  ((struct timeval *)(optval))->tv_usec = ((loc) % 1000U) * 1000U; }while(0)
+#define LWIP_SO_SNDRCVTIMEO_GET_MS(optval) ((((const struct timeval *)(optval))->tv_sec * 1000U) + (((const struct timeval *)(optval))->tv_usec / 1000U))
+#endif
+
+#define NUM_SOCKETS MEMP_NUM_NETCONN
+
+/** This is overridable for the rare case where more than 255 threads
+ * select on the same socket...
+ */
+#ifndef SELWAIT_T
+#define SELWAIT_T u8_t
+#endif
+
+/** Contains all internal pointers and states used for a socket */
+struct lwip_sock {
+  /** sockets currently are built on netconns, each socket has one netconn */
+  struct netconn *conn;
+  /** data that was left from the previous read */
+  void *lastdata;
+  /** offset in the data that was left from the previous read */
+  u16_t lastoffset;
+  /** number of times data was received, set by event_callback(),
+      tested by the receive and select functions */
+  s16_t rcvevent;
+  /** number of times data was ACKed (free send buffer), set by event_callback(),
+      tested by select */
+  u16_t sendevent;
+  /** error happened for this socket, set by event_callback(), tested by select */
+  u16_t errevent;
+  /** last error that occurred on this socket (in fact, all our errnos fit into an u8_t) */
+  u8_t err;
+  /** counter of how many threads are waiting for this socket using select */
+  SELWAIT_T select_waiting;
+};
+
+#if LWIP_NETCONN_SEM_PER_THREAD
+#define SELECT_SEM_T        sys_sem_t*
+#define SELECT_SEM_PTR(sem) (sem)
+#else /* LWIP_NETCONN_SEM_PER_THREAD */
+#define SELECT_SEM_T        sys_sem_t
+#define SELECT_SEM_PTR(sem) (&(sem))
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
+
+/** Description for a task waiting in select */
+struct lwip_select_cb {
+  /** Pointer to the next waiting task */
+  struct lwip_select_cb *next;
+  /** Pointer to the previous waiting task */
+  struct lwip_select_cb *prev;
+  /** readset passed to select */
+  fd_set *readset;
+  /** writeset passed to select */
+  fd_set *writeset;
+  /** unimplemented: exceptset passed to select */
+  fd_set *exceptset;
+  /** don't signal the same semaphore twice: set to 1 when signalled */
+  int sem_signalled;
+  /** semaphore to wake up a task waiting for select */
+  SELECT_SEM_T sem;
+};
+
+/** A struct sockaddr replacement that has the same alignment as sockaddr_in/
+ *  sockaddr_in6 if instantiated.
+ */
+union sockaddr_aligned {
+   struct sockaddr sa;
+#if LWIP_IPV6
+   struct sockaddr_in6 sin6;
+#endif /* LWIP_IPV6 */
+#if LWIP_IPV4
+   struct sockaddr_in sin;
+#endif /* LWIP_IPV4 */
+};
+
+#if LWIP_IGMP
+/* Define the number of IPv4 multicast memberships, default is one per socket */
+#ifndef LWIP_SOCKET_MAX_MEMBERSHIPS
+#define LWIP_SOCKET_MAX_MEMBERSHIPS NUM_SOCKETS
+#endif
+
+/* This is to keep track of IP_ADD_MEMBERSHIP calls to drop the membership when
+   a socket is closed */
+struct lwip_socket_multicast_pair {
+  /** the socket */
+  struct lwip_sock* sock;
+  /** the interface address */
+  ip4_addr_t if_addr;
+  /** the group address */
+  ip4_addr_t multi_addr;
+};
+
+struct lwip_socket_multicast_pair socket_ipv4_multicast_memberships[LWIP_SOCKET_MAX_MEMBERSHIPS];
+
+static int  lwip_socket_register_membership(int s, const ip4_addr_t *if_addr, const ip4_addr_t *multi_addr);
+static void lwip_socket_unregister_membership(int s, const ip4_addr_t *if_addr, const ip4_addr_t *multi_addr);
+static void lwip_socket_drop_registered_memberships(int s);
+#endif /* LWIP_IGMP */
+
+/** The global array of available sockets */
+static struct lwip_sock sockets[NUM_SOCKETS];
+/** The global list of tasks waiting for select */
+static struct lwip_select_cb *select_cb_list;
+/** This counter is increased from lwip_select when the list is changed
+    and checked in event_callback to see if it has changed. */
+static volatile int select_cb_ctr;
+
+#if LWIP_SOCKET_SET_ERRNO
+#ifndef set_errno
+#define set_errno(err) do { if (err) { errno = (err); } } while(0)
+#endif
+#else /* LWIP_SOCKET_SET_ERRNO */
+#define set_errno(err)
+#endif /* LWIP_SOCKET_SET_ERRNO */
+
+#define sock_set_errno(sk, e) do { \
+  const int sockerr = (e); \
+  sk->err = (u8_t)sockerr; \
+  set_errno(sockerr); \
+} while (0)
+
+/* Forward declaration of some functions */
+static void event_callback(struct netconn *conn, enum netconn_evt evt, u16_t len);
+#if !LWIP_TCPIP_CORE_LOCKING
+static void lwip_getsockopt_callback(void *arg);
+static void lwip_setsockopt_callback(void *arg);
+#endif
+static u8_t lwip_getsockopt_impl(int s, int level, int optname, void *optval, socklen_t *optlen);
+static u8_t lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_t optlen);
+
+#if LWIP_IPV4 && LWIP_IPV6
+static void
+sockaddr_to_ipaddr_port(const struct sockaddr* sockaddr, ip_addr_t* ipaddr, u16_t* port)
+{
+  if ((sockaddr->sa_family) == AF_INET6) {
+    SOCKADDR6_TO_IP6ADDR_PORT((const struct sockaddr_in6*)(const void*)(sockaddr), ipaddr, *port);
+    ipaddr->type = IPADDR_TYPE_V6;
+  } else {
+    SOCKADDR4_TO_IP4ADDR_PORT((const struct sockaddr_in*)(const void*)(sockaddr), ipaddr, *port);
+    ipaddr->type = IPADDR_TYPE_V4;
+  }
+}
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
+/** LWIP_NETCONN_SEM_PER_THREAD==1: initialize thread-local semaphore */
+void
+lwip_socket_thread_init(void)
+{
+   netconn_thread_init();
+}
+
+/** LWIP_NETCONN_SEM_PER_THREAD==1: destroy thread-local semaphore */
+void
+lwip_socket_thread_cleanup(void)
+{
+   netconn_thread_cleanup();
+}
+
+/**
+ * Map a externally used socket index to the internal socket representation.
+ *
+ * @param s externally used socket index
+ * @return struct lwip_sock for the socket or NULL if not found
+ */
+static struct lwip_sock *
+get_socket(int s)
+{
+  struct lwip_sock *sock;
+
+  s -= LWIP_SOCKET_OFFSET;
+
+  if ((s < 0) || (s >= NUM_SOCKETS)) {
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("get_socket(%d): invalid\n", s + LWIP_SOCKET_OFFSET));
+    set_errno(EBADF);
+    return NULL;
+  }
+
+  sock = &sockets[s];
+
+  if (!sock->conn) {
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("get_socket(%d): not active\n", s + LWIP_SOCKET_OFFSET));
+    set_errno(EBADF);
+    return NULL;
+  }
+
+  return sock;
+}
+
+/**
+ * Same as get_socket but doesn't set errno
+ *
+ * @param s externally used socket index
+ * @return struct lwip_sock for the socket or NULL if not found
+ */
+static struct lwip_sock *
+tryget_socket(int s)
+{
+  s -= LWIP_SOCKET_OFFSET;
+  if ((s < 0) || (s >= NUM_SOCKETS)) {
+    return NULL;
+  }
+  if (!sockets[s].conn) {
+    return NULL;
+  }
+  return &sockets[s];
+}
+
+/**
+ * Allocate a new socket for a given netconn.
+ *
+ * @param newconn the netconn for which to allocate a socket
+ * @param accepted 1 if socket has been created by accept(),
+ *                 0 if socket has been created by socket()
+ * @return the index of the new socket; -1 on error
+ */
+static int
+alloc_socket(struct netconn *newconn, int accepted)
+{
+  int i;
+  SYS_ARCH_DECL_PROTECT(lev);
+
+  /* allocate a new socket identifier */
+  for (i = 0; i < NUM_SOCKETS; ++i) {
+    /* Protect socket array */
+    SYS_ARCH_PROTECT(lev);
+    if (!sockets[i].conn && (sockets[i].select_waiting == 0)) {
+      sockets[i].conn       = newconn;
+      /* The socket is not yet known to anyone, so no need to protect
+         after having marked it as used. */
+      SYS_ARCH_UNPROTECT(lev);
+      sockets[i].lastdata   = NULL;
+      sockets[i].lastoffset = 0;
+      sockets[i].rcvevent   = 0;
+      /* TCP sendbuf is empty, but the socket is not yet writable until connected
+       * (unless it has been created by accept()). */
+      sockets[i].sendevent  = (NETCONNTYPE_GROUP(newconn->type) == NETCONN_TCP ? (accepted != 0) : 1);
+      sockets[i].errevent   = 0;
+      sockets[i].err        = 0;
+      return i + LWIP_SOCKET_OFFSET;
+    }
+    SYS_ARCH_UNPROTECT(lev);
+  }
+  return -1;
+}
+
+/** Free a socket. The socket's netconn must have been
+ * delete before!
+ *
+ * @param sock the socket to free
+ * @param is_tcp != 0 for TCP sockets, used to free lastdata
+ */
+static void
+free_socket(struct lwip_sock *sock, int is_tcp)
+{
+  void *lastdata;
+
+  lastdata         = sock->lastdata;
+  sock->lastdata   = NULL;
+  sock->lastoffset = 0;
+  sock->err        = 0;
+
+  /* Protect socket array */
+  SYS_ARCH_SET(sock->conn, NULL);
+  /* don't use 'sock' after this line, as another task might have allocated it */
+
+  if (lastdata != NULL) {
+    if (is_tcp) {
+      pbuf_free((struct pbuf *)lastdata);
+    } else {
+      netbuf_delete((struct netbuf *)lastdata);
+    }
+  }
+}
+
+/* Below this, the well-known socket functions are implemented.
+ * Use google.com or opengroup.org to get a good description :-)
+ *
+ * Exceptions are documented!
+ */
+
+int
+lwip_accept(int s, struct sockaddr *addr, socklen_t *addrlen)
+{
+  struct lwip_sock *sock, *nsock;
+  struct netconn *newconn;
+  ip_addr_t naddr;
+  u16_t port = 0;
+  int newsock;
+  err_t err;
+  SYS_ARCH_DECL_PROTECT(lev);
+
+  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d)...\n", s));
+  sock = get_socket(s);
+  if (!sock) {
+    return -1;
+  }
+
+  if (netconn_is_nonblocking(sock->conn) && (sock->rcvevent <= 0)) {
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d): returning EWOULDBLOCK\n", s));
+    set_errno(EWOULDBLOCK);
+    return -1;
+  }
+
+  /* wait for a new connection */
+  err = netconn_accept(sock->conn, &newconn);
+  if (err != ERR_OK) {
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d): netconn_acept failed, err=%d\n", s, err));
+    if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
+      sock_set_errno(sock, EOPNOTSUPP);
+    } else if (err == ERR_CLSD) {
+      sock_set_errno(sock, EINVAL);
+    } else {
+      sock_set_errno(sock, err_to_errno(err));
+    }
+    return -1;
+  }
+  LWIP_ASSERT("newconn != NULL", newconn != NULL);
+
+  newsock = alloc_socket(newconn, 1);
+  if (newsock == -1) {
+    netconn_delete(newconn);
+    sock_set_errno(sock, ENFILE);
+    return -1;
+  }
+  LWIP_ASSERT("invalid socket index", (newsock >= LWIP_SOCKET_OFFSET) && (newsock < NUM_SOCKETS + LWIP_SOCKET_OFFSET));
+  LWIP_ASSERT("newconn->callback == event_callback", newconn->callback == event_callback);
+  nsock = &sockets[newsock - LWIP_SOCKET_OFFSET];
+
+  /* See event_callback: If data comes in right away after an accept, even
+   * though the server task might not have created a new socket yet.
+   * In that case, newconn->socket is counted down (newconn->socket--),
+   * so nsock->rcvevent is >= 1 here!
+   */
+  SYS_ARCH_PROTECT(lev);
+  nsock->rcvevent += (s16_t)(-1 - newconn->socket);
+  newconn->socket = newsock;
+  SYS_ARCH_UNPROTECT(lev);
+
+  /* Note that POSIX only requires us to check addr is non-NULL. addrlen must
+   * not be NULL if addr is valid.
+   */
+  if (addr != NULL) {
+    union sockaddr_aligned tempaddr;
+    /* get the IP address and port of the remote host */
+    err = netconn_peer(newconn, &naddr, &port);
+    if (err != ERR_OK) {
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d): netconn_peer failed, err=%d\n", s, err));
+      netconn_delete(newconn);
+      free_socket(nsock, 1);
+      sock_set_errno(sock, err_to_errno(err));
+      return -1;
+    }
+    LWIP_ASSERT("addr valid but addrlen NULL", addrlen != NULL);
+
+    IPADDR_PORT_TO_SOCKADDR(&tempaddr, &naddr, port);
+    if (*addrlen > tempaddr.sa.sa_len) {
+      *addrlen = tempaddr.sa.sa_len;
+    }
+    MEMCPY(addr, &tempaddr, *addrlen);
+
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d) returning new sock=%d addr=", s, newsock));
+    ip_addr_debug_print_val(SOCKETS_DEBUG, naddr);
+    LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F"\n", port));
+  } else {
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d) returning new sock=%d", s, newsock));
+  }
+
+  sock_set_errno(sock, 0);
+  return newsock;
+}
+
+int
+lwip_bind(int s, const struct sockaddr *name, socklen_t namelen)
+{
+  struct lwip_sock *sock;
+  ip_addr_t local_addr;
+  u16_t local_port;
+  err_t err;
+
+  sock = get_socket(s);
+  if (!sock) {
+    return -1;
+  }
+
+  if (!SOCK_ADDR_TYPE_MATCH(name, sock)) {
+    /* sockaddr does not match socket type (IPv4/IPv6) */
+    sock_set_errno(sock, err_to_errno(ERR_VAL));
+    return -1;
+  }
+
+  /* check size, family and alignment of 'name' */
+  LWIP_ERROR("lwip_bind: invalid address", (IS_SOCK_ADDR_LEN_VALID(namelen) &&
+             IS_SOCK_ADDR_TYPE_VALID(name) && IS_SOCK_ADDR_ALIGNED(name)),
+             sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;);
+  LWIP_UNUSED_ARG(namelen);
+
+  SOCKADDR_TO_IPADDR_PORT(name, &local_addr, local_port);
+  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d, addr=", s));
+  ip_addr_debug_print_val(SOCKETS_DEBUG, local_addr);
+  LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", local_port));
+
+#if LWIP_IPV4 && LWIP_IPV6
+  /* Dual-stack: Unmap IPv4 mapped IPv6 addresses */
+  if (IP_IS_V6_VAL(local_addr) && ip6_addr_isipv4mappedipv6(ip_2_ip6(&local_addr))) {
+    unmap_ipv4_mapped_ipv6(ip_2_ip4(&local_addr), ip_2_ip6(&local_addr));
+    IP_SET_TYPE_VAL(local_addr, IPADDR_TYPE_V4);
+  }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
+  err = netconn_bind(sock->conn, &local_addr, local_port);
+
+  if (err != ERR_OK) {
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d) failed, err=%d\n", s, err));
+    sock_set_errno(sock, err_to_errno(err));
+    return -1;
+  }
+
+  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d) succeeded\n", s));
+  sock_set_errno(sock, 0);
+  return 0;
+}
+
+int
+lwip_close(int s)
+{
+  struct lwip_sock *sock;
+  int is_tcp = 0;
+  err_t err;
+
+  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_close(%d)\n", s));
+
+  sock = get_socket(s);
+  if (!sock) {
+    return -1;
+  }
+
+  if (sock->conn != NULL) {
+    is_tcp = NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP;
+  } else {
+    LWIP_ASSERT("sock->lastdata == NULL", sock->lastdata == NULL);
+  }
+
+#if LWIP_IGMP
+  /* drop all possibly joined IGMP memberships */
+  lwip_socket_drop_registered_memberships(s);
+#endif /* LWIP_IGMP */
+
+  err = netconn_delete(sock->conn);
+  if (err != ERR_OK) {
+    sock_set_errno(sock, err_to_errno(err));
+    return -1;
+  }
+
+  free_socket(sock, is_tcp);
+  set_errno(0);
+  return 0;
+}
+
+int
+lwip_connect(int s, const struct sockaddr *name, socklen_t namelen)
+{
+  struct lwip_sock *sock;
+  err_t err;
+
+  sock = get_socket(s);
+  if (!sock) {
+    return -1;
+  }
+
+  if (!SOCK_ADDR_TYPE_MATCH_OR_UNSPEC(name, sock)) {
+    /* sockaddr does not match socket type (IPv4/IPv6) */
+    sock_set_errno(sock, err_to_errno(ERR_VAL));
+    return -1;
+  }
+
+  LWIP_UNUSED_ARG(namelen);
+  if (name->sa_family == AF_UNSPEC) {
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d, AF_UNSPEC)\n", s));
+    err = netconn_disconnect(sock->conn);
+  } else {
+    ip_addr_t remote_addr;
+    u16_t remote_port;
+
+    /* check size, family and alignment of 'name' */
+    LWIP_ERROR("lwip_connect: invalid address", IS_SOCK_ADDR_LEN_VALID(namelen) &&
+               IS_SOCK_ADDR_TYPE_VALID_OR_UNSPEC(name) && IS_SOCK_ADDR_ALIGNED(name),
+               sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;);
+
+    SOCKADDR_TO_IPADDR_PORT(name, &remote_addr, remote_port);
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d, addr=", s));
+    ip_addr_debug_print_val(SOCKETS_DEBUG, remote_addr);
+    LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", remote_port));
+
+#if LWIP_IPV4 && LWIP_IPV6
+    /* Dual-stack: Unmap IPv4 mapped IPv6 addresses */
+    if (IP_IS_V6_VAL(remote_addr) && ip6_addr_isipv4mappedipv6(ip_2_ip6(&remote_addr))) {
+      unmap_ipv4_mapped_ipv6(ip_2_ip4(&remote_addr), ip_2_ip6(&remote_addr));
+      IP_SET_TYPE_VAL(remote_addr, IPADDR_TYPE_V4);
+    }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
+    err = netconn_connect(sock->conn, &remote_addr, remote_port);
+  }
+
+  if (err != ERR_OK) {
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d) failed, err=%d\n", s, err));
+    sock_set_errno(sock, err_to_errno(err));
+    return -1;
+  }
+
+  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d) succeeded\n", s));
+  sock_set_errno(sock, 0);
+  return 0;
+}
+
+/**
+ * Set a socket into listen mode.
+ * The socket may not have been used for another connection previously.
+ *
+ * @param s the socket to set to listening mode
+ * @param backlog (ATTENTION: needs TCP_LISTEN_BACKLOG=1)
+ * @return 0 on success, non-zero on failure
+ */
+int
+lwip_listen(int s, int backlog)
+{
+  struct lwip_sock *sock;
+  err_t err;
+
+  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_listen(%d, backlog=%d)\n", s, backlog));
+
+  sock = get_socket(s);
+  if (!sock) {
+    return -1;
+  }
+
+  /* limit the "backlog" parameter to fit in an u8_t */
+  backlog = LWIP_MIN(LWIP_MAX(backlog, 0), 0xff);
+
+  err = netconn_listen_with_backlog(sock->conn, (u8_t)backlog);
+
+  if (err != ERR_OK) {
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_listen(%d) failed, err=%d\n", s, err));
+    if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
+      sock_set_errno(sock, EOPNOTSUPP);
+      return -1;
+    }
+    sock_set_errno(sock, err_to_errno(err));
+    return -1;
+  }
+
+  sock_set_errno(sock, 0);
+  return 0;
+}
+
+int
+lwip_recvfrom(int s, void *mem, size_t len, int flags,
+              struct sockaddr *from, socklen_t *fromlen)
+{
+  struct lwip_sock *sock;
+  void             *buf = NULL;
+  struct pbuf      *p;
+  u16_t            buflen, copylen;
+  int              off = 0;
+  u8_t             done = 0;
+  err_t            err;
+
+  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d, %p, %"SZT_F", 0x%x, ..)\n", s, mem, len, flags));
+  sock = get_socket(s);
+  if (!sock) {
+    return -1;
+  }
+
+  do {
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: top while sock->lastdata=%p\n", sock->lastdata));
+    /* Check if there is data left from the last recv operation. */
+    if (sock->lastdata) {
+      buf = sock->lastdata;
+    } else {
+      /* If this is non-blocking call, then check first */
+      if (((flags & MSG_DONTWAIT) || netconn_is_nonblocking(sock->conn)) &&
+          (sock->rcvevent <= 0)) {
+        if (off > 0) {
+          /* already received data, return that */
+          sock_set_errno(sock, 0);
+          return off;
+        }
+        LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): returning EWOULDBLOCK\n", s));
+        set_errno(EWOULDBLOCK);
+        return -1;
+      }
+
+      /* No data was left from the previous operation, so we try to get
+         some from the network. */
+      if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
+        err = netconn_recv_tcp_pbuf(sock->conn, (struct pbuf **)&buf);
+      } else {
+        err = netconn_recv(sock->conn, (struct netbuf **)&buf);
+      }
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: netconn_recv err=%d, netbuf=%p\n",
+        err, buf));
+
+      if (err != ERR_OK) {
+        if (off > 0) {
+          if (err == ERR_CLSD) {
+            /* closed but already received data, ensure select gets the FIN, too */
+            event_callback(sock->conn, NETCONN_EVT_RCVPLUS, 0);
+          }
+          /* already received data, return that */
+          sock_set_errno(sock, 0);
+          return off;
+        }
+        /* We should really do some error checking here. */
+        LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): buf == NULL, error is \"%s\"!\n",
+          s, lwip_strerr(err)));
+        sock_set_errno(sock, err_to_errno(err));
+        if (err == ERR_CLSD) {
+          return 0;
+        } else {
+          return -1;
+        }
+      }
+      LWIP_ASSERT("buf != NULL", buf != NULL);
+      sock->lastdata = buf;
+    }
+
+    if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
+      p = (struct pbuf *)buf;
+    } else {
+      p = ((struct netbuf *)buf)->p;
+    }
+    buflen = p->tot_len;
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: buflen=%"U16_F" len=%"SZT_F" off=%d sock->lastoffset=%"U16_F"\n",
+      buflen, len, off, sock->lastoffset));
+
+    buflen -= sock->lastoffset;
+
+    if (len > buflen) {
+      copylen = buflen;
+    } else {
+      copylen = (u16_t)len;
+    }
+
+    /* copy the contents of the received buffer into
+    the supplied memory pointer mem */
+    pbuf_copy_partial(p, (u8_t*)mem + off, copylen, sock->lastoffset);
+
+    off += copylen;
+
+    if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
+      LWIP_ASSERT("invalid copylen, len would underflow", len >= copylen);
+      len -= copylen;
+      if ((len <= 0) ||
+          (p->flags & PBUF_FLAG_PUSH) ||
+          (sock->rcvevent <= 0) ||
+          ((flags & MSG_PEEK) != 0)) {
+        done = 1;
+      }
+    } else {
+      done = 1;
+    }
+
+    /* Check to see from where the data was.*/
+    if (done) {
+#if !SOCKETS_DEBUG
+      if (from && fromlen)
+#endif /* !SOCKETS_DEBUG */
+      {
+        u16_t port;
+        ip_addr_t tmpaddr;
+        ip_addr_t *fromaddr;
+        union sockaddr_aligned saddr;
+        LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): addr=", s));
+        if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
+          fromaddr = &tmpaddr;
+          netconn_getaddr(sock->conn, fromaddr, &port, 0);
+        } else {
+          port = netbuf_fromport((struct netbuf *)buf);
+          fromaddr = netbuf_fromaddr((struct netbuf *)buf);
+        }
+
+#if LWIP_IPV4 && LWIP_IPV6
+        /* Dual-stack: Map IPv4 addresses to IPv4 mapped IPv6 */
+        if (NETCONNTYPE_ISIPV6(netconn_type(sock->conn)) && IP_IS_V4(fromaddr)) {
+          ip4_2_ipv4_mapped_ipv6(ip_2_ip6(fromaddr), ip_2_ip4(fromaddr));
+          IP_SET_TYPE(fromaddr, IPADDR_TYPE_V6);
+        }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
+        IPADDR_PORT_TO_SOCKADDR(&saddr, fromaddr, port);
+        ip_addr_debug_print(SOCKETS_DEBUG, fromaddr);
+        LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F" len=%d\n", port, off));
+#if SOCKETS_DEBUG
+        if (from && fromlen)
+#endif /* SOCKETS_DEBUG */
+        {
+          if (*fromlen > saddr.sa.sa_len) {
+            *fromlen = saddr.sa.sa_len;
+          }
+          MEMCPY(from, &saddr, *fromlen);
+        }
+      }
+    }
+
+    /* If we don't peek the incoming message... */
+    if ((flags & MSG_PEEK) == 0) {
+      /* If this is a TCP socket, check if there is data left in the
+         buffer. If so, it should be saved in the sock structure for next
+         time around. */
+      if ((NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) && (buflen - copylen > 0)) {
+        sock->lastdata = buf;
+        sock->lastoffset += copylen;
+        LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: lastdata now netbuf=%p\n", buf));
+      } else {
+        sock->lastdata = NULL;
+        sock->lastoffset = 0;
+        LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: deleting netbuf=%p\n", buf));
+        if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
+          pbuf_free((struct pbuf *)buf);
+        } else {
+          netbuf_delete((struct netbuf *)buf);
+        }
+        buf = NULL;
+      }
+    }
+  } while (!done);
+
+  sock_set_errno(sock, 0);
+  return off;
+}
+
+int
+lwip_read(int s, void *mem, size_t len)
+{
+  return lwip_recvfrom(s, mem, len, 0, NULL, NULL);
+}
+
+int
+lwip_recv(int s, void *mem, size_t len, int flags)
+{
+  return lwip_recvfrom(s, mem, len, flags, NULL, NULL);
+}
+
+int
+lwip_send(int s, const void *data, size_t size, int flags)
+{
+  struct lwip_sock *sock;
+  err_t err;
+  u8_t write_flags;
+  size_t written;
+
+  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_send(%d, data=%p, size=%"SZT_F", flags=0x%x)\n",
+                              s, data, size, flags));
+
+  sock = get_socket(s);
+  if (!sock) {
+    return -1;
+  }
+
+  if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
+#if (LWIP_UDP || LWIP_RAW)
+    return lwip_sendto(s, data, size, flags, NULL, 0);
+#else /* (LWIP_UDP || LWIP_RAW) */
+    sock_set_errno(sock, err_to_errno(ERR_ARG));
+    return -1;
+#endif /* (LWIP_UDP || LWIP_RAW) */
+  }
+
+  write_flags = NETCONN_COPY |
+    ((flags & MSG_MORE)     ? NETCONN_MORE      : 0) |
+    ((flags & MSG_DONTWAIT) ? NETCONN_DONTBLOCK : 0);
+  written = 0;
+  err = netconn_write_partly(sock->conn, data, size, write_flags, &written);
+
+  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_send(%d) err=%d written=%"SZT_F"\n", s, err, written));
+  sock_set_errno(sock, err_to_errno(err));
+  return (err == ERR_OK ? (int)written : -1);
+}
+
+int
+lwip_sendmsg(int s, const struct msghdr *msg, int flags)
+{
+  struct lwip_sock *sock;
+  int i;
+#if LWIP_TCP
+  u8_t write_flags;
+  size_t written;
+#endif
+  int size = 0;
+  err_t err = ERR_OK;
+
+  sock = get_socket(s);
+  if (!sock) {
+    return -1;
+  }
+
+  LWIP_ERROR("lwip_sendmsg: invalid msghdr", msg != NULL,
+             sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;);
+
+  LWIP_UNUSED_ARG(msg->msg_control);
+  LWIP_UNUSED_ARG(msg->msg_controllen);
+  LWIP_UNUSED_ARG(msg->msg_flags);
+  LWIP_ERROR("lwip_sendmsg: invalid msghdr iov", (msg->msg_iov != NULL && msg->msg_iovlen != 0),
+             sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;);
+
+  if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
+#if LWIP_TCP
+    write_flags = NETCONN_COPY |
+    ((flags & MSG_MORE)     ? NETCONN_MORE      : 0) |
+    ((flags & MSG_DONTWAIT) ? NETCONN_DONTBLOCK : 0);
+
+    for (i = 0; i < msg->msg_iovlen; i++) {
+      u8_t apiflags = write_flags;
+      if (i + 1 < msg->msg_iovlen) {
+        apiflags |= NETCONN_MORE;
+      }
+      written = 0;
+      err = netconn_write_partly(sock->conn, msg->msg_iov[i].iov_base, msg->msg_iov[i].iov_len, write_flags, &written);
+      if (err == ERR_OK) {
+        size += written;
+        /* check that the entire IO vector was accepected, if not return a partial write */
+        if (written != msg->msg_iov[i].iov_len)
+          break;
+      }
+      /* none of this IO vector was accepted, but previous was, return partial write and conceal ERR_WOULDBLOCK */
+      else if (err == ERR_WOULDBLOCK && size > 0) {
+        err = ERR_OK;
+        /* let ERR_WOULDBLOCK persist on the netconn since we are returning ERR_OK */
+        break;
+      } else {
+        size = -1;
+        break;
+      }
+    }
+    sock_set_errno(sock, err_to_errno(err));
+    return size;
+#else /* LWIP_TCP */
+    sock_set_errno(sock, err_to_errno(ERR_ARG));
+    return -1;
+#endif /* LWIP_TCP */
+  }
+  /* else, UDP and RAW NETCONNs */
+#if LWIP_UDP || LWIP_RAW
+  {
+    struct netbuf *chain_buf;
+
+    LWIP_UNUSED_ARG(flags);
+    LWIP_ERROR("lwip_sendmsg: invalid msghdr name", (((msg->msg_name == NULL) && (msg->msg_namelen == 0)) ||
+               IS_SOCK_ADDR_LEN_VALID(msg->msg_namelen)) ,
+               sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;);
+
+    /* initialize chain buffer with destination */
+    chain_buf = netbuf_new();
+    if (!chain_buf) {
+      sock_set_errno(sock, err_to_errno(ERR_MEM));
+      return -1;
+    }
+    if (msg->msg_name) {
+      u16_t remote_port;
+      SOCKADDR_TO_IPADDR_PORT((const struct sockaddr *)msg->msg_name, &chain_buf->addr, remote_port);
+      netbuf_fromport(chain_buf) = remote_port;
+    }
+#if LWIP_NETIF_TX_SINGLE_PBUF
+    for (i = 0; i < msg->msg_iovlen; i++) {
+      size += msg->msg_iov[i].iov_len;
+    }
+    /* Allocate a new netbuf and copy the data into it. */
+    if (netbuf_alloc(chain_buf, (u16_t)size) == NULL) {
+       err = ERR_MEM;
+    } else {
+      /* flatten the IO vectors */
+      size_t offset = 0;
+      for (i = 0; i < msg->msg_iovlen; i++) {
+        MEMCPY(&((u8_t*)chain_buf->p->payload)[offset], msg->msg_iov[i].iov_base, msg->msg_iov[i].iov_len);
+        offset += msg->msg_iov[i].iov_len;
+      }
+#if LWIP_CHECKSUM_ON_COPY
+      {
+        /* This can be improved by using LWIP_CHKSUM_COPY() and aggregating the checksum for each IO vector */
+        u16_t chksum = ~inet_chksum_pbuf(chain_buf->p);
+        netbuf_set_chksum(chain_buf, chksum);
+      }
+#endif /* LWIP_CHECKSUM_ON_COPY */
+      err = ERR_OK;
+    }
+#else /* LWIP_NETIF_TX_SINGLE_PBUF */
+    /* create a chained netbuf from the IO vectors. NOTE: we assemble a pbuf chain
+       manually to avoid having to allocate, chain, and delete a netbuf for each iov */
+    for (i = 0; i < msg->msg_iovlen; i++) {
+      struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, 0, PBUF_REF);
+      if (p == NULL) {
+        err = ERR_MEM; /* let netbuf_delete() cleanup chain_buf */
+        break;
+      }
+      p->payload = msg->msg_iov[i].iov_base;
+      LWIP_ASSERT("iov_len < u16_t", msg->msg_iov[i].iov_len <= 0xFFFF);
+      p->len = p->tot_len = (u16_t)msg->msg_iov[i].iov_len;
+      /* netbuf empty, add new pbuf */
+      if (chain_buf->p == NULL) {
+        chain_buf->p = chain_buf->ptr = p;
+        /* add pbuf to existing pbuf chain */
+      } else {
+        pbuf_cat(chain_buf->p, p);
+      }
+    }
+    /* save size of total chain */
+    if (err == ERR_OK) {
+      size = netbuf_len(chain_buf);
+    }
+#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
+
+    if (err == ERR_OK) {
+#if LWIP_IPV4 && LWIP_IPV6
+      /* Dual-stack: Unmap IPv4 mapped IPv6 addresses */
+      if (IP_IS_V6_VAL(chain_buf->addr) && ip6_addr_isipv4mappedipv6(ip_2_ip6(&chain_buf->addr))) {
+        unmap_ipv4_mapped_ipv6(ip_2_ip4(&chain_buf->addr), ip_2_ip6(&chain_buf->addr));
+        IP_SET_TYPE_VAL(chain_buf->addr, IPADDR_TYPE_V4);
+      }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
+      /* send the data */
+      err = netconn_send(sock->conn, chain_buf);
+    }
+
+    /* deallocated the buffer */
+    netbuf_delete(chain_buf);
+
+    sock_set_errno(sock, err_to_errno(err));
+    return (err == ERR_OK ? size : -1);
+  }
+#else /* LWIP_UDP || LWIP_RAW */
+  sock_set_errno(sock, err_to_errno(ERR_ARG));
+  return -1;
+#endif /* LWIP_UDP || LWIP_RAW */
+}
+
+int
+lwip_sendto(int s, const void *data, size_t size, int flags,
+       const struct sockaddr *to, socklen_t tolen)
+{
+  struct lwip_sock *sock;
+  err_t err;
+  u16_t short_size;
+  u16_t remote_port;
+  struct netbuf buf;
+
+  sock = get_socket(s);
+  if (!sock) {
+    return -1;
+  }
+
+  if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
+#if LWIP_TCP
+    return lwip_send(s, data, size, flags);
+#else /* LWIP_TCP */
+    LWIP_UNUSED_ARG(flags);
+    sock_set_errno(sock, err_to_errno(ERR_ARG));
+    return -1;
+#endif /* LWIP_TCP */
+  }
+
+  /* @todo: split into multiple sendto's? */
+  LWIP_ASSERT("lwip_sendto: size must fit in u16_t", size <= 0xffff);
+  short_size = (u16_t)size;
+  LWIP_ERROR("lwip_sendto: invalid address", (((to == NULL) && (tolen == 0)) ||
+             (IS_SOCK_ADDR_LEN_VALID(tolen) &&
+             IS_SOCK_ADDR_TYPE_VALID(to) && IS_SOCK_ADDR_ALIGNED(to))),
+             sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;);
+  LWIP_UNUSED_ARG(tolen);
+
+  /* initialize a buffer */
+  buf.p = buf.ptr = NULL;
+#if LWIP_CHECKSUM_ON_COPY
+  buf.flags = 0;
+#endif /* LWIP_CHECKSUM_ON_COPY */
+  if (to) {
+    SOCKADDR_TO_IPADDR_PORT(to, &buf.addr, remote_port);
+  } else {
+    remote_port = 0;
+    ip_addr_set_any(NETCONNTYPE_ISIPV6(netconn_type(sock->conn)), &buf.addr);
+  }
+  netbuf_fromport(&buf) = remote_port;
+
+
+  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_sendto(%d, data=%p, short_size=%"U16_F", flags=0x%x to=",
+              s, data, short_size, flags));
+  ip_addr_debug_print(SOCKETS_DEBUG, &buf.addr);
+  LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F"\n", remote_port));
+
+  /* make the buffer point to the data that should be sent */
+#if LWIP_NETIF_TX_SINGLE_PBUF
+  /* Allocate a new netbuf and copy the data into it. */
+  if (netbuf_alloc(&buf, short_size) == NULL) {
+    err = ERR_MEM;
+  } else {
+#if LWIP_CHECKSUM_ON_COPY
+    if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_RAW) {
+      u16_t chksum = LWIP_CHKSUM_COPY(buf.p->payload, data, short_size);
+      netbuf_set_chksum(&buf, chksum);
+    } else
+#endif /* LWIP_CHECKSUM_ON_COPY */
+    {
+      MEMCPY(buf.p->payload, data, short_size);
+    }
+    err = ERR_OK;
+  }
+#else /* LWIP_NETIF_TX_SINGLE_PBUF */
+  err = netbuf_ref(&buf, data, short_size);
+#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
+  if (err == ERR_OK) {
+#if LWIP_IPV4 && LWIP_IPV6
+    /* Dual-stack: Unmap IPv4 mapped IPv6 addresses */
+    if (IP_IS_V6_VAL(buf.addr) && ip6_addr_isipv4mappedipv6(ip_2_ip6(&buf.addr))) {
+      unmap_ipv4_mapped_ipv6(ip_2_ip4(&buf.addr), ip_2_ip6(&buf.addr));
+      IP_SET_TYPE_VAL(buf.addr, IPADDR_TYPE_V4);
+    }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
+    /* send the data */
+    err = netconn_send(sock->conn, &buf);
+  }
+
+  /* deallocated the buffer */
+  netbuf_free(&buf);
+
+  sock_set_errno(sock, err_to_errno(err));
+  return (err == ERR_OK ? short_size : -1);
+}
+
+int
+lwip_socket(int domain, int type, int protocol)
+{
+  struct netconn *conn;
+  int i;
+
+  LWIP_UNUSED_ARG(domain); /* @todo: check this */
+
+  /* create a netconn */
+  switch (type) {
+  case SOCK_RAW:
+    conn = netconn_new_with_proto_and_callback(DOMAIN_TO_NETCONN_TYPE(domain, NETCONN_RAW),
+                                               (u8_t)protocol, event_callback);
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_RAW, %d) = ",
+                                 domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol));
+    break;
+  case SOCK_DGRAM:
+    conn = netconn_new_with_callback(DOMAIN_TO_NETCONN_TYPE(domain,
+                 ((protocol == IPPROTO_UDPLITE) ? NETCONN_UDPLITE : NETCONN_UDP)) ,
+                 event_callback);
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_DGRAM, %d) = ",
+                                 domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol));
+    break;
+  case SOCK_STREAM:
+    conn = netconn_new_with_callback(DOMAIN_TO_NETCONN_TYPE(domain, NETCONN_TCP), event_callback);
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_STREAM, %d) = ",
+                                 domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol));
+    break;
+  default:
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%d, %d/UNKNOWN, %d) = -1\n",
+                                 domain, type, protocol));
+    set_errno(EINVAL);
+    return -1;
+  }
+
+  if (!conn) {
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("-1 / ENOBUFS (could not create netconn)\n"));
+    set_errno(ENOBUFS);
+    return -1;
+  }
+
+  i = alloc_socket(conn, 0);
+
+  if (i == -1) {
+    netconn_delete(conn);
+    set_errno(ENFILE);
+    return -1;
+  }
+  conn->socket = i;
+  LWIP_DEBUGF(SOCKETS_DEBUG, ("%d\n", i));
+  set_errno(0);
+  return i;
+}
+
+int
+lwip_write(int s, const void *data, size_t size)
+{
+  return lwip_send(s, data, size, 0);
+}
+
+int
+lwip_writev(int s, const struct iovec *iov, int iovcnt)
+{
+  struct msghdr msg;
+
+  msg.msg_name = NULL;
+  msg.msg_namelen = 0;
+  /* Hack: we have to cast via number to cast from 'const' pointer to non-const.
+     Blame the opengroup standard for this inconsistency. */
+  msg.msg_iov = LWIP_CONST_CAST(struct iovec *, iov);
+  msg.msg_iovlen = iovcnt;
+  msg.msg_control = NULL;
+  msg.msg_controllen = 0;
+  msg.msg_flags = 0;
+  return lwip_sendmsg(s, &msg, 0);
+}
+
+/**
+ * Go through the readset and writeset lists and see which socket of the sockets
+ * set in the sets has events. On return, readset, writeset and exceptset have
+ * the sockets enabled that had events.
+ *
+ * @param maxfdp1 the highest socket index in the sets
+ * @param readset_in    set of sockets to check for read events
+ * @param writeset_in   set of sockets to check for write events
+ * @param exceptset_in  set of sockets to check for error events
+ * @param readset_out   set of sockets that had read events
+ * @param writeset_out  set of sockets that had write events
+ * @param exceptset_out set os sockets that had error events
+ * @return number of sockets that had events (read/write/exception) (>= 0)
+ */
+static int
+lwip_selscan(int maxfdp1, fd_set *readset_in, fd_set *writeset_in, fd_set *exceptset_in,
+             fd_set *readset_out, fd_set *writeset_out, fd_set *exceptset_out)
+{
+  int i, nready = 0;
+  fd_set lreadset, lwriteset, lexceptset;
+  struct lwip_sock *sock;
+  SYS_ARCH_DECL_PROTECT(lev);
+
+  FD_ZERO(&lreadset);
+  FD_ZERO(&lwriteset);
+  FD_ZERO(&lexceptset);
+
+  /* Go through each socket in each list to count number of sockets which
+     currently match */
+  for (i = LWIP_SOCKET_OFFSET; i < maxfdp1; i++) {
+    /* if this FD is not in the set, continue */
+    if (!(readset_in && FD_ISSET(i, readset_in)) &&
+        !(writeset_in && FD_ISSET(i, writeset_in)) &&
+        !(exceptset_in && FD_ISSET(i, exceptset_in))) {
+      continue;
+    }
+    /* First get the socket's status (protected)... */
+    SYS_ARCH_PROTECT(lev);
+    sock = tryget_socket(i);
+    if (sock != NULL) {
+      void* lastdata = sock->lastdata;
+      s16_t rcvevent = sock->rcvevent;
+      u16_t sendevent = sock->sendevent;
+      u16_t errevent = sock->errevent;
+      SYS_ARCH_UNPROTECT(lev);
+
+      /* ... then examine it: */
+      /* See if netconn of this socket is ready for read */
+      if (readset_in && FD_ISSET(i, readset_in) && ((lastdata != NULL) || (rcvevent > 0))) {
+        FD_SET(i, &lreadset);
+        LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_selscan: fd=%d ready for reading\n", i));
+        nready++;
+      }
+      /* See if netconn of this socket is ready for write */
+      if (writeset_in && FD_ISSET(i, writeset_in) && (sendevent != 0)) {
+        FD_SET(i, &lwriteset);
+        LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_selscan: fd=%d ready for writing\n", i));
+        nready++;
+      }
+      /* See if netconn of this socket had an error */
+      if (exceptset_in && FD_ISSET(i, exceptset_in) && (errevent != 0)) {
+        FD_SET(i, &lexceptset);
+        LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_selscan: fd=%d ready for exception\n", i));
+        nready++;
+      }
+    } else {
+      SYS_ARCH_UNPROTECT(lev);
+      /* continue on to next FD in list */
+    }
+  }
+  /* copy local sets to the ones provided as arguments */
+  *readset_out = lreadset;
+  *writeset_out = lwriteset;
+  *exceptset_out = lexceptset;
+
+  LWIP_ASSERT("nready >= 0", nready >= 0);
+  return nready;
+}
+
+int
+lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset,
+            struct timeval *timeout)
+{
+  u32_t waitres = 0;
+  int nready;
+  fd_set lreadset, lwriteset, lexceptset;
+  u32_t msectimeout;
+  struct lwip_select_cb select_cb;
+  int i;
+  int maxfdp2;
+#if LWIP_NETCONN_SEM_PER_THREAD
+  int waited = 0;
+#endif
+  SYS_ARCH_DECL_PROTECT(lev);
+
+  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select(%d, %p, %p, %p, tvsec=%"S32_F" tvusec=%"S32_F")\n",
+                  maxfdp1, (void *)readset, (void *) writeset, (void *) exceptset,
+                  timeout ? (s32_t)timeout->tv_sec : (s32_t)-1,
+                  timeout ? (s32_t)timeout->tv_usec : (s32_t)-1));
+
+  /* Go through each socket in each list to count number of sockets which
+     currently match */
+  nready = lwip_selscan(maxfdp1, readset, writeset, exceptset, &lreadset, &lwriteset, &lexceptset);
+
+  /* If we don't have any current events, then suspend if we are supposed to */
+  if (!nready) {
+    if (timeout && timeout->tv_sec == 0 && timeout->tv_usec == 0) {
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: no timeout, returning 0\n"));
+      /* This is OK as the local fdsets are empty and nready is zero,
+         or we would have returned earlier. */
+      goto return_copy_fdsets;
+    }
+
+    /* None ready: add our semaphore to list:
+       We don't actually need any dynamic memory. Our entry on the
+       list is only valid while we are in this function, so it's ok
+       to use local variables. */
+
+    select_cb.next = NULL;
+    select_cb.prev = NULL;
+    select_cb.readset = readset;
+    select_cb.writeset = writeset;
+    select_cb.exceptset = exceptset;
+    select_cb.sem_signalled = 0;
+#if LWIP_NETCONN_SEM_PER_THREAD
+    select_cb.sem = LWIP_NETCONN_THREAD_SEM_GET();
+#else /* LWIP_NETCONN_SEM_PER_THREAD */
+    if (sys_sem_new(&select_cb.sem, 0) != ERR_OK) {
+      /* failed to create semaphore */
+      set_errno(ENOMEM);
+      return -1;
+    }
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
+
+    /* Protect the select_cb_list */
+    SYS_ARCH_PROTECT(lev);
+
+    /* Put this select_cb on top of list */
+    select_cb.next = select_cb_list;
+    if (select_cb_list != NULL) {
+      select_cb_list->prev = &select_cb;
+    }
+    select_cb_list = &select_cb;
+    /* Increasing this counter tells event_callback that the list has changed. */
+    select_cb_ctr++;
+
+    /* Now we can safely unprotect */
+    SYS_ARCH_UNPROTECT(lev);
+
+    /* Increase select_waiting for each socket we are interested in */
+    maxfdp2 = maxfdp1;
+    for (i = LWIP_SOCKET_OFFSET; i < maxfdp1; i++) {
+      if ((readset && FD_ISSET(i, readset)) ||
+          (writeset && FD_ISSET(i, writeset)) ||
+          (exceptset && FD_ISSET(i, exceptset))) {
+        struct lwip_sock *sock;
+        SYS_ARCH_PROTECT(lev);
+        sock = tryget_socket(i);
+        if (sock != NULL) {
+          sock->select_waiting++;
+          LWIP_ASSERT("sock->select_waiting > 0", sock->select_waiting > 0);
+        } else {
+          /* Not a valid socket */
+          nready = -1;
+          maxfdp2 = i;
+          SYS_ARCH_UNPROTECT(lev);
+          break;
+        }
+        SYS_ARCH_UNPROTECT(lev);
+      }
+    }
+
+    if (nready >= 0) {
+      /* Call lwip_selscan again: there could have been events between
+         the last scan (without us on the list) and putting us on the list! */
+      nready = lwip_selscan(maxfdp1, readset, writeset, exceptset, &lreadset, &lwriteset, &lexceptset);
+      if (!nready) {
+        /* Still none ready, just wait to be woken */
+        if (timeout == 0) {
+          /* Wait forever */
+          msectimeout = 0;
+        } else {
+          msectimeout =  ((timeout->tv_sec * 1000) + ((timeout->tv_usec + 500)/1000));
+          if (msectimeout == 0) {
+            /* Wait 1ms at least (0 means wait forever) */
+            msectimeout = 1;
+          }
+        }
+
+        waitres = sys_arch_sem_wait(SELECT_SEM_PTR(select_cb.sem), msectimeout);
+#if LWIP_NETCONN_SEM_PER_THREAD
+        waited = 1;
+#endif
+      }
+    }
+
+    /* Decrease select_waiting for each socket we are interested in */
+    for (i = LWIP_SOCKET_OFFSET; i < maxfdp2; i++) {
+      if ((readset && FD_ISSET(i, readset)) ||
+          (writeset && FD_ISSET(i, writeset)) ||
+          (exceptset && FD_ISSET(i, exceptset))) {
+        struct lwip_sock *sock;
+        SYS_ARCH_PROTECT(lev);
+        sock = tryget_socket(i);
+        if (sock != NULL) {
+          /* for now, handle select_waiting==0... */
+          LWIP_ASSERT("sock->select_waiting > 0", sock->select_waiting > 0);
+          if (sock->select_waiting > 0) {
+            sock->select_waiting--;
+          }
+        } else {
+          /* Not a valid socket */
+          nready = -1;
+        }
+        SYS_ARCH_UNPROTECT(lev);
+      }
+    }
+    /* Take us off the list */
+    SYS_ARCH_PROTECT(lev);
+    if (select_cb.next != NULL) {
+      select_cb.next->prev = select_cb.prev;
+    }
+    if (select_cb_list == &select_cb) {
+      LWIP_ASSERT("select_cb.prev == NULL", select_cb.prev == NULL);
+      select_cb_list = select_cb.next;
+    } else {
+      LWIP_ASSERT("select_cb.prev != NULL", select_cb.prev != NULL);
+      select_cb.prev->next = select_cb.next;
+    }
+    /* Increasing this counter tells event_callback that the list has changed. */
+    select_cb_ctr++;
+    SYS_ARCH_UNPROTECT(lev);
+
+#if LWIP_NETCONN_SEM_PER_THREAD
+    if (select_cb.sem_signalled && (!waited || (waitres == SYS_ARCH_TIMEOUT))) {
+      /* don't leave the thread-local semaphore signalled */
+      sys_arch_sem_wait(select_cb.sem, 1);
+    }
+#else /* LWIP_NETCONN_SEM_PER_THREAD */
+    sys_sem_free(&select_cb.sem);
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
+
+    if (nready < 0) {
+      /* This happens when a socket got closed while waiting */
+      set_errno(EBADF);
+      return -1;
+    }
+
+    if (waitres == SYS_ARCH_TIMEOUT) {
+      /* Timeout */
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: timeout expired\n"));
+      /* This is OK as the local fdsets are empty and nready is zero,
+         or we would have returned earlier. */
+      goto return_copy_fdsets;
+    }
+
+    /* See what's set */
+    nready = lwip_selscan(maxfdp1, readset, writeset, exceptset, &lreadset, &lwriteset, &lexceptset);
+  }
+
+  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: nready=%d\n", nready));
+return_copy_fdsets:
+  set_errno(0);
+  if (readset) {
+    *readset = lreadset;
+  }
+  if (writeset) {
+    *writeset = lwriteset;
+  }
+  if (exceptset) {
+    *exceptset = lexceptset;
+  }
+  return nready;
+}
+
+/**
+ * Callback registered in the netconn layer for each socket-netconn.
+ * Processes recvevent (data available) and wakes up tasks waiting for select.
+ */
+static void
+event_callback(struct netconn *conn, enum netconn_evt evt, u16_t len)
+{
+  int s;
+  struct lwip_sock *sock;
+  struct lwip_select_cb *scb;
+  int last_select_cb_ctr;
+  SYS_ARCH_DECL_PROTECT(lev);
+
+  LWIP_UNUSED_ARG(len);
+
+  /* Get socket */
+  if (conn) {
+    s = conn->socket;
+    if (s < 0) {
+      /* Data comes in right away after an accept, even though
+       * the server task might not have created a new socket yet.
+       * Just count down (or up) if that's the case and we
+       * will use the data later. Note that only receive events
+       * can happen before the new socket is set up. */
+      SYS_ARCH_PROTECT(lev);
+      if (conn->socket < 0) {
+        if (evt == NETCONN_EVT_RCVPLUS) {
+          conn->socket--;
+        }
+        SYS_ARCH_UNPROTECT(lev);
+        return;
+      }
+      s = conn->socket;
+      SYS_ARCH_UNPROTECT(lev);
+    }
+
+    sock = get_socket(s);
+    if (!sock) {
+      return;
+    }
+  } else {
+    return;
+  }
+
+  SYS_ARCH_PROTECT(lev);
+  /* Set event as required */
+  switch (evt) {
+    case NETCONN_EVT_RCVPLUS:
+      sock->rcvevent++;
+      break;
+    case NETCONN_EVT_RCVMINUS:
+      sock->rcvevent--;
+      break;
+    case NETCONN_EVT_SENDPLUS:
+      sock->sendevent = 1;
+      break;
+    case NETCONN_EVT_SENDMINUS:
+      sock->sendevent = 0;
+      break;
+    case NETCONN_EVT_ERROR:
+      sock->errevent = 1;
+      break;
+    default:
+      LWIP_ASSERT("unknown event", 0);
+      break;
+  }
+
+  if (sock->select_waiting == 0) {
+    /* noone is waiting for this socket, no need to check select_cb_list */
+    SYS_ARCH_UNPROTECT(lev);
+    return;
+  }
+
+  /* Now decide if anyone is waiting for this socket */
+  /* NOTE: This code goes through the select_cb_list list multiple times
+     ONLY IF a select was actually waiting. We go through the list the number
+     of waiting select calls + 1. This list is expected to be small. */
+
+  /* At this point, SYS_ARCH is still protected! */
+again:
+  for (scb = select_cb_list; scb != NULL; scb = scb->next) {
+    /* remember the state of select_cb_list to detect changes */
+    last_select_cb_ctr = select_cb_ctr;
+    if (scb->sem_signalled == 0) {
+      /* semaphore not signalled yet */
+      int do_signal = 0;
+      /* Test this select call for our socket */
+      if (sock->rcvevent > 0) {
+        if (scb->readset && FD_ISSET(s, scb->readset)) {
+          do_signal = 1;
+        }
+      }
+      if (sock->sendevent != 0) {
+        if (!do_signal && scb->writeset && FD_ISSET(s, scb->writeset)) {
+          do_signal = 1;
+        }
+      }
+      if (sock->errevent != 0) {
+        if (!do_signal && scb->exceptset && FD_ISSET(s, scb->exceptset)) {
+          do_signal = 1;
+        }
+      }
+      if (do_signal) {
+        scb->sem_signalled = 1;
+        /* Don't call SYS_ARCH_UNPROTECT() before signaling the semaphore, as this might
+           lead to the select thread taking itself off the list, invalidating the semaphore. */
+        sys_sem_signal(SELECT_SEM_PTR(scb->sem));
+      }
+    }
+    /* unlock interrupts with each step */
+    SYS_ARCH_UNPROTECT(lev);
+    /* this makes sure interrupt protection time is short */
+    SYS_ARCH_PROTECT(lev);
+    if (last_select_cb_ctr != select_cb_ctr) {
+      /* someone has changed select_cb_list, restart at the beginning */
+      goto again;
+    }
+  }
+  SYS_ARCH_UNPROTECT(lev);
+}
+
+/**
+ * Close one end of a full-duplex connection.
+ */
+int
+lwip_shutdown(int s, int how)
+{
+  struct lwip_sock *sock;
+  err_t err;
+  u8_t shut_rx = 0, shut_tx = 0;
+
+  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_shutdown(%d, how=%d)\n", s, how));
+
+  sock = get_socket(s);
+  if (!sock) {
+    return -1;
+  }
+
+  if (sock->conn != NULL) {
+    if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
+      sock_set_errno(sock, EOPNOTSUPP);
+      return -1;
+    }
+  } else {
+    sock_set_errno(sock, ENOTCONN);
+    return -1;
+  }
+
+  if (how == SHUT_RD) {
+    shut_rx = 1;
+  } else if (how == SHUT_WR) {
+    shut_tx = 1;
+  } else if (how == SHUT_RDWR) {
+    shut_rx = 1;
+    shut_tx = 1;
+  } else {
+    sock_set_errno(sock, EINVAL);
+    return -1;
+  }
+  err = netconn_shutdown(sock->conn, shut_rx, shut_tx);
+
+  sock_set_errno(sock, err_to_errno(err));
+  return (err == ERR_OK ? 0 : -1);
+}
+
+static int
+lwip_getaddrname(int s, struct sockaddr *name, socklen_t *namelen, u8_t local)
+{
+  struct lwip_sock *sock;
+  union sockaddr_aligned saddr;
+  ip_addr_t naddr;
+  u16_t port;
+  err_t err;
+
+  sock = get_socket(s);
+  if (!sock) {
+    return -1;
+  }
+
+  /* get the IP address and port */
+  err = netconn_getaddr(sock->conn, &naddr, &port, local);
+  if (err != ERR_OK) {
+    sock_set_errno(sock, err_to_errno(err));
+    return -1;
+  }
+
+#if LWIP_IPV4 && LWIP_IPV6
+  /* Dual-stack: Map IPv4 addresses to IPv4 mapped IPv6 */
+  if (NETCONNTYPE_ISIPV6(netconn_type(sock->conn)) &&
+      IP_IS_V4_VAL(naddr)) {
+    ip4_2_ipv4_mapped_ipv6(ip_2_ip6(&naddr), ip_2_ip4(&naddr));
+    IP_SET_TYPE_VAL(naddr, IPADDR_TYPE_V6);
+  }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
+  IPADDR_PORT_TO_SOCKADDR(&saddr, &naddr, port);
+
+  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getaddrname(%d, addr=", s));
+  ip_addr_debug_print_val(SOCKETS_DEBUG, naddr);
+  LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", port));
+
+  if (*namelen > saddr.sa.sa_len) {
+    *namelen = saddr.sa.sa_len;
+  }
+  MEMCPY(name, &saddr, *namelen);
+
+  sock_set_errno(sock, 0);
+  return 0;
+}
+
+int
+lwip_getpeername(int s, struct sockaddr *name, socklen_t *namelen)
+{
+  return lwip_getaddrname(s, name, namelen, 0);
+}
+
+int
+lwip_getsockname(int s, struct sockaddr *name, socklen_t *namelen)
+{
+  return lwip_getaddrname(s, name, namelen, 1);
+}
+
+int
+lwip_getsockopt(int s, int level, int optname, void *optval, socklen_t *optlen)
+{
+  u8_t err;
+  struct lwip_sock *sock = get_socket(s);
+#if !LWIP_TCPIP_CORE_LOCKING
+  LWIP_SETGETSOCKOPT_DATA_VAR_DECLARE(data);
+#endif /* !LWIP_TCPIP_CORE_LOCKING */
+
+  if (!sock) {
+    return -1;
+  }
+
+  if ((NULL == optval) || (NULL == optlen)) {
+    sock_set_errno(sock, EFAULT);
+    return -1;
+  }
+
+#if LWIP_TCPIP_CORE_LOCKING
+  /* core-locking can just call the -impl function */
+  LOCK_TCPIP_CORE();
+  err = lwip_getsockopt_impl(s, level, optname, optval, optlen);
+  UNLOCK_TCPIP_CORE();
+
+#else /* LWIP_TCPIP_CORE_LOCKING */
+
+#if LWIP_MPU_COMPATIBLE
+  /* MPU_COMPATIBLE copies the optval data, so check for max size here */
+  if (*optlen > LWIP_SETGETSOCKOPT_MAXOPTLEN) {
+    sock_set_errno(sock, ENOBUFS);
+    return -1;
+  }
+#endif /* LWIP_MPU_COMPATIBLE */
+
+  LWIP_SETGETSOCKOPT_DATA_VAR_ALLOC(data, sock);
+  LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).s = s;
+  LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).level = level;
+  LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optname = optname;
+  LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optlen = *optlen;
+#if !LWIP_MPU_COMPATIBLE
+  LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optval.p = optval;
+#endif /* !LWIP_MPU_COMPATIBLE */
+  LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).err = 0;
+#if LWIP_NETCONN_SEM_PER_THREAD
+  LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem = LWIP_NETCONN_THREAD_SEM_GET();
+#else
+  LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem = &sock->conn->op_completed;
+#endif
+  err = tcpip_callback(lwip_getsockopt_callback, &LWIP_SETGETSOCKOPT_DATA_VAR_REF(data));
+  if (err != ERR_OK) {
+    LWIP_SETGETSOCKOPT_DATA_VAR_FREE(data);
+    sock_set_errno(sock, err_to_errno(err));
+    return -1;
+  }
+  sys_arch_sem_wait((sys_sem_t*)(LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem), 0);
+
+  /* write back optlen and optval */
+  *optlen = LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optlen;
+#if LWIP_MPU_COMPATIBLE
+  MEMCPY(optval, LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optval,
+    LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optlen);
+#endif /* LWIP_MPU_COMPATIBLE */
+
+  /* maybe lwip_getsockopt_internal has changed err */
+  err = LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).err;
+  LWIP_SETGETSOCKOPT_DATA_VAR_FREE(data);
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+
+  sock_set_errno(sock, err);
+  return err ? -1 : 0;
+}
+
+#if !LWIP_TCPIP_CORE_LOCKING
+/** lwip_getsockopt_callback: only used without CORE_LOCKING
+ * to get into the tcpip_thread
+ */
+static void
+lwip_getsockopt_callback(void *arg)
+{
+  struct lwip_setgetsockopt_data *data;
+  LWIP_ASSERT("arg != NULL", arg != NULL);
+  data = (struct lwip_setgetsockopt_data*)arg;
+
+  data->err = lwip_getsockopt_impl(data->s, data->level, data->optname,
+#if LWIP_MPU_COMPATIBLE
+    data->optval,
+#else /* LWIP_MPU_COMPATIBLE */
+    data->optval.p,
+#endif /* LWIP_MPU_COMPATIBLE */
+    &data->optlen);
+
+  sys_sem_signal((sys_sem_t*)(data->completed_sem));
+}
+#endif  /* LWIP_TCPIP_CORE_LOCKING */
+
+/** lwip_getsockopt_impl: the actual implementation of getsockopt:
+ * same argument as lwip_getsockopt, either called directly or through callback
+ */
+static u8_t
+lwip_getsockopt_impl(int s, int level, int optname, void *optval, socklen_t *optlen)
+{
+  u8_t err = 0;
+  struct lwip_sock *sock = tryget_socket(s);
+  if (!sock) {
+    return EBADF;
+  }
+
+  switch (level) {
+
+/* Level: SOL_SOCKET */
+  case SOL_SOCKET:
+    switch (optname) {
+
+#if LWIP_TCP
+    case SO_ACCEPTCONN:
+      LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, int);
+      if (NETCONNTYPE_GROUP(sock->conn->type) != NETCONN_TCP) {
+        return ENOPROTOOPT;
+      }
+      if ((sock->conn->pcb.tcp != NULL) && (sock->conn->pcb.tcp->state == LISTEN)) {
+        *(int*)optval = 1;
+      } else {
+        *(int*)optval = 0;
+      }
+      break;
+#endif /* LWIP_TCP */
+
+    /* The option flags */
+    case SO_BROADCAST:
+    case SO_KEEPALIVE:
+#if SO_REUSE
+    case SO_REUSEADDR:
+#endif /* SO_REUSE */
+      LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, int);
+      *(int*)optval = ip_get_option(sock->conn->pcb.ip, optname);
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, optname=0x%x, ..) = %s\n",
+                                  s, optname, (*(int*)optval?"on":"off")));
+      break;
+
+    case SO_TYPE:
+      LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, int);
+      switch (NETCONNTYPE_GROUP(netconn_type(sock->conn))) {
+      case NETCONN_RAW:
+        *(int*)optval = SOCK_RAW;
+        break;
+      case NETCONN_TCP:
+        *(int*)optval = SOCK_STREAM;
+        break;
+      case NETCONN_UDP:
+        *(int*)optval = SOCK_DGRAM;
+        break;
+      default: /* unrecognized socket type */
+        *(int*)optval = netconn_type(sock->conn);
+        LWIP_DEBUGF(SOCKETS_DEBUG,
+                    ("lwip_getsockopt(%d, SOL_SOCKET, SO_TYPE): unrecognized socket type %d\n",
+                    s, *(int *)optval));
+      }  /* switch (netconn_type(sock->conn)) */
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, SO_TYPE) = %d\n",
+                  s, *(int *)optval));
+      break;
+
+    case SO_ERROR:
+      LWIP_SOCKOPT_CHECK_OPTLEN(*optlen, int);
+      /* only overwrite ERR_OK or temporary errors */
+      if (((sock->err == 0) || (sock->err == EINPROGRESS)) && (sock->conn != NULL)) {
+        sock_set_errno(sock, err_to_errno(sock->conn->last_err));
+      }
+      *(int *)optval = (sock->err == 0xFF ? (int)-1 : (int)sock->err);
+      sock->err = 0;
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, SO_ERROR) = %d\n",
+                  s, *(int *)optval));
+      break;
+
+#if LWIP_SO_SNDTIMEO
+    case SO_SNDTIMEO:
+      LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, LWIP_SO_SNDRCVTIMEO_OPTTYPE);
+      LWIP_SO_SNDRCVTIMEO_SET(optval, netconn_get_sendtimeout(sock->conn));
+      break;
+#endif /* LWIP_SO_SNDTIMEO */
+#if LWIP_SO_RCVTIMEO
+    case SO_RCVTIMEO:
+      LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, LWIP_SO_SNDRCVTIMEO_OPTTYPE);
+      LWIP_SO_SNDRCVTIMEO_SET(optval, netconn_get_recvtimeout(sock->conn));
+      break;
+#endif /* LWIP_SO_RCVTIMEO */
+#if LWIP_SO_RCVBUF
+    case SO_RCVBUF:
+      LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, int);
+      *(int *)optval = netconn_get_recvbufsize(sock->conn);
+      break;
+#endif /* LWIP_SO_RCVBUF */
+#if LWIP_SO_LINGER
+    case SO_LINGER:
+      {
+        s16_t conn_linger;
+        struct linger* linger = (struct linger*)optval;
+        LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, struct linger);
+        conn_linger = sock->conn->linger;
+        if (conn_linger >= 0) {
+          linger->l_onoff = 1;
+          linger->l_linger = (int)conn_linger;
+        } else {
+          linger->l_onoff = 0;
+          linger->l_linger = 0;
+        }
+      }
+      break;
+#endif /* LWIP_SO_LINGER */
+#if LWIP_UDP
+    case SO_NO_CHECK:
+      LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, *optlen, int, NETCONN_UDP);
+#if LWIP_UDPLITE
+      if ((udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_UDPLITE) != 0) {
+        /* this flag is only available for UDP, not for UDP lite */
+        return EAFNOSUPPORT;
+      }
+#endif /* LWIP_UDPLITE */
+      *(int*)optval = (udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_NOCHKSUM) ? 1 : 0;
+      break;
+#endif /* LWIP_UDP*/
+    default:
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, UNIMPL: optname=0x%x, ..)\n",
+                  s, optname));
+      err = ENOPROTOOPT;
+      break;
+    }  /* switch (optname) */
+    break;
+
+/* Level: IPPROTO_IP */
+  case IPPROTO_IP:
+    switch (optname) {
+    case IP_TTL:
+      LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, int);
+      *(int*)optval = sock->conn->pcb.ip->ttl;
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_TTL) = %d\n",
+                  s, *(int *)optval));
+      break;
+    case IP_TOS:
+      LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, int);
+      *(int*)optval = sock->conn->pcb.ip->tos;
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_TOS) = %d\n",
+                  s, *(int *)optval));
+      break;
+#if LWIP_MULTICAST_TX_OPTIONS
+    case IP_MULTICAST_TTL:
+      LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, u8_t);
+      if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) {
+        return ENOPROTOOPT;
+      }
+      *(u8_t*)optval = udp_get_multicast_ttl(sock->conn->pcb.udp);
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_TTL) = %d\n",
+                  s, *(int *)optval));
+      break;
+    case IP_MULTICAST_IF:
+      LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, struct in_addr);
+      if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) {
+        return ENOPROTOOPT;
+      }
+      inet_addr_from_ip4addr((struct in_addr*)optval, udp_get_multicast_netif_addr(sock->conn->pcb.udp));
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_IF) = 0x%"X32_F"\n",
+                  s, *(u32_t *)optval));
+      break;
+    case IP_MULTICAST_LOOP:
+      LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, u8_t);
+      if ((sock->conn->pcb.udp->flags & UDP_FLAGS_MULTICAST_LOOP) != 0) {
+        *(u8_t*)optval = 1;
+      } else {
+        *(u8_t*)optval = 0;
+      }
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_LOOP) = %d\n",
+                  s, *(int *)optval));
+      break;
+#endif /* LWIP_MULTICAST_TX_OPTIONS */
+    default:
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, UNIMPL: optname=0x%x, ..)\n",
+                  s, optname));
+      err = ENOPROTOOPT;
+      break;
+    }  /* switch (optname) */
+    break;
+
+#if LWIP_TCP
+/* Level: IPPROTO_TCP */
+  case IPPROTO_TCP:
+    /* Special case: all IPPROTO_TCP option take an int */
+    LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, *optlen, int, NETCONN_TCP);
+    if (sock->conn->pcb.tcp->state == LISTEN) {
+      return EINVAL;
+    }
+    switch (optname) {
+    case TCP_NODELAY:
+      *(int*)optval = tcp_nagle_disabled(sock->conn->pcb.tcp);
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, TCP_NODELAY) = %s\n",
+                  s, (*(int*)optval)?"on":"off") );
+      break;
+    case TCP_KEEPALIVE:
+      *(int*)optval = (int)sock->conn->pcb.tcp->keep_idle;
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, TCP_KEEPALIVE) = %d\n",
+                  s, *(int *)optval));
+      break;
+
+#if LWIP_TCP_KEEPALIVE
+    case TCP_KEEPIDLE:
+      *(int*)optval = (int)(sock->conn->pcb.tcp->keep_idle/1000);
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, TCP_KEEPIDLE) = %d\n",
+                  s, *(int *)optval));
+      break;
+    case TCP_KEEPINTVL:
+      *(int*)optval = (int)(sock->conn->pcb.tcp->keep_intvl/1000);
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, TCP_KEEPINTVL) = %d\n",
+                  s, *(int *)optval));
+      break;
+    case TCP_KEEPCNT:
+      *(int*)optval = (int)sock->conn->pcb.tcp->keep_cnt;
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, TCP_KEEPCNT) = %d\n",
+                  s, *(int *)optval));
+      break;
+#endif /* LWIP_TCP_KEEPALIVE */
+    default:
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, UNIMPL: optname=0x%x, ..)\n",
+                  s, optname));
+      err = ENOPROTOOPT;
+      break;
+    }  /* switch (optname) */
+    break;
+#endif /* LWIP_TCP */
+
+#if LWIP_IPV6
+/* Level: IPPROTO_IPV6 */
+  case IPPROTO_IPV6:
+    switch (optname) {
+    case IPV6_V6ONLY:
+      LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, int);
+      *(int*)optval = (netconn_get_ipv6only(sock->conn) ? 1 : 0);
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, IPV6_V6ONLY) = %d\n",
+                  s, *(int *)optval));
+      break;
+    default:
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, UNIMPL: optname=0x%x, ..)\n",
+                  s, optname));
+      err = ENOPROTOOPT;
+      break;
+    }  /* switch (optname) */
+    break;
+#endif /* LWIP_IPV6 */
+
+#if LWIP_UDP && LWIP_UDPLITE
+  /* Level: IPPROTO_UDPLITE */
+  case IPPROTO_UDPLITE:
+    /* Special case: all IPPROTO_UDPLITE option take an int */
+    LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, int);
+    /* If this is no UDP lite socket, ignore any options. */
+    if (!NETCONNTYPE_ISUDPLITE(netconn_type(sock->conn))) {
+      return ENOPROTOOPT;
+    }
+    switch (optname) {
+    case UDPLITE_SEND_CSCOV:
+      *(int*)optval = sock->conn->pcb.udp->chksum_len_tx;
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_UDPLITE, UDPLITE_SEND_CSCOV) = %d\n",
+                  s, (*(int*)optval)) );
+      break;
+    case UDPLITE_RECV_CSCOV:
+      *(int*)optval = sock->conn->pcb.udp->chksum_len_rx;
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_UDPLITE, UDPLITE_RECV_CSCOV) = %d\n",
+                  s, (*(int*)optval)) );
+      break;
+    default:
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_UDPLITE, UNIMPL: optname=0x%x, ..)\n",
+                  s, optname));
+      err = ENOPROTOOPT;
+      break;
+    }  /* switch (optname) */
+    break;
+#endif /* LWIP_UDP */
+  /* Level: IPPROTO_RAW */
+  case IPPROTO_RAW:
+    switch (optname) {
+#if LWIP_IPV6 && LWIP_RAW
+    case IPV6_CHECKSUM:
+      LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, *optlen, int, NETCONN_RAW);
+      if (sock->conn->pcb.raw->chksum_reqd == 0) {
+        *(int *)optval = -1;
+      } else {
+        *(int *)optval = sock->conn->pcb.raw->chksum_offset;
+      }
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_RAW, IPV6_CHECKSUM) = %d\n",
+                  s, (*(int*)optval)) );
+      break;
+#endif /* LWIP_IPV6 && LWIP_RAW */
+    default:
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_RAW, UNIMPL: optname=0x%x, ..)\n",
+                  s, optname));
+      err = ENOPROTOOPT;
+      break;
+    }  /* switch (optname) */
+    break;
+  default:
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, level=0x%x, UNIMPL: optname=0x%x, ..)\n",
+                                s, level, optname));
+    err = ENOPROTOOPT;
+    break;
+  } /* switch (level) */
+
+  return err;
+}
+
+int
+lwip_setsockopt(int s, int level, int optname, const void *optval, socklen_t optlen)
+{
+  u8_t err = 0;
+  struct lwip_sock *sock = get_socket(s);
+#if !LWIP_TCPIP_CORE_LOCKING
+  LWIP_SETGETSOCKOPT_DATA_VAR_DECLARE(data);
+#endif /* !LWIP_TCPIP_CORE_LOCKING */
+
+  if (!sock) {
+    return -1;
+  }
+
+  if (NULL == optval) {
+    sock_set_errno(sock, EFAULT);
+    return -1;
+  }
+
+#if LWIP_TCPIP_CORE_LOCKING
+  /* core-locking can just call the -impl function */
+  LOCK_TCPIP_CORE();
+  err = lwip_setsockopt_impl(s, level, optname, optval, optlen);
+  UNLOCK_TCPIP_CORE();
+
+#else /* LWIP_TCPIP_CORE_LOCKING */
+
+#if LWIP_MPU_COMPATIBLE
+  /* MPU_COMPATIBLE copies the optval data, so check for max size here */
+  if (optlen > LWIP_SETGETSOCKOPT_MAXOPTLEN) {
+    sock_set_errno(sock, ENOBUFS);
+    return -1;
+  }
+#endif /* LWIP_MPU_COMPATIBLE */
+
+  LWIP_SETGETSOCKOPT_DATA_VAR_ALLOC(data, sock);
+  LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).s = s;
+  LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).level = level;
+  LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optname = optname;
+  LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optlen = optlen;
+#if LWIP_MPU_COMPATIBLE
+  MEMCPY(LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optval, optval, optlen);
+#else /* LWIP_MPU_COMPATIBLE */
+  LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optval.pc = (const void*)optval;
+#endif /* LWIP_MPU_COMPATIBLE */
+  LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).err = 0;
+#if LWIP_NETCONN_SEM_PER_THREAD
+  LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem = LWIP_NETCONN_THREAD_SEM_GET();
+#else
+  LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem = &sock->conn->op_completed;
+#endif
+  err = tcpip_callback(lwip_setsockopt_callback, &LWIP_SETGETSOCKOPT_DATA_VAR_REF(data));
+  if (err != ERR_OK) {
+    LWIP_SETGETSOCKOPT_DATA_VAR_FREE(data);
+    sock_set_errno(sock, err_to_errno(err));
+    return -1;
+  }
+  sys_arch_sem_wait((sys_sem_t*)(LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem), 0);
+
+  /* maybe lwip_getsockopt_internal has changed err */
+  err = LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).err;
+  LWIP_SETGETSOCKOPT_DATA_VAR_FREE(data);
+#endif  /* LWIP_TCPIP_CORE_LOCKING */
+
+  sock_set_errno(sock, err);
+  return err ? -1 : 0;
+}
+
+#if !LWIP_TCPIP_CORE_LOCKING
+/** lwip_setsockopt_callback: only used without CORE_LOCKING
+ * to get into the tcpip_thread
+ */
+static void
+lwip_setsockopt_callback(void *arg)
+{
+  struct lwip_setgetsockopt_data *data;
+  LWIP_ASSERT("arg != NULL", arg != NULL);
+  data = (struct lwip_setgetsockopt_data*)arg;
+
+  data->err = lwip_setsockopt_impl(data->s, data->level, data->optname,
+#if LWIP_MPU_COMPATIBLE
+    data->optval,
+#else /* LWIP_MPU_COMPATIBLE */
+    data->optval.pc,
+#endif /* LWIP_MPU_COMPATIBLE */
+    data->optlen);
+
+  sys_sem_signal((sys_sem_t*)(data->completed_sem));
+}
+#endif  /* LWIP_TCPIP_CORE_LOCKING */
+
+/** lwip_setsockopt_impl: the actual implementation of setsockopt:
+ * same argument as lwip_setsockopt, either called directly or through callback
+ */
+static u8_t
+lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_t optlen)
+{
+  u8_t err = 0;
+  struct lwip_sock *sock = tryget_socket(s);
+  if (!sock) {
+    return EBADF;
+  }
+
+  switch (level) {
+
+/* Level: SOL_SOCKET */
+  case SOL_SOCKET:
+    switch (optname) {
+
+    /* SO_ACCEPTCONN is get-only */
+
+    /* The option flags */
+    case SO_BROADCAST:
+    case SO_KEEPALIVE:
+#if SO_REUSE
+    case SO_REUSEADDR:
+#endif /* SO_REUSE */
+      LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, int);
+      if (*(const int*)optval) {
+        ip_set_option(sock->conn->pcb.ip, optname);
+      } else {
+        ip_reset_option(sock->conn->pcb.ip, optname);
+      }
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, SOL_SOCKET, optname=0x%x, ..) -> %s\n",
+                  s, optname, (*(const int*)optval?"on":"off")));
+      break;
+
+    /* SO_TYPE is get-only */
+    /* SO_ERROR is get-only */
+
+#if LWIP_SO_SNDTIMEO
+    case SO_SNDTIMEO:
+      LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, LWIP_SO_SNDRCVTIMEO_OPTTYPE);
+      netconn_set_sendtimeout(sock->conn, LWIP_SO_SNDRCVTIMEO_GET_MS(optval));
+      break;
+#endif /* LWIP_SO_SNDTIMEO */
+#if LWIP_SO_RCVTIMEO
+    case SO_RCVTIMEO:
+      LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, LWIP_SO_SNDRCVTIMEO_OPTTYPE);
+      netconn_set_recvtimeout(sock->conn, (int)LWIP_SO_SNDRCVTIMEO_GET_MS(optval));
+      break;
+#endif /* LWIP_SO_RCVTIMEO */
+#if LWIP_SO_RCVBUF
+    case SO_RCVBUF:
+      LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, int);
+      netconn_set_recvbufsize(sock->conn, *(const int*)optval);
+      break;
+#endif /* LWIP_SO_RCVBUF */
+#if LWIP_SO_LINGER
+    case SO_LINGER:
+      {
+        const struct linger* linger = (const struct linger*)optval;
+        LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, struct linger);
+        if (linger->l_onoff) {
+          int lingersec = linger->l_linger;
+          if (lingersec < 0) {
+            return EINVAL;
+          }
+          if (lingersec > 0xFFFF) {
+            lingersec = 0xFFFF;
+          }
+          sock->conn->linger = (s16_t)lingersec;
+        } else {
+          sock->conn->linger = -1;
+        }
+      }
+      break;
+#endif /* LWIP_SO_LINGER */
+#if LWIP_UDP
+    case SO_NO_CHECK:
+      LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_UDP);
+#if LWIP_UDPLITE
+      if ((udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_UDPLITE) != 0) {
+        /* this flag is only available for UDP, not for UDP lite */
+        return EAFNOSUPPORT;
+      }
+#endif /* LWIP_UDPLITE */
+      if (*(const int*)optval) {
+        udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) | UDP_FLAGS_NOCHKSUM);
+      } else {
+        udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) & ~UDP_FLAGS_NOCHKSUM);
+      }
+      break;
+#endif /* LWIP_UDP */
+    default:
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, SOL_SOCKET, UNIMPL: optname=0x%x, ..)\n",
+                  s, optname));
+      err = ENOPROTOOPT;
+      break;
+    }  /* switch (optname) */
+    break;
+
+/* Level: IPPROTO_IP */
+  case IPPROTO_IP:
+    switch (optname) {
+    case IP_TTL:
+      LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, int);
+      sock->conn->pcb.ip->ttl = (u8_t)(*(const int*)optval);
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, IP_TTL, ..) -> %d\n",
+                  s, sock->conn->pcb.ip->ttl));
+      break;
+    case IP_TOS:
+      LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, int);
+      sock->conn->pcb.ip->tos = (u8_t)(*(const int*)optval);
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, IP_TOS, ..)-> %d\n",
+                  s, sock->conn->pcb.ip->tos));
+      break;
+#if LWIP_MULTICAST_TX_OPTIONS
+    case IP_MULTICAST_TTL:
+      LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, u8_t, NETCONN_UDP);
+      udp_set_multicast_ttl(sock->conn->pcb.udp, (u8_t)(*(const u8_t*)optval));
+      break;
+    case IP_MULTICAST_IF:
+      {
+        ip4_addr_t if_addr;
+        LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, struct in_addr, NETCONN_UDP);
+        inet_addr_to_ip4addr(&if_addr, (const struct in_addr*)optval);
+        udp_set_multicast_netif_addr(sock->conn->pcb.udp, &if_addr);
+      }
+      break;
+    case IP_MULTICAST_LOOP:
+      LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, u8_t, NETCONN_UDP);
+      if (*(const u8_t*)optval) {
+        udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) | UDP_FLAGS_MULTICAST_LOOP);
+      } else {
+        udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) & ~UDP_FLAGS_MULTICAST_LOOP);
+      }
+      break;
+#endif /* LWIP_MULTICAST_TX_OPTIONS */
+#if LWIP_IGMP
+    case IP_ADD_MEMBERSHIP:
+    case IP_DROP_MEMBERSHIP:
+      {
+        /* If this is a TCP or a RAW socket, ignore these options. */
+        /* @todo: assign membership to this socket so that it is dropped when closing the socket */
+        err_t igmp_err;
+        const struct ip_mreq *imr = (const struct ip_mreq *)optval;
+        ip4_addr_t if_addr;
+        ip4_addr_t multi_addr;
+        LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, struct ip_mreq, NETCONN_UDP);
+        inet_addr_to_ip4addr(&if_addr, &imr->imr_interface);
+        inet_addr_to_ip4addr(&multi_addr, &imr->imr_multiaddr);
+        if (optname == IP_ADD_MEMBERSHIP) {
+          if (!lwip_socket_register_membership(s, &if_addr, &multi_addr)) {
+            /* cannot track membership (out of memory) */
+            err = ENOMEM;
+            igmp_err = ERR_OK;
+          } else {
+            igmp_err = igmp_joingroup(&if_addr, &multi_addr);
+          }
+        } else {
+          igmp_err = igmp_leavegroup(&if_addr, &multi_addr);
+          lwip_socket_unregister_membership(s, &if_addr, &multi_addr);
+        }
+        if (igmp_err != ERR_OK) {
+          err = EADDRNOTAVAIL;
+        }
+      }
+      break;
+#endif /* LWIP_IGMP */
+    default:
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, UNIMPL: optname=0x%x, ..)\n",
+                  s, optname));
+      err = ENOPROTOOPT;
+      break;
+    }  /* switch (optname) */
+    break;
+
+#if LWIP_TCP
+/* Level: IPPROTO_TCP */
+  case IPPROTO_TCP:
+    /* Special case: all IPPROTO_TCP option take an int */
+    LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_TCP);
+    if (sock->conn->pcb.tcp->state == LISTEN) {
+      return EINVAL;
+    }
+    switch (optname) {
+    case TCP_NODELAY:
+      if (*(const int*)optval) {
+        tcp_nagle_disable(sock->conn->pcb.tcp);
+      } else {
+        tcp_nagle_enable(sock->conn->pcb.tcp);
+      }
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_NODELAY) -> %s\n",
+                  s, (*(const int *)optval)?"on":"off") );
+      break;
+    case TCP_KEEPALIVE:
+      sock->conn->pcb.tcp->keep_idle = (u32_t)(*(const int*)optval);
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPALIVE) -> %"U32_F"\n",
+                  s, sock->conn->pcb.tcp->keep_idle));
+      break;
+
+#if LWIP_TCP_KEEPALIVE
+    case TCP_KEEPIDLE:
+      sock->conn->pcb.tcp->keep_idle = 1000*(u32_t)(*(const int*)optval);
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPIDLE) -> %"U32_F"\n",
+                  s, sock->conn->pcb.tcp->keep_idle));
+      break;
+    case TCP_KEEPINTVL:
+      sock->conn->pcb.tcp->keep_intvl = 1000*(u32_t)(*(const int*)optval);
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPINTVL) -> %"U32_F"\n",
+                  s, sock->conn->pcb.tcp->keep_intvl));
+      break;
+    case TCP_KEEPCNT:
+      sock->conn->pcb.tcp->keep_cnt = (u32_t)(*(const int*)optval);
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPCNT) -> %"U32_F"\n",
+                  s, sock->conn->pcb.tcp->keep_cnt));
+      break;
+#endif /* LWIP_TCP_KEEPALIVE */
+    default:
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, UNIMPL: optname=0x%x, ..)\n",
+                  s, optname));
+      err = ENOPROTOOPT;
+      break;
+    }  /* switch (optname) */
+    break;
+#endif /* LWIP_TCP*/
+
+#if LWIP_IPV6
+/* Level: IPPROTO_IPV6 */
+  case IPPROTO_IPV6:
+    switch (optname) {
+    case IPV6_V6ONLY:
+      LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_TCP);
+      if (*(const int*)optval) {
+        netconn_set_ipv6only(sock->conn, 1);
+      } else {
+        netconn_set_ipv6only(sock->conn, 0);
+      }
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IPV6, IPV6_V6ONLY, ..) -> %d\n",
+                  s, (netconn_get_ipv6only(sock->conn) ? 1 : 0)));
+      break;
+    default:
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IPV6, UNIMPL: optname=0x%x, ..)\n",
+                  s, optname));
+      err = ENOPROTOOPT;
+      break;
+    }  /* switch (optname) */
+    break;
+#endif /* LWIP_IPV6 */
+
+#if LWIP_UDP && LWIP_UDPLITE
+  /* Level: IPPROTO_UDPLITE */
+  case IPPROTO_UDPLITE:
+    /* Special case: all IPPROTO_UDPLITE option take an int */
+    LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, int);
+    /* If this is no UDP lite socket, ignore any options. */
+    if (!NETCONNTYPE_ISUDPLITE(netconn_type(sock->conn))) {
+      return ENOPROTOOPT;
+    }
+    switch (optname) {
+    case UDPLITE_SEND_CSCOV:
+      if ((*(const int*)optval != 0) && ((*(const int*)optval < 8) || (*(const int*)optval > 0xffff))) {
+        /* don't allow illegal values! */
+        sock->conn->pcb.udp->chksum_len_tx = 8;
+      } else {
+        sock->conn->pcb.udp->chksum_len_tx = (u16_t)*(const int*)optval;
+      }
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UDPLITE_SEND_CSCOV) -> %d\n",
+                  s, (*(const int*)optval)) );
+      break;
+    case UDPLITE_RECV_CSCOV:
+      if ((*(const int*)optval != 0) && ((*(const int*)optval < 8) || (*(const int*)optval > 0xffff))) {
+        /* don't allow illegal values! */
+        sock->conn->pcb.udp->chksum_len_rx = 8;
+      } else {
+        sock->conn->pcb.udp->chksum_len_rx = (u16_t)*(const int*)optval;
+      }
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UDPLITE_RECV_CSCOV) -> %d\n",
+                  s, (*(const int*)optval)) );
+      break;
+    default:
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UNIMPL: optname=0x%x, ..)\n",
+                  s, optname));
+      err = ENOPROTOOPT;
+      break;
+    }  /* switch (optname) */
+    break;
+#endif /* LWIP_UDP */
+  /* Level: IPPROTO_RAW */
+  case IPPROTO_RAW:
+    switch (optname) {
+#if LWIP_IPV6 && LWIP_RAW
+    case IPV6_CHECKSUM:
+      /* It should not be possible to disable the checksum generation with ICMPv6
+       * as per RFC 3542 chapter 3.1 */
+      if(sock->conn->pcb.raw->protocol == IPPROTO_ICMPV6) {
+        return EINVAL;
+      }
+
+      LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_RAW);
+      if (*(const int *)optval < 0) {
+        sock->conn->pcb.raw->chksum_reqd = 0;
+      } else if (*(const int *)optval & 1) {
+        /* Per RFC3542, odd offsets are not allowed */
+        return EINVAL;
+      } else {
+        sock->conn->pcb.raw->chksum_reqd = 1;
+        sock->conn->pcb.raw->chksum_offset = (u16_t)*(const int *)optval;
+      }
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_RAW, IPV6_CHECKSUM, ..) -> %d\n",
+                  s, sock->conn->pcb.raw->chksum_reqd));
+      break;
+#endif /* LWIP_IPV6 && LWIP_RAW */
+    default:
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_RAW, UNIMPL: optname=0x%x, ..)\n",
+                                  s, optname));
+      err = ENOPROTOOPT;
+      break;
+    }  /* switch (optname) */
+    break;
+  default:
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, level=0x%x, UNIMPL: optname=0x%x, ..)\n",
+                s, level, optname));
+    err = ENOPROTOOPT;
+    break;
+  }  /* switch (level) */
+
+  return err;
+}
+
+int
+lwip_ioctl(int s, long cmd, void *argp)
+{
+  struct lwip_sock *sock = get_socket(s);
+  u8_t val;
+#if LWIP_SO_RCVBUF
+  u16_t buflen = 0;
+  int recv_avail;
+#endif /* LWIP_SO_RCVBUF */
+
+  if (!sock) {
+    return -1;
+  }
+
+  switch (cmd) {
+#if LWIP_SO_RCVBUF || LWIP_FIONREAD_LINUXMODE
+  case FIONREAD:
+    if (!argp) {
+      sock_set_errno(sock, EINVAL);
+      return -1;
+    }
+#if LWIP_FIONREAD_LINUXMODE
+    if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
+      struct pbuf *p;
+      if (sock->lastdata) {
+        p = ((struct netbuf *)sock->lastdata)->p;
+        *((int*)argp) = p->tot_len - sock->lastoffset;
+      } else {
+        struct netbuf *rxbuf;
+        err_t err;
+        if (sock->rcvevent <= 0) {
+          *((int*)argp) = 0;
+        } else {
+          err = netconn_recv(sock->conn, &rxbuf);
+          if (err != ERR_OK) {
+            *((int*)argp) = 0;
+          } else {
+            sock->lastdata = rxbuf;
+            sock->lastoffset = 0;
+            *((int*)argp) = rxbuf->p->tot_len;
+          }
+        }
+      }
+      return 0;
+    }
+#endif /* LWIP_FIONREAD_LINUXMODE */
+
+#if LWIP_SO_RCVBUF
+    /* we come here if either LWIP_FIONREAD_LINUXMODE==0 or this is a TCP socket */
+    SYS_ARCH_GET(sock->conn->recv_avail, recv_avail);
+    if (recv_avail < 0) {
+      recv_avail = 0;
+    }
+    *((int*)argp) = recv_avail;
+
+    /* Check if there is data left from the last recv operation. /maq 041215 */
+    if (sock->lastdata) {
+      struct pbuf *p = (struct pbuf *)sock->lastdata;
+      if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
+        p = ((struct netbuf *)p)->p;
+      }
+      buflen = p->tot_len;
+      buflen -= sock->lastoffset;
+
+      *((int*)argp) += buflen;
+    }
+
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, FIONREAD, %p) = %"U16_F"\n", s, argp, *((u16_t*)argp)));
+    sock_set_errno(sock, 0);
+    return 0;
+#else /* LWIP_SO_RCVBUF */
+    break;
+#endif /* LWIP_SO_RCVBUF */
+#endif /* LWIP_SO_RCVBUF || LWIP_FIONREAD_LINUXMODE */
+
+  case (long)FIONBIO:
+    val = 0;
+    if (argp && *(u32_t*)argp) {
+      val = 1;
+    }
+    netconn_set_nonblocking(sock->conn, val);
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, FIONBIO, %d)\n", s, val));
+    sock_set_errno(sock, 0);
+    return 0;
+
+  default:
+    break;
+  } /* switch (cmd) */
+  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, UNIMPL: 0x%lx, %p)\n", s, cmd, argp));
+  sock_set_errno(sock, ENOSYS); /* not yet implemented */
+  return -1;
+}
+
+/** A minimal implementation of fcntl.
+ * Currently only the commands F_GETFL and F_SETFL are implemented.
+ * Only the flag O_NONBLOCK is implemented.
+ */
+int
+lwip_fcntl(int s, int cmd, int val)
+{
+  struct lwip_sock *sock = get_socket(s);
+  int ret = -1;
+
+  if (!sock) {
+    return -1;
+  }
+
+  switch (cmd) {
+  case F_GETFL:
+    ret = netconn_is_nonblocking(sock->conn) ? O_NONBLOCK : 0;
+    sock_set_errno(sock, 0);
+    break;
+  case F_SETFL:
+    if ((val & ~O_NONBLOCK) == 0) {
+      /* only O_NONBLOCK, all other bits are zero */
+      netconn_set_nonblocking(sock->conn, val & O_NONBLOCK);
+      ret = 0;
+      sock_set_errno(sock, 0);
+    } else {
+      sock_set_errno(sock, ENOSYS); /* not yet implemented */
+    }
+    break;
+  default:
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_fcntl(%d, UNIMPL: %d, %d)\n", s, cmd, val));
+    sock_set_errno(sock, ENOSYS); /* not yet implemented */
+    break;
+  }
+  return ret;
+}
+
+#if LWIP_IGMP
+/** Register a new IGMP membership. On socket close, the membership is dropped automatically.
+ *
+ * ATTENTION: this function is called from tcpip_thread (or under CORE_LOCK).
+ *
+ * @return 1 on success, 0 on failure
+ */
+static int
+lwip_socket_register_membership(int s, const ip4_addr_t *if_addr, const ip4_addr_t *multi_addr)
+{
+  struct lwip_sock *sock = get_socket(s);
+  int i;
+
+  if (!sock) {
+    return 0;
+  }
+
+  for (i = 0; i < LWIP_SOCKET_MAX_MEMBERSHIPS; i++) {
+    if (socket_ipv4_multicast_memberships[i].sock == NULL) {
+      socket_ipv4_multicast_memberships[i].sock = sock;
+      ip4_addr_copy(socket_ipv4_multicast_memberships[i].if_addr, *if_addr);
+      ip4_addr_copy(socket_ipv4_multicast_memberships[i].multi_addr, *multi_addr);
+      return 1;
+    }
+  }
+  return 0;
+}
+
+/** Unregister a previously registered membership. This prevents dropping the membership
+ * on socket close.
+ *
+ * ATTENTION: this function is called from tcpip_thread (or under CORE_LOCK).
+ */
+static void
+lwip_socket_unregister_membership(int s, const ip4_addr_t *if_addr, const ip4_addr_t *multi_addr)
+{
+  struct lwip_sock *sock = get_socket(s);
+  int i;
+
+  if (!sock) {
+    return;
+  }
+
+  for (i = 0; i < LWIP_SOCKET_MAX_MEMBERSHIPS; i++) {
+    if ((socket_ipv4_multicast_memberships[i].sock == sock) &&
+        ip4_addr_cmp(&socket_ipv4_multicast_memberships[i].if_addr, if_addr) &&
+        ip4_addr_cmp(&socket_ipv4_multicast_memberships[i].multi_addr, multi_addr)) {
+      socket_ipv4_multicast_memberships[i].sock = NULL;
+      ip4_addr_set_zero(&socket_ipv4_multicast_memberships[i].if_addr);
+      ip4_addr_set_zero(&socket_ipv4_multicast_memberships[i].multi_addr);
+      return;
+    }
+  }
+}
+
+/** Drop all memberships of a socket that were not dropped explicitly via setsockopt.
+ *
+ * ATTENTION: this function is NOT called from tcpip_thread (or under CORE_LOCK).
+ */
+static void
+lwip_socket_drop_registered_memberships(int s)
+{
+  struct lwip_sock *sock = get_socket(s);
+  int i;
+
+  if (!sock) {
+    return;
+  }
+
+  for (i = 0; i < LWIP_SOCKET_MAX_MEMBERSHIPS; i++) {
+    if (socket_ipv4_multicast_memberships[i].sock == sock) {
+      ip_addr_t multi_addr, if_addr;
+      ip_addr_copy_from_ip4(multi_addr, socket_ipv4_multicast_memberships[i].multi_addr);
+      ip_addr_copy_from_ip4(if_addr, socket_ipv4_multicast_memberships[i].if_addr);
+      socket_ipv4_multicast_memberships[i].sock = NULL;
+      ip4_addr_set_zero(&socket_ipv4_multicast_memberships[i].if_addr);
+      ip4_addr_set_zero(&socket_ipv4_multicast_memberships[i].multi_addr);
+
+      netconn_join_leave_group(sock->conn, &multi_addr, &if_addr, NETCONN_LEAVE);
+    }
+  }
+}
+#endif /* LWIP_IGMP */
+#endif /* LWIP_SOCKET */

+ 518 - 511
thirdparty/lwip/src/api/tcpip.c

@@ -1,511 +1,518 @@
-/**
- * @file
- * Sequential API Main thread module
- *
- */
-
-/*
- * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * This file is part of the lwIP TCP/IP stack.
- *
- * Author: Adam Dunkels <adam@sics.se>
- *
- */
-
-#include "lwip/opt.h"
-
-#if !NO_SYS /* don't build if not configured for use in lwipopts.h */
-
-#include "lwip/sys.h"
-#include "lwip/memp.h"
-#include "lwip/mem.h"
-#include "lwip/pbuf.h"
-#include "lwip/tcpip.h"
-#include "lwip/init.h"
-#include "netif/etharp.h"
-#include "netif/ppp_oe.h"
-
-/* global variables */
-static tcpip_init_done_fn tcpip_init_done;
-static void *tcpip_init_done_arg;
-static sys_mbox_t mbox;
-
-#if LWIP_TCPIP_CORE_LOCKING
-/** The global semaphore to lock the stack. */
-sys_mutex_t lock_tcpip_core;
-#endif /* LWIP_TCPIP_CORE_LOCKING */
-
-
-/**
- * The main lwIP thread. This thread has exclusive access to lwIP core functions
- * (unless access to them is not locked). Other threads communicate with this
- * thread using message boxes.
- *
- * It also starts all the timers to make sure they are running in the right
- * thread context.
- *
- * @param arg unused argument
- */
-static void
-tcpip_thread(void *arg)
-{
-  struct tcpip_msg *msg;
-  LWIP_UNUSED_ARG(arg);
-
-  if (tcpip_init_done != NULL) {
-    tcpip_init_done(tcpip_init_done_arg);
-  }
-
-  LOCK_TCPIP_CORE();
-  while (1) {                          /* MAIN Loop */
-    UNLOCK_TCPIP_CORE();
-    LWIP_TCPIP_THREAD_ALIVE();
-    /* wait for a message, timeouts are processed while waiting */
-    sys_timeouts_mbox_fetch(&mbox, (void **)&msg);
-    LOCK_TCPIP_CORE();
-    switch (msg->type) {
-#if LWIP_NETCONN
-    case TCPIP_MSG_API:
-      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: API message %p\n", (void *)msg));
-      msg->msg.apimsg->function(&(msg->msg.apimsg->msg));
-      break;
-#endif /* LWIP_NETCONN */
-
-#if !LWIP_TCPIP_CORE_LOCKING_INPUT
-    case TCPIP_MSG_INPKT:
-      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: PACKET %p\n", (void *)msg));
-#if LWIP_ETHERNET
-      if (msg->msg.inp.netif->flags & (NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET)) {
-        ethernet_input(msg->msg.inp.p, msg->msg.inp.netif);
-      } else
-#endif /* LWIP_ETHERNET */
-      {
-        ip_input(msg->msg.inp.p, msg->msg.inp.netif);
-      }
-      memp_free(MEMP_TCPIP_MSG_INPKT, msg);
-      break;
-#endif /* LWIP_TCPIP_CORE_LOCKING_INPUT */
-
-#if LWIP_NETIF_API
-    case TCPIP_MSG_NETIFAPI:
-      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: Netif API message %p\n", (void *)msg));
-      msg->msg.netifapimsg->function(&(msg->msg.netifapimsg->msg));
-      break;
-#endif /* LWIP_NETIF_API */
-
-#if LWIP_TCPIP_TIMEOUT
-    case TCPIP_MSG_TIMEOUT:
-      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: TIMEOUT %p\n", (void *)msg));
-      sys_timeout(msg->msg.tmo.msecs, msg->msg.tmo.h, msg->msg.tmo.arg);
-      memp_free(MEMP_TCPIP_MSG_API, msg);
-      break;
-    case TCPIP_MSG_UNTIMEOUT:
-      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: UNTIMEOUT %p\n", (void *)msg));
-      sys_untimeout(msg->msg.tmo.h, msg->msg.tmo.arg);
-      memp_free(MEMP_TCPIP_MSG_API, msg);
-      break;
-#endif /* LWIP_TCPIP_TIMEOUT */
-
-    case TCPIP_MSG_CALLBACK:
-      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: CALLBACK %p\n", (void *)msg));
-      msg->msg.cb.function(msg->msg.cb.ctx);
-      memp_free(MEMP_TCPIP_MSG_API, msg);
-      break;
-
-    case TCPIP_MSG_CALLBACK_STATIC:
-      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: CALLBACK_STATIC %p\n", (void *)msg));
-      msg->msg.cb.function(msg->msg.cb.ctx);
-      break;
-
-    default:
-      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: invalid message: %d\n", msg->type));
-      LWIP_ASSERT("tcpip_thread: invalid message", 0);
-      break;
-    }
-  }
-}
-
-/**
- * Pass a received packet to tcpip_thread for input processing
- *
- * @param p the received packet, p->payload pointing to the Ethernet header or
- *          to an IP header (if inp doesn't have NETIF_FLAG_ETHARP or
- *          NETIF_FLAG_ETHERNET flags)
- * @param inp the network interface on which the packet was received
- */
-err_t
-tcpip_input(struct pbuf *p, struct netif *inp)
-{
-#if LWIP_TCPIP_CORE_LOCKING_INPUT
-  err_t ret;
-  LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_input: PACKET %p/%p\n", (void *)p, (void *)inp));
-  LOCK_TCPIP_CORE();
-#if LWIP_ETHERNET
-  if (inp->flags & (NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET)) {
-    ret = ethernet_input(p, inp);
-  } else
-#endif /* LWIP_ETHERNET */
-  {
-    ret = ip_input(p, inp);
-  }
-  UNLOCK_TCPIP_CORE();
-  return ret;
-#else /* LWIP_TCPIP_CORE_LOCKING_INPUT */
-  struct tcpip_msg *msg;
-
-  if (!sys_mbox_valid(&mbox)) {
-    return ERR_VAL;
-  }
-  msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_INPKT);
-  if (msg == NULL) {
-    return ERR_MEM;
-  }
-
-  msg->type = TCPIP_MSG_INPKT;
-  msg->msg.inp.p = p;
-  msg->msg.inp.netif = inp;
-  if (sys_mbox_trypost(&mbox, msg) != ERR_OK) {
-    memp_free(MEMP_TCPIP_MSG_INPKT, msg);
-    return ERR_MEM;
-  }
-  return ERR_OK;
-#endif /* LWIP_TCPIP_CORE_LOCKING_INPUT */
-}
-
-/**
- * Call a specific function in the thread context of
- * tcpip_thread for easy access synchronization.
- * A function called in that way may access lwIP core code
- * without fearing concurrent access.
- *
- * @param f the function to call
- * @param ctx parameter passed to f
- * @param block 1 to block until the request is posted, 0 to non-blocking mode
- * @return ERR_OK if the function was called, another err_t if not
- */
-err_t
-tcpip_callback_with_block(tcpip_callback_fn function, void *ctx, u8_t block)
-{
-  struct tcpip_msg *msg;
-
-  if (sys_mbox_valid(&mbox)) {
-    msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API);
-    if (msg == NULL) {
-      return ERR_MEM;
-    }
-
-    msg->type = TCPIP_MSG_CALLBACK;
-    msg->msg.cb.function = function;
-    msg->msg.cb.ctx = ctx;
-    if (block) {
-      sys_mbox_post(&mbox, msg);
-    } else {
-      if (sys_mbox_trypost(&mbox, msg) != ERR_OK) {
-        memp_free(MEMP_TCPIP_MSG_API, msg);
-        return ERR_MEM;
-      }
-    }
-    return ERR_OK;
-  }
-  return ERR_VAL;
-}
-
-#if LWIP_TCPIP_TIMEOUT
-/**
- * call sys_timeout in tcpip_thread
- *
- * @param msec time in milliseconds for timeout
- * @param h function to be called on timeout
- * @param arg argument to pass to timeout function h
- * @return ERR_MEM on memory error, ERR_OK otherwise
- */
-err_t
-tcpip_timeout(u32_t msecs, sys_timeout_handler h, void *arg)
-{
-  struct tcpip_msg *msg;
-
-  if (sys_mbox_valid(&mbox)) {
-    msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API);
-    if (msg == NULL) {
-      return ERR_MEM;
-    }
-
-    msg->type = TCPIP_MSG_TIMEOUT;
-    msg->msg.tmo.msecs = msecs;
-    msg->msg.tmo.h = h;
-    msg->msg.tmo.arg = arg;
-    sys_mbox_post(&mbox, msg);
-    return ERR_OK;
-  }
-  return ERR_VAL;
-}
-
-/**
- * call sys_untimeout in tcpip_thread
- *
- * @param msec time in milliseconds for timeout
- * @param h function to be called on timeout
- * @param arg argument to pass to timeout function h
- * @return ERR_MEM on memory error, ERR_OK otherwise
- */
-err_t
-tcpip_untimeout(sys_timeout_handler h, void *arg)
-{
-  struct tcpip_msg *msg;
-
-  if (sys_mbox_valid(&mbox)) {
-    msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API);
-    if (msg == NULL) {
-      return ERR_MEM;
-    }
-
-    msg->type = TCPIP_MSG_UNTIMEOUT;
-    msg->msg.tmo.h = h;
-    msg->msg.tmo.arg = arg;
-    sys_mbox_post(&mbox, msg);
-    return ERR_OK;
-  }
-  return ERR_VAL;
-}
-#endif /* LWIP_TCPIP_TIMEOUT */
-
-#if LWIP_NETCONN
-/**
- * Call the lower part of a netconn_* function
- * This function is then running in the thread context
- * of tcpip_thread and has exclusive access to lwIP core code.
- *
- * @param apimsg a struct containing the function to call and its parameters
- * @return ERR_OK if the function was called, another err_t if not
- */
-err_t
-tcpip_apimsg(struct api_msg *apimsg)
-{
-  struct tcpip_msg msg;
-#ifdef LWIP_DEBUG
-  /* catch functions that don't set err */
-  apimsg->msg.err = ERR_VAL;
-#endif
-  
-  if (sys_mbox_valid(&mbox)) {
-    msg.type = TCPIP_MSG_API;
-    msg.msg.apimsg = apimsg;
-    sys_mbox_post(&mbox, &msg);
-    sys_arch_sem_wait(&apimsg->msg.conn->op_completed, 0);
-    return apimsg->msg.err;
-  }
-  return ERR_VAL;
-}
-
-#if LWIP_TCPIP_CORE_LOCKING
-/**
- * Call the lower part of a netconn_* function
- * This function has exclusive access to lwIP core code by locking it
- * before the function is called.
- *
- * @param apimsg a struct containing the function to call and its parameters
- * @return ERR_OK (only for compatibility fo tcpip_apimsg())
- */
-err_t
-tcpip_apimsg_lock(struct api_msg *apimsg)
-{
-#ifdef LWIP_DEBUG
-  /* catch functions that don't set err */
-  apimsg->msg.err = ERR_VAL;
-#endif
-
-  LOCK_TCPIP_CORE();
-  apimsg->function(&(apimsg->msg));
-  UNLOCK_TCPIP_CORE();
-  return apimsg->msg.err;
-
-}
-#endif /* LWIP_TCPIP_CORE_LOCKING */
-#endif /* LWIP_NETCONN */
-
-#if LWIP_NETIF_API
-#if !LWIP_TCPIP_CORE_LOCKING
-/**
- * Much like tcpip_apimsg, but calls the lower part of a netifapi_*
- * function.
- *
- * @param netifapimsg a struct containing the function to call and its parameters
- * @return error code given back by the function that was called
- */
-err_t
-tcpip_netifapi(struct netifapi_msg* netifapimsg)
-{
-  struct tcpip_msg msg;
-  
-  if (sys_mbox_valid(&mbox)) {
-    err_t err = sys_sem_new(&netifapimsg->msg.sem, 0);
-    if (err != ERR_OK) {
-      netifapimsg->msg.err = err;
-      return err;
-    }
-    
-    msg.type = TCPIP_MSG_NETIFAPI;
-    msg.msg.netifapimsg = netifapimsg;
-    sys_mbox_post(&mbox, &msg);
-    sys_sem_wait(&netifapimsg->msg.sem);
-    sys_sem_free(&netifapimsg->msg.sem);
-    return netifapimsg->msg.err;
-  }
-  return ERR_VAL;
-}
-#else /* !LWIP_TCPIP_CORE_LOCKING */
-/**
- * Call the lower part of a netifapi_* function
- * This function has exclusive access to lwIP core code by locking it
- * before the function is called.
- *
- * @param netifapimsg a struct containing the function to call and its parameters
- * @return ERR_OK (only for compatibility fo tcpip_netifapi())
- */
-err_t
-tcpip_netifapi_lock(struct netifapi_msg* netifapimsg)
-{
-  LOCK_TCPIP_CORE();  
-  netifapimsg->function(&(netifapimsg->msg));
-  UNLOCK_TCPIP_CORE();
-  return netifapimsg->msg.err;
-}
-#endif /* !LWIP_TCPIP_CORE_LOCKING */
-#endif /* LWIP_NETIF_API */
-
-/**
- * Allocate a structure for a static callback message and initialize it.
- * This is intended to be used to send "static" messages from interrupt context.
- *
- * @param function the function to call
- * @param ctx parameter passed to function
- * @return a struct pointer to pass to tcpip_trycallback().
- */
-struct tcpip_callback_msg* tcpip_callbackmsg_new(tcpip_callback_fn function, void *ctx)
-{
-  struct tcpip_msg *msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API);
-  if (msg == NULL) {
-    return NULL;
-  }
-  msg->type = TCPIP_MSG_CALLBACK_STATIC;
-  msg->msg.cb.function = function;
-  msg->msg.cb.ctx = ctx;
-  return (struct tcpip_callback_msg*)msg;
-}
-
-/**
- * Free a callback message allocated by tcpip_callbackmsg_new().
- *
- * @param msg the message to free
- */
-void tcpip_callbackmsg_delete(struct tcpip_callback_msg* msg)
-{
-  memp_free(MEMP_TCPIP_MSG_API, msg);
-}
-
-/**
- * Try to post a callback-message to the tcpip_thread mbox
- * This is intended to be used to send "static" messages from interrupt context.
- *
- * @param msg pointer to the message to post
- * @return sys_mbox_trypost() return code
- */
-err_t
-tcpip_trycallback(struct tcpip_callback_msg* msg)
-{
-  if (!sys_mbox_valid(&mbox)) {
-    return ERR_VAL;
-  }
-  return sys_mbox_trypost(&mbox, msg);
-}
-
-/**
- * Initialize this module:
- * - initialize all sub modules
- * - start the tcpip_thread
- *
- * @param initfunc a function to call when tcpip_thread is running and finished initializing
- * @param arg argument to pass to initfunc
- */
-void
-tcpip_init(tcpip_init_done_fn initfunc, void *arg)
-{
-  lwip_init();
-
-  tcpip_init_done = initfunc;
-  tcpip_init_done_arg = arg;
-  if(sys_mbox_new(&mbox, TCPIP_MBOX_SIZE) != ERR_OK) {
-    LWIP_ASSERT("failed to create tcpip_thread mbox", 0);
-  }
-#if LWIP_TCPIP_CORE_LOCKING
-  if(sys_mutex_new(&lock_tcpip_core) != ERR_OK) {
-    LWIP_ASSERT("failed to create lock_tcpip_core", 0);
-  }
-#endif /* LWIP_TCPIP_CORE_LOCKING */
-
-  sys_thread_new(TCPIP_THREAD_NAME, tcpip_thread, NULL, TCPIP_THREAD_STACKSIZE, TCPIP_THREAD_PRIO);
-}
-
-/**
- * Simple callback function used with tcpip_callback to free a pbuf
- * (pbuf_free has a wrong signature for tcpip_callback)
- *
- * @param p The pbuf (chain) to be dereferenced.
- */
-static void
-pbuf_free_int(void *p)
-{
-  struct pbuf *q = (struct pbuf *)p;
-  pbuf_free(q);
-}
-
-/**
- * A simple wrapper function that allows you to free a pbuf from interrupt context.
- *
- * @param p The pbuf (chain) to be dereferenced.
- * @return ERR_OK if callback could be enqueued, an err_t if not
- */
-err_t
-pbuf_free_callback(struct pbuf *p)
-{
-  return tcpip_callback_with_block(pbuf_free_int, p, 0);
-}
-
-/**
- * A simple wrapper function that allows you to free heap memory from
- * interrupt context.
- *
- * @param m the heap memory to free
- * @return ERR_OK if callback could be enqueued, an err_t if not
- */
-err_t
-mem_free_callback(void *m)
-{
-  return tcpip_callback_with_block(mem_free, m, 0);
-}
-
-#endif /* !NO_SYS */
+/**
+ * @file
+ * Sequential API Main thread module
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if !NO_SYS /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/priv/tcpip_priv.h"
+#include "lwip/sys.h"
+#include "lwip/memp.h"
+#include "lwip/mem.h"
+#include "lwip/init.h"
+#include "lwip/ip.h"
+#include "lwip/pbuf.h"
+#include "lwip/etharp.h"
+#include "netif/ethernet.h"
+
+#define TCPIP_MSG_VAR_REF(name)     API_VAR_REF(name)
+#define TCPIP_MSG_VAR_DECLARE(name) API_VAR_DECLARE(struct tcpip_msg, name)
+#define TCPIP_MSG_VAR_ALLOC(name)   API_VAR_ALLOC(struct tcpip_msg, MEMP_TCPIP_MSG_API, name, ERR_MEM)
+#define TCPIP_MSG_VAR_FREE(name)    API_VAR_FREE(MEMP_TCPIP_MSG_API, name)
+
+/* global variables */
+static tcpip_init_done_fn tcpip_init_done;
+static void *tcpip_init_done_arg;
+static sys_mbox_t mbox;
+
+#if LWIP_TCPIP_CORE_LOCKING
+/** The global semaphore to lock the stack. */
+sys_mutex_t lock_tcpip_core;
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+
+#if LWIP_TIMERS
+/* wait for a message, timeouts are processed while waiting */
+#define TCPIP_MBOX_FETCH(mbox, msg) sys_timeouts_mbox_fetch(mbox, msg)
+#else /* LWIP_TIMERS */
+/* wait for a message with timers disabled (e.g. pass a timer-check trigger into tcpip_thread) */
+#define TCPIP_MBOX_FETCH(mbox, msg) sys_mbox_fetch(mbox, msg)
+#endif /* LWIP_TIMERS */
+
+/**
+ * The main lwIP thread. This thread has exclusive access to lwIP core functions
+ * (unless access to them is not locked). Other threads communicate with this
+ * thread using message boxes.
+ *
+ * It also starts all the timers to make sure they are running in the right
+ * thread context.
+ *
+ * @param arg unused argument
+ */
+static void
+tcpip_thread(void *arg)
+{
+  struct tcpip_msg *msg;
+  LWIP_UNUSED_ARG(arg);
+
+  if (tcpip_init_done != NULL) {
+    tcpip_init_done(tcpip_init_done_arg);
+  }
+
+  LOCK_TCPIP_CORE();
+  while (1) {                          /* MAIN Loop */
+    UNLOCK_TCPIP_CORE();
+    LWIP_TCPIP_THREAD_ALIVE();
+    /* wait for a message, timeouts are processed while waiting */
+    TCPIP_MBOX_FETCH(&mbox, (void **)&msg);
+    LOCK_TCPIP_CORE();
+    if (msg == NULL) {
+      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: invalid message: NULL\n"));
+      LWIP_ASSERT("tcpip_thread: invalid message", 0);
+      continue;
+    }
+    switch (msg->type) {
+#if !LWIP_TCPIP_CORE_LOCKING
+    case TCPIP_MSG_API:
+      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: API message %p\n", (void *)msg));
+      msg->msg.api_msg.function(msg->msg.api_msg.msg);
+      break;
+    case TCPIP_MSG_API_CALL:
+      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: API CALL message %p\n", (void *)msg));
+      msg->msg.api_call.arg->err = msg->msg.api_call.function(msg->msg.api_call.arg);
+      sys_sem_signal(msg->msg.api_call.sem);
+      break;
+#endif /* !LWIP_TCPIP_CORE_LOCKING */
+
+#if !LWIP_TCPIP_CORE_LOCKING_INPUT
+    case TCPIP_MSG_INPKT:
+      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: PACKET %p\n", (void *)msg));
+      msg->msg.inp.input_fn(msg->msg.inp.p, msg->msg.inp.netif);
+      memp_free(MEMP_TCPIP_MSG_INPKT, msg);
+      break;
+#endif /* !LWIP_TCPIP_CORE_LOCKING_INPUT */
+
+#if LWIP_TCPIP_TIMEOUT && LWIP_TIMERS
+    case TCPIP_MSG_TIMEOUT:
+      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: TIMEOUT %p\n", (void *)msg));
+      sys_timeout(msg->msg.tmo.msecs, msg->msg.tmo.h, msg->msg.tmo.arg);
+      memp_free(MEMP_TCPIP_MSG_API, msg);
+      break;
+    case TCPIP_MSG_UNTIMEOUT:
+      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: UNTIMEOUT %p\n", (void *)msg));
+      sys_untimeout(msg->msg.tmo.h, msg->msg.tmo.arg);
+      memp_free(MEMP_TCPIP_MSG_API, msg);
+      break;
+#endif /* LWIP_TCPIP_TIMEOUT && LWIP_TIMERS */
+
+    case TCPIP_MSG_CALLBACK:
+      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: CALLBACK %p\n", (void *)msg));
+      msg->msg.cb.function(msg->msg.cb.ctx);
+      memp_free(MEMP_TCPIP_MSG_API, msg);
+      break;
+
+    case TCPIP_MSG_CALLBACK_STATIC:
+      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: CALLBACK_STATIC %p\n", (void *)msg));
+      msg->msg.cb.function(msg->msg.cb.ctx);
+      break;
+
+    default:
+      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: invalid message: %d\n", msg->type));
+      LWIP_ASSERT("tcpip_thread: invalid message", 0);
+      break;
+    }
+  }
+}
+
+/**
+ * Pass a received packet to tcpip_thread for input processing
+ *
+ * @param p the received packet
+ * @param inp the network interface on which the packet was received
+ * @param input_fn input function to call
+ */
+err_t
+tcpip_inpkt(struct pbuf *p, struct netif *inp, netif_input_fn input_fn)
+{
+#if LWIP_TCPIP_CORE_LOCKING_INPUT
+  err_t ret;
+  LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_inpkt: PACKET %p/%p\n", (void *)p, (void *)inp));
+  LOCK_TCPIP_CORE();
+  ret = input_fn(p, inp);
+  UNLOCK_TCPIP_CORE();
+  return ret;
+#else /* LWIP_TCPIP_CORE_LOCKING_INPUT */
+  struct tcpip_msg *msg;
+
+  LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(mbox));
+
+  msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_INPKT);
+  if (msg == NULL) {
+    return ERR_MEM;
+  }
+
+  msg->type = TCPIP_MSG_INPKT;
+  msg->msg.inp.p = p;
+  msg->msg.inp.netif = inp;
+  msg->msg.inp.input_fn = input_fn;
+  if (sys_mbox_trypost(&mbox, msg) != ERR_OK) {
+    memp_free(MEMP_TCPIP_MSG_INPKT, msg);
+    return ERR_MEM;
+  }
+  return ERR_OK;
+#endif /* LWIP_TCPIP_CORE_LOCKING_INPUT */
+}
+
+/**
+ * @ingroup lwip_os
+ * Pass a received packet to tcpip_thread for input processing with
+ * ethernet_input or ip_input. Don't call directly, pass to netif_add()
+ * and call netif->input().
+ *
+ * @param p the received packet, p->payload pointing to the Ethernet header or
+ *          to an IP header (if inp doesn't have NETIF_FLAG_ETHARP or
+ *          NETIF_FLAG_ETHERNET flags)
+ * @param inp the network interface on which the packet was received
+ */
+err_t
+tcpip_input(struct pbuf *p, struct netif *inp)
+{
+#if LWIP_ETHERNET
+  if (inp->flags & (NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET)) {
+    return tcpip_inpkt(p, inp, ethernet_input);
+  } else
+#endif /* LWIP_ETHERNET */
+  return tcpip_inpkt(p, inp, ip_input);
+}
+
+/**
+ * Call a specific function in the thread context of
+ * tcpip_thread for easy access synchronization.
+ * A function called in that way may access lwIP core code
+ * without fearing concurrent access.
+ *
+ * @param function the function to call
+ * @param ctx parameter passed to f
+ * @param block 1 to block until the request is posted, 0 to non-blocking mode
+ * @return ERR_OK if the function was called, another err_t if not
+ */
+err_t
+tcpip_callback_with_block(tcpip_callback_fn function, void *ctx, u8_t block)
+{
+  struct tcpip_msg *msg;
+
+  LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(mbox));
+
+  msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API);
+  if (msg == NULL) {
+    return ERR_MEM;
+  }
+
+  msg->type = TCPIP_MSG_CALLBACK;
+  msg->msg.cb.function = function;
+  msg->msg.cb.ctx = ctx;
+  if (block) {
+    sys_mbox_post(&mbox, msg);
+  } else {
+    if (sys_mbox_trypost(&mbox, msg) != ERR_OK) {
+      memp_free(MEMP_TCPIP_MSG_API, msg);
+      return ERR_MEM;
+    }
+  }
+  return ERR_OK;
+}
+
+#if LWIP_TCPIP_TIMEOUT && LWIP_TIMERS
+/**
+ * call sys_timeout in tcpip_thread
+ *
+ * @param msecs time in milliseconds for timeout
+ * @param h function to be called on timeout
+ * @param arg argument to pass to timeout function h
+ * @return ERR_MEM on memory error, ERR_OK otherwise
+ */
+err_t
+tcpip_timeout(u32_t msecs, sys_timeout_handler h, void *arg)
+{
+  struct tcpip_msg *msg;
+
+  LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(mbox));
+
+  msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API);
+  if (msg == NULL) {
+    return ERR_MEM;
+  }
+
+  msg->type = TCPIP_MSG_TIMEOUT;
+  msg->msg.tmo.msecs = msecs;
+  msg->msg.tmo.h = h;
+  msg->msg.tmo.arg = arg;
+  sys_mbox_post(&mbox, msg);
+  return ERR_OK;
+}
+
+/**
+ * call sys_untimeout in tcpip_thread
+ *
+ * @param h function to be called on timeout
+ * @param arg argument to pass to timeout function h
+ * @return ERR_MEM on memory error, ERR_OK otherwise
+ */
+err_t
+tcpip_untimeout(sys_timeout_handler h, void *arg)
+{
+  struct tcpip_msg *msg;
+
+  LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(mbox));
+
+  msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API);
+  if (msg == NULL) {
+    return ERR_MEM;
+  }
+
+  msg->type = TCPIP_MSG_UNTIMEOUT;
+  msg->msg.tmo.h = h;
+  msg->msg.tmo.arg = arg;
+  sys_mbox_post(&mbox, msg);
+  return ERR_OK;
+}
+#endif /* LWIP_TCPIP_TIMEOUT && LWIP_TIMERS */
+
+
+/**
+ * Sends a message to TCPIP thread to call a function. Caller thread blocks on
+ * on a provided semaphore, which ist NOT automatically signalled by TCPIP thread,
+ * this has to be done by the user.
+ * It is recommended to use LWIP_TCPIP_CORE_LOCKING since this is the way
+ * with least runtime overhead.
+ *
+ * @param fn function to be called from TCPIP thread
+ * @param apimsg argument to API function
+ * @param sem semaphore to wait on
+ * @return ERR_OK if the function was called, another err_t if not
+ */
+err_t
+tcpip_send_msg_wait_sem(tcpip_callback_fn fn, void *apimsg, sys_sem_t* sem)
+{
+#if LWIP_TCPIP_CORE_LOCKING
+  LWIP_UNUSED_ARG(sem);
+  LOCK_TCPIP_CORE();
+  fn(apimsg);
+  UNLOCK_TCPIP_CORE();
+  return ERR_OK;
+#else /* LWIP_TCPIP_CORE_LOCKING */
+  TCPIP_MSG_VAR_DECLARE(msg);
+
+  LWIP_ASSERT("semaphore not initialized", sys_sem_valid(sem));
+  LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(mbox));
+
+  TCPIP_MSG_VAR_ALLOC(msg);
+  TCPIP_MSG_VAR_REF(msg).type = TCPIP_MSG_API;
+  TCPIP_MSG_VAR_REF(msg).msg.api_msg.function = fn;
+  TCPIP_MSG_VAR_REF(msg).msg.api_msg.msg = apimsg;
+  sys_mbox_post(&mbox, &TCPIP_MSG_VAR_REF(msg));
+  sys_arch_sem_wait(sem, 0);
+  TCPIP_MSG_VAR_FREE(msg);
+  return ERR_OK;
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+}
+
+/**
+ * Synchronously calls function in TCPIP thread and waits for its completion.
+ * It is recommended to use LWIP_TCPIP_CORE_LOCKING (preferred) or
+ * LWIP_NETCONN_SEM_PER_THREAD. 
+ * If not, a semaphore is created and destroyed on every call which is usually
+ * an expensive/slow operation.
+ * @param fn Function to call
+ * @param call Call parameters
+ * @return Return value from tcpip_api_call_fn
+ */
+err_t
+tcpip_api_call(tcpip_api_call_fn fn, struct tcpip_api_call_data *call)
+{
+#if LWIP_TCPIP_CORE_LOCKING
+  err_t err;
+  LOCK_TCPIP_CORE();
+  err = fn(call);
+  UNLOCK_TCPIP_CORE();
+  return err;
+#else /* LWIP_TCPIP_CORE_LOCKING */
+  TCPIP_MSG_VAR_DECLARE(msg);
+
+#if !LWIP_NETCONN_SEM_PER_THREAD
+  err_t err = sys_sem_new(&call->sem, 0);
+  if (err != ERR_OK) {
+    return err;
+  }
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
+
+  LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(mbox));
+
+  TCPIP_MSG_VAR_ALLOC(msg);
+  TCPIP_MSG_VAR_REF(msg).type = TCPIP_MSG_API_CALL;
+  TCPIP_MSG_VAR_REF(msg).msg.api_call.arg = call;
+  TCPIP_MSG_VAR_REF(msg).msg.api_call.function = fn;
+#if LWIP_NETCONN_SEM_PER_THREAD
+  TCPIP_MSG_VAR_REF(msg).msg.api_call.sem = LWIP_NETCONN_THREAD_SEM_GET();
+#else /* LWIP_NETCONN_SEM_PER_THREAD */
+  TCPIP_MSG_VAR_REF(msg).msg.api_call.sem = &call->sem;
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
+  sys_mbox_post(&mbox, &TCPIP_MSG_VAR_REF(msg));
+  sys_arch_sem_wait(TCPIP_MSG_VAR_REF(msg).msg.api_call.sem, 0);
+  TCPIP_MSG_VAR_FREE(msg);
+
+#if !LWIP_NETCONN_SEM_PER_THREAD
+  sys_sem_free(&call->sem);
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
+
+  return call->err;
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+}
+
+/**
+ * Allocate a structure for a static callback message and initialize it.
+ * This is intended to be used to send "static" messages from interrupt context.
+ *
+ * @param function the function to call
+ * @param ctx parameter passed to function
+ * @return a struct pointer to pass to tcpip_trycallback().
+ */
+struct tcpip_callback_msg*
+tcpip_callbackmsg_new(tcpip_callback_fn function, void *ctx)
+{
+  struct tcpip_msg *msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API);
+  if (msg == NULL) {
+    return NULL;
+  }
+  msg->type = TCPIP_MSG_CALLBACK_STATIC;
+  msg->msg.cb.function = function;
+  msg->msg.cb.ctx = ctx;
+  return (struct tcpip_callback_msg*)msg;
+}
+
+/**
+ * Free a callback message allocated by tcpip_callbackmsg_new().
+ *
+ * @param msg the message to free
+ */
+void
+tcpip_callbackmsg_delete(struct tcpip_callback_msg* msg)
+{
+  memp_free(MEMP_TCPIP_MSG_API, msg);
+}
+
+/**
+ * Try to post a callback-message to the tcpip_thread mbox
+ * This is intended to be used to send "static" messages from interrupt context.
+ *
+ * @param msg pointer to the message to post
+ * @return sys_mbox_trypost() return code
+ */
+err_t
+tcpip_trycallback(struct tcpip_callback_msg* msg)
+{
+  LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(mbox));
+  return sys_mbox_trypost(&mbox, msg);
+}
+
+/**
+ * @ingroup lwip_os
+ * Initialize this module:
+ * - initialize all sub modules
+ * - start the tcpip_thread
+ *
+ * @param initfunc a function to call when tcpip_thread is running and finished initializing
+ * @param arg argument to pass to initfunc
+ */
+void
+tcpip_init(tcpip_init_done_fn initfunc, void *arg)
+{
+  lwip_init();
+
+  tcpip_init_done = initfunc;
+  tcpip_init_done_arg = arg;
+  if (sys_mbox_new(&mbox, TCPIP_MBOX_SIZE) != ERR_OK) {
+    LWIP_ASSERT("failed to create tcpip_thread mbox", 0);
+  }
+#if LWIP_TCPIP_CORE_LOCKING
+  if (sys_mutex_new(&lock_tcpip_core) != ERR_OK) {
+    LWIP_ASSERT("failed to create lock_tcpip_core", 0);
+  }
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+
+  sys_thread_new(TCPIP_THREAD_NAME, tcpip_thread, NULL, TCPIP_THREAD_STACKSIZE, TCPIP_THREAD_PRIO);
+}
+
+/**
+ * Simple callback function used with tcpip_callback to free a pbuf
+ * (pbuf_free has a wrong signature for tcpip_callback)
+ *
+ * @param p The pbuf (chain) to be dereferenced.
+ */
+static void
+pbuf_free_int(void *p)
+{
+  struct pbuf *q = (struct pbuf *)p;
+  pbuf_free(q);
+}
+
+/**
+ * A simple wrapper function that allows you to free a pbuf from interrupt context.
+ *
+ * @param p The pbuf (chain) to be dereferenced.
+ * @return ERR_OK if callback could be enqueued, an err_t if not
+ */
+err_t
+pbuf_free_callback(struct pbuf *p)
+{
+  return tcpip_callback_with_block(pbuf_free_int, p, 0);
+}
+
+/**
+ * A simple wrapper function that allows you to free heap memory from
+ * interrupt context.
+ *
+ * @param m the heap memory to free
+ * @return ERR_OK if callback could be enqueued, an err_t if not
+ */
+err_t
+mem_free_callback(void *m)
+{
+  return tcpip_callback_with_block(mem_free, m, 0);
+}
+
+#endif /* !NO_SYS */

二進制
thirdparty/lwip/src/core/.DS_Store


+ 222 - 108
thirdparty/lwip/src/core/def.c

@@ -1,108 +1,222 @@
-/**
- * @file
- * Common functions used throughout the stack.
- *
- */
-
-/*
- * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * This file is part of the lwIP TCP/IP stack.
- *
- * Author: Simon Goldschmidt
- *
- */
-
-#include "lwip/opt.h"
-#include "lwip/def.h"
-
-/**
- * These are reference implementations of the byte swapping functions.
- * Again with the aim of being simple, correct and fully portable.
- * Byte swapping is the second thing you would want to optimize. You will
- * need to port it to your architecture and in your cc.h:
- * 
- * #define LWIP_PLATFORM_BYTESWAP 1
- * #define LWIP_PLATFORM_HTONS(x) <your_htons>
- * #define LWIP_PLATFORM_HTONL(x) <your_htonl>
- *
- * Note ntohs() and ntohl() are merely references to the htonx counterparts.
- */
-
-#if (LWIP_PLATFORM_BYTESWAP == 0) && (BYTE_ORDER == LITTLE_ENDIAN)
-
-/**
- * Convert an u16_t from host- to network byte order.
- *
- * @param n u16_t in host byte order
- * @return n in network byte order
- */
-u16_t
-lwip_htons(u16_t n)
-{
-  return ((n & 0xff) << 8) | ((n & 0xff00) >> 8);
-}
-
-/**
- * Convert an u16_t from network- to host byte order.
- *
- * @param n u16_t in network byte order
- * @return n in host byte order
- */
-u16_t
-lwip_ntohs(u16_t n)
-{
-  return lwip_htons(n);
-}
-
-/**
- * Convert an u32_t from host- to network byte order.
- *
- * @param n u32_t in host byte order
- * @return n in network byte order
- */
-u32_t
-lwip_htonl(u32_t n)
-{
-  return ((n & 0xff) << 24) |
-    ((n & 0xff00) << 8) |
-    ((n & 0xff0000UL) >> 8) |
-    ((n & 0xff000000UL) >> 24);
-}
-
-/**
- * Convert an u32_t from network- to host byte order.
- *
- * @param n u32_t in network byte order
- * @return n in host byte order
- */
-u32_t
-lwip_ntohl(u32_t n)
-{
-  return lwip_htonl(n);
-}
-
-#endif /* (LWIP_PLATFORM_BYTESWAP == 0) && (BYTE_ORDER == LITTLE_ENDIAN) */
+/**
+ * @file
+ * Common functions used throughout the stack.
+ *
+ * These are reference implementations of the byte swapping functions.
+ * Again with the aim of being simple, correct and fully portable.
+ * Byte swapping is the second thing you would want to optimize. You will
+ * need to port it to your architecture and in your cc.h:
+ *
+ * \#define lwip_htons(x) your_htons
+ * \#define lwip_htonl(x) your_htonl
+ *
+ * Note lwip_ntohs() and lwip_ntohl() are merely references to the htonx counterparts.
+ * 
+ * If you \#define them to htons() and htonl(), you should
+ * \#define LWIP_DONT_PROVIDE_BYTEORDER_FUNCTIONS to prevent lwIP from
+ * defining htonx/ntohx compatibility macros.
+
+ * @defgroup sys_nonstandard Non-standard functions
+ * @ingroup sys_layer
+ * lwIP provides default implementations for non-standard functions.
+ * These can be mapped to OS functions to reduce code footprint if desired.
+ * All defines related to this section must not be placed in lwipopts.h,
+ * but in arch/cc.h!
+ * These options cannot be \#defined in lwipopts.h since they are not options
+ * of lwIP itself, but options of the lwIP port to your system.
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt
+ *
+ */
+
+#include "lwip/opt.h"
+#include "lwip/def.h"
+
+#include <string.h>
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+
+#if !defined(lwip_htons)
+/**
+ * Convert an u16_t from host- to network byte order.
+ *
+ * @param n u16_t in host byte order
+ * @return n in network byte order
+ */
+u16_t
+lwip_htons(u16_t n)
+{
+  return (u16_t)PP_HTONS(n);
+}
+#endif /* lwip_htons */
+
+#if !defined(lwip_htonl)
+/**
+ * Convert an u32_t from host- to network byte order.
+ *
+ * @param n u32_t in host byte order
+ * @return n in network byte order
+ */
+u32_t
+lwip_htonl(u32_t n)
+{
+  return (u32_t)PP_HTONL(n);
+}
+#endif /* lwip_htonl */
+
+#endif /* BYTE_ORDER == LITTLE_ENDIAN */
+
+#ifndef lwip_strnstr
+/**
+ * @ingroup sys_nonstandard
+ * lwIP default implementation for strnstr() non-standard function.
+ * This can be \#defined to strnstr() depending on your platform port.
+ */
+char*
+lwip_strnstr(const char* buffer, const char* token, size_t n)
+{
+  const char* p;
+  size_t tokenlen = strlen(token);
+  if (tokenlen == 0) {
+    return LWIP_CONST_CAST(char *, buffer);
+  }
+  for (p = buffer; *p && (p + tokenlen <= buffer + n); p++) {
+    if ((*p == *token) && (strncmp(p, token, tokenlen) == 0)) {
+      return LWIP_CONST_CAST(char *, p);
+    }
+  }
+  return NULL;
+}
+#endif
+
+#ifndef lwip_stricmp
+/**
+ * @ingroup sys_nonstandard
+ * lwIP default implementation for stricmp() non-standard function.
+ * This can be \#defined to stricmp() depending on your platform port.
+ */
+int
+lwip_stricmp(const char* str1, const char* str2)
+{
+  char c1, c2;
+
+  do {
+    c1 = *str1++;
+    c2 = *str2++;
+    if (c1 != c2) {
+      char c1_upc = c1 | 0x20;
+      if ((c1_upc >= 'a') && (c1_upc <= 'z')) {
+        /* characters are not equal an one is in the alphabet range:
+        downcase both chars and check again */
+        char c2_upc = c2 | 0x20;
+        if (c1_upc != c2_upc) {
+          /* still not equal */
+          /* don't care for < or > */
+          return 1;
+        }
+      } else {
+        /* characters are not equal but none is in the alphabet range */
+        return 1;
+      }
+    }
+  } while (c1 != 0);
+  return 0;
+}
+#endif
+
+#ifndef lwip_strnicmp
+/**
+ * @ingroup sys_nonstandard
+ * lwIP default implementation for strnicmp() non-standard function.
+ * This can be \#defined to strnicmp() depending on your platform port.
+ */
+int
+lwip_strnicmp(const char* str1, const char* str2, size_t len)
+{
+  char c1, c2;
+
+  do {
+    c1 = *str1++;
+    c2 = *str2++;
+    if (c1 != c2) {
+      char c1_upc = c1 | 0x20;
+      if ((c1_upc >= 'a') && (c1_upc <= 'z')) {
+        /* characters are not equal an one is in the alphabet range:
+        downcase both chars and check again */
+        char c2_upc = c2 | 0x20;
+        if (c1_upc != c2_upc) {
+          /* still not equal */
+          /* don't care for < or > */
+          return 1;
+        }
+      } else {
+        /* characters are not equal but none is in the alphabet range */
+        return 1;
+      }
+    }
+  } while (len-- && c1 != 0);
+  return 0;
+}
+#endif
+
+#ifndef lwip_itoa
+/**
+ * @ingroup sys_nonstandard
+ * lwIP default implementation for itoa() non-standard function.
+ * This can be \#defined to itoa() or snprintf(result, bufsize, "%d", number) depending on your platform port.
+ */
+void
+lwip_itoa(char* result, size_t bufsize, int number)
+{
+  const int base = 10;
+  char* ptr = result, *ptr1 = result, tmp_char;
+  int tmp_value;
+  LWIP_UNUSED_ARG(bufsize);
+
+  do {
+    tmp_value = number;
+    number /= base;
+    *ptr++ = "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz"[35 + (tmp_value - number * base)];
+  } while(number);
+
+   /* Apply negative sign */
+  if (tmp_value < 0) {
+     *ptr++ = '-';
+  }
+  *ptr-- = '\0';
+  while(ptr1 < ptr) {
+    tmp_char = *ptr;
+    *ptr--= *ptr1;
+    *ptr1++ = tmp_char;
+  }
+}
+#endif

+ 1573 - 970
thirdparty/lwip/src/core/dns.c

@@ -1,970 +1,1573 @@
-/**
- * @file
- * DNS - host name to IP address resolver.
- *
- */
-
-/**
-
- * This file implements a DNS host name to IP address resolver.
-
- * Port to lwIP from uIP
- * by Jim Pettinato April 2007
-
- * uIP version Copyright (c) 2002-2003, Adam Dunkels.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote
- *    products derived from this software without specific prior
- *    written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
- * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
- * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
- * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *
- * DNS.C
- *
- * The lwIP DNS resolver functions are used to lookup a host name and
- * map it to a numerical IP address. It maintains a list of resolved
- * hostnames that can be queried with the dns_lookup() function.
- * New hostnames can be resolved using the dns_query() function.
- *
- * The lwIP version of the resolver also adds a non-blocking version of
- * gethostbyname() that will work with a raw API application. This function
- * checks for an IP address string first and converts it if it is valid.
- * gethostbyname() then does a dns_lookup() to see if the name is 
- * already in the table. If so, the IP is returned. If not, a query is 
- * issued and the function returns with a ERR_INPROGRESS status. The app
- * using the dns client must then go into a waiting state.
- *
- * Once a hostname has been resolved (or found to be non-existent),
- * the resolver code calls a specified callback function (which 
- * must be implemented by the module that uses the resolver).
- */
-
-/*-----------------------------------------------------------------------------
- * RFC 1035 - Domain names - implementation and specification
- * RFC 2181 - Clarifications to the DNS Specification
- *----------------------------------------------------------------------------*/
-
-/** @todo: define good default values (rfc compliance) */
-/** @todo: improve answer parsing, more checkings... */
-/** @todo: check RFC1035 - 7.3. Processing responses */
-
-/*-----------------------------------------------------------------------------
- * Includes
- *----------------------------------------------------------------------------*/
-
-#include "lwip/opt.h"
-
-#if LWIP_DNS /* don't build if not configured for use in lwipopts.h */
-
-#include "lwip/udp.h"
-#include "lwip/mem.h"
-#include "lwip/memp.h"
-#include "lwip/dns.h"
-
-#include <string.h>
-
-/** DNS server IP address */
-#ifndef DNS_SERVER_ADDRESS
-#define DNS_SERVER_ADDRESS(ipaddr)        (ip4_addr_set_u32(ipaddr, ipaddr_addr("208.67.222.222"))) /* resolver1.opendns.com */
-#endif
-
-/** DNS server port address */
-#ifndef DNS_SERVER_PORT
-#define DNS_SERVER_PORT           53
-#endif
-
-/** DNS maximum number of retries when asking for a name, before "timeout". */
-#ifndef DNS_MAX_RETRIES
-#define DNS_MAX_RETRIES           4
-#endif
-
-/** DNS resource record max. TTL (one week as default) */
-#ifndef DNS_MAX_TTL
-#define DNS_MAX_TTL               604800
-#endif
-
-/* DNS protocol flags */
-#define DNS_FLAG1_RESPONSE        0x80
-#define DNS_FLAG1_OPCODE_STATUS   0x10
-#define DNS_FLAG1_OPCODE_INVERSE  0x08
-#define DNS_FLAG1_OPCODE_STANDARD 0x00
-#define DNS_FLAG1_AUTHORATIVE     0x04
-#define DNS_FLAG1_TRUNC           0x02
-#define DNS_FLAG1_RD              0x01
-#define DNS_FLAG2_RA              0x80
-#define DNS_FLAG2_ERR_MASK        0x0f
-#define DNS_FLAG2_ERR_NONE        0x00
-#define DNS_FLAG2_ERR_NAME        0x03
-
-/* DNS protocol states */
-#define DNS_STATE_UNUSED          0
-#define DNS_STATE_NEW             1
-#define DNS_STATE_ASKING          2
-#define DNS_STATE_DONE            3
-
-#ifdef PACK_STRUCT_USE_INCLUDES
-#  include "arch/bpstruct.h"
-#endif
-PACK_STRUCT_BEGIN
-/** DNS message header */
-struct dns_hdr {
-  PACK_STRUCT_FIELD(u16_t id);
-  PACK_STRUCT_FIELD(u8_t flags1);
-  PACK_STRUCT_FIELD(u8_t flags2);
-  PACK_STRUCT_FIELD(u16_t numquestions);
-  PACK_STRUCT_FIELD(u16_t numanswers);
-  PACK_STRUCT_FIELD(u16_t numauthrr);
-  PACK_STRUCT_FIELD(u16_t numextrarr);
-} PACK_STRUCT_STRUCT;
-PACK_STRUCT_END
-#ifdef PACK_STRUCT_USE_INCLUDES
-#  include "arch/epstruct.h"
-#endif
-#define SIZEOF_DNS_HDR 12
-
-/** DNS query message structure.
-    No packing needed: only used locally on the stack. */
-struct dns_query {
-  /* DNS query record starts with either a domain name or a pointer
-     to a name already present somewhere in the packet. */
-  u16_t type;
-  u16_t cls;
-};
-#define SIZEOF_DNS_QUERY 4
-
-/** DNS answer message structure.
-    No packing needed: only used locally on the stack. */
-struct dns_answer {
-  /* DNS answer record starts with either a domain name or a pointer
-     to a name already present somewhere in the packet. */
-  u16_t type;
-  u16_t cls;
-  u32_t ttl;
-  u16_t len;
-};
-#define SIZEOF_DNS_ANSWER 10
-
-/** DNS table entry */
-struct dns_table_entry {
-  u8_t  state;
-  u8_t  numdns;
-  u8_t  tmr;
-  u8_t  retries;
-  u8_t  seqno;
-  u8_t  err;
-  u32_t ttl;
-  char name[DNS_MAX_NAME_LENGTH];
-  ip_addr_t ipaddr;
-  /* pointer to callback on DNS query done */
-  dns_found_callback found;
-  void *arg;
-};
-
-#if DNS_LOCAL_HOSTLIST
-
-#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC
-/** Local host-list. For hostnames in this list, no
- *  external name resolution is performed */
-static struct local_hostlist_entry *local_hostlist_dynamic;
-#else /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
-
-/** Defining this allows the local_hostlist_static to be placed in a different
- * linker section (e.g. FLASH) */
-#ifndef DNS_LOCAL_HOSTLIST_STORAGE_PRE
-#define DNS_LOCAL_HOSTLIST_STORAGE_PRE static
-#endif /* DNS_LOCAL_HOSTLIST_STORAGE_PRE */
-/** Defining this allows the local_hostlist_static to be placed in a different
- * linker section (e.g. FLASH) */
-#ifndef DNS_LOCAL_HOSTLIST_STORAGE_POST
-#define DNS_LOCAL_HOSTLIST_STORAGE_POST
-#endif /* DNS_LOCAL_HOSTLIST_STORAGE_POST */
-DNS_LOCAL_HOSTLIST_STORAGE_PRE struct local_hostlist_entry local_hostlist_static[]
-  DNS_LOCAL_HOSTLIST_STORAGE_POST = DNS_LOCAL_HOSTLIST_INIT;
-
-#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
-
-static void dns_init_local();
-#endif /* DNS_LOCAL_HOSTLIST */
-
-
-/* forward declarations */
-static void dns_recv(void *s, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port);
-static void dns_check_entries(void);
-
-/*-----------------------------------------------------------------------------
- * Globales
- *----------------------------------------------------------------------------*/
-
-/* DNS variables */
-static struct udp_pcb        *dns_pcb;
-static u8_t                   dns_seqno;
-static struct dns_table_entry dns_table[DNS_TABLE_SIZE];
-static ip_addr_t              dns_servers[DNS_MAX_SERVERS];
-/** Contiguous buffer for processing responses */
-static u8_t                   dns_payload_buffer[LWIP_MEM_ALIGN_BUFFER(DNS_MSG_SIZE)];
-static u8_t*                  dns_payload;
-
-/**
- * Initialize the resolver: set up the UDP pcb and configure the default server
- * (DNS_SERVER_ADDRESS).
- */
-void
-dns_init()
-{
-  ip_addr_t dnsserver;
-
-  dns_payload = (u8_t *)LWIP_MEM_ALIGN(dns_payload_buffer);
-  
-  /* initialize default DNS server address */
-  DNS_SERVER_ADDRESS(&dnsserver);
-
-  LWIP_DEBUGF(DNS_DEBUG, ("dns_init: initializing\n"));
-
-  /* if dns client not yet initialized... */
-  if (dns_pcb == NULL) {
-    dns_pcb = udp_new();
-
-    if (dns_pcb != NULL) {
-      /* initialize DNS table not needed (initialized to zero since it is a
-       * global variable) */
-      LWIP_ASSERT("For implicit initialization to work, DNS_STATE_UNUSED needs to be 0",
-        DNS_STATE_UNUSED == 0);
-
-      /* initialize DNS client */
-      udp_bind(dns_pcb, IP_ADDR_ANY, 0);
-      udp_recv(dns_pcb, dns_recv, NULL);
-
-      /* initialize default DNS primary server */
-      dns_setserver(0, &dnsserver);
-    }
-  }
-#if DNS_LOCAL_HOSTLIST
-  dns_init_local();
-#endif
-}
-
-/**
- * Initialize one of the DNS servers.
- *
- * @param numdns the index of the DNS server to set must be < DNS_MAX_SERVERS
- * @param dnsserver IP address of the DNS server to set
- */
-void
-dns_setserver(u8_t numdns, ip_addr_t *dnsserver)
-{
-  if ((numdns < DNS_MAX_SERVERS) && (dns_pcb != NULL) &&
-      (dnsserver != NULL) && !ip_addr_isany(dnsserver)) {
-    dns_servers[numdns] = (*dnsserver);
-  }
-}
-
-/**
- * Obtain one of the currently configured DNS server.
- *
- * @param numdns the index of the DNS server
- * @return IP address of the indexed DNS server or "ip_addr_any" if the DNS
- *         server has not been configured.
- */
-ip_addr_t
-dns_getserver(u8_t numdns)
-{
-  if (numdns < DNS_MAX_SERVERS) {
-    return dns_servers[numdns];
-  } else {
-    return *IP_ADDR_ANY;
-  }
-}
-
-/**
- * The DNS resolver client timer - handle retries and timeouts and should
- * be called every DNS_TMR_INTERVAL milliseconds (every second by default).
- */
-void
-dns_tmr(void)
-{
-  if (dns_pcb != NULL) {
-    LWIP_DEBUGF(DNS_DEBUG, ("dns_tmr: dns_check_entries\n"));
-    dns_check_entries();
-  }
-}
-
-#if DNS_LOCAL_HOSTLIST
-static void
-dns_init_local()
-{
-#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC && defined(DNS_LOCAL_HOSTLIST_INIT)
-  int i;
-  struct local_hostlist_entry *entry;
-  /* Dynamic: copy entries from DNS_LOCAL_HOSTLIST_INIT to list */
-  struct local_hostlist_entry local_hostlist_init[] = DNS_LOCAL_HOSTLIST_INIT;
-  size_t namelen;
-  for (i = 0; i < sizeof(local_hostlist_init) / sizeof(struct local_hostlist_entry); i++) {
-    struct local_hostlist_entry *init_entry = &local_hostlist_init[i];
-    LWIP_ASSERT("invalid host name (NULL)", init_entry->name != NULL);
-    namelen = strlen(init_entry->name);
-    LWIP_ASSERT("namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN", namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN);
-    entry = (struct local_hostlist_entry *)memp_malloc(MEMP_LOCALHOSTLIST);
-    LWIP_ASSERT("mem-error in dns_init_local", entry != NULL);
-    if (entry != NULL) {
-      entry->name = (char*)entry + sizeof(struct local_hostlist_entry);
-      MEMCPY((char*)entry->name, init_entry->name, namelen);
-      ((char*)entry->name)[namelen] = 0;
-      entry->addr = init_entry->addr;
-      entry->next = local_hostlist_dynamic;
-      local_hostlist_dynamic = entry;
-    }
-  }
-#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC && defined(DNS_LOCAL_HOSTLIST_INIT) */
-}
-
-/**
- * Scans the local host-list for a hostname.
- *
- * @param hostname Hostname to look for in the local host-list
- * @return The first IP address for the hostname in the local host-list or
- *         IPADDR_NONE if not found.
- */
-static u32_t
-dns_lookup_local(const char *hostname)
-{
-#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC
-  struct local_hostlist_entry *entry = local_hostlist_dynamic;
-  while(entry != NULL) {
-    if(strcmp(entry->name, hostname) == 0) {
-      return ip4_addr_get_u32(&entry->addr);
-    }
-    entry = entry->next;
-  }
-#else /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
-  int i;
-  for (i = 0; i < sizeof(local_hostlist_static) / sizeof(struct local_hostlist_entry); i++) {
-    if(strcmp(local_hostlist_static[i].name, hostname) == 0) {
-      return ip4_addr_get_u32(&local_hostlist_static[i].addr);
-    }
-  }
-#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
-  return IPADDR_NONE;
-}
-
-#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC
-/** Remove all entries from the local host-list for a specific hostname
- * and/or IP addess
- *
- * @param hostname hostname for which entries shall be removed from the local
- *                 host-list
- * @param addr address for which entries shall be removed from the local host-list
- * @return the number of removed entries
- */
-int
-dns_local_removehost(const char *hostname, const ip_addr_t *addr)
-{
-  int removed = 0;
-  struct local_hostlist_entry *entry = local_hostlist_dynamic;
-  struct local_hostlist_entry *last_entry = NULL;
-  while (entry != NULL) {
-    if (((hostname == NULL) || !strcmp(entry->name, hostname)) &&
-        ((addr == NULL) || ip_addr_cmp(&entry->addr, addr))) {
-      struct local_hostlist_entry *free_entry;
-      if (last_entry != NULL) {
-        last_entry->next = entry->next;
-      } else {
-        local_hostlist_dynamic = entry->next;
-      }
-      free_entry = entry;
-      entry = entry->next;
-      memp_free(MEMP_LOCALHOSTLIST, free_entry);
-      removed++;
-    } else {
-      last_entry = entry;
-      entry = entry->next;
-    }
-  }
-  return removed;
-}
-
-/**
- * Add a hostname/IP address pair to the local host-list.
- * Duplicates are not checked.
- *
- * @param hostname hostname of the new entry
- * @param addr IP address of the new entry
- * @return ERR_OK if succeeded or ERR_MEM on memory error
- */
-err_t
-dns_local_addhost(const char *hostname, const ip_addr_t *addr)
-{
-  struct local_hostlist_entry *entry;
-  size_t namelen;
-  LWIP_ASSERT("invalid host name (NULL)", hostname != NULL);
-  namelen = strlen(hostname);
-  LWIP_ASSERT("namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN", namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN);
-  entry = (struct local_hostlist_entry *)memp_malloc(MEMP_LOCALHOSTLIST);
-  if (entry == NULL) {
-    return ERR_MEM;
-  }
-  entry->name = (char*)entry + sizeof(struct local_hostlist_entry);
-  MEMCPY((char*)entry->name, hostname, namelen);
-  ((char*)entry->name)[namelen] = 0;
-  ip_addr_copy(entry->addr, *addr);
-  entry->next = local_hostlist_dynamic;
-  local_hostlist_dynamic = entry;
-  return ERR_OK;
-}
-#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC*/
-#endif /* DNS_LOCAL_HOSTLIST */
-
-/**
- * Look up a hostname in the array of known hostnames.
- *
- * @note This function only looks in the internal array of known
- * hostnames, it does not send out a query for the hostname if none
- * was found. The function dns_enqueue() can be used to send a query
- * for a hostname.
- *
- * @param name the hostname to look up
- * @return the hostname's IP address, as u32_t (instead of ip_addr_t to
- *         better check for failure: != IPADDR_NONE) or IPADDR_NONE if the hostname
- *         was not found in the cached dns_table.
- */
-static u32_t
-dns_lookup(const char *name)
-{
-  u8_t i;
-#if DNS_LOCAL_HOSTLIST || defined(DNS_LOOKUP_LOCAL_EXTERN)
-  u32_t addr;
-#endif /* DNS_LOCAL_HOSTLIST || defined(DNS_LOOKUP_LOCAL_EXTERN) */
-#if DNS_LOCAL_HOSTLIST
-  if ((addr = dns_lookup_local(name)) != IPADDR_NONE) {
-    return addr;
-  }
-#endif /* DNS_LOCAL_HOSTLIST */
-#ifdef DNS_LOOKUP_LOCAL_EXTERN
-  if((addr = DNS_LOOKUP_LOCAL_EXTERN(name)) != IPADDR_NONE) {
-    return addr;
-  }
-#endif /* DNS_LOOKUP_LOCAL_EXTERN */
-
-  /* Walk through name list, return entry if found. If not, return NULL. */
-  for (i = 0; i < DNS_TABLE_SIZE; ++i) {
-    if ((dns_table[i].state == DNS_STATE_DONE) &&
-        (strcmp(name, dns_table[i].name) == 0)) {
-      LWIP_DEBUGF(DNS_DEBUG, ("dns_lookup: \"%s\": found = ", name));
-      ip_addr_debug_print(DNS_DEBUG, &(dns_table[i].ipaddr));
-      LWIP_DEBUGF(DNS_DEBUG, ("\n"));
-      return ip4_addr_get_u32(&dns_table[i].ipaddr);
-    }
-  }
-
-  return IPADDR_NONE;
-}
-
-#if DNS_DOES_NAME_CHECK
-/**
- * Compare the "dotted" name "query" with the encoded name "response"
- * to make sure an answer from the DNS server matches the current dns_table
- * entry (otherwise, answers might arrive late for hostname not on the list
- * any more).
- *
- * @param query hostname (not encoded) from the dns_table
- * @param response encoded hostname in the DNS response
- * @return 0: names equal; 1: names differ
- */
-static u8_t
-dns_compare_name(unsigned char *query, unsigned char *response)
-{
-  unsigned char n;
-
-  do {
-    n = *response++;
-    /** @see RFC 1035 - 4.1.4. Message compression */
-    if ((n & 0xc0) == 0xc0) {
-      /* Compressed name */
-      break;
-    } else {
-      /* Not compressed name */
-      while (n > 0) {
-        if ((*query) != (*response)) {
-          return 1;
-        }
-        ++response;
-        ++query;
-        --n;
-      };
-      ++query;
-    }
-  } while (*response != 0);
-
-  return 0;
-}
-#endif /* DNS_DOES_NAME_CHECK */
-
-/**
- * Walk through a compact encoded DNS name and return the end of the name.
- *
- * @param query encoded DNS name in the DNS server response
- * @return end of the name
- */
-static unsigned char *
-dns_parse_name(unsigned char *query)
-{
-  unsigned char n;
-
-  do {
-    n = *query++;
-    /** @see RFC 1035 - 4.1.4. Message compression */
-    if ((n & 0xc0) == 0xc0) {
-      /* Compressed name */
-      break;
-    } else {
-      /* Not compressed name */
-      while (n > 0) {
-        ++query;
-        --n;
-      };
-    }
-  } while (*query != 0);
-
-  return query + 1;
-}
-
-/**
- * Send a DNS query packet.
- *
- * @param numdns index of the DNS server in the dns_servers table
- * @param name hostname to query
- * @param id index of the hostname in dns_table, used as transaction ID in the
- *        DNS query packet
- * @return ERR_OK if packet is sent; an err_t indicating the problem otherwise
- */
-static err_t
-dns_send(u8_t numdns, const char* name, u8_t id)
-{
-  err_t err;
-  struct dns_hdr *hdr;
-  struct dns_query qry;
-  struct pbuf *p;
-  char *query, *nptr;
-  const char *pHostname;
-  u8_t n;
-
-  LWIP_DEBUGF(DNS_DEBUG, ("dns_send: dns_servers[%"U16_F"] \"%s\": request\n",
-              (u16_t)(numdns), name));
-  LWIP_ASSERT("dns server out of array", numdns < DNS_MAX_SERVERS);
-  LWIP_ASSERT("dns server has no IP address set", !ip_addr_isany(&dns_servers[numdns]));
-
-  /* if here, we have either a new query or a retry on a previous query to process */
-  p = pbuf_alloc(PBUF_TRANSPORT, SIZEOF_DNS_HDR + DNS_MAX_NAME_LENGTH +
-                 SIZEOF_DNS_QUERY, PBUF_RAM);
-  if (p != NULL) {
-    LWIP_ASSERT("pbuf must be in one piece", p->next == NULL);
-    /* fill dns header */
-    hdr = (struct dns_hdr*)p->payload;
-    memset(hdr, 0, SIZEOF_DNS_HDR);
-    hdr->id = htons(id);
-    hdr->flags1 = DNS_FLAG1_RD;
-    hdr->numquestions = PP_HTONS(1);
-    query = (char*)hdr + SIZEOF_DNS_HDR;
-    pHostname = name;
-    --pHostname;
-
-    /* convert hostname into suitable query format. */
-    do {
-      ++pHostname;
-      nptr = query;
-      ++query;
-      for(n = 0; *pHostname != '.' && *pHostname != 0; ++pHostname) {
-        *query = *pHostname;
-        ++query;
-        ++n;
-      }
-      *nptr = n;
-    } while(*pHostname != 0);
-    *query++='\0';
-
-    /* fill dns query */
-    qry.type = PP_HTONS(DNS_RRTYPE_A);
-    qry.cls = PP_HTONS(DNS_RRCLASS_IN);
-    SMEMCPY(query, &qry, SIZEOF_DNS_QUERY);
-
-    /* resize pbuf to the exact dns query */
-    pbuf_realloc(p, (u16_t)((query + SIZEOF_DNS_QUERY) - ((char*)(p->payload))));
-
-    /* connect to the server for faster receiving */
-    udp_connect(dns_pcb, &dns_servers[numdns], DNS_SERVER_PORT);
-    /* send dns packet */
-    err = udp_sendto(dns_pcb, p, &dns_servers[numdns], DNS_SERVER_PORT);
-
-    /* free pbuf */
-    pbuf_free(p);
-  } else {
-    err = ERR_MEM;
-  }
-
-  return err;
-}
-
-/**
- * dns_check_entry() - see if pEntry has not yet been queried and, if so, sends out a query.
- * Check an entry in the dns_table:
- * - send out query for new entries
- * - retry old pending entries on timeout (also with different servers)
- * - remove completed entries from the table if their TTL has expired
- *
- * @param i index of the dns_table entry to check
- */
-static void
-dns_check_entry(u8_t i)
-{
-  err_t err;
-  struct dns_table_entry *pEntry = &dns_table[i];
-
-  LWIP_ASSERT("array index out of bounds", i < DNS_TABLE_SIZE);
-
-  switch(pEntry->state) {
-
-    case DNS_STATE_NEW: {
-      /* initialize new entry */
-      pEntry->state   = DNS_STATE_ASKING;
-      pEntry->numdns  = 0;
-      pEntry->tmr     = 1;
-      pEntry->retries = 0;
-      
-      /* send DNS packet for this entry */
-      err = dns_send(pEntry->numdns, pEntry->name, i);
-      if (err != ERR_OK) {
-        LWIP_DEBUGF(DNS_DEBUG | LWIP_DBG_LEVEL_WARNING,
-                    ("dns_send returned error: %s\n", lwip_strerr(err)));
-      }
-      break;
-    }
-
-    case DNS_STATE_ASKING: {
-      if (--pEntry->tmr == 0) {
-        if (++pEntry->retries == DNS_MAX_RETRIES) {
-          if ((pEntry->numdns+1<DNS_MAX_SERVERS) && !ip_addr_isany(&dns_servers[pEntry->numdns+1])) {
-            /* change of server */
-            pEntry->numdns++;
-            pEntry->tmr     = 1;
-            pEntry->retries = 0;
-            break;
-          } else {
-            LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entry: \"%s\": timeout\n", pEntry->name));
-            /* call specified callback function if provided */
-            if (pEntry->found)
-              (*pEntry->found)(pEntry->name, NULL, pEntry->arg);
-            /* flush this entry */
-            pEntry->state   = DNS_STATE_UNUSED;
-            pEntry->found   = NULL;
-            break;
-          }
-        }
-
-        /* wait longer for the next retry */
-        pEntry->tmr = pEntry->retries;
-
-        /* send DNS packet for this entry */
-        err = dns_send(pEntry->numdns, pEntry->name, i);
-        if (err != ERR_OK) {
-          LWIP_DEBUGF(DNS_DEBUG | LWIP_DBG_LEVEL_WARNING,
-                      ("dns_send returned error: %s\n", lwip_strerr(err)));
-        }
-      }
-      break;
-    }
-
-    case DNS_STATE_DONE: {
-      /* if the time to live is nul */
-      if (--pEntry->ttl == 0) {
-        LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entry: \"%s\": flush\n", pEntry->name));
-        /* flush this entry */
-        pEntry->state = DNS_STATE_UNUSED;
-        pEntry->found = NULL;
-      }
-      break;
-    }
-    case DNS_STATE_UNUSED:
-      /* nothing to do */
-      break;
-    default:
-      LWIP_ASSERT("unknown dns_table entry state:", 0);
-      break;
-  }
-}
-
-/**
- * Call dns_check_entry for each entry in dns_table - check all entries.
- */
-static void
-dns_check_entries(void)
-{
-  u8_t i;
-
-  for (i = 0; i < DNS_TABLE_SIZE; ++i) {
-    dns_check_entry(i);
-  }
-}
-
-/**
- * Receive input function for DNS response packets arriving for the dns UDP pcb.
- *
- * @params see udp.h
- */
-static void
-dns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port)
-{
-  u16_t i;
-  char *pHostname;
-  struct dns_hdr *hdr;
-  struct dns_answer ans;
-  struct dns_table_entry *pEntry;
-  u16_t nquestions, nanswers;
-
-  LWIP_UNUSED_ARG(arg);
-  LWIP_UNUSED_ARG(pcb);
-  LWIP_UNUSED_ARG(addr);
-  LWIP_UNUSED_ARG(port);
-
-  /* is the dns message too big ? */
-  if (p->tot_len > DNS_MSG_SIZE) {
-    LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: pbuf too big\n"));
-    /* free pbuf and return */
-    goto memerr;
-  }
-
-  /* is the dns message big enough ? */
-  if (p->tot_len < (SIZEOF_DNS_HDR + SIZEOF_DNS_QUERY + SIZEOF_DNS_ANSWER)) {
-    LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: pbuf too small\n"));
-    /* free pbuf and return */
-    goto memerr;
-  }
-
-  /* copy dns payload inside static buffer for processing */ 
-  if (pbuf_copy_partial(p, dns_payload, p->tot_len, 0) == p->tot_len) {
-    /* The ID in the DNS header should be our entry into the name table. */
-    hdr = (struct dns_hdr*)dns_payload;
-    i = htons(hdr->id);
-    if (i < DNS_TABLE_SIZE) {
-      pEntry = &dns_table[i];
-      if(pEntry->state == DNS_STATE_ASKING) {
-        /* This entry is now completed. */
-        pEntry->state = DNS_STATE_DONE;
-        pEntry->err   = hdr->flags2 & DNS_FLAG2_ERR_MASK;
-
-        /* We only care about the question(s) and the answers. The authrr
-           and the extrarr are simply discarded. */
-        nquestions = htons(hdr->numquestions);
-        nanswers   = htons(hdr->numanswers);
-
-        /* Check for error. If so, call callback to inform. */
-        if (((hdr->flags1 & DNS_FLAG1_RESPONSE) == 0) || (pEntry->err != 0) || (nquestions != 1)) {
-          LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": error in flags\n", pEntry->name));
-          /* call callback to indicate error, clean up memory and return */
-          goto responseerr;
-        }
-
-#if DNS_DOES_NAME_CHECK
-        /* Check if the name in the "question" part match with the name in the entry. */
-        if (dns_compare_name((unsigned char *)(pEntry->name), (unsigned char *)dns_payload + SIZEOF_DNS_HDR) != 0) {
-          LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response not match to query\n", pEntry->name));
-          /* call callback to indicate error, clean up memory and return */
-          goto responseerr;
-        }
-#endif /* DNS_DOES_NAME_CHECK */
-
-        /* Skip the name in the "question" part */
-        pHostname = (char *) dns_parse_name((unsigned char *)dns_payload + SIZEOF_DNS_HDR) + SIZEOF_DNS_QUERY;
-
-        while (nanswers > 0) {
-          /* skip answer resource record's host name */
-          pHostname = (char *) dns_parse_name((unsigned char *)pHostname);
-
-          /* Check for IP address type and Internet class. Others are discarded. */
-          SMEMCPY(&ans, pHostname, SIZEOF_DNS_ANSWER);
-          if((ans.type == PP_HTONS(DNS_RRTYPE_A)) && (ans.cls == PP_HTONS(DNS_RRCLASS_IN)) &&
-             (ans.len == PP_HTONS(sizeof(ip_addr_t))) ) {
-            /* read the answer resource record's TTL, and maximize it if needed */
-            pEntry->ttl = ntohl(ans.ttl);
-            if (pEntry->ttl > DNS_MAX_TTL) {
-              pEntry->ttl = DNS_MAX_TTL;
-            }
-            /* read the IP address after answer resource record's header */
-            SMEMCPY(&(pEntry->ipaddr), (pHostname+SIZEOF_DNS_ANSWER), sizeof(ip_addr_t));
-            LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response = ", pEntry->name));
-            ip_addr_debug_print(DNS_DEBUG, (&(pEntry->ipaddr)));
-            LWIP_DEBUGF(DNS_DEBUG, ("\n"));
-            /* call specified callback function if provided */
-            if (pEntry->found) {
-              (*pEntry->found)(pEntry->name, &pEntry->ipaddr, pEntry->arg);
-            }
-            /* deallocate memory and return */
-            goto memerr;
-          } else {
-            pHostname = pHostname + SIZEOF_DNS_ANSWER + htons(ans.len);
-          }
-          --nanswers;
-        }
-        LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": error in response\n", pEntry->name));
-        /* call callback to indicate error, clean up memory and return */
-        goto responseerr;
-      }
-    }
-  }
-
-  /* deallocate memory and return */
-  goto memerr;
-
-responseerr:
-  /* ERROR: call specified callback function with NULL as name to indicate an error */
-  if (pEntry->found) {
-    (*pEntry->found)(pEntry->name, NULL, pEntry->arg);
-  }
-  /* flush this entry */
-  pEntry->state = DNS_STATE_UNUSED;
-  pEntry->found = NULL;
-
-memerr:
-  /* free pbuf */
-  pbuf_free(p);
-  return;
-}
-
-/**
- * Queues a new hostname to resolve and sends out a DNS query for that hostname
- *
- * @param name the hostname that is to be queried
- * @param found a callback founction to be called on success, failure or timeout
- * @param callback_arg argument to pass to the callback function
- * @return @return a err_t return code.
- */
-static err_t
-dns_enqueue(const char *name, dns_found_callback found, void *callback_arg)
-{
-  u8_t i;
-  u8_t lseq, lseqi;
-  struct dns_table_entry *pEntry = NULL;
-  size_t namelen;
-
-  /* search an unused entry, or the oldest one */
-  lseq = lseqi = 0;
-  for (i = 0; i < DNS_TABLE_SIZE; ++i) {
-    pEntry = &dns_table[i];
-    /* is it an unused entry ? */
-    if (pEntry->state == DNS_STATE_UNUSED)
-      break;
-
-    /* check if this is the oldest completed entry */
-    if (pEntry->state == DNS_STATE_DONE) {
-      if ((dns_seqno - pEntry->seqno) > lseq) {
-        lseq = dns_seqno - pEntry->seqno;
-        lseqi = i;
-      }
-    }
-  }
-
-  /* if we don't have found an unused entry, use the oldest completed one */
-  if (i == DNS_TABLE_SIZE) {
-    if ((lseqi >= DNS_TABLE_SIZE) || (dns_table[lseqi].state != DNS_STATE_DONE)) {
-      /* no entry can't be used now, table is full */
-      LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": DNS entries table is full\n", name));
-      return ERR_MEM;
-    } else {
-      /* use the oldest completed one */
-      i = lseqi;
-      pEntry = &dns_table[i];
-    }
-  }
-
-  /* use this entry */
-  LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": use DNS entry %"U16_F"\n", name, (u16_t)(i)));
-
-  /* fill the entry */
-  pEntry->state = DNS_STATE_NEW;
-  pEntry->seqno = dns_seqno++;
-  pEntry->found = found;
-  pEntry->arg   = callback_arg;
-  namelen = LWIP_MIN(strlen(name), DNS_MAX_NAME_LENGTH-1);
-  MEMCPY(pEntry->name, name, namelen);
-  pEntry->name[namelen] = 0;
-
-  /* force to send query without waiting timer */
-  dns_check_entry(i);
-
-  /* dns query is enqueued */
-  return ERR_INPROGRESS;
-}
-
-/**
- * Resolve a hostname (string) into an IP address.
- * NON-BLOCKING callback version for use with raw API!!!
- *
- * Returns immediately with one of err_t return codes:
- * - ERR_OK if hostname is a valid IP address string or the host
- *   name is already in the local names table.
- * - ERR_INPROGRESS enqueue a request to be sent to the DNS server
- *   for resolution if no errors are present.
- * - ERR_ARG: dns client not initialized or invalid hostname
- *
- * @param hostname the hostname that is to be queried
- * @param addr pointer to a ip_addr_t where to store the address if it is already
- *             cached in the dns_table (only valid if ERR_OK is returned!)
- * @param found a callback function to be called on success, failure or timeout (only if
- *              ERR_INPROGRESS is returned!)
- * @param callback_arg argument to pass to the callback function
- * @return a err_t return code.
- */
-err_t
-dns_gethostbyname(const char *hostname, ip_addr_t *addr, dns_found_callback found,
-                  void *callback_arg)
-{
-  u32_t ipaddr;
-  /* not initialized or no valid server yet, or invalid addr pointer
-   * or invalid hostname or invalid hostname length */
-  if ((dns_pcb == NULL) || (addr == NULL) ||
-      (!hostname) || (!hostname[0]) ||
-      (strlen(hostname) >= DNS_MAX_NAME_LENGTH)) {
-    return ERR_ARG;
-  }
-
-#if LWIP_HAVE_LOOPIF
-  if (strcmp(hostname, "localhost")==0) {
-    ip_addr_set_loopback(addr);
-    return ERR_OK;
-  }
-#endif /* LWIP_HAVE_LOOPIF */
-
-  /* host name already in octet notation? set ip addr and return ERR_OK */
-  ipaddr = ipaddr_addr(hostname);
-  if (ipaddr == IPADDR_NONE) {
-    /* already have this address cached? */
-    ipaddr = dns_lookup(hostname);
-  }
-  if (ipaddr != IPADDR_NONE) {
-    ip4_addr_set_u32(addr, ipaddr);
-    return ERR_OK;
-  }
-
-  /* queue query with specified callback */
-  return dns_enqueue(hostname, found, callback_arg);
-}
-
-#endif /* LWIP_DNS */
+/**
+ * @file
+ * DNS - host name to IP address resolver.
+ *
+ * @defgroup dns DNS
+ * @ingroup callbackstyle_api
+ *
+ * Implements a DNS host name to IP address resolver.
+ *
+ * The lwIP DNS resolver functions are used to lookup a host name and
+ * map it to a numerical IP address. It maintains a list of resolved
+ * hostnames that can be queried with the dns_lookup() function.
+ * New hostnames can be resolved using the dns_query() function.
+ *
+ * The lwIP version of the resolver also adds a non-blocking version of
+ * gethostbyname() that will work with a raw API application. This function
+ * checks for an IP address string first and converts it if it is valid.
+ * gethostbyname() then does a dns_lookup() to see if the name is
+ * already in the table. If so, the IP is returned. If not, a query is
+ * issued and the function returns with a ERR_INPROGRESS status. The app
+ * using the dns client must then go into a waiting state.
+ *
+ * Once a hostname has been resolved (or found to be non-existent),
+ * the resolver code calls a specified callback function (which
+ * must be implemented by the module that uses the resolver).
+ * 
+ * Multicast DNS queries are supported for names ending on ".local".
+ * However, only "One-Shot Multicast DNS Queries" are supported (RFC 6762
+ * chapter 5.1), this is not a fully compliant implementation of continuous
+ * mDNS querying!
+ *
+ * All functions must be called from TCPIP thread.
+ * 
+ * @see @ref netconn_common for thread-safe access.
+ */
+
+/*
+ * Port to lwIP from uIP
+ * by Jim Pettinato April 2007
+ *
+ * security fixes and more by Simon Goldschmidt
+ *
+ * uIP version Copyright (c) 2002-2003, Adam Dunkels.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*-----------------------------------------------------------------------------
+ * RFC 1035 - Domain names - implementation and specification
+ * RFC 2181 - Clarifications to the DNS Specification
+ *----------------------------------------------------------------------------*/
+
+/** @todo: define good default values (rfc compliance) */
+/** @todo: improve answer parsing, more checkings... */
+/** @todo: check RFC1035 - 7.3. Processing responses */
+/** @todo: one-shot mDNS: dual-stack fallback to another IP version */
+
+/*-----------------------------------------------------------------------------
+ * Includes
+ *----------------------------------------------------------------------------*/
+
+#include "lwip/opt.h"
+
+#if LWIP_DNS /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/def.h"
+#include "lwip/udp.h"
+#include "lwip/mem.h"
+#include "lwip/memp.h"
+#include "lwip/dns.h"
+#include "lwip/prot/dns.h"
+
+#include <string.h>
+
+/** Random generator function to create random TXIDs and source ports for queries */
+#ifndef DNS_RAND_TXID
+#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_XID) != 0)
+#define DNS_RAND_TXID LWIP_RAND
+#else
+static u16_t dns_txid;
+#define DNS_RAND_TXID() (++dns_txid)
+#endif
+#endif
+
+/** Limits the source port to be >= 1024 by default */
+#ifndef DNS_PORT_ALLOWED
+#define DNS_PORT_ALLOWED(port) ((port) >= 1024)
+#endif
+
+/** DNS maximum number of retries when asking for a name, before "timeout". */
+#ifndef DNS_MAX_RETRIES
+#define DNS_MAX_RETRIES           4
+#endif
+
+/** DNS resource record max. TTL (one week as default) */
+#ifndef DNS_MAX_TTL
+#define DNS_MAX_TTL               604800
+#elif DNS_MAX_TTL > 0x7FFFFFFF
+#error DNS_MAX_TTL must be a positive 32-bit value
+#endif
+
+#if DNS_TABLE_SIZE > 255
+#error DNS_TABLE_SIZE must fit into an u8_t
+#endif
+#if DNS_MAX_SERVERS > 255
+#error DNS_MAX_SERVERS must fit into an u8_t
+#endif
+
+/* The number of parallel requests (i.e. calls to dns_gethostbyname
+ * that cannot be answered from the DNS table.
+ * This is set to the table size by default.
+ */
+#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING) != 0)
+#ifndef DNS_MAX_REQUESTS
+#define DNS_MAX_REQUESTS          DNS_TABLE_SIZE
+#else
+#if DNS_MAX_REQUESTS > 255
+#error DNS_MAX_REQUESTS must fit into an u8_t
+#endif
+#endif
+#else
+/* In this configuration, both arrays have to have the same size and are used
+ * like one entry (used/free) */
+#define DNS_MAX_REQUESTS          DNS_TABLE_SIZE
+#endif
+
+/* The number of UDP source ports used in parallel */
+#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0)
+#ifndef DNS_MAX_SOURCE_PORTS
+#define DNS_MAX_SOURCE_PORTS      DNS_MAX_REQUESTS
+#else
+#if DNS_MAX_SOURCE_PORTS > 255
+#error DNS_MAX_SOURCE_PORTS must fit into an u8_t
+#endif
+#endif
+#else
+#ifdef DNS_MAX_SOURCE_PORTS
+#undef DNS_MAX_SOURCE_PORTS
+#endif
+#define DNS_MAX_SOURCE_PORTS      1
+#endif
+
+#if LWIP_IPV4 && LWIP_IPV6
+#define LWIP_DNS_ADDRTYPE_IS_IPV6(t) (((t) == LWIP_DNS_ADDRTYPE_IPV6_IPV4) || ((t) == LWIP_DNS_ADDRTYPE_IPV6))
+#define LWIP_DNS_ADDRTYPE_MATCH_IP(t, ip) (IP_IS_V6_VAL(ip) ? LWIP_DNS_ADDRTYPE_IS_IPV6(t) : (!LWIP_DNS_ADDRTYPE_IS_IPV6(t)))
+#define LWIP_DNS_ADDRTYPE_ARG(x) , x
+#define LWIP_DNS_ADDRTYPE_ARG_OR_ZERO(x) x
+#define LWIP_DNS_SET_ADDRTYPE(x, y) do { x = y; } while(0)
+#else
+#if LWIP_IPV6
+#define LWIP_DNS_ADDRTYPE_IS_IPV6(t) 1
+#else
+#define LWIP_DNS_ADDRTYPE_IS_IPV6(t) 0
+#endif
+#define LWIP_DNS_ADDRTYPE_MATCH_IP(t, ip) 1
+#define LWIP_DNS_ADDRTYPE_ARG(x)
+#define LWIP_DNS_ADDRTYPE_ARG_OR_ZERO(x) 0
+#define LWIP_DNS_SET_ADDRTYPE(x, y)
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
+#if LWIP_DNS_SUPPORT_MDNS_QUERIES
+#define LWIP_DNS_ISMDNS_ARG(x) , x
+#else
+#define LWIP_DNS_ISMDNS_ARG(x)
+#endif
+
+/** DNS query message structure.
+    No packing needed: only used locally on the stack. */
+struct dns_query {
+  /* DNS query record starts with either a domain name or a pointer
+     to a name already present somewhere in the packet. */
+  u16_t type;
+  u16_t cls;
+};
+#define SIZEOF_DNS_QUERY 4
+
+/** DNS answer message structure.
+    No packing needed: only used locally on the stack. */
+struct dns_answer {
+  /* DNS answer record starts with either a domain name or a pointer
+     to a name already present somewhere in the packet. */
+  u16_t type;
+  u16_t cls;
+  u32_t ttl;
+  u16_t len;
+};
+#define SIZEOF_DNS_ANSWER 10
+/* maximum allowed size for the struct due to non-packed */
+#define SIZEOF_DNS_ANSWER_ASSERT 12
+
+/* DNS table entry states */
+typedef enum {
+  DNS_STATE_UNUSED           = 0,
+  DNS_STATE_NEW              = 1,
+  DNS_STATE_ASKING           = 2,
+  DNS_STATE_DONE             = 3
+} dns_state_enum_t;
+
+/** DNS table entry */
+struct dns_table_entry {
+  u32_t ttl;
+  ip_addr_t ipaddr;
+  u16_t txid;
+  u8_t  state;
+  u8_t  server_idx;
+  u8_t  tmr;
+  u8_t  retries;
+  u8_t  seqno;
+#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0)
+  u8_t pcb_idx;
+#endif
+  char name[DNS_MAX_NAME_LENGTH];
+#if LWIP_IPV4 && LWIP_IPV6
+  u8_t reqaddrtype;
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+#if LWIP_DNS_SUPPORT_MDNS_QUERIES
+  u8_t is_mdns;
+#endif
+};
+
+/** DNS request table entry: used when dns_gehostbyname cannot answer the
+ * request from the DNS table */
+struct dns_req_entry {
+  /* pointer to callback on DNS query done */
+  dns_found_callback found;
+  /* argument passed to the callback function */
+  void *arg;
+#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING) != 0)
+  u8_t dns_table_idx;
+#endif
+#if LWIP_IPV4 && LWIP_IPV6
+  u8_t reqaddrtype;
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+};
+
+#if DNS_LOCAL_HOSTLIST
+
+#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC
+/** Local host-list. For hostnames in this list, no
+ *  external name resolution is performed */
+static struct local_hostlist_entry *local_hostlist_dynamic;
+#else /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
+
+/** Defining this allows the local_hostlist_static to be placed in a different
+ * linker section (e.g. FLASH) */
+#ifndef DNS_LOCAL_HOSTLIST_STORAGE_PRE
+#define DNS_LOCAL_HOSTLIST_STORAGE_PRE static
+#endif /* DNS_LOCAL_HOSTLIST_STORAGE_PRE */
+/** Defining this allows the local_hostlist_static to be placed in a different
+ * linker section (e.g. FLASH) */
+#ifndef DNS_LOCAL_HOSTLIST_STORAGE_POST
+#define DNS_LOCAL_HOSTLIST_STORAGE_POST
+#endif /* DNS_LOCAL_HOSTLIST_STORAGE_POST */
+DNS_LOCAL_HOSTLIST_STORAGE_PRE struct local_hostlist_entry local_hostlist_static[]
+  DNS_LOCAL_HOSTLIST_STORAGE_POST = DNS_LOCAL_HOSTLIST_INIT;
+
+#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
+
+static void dns_init_local(void);
+static err_t dns_lookup_local(const char *hostname, ip_addr_t *addr LWIP_DNS_ADDRTYPE_ARG(u8_t dns_addrtype));
+#endif /* DNS_LOCAL_HOSTLIST */
+
+
+/* forward declarations */
+static void dns_recv(void *s, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port);
+static void dns_check_entries(void);
+static void dns_call_found(u8_t idx, ip_addr_t* addr);
+
+/*-----------------------------------------------------------------------------
+ * Globals
+ *----------------------------------------------------------------------------*/
+
+/* DNS variables */
+static struct udp_pcb        *dns_pcbs[DNS_MAX_SOURCE_PORTS];
+#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0)
+static u8_t                   dns_last_pcb_idx;
+#endif
+static u8_t                   dns_seqno;
+static struct dns_table_entry dns_table[DNS_TABLE_SIZE];
+static struct dns_req_entry   dns_requests[DNS_MAX_REQUESTS];
+static ip_addr_t              dns_servers[DNS_MAX_SERVERS];
+
+#if LWIP_IPV4
+const ip_addr_t dns_mquery_v4group = DNS_MQUERY_IPV4_GROUP_INIT;
+#endif /* LWIP_IPV4 */
+#if LWIP_IPV6
+const ip_addr_t dns_mquery_v6group = DNS_MQUERY_IPV6_GROUP_INIT;
+#endif /* LWIP_IPV6 */
+
+/**
+ * Initialize the resolver: set up the UDP pcb and configure the default server
+ * (if DNS_SERVER_ADDRESS is set).
+ */
+void
+dns_init(void)
+{
+#ifdef DNS_SERVER_ADDRESS
+  /* initialize default DNS server address */
+  ip_addr_t dnsserver;
+  DNS_SERVER_ADDRESS(&dnsserver);
+  dns_setserver(0, &dnsserver);
+#endif /* DNS_SERVER_ADDRESS */
+
+  LWIP_ASSERT("sanity check SIZEOF_DNS_QUERY",
+    sizeof(struct dns_query) == SIZEOF_DNS_QUERY);
+  LWIP_ASSERT("sanity check SIZEOF_DNS_ANSWER",
+    sizeof(struct dns_answer) <= SIZEOF_DNS_ANSWER_ASSERT);
+
+  LWIP_DEBUGF(DNS_DEBUG, ("dns_init: initializing\n"));
+
+  /* if dns client not yet initialized... */
+#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) == 0)
+  if (dns_pcbs[0] == NULL) {
+    dns_pcbs[0] = udp_new_ip_type(IPADDR_TYPE_ANY);
+    LWIP_ASSERT("dns_pcbs[0] != NULL", dns_pcbs[0] != NULL);
+
+    /* initialize DNS table not needed (initialized to zero since it is a
+     * global variable) */
+    LWIP_ASSERT("For implicit initialization to work, DNS_STATE_UNUSED needs to be 0",
+      DNS_STATE_UNUSED == 0);
+
+    /* initialize DNS client */
+    udp_bind(dns_pcbs[0], IP_ANY_TYPE, 0);
+    udp_recv(dns_pcbs[0], dns_recv, NULL);
+  }
+#endif
+
+#if DNS_LOCAL_HOSTLIST
+  dns_init_local();
+#endif
+}
+
+/**
+ * @ingroup dns
+ * Initialize one of the DNS servers.
+ *
+ * @param numdns the index of the DNS server to set must be < DNS_MAX_SERVERS
+ * @param dnsserver IP address of the DNS server to set
+ */
+void
+dns_setserver(u8_t numdns, const ip_addr_t *dnsserver)
+{
+  if (numdns < DNS_MAX_SERVERS) {
+    if (dnsserver != NULL) {
+      dns_servers[numdns] = (*dnsserver);
+    } else {
+      dns_servers[numdns] = *IP_ADDR_ANY;
+    }
+  }
+}
+
+/**
+ * @ingroup dns
+ * Obtain one of the currently configured DNS server.
+ *
+ * @param numdns the index of the DNS server
+ * @return IP address of the indexed DNS server or "ip_addr_any" if the DNS
+ *         server has not been configured.
+ */
+const ip_addr_t*
+dns_getserver(u8_t numdns)
+{
+  if (numdns < DNS_MAX_SERVERS) {
+    return &dns_servers[numdns];
+  } else {
+    return IP_ADDR_ANY;
+  }
+}
+
+/**
+ * The DNS resolver client timer - handle retries and timeouts and should
+ * be called every DNS_TMR_INTERVAL milliseconds (every second by default).
+ */
+void
+dns_tmr(void)
+{
+  LWIP_DEBUGF(DNS_DEBUG, ("dns_tmr: dns_check_entries\n"));
+  dns_check_entries();
+}
+
+#if DNS_LOCAL_HOSTLIST
+static void
+dns_init_local(void)
+{
+#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC && defined(DNS_LOCAL_HOSTLIST_INIT)
+  size_t i;
+  struct local_hostlist_entry *entry;
+  /* Dynamic: copy entries from DNS_LOCAL_HOSTLIST_INIT to list */
+  struct local_hostlist_entry local_hostlist_init[] = DNS_LOCAL_HOSTLIST_INIT;
+  size_t namelen;
+  for (i = 0; i < LWIP_ARRAYSIZE(local_hostlist_init); i++) {
+    struct local_hostlist_entry *init_entry = &local_hostlist_init[i];
+    LWIP_ASSERT("invalid host name (NULL)", init_entry->name != NULL);
+    namelen = strlen(init_entry->name);
+    LWIP_ASSERT("namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN", namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN);
+    entry = (struct local_hostlist_entry *)memp_malloc(MEMP_LOCALHOSTLIST);
+    LWIP_ASSERT("mem-error in dns_init_local", entry != NULL);
+    if (entry != NULL) {
+      char* entry_name = (char*)entry + sizeof(struct local_hostlist_entry);
+      MEMCPY(entry_name, init_entry->name, namelen);
+      entry_name[namelen] = 0;
+      entry->name = entry_name;
+      entry->addr = init_entry->addr;
+      entry->next = local_hostlist_dynamic;
+      local_hostlist_dynamic = entry;
+    }
+  }
+#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC && defined(DNS_LOCAL_HOSTLIST_INIT) */
+}
+
+/**
+ * @ingroup dns
+ * Iterate the local host-list for a hostname.
+ *
+ * @param iterator_fn a function that is called for every entry in the local host-list
+ * @param iterator_arg 3rd argument passed to iterator_fn
+ * @return the number of entries in the local host-list
+ */
+size_t
+dns_local_iterate(dns_found_callback iterator_fn, void *iterator_arg)
+{
+  size_t i;
+#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC
+  struct local_hostlist_entry *entry = local_hostlist_dynamic;
+  i = 0;
+  while (entry != NULL) {
+    if (iterator_fn != NULL) {
+      iterator_fn(entry->name, &entry->addr, iterator_arg);
+    }
+    i++;
+    entry = entry->next;
+  }
+#else /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
+  for (i = 0; i < LWIP_ARRAYSIZE(local_hostlist_static); i++) {
+    if (iterator_fn != NULL) {
+      iterator_fn(local_hostlist_static[i].name, &local_hostlist_static[i].addr, iterator_arg);
+    }
+  }
+#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
+  return i;
+}
+
+/**
+ * @ingroup dns
+ * Scans the local host-list for a hostname.
+ *
+ * @param hostname Hostname to look for in the local host-list
+ * @param addr the first IP address for the hostname in the local host-list or
+ *         IPADDR_NONE if not found.
+ * @param dns_addrtype - LWIP_DNS_ADDRTYPE_IPV4_IPV6: try to resolve IPv4 (ATTENTION: no fallback here!)
+ *                     - LWIP_DNS_ADDRTYPE_IPV6_IPV4: try to resolve IPv6 (ATTENTION: no fallback here!)
+ *                     - LWIP_DNS_ADDRTYPE_IPV4: try to resolve IPv4 only
+ *                     - LWIP_DNS_ADDRTYPE_IPV6: try to resolve IPv6 only
+ * @return ERR_OK if found, ERR_ARG if not found
+ */
+err_t
+dns_local_lookup(const char *hostname, ip_addr_t *addr, u8_t dns_addrtype)
+{
+  LWIP_UNUSED_ARG(dns_addrtype);
+  return dns_lookup_local(hostname, addr LWIP_DNS_ADDRTYPE_ARG(dns_addrtype));
+}
+
+/* Internal implementation for dns_local_lookup and dns_lookup */
+static err_t
+dns_lookup_local(const char *hostname, ip_addr_t *addr LWIP_DNS_ADDRTYPE_ARG(u8_t dns_addrtype))
+{
+#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC
+  struct local_hostlist_entry *entry = local_hostlist_dynamic;
+  while (entry != NULL) {
+    if ((lwip_stricmp(entry->name, hostname) == 0) &&
+        LWIP_DNS_ADDRTYPE_MATCH_IP(dns_addrtype, entry->addr)) {
+      if (addr) {
+        ip_addr_copy(*addr, entry->addr);
+      }
+      return ERR_OK;
+    }
+    entry = entry->next;
+  }
+#else /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
+  size_t i;
+  for (i = 0; i < LWIP_ARRAYSIZE(local_hostlist_static); i++) {
+    if ((lwip_stricmp(local_hostlist_static[i].name, hostname) == 0) &&
+        LWIP_DNS_ADDRTYPE_MATCH_IP(dns_addrtype, local_hostlist_static[i].addr)) {
+      if (addr) {
+        ip_addr_copy(*addr, local_hostlist_static[i].addr);
+      }
+      return ERR_OK;
+    }
+  }
+#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
+  return ERR_ARG;
+}
+
+#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC
+/**
+ * @ingroup dns
+ * Remove all entries from the local host-list for a specific hostname
+ * and/or IP address
+ *
+ * @param hostname hostname for which entries shall be removed from the local
+ *                 host-list
+ * @param addr address for which entries shall be removed from the local host-list
+ * @return the number of removed entries
+ */
+int
+dns_local_removehost(const char *hostname, const ip_addr_t *addr)
+{
+  int removed = 0;
+  struct local_hostlist_entry *entry = local_hostlist_dynamic;
+  struct local_hostlist_entry *last_entry = NULL;
+  while (entry != NULL) {
+    if (((hostname == NULL) || !lwip_stricmp(entry->name, hostname)) &&
+        ((addr == NULL) || ip_addr_cmp(&entry->addr, addr))) {
+      struct local_hostlist_entry *free_entry;
+      if (last_entry != NULL) {
+        last_entry->next = entry->next;
+      } else {
+        local_hostlist_dynamic = entry->next;
+      }
+      free_entry = entry;
+      entry = entry->next;
+      memp_free(MEMP_LOCALHOSTLIST, free_entry);
+      removed++;
+    } else {
+      last_entry = entry;
+      entry = entry->next;
+    }
+  }
+  return removed;
+}
+
+/**
+ * @ingroup dns
+ * Add a hostname/IP address pair to the local host-list.
+ * Duplicates are not checked.
+ *
+ * @param hostname hostname of the new entry
+ * @param addr IP address of the new entry
+ * @return ERR_OK if succeeded or ERR_MEM on memory error
+ */
+err_t
+dns_local_addhost(const char *hostname, const ip_addr_t *addr)
+{
+  struct local_hostlist_entry *entry;
+  size_t namelen;
+  char* entry_name;
+  LWIP_ASSERT("invalid host name (NULL)", hostname != NULL);
+  namelen = strlen(hostname);
+  LWIP_ASSERT("namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN", namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN);
+  entry = (struct local_hostlist_entry *)memp_malloc(MEMP_LOCALHOSTLIST);
+  if (entry == NULL) {
+    return ERR_MEM;
+  }
+  entry_name = (char*)entry + sizeof(struct local_hostlist_entry);
+  MEMCPY(entry_name, hostname, namelen);
+  entry_name[namelen] = 0;
+  entry->name = entry_name;
+  ip_addr_copy(entry->addr, *addr);
+  entry->next = local_hostlist_dynamic;
+  local_hostlist_dynamic = entry;
+  return ERR_OK;
+}
+#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC*/
+#endif /* DNS_LOCAL_HOSTLIST */
+
+/**
+ * @ingroup dns
+ * Look up a hostname in the array of known hostnames.
+ *
+ * @note This function only looks in the internal array of known
+ * hostnames, it does not send out a query for the hostname if none
+ * was found. The function dns_enqueue() can be used to send a query
+ * for a hostname.
+ *
+ * @param name the hostname to look up
+ * @param addr the hostname's IP address, as u32_t (instead of ip_addr_t to
+ *         better check for failure: != IPADDR_NONE) or IPADDR_NONE if the hostname
+ *         was not found in the cached dns_table.
+ * @return ERR_OK if found, ERR_ARG if not found
+ */
+static err_t
+dns_lookup(const char *name, ip_addr_t *addr LWIP_DNS_ADDRTYPE_ARG(u8_t dns_addrtype))
+{
+  u8_t i;
+#if DNS_LOCAL_HOSTLIST || defined(DNS_LOOKUP_LOCAL_EXTERN)
+#endif /* DNS_LOCAL_HOSTLIST || defined(DNS_LOOKUP_LOCAL_EXTERN) */
+#if DNS_LOCAL_HOSTLIST
+  if (dns_lookup_local(name, addr LWIP_DNS_ADDRTYPE_ARG(dns_addrtype)) == ERR_OK) {
+    return ERR_OK;
+  }
+#endif /* DNS_LOCAL_HOSTLIST */
+#ifdef DNS_LOOKUP_LOCAL_EXTERN
+  if (DNS_LOOKUP_LOCAL_EXTERN(name, addr, LWIP_DNS_ADDRTYPE_ARG_OR_ZERO(dns_addrtype)) == ERR_OK) {
+    return ERR_OK;
+  }
+#endif /* DNS_LOOKUP_LOCAL_EXTERN */
+
+  /* Walk through name list, return entry if found. If not, return NULL. */
+  for (i = 0; i < DNS_TABLE_SIZE; ++i) {
+    if ((dns_table[i].state == DNS_STATE_DONE) &&
+        (lwip_strnicmp(name, dns_table[i].name, sizeof(dns_table[i].name)) == 0) &&
+        LWIP_DNS_ADDRTYPE_MATCH_IP(dns_addrtype, dns_table[i].ipaddr)) {
+      LWIP_DEBUGF(DNS_DEBUG, ("dns_lookup: \"%s\": found = ", name));
+      ip_addr_debug_print(DNS_DEBUG, &(dns_table[i].ipaddr));
+      LWIP_DEBUGF(DNS_DEBUG, ("\n"));
+      if (addr) {
+        ip_addr_copy(*addr, dns_table[i].ipaddr);
+      }
+      return ERR_OK;
+    }
+  }
+
+  return ERR_ARG;
+}
+
+/**
+ * Compare the "dotted" name "query" with the encoded name "response"
+ * to make sure an answer from the DNS server matches the current dns_table
+ * entry (otherwise, answers might arrive late for hostname not on the list
+ * any more).
+ *
+ * @param query hostname (not encoded) from the dns_table
+ * @param p pbuf containing the encoded hostname in the DNS response
+ * @param start_offset offset into p where the name starts
+ * @return 0xFFFF: names differ, other: names equal -> offset behind name
+ */
+static u16_t
+dns_compare_name(const char *query, struct pbuf* p, u16_t start_offset)
+{
+  int n;
+  u16_t response_offset = start_offset;
+
+  do {
+    n = pbuf_try_get_at(p, response_offset++);
+    if (n < 0) {
+      return 0xFFFF;
+    }
+    /** @see RFC 1035 - 4.1.4. Message compression */
+    if ((n & 0xc0) == 0xc0) {
+      /* Compressed name: cannot be equal since we don't send them */
+      return 0xFFFF;
+    } else {
+      /* Not compressed name */
+      while (n > 0) {
+        int c = pbuf_try_get_at(p, response_offset);
+        if (c < 0) {
+          return 0xFFFF;
+        }
+        if ((*query) != (u8_t)c) {
+          return 0xFFFF;
+        }
+        ++response_offset;
+        ++query;
+        --n;
+      }
+      ++query;
+    }
+    n = pbuf_try_get_at(p, response_offset);
+    if (n < 0) {
+      return 0xFFFF;
+    }
+  } while (n != 0);
+
+  return response_offset + 1;
+}
+
+/**
+ * Walk through a compact encoded DNS name and return the end of the name.
+ *
+ * @param p pbuf containing the name
+ * @param query_idx start index into p pointing to encoded DNS name in the DNS server response
+ * @return index to end of the name
+ */
+static u16_t
+dns_skip_name(struct pbuf* p, u16_t query_idx)
+{
+  int n;
+  u16_t offset = query_idx;
+
+  do {
+    n = pbuf_try_get_at(p, offset++);
+    if (n < 0) {
+      return 0xFFFF;
+    }
+    /** @see RFC 1035 - 4.1.4. Message compression */
+    if ((n & 0xc0) == 0xc0) {
+      /* Compressed name: since we only want to skip it (not check it), stop here */
+      break;
+    } else {
+      /* Not compressed name */
+      if (offset + n >= p->tot_len) {
+        return 0xFFFF;
+      }
+      offset = (u16_t)(offset + n);
+    }
+    n = pbuf_try_get_at(p, offset);
+    if (n < 0) {
+      return 0xFFFF;
+    }
+  } while (n != 0);
+
+  return offset + 1;
+}
+
+/**
+ * Send a DNS query packet.
+ *
+ * @param idx the DNS table entry index for which to send a request
+ * @return ERR_OK if packet is sent; an err_t indicating the problem otherwise
+ */
+static err_t
+dns_send(u8_t idx)
+{
+  err_t err;
+  struct dns_hdr hdr;
+  struct dns_query qry;
+  struct pbuf *p;
+  u16_t query_idx, copy_len;
+  const char *hostname, *hostname_part;
+  u8_t n;
+  u8_t pcb_idx;
+  struct dns_table_entry* entry = &dns_table[idx];
+
+  LWIP_DEBUGF(DNS_DEBUG, ("dns_send: dns_servers[%"U16_F"] \"%s\": request\n",
+              (u16_t)(entry->server_idx), entry->name));
+  LWIP_ASSERT("dns server out of array", entry->server_idx < DNS_MAX_SERVERS);
+  if (ip_addr_isany_val(dns_servers[entry->server_idx])
+#if LWIP_DNS_SUPPORT_MDNS_QUERIES
+      && !entry->is_mdns
+#endif
+    ) {
+    /* DNS server not valid anymore, e.g. PPP netif has been shut down */
+    /* call specified callback function if provided */
+    dns_call_found(idx, NULL);
+    /* flush this entry */
+    entry->state = DNS_STATE_UNUSED;
+    return ERR_OK;
+  }
+
+  /* if here, we have either a new query or a retry on a previous query to process */
+  p = pbuf_alloc(PBUF_TRANSPORT, (u16_t)(SIZEOF_DNS_HDR + strlen(entry->name) + 2 +
+                 SIZEOF_DNS_QUERY), PBUF_RAM);
+  if (p != NULL) {
+    const ip_addr_t* dst;
+    u16_t dst_port;
+    /* fill dns header */
+    memset(&hdr, 0, SIZEOF_DNS_HDR);
+    hdr.id = lwip_htons(entry->txid);
+    hdr.flags1 = DNS_FLAG1_RD;
+    hdr.numquestions = PP_HTONS(1);
+    pbuf_take(p, &hdr, SIZEOF_DNS_HDR);
+    hostname = entry->name;
+    --hostname;
+
+    /* convert hostname into suitable query format. */
+    query_idx = SIZEOF_DNS_HDR;
+    do {
+      ++hostname;
+      hostname_part = hostname;
+      for (n = 0; *hostname != '.' && *hostname != 0; ++hostname) {
+        ++n;
+      }
+      copy_len = (u16_t)(hostname - hostname_part);
+      pbuf_put_at(p, query_idx, n);
+      pbuf_take_at(p, hostname_part, copy_len, query_idx + 1);
+      query_idx += n + 1;
+    } while (*hostname != 0);
+    pbuf_put_at(p, query_idx, 0);
+    query_idx++;
+
+    /* fill dns query */
+    if (LWIP_DNS_ADDRTYPE_IS_IPV6(entry->reqaddrtype)) {
+      qry.type = PP_HTONS(DNS_RRTYPE_AAAA);
+    } else {
+      qry.type = PP_HTONS(DNS_RRTYPE_A);
+    }
+    qry.cls = PP_HTONS(DNS_RRCLASS_IN);
+    pbuf_take_at(p, &qry, SIZEOF_DNS_QUERY, query_idx);
+
+#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0)
+    pcb_idx = entry->pcb_idx;
+#else
+    pcb_idx = 0;
+#endif
+    /* send dns packet */
+    LWIP_DEBUGF(DNS_DEBUG, ("sending DNS request ID %d for name \"%s\" to server %d\r\n",
+      entry->txid, entry->name, entry->server_idx));
+#if LWIP_DNS_SUPPORT_MDNS_QUERIES
+    if (entry->is_mdns) {
+      dst_port = DNS_MQUERY_PORT;
+#if LWIP_IPV6
+      if (LWIP_DNS_ADDRTYPE_IS_IPV6(entry->reqaddrtype))
+      {
+        dst = &dns_mquery_v6group;
+      }
+#endif
+#if LWIP_IPV4 && LWIP_IPV6
+      else
+#endif
+#if LWIP_IPV4
+      {
+        dst = &dns_mquery_v4group;
+      }
+#endif
+    } else
+#endif /* LWIP_DNS_SUPPORT_MDNS_QUERIES */
+    {
+      dst_port = DNS_SERVER_PORT;
+      dst = &dns_servers[entry->server_idx];
+    }
+    err = udp_sendto(dns_pcbs[pcb_idx], p, dst, dst_port);
+
+    /* free pbuf */
+    pbuf_free(p);
+  } else {
+    err = ERR_MEM;
+  }
+
+  return err;
+}
+
+#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0)
+static struct udp_pcb*
+dns_alloc_random_port(void)
+{
+  err_t err;
+  struct udp_pcb* ret;
+
+  ret = udp_new_ip_type(IPADDR_TYPE_ANY);
+  if (ret == NULL) {
+    /* out of memory, have to reuse an existing pcb */
+    return NULL;
+  }
+  do {
+    u16_t port = (u16_t)DNS_RAND_TXID();
+    if (!DNS_PORT_ALLOWED(port)) {
+      /* this port is not allowed, try again */
+      err = ERR_USE;
+      continue;
+    }
+    err = udp_bind(ret, IP_ANY_TYPE, port);
+  } while (err == ERR_USE);
+  if (err != ERR_OK) {
+    udp_remove(ret);
+    return NULL;
+  }
+  udp_recv(ret, dns_recv, NULL);
+  return ret;
+}
+
+/**
+ * dns_alloc_pcb() - allocates a new pcb (or reuses an existing one) to be used
+ * for sending a request
+ *
+ * @return an index into dns_pcbs
+ */
+static u8_t
+dns_alloc_pcb(void)
+{
+  u8_t i;
+  u8_t idx;
+
+  for (i = 0; i < DNS_MAX_SOURCE_PORTS; i++) {
+    if (dns_pcbs[i] == NULL) {
+      break;
+    }
+  }
+  if (i < DNS_MAX_SOURCE_PORTS) {
+    dns_pcbs[i] = dns_alloc_random_port();
+    if (dns_pcbs[i] != NULL) {
+      /* succeeded */
+      dns_last_pcb_idx = i;
+      return i;
+    }
+  }
+  /* if we come here, creating a new UDP pcb failed, so we have to use
+     an already existing one */
+  for (i = 0, idx = dns_last_pcb_idx + 1; i < DNS_MAX_SOURCE_PORTS; i++, idx++) {
+    if (idx >= DNS_MAX_SOURCE_PORTS) {
+      idx = 0;
+    }
+    if (dns_pcbs[idx] != NULL) {
+      dns_last_pcb_idx = idx;
+      return idx;
+    }
+  }
+  return DNS_MAX_SOURCE_PORTS;
+}
+#endif /* ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0) */
+
+/**
+ * dns_call_found() - call the found callback and check if there are duplicate
+ * entries for the given hostname. If there are any, their found callback will
+ * be called and they will be removed.
+ *
+ * @param idx dns table index of the entry that is resolved or removed
+ * @param addr IP address for the hostname (or NULL on error or memory shortage)
+ */
+static void
+dns_call_found(u8_t idx, ip_addr_t* addr)
+{
+#if ((LWIP_DNS_SECURE & (LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING | LWIP_DNS_SECURE_RAND_SRC_PORT)) != 0)
+  u8_t i;
+#endif
+
+#if LWIP_IPV4 && LWIP_IPV6
+  if (addr != NULL) {
+    /* check that address type matches the request and adapt the table entry */
+    if (IP_IS_V6_VAL(*addr)) {
+      LWIP_ASSERT("invalid response", LWIP_DNS_ADDRTYPE_IS_IPV6(dns_table[idx].reqaddrtype));
+      dns_table[idx].reqaddrtype = LWIP_DNS_ADDRTYPE_IPV6;
+    } else {
+      LWIP_ASSERT("invalid response", !LWIP_DNS_ADDRTYPE_IS_IPV6(dns_table[idx].reqaddrtype));
+      dns_table[idx].reqaddrtype = LWIP_DNS_ADDRTYPE_IPV4;
+    }
+  }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
+#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING) != 0)
+  for (i = 0; i < DNS_MAX_REQUESTS; i++) {
+    if (dns_requests[i].found && (dns_requests[i].dns_table_idx == idx)) {
+      (*dns_requests[i].found)(dns_table[idx].name, addr, dns_requests[i].arg);
+      /* flush this entry */
+      dns_requests[i].found = NULL;
+    }
+  }
+#else
+  if (dns_requests[idx].found) {
+    (*dns_requests[idx].found)(dns_table[idx].name, addr, dns_requests[idx].arg);
+  }
+  dns_requests[idx].found = NULL;
+#endif
+#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0)
+  /* close the pcb used unless other request are using it */
+  for (i = 0; i < DNS_MAX_REQUESTS; i++) {
+    if (i == idx) {
+      continue; /* only check other requests */
+    }
+    if (dns_table[i].state == DNS_STATE_ASKING) {
+      if (dns_table[i].pcb_idx == dns_table[idx].pcb_idx) {
+        /* another request is still using the same pcb */
+        dns_table[idx].pcb_idx = DNS_MAX_SOURCE_PORTS;
+        break;
+      }
+    }
+  }
+  if (dns_table[idx].pcb_idx < DNS_MAX_SOURCE_PORTS) {
+    /* if we come here, the pcb is not used any more and can be removed */
+    udp_remove(dns_pcbs[dns_table[idx].pcb_idx]);
+    dns_pcbs[dns_table[idx].pcb_idx] = NULL;
+    dns_table[idx].pcb_idx = DNS_MAX_SOURCE_PORTS;
+  }
+#endif
+}
+
+/* Create a query transmission ID that is unique for all outstanding queries */
+static u16_t
+dns_create_txid(void)
+{
+  u16_t txid;
+  u8_t i;
+
+again:
+  txid = (u16_t)DNS_RAND_TXID();
+
+  /* check whether the ID is unique */
+  for (i = 0; i < DNS_TABLE_SIZE; i++) {
+    if ((dns_table[i].state == DNS_STATE_ASKING) &&
+        (dns_table[i].txid == txid)) {
+      /* ID already used by another pending query */
+      goto again;
+    }
+  }
+
+  return txid;
+}
+
+/**
+ * dns_check_entry() - see if entry has not yet been queried and, if so, sends out a query.
+ * Check an entry in the dns_table:
+ * - send out query for new entries
+ * - retry old pending entries on timeout (also with different servers)
+ * - remove completed entries from the table if their TTL has expired
+ *
+ * @param i index of the dns_table entry to check
+ */
+static void
+dns_check_entry(u8_t i)
+{
+  err_t err;
+  struct dns_table_entry *entry = &dns_table[i];
+
+  LWIP_ASSERT("array index out of bounds", i < DNS_TABLE_SIZE);
+
+  switch (entry->state) {
+    case DNS_STATE_NEW:
+      /* initialize new entry */
+      entry->txid = dns_create_txid();
+      entry->state = DNS_STATE_ASKING;
+      entry->server_idx = 0;
+      entry->tmr = 1;
+      entry->retries = 0;
+
+      /* send DNS packet for this entry */
+      err = dns_send(i);
+      if (err != ERR_OK) {
+        LWIP_DEBUGF(DNS_DEBUG | LWIP_DBG_LEVEL_WARNING,
+                    ("dns_send returned error: %s\n", lwip_strerr(err)));
+      }
+      break;
+    case DNS_STATE_ASKING:
+      if (--entry->tmr == 0) {
+        if (++entry->retries == DNS_MAX_RETRIES) {
+          if ((entry->server_idx + 1 < DNS_MAX_SERVERS) && !ip_addr_isany_val(dns_servers[entry->server_idx + 1])
+#if LWIP_DNS_SUPPORT_MDNS_QUERIES
+            && !entry->is_mdns
+#endif /* LWIP_DNS_SUPPORT_MDNS_QUERIES */
+            ) {
+            /* change of server */
+            entry->server_idx++;
+            entry->tmr = 1;
+            entry->retries = 0;
+          } else {
+            LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entry: \"%s\": timeout\n", entry->name));
+            /* call specified callback function if provided */
+            dns_call_found(i, NULL);
+            /* flush this entry */
+            entry->state = DNS_STATE_UNUSED;
+            break;
+          }
+        } else {
+          /* wait longer for the next retry */
+          entry->tmr = entry->retries;
+        }
+
+        /* send DNS packet for this entry */
+        err = dns_send(i);
+        if (err != ERR_OK) {
+          LWIP_DEBUGF(DNS_DEBUG | LWIP_DBG_LEVEL_WARNING,
+                      ("dns_send returned error: %s\n", lwip_strerr(err)));
+        }
+      }
+      break;
+    case DNS_STATE_DONE:
+      /* if the time to live is nul */
+      if ((entry->ttl == 0) || (--entry->ttl == 0)) {
+        LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entry: \"%s\": flush\n", entry->name));
+        /* flush this entry, there cannot be any related pending entries in this state */
+        entry->state = DNS_STATE_UNUSED;
+      }
+      break;
+    case DNS_STATE_UNUSED:
+      /* nothing to do */
+      break;
+    default:
+      LWIP_ASSERT("unknown dns_table entry state:", 0);
+      break;
+  }
+}
+
+/**
+ * Call dns_check_entry for each entry in dns_table - check all entries.
+ */
+static void
+dns_check_entries(void)
+{
+  u8_t i;
+
+  for (i = 0; i < DNS_TABLE_SIZE; ++i) {
+    dns_check_entry(i);
+  }
+}
+
+/**
+ * Save TTL and call dns_call_found for correct response.
+ */
+static void
+dns_correct_response(u8_t idx, u32_t ttl)
+{
+  struct dns_table_entry *entry = &dns_table[idx];
+
+  entry->state = DNS_STATE_DONE;
+
+  LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response = ", entry->name));
+  ip_addr_debug_print(DNS_DEBUG, (&(entry->ipaddr)));
+  LWIP_DEBUGF(DNS_DEBUG, ("\n"));
+
+  /* read the answer resource record's TTL, and maximize it if needed */
+  entry->ttl = ttl;
+  if (entry->ttl > DNS_MAX_TTL) {
+    entry->ttl = DNS_MAX_TTL;
+  }
+  dns_call_found(idx, &entry->ipaddr);
+
+  if (entry->ttl == 0) {
+    /* RFC 883, page 29: "Zero values are
+       interpreted to mean that the RR can only be used for the
+       transaction in progress, and should not be cached."
+       -> flush this entry now */
+    /* entry reused during callback? */
+    if (entry->state == DNS_STATE_DONE) {
+      entry->state = DNS_STATE_UNUSED;
+    }
+  }
+}
+/**
+ * Receive input function for DNS response packets arriving for the dns UDP pcb.
+ */
+static void
+dns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
+{
+  u8_t i;
+  u16_t txid;
+  u16_t res_idx;
+  struct dns_hdr hdr;
+  struct dns_answer ans;
+  struct dns_query qry;
+  u16_t nquestions, nanswers;
+
+  LWIP_UNUSED_ARG(arg);
+  LWIP_UNUSED_ARG(pcb);
+  LWIP_UNUSED_ARG(port);
+
+  /* is the dns message big enough ? */
+  if (p->tot_len < (SIZEOF_DNS_HDR + SIZEOF_DNS_QUERY)) {
+    LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: pbuf too small\n"));
+    /* free pbuf and return */
+    goto memerr;
+  }
+
+  /* copy dns payload inside static buffer for processing */
+  if (pbuf_copy_partial(p, &hdr, SIZEOF_DNS_HDR, 0) == SIZEOF_DNS_HDR) {
+    /* Match the ID in the DNS header with the name table. */
+    txid = lwip_htons(hdr.id);
+    for (i = 0; i < DNS_TABLE_SIZE; i++) {
+      const struct dns_table_entry *entry = &dns_table[i];
+      if ((entry->state == DNS_STATE_ASKING) &&
+          (entry->txid == txid)) {
+
+        /* We only care about the question(s) and the answers. The authrr
+           and the extrarr are simply discarded. */
+        nquestions = lwip_htons(hdr.numquestions);
+        nanswers   = lwip_htons(hdr.numanswers);
+
+        /* Check for correct response. */
+        if ((hdr.flags1 & DNS_FLAG1_RESPONSE) == 0) {
+          LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": not a response\n", entry->name));
+          goto memerr; /* ignore this packet */
+        }
+        if (nquestions != 1) {
+          LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response not match to query\n", entry->name));
+          goto memerr; /* ignore this packet */
+        }
+
+#if LWIP_DNS_SUPPORT_MDNS_QUERIES
+        if (!entry->is_mdns)
+#endif /* LWIP_DNS_SUPPORT_MDNS_QUERIES */
+        {
+          /* Check whether response comes from the same network address to which the
+             question was sent. (RFC 5452) */
+          if (!ip_addr_cmp(addr, &dns_servers[entry->server_idx])) {
+            goto memerr; /* ignore this packet */
+          }
+        }
+
+        /* Check if the name in the "question" part match with the name in the entry and
+           skip it if equal. */
+        res_idx = dns_compare_name(entry->name, p, SIZEOF_DNS_HDR);
+        if (res_idx == 0xFFFF) {
+          LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response not match to query\n", entry->name));
+          goto memerr; /* ignore this packet */
+        }
+
+        /* check if "question" part matches the request */
+        if (pbuf_copy_partial(p, &qry, SIZEOF_DNS_QUERY, res_idx) != SIZEOF_DNS_QUERY) {
+          goto memerr; /* ignore this packet */
+        }
+        if ((qry.cls != PP_HTONS(DNS_RRCLASS_IN)) ||
+          (LWIP_DNS_ADDRTYPE_IS_IPV6(entry->reqaddrtype) && (qry.type != PP_HTONS(DNS_RRTYPE_AAAA))) ||
+          (!LWIP_DNS_ADDRTYPE_IS_IPV6(entry->reqaddrtype) && (qry.type != PP_HTONS(DNS_RRTYPE_A)))) {
+          LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response not match to query\n", entry->name));
+          goto memerr; /* ignore this packet */
+        }
+        /* skip the rest of the "question" part */
+        res_idx += SIZEOF_DNS_QUERY;
+
+        /* Check for error. If so, call callback to inform. */
+        if (hdr.flags2 & DNS_FLAG2_ERR_MASK) {
+          LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": error in flags\n", entry->name));
+        } else {
+          while ((nanswers > 0) && (res_idx < p->tot_len)) {
+            /* skip answer resource record's host name */
+            res_idx = dns_skip_name(p, res_idx);
+            if (res_idx == 0xFFFF) {
+              goto memerr; /* ignore this packet */
+            }
+
+            /* Check for IP address type and Internet class. Others are discarded. */
+            if (pbuf_copy_partial(p, &ans, SIZEOF_DNS_ANSWER, res_idx) != SIZEOF_DNS_ANSWER) {
+              goto memerr; /* ignore this packet */
+            }
+            res_idx += SIZEOF_DNS_ANSWER;
+
+            if (ans.cls == PP_HTONS(DNS_RRCLASS_IN)) {
+#if LWIP_IPV4
+              if ((ans.type == PP_HTONS(DNS_RRTYPE_A)) && (ans.len == PP_HTONS(sizeof(ip4_addr_t)))) {
+#if LWIP_IPV4 && LWIP_IPV6
+                if (!LWIP_DNS_ADDRTYPE_IS_IPV6(entry->reqaddrtype))
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+                {
+                  ip4_addr_t ip4addr;
+                  /* read the IP address after answer resource record's header */
+                  if (pbuf_copy_partial(p, &ip4addr, sizeof(ip4_addr_t), res_idx) != sizeof(ip4_addr_t)) {
+                    goto memerr; /* ignore this packet */
+                  }
+                  ip_addr_copy_from_ip4(dns_table[i].ipaddr, ip4addr);
+                  pbuf_free(p);
+                  /* handle correct response */
+                  dns_correct_response(i, lwip_ntohl(ans.ttl));
+                  return;
+                }
+              }
+#endif /* LWIP_IPV4 */
+#if LWIP_IPV6
+              if ((ans.type == PP_HTONS(DNS_RRTYPE_AAAA)) && (ans.len == PP_HTONS(sizeof(ip6_addr_t)))) {
+#if LWIP_IPV4 && LWIP_IPV6
+                if (LWIP_DNS_ADDRTYPE_IS_IPV6(entry->reqaddrtype))
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+                {
+                  ip6_addr_t ip6addr;
+                  /* read the IP address after answer resource record's header */
+                  if (pbuf_copy_partial(p, &ip6addr, sizeof(ip6_addr_t), res_idx) != sizeof(ip6_addr_t)) {
+                    goto memerr; /* ignore this packet */
+                  }
+                  ip_addr_copy_from_ip6(dns_table[i].ipaddr, ip6addr);
+                  pbuf_free(p);
+                  /* handle correct response */
+                  dns_correct_response(i, lwip_ntohl(ans.ttl));
+                  return;
+                }
+              }
+#endif /* LWIP_IPV6 */
+            }
+            /* skip this answer */
+            if ((int)(res_idx + lwip_htons(ans.len)) > 0xFFFF) {
+              goto memerr; /* ignore this packet */
+            }
+            res_idx += lwip_htons(ans.len);
+            --nanswers;
+          }
+#if LWIP_IPV4 && LWIP_IPV6
+          if ((entry->reqaddrtype == LWIP_DNS_ADDRTYPE_IPV4_IPV6) ||
+              (entry->reqaddrtype == LWIP_DNS_ADDRTYPE_IPV6_IPV4)) {
+            if (entry->reqaddrtype == LWIP_DNS_ADDRTYPE_IPV4_IPV6) {
+              /* IPv4 failed, try IPv6 */
+              dns_table[i].reqaddrtype = LWIP_DNS_ADDRTYPE_IPV6;
+            } else {
+              /* IPv6 failed, try IPv4 */
+              dns_table[i].reqaddrtype = LWIP_DNS_ADDRTYPE_IPV4;
+            }
+            pbuf_free(p);
+            dns_table[i].state = DNS_STATE_NEW;
+            dns_check_entry(i);
+            return;
+          }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+          LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": error in response\n", entry->name));
+        }
+        /* call callback to indicate error, clean up memory and return */
+        pbuf_free(p);
+        dns_call_found(i, NULL);
+        dns_table[i].state = DNS_STATE_UNUSED;
+        return;
+      }
+    }
+  }
+
+memerr:
+  /* deallocate memory and return */
+  pbuf_free(p);
+  return;
+}
+
+/**
+ * Queues a new hostname to resolve and sends out a DNS query for that hostname
+ *
+ * @param name the hostname that is to be queried
+ * @param hostnamelen length of the hostname
+ * @param found a callback function to be called on success, failure or timeout
+ * @param callback_arg argument to pass to the callback function
+ * @return err_t return code.
+ */
+static err_t
+dns_enqueue(const char *name, size_t hostnamelen, dns_found_callback found,
+            void *callback_arg LWIP_DNS_ADDRTYPE_ARG(u8_t dns_addrtype) LWIP_DNS_ISMDNS_ARG(u8_t is_mdns))
+{
+  u8_t i;
+  u8_t lseq, lseqi;
+  struct dns_table_entry *entry = NULL;
+  size_t namelen;
+  struct dns_req_entry* req;
+
+#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING) != 0)
+  u8_t r;
+  /* check for duplicate entries */
+  for (i = 0; i < DNS_TABLE_SIZE; i++) {
+    if ((dns_table[i].state == DNS_STATE_ASKING) &&
+        (lwip_strnicmp(name, dns_table[i].name, sizeof(dns_table[i].name)) == 0)) {
+#if LWIP_IPV4 && LWIP_IPV6
+      if (dns_table[i].reqaddrtype != dns_addrtype) {
+        /* requested address types don't match
+           this can lead to 2 concurrent requests, but mixing the address types
+           for the same host should not be that common */
+        continue;
+      }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+      /* this is a duplicate entry, find a free request entry */
+      for (r = 0; r < DNS_MAX_REQUESTS; r++) {
+        if (dns_requests[r].found == 0) {
+          dns_requests[r].found = found;
+          dns_requests[r].arg = callback_arg;
+          dns_requests[r].dns_table_idx = i;
+          LWIP_DNS_SET_ADDRTYPE(dns_requests[r].reqaddrtype, dns_addrtype);
+          LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": duplicate request\n", name));
+          return ERR_INPROGRESS;
+        }
+      }
+    }
+  }
+  /* no duplicate entries found */
+#endif
+
+  /* search an unused entry, or the oldest one */
+  lseq = 0;
+  lseqi = DNS_TABLE_SIZE;
+  for (i = 0; i < DNS_TABLE_SIZE; ++i) {
+    entry = &dns_table[i];
+    /* is it an unused entry ? */
+    if (entry->state == DNS_STATE_UNUSED) {
+      break;
+    }
+    /* check if this is the oldest completed entry */
+    if (entry->state == DNS_STATE_DONE) {
+      u8_t age = dns_seqno - entry->seqno;
+      if (age > lseq) {
+        lseq = age;
+        lseqi = i;
+      }
+    }
+  }
+
+  /* if we don't have found an unused entry, use the oldest completed one */
+  if (i == DNS_TABLE_SIZE) {
+    if ((lseqi >= DNS_TABLE_SIZE) || (dns_table[lseqi].state != DNS_STATE_DONE)) {
+      /* no entry can be used now, table is full */
+      LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": DNS entries table is full\n", name));
+      return ERR_MEM;
+    } else {
+      /* use the oldest completed one */
+      i = lseqi;
+      entry = &dns_table[i];
+    }
+  }
+
+#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING) != 0)
+  /* find a free request entry */
+  req = NULL;
+  for (r = 0; r < DNS_MAX_REQUESTS; r++) {
+    if (dns_requests[r].found == NULL) {
+      req = &dns_requests[r];
+      break;
+    }
+  }
+  if (req == NULL) {
+    /* no request entry can be used now, table is full */
+    LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": DNS request entries table is full\n", name));
+    return ERR_MEM;
+  }
+  req->dns_table_idx = i;
+#else
+  /* in this configuration, the entry index is the same as the request index */
+  req = &dns_requests[i];
+#endif
+
+  /* use this entry */
+  LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": use DNS entry %"U16_F"\n", name, (u16_t)(i)));
+
+  /* fill the entry */
+  entry->state = DNS_STATE_NEW;
+  entry->seqno = dns_seqno;
+  LWIP_DNS_SET_ADDRTYPE(entry->reqaddrtype, dns_addrtype);
+  LWIP_DNS_SET_ADDRTYPE(req->reqaddrtype, dns_addrtype);
+  req->found = found;
+  req->arg   = callback_arg;
+  namelen = LWIP_MIN(hostnamelen, DNS_MAX_NAME_LENGTH-1);
+  MEMCPY(entry->name, name, namelen);
+  entry->name[namelen] = 0;
+
+#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0)
+  entry->pcb_idx = dns_alloc_pcb();
+  if (entry->pcb_idx >= DNS_MAX_SOURCE_PORTS) {
+    /* failed to get a UDP pcb */
+    LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": failed to allocate a pcb\n", name));
+    entry->state = DNS_STATE_UNUSED;
+    req->found = NULL;
+    return ERR_MEM;
+  }
+  LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": use DNS pcb %"U16_F"\n", name, (u16_t)(entry->pcb_idx)));
+#endif
+
+#if LWIP_DNS_SUPPORT_MDNS_QUERIES
+  entry->is_mdns = is_mdns;
+#endif
+
+  dns_seqno++;
+
+  /* force to send query without waiting timer */
+  dns_check_entry(i);
+
+  /* dns query is enqueued */
+  return ERR_INPROGRESS;
+}
+
+/**
+ * @ingroup dns
+ * Resolve a hostname (string) into an IP address.
+ * NON-BLOCKING callback version for use with raw API!!!
+ *
+ * Returns immediately with one of err_t return codes:
+ * - ERR_OK if hostname is a valid IP address string or the host
+ *   name is already in the local names table.
+ * - ERR_INPROGRESS enqueue a request to be sent to the DNS server
+ *   for resolution if no errors are present.
+ * - ERR_ARG: dns client not initialized or invalid hostname
+ *
+ * @param hostname the hostname that is to be queried
+ * @param addr pointer to a ip_addr_t where to store the address if it is already
+ *             cached in the dns_table (only valid if ERR_OK is returned!)
+ * @param found a callback function to be called on success, failure or timeout (only if
+ *              ERR_INPROGRESS is returned!)
+ * @param callback_arg argument to pass to the callback function
+ * @return a err_t return code.
+ */
+err_t
+dns_gethostbyname(const char *hostname, ip_addr_t *addr, dns_found_callback found,
+                  void *callback_arg)
+{
+  return dns_gethostbyname_addrtype(hostname, addr, found, callback_arg, LWIP_DNS_ADDRTYPE_DEFAULT);
+}
+
+/**
+ * @ingroup dns
+ * Like dns_gethostbyname, but returned address type can be controlled:
+ * @param hostname the hostname that is to be queried
+ * @param addr pointer to a ip_addr_t where to store the address if it is already
+ *             cached in the dns_table (only valid if ERR_OK is returned!)
+ * @param found a callback function to be called on success, failure or timeout (only if
+ *              ERR_INPROGRESS is returned!)
+ * @param callback_arg argument to pass to the callback function
+ * @param dns_addrtype - LWIP_DNS_ADDRTYPE_IPV4_IPV6: try to resolve IPv4 first, try IPv6 if IPv4 fails only
+ *                     - LWIP_DNS_ADDRTYPE_IPV6_IPV4: try to resolve IPv6 first, try IPv4 if IPv6 fails only
+ *                     - LWIP_DNS_ADDRTYPE_IPV4: try to resolve IPv4 only
+ *                     - LWIP_DNS_ADDRTYPE_IPV6: try to resolve IPv6 only
+ */
+err_t
+dns_gethostbyname_addrtype(const char *hostname, ip_addr_t *addr, dns_found_callback found,
+                           void *callback_arg, u8_t dns_addrtype)
+{
+  size_t hostnamelen;
+#if LWIP_DNS_SUPPORT_MDNS_QUERIES
+  u8_t is_mdns;
+#endif
+  /* not initialized or no valid server yet, or invalid addr pointer
+   * or invalid hostname or invalid hostname length */
+  if ((addr == NULL) ||
+      (!hostname) || (!hostname[0])) {
+    return ERR_ARG;
+  }
+#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) == 0)
+  if (dns_pcbs[0] == NULL) {
+    return ERR_ARG;
+  }
+#endif
+  hostnamelen = strlen(hostname);
+  if (hostnamelen >= DNS_MAX_NAME_LENGTH) {
+    LWIP_DEBUGF(DNS_DEBUG, ("dns_gethostbyname: name too long to resolve"));
+    return ERR_ARG;
+  }
+
+
+#if LWIP_HAVE_LOOPIF
+  if (strcmp(hostname, "localhost") == 0) {
+    ip_addr_set_loopback(LWIP_DNS_ADDRTYPE_IS_IPV6(dns_addrtype), addr);
+    return ERR_OK;
+  }
+#endif /* LWIP_HAVE_LOOPIF */
+
+  /* host name already in octet notation? set ip addr and return ERR_OK */
+  if (ipaddr_aton(hostname, addr)) {
+#if LWIP_IPV4 && LWIP_IPV6
+    if ((IP_IS_V6(addr) && (dns_addrtype != LWIP_DNS_ADDRTYPE_IPV4)) ||
+        (IP_IS_V4(addr) && (dns_addrtype != LWIP_DNS_ADDRTYPE_IPV6)))
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+    {
+      return ERR_OK;
+    }
+  }
+  /* already have this address cached? */
+  if (dns_lookup(hostname, addr LWIP_DNS_ADDRTYPE_ARG(dns_addrtype)) == ERR_OK) {
+    return ERR_OK;
+  }
+#if LWIP_IPV4 && LWIP_IPV6
+  if ((dns_addrtype == LWIP_DNS_ADDRTYPE_IPV4_IPV6) || (dns_addrtype == LWIP_DNS_ADDRTYPE_IPV6_IPV4)) {
+    /* fallback to 2nd IP type and try again to lookup */
+    u8_t fallback;
+    if (dns_addrtype == LWIP_DNS_ADDRTYPE_IPV4_IPV6) {
+      fallback = LWIP_DNS_ADDRTYPE_IPV6;
+    } else {
+      fallback = LWIP_DNS_ADDRTYPE_IPV4;
+    }
+    if (dns_lookup(hostname, addr LWIP_DNS_ADDRTYPE_ARG(fallback)) == ERR_OK) {
+      return ERR_OK;
+    }
+  }
+#else /* LWIP_IPV4 && LWIP_IPV6 */
+  LWIP_UNUSED_ARG(dns_addrtype);
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
+#if LWIP_DNS_SUPPORT_MDNS_QUERIES
+  if (strstr(hostname, ".local") == &hostname[hostnamelen] - 6) {
+    is_mdns = 1;
+  } else {
+    is_mdns = 0;
+  }
+
+  if (!is_mdns)
+#endif /* LWIP_DNS_SUPPORT_MDNS_QUERIES */
+  {
+    /* prevent calling found callback if no server is set, return error instead */
+    if (ip_addr_isany_val(dns_servers[0])) {
+      return ERR_VAL;
+    }
+  }
+
+  /* queue query with specified callback */
+  return dns_enqueue(hostname, hostnamelen, found, callback_arg LWIP_DNS_ADDRTYPE_ARG(dns_addrtype)
+     LWIP_DNS_ISMDNS_ARG(is_mdns));
+}
+
+#endif /* LWIP_DNS */

+ 609 - 450
thirdparty/lwip/src/core/ipv4/inet_chksum.c → thirdparty/lwip/src/core/inet_chksum.c

@@ -1,450 +1,609 @@
-/**
- * @file
- * Incluse internet checksum functions.
- *
- */
-
-/*
- * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * This file is part of the lwIP TCP/IP stack.
- *
- * Author: Adam Dunkels <adam@sics.se>
- *
- */
-
-#include "lwip/opt.h"
-
-#include "lwip/inet_chksum.h"
-#include "lwip/def.h"
-
-#include <stddef.h>
-#include <string.h>
-
-/* These are some reference implementations of the checksum algorithm, with the
- * aim of being simple, correct and fully portable. Checksumming is the
- * first thing you would want to optimize for your platform. If you create
- * your own version, link it in and in your cc.h put:
- * 
- * #define LWIP_CHKSUM <your_checksum_routine> 
- *
- * Or you can select from the implementations below by defining
- * LWIP_CHKSUM_ALGORITHM to 1, 2 or 3.
- */
-
-#ifndef LWIP_CHKSUM
-# define LWIP_CHKSUM lwip_standard_chksum
-# ifndef LWIP_CHKSUM_ALGORITHM
-#  define LWIP_CHKSUM_ALGORITHM 2
-# endif
-#endif
-/* If none set: */
-#ifndef LWIP_CHKSUM_ALGORITHM
-# define LWIP_CHKSUM_ALGORITHM 0
-#endif
-
-#if (LWIP_CHKSUM_ALGORITHM == 1) /* Version #1 */
-/**
- * lwip checksum
- *
- * @param dataptr points to start of data to be summed at any boundary
- * @param len length of data to be summed
- * @return host order (!) lwip checksum (non-inverted Internet sum) 
- *
- * @note accumulator size limits summable length to 64k
- * @note host 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 & 0xffff0000UL) != 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 (LWIP_CHKSUM_ALGORITHM == 2) /* Alternative version #2 */
-/*
- * Curt McDowell
- * Broadcom Corp.
- * csm@broadcom.com
- *
- * IP checksum two bytes at a time with support for
- * unaligned buffer.
- * Works for len up to and including 0x20000.
- * by Curt McDowell, Broadcom Corp. 12/08/2005
- *
- * @param dataptr points to start of data to be summed at any boundary
- * @param len length of data to be summed
- * @return host order (!) lwip checksum (non-inverted Internet sum) 
- */
-
-static u16_t
-lwip_standard_chksum(void *dataptr, int len)
-{
-  u8_t *pb = (u8_t *)dataptr;
-  u16_t *ps, t = 0;
-  u32_t sum = 0;
-  int odd = ((mem_ptr_t)pb & 1);
-
-  /* Get aligned to u16_t */
-  if (odd && len > 0) {
-    ((u8_t *)&t)[1] = *pb++;
-    len--;
-  }
-
-  /* Add the bulk of the data */
-  ps = (u16_t *)(void *)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
-     calling this twice is propably faster than if statements... */
-  sum = FOLD_U32T(sum);
-  sum = FOLD_U32T(sum);
-
-  /* Swap if alignment was odd */
-  if (odd) {
-    sum = SWAP_BYTES_IN_WORD(sum);
-  }
-
-  return (u16_t)sum;
-}
-#endif
-
-#if (LWIP_CHKSUM_ALGORITHM == 3) /* Alternative version #3 */
-/**
- * An optimized checksum routine. Basically, it uses loop-unrolling on
- * the checksum loop, treating the head and tail bytes specially, whereas
- * the inner loop acts on 8 bytes at a time. 
- *
- * @arg start of buffer to be checksummed. May be an odd byte address.
- * @len number of bytes in the buffer to be checksummed.
- * @return host order (!) lwip checksum (non-inverted Internet sum) 
- * 
- * by Curt McDowell, Broadcom Corp. December 8th, 2005
- */
-
-static u16_t
-lwip_standard_chksum(void *dataptr, int len)
-{
-  u8_t *pb = (u8_t *)dataptr;
-  u16_t *ps, t = 0;
-  u32_t *pl;
-  u32_t sum = 0, tmp;
-  /* starts at odd byte address? */
-  int odd = ((mem_ptr_t)pb & 1);
-
-  if (odd && len > 0) {
-    ((u8_t *)&t)[1] = *pb++;
-    len--;
-  }
-
-  ps = (u16_t *)pb;
-
-  if (((mem_ptr_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 = FOLD_U32T(sum);
-
-  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 */
-
-  /* Fold 32-bit sum to 16 bits
-     calling this twice is propably faster than if statements... */
-  sum = FOLD_U32T(sum);
-  sum = FOLD_U32T(sum);
-
-  if (odd) {
-    sum = SWAP_BYTES_IN_WORD(sum);
-  }
-
-  return (u16_t)sum;
-}
-#endif
-
-/* inet_chksum_pseudo:
- *
- * Calculates the pseudo Internet checksum used by TCP and UDP for a pbuf chain.
- * IP addresses are expected to be in network byte order.
- *
- * @param p chain of pbufs over that a checksum should be calculated (ip data part)
- * @param src source ip address (used for checksum of pseudo header)
- * @param dst destination ip address (used for checksum of pseudo header)
- * @param proto ip protocol (used for checksum of pseudo header)
- * @param proto_len length of the ip data part (used for checksum of pseudo header)
- * @return checksum (as u16_t) to be saved directly in the protocol header
- */
-u16_t
-inet_chksum_pseudo(struct pbuf *p,
-       ip_addr_t *src, ip_addr_t *dest,
-       u8_t proto, u16_t proto_len)
-{
-  u32_t acc;
-  u32_t addr;
-  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));*/
-    /* just executing this next line is probably faster that the if statement needed
-       to check whether we really need to execute it, and does no harm */
-    acc = FOLD_U32T(acc);
-    if (q->len % 2 != 0) {
-      swapped = 1 - swapped;
-      acc = SWAP_BYTES_IN_WORD(acc);
-    }
-    /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): wrapped lwip_chksum()=%"X32_F" \n", acc));*/
-  }
-
-  if (swapped) {
-    acc = SWAP_BYTES_IN_WORD(acc);
-  }
-  addr = ip4_addr_get_u32(src);
-  acc += (addr & 0xffffUL);
-  acc += ((addr >> 16) & 0xffffUL);
-  addr = ip4_addr_get_u32(dest);
-  acc += (addr & 0xffffUL);
-  acc += ((addr >> 16) & 0xffffUL);
-  acc += (u32_t)htons((u16_t)proto);
-  acc += (u32_t)htons(proto_len);
-
-  /* Fold 32-bit sum to 16 bits
-     calling this twice is propably faster than if statements... */
-  acc = FOLD_U32T(acc);
-  acc = FOLD_U32T(acc);
-  LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): pbuf chain lwip_chksum()=%"X32_F"\n", acc));
-  return (u16_t)~(acc & 0xffffUL);
-}
-
-/* inet_chksum_pseudo:
- *
- * Calculates the pseudo Internet checksum used by TCP and UDP for a pbuf chain.
- * IP addresses are expected to be in network byte order.
- *
- * @param p chain of pbufs over that a checksum should be calculated (ip data part)
- * @param src source ip address (used for checksum of pseudo header)
- * @param dst destination ip address (used for checksum of pseudo header)
- * @param proto ip protocol (used for checksum of pseudo header)
- * @param proto_len length of the ip data part (used for checksum of pseudo header)
- * @return checksum (as u16_t) to be saved directly in the protocol header
- */
-u16_t
-inet_chksum_pseudo_partial(struct pbuf *p,
-       ip_addr_t *src, ip_addr_t *dest,
-       u8_t proto, u16_t proto_len, u16_t chksum_len)
-{
-  u32_t acc;
-  u32_t addr;
-  struct pbuf *q;
-  u8_t swapped;
-  u16_t chklen;
-
-  acc = 0;
-  swapped = 0;
-  /* iterate through all pbuf in chain */
-  for(q = p; (q != NULL) && (chksum_len > 0); q = q->next) {
-    LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): checksumming pbuf %p (has next %p) \n",
-      (void *)q, (void *)q->next));
-    chklen = q->len;
-    if (chklen > chksum_len) {
-      chklen = chksum_len;
-    }
-    acc += LWIP_CHKSUM(q->payload, chklen);
-    chksum_len -= chklen;
-    LWIP_ASSERT("delete me", chksum_len < 0x7fff);
-    /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): unwrapped lwip_chksum()=%"X32_F" \n", acc));*/
-    /* fold the upper bit down */
-    acc = FOLD_U32T(acc);
-    if (q->len % 2 != 0) {
-      swapped = 1 - swapped;
-      acc = SWAP_BYTES_IN_WORD(acc);
-    }
-    /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): wrapped lwip_chksum()=%"X32_F" \n", acc));*/
-  }
-
-  if (swapped) {
-    acc = SWAP_BYTES_IN_WORD(acc);
-  }
-  addr = ip4_addr_get_u32(src);
-  acc += (addr & 0xffffUL);
-  acc += ((addr >> 16) & 0xffffUL);
-  addr = ip4_addr_get_u32(dest);
-  acc += (addr & 0xffffUL);
-  acc += ((addr >> 16) & 0xffffUL);
-  acc += (u32_t)htons((u16_t)proto);
-  acc += (u32_t)htons(proto_len);
-
-  /* Fold 32-bit sum to 16 bits
-     calling this twice is propably faster than if statements... */
-  acc = FOLD_U32T(acc);
-  acc = FOLD_U32T(acc);
-  LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): pbuf chain lwip_chksum()=%"X32_F"\n", acc));
-  return (u16_t)~(acc & 0xffffUL);
-}
-
-/* inet_chksum:
- *
- * Calculates the Internet checksum over a portion of memory. Used primarily for IP
- * and ICMP.
- *
- * @param dataptr start of the buffer to calculate the checksum (no alignment needed)
- * @param len length of the buffer to calculate the checksum
- * @return checksum (as u16_t) to be saved directly in the protocol header
- */
-
-u16_t
-inet_chksum(void *dataptr, u16_t len)
-{
-  return ~LWIP_CHKSUM(dataptr, len);
-}
-
-/**
- * Calculate a checksum over a chain of pbufs (without pseudo-header, much like
- * inet_chksum only pbufs are used).
- *
- * @param p pbuf chain over that the checksum should be calculated
- * @return checksum (as u16_t) to be saved directly in the protocol header
- */
-u16_t
-inet_chksum_pbuf(struct pbuf *p)
-{
-  u32_t acc;
-  struct pbuf *q;
-  u8_t swapped;
-
-  acc = 0;
-  swapped = 0;
-  for(q = p; q != NULL; q = q->next) {
-    acc += LWIP_CHKSUM(q->payload, q->len);
-    acc = FOLD_U32T(acc);
-    if (q->len % 2 != 0) {
-      swapped = 1 - swapped;
-      acc = SWAP_BYTES_IN_WORD(acc);
-    }
-  }
-
-  if (swapped) {
-    acc = SWAP_BYTES_IN_WORD(acc);
-  }
-  return (u16_t)~(acc & 0xffffUL);
-}
-
-/* These are some implementations for LWIP_CHKSUM_COPY, which copies data
- * like MEMCPY but generates a checksum at the same time. Since this is a
- * performance-sensitive function, you might want to create your own version
- * in assembly targeted at your hardware by defining it in lwipopts.h:
- *   #define LWIP_CHKSUM_COPY(dst, src, len) your_chksum_copy(dst, src, len)
- */
-
-#if (LWIP_CHKSUM_COPY_ALGORITHM == 1) /* Version #1 */
-/** Safe but slow: first call MEMCPY, then call LWIP_CHKSUM.
- * For architectures with big caches, data might still be in cache when
- * generating the checksum after copying.
- */
-u16_t
-lwip_chksum_copy(void *dst, const void *src, u16_t len)
-{
-  MEMCPY(dst, src, len);
-  return LWIP_CHKSUM(dst, len);
-}
-#endif /* (LWIP_CHKSUM_COPY_ALGORITHM == 1) */
+/**
+ * @file
+ * Incluse internet checksum functions.\n
+ *
+ * These are some reference implementations of the checksum algorithm, with the
+ * aim of being simple, correct and fully portable. Checksumming is the
+ * first thing you would want to optimize for your platform. If you create
+ * your own version, link it in and in your cc.h put:
+ *
+ * \#define LWIP_CHKSUM your_checksum_routine
+ * 
+ * Or you can select from the implementations below by defining
+ * LWIP_CHKSUM_ALGORITHM to 1, 2 or 3.
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#include "lwip/inet_chksum.h"
+#include "lwip/def.h"
+#include "lwip/ip_addr.h"
+
+#include <string.h>
+
+#ifndef LWIP_CHKSUM
+# define LWIP_CHKSUM lwip_standard_chksum
+# ifndef LWIP_CHKSUM_ALGORITHM
+#  define LWIP_CHKSUM_ALGORITHM 2
+# endif
+u16_t lwip_standard_chksum(const void *dataptr, int len);
+#endif
+/* If none set: */
+#ifndef LWIP_CHKSUM_ALGORITHM
+# define LWIP_CHKSUM_ALGORITHM 0
+#endif
+
+#if (LWIP_CHKSUM_ALGORITHM == 1) /* Version #1 */
+/**
+ * lwip checksum
+ *
+ * @param dataptr points to start of data to be summed at any boundary
+ * @param len length of data to be summed
+ * @return host order (!) lwip checksum (non-inverted Internet sum)
+ *
+ * @note accumulator size limits summable length to 64k
+ * @note host endianess is irrelevant (p3 RFC1071)
+ */
+u16_t
+lwip_standard_chksum(const void *dataptr, int len)
+{
+  u32_t acc;
+  u16_t src;
+  const u8_t *octetptr;
+
+  acc = 0;
+  /* dataptr may be at odd or even addresses */
+  octetptr = (const u8_t*)dataptr;
+  while (len > 1) {
+    /* declare first octet as most significant
+       thus assume network order, ignoring host order */
+    src = (*octetptr) << 8;
+    octetptr++;
+    /* declare second octet as least significant */
+    src |= (*octetptr);
+    octetptr++;
+    acc += src;
+    len -= 2;
+  }
+  if (len > 0) {
+    /* accumulate remaining octet */
+    src = (*octetptr) << 8;
+    acc += src;
+  }
+  /* add deferred carry bits */
+  acc = (acc >> 16) + (acc & 0x0000ffffUL);
+  if ((acc & 0xffff0000UL) != 0) {
+    acc = (acc >> 16) + (acc & 0x0000ffffUL);
+  }
+  /* This maybe a little confusing: reorder sum using lwip_htons()
+     instead of lwip_ntohs() since it has a little less call overhead.
+     The caller must invert bits for Internet sum ! */
+  return lwip_htons((u16_t)acc);
+}
+#endif
+
+#if (LWIP_CHKSUM_ALGORITHM == 2) /* Alternative version #2 */
+/*
+ * Curt McDowell
+ * Broadcom Corp.
+ * csm@broadcom.com
+ *
+ * IP checksum two bytes at a time with support for
+ * unaligned buffer.
+ * Works for len up to and including 0x20000.
+ * by Curt McDowell, Broadcom Corp. 12/08/2005
+ *
+ * @param dataptr points to start of data to be summed at any boundary
+ * @param len length of data to be summed
+ * @return host order (!) lwip checksum (non-inverted Internet sum)
+ */
+u16_t
+lwip_standard_chksum(const void *dataptr, int len)
+{
+  const u8_t *pb = (const u8_t *)dataptr;
+  const u16_t *ps;
+  u16_t t = 0;
+  u32_t sum = 0;
+  int odd = ((mem_ptr_t)pb & 1);
+
+  /* Get aligned to u16_t */
+  if (odd && len > 0) {
+    ((u8_t *)&t)[1] = *pb++;
+    len--;
+  }
+
+  /* Add the bulk of the data */
+  ps = (const u16_t *)(const void *)pb;
+  while (len > 1) {
+    sum += *ps++;
+    len -= 2;
+  }
+
+  /* Consume left-over byte, if any */
+  if (len > 0) {
+    ((u8_t *)&t)[0] = *(const u8_t *)ps;
+  }
+
+  /* Add end bytes */
+  sum += t;
+
+  /* Fold 32-bit sum to 16 bits
+     calling this twice is probably faster than if statements... */
+  sum = FOLD_U32T(sum);
+  sum = FOLD_U32T(sum);
+
+  /* Swap if alignment was odd */
+  if (odd) {
+    sum = SWAP_BYTES_IN_WORD(sum);
+  }
+
+  return (u16_t)sum;
+}
+#endif
+
+#if (LWIP_CHKSUM_ALGORITHM == 3) /* Alternative version #3 */
+/**
+ * An optimized checksum routine. Basically, it uses loop-unrolling on
+ * the checksum loop, treating the head and tail bytes specially, whereas
+ * the inner loop acts on 8 bytes at a time.
+ *
+ * @arg start of buffer to be checksummed. May be an odd byte address.
+ * @len number of bytes in the buffer to be checksummed.
+ * @return host order (!) lwip checksum (non-inverted Internet sum)
+ *
+ * by Curt McDowell, Broadcom Corp. December 8th, 2005
+ */
+u16_t
+lwip_standard_chksum(const void *dataptr, int len)
+{
+  const u8_t *pb = (const u8_t *)dataptr;
+  const u16_t *ps;
+  u16_t t = 0;
+  const u32_t *pl;
+  u32_t sum = 0, tmp;
+  /* starts at odd byte address? */
+  int odd = ((mem_ptr_t)pb & 1);
+
+  if (odd && len > 0) {
+    ((u8_t *)&t)[1] = *pb++;
+    len--;
+  }
+
+  ps = (const u16_t *)(const void*)pb;
+
+  if (((mem_ptr_t)ps & 3) && len > 1) {
+    sum += *ps++;
+    len -= 2;
+  }
+
+  pl = (const u32_t *)(const void*)ps;
+
+  while (len > 7)  {
+    tmp = sum + *pl++;          /* ping */
+    if (tmp < sum) {
+      tmp++;                    /* add back carry */
+    }
+
+    sum = tmp + *pl++;          /* pong */
+    if (sum < tmp) {
+      sum++;                    /* add back carry */
+    }
+
+    len -= 8;
+  }
+
+  /* make room in upper bits */
+  sum = FOLD_U32T(sum);
+
+  ps = (const u16_t *)pl;
+
+  /* 16-bit aligned word remaining? */
+  while (len > 1) {
+    sum += *ps++;
+    len -= 2;
+  }
+
+  /* dangling tail byte remaining? */
+  if (len > 0) {                /* include odd byte */
+    ((u8_t *)&t)[0] = *(const u8_t *)ps;
+  }
+
+  sum += t;                     /* add end bytes */
+
+  /* Fold 32-bit sum to 16 bits
+     calling this twice is probably faster than if statements... */
+  sum = FOLD_U32T(sum);
+  sum = FOLD_U32T(sum);
+
+  if (odd) {
+    sum = SWAP_BYTES_IN_WORD(sum);
+  }
+
+  return (u16_t)sum;
+}
+#endif
+
+/** Parts of the pseudo checksum which are common to IPv4 and IPv6 */
+static u16_t
+inet_cksum_pseudo_base(struct pbuf *p, u8_t proto, u16_t proto_len, u32_t acc)
+{
+  struct pbuf *q;
+  u8_t swapped = 0;
+
+  /* iterate through all pbuf in chain */
+  for (q = p; q != NULL; q = q->next) {
+    LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): checksumming pbuf %p (has next %p) \n",
+      (void *)q, (void *)q->next));
+    acc += LWIP_CHKSUM(q->payload, q->len);
+    /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): unwrapped lwip_chksum()=%"X32_F" \n", acc));*/
+    /* just executing this next line is probably faster that the if statement needed
+       to check whether we really need to execute it, and does no harm */
+    acc = FOLD_U32T(acc);
+    if (q->len % 2 != 0) {
+      swapped = 1 - swapped;
+      acc = SWAP_BYTES_IN_WORD(acc);
+    }
+    /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): wrapped lwip_chksum()=%"X32_F" \n", acc));*/
+  }
+
+  if (swapped) {
+    acc = SWAP_BYTES_IN_WORD(acc);
+  }
+
+  acc += (u32_t)lwip_htons((u16_t)proto);
+  acc += (u32_t)lwip_htons(proto_len);
+
+  /* Fold 32-bit sum to 16 bits
+     calling this twice is probably faster than if statements... */
+  acc = FOLD_U32T(acc);
+  acc = FOLD_U32T(acc);
+  LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): pbuf chain lwip_chksum()=%"X32_F"\n", acc));
+  return (u16_t)~(acc & 0xffffUL);
+}
+
+#if LWIP_IPV4
+/* inet_chksum_pseudo:
+ *
+ * Calculates the IPv4 pseudo Internet checksum used by TCP and UDP for a pbuf chain.
+ * IP addresses are expected to be in network byte order.
+ *
+ * @param p chain of pbufs over that a checksum should be calculated (ip data part)
+ * @param src source ip address (used for checksum of pseudo header)
+ * @param dst destination ip address (used for checksum of pseudo header)
+ * @param proto ip protocol (used for checksum of pseudo header)
+ * @param proto_len length of the ip data part (used for checksum of pseudo header)
+ * @return checksum (as u16_t) to be saved directly in the protocol header
+ */
+u16_t
+inet_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len,
+       const ip4_addr_t *src, const ip4_addr_t *dest)
+{
+  u32_t acc;
+  u32_t addr;
+
+  addr = ip4_addr_get_u32(src);
+  acc = (addr & 0xffffUL);
+  acc += ((addr >> 16) & 0xffffUL);
+  addr = ip4_addr_get_u32(dest);
+  acc += (addr & 0xffffUL);
+  acc += ((addr >> 16) & 0xffffUL);
+  /* fold down to 16 bits */
+  acc = FOLD_U32T(acc);
+  acc = FOLD_U32T(acc);
+
+  return inet_cksum_pseudo_base(p, proto, proto_len, acc);
+}
+#endif /* LWIP_IPV4 */
+
+#if LWIP_IPV6
+/**
+ * Calculates the checksum with IPv6 pseudo header used by TCP and UDP for a pbuf chain.
+ * IPv6 addresses are expected to be in network byte order.
+ *
+ * @param p chain of pbufs over that a checksum should be calculated (ip data part)
+ * @param proto ipv6 protocol/next header (used for checksum of pseudo header)
+ * @param proto_len length of the ipv6 payload (used for checksum of pseudo header)
+ * @param src source ipv6 address (used for checksum of pseudo header)
+ * @param dest destination ipv6 address (used for checksum of pseudo header)
+ * @return checksum (as u16_t) to be saved directly in the protocol header
+ */
+u16_t
+ip6_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len,
+       const ip6_addr_t *src, const ip6_addr_t *dest)
+{
+  u32_t acc = 0;
+  u32_t addr;
+  u8_t addr_part;
+
+  for (addr_part = 0; addr_part < 4; addr_part++) {
+    addr = src->addr[addr_part];
+    acc += (addr & 0xffffUL);
+    acc += ((addr >> 16) & 0xffffUL);
+    addr = dest->addr[addr_part];
+    acc += (addr & 0xffffUL);
+    acc += ((addr >> 16) & 0xffffUL);
+  }
+  /* fold down to 16 bits */
+  acc = FOLD_U32T(acc);
+  acc = FOLD_U32T(acc);
+
+  return inet_cksum_pseudo_base(p, proto, proto_len, acc);
+}
+#endif /* LWIP_IPV6 */
+
+/* ip_chksum_pseudo:
+ *
+ * Calculates the IPv4 or IPv6 pseudo Internet checksum used by TCP and UDP for a pbuf chain.
+ * IP addresses are expected to be in network byte order.
+ *
+ * @param p chain of pbufs over that a checksum should be calculated (ip data part)
+ * @param src source ip address (used for checksum of pseudo header)
+ * @param dst destination ip address (used for checksum of pseudo header)
+ * @param proto ip protocol (used for checksum of pseudo header)
+ * @param proto_len length of the ip data part (used for checksum of pseudo header)
+ * @return checksum (as u16_t) to be saved directly in the protocol header
+ */
+u16_t
+ip_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len,
+       const ip_addr_t *src, const ip_addr_t *dest)
+{
+#if LWIP_IPV6
+  if (IP_IS_V6(dest)) {
+    return ip6_chksum_pseudo(p, proto, proto_len, ip_2_ip6(src), ip_2_ip6(dest));
+  }
+#endif /* LWIP_IPV6 */
+#if LWIP_IPV4 && LWIP_IPV6
+  else
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+#if LWIP_IPV4
+  {
+    return inet_chksum_pseudo(p, proto, proto_len, ip_2_ip4(src), ip_2_ip4(dest));
+  }
+#endif /* LWIP_IPV4 */
+}
+
+/** Parts of the pseudo checksum which are common to IPv4 and IPv6 */
+static u16_t
+inet_cksum_pseudo_partial_base(struct pbuf *p, u8_t proto, u16_t proto_len,
+       u16_t chksum_len, u32_t acc)
+{
+  struct pbuf *q;
+  u8_t swapped = 0;
+  u16_t chklen;
+
+  /* iterate through all pbuf in chain */
+  for (q = p; (q != NULL) && (chksum_len > 0); q = q->next) {
+    LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): checksumming pbuf %p (has next %p) \n",
+      (void *)q, (void *)q->next));
+    chklen = q->len;
+    if (chklen > chksum_len) {
+      chklen = chksum_len;
+    }
+    acc += LWIP_CHKSUM(q->payload, chklen);
+    chksum_len -= chklen;
+    LWIP_ASSERT("delete me", chksum_len < 0x7fff);
+    /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): unwrapped lwip_chksum()=%"X32_F" \n", acc));*/
+    /* fold the upper bit down */
+    acc = FOLD_U32T(acc);
+    if (q->len % 2 != 0) {
+      swapped = 1 - swapped;
+      acc = SWAP_BYTES_IN_WORD(acc);
+    }
+    /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): wrapped lwip_chksum()=%"X32_F" \n", acc));*/
+  }
+
+  if (swapped) {
+    acc = SWAP_BYTES_IN_WORD(acc);
+  }
+
+  acc += (u32_t)lwip_htons((u16_t)proto);
+  acc += (u32_t)lwip_htons(proto_len);
+
+  /* Fold 32-bit sum to 16 bits
+     calling this twice is probably faster than if statements... */
+  acc = FOLD_U32T(acc);
+  acc = FOLD_U32T(acc);
+  LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): pbuf chain lwip_chksum()=%"X32_F"\n", acc));
+  return (u16_t)~(acc & 0xffffUL);
+}
+
+#if LWIP_IPV4
+/* inet_chksum_pseudo_partial:
+ *
+ * Calculates the IPv4 pseudo Internet checksum used by TCP and UDP for a pbuf chain.
+ * IP addresses are expected to be in network byte order.
+ *
+ * @param p chain of pbufs over that a checksum should be calculated (ip data part)
+ * @param src source ip address (used for checksum of pseudo header)
+ * @param dst destination ip address (used for checksum of pseudo header)
+ * @param proto ip protocol (used for checksum of pseudo header)
+ * @param proto_len length of the ip data part (used for checksum of pseudo header)
+ * @return checksum (as u16_t) to be saved directly in the protocol header
+ */
+u16_t
+inet_chksum_pseudo_partial(struct pbuf *p, u8_t proto, u16_t proto_len,
+       u16_t chksum_len, const ip4_addr_t *src, const ip4_addr_t *dest)
+{
+  u32_t acc;
+  u32_t addr;
+
+  addr = ip4_addr_get_u32(src);
+  acc = (addr & 0xffffUL);
+  acc += ((addr >> 16) & 0xffffUL);
+  addr = ip4_addr_get_u32(dest);
+  acc += (addr & 0xffffUL);
+  acc += ((addr >> 16) & 0xffffUL);
+  /* fold down to 16 bits */
+  acc = FOLD_U32T(acc);
+  acc = FOLD_U32T(acc);
+
+  return inet_cksum_pseudo_partial_base(p, proto, proto_len, chksum_len, acc);
+}
+#endif /* LWIP_IPV4 */
+
+#if LWIP_IPV6
+/**
+ * Calculates the checksum with IPv6 pseudo header used by TCP and UDP for a pbuf chain.
+ * IPv6 addresses are expected to be in network byte order. Will only compute for a
+ * portion of the payload.
+ *
+ * @param p chain of pbufs over that a checksum should be calculated (ip data part)
+ * @param proto ipv6 protocol/next header (used for checksum of pseudo header)
+ * @param proto_len length of the ipv6 payload (used for checksum of pseudo header)
+ * @param chksum_len number of payload bytes used to compute chksum
+ * @param src source ipv6 address (used for checksum of pseudo header)
+ * @param dest destination ipv6 address (used for checksum of pseudo header)
+ * @return checksum (as u16_t) to be saved directly in the protocol header
+ */
+u16_t
+ip6_chksum_pseudo_partial(struct pbuf *p, u8_t proto, u16_t proto_len,
+       u16_t chksum_len, const ip6_addr_t *src, const ip6_addr_t *dest)
+{
+  u32_t acc = 0;
+  u32_t addr;
+  u8_t addr_part;
+
+  for (addr_part = 0; addr_part < 4; addr_part++) {
+    addr = src->addr[addr_part];
+    acc += (addr & 0xffffUL);
+    acc += ((addr >> 16) & 0xffffUL);
+    addr = dest->addr[addr_part];
+    acc += (addr & 0xffffUL);
+    acc += ((addr >> 16) & 0xffffUL);
+  }
+  /* fold down to 16 bits */
+  acc = FOLD_U32T(acc);
+  acc = FOLD_U32T(acc);
+
+  return inet_cksum_pseudo_partial_base(p, proto, proto_len, chksum_len, acc);
+}
+#endif /* LWIP_IPV6 */
+
+/* ip_chksum_pseudo_partial:
+ *
+ * Calculates the IPv4 or IPv6 pseudo Internet checksum used by TCP and UDP for a pbuf chain.
+ *
+ * @param p chain of pbufs over that a checksum should be calculated (ip data part)
+ * @param src source ip address (used for checksum of pseudo header)
+ * @param dst destination ip address (used for checksum of pseudo header)
+ * @param proto ip protocol (used for checksum of pseudo header)
+ * @param proto_len length of the ip data part (used for checksum of pseudo header)
+ * @return checksum (as u16_t) to be saved directly in the protocol header
+ */
+u16_t
+ip_chksum_pseudo_partial(struct pbuf *p, u8_t proto, u16_t proto_len,
+       u16_t chksum_len, const ip_addr_t *src, const ip_addr_t *dest)
+{
+#if LWIP_IPV6
+  if (IP_IS_V6(dest)) {
+    return ip6_chksum_pseudo_partial(p, proto, proto_len, chksum_len, ip_2_ip6(src), ip_2_ip6(dest));
+  }
+#endif /* LWIP_IPV6 */
+#if LWIP_IPV4 && LWIP_IPV6
+  else
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+#if LWIP_IPV4
+  {
+    return inet_chksum_pseudo_partial(p, proto, proto_len, chksum_len, ip_2_ip4(src), ip_2_ip4(dest));
+  }
+#endif /* LWIP_IPV4 */
+}
+
+/* inet_chksum:
+ *
+ * Calculates the Internet checksum over a portion of memory. Used primarily for IP
+ * and ICMP.
+ *
+ * @param dataptr start of the buffer to calculate the checksum (no alignment needed)
+ * @param len length of the buffer to calculate the checksum
+ * @return checksum (as u16_t) to be saved directly in the protocol header
+ */
+
+u16_t
+inet_chksum(const void *dataptr, u16_t len)
+{
+  return (u16_t)~(unsigned int)LWIP_CHKSUM(dataptr, len);
+}
+
+/**
+ * Calculate a checksum over a chain of pbufs (without pseudo-header, much like
+ * inet_chksum only pbufs are used).
+ *
+ * @param p pbuf chain over that the checksum should be calculated
+ * @return checksum (as u16_t) to be saved directly in the protocol header
+ */
+u16_t
+inet_chksum_pbuf(struct pbuf *p)
+{
+  u32_t acc;
+  struct pbuf *q;
+  u8_t swapped;
+
+  acc = 0;
+  swapped = 0;
+  for (q = p; q != NULL; q = q->next) {
+    acc += LWIP_CHKSUM(q->payload, q->len);
+    acc = FOLD_U32T(acc);
+    if (q->len % 2 != 0) {
+      swapped = 1 - swapped;
+      acc = SWAP_BYTES_IN_WORD(acc);
+    }
+  }
+
+  if (swapped) {
+    acc = SWAP_BYTES_IN_WORD(acc);
+  }
+  return (u16_t)~(acc & 0xffffUL);
+}
+
+/* These are some implementations for LWIP_CHKSUM_COPY, which copies data
+ * like MEMCPY but generates a checksum at the same time. Since this is a
+ * performance-sensitive function, you might want to create your own version
+ * in assembly targeted at your hardware by defining it in lwipopts.h:
+ *   #define LWIP_CHKSUM_COPY(dst, src, len) your_chksum_copy(dst, src, len)
+ */
+
+#if (LWIP_CHKSUM_COPY_ALGORITHM == 1) /* Version #1 */
+/** Safe but slow: first call MEMCPY, then call LWIP_CHKSUM.
+ * For architectures with big caches, data might still be in cache when
+ * generating the checksum after copying.
+ */
+u16_t
+lwip_chksum_copy(void *dst, const void *src, u16_t len)
+{
+  MEMCPY(dst, src, len);
+  return LWIP_CHKSUM(dst, len);
+}
+#endif /* (LWIP_CHKSUM_COPY_ALGORITHM == 1) */

+ 385 - 332
thirdparty/lwip/src/core/init.c

@@ -1,332 +1,385 @@
-/*
- * @file
- * Modules initialization
- *
- */
-
-/*
- * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
- * All rights reserved. 
- * 
- * Redistribution and use in source and binary forms, with or without modification, 
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission. 
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
- * OF SUCH DAMAGE.
- *
- * This file is part of the lwIP TCP/IP stack.
- * 
- * Author: Adam Dunkels <adam@sics.se>
- *
- */
-
-#include "lwip/opt.h"
-
-#include "lwip/init.h"
-#include "lwip/stats.h"
-#include "lwip/sys.h"
-#include "lwip/mem.h"
-#include "lwip/memp.h"
-#include "lwip/pbuf.h"
-#include "lwip/netif.h"
-#include "lwip/sockets.h"
-#include "lwip/ip.h"
-#include "lwip/raw.h"
-#include "lwip/udp.h"
-#include "lwip/tcp_impl.h"
-#include "lwip/snmp_msg.h"
-#include "lwip/autoip.h"
-#include "lwip/igmp.h"
-#include "lwip/dns.h"
-#include "lwip/timers.h"
-#include "netif/etharp.h"
-#include "lwip/api.h"
-
-/* Compile-time sanity checks for configuration errors.
- * These can be done independently of LWIP_DEBUG, without penalty.
- */
-#ifndef BYTE_ORDER
-  #error "BYTE_ORDER is not defined, you have to define it in your cc.h"
-#endif
-#if (!IP_SOF_BROADCAST && IP_SOF_BROADCAST_RECV)
-  #error "If you want to use broadcast filter per pcb on recv operations, you have to define IP_SOF_BROADCAST=1 in your lwipopts.h"
-#endif
-#if (!LWIP_UDP && LWIP_UDPLITE)
-  #error "If you want to use UDP Lite, you have to define LWIP_UDP=1 in your lwipopts.h"
-#endif
-#if (!LWIP_UDP && LWIP_SNMP)
-  #error "If you want to use SNMP, you have to define LWIP_UDP=1 in your lwipopts.h"
-#endif
-#if (!LWIP_UDP && LWIP_DHCP)
-  #error "If you want to use DHCP, you have to define LWIP_UDP=1 in your lwipopts.h"
-#endif
-#if (!LWIP_UDP && LWIP_IGMP)
-  #error "If you want to use IGMP, you have to define LWIP_UDP=1 in your lwipopts.h"
-#endif
-#if (!LWIP_UDP && LWIP_SNMP)
-  #error "If you want to use SNMP, you have to define LWIP_UDP=1 in your lwipopts.h"
-#endif
-#if (!LWIP_UDP && LWIP_DNS)
-  #error "If you want to use DNS, you have to define LWIP_UDP=1 in your lwipopts.h"
-#endif
-#if !MEMP_MEM_MALLOC /* MEMP_NUM_* checks are disabled when not using the pool allocator */
-#if (LWIP_ARP && ARP_QUEUEING && (MEMP_NUM_ARP_QUEUE<=0))
-  #error "If you want to use ARP Queueing, you have to define MEMP_NUM_ARP_QUEUE>=1 in your lwipopts.h"
-#endif
-#if (LWIP_RAW && (MEMP_NUM_RAW_PCB<=0))
-  #error "If you want to use RAW, you have to define MEMP_NUM_RAW_PCB>=1 in your lwipopts.h"
-#endif
-#if (LWIP_UDP && (MEMP_NUM_UDP_PCB<=0))
-  #error "If you want to use UDP, you have to define MEMP_NUM_UDP_PCB>=1 in your lwipopts.h"
-#endif
-#if (LWIP_TCP && (MEMP_NUM_TCP_PCB<=0))
-  #error "If you want to use TCP, you have to define MEMP_NUM_TCP_PCB>=1 in your lwipopts.h"
-#endif
-#if (LWIP_IGMP && (MEMP_NUM_IGMP_GROUP<=1))
-  #error "If you want to use IGMP, you have to define MEMP_NUM_IGMP_GROUP>1 in your lwipopts.h"
-#endif
-#if ((LWIP_NETCONN || LWIP_SOCKET) && (MEMP_NUM_TCPIP_MSG_API<=0))
-  #error "If you want to use Sequential API, you have to define MEMP_NUM_TCPIP_MSG_API>=1 in your lwipopts.h"
-#endif
-/* There must be sufficient timeouts, taking into account requirements of the subsystems. */
-#if LWIP_TIMERS && (MEMP_NUM_SYS_TIMEOUT < (LWIP_TCP + IP_REASSEMBLY + LWIP_ARP + (2*LWIP_DHCP) + LWIP_AUTOIP + LWIP_IGMP + LWIP_DNS + PPP_SUPPORT))
-  #error "MEMP_NUM_SYS_TIMEOUT is too low to accomodate all required timeouts"
-#endif
-#if (IP_REASSEMBLY && (MEMP_NUM_REASSDATA > IP_REASS_MAX_PBUFS))
-  #error "MEMP_NUM_REASSDATA > IP_REASS_MAX_PBUFS doesn't make sense since each struct ip_reassdata must hold 2 pbufs at least!"
-#endif
-#endif /* !MEMP_MEM_MALLOC */
-#if (LWIP_TCP && (TCP_WND > 0xffff))
-  #error "If you want to use TCP, TCP_WND must fit in an u16_t, so, you have to reduce it in your lwipopts.h"
-#endif
-#if (LWIP_TCP && (TCP_SND_QUEUELEN > 0xffff))
-  #error "If you want to use TCP, TCP_SND_QUEUELEN must fit in an u16_t, so, you have to reduce it in your lwipopts.h"
-#endif
-#if (LWIP_TCP && (TCP_SND_QUEUELEN < 2))
-  #error "TCP_SND_QUEUELEN must be at least 2 for no-copy TCP writes to work"
-#endif
-#if (LWIP_TCP && ((TCP_MAXRTX > 12) || (TCP_SYNMAXRTX > 12)))
-  #error "If you want to use TCP, TCP_MAXRTX and TCP_SYNMAXRTX must less or equal to 12 (due to tcp_backoff table), so, you have to reduce them in your lwipopts.h"
-#endif
-#if (LWIP_TCP && TCP_LISTEN_BACKLOG && (TCP_DEFAULT_LISTEN_BACKLOG < 0) || (TCP_DEFAULT_LISTEN_BACKLOG > 0xff))
-  #error "If you want to use TCP backlog, TCP_DEFAULT_LISTEN_BACKLOG must fit into an u8_t"
-#endif
-#if (LWIP_NETIF_API && (NO_SYS==1))
-  #error "If you want to use NETIF API, you have to define NO_SYS=0 in your lwipopts.h"
-#endif
-#if ((LWIP_SOCKET || LWIP_NETCONN) && (NO_SYS==1))
-  #error "If you want to use Sequential API, you have to define NO_SYS=0 in your lwipopts.h"
-#endif
-#if (!LWIP_NETCONN && LWIP_SOCKET)
-  #error "If you want to use Socket API, you have to define LWIP_NETCONN=1 in your lwipopts.h"
-#endif
-#if (((!LWIP_DHCP) || (!LWIP_AUTOIP)) && LWIP_DHCP_AUTOIP_COOP)
-  #error "If you want to use DHCP/AUTOIP cooperation mode, you have to define LWIP_DHCP=1 and LWIP_AUTOIP=1 in your lwipopts.h"
-#endif
-#if (((!LWIP_DHCP) || (!LWIP_ARP)) && DHCP_DOES_ARP_CHECK)
-  #error "If you want to use DHCP ARP checking, you have to define LWIP_DHCP=1 and LWIP_ARP=1 in your lwipopts.h"
-#endif
-#if (!LWIP_ARP && LWIP_AUTOIP)
-  #error "If you want to use AUTOIP, you have to define LWIP_ARP=1 in your lwipopts.h"
-#endif
-#if (LWIP_SNMP && (SNMP_CONCURRENT_REQUESTS<=0))
-  #error "If you want to use SNMP, you have to define SNMP_CONCURRENT_REQUESTS>=1 in your lwipopts.h"
-#endif
-#if (LWIP_SNMP && (SNMP_TRAP_DESTINATIONS<=0))
-  #error "If you want to use SNMP, you have to define SNMP_TRAP_DESTINATIONS>=1 in your lwipopts.h"
-#endif
-#if (LWIP_TCP && ((LWIP_EVENT_API && LWIP_CALLBACK_API) || (!LWIP_EVENT_API && !LWIP_CALLBACK_API)))
-  #error "One and exactly one of LWIP_EVENT_API and LWIP_CALLBACK_API has to be enabled in your lwipopts.h"
-#endif
-#if (MEM_LIBC_MALLOC && MEM_USE_POOLS)
-  #error "MEM_LIBC_MALLOC and MEM_USE_POOLS may not both be simultaneously enabled in your lwipopts.h"
-#endif
-#if (MEM_USE_POOLS && !MEMP_USE_CUSTOM_POOLS)
-  #error "MEM_USE_POOLS requires custom pools (MEMP_USE_CUSTOM_POOLS) to be enabled in your lwipopts.h"
-#endif
-#if (PBUF_POOL_BUFSIZE <= MEM_ALIGNMENT)
-  #error "PBUF_POOL_BUFSIZE must be greater than MEM_ALIGNMENT or the offset may take the full first pbuf"
-#endif
-#if (DNS_LOCAL_HOSTLIST && !DNS_LOCAL_HOSTLIST_IS_DYNAMIC && !(defined(DNS_LOCAL_HOSTLIST_INIT)))
-  #error "you have to define define DNS_LOCAL_HOSTLIST_INIT {{'host1', 0x123}, {'host2', 0x234}} to initialize DNS_LOCAL_HOSTLIST"
-#endif
-#if PPP_SUPPORT && !PPPOS_SUPPORT & !PPPOE_SUPPORT
-  #error "PPP_SUPPORT needs either PPPOS_SUPPORT or PPPOE_SUPPORT turned on"
-#endif
-#if !LWIP_ETHERNET && (LWIP_ARP || PPPOE_SUPPORT)
-  #error "LWIP_ETHERNET needs to be turned on for LWIP_ARP or PPPOE_SUPPORT"
-#endif
-#if LWIP_IGMP && !defined(LWIP_RAND)
-  #error "When using IGMP, LWIP_RAND() needs to be defined to a random-function returning an u32_t random value"
-#endif
-#if LWIP_TCPIP_CORE_LOCKING_INPUT && !LWIP_TCPIP_CORE_LOCKING
-  #error "When using LWIP_TCPIP_CORE_LOCKING_INPUT, LWIP_TCPIP_CORE_LOCKING must be enabled, too"
-#endif
-#if LWIP_TCP && LWIP_NETIF_TX_SINGLE_PBUF && !TCP_OVERSIZE
-  #error "LWIP_NETIF_TX_SINGLE_PBUF needs TCP_OVERSIZE enabled to create single-pbuf TCP packets"
-#endif
-#if IP_FRAG && IP_FRAG_USES_STATIC_BUF && LWIP_NETIF_TX_SINGLE_PBUF
-  #error "LWIP_NETIF_TX_SINGLE_PBUF does not work with IP_FRAG_USES_STATIC_BUF==1 as that creates pbuf queues"
-#endif
-#if LWIP_NETCONN && LWIP_TCP
-#if NETCONN_COPY != TCP_WRITE_FLAG_COPY
-  #error "NETCONN_COPY != TCP_WRITE_FLAG_COPY"
-#endif
-#if NETCONN_MORE != TCP_WRITE_FLAG_MORE
-  #error "NETCONN_MORE != TCP_WRITE_FLAG_MORE"
-#endif
-#endif /* LWIP_NETCONN && LWIP_TCP */ 
-#if LWIP_SOCKET
-/* Check that the SO_* socket options and SOF_* lwIP-internal flags match */
-#if SO_ACCEPTCONN != SOF_ACCEPTCONN
-  #error "SO_ACCEPTCONN != SOF_ACCEPTCONN"
-#endif
-#if SO_REUSEADDR != SOF_REUSEADDR
-  #error "WARNING: SO_REUSEADDR != SOF_REUSEADDR"
-#endif
-#if SO_KEEPALIVE != SOF_KEEPALIVE
-  #error "WARNING: SO_KEEPALIVE != SOF_KEEPALIVE"
-#endif
-#if SO_BROADCAST != SOF_BROADCAST
-  #error "WARNING: SO_BROADCAST != SOF_BROADCAST"
-#endif
-#if SO_LINGER != SOF_LINGER
-  #error "WARNING: SO_LINGER != SOF_LINGER"
-#endif
-#endif /* LWIP_SOCKET */
-
-
-/* Compile-time checks for deprecated options.
- */
-#ifdef MEMP_NUM_TCPIP_MSG
-  #error "MEMP_NUM_TCPIP_MSG option is deprecated. Remove it from your lwipopts.h."
-#endif
-#ifdef MEMP_NUM_API_MSG
-  #error "MEMP_NUM_API_MSG option is deprecated. Remove it from your lwipopts.h."
-#endif
-#ifdef TCP_REXMIT_DEBUG
-  #error "TCP_REXMIT_DEBUG option is deprecated. Remove it from your lwipopts.h."
-#endif
-#ifdef RAW_STATS
-  #error "RAW_STATS option is deprecated. Remove it from your lwipopts.h."
-#endif
-#ifdef ETHARP_QUEUE_FIRST
-  #error "ETHARP_QUEUE_FIRST option is deprecated. Remove it from your lwipopts.h."
-#endif
-#ifdef ETHARP_ALWAYS_INSERT
-  #error "ETHARP_ALWAYS_INSERT option is deprecated. Remove it from your lwipopts.h."
-#endif
-
-#ifndef LWIP_DISABLE_TCP_SANITY_CHECKS
-#define LWIP_DISABLE_TCP_SANITY_CHECKS  1
-#endif
-#ifndef LWIP_DISABLE_MEMP_SANITY_CHECKS
-#define LWIP_DISABLE_MEMP_SANITY_CHECKS 0
-#endif
-
-/* MEMP sanity checks */
-#if !LWIP_DISABLE_MEMP_SANITY_CHECKS
-#if LWIP_NETCONN
-#if MEMP_MEM_MALLOC
-#if !MEMP_NUM_NETCONN && LWIP_SOCKET
-#error "lwip_sanity_check: WARNING: MEMP_NUM_NETCONN cannot be 0 when using sockets!"
-#endif
-#else /* MEMP_MEM_MALLOC */
-#if MEMP_NUM_NETCONN > (MEMP_NUM_TCP_PCB+MEMP_NUM_TCP_PCB_LISTEN+MEMP_NUM_UDP_PCB+MEMP_NUM_RAW_PCB)
-#error "lwip_sanity_check: WARNING: MEMP_NUM_NETCONN should be less than the sum of MEMP_NUM_{TCP,RAW,UDP}_PCB+MEMP_NUM_TCP_PCB_LISTEN. If you know what you are doing, define LWIP_DISABLE_MEMP_SANITY_CHECKS to 1 to disable this error."
-#endif
-#endif /* MEMP_MEM_MALLOC */
-#endif /* LWIP_NETCONN */
-#endif /* !LWIP_DISABLE_MEMP_SANITY_CHECKS */
-
-/* TCP sanity checks */
-#if !LWIP_DISABLE_TCP_SANITY_CHECKS
-#if LWIP_TCP
-#if !MEMP_MEM_MALLOC && (MEMP_NUM_TCP_SEG < TCP_SND_QUEUELEN)
-  #error "lwip_sanity_check: WARNING: MEMP_NUM_TCP_SEG should be at least as big as TCP_SND_QUEUELEN. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error."
-#endif
-#if TCP_SND_BUF < (2 * TCP_MSS)
-  #error "lwip_sanity_check: WARNING: TCP_SND_BUF must be at least as much as (2 * TCP_MSS) for things to work smoothly. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error."
-#endif
-#if TCP_SND_QUEUELEN < (2 * (TCP_SND_BUF / TCP_MSS))
-  #error "lwip_sanity_check: WARNING: TCP_SND_QUEUELEN must be at least as much as (2 * TCP_SND_BUF/TCP_MSS) for things to work. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error."
-#endif
-#if TCP_SNDLOWAT >= TCP_SND_BUF
-  #error "lwip_sanity_check: WARNING: TCP_SNDLOWAT must be less than TCP_SND_BUF. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error."
-#endif
-#if TCP_SNDQUEUELOWAT >= TCP_SND_QUEUELEN
-  #error "lwip_sanity_check: WARNING: TCP_SNDQUEUELOWAT must be less than TCP_SND_QUEUELEN. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error."
-#endif
-#if !MEMP_MEM_MALLOC && (TCP_WND > (PBUF_POOL_SIZE * (PBUF_POOL_BUFSIZE - (PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN))))
-  #error "lwip_sanity_check: WARNING: TCP_WND is larger than space provided by PBUF_POOL_SIZE * (PBUF_POOL_BUFSIZE - protocol headers). If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error."
-#endif
-#if TCP_WND < TCP_MSS
-  #error "lwip_sanity_check: WARNING: TCP_WND is smaller than MSS. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error."
-#endif
-#endif /* LWIP_TCP */
-#endif /* !LWIP_DISABLE_TCP_SANITY_CHECKS */
-
-/**
- * Perform Sanity check of user-configurable values, and initialize all modules.
- */
-void
-lwip_init(void)
-{
-  /* Modules initialization */
-  stats_init();
-#if !NO_SYS
-  sys_init();
-#endif /* !NO_SYS */
-  mem_init();
-  memp_init();
-  pbuf_init();
-  netif_init();
-#if LWIP_SOCKET
-  lwip_socket_init();
-#endif /* LWIP_SOCKET */
-  ip_init();
-#if LWIP_ARP
-  etharp_init();
-#endif /* LWIP_ARP */
-#if LWIP_RAW
-  raw_init();
-#endif /* LWIP_RAW */
-#if LWIP_UDP
-  udp_init();
-#endif /* LWIP_UDP */
-#if LWIP_TCP
-  tcp_init();
-#endif /* LWIP_TCP */
-#if LWIP_SNMP
-  snmp_init();
-#endif /* LWIP_SNMP */
-#if LWIP_AUTOIP
-  autoip_init();
-#endif /* LWIP_AUTOIP */
-#if LWIP_IGMP
-  igmp_init();
-#endif /* LWIP_IGMP */
-#if LWIP_DNS
-  dns_init();
-#endif /* LWIP_DNS */
-
-#if LWIP_TIMERS
-  sys_timeouts_init();
-#endif /* LWIP_TIMERS */
-}
+/**
+ * @file
+ * Modules initialization
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ */
+
+#include "lwip/opt.h"
+
+#include "lwip/init.h"
+#include "lwip/stats.h"
+#include "lwip/sys.h"
+#include "lwip/mem.h"
+#include "lwip/memp.h"
+#include "lwip/pbuf.h"
+#include "lwip/netif.h"
+#include "lwip/sockets.h"
+#include "lwip/ip.h"
+#include "lwip/raw.h"
+#include "lwip/udp.h"
+#include "lwip/priv/tcp_priv.h"
+#include "lwip/igmp.h"
+#include "lwip/dns.h"
+#include "lwip/timeouts.h"
+#include "lwip/etharp.h"
+#include "lwip/ip6.h"
+#include "lwip/nd6.h"
+#include "lwip/mld6.h"
+#include "lwip/api.h"
+
+#include "netif/ppp/ppp_opts.h"
+#include "netif/ppp/ppp_impl.h"
+
+#ifndef LWIP_SKIP_PACKING_CHECK
+
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct packed_struct_test
+{
+  PACK_STRUCT_FLD_8(u8_t  dummy1);
+  PACK_STRUCT_FIELD(u32_t dummy2);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
+#define PACKED_STRUCT_TEST_EXPECTED_SIZE 5
+
+#endif
+
+/* Compile-time sanity checks for configuration errors.
+ * These can be done independently of LWIP_DEBUG, without penalty.
+ */
+#ifndef BYTE_ORDER
+  #error "BYTE_ORDER is not defined, you have to define it in your cc.h"
+#endif
+#if (!IP_SOF_BROADCAST && IP_SOF_BROADCAST_RECV)
+  #error "If you want to use broadcast filter per pcb on recv operations, you have to define IP_SOF_BROADCAST=1 in your lwipopts.h"
+#endif
+#if (!LWIP_UDP && LWIP_UDPLITE)
+  #error "If you want to use UDP Lite, you have to define LWIP_UDP=1 in your lwipopts.h"
+#endif
+#if (!LWIP_UDP && LWIP_DHCP)
+  #error "If you want to use DHCP, you have to define LWIP_UDP=1 in your lwipopts.h"
+#endif
+#if (!LWIP_UDP && LWIP_MULTICAST_TX_OPTIONS)
+  #error "If you want to use IGMP/LWIP_MULTICAST_TX_OPTIONS, you have to define LWIP_UDP=1 in your lwipopts.h"
+#endif
+#if (!LWIP_UDP && LWIP_DNS)
+  #error "If you want to use DNS, you have to define LWIP_UDP=1 in your lwipopts.h"
+#endif
+#if !MEMP_MEM_MALLOC /* MEMP_NUM_* checks are disabled when not using the pool allocator */
+#if (LWIP_ARP && ARP_QUEUEING && (MEMP_NUM_ARP_QUEUE<=0))
+  #error "If you want to use ARP Queueing, you have to define MEMP_NUM_ARP_QUEUE>=1 in your lwipopts.h"
+#endif
+#if (LWIP_RAW && (MEMP_NUM_RAW_PCB<=0))
+  #error "If you want to use RAW, you have to define MEMP_NUM_RAW_PCB>=1 in your lwipopts.h"
+#endif
+#if (LWIP_UDP && (MEMP_NUM_UDP_PCB<=0))
+  #error "If you want to use UDP, you have to define MEMP_NUM_UDP_PCB>=1 in your lwipopts.h"
+#endif
+#if (LWIP_TCP && (MEMP_NUM_TCP_PCB<=0))
+  #error "If you want to use TCP, you have to define MEMP_NUM_TCP_PCB>=1 in your lwipopts.h"
+#endif
+#if (LWIP_IGMP && (MEMP_NUM_IGMP_GROUP<=1))
+  #error "If you want to use IGMP, you have to define MEMP_NUM_IGMP_GROUP>1 in your lwipopts.h"
+#endif
+#if (LWIP_IGMP && !LWIP_MULTICAST_TX_OPTIONS)
+  #error "If you want to use IGMP, you have to define LWIP_MULTICAST_TX_OPTIONS==1 in your lwipopts.h"
+#endif
+#if (LWIP_IGMP && !LWIP_IPV4)
+  #error "IGMP needs LWIP_IPV4 enabled in your lwipopts.h"
+#endif
+#if (LWIP_MULTICAST_TX_OPTIONS && !LWIP_IPV4)
+  #error "LWIP_MULTICAST_TX_OPTIONS needs LWIP_IPV4 enabled in your lwipopts.h"
+#endif
+#if ((LWIP_NETCONN || LWIP_SOCKET) && (MEMP_NUM_TCPIP_MSG_API<=0))
+  #error "If you want to use Sequential API, you have to define MEMP_NUM_TCPIP_MSG_API>=1 in your lwipopts.h"
+#endif
+/* There must be sufficient timeouts, taking into account requirements of the subsystems. */
+#if LWIP_TIMERS && (MEMP_NUM_SYS_TIMEOUT < (LWIP_TCP + IP_REASSEMBLY + LWIP_ARP + (2*LWIP_DHCP) + LWIP_AUTOIP + LWIP_IGMP + LWIP_DNS + PPP_SUPPORT + (LWIP_IPV6 ? (1 + LWIP_IPV6_REASS + LWIP_IPV6_MLD) : 0)))
+  #error "MEMP_NUM_SYS_TIMEOUT is too low to accomodate all required timeouts"
+#endif
+#if (IP_REASSEMBLY && (MEMP_NUM_REASSDATA > IP_REASS_MAX_PBUFS))
+  #error "MEMP_NUM_REASSDATA > IP_REASS_MAX_PBUFS doesn't make sense since each struct ip_reassdata must hold 2 pbufs at least!"
+#endif
+#endif /* !MEMP_MEM_MALLOC */
+#if LWIP_WND_SCALE
+#if (LWIP_TCP && (TCP_WND > 0xffffffff))
+  #error "If you want to use TCP, TCP_WND must fit in an u32_t, so, you have to reduce it in your lwipopts.h"
+#endif
+#if (LWIP_TCP && (TCP_RCV_SCALE > 14))
+  #error "The maximum valid window scale value is 14!"
+#endif
+#if (LWIP_TCP && (TCP_WND > (0xFFFFU << TCP_RCV_SCALE)))
+  #error "TCP_WND is bigger than the configured LWIP_WND_SCALE allows!"
+#endif
+#if (LWIP_TCP && ((TCP_WND >> TCP_RCV_SCALE) == 0))
+  #error "TCP_WND is too small for the configured LWIP_WND_SCALE (results in zero window)!"
+#endif
+#else /* LWIP_WND_SCALE */
+#if (LWIP_TCP && (TCP_WND > 0xffff))
+  #error "If you want to use TCP, TCP_WND must fit in an u16_t, so, you have to reduce it in your lwipopts.h (or enable window scaling)"
+#endif
+#endif /* LWIP_WND_SCALE */
+#if (LWIP_TCP && (TCP_SND_QUEUELEN > 0xffff))
+  #error "If you want to use TCP, TCP_SND_QUEUELEN must fit in an u16_t, so, you have to reduce it in your lwipopts.h"
+#endif
+#if (LWIP_TCP && (TCP_SND_QUEUELEN < 2))
+  #error "TCP_SND_QUEUELEN must be at least 2 for no-copy TCP writes to work"
+#endif
+#if (LWIP_TCP && ((TCP_MAXRTX > 12) || (TCP_SYNMAXRTX > 12)))
+  #error "If you want to use TCP, TCP_MAXRTX and TCP_SYNMAXRTX must less or equal to 12 (due to tcp_backoff table), so, you have to reduce them in your lwipopts.h"
+#endif
+#if (LWIP_TCP && TCP_LISTEN_BACKLOG && ((TCP_DEFAULT_LISTEN_BACKLOG < 0) || (TCP_DEFAULT_LISTEN_BACKLOG > 0xff)))
+  #error "If you want to use TCP backlog, TCP_DEFAULT_LISTEN_BACKLOG must fit into an u8_t"
+#endif
+#if (LWIP_NETIF_API && (NO_SYS==1))
+  #error "If you want to use NETIF API, you have to define NO_SYS=0 in your lwipopts.h"
+#endif
+#if ((LWIP_SOCKET || LWIP_NETCONN) && (NO_SYS==1))
+  #error "If you want to use Sequential API, you have to define NO_SYS=0 in your lwipopts.h"
+#endif
+#if (LWIP_PPP_API && (NO_SYS==1))
+  #error "If you want to use PPP API, you have to define NO_SYS=0 in your lwipopts.h"
+#endif
+#if (LWIP_PPP_API && (PPP_SUPPORT==0))
+  #error "If you want to use PPP API, you have to enable PPP_SUPPORT in your lwipopts.h"
+#endif
+#if (((!LWIP_DHCP) || (!LWIP_AUTOIP)) && LWIP_DHCP_AUTOIP_COOP)
+  #error "If you want to use DHCP/AUTOIP cooperation mode, you have to define LWIP_DHCP=1 and LWIP_AUTOIP=1 in your lwipopts.h"
+#endif
+#if (((!LWIP_DHCP) || (!LWIP_ARP)) && DHCP_DOES_ARP_CHECK)
+  #error "If you want to use DHCP ARP checking, you have to define LWIP_DHCP=1 and LWIP_ARP=1 in your lwipopts.h"
+#endif
+#if (!LWIP_ARP && LWIP_AUTOIP)
+  #error "If you want to use AUTOIP, you have to define LWIP_ARP=1 in your lwipopts.h"
+#endif
+#if (LWIP_TCP && ((LWIP_EVENT_API && LWIP_CALLBACK_API) || (!LWIP_EVENT_API && !LWIP_CALLBACK_API)))
+  #error "One and exactly one of LWIP_EVENT_API and LWIP_CALLBACK_API has to be enabled in your lwipopts.h"
+#endif
+#if (MEM_LIBC_MALLOC && MEM_USE_POOLS)
+  #error "MEM_LIBC_MALLOC and MEM_USE_POOLS may not both be simultaneously enabled in your lwipopts.h"
+#endif
+#if (MEM_USE_POOLS && !MEMP_USE_CUSTOM_POOLS)
+  #error "MEM_USE_POOLS requires custom pools (MEMP_USE_CUSTOM_POOLS) to be enabled in your lwipopts.h"
+#endif
+#if (PBUF_POOL_BUFSIZE <= MEM_ALIGNMENT)
+  #error "PBUF_POOL_BUFSIZE must be greater than MEM_ALIGNMENT or the offset may take the full first pbuf"
+#endif
+#if (DNS_LOCAL_HOSTLIST && !DNS_LOCAL_HOSTLIST_IS_DYNAMIC && !(defined(DNS_LOCAL_HOSTLIST_INIT)))
+  #error "you have to define define DNS_LOCAL_HOSTLIST_INIT {{'host1', 0x123}, {'host2', 0x234}} to initialize DNS_LOCAL_HOSTLIST"
+#endif
+#if PPP_SUPPORT && !PPPOS_SUPPORT && !PPPOE_SUPPORT && !PPPOL2TP_SUPPORT
+  #error "PPP_SUPPORT needs at least one of PPPOS_SUPPORT, PPPOE_SUPPORT or PPPOL2TP_SUPPORT turned on"
+#endif
+#if PPP_SUPPORT && !PPP_IPV4_SUPPORT && !PPP_IPV6_SUPPORT
+  #error "PPP_SUPPORT needs PPP_IPV4_SUPPORT and/or PPP_IPV6_SUPPORT turned on"
+#endif
+#if PPP_SUPPORT && PPP_IPV4_SUPPORT && !LWIP_IPV4
+  #error "PPP_IPV4_SUPPORT needs LWIP_IPV4 turned on"
+#endif
+#if PPP_SUPPORT && PPP_IPV6_SUPPORT && !LWIP_IPV6
+  #error "PPP_IPV6_SUPPORT needs LWIP_IPV6 turned on"
+#endif
+#if !LWIP_ETHERNET && (LWIP_ARP || PPPOE_SUPPORT)
+  #error "LWIP_ETHERNET needs to be turned on for LWIP_ARP or PPPOE_SUPPORT"
+#endif
+#if LWIP_TCPIP_CORE_LOCKING_INPUT && !LWIP_TCPIP_CORE_LOCKING
+  #error "When using LWIP_TCPIP_CORE_LOCKING_INPUT, LWIP_TCPIP_CORE_LOCKING must be enabled, too"
+#endif
+#if LWIP_TCP && LWIP_NETIF_TX_SINGLE_PBUF && !TCP_OVERSIZE
+  #error "LWIP_NETIF_TX_SINGLE_PBUF needs TCP_OVERSIZE enabled to create single-pbuf TCP packets"
+#endif
+#if LWIP_NETCONN && LWIP_TCP
+#if NETCONN_COPY != TCP_WRITE_FLAG_COPY
+  #error "NETCONN_COPY != TCP_WRITE_FLAG_COPY"
+#endif
+#if NETCONN_MORE != TCP_WRITE_FLAG_MORE
+  #error "NETCONN_MORE != TCP_WRITE_FLAG_MORE"
+#endif
+#endif /* LWIP_NETCONN && LWIP_TCP */
+#if LWIP_SOCKET
+/* Check that the SO_* socket options and SOF_* lwIP-internal flags match */
+#if SO_REUSEADDR != SOF_REUSEADDR
+  #error "WARNING: SO_REUSEADDR != SOF_REUSEADDR"
+#endif
+#if SO_KEEPALIVE != SOF_KEEPALIVE
+  #error "WARNING: SO_KEEPALIVE != SOF_KEEPALIVE"
+#endif
+#if SO_BROADCAST != SOF_BROADCAST
+  #error "WARNING: SO_BROADCAST != SOF_BROADCAST"
+#endif
+#endif /* LWIP_SOCKET */
+
+
+/* Compile-time checks for deprecated options.
+ */
+#ifdef MEMP_NUM_TCPIP_MSG
+  #error "MEMP_NUM_TCPIP_MSG option is deprecated. Remove it from your lwipopts.h."
+#endif
+#ifdef TCP_REXMIT_DEBUG
+  #error "TCP_REXMIT_DEBUG option is deprecated. Remove it from your lwipopts.h."
+#endif
+#ifdef RAW_STATS
+  #error "RAW_STATS option is deprecated. Remove it from your lwipopts.h."
+#endif
+#ifdef ETHARP_QUEUE_FIRST
+  #error "ETHARP_QUEUE_FIRST option is deprecated. Remove it from your lwipopts.h."
+#endif
+#ifdef ETHARP_ALWAYS_INSERT
+  #error "ETHARP_ALWAYS_INSERT option is deprecated. Remove it from your lwipopts.h."
+#endif
+#if !NO_SYS && LWIP_TCPIP_CORE_LOCKING && LWIP_COMPAT_MUTEX && !defined(LWIP_COMPAT_MUTEX_ALLOWED)
+  #error "LWIP_COMPAT_MUTEX cannot prevent priority inversion. It is recommended to implement priority-aware mutexes. (Define LWIP_COMPAT_MUTEX_ALLOWED to disable this error.)"
+#endif
+
+#ifndef LWIP_DISABLE_TCP_SANITY_CHECKS
+#define LWIP_DISABLE_TCP_SANITY_CHECKS  1
+#endif
+#ifndef LWIP_DISABLE_MEMP_SANITY_CHECKS
+#define LWIP_DISABLE_MEMP_SANITY_CHECKS 0
+#endif
+
+/* MEMP sanity checks */
+#if MEMP_MEM_MALLOC
+#if !LWIP_DISABLE_MEMP_SANITY_CHECKS
+#if LWIP_NETCONN || LWIP_SOCKET
+#if !MEMP_NUM_NETCONN && LWIP_SOCKET
+#error "lwip_sanity_check: WARNING: MEMP_NUM_NETCONN cannot be 0 when using sockets!"
+#endif
+#else /* MEMP_MEM_MALLOC */
+#if MEMP_NUM_NETCONN > (MEMP_NUM_TCP_PCB+MEMP_NUM_TCP_PCB_LISTEN+MEMP_NUM_UDP_PCB+MEMP_NUM_RAW_PCB)
+#error "lwip_sanity_check: WARNING: MEMP_NUM_NETCONN should be less than the sum of MEMP_NUM_{TCP,RAW,UDP}_PCB+MEMP_NUM_TCP_PCB_LISTEN. If you know what you are doing, define LWIP_DISABLE_MEMP_SANITY_CHECKS to 1 to disable this error."
+#endif
+#endif /* LWIP_NETCONN || LWIP_SOCKET */
+#endif /* !LWIP_DISABLE_MEMP_SANITY_CHECKS */
+#if MEM_USE_POOLS
+#error "MEMP_MEM_MALLOC and MEM_USE_POOLS cannot be enabled at the same time"
+#endif
+#ifdef LWIP_HOOK_MEMP_AVAILABLE
+#error "LWIP_HOOK_MEMP_AVAILABLE doesn't make sense with MEMP_MEM_MALLOC"
+#endif
+#endif /* MEMP_MEM_MALLOC */
+
+/* TCP sanity checks */
+#if !LWIP_DISABLE_TCP_SANITY_CHECKS
+#if LWIP_TCP
+#if !MEMP_MEM_MALLOC && (MEMP_NUM_TCP_SEG < TCP_SND_QUEUELEN)
+  #error "lwip_sanity_check: WARNING: MEMP_NUM_TCP_SEG should be at least as big as TCP_SND_QUEUELEN. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error."
+#endif
+#if TCP_SND_BUF < (2 * TCP_MSS)
+  #error "lwip_sanity_check: WARNING: TCP_SND_BUF must be at least as much as (2 * TCP_MSS) for things to work smoothly. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error."
+#endif
+#if TCP_SND_QUEUELEN < (2 * (TCP_SND_BUF / TCP_MSS))
+  #error "lwip_sanity_check: WARNING: TCP_SND_QUEUELEN must be at least as much as (2 * TCP_SND_BUF/TCP_MSS) for things to work. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error."
+#endif
+#if TCP_SNDLOWAT >= TCP_SND_BUF
+  #error "lwip_sanity_check: WARNING: TCP_SNDLOWAT must be less than TCP_SND_BUF. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error."
+#endif
+#if TCP_SNDLOWAT >= (0xFFFF - (4 * TCP_MSS))
+  #error "lwip_sanity_check: WARNING: TCP_SNDLOWAT must at least be 4*MSS below u16_t overflow!"
+#endif
+#if TCP_SNDQUEUELOWAT >= TCP_SND_QUEUELEN
+  #error "lwip_sanity_check: WARNING: TCP_SNDQUEUELOWAT must be less than TCP_SND_QUEUELEN. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error."
+#endif
+#if !MEMP_MEM_MALLOC && PBUF_POOL_SIZE && (PBUF_POOL_BUFSIZE <= (PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN))
+  #error "lwip_sanity_check: WARNING: PBUF_POOL_BUFSIZE does not provide enough space for protocol headers. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error."
+#endif
+#if !MEMP_MEM_MALLOC && PBUF_POOL_SIZE && (TCP_WND > (PBUF_POOL_SIZE * (PBUF_POOL_BUFSIZE - (PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN))))
+  #error "lwip_sanity_check: WARNING: TCP_WND is larger than space provided by PBUF_POOL_SIZE * (PBUF_POOL_BUFSIZE - protocol headers). If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error."
+#endif
+#if TCP_WND < TCP_MSS
+  #error "lwip_sanity_check: WARNING: TCP_WND is smaller than MSS. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error."
+#endif
+#endif /* LWIP_TCP */
+#endif /* !LWIP_DISABLE_TCP_SANITY_CHECKS */
+
+/**
+ * @ingroup lwip_nosys
+ * Initialize all modules.
+ * Use this in NO_SYS mode. Use tcpip_init() otherwise.
+ */
+void
+lwip_init(void)
+{
+#ifndef LWIP_SKIP_CONST_CHECK
+  int a;
+  LWIP_UNUSED_ARG(a);
+  LWIP_ASSERT("LWIP_CONST_CAST not implemented correctly. Check your lwIP port.", LWIP_CONST_CAST(void*, &a) == &a);
+#endif
+#ifndef LWIP_SKIP_PACKING_CHECK
+  LWIP_ASSERT("Struct packing not implemented correctly. Check your lwIP port.", sizeof(struct packed_struct_test) == PACKED_STRUCT_TEST_EXPECTED_SIZE);
+#endif
+
+  /* Modules initialization */
+  stats_init();
+#if !NO_SYS
+  sys_init();
+#endif /* !NO_SYS */
+  mem_init();
+  memp_init();
+  pbuf_init();
+  netif_init();
+#if LWIP_IPV4
+  ip_init();
+#if LWIP_ARP
+  etharp_init();
+#endif /* LWIP_ARP */
+#endif /* LWIP_IPV4 */
+#if LWIP_RAW
+  raw_init();
+#endif /* LWIP_RAW */
+#if LWIP_UDP
+  udp_init();
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+  tcp_init();
+#endif /* LWIP_TCP */
+#if LWIP_IGMP
+  igmp_init();
+#endif /* LWIP_IGMP */
+#if LWIP_DNS
+  dns_init();
+#endif /* LWIP_DNS */
+#if PPP_SUPPORT
+  ppp_init();
+#endif
+ 
+#if LWIP_TIMERS
+  sys_timeouts_init();
+#endif /* LWIP_TIMERS */
+}

+ 124 - 0
thirdparty/lwip/src/core/ip.c

@@ -0,0 +1,124 @@
+/**
+ * @file
+ * Common IPv4 and IPv6 code
+ *
+ * @defgroup ip IP
+ * @ingroup callbackstyle_api
+ * 
+ * @defgroup ip4 IPv4
+ * @ingroup ip
+ *
+ * @defgroup ip6 IPv6
+ * @ingroup ip
+ * 
+ * @defgroup ipaddr IP address handling
+ * @ingroup infrastructure
+ * 
+ * @defgroup ip4addr IPv4 only
+ * @ingroup ipaddr
+ * 
+ * @defgroup ip6addr IPv6 only
+ * @ingroup ipaddr
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV4 || LWIP_IPV6
+
+#include "lwip/ip_addr.h"
+#include "lwip/ip.h"
+
+/** Global data for both IPv4 and IPv6 */
+struct ip_globals ip_data;
+
+#if LWIP_IPV4 && LWIP_IPV6
+
+const ip_addr_t ip_addr_any_type = IPADDR_ANY_TYPE_INIT;
+
+/**
+ * @ingroup ipaddr
+ * Convert IP address string (both versions) to numeric.
+ * The version is auto-detected from the string.
+ *
+ * @param cp IP address string to convert
+ * @param addr conversion result is stored here
+ * @return 1 on success, 0 on error
+ */
+int
+ipaddr_aton(const char *cp, ip_addr_t *addr)
+{
+  if (cp != NULL) {
+    const char* c;
+    for (c = cp; *c != 0; c++) {
+      if (*c == ':') {
+        /* contains a colon: IPv6 address */
+        if (addr) {
+          IP_SET_TYPE_VAL(*addr, IPADDR_TYPE_V6);
+        }
+        return ip6addr_aton(cp, ip_2_ip6(addr));
+      } else if (*c == '.') {
+        /* contains a dot: IPv4 address */
+        break;
+      }
+    }
+    /* call ip4addr_aton as fallback or if IPv4 was found */
+    if (addr) {
+      IP_SET_TYPE_VAL(*addr, IPADDR_TYPE_V4);
+    }
+    return ip4addr_aton(cp, ip_2_ip4(addr));
+  }
+  return 0;
+}
+
+/**
+ * @ingroup lwip_nosys
+ * If both IP versions are enabled, this function can dispatch packets to the correct one.
+ * Don't call directly, pass to netif_add() and call netif->input().
+ */
+err_t
+ip_input(struct pbuf *p, struct netif *inp)
+{
+  if (p != NULL) {
+    if (IP_HDR_GET_VERSION(p->payload) == 6) {
+      return ip6_input(p, inp);
+    }
+    return ip4_input(p, inp);
+  }
+  return ERR_VAL;
+}
+
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
+#endif /* LWIP_IPV4 || LWIP_IPV6 */

+ 527 - 528
thirdparty/lwip/src/core/ipv4/autoip.c

@@ -1,528 +1,527 @@
-/**
- * @file
- * AutoIP Automatic LinkLocal IP Configuration
- *
- */
-
-/*
- *
- * Copyright (c) 2007 Dominik Spies <kontakt@dspies.de>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * Author: Dominik Spies <kontakt@dspies.de>
- *
- * This is a AutoIP implementation for the lwIP TCP/IP stack. It aims to conform
- * with RFC 3927.
- *
- *
- * Please coordinate changes and requests with Dominik Spies
- * <kontakt@dspies.de>
- */
-
-/*******************************************************************************
- * USAGE:
- * 
- * define LWIP_AUTOIP 1  in your lwipopts.h
- * 
- * If you don't use tcpip.c (so, don't call, you don't call tcpip_init):
- * - First, call autoip_init().
- * - call autoip_tmr() all AUTOIP_TMR_INTERVAL msces,
- *   that should be defined in autoip.h.
- *   I recommend a value of 100. The value must divide 1000 with a remainder almost 0.
- *   Possible values are 1000, 500, 333, 250, 200, 166, 142, 125, 111, 100 ....
- *
- * Without DHCP:
- * - Call autoip_start() after netif_add().
- * 
- * With DHCP:
- * - define LWIP_DHCP_AUTOIP_COOP 1 in your lwipopts.h.
- * - Configure your DHCP Client.
- *
- */
-
-#include "lwip/opt.h"
-
-#if LWIP_AUTOIP /* don't build if not configured for use in lwipopts.h */
-
-#include "lwip/mem.h"
-#include "lwip/udp.h"
-#include "lwip/ip_addr.h"
-#include "lwip/netif.h"
-#include "lwip/autoip.h"
-#include "netif/etharp.h"
-
-#include <stdlib.h>
-#include <string.h>
-
-/* 169.254.0.0 */
-#define AUTOIP_NET         0xA9FE0000
-/* 169.254.1.0 */
-#define AUTOIP_RANGE_START (AUTOIP_NET | 0x0100)
-/* 169.254.254.255 */
-#define AUTOIP_RANGE_END   (AUTOIP_NET | 0xFEFF)
-
-
-/** Pseudo random macro based on netif informations.
- * You could use "rand()" from the C Library if you define LWIP_AUTOIP_RAND in lwipopts.h */
-#ifndef LWIP_AUTOIP_RAND
-#define LWIP_AUTOIP_RAND(netif) ( (((u32_t)((netif->hwaddr[5]) & 0xff) << 24) | \
-                                   ((u32_t)((netif->hwaddr[3]) & 0xff) << 16) | \
-                                   ((u32_t)((netif->hwaddr[2]) & 0xff) << 8) | \
-                                   ((u32_t)((netif->hwaddr[4]) & 0xff))) + \
-                                   (netif->autoip?netif->autoip->tried_llipaddr:0))
-#endif /* LWIP_AUTOIP_RAND */
-
-/**
- * Macro that generates the initial IP address to be tried by AUTOIP.
- * If you want to override this, define it to something else in lwipopts.h.
- */
-#ifndef LWIP_AUTOIP_CREATE_SEED_ADDR
-#define LWIP_AUTOIP_CREATE_SEED_ADDR(netif) \
-  htonl(AUTOIP_RANGE_START + ((u32_t)(((u8_t)(netif->hwaddr[4])) | \
-                 ((u32_t)((u8_t)(netif->hwaddr[5]))) << 8)))
-#endif /* LWIP_AUTOIP_CREATE_SEED_ADDR */
-
-/* static functions */
-static void autoip_handle_arp_conflict(struct netif *netif);
-
-/* creates a pseudo random LL IP-Address for a network interface */
-static void autoip_create_addr(struct netif *netif, ip_addr_t *ipaddr);
-
-/* sends an ARP probe */
-static err_t autoip_arp_probe(struct netif *netif);
-
-/* sends an ARP announce */
-static err_t autoip_arp_announce(struct netif *netif);
-
-/* configure interface for use with current LL IP-Address */
-static err_t autoip_bind(struct netif *netif);
-
-/* start sending probes for llipaddr */
-static void autoip_start_probing(struct netif *netif);
-
-
-/** Set a statically allocated struct autoip to work with.
- * Using this prevents autoip_start to allocate it using mem_malloc.
- *
- * @param netif the netif for which to set the struct autoip
- * @param dhcp (uninitialised) dhcp struct allocated by the application
- */
-void
-autoip_set_struct(struct netif *netif, struct autoip *autoip)
-{
-  LWIP_ASSERT("netif != NULL", netif != NULL);
-  LWIP_ASSERT("autoip != NULL", autoip != NULL);
-  LWIP_ASSERT("netif already has a struct autoip set", netif->autoip == NULL);
-
-  /* clear data structure */
-  memset(autoip, 0, sizeof(struct autoip));
-  /* autoip->state = AUTOIP_STATE_OFF; */
-  netif->autoip = autoip;
-}
-
-/** Restart AutoIP client and check the next address (conflict detected)
- *
- * @param netif The netif under AutoIP control
- */
-static void
-autoip_restart(struct netif *netif)
-{
-  netif->autoip->tried_llipaddr++;
-  autoip_start(netif);
-}
-
-/**
- * Handle a IP address conflict after an ARP conflict detection
- */
-static void
-autoip_handle_arp_conflict(struct netif *netif)
-{
-  /* Somehow detect if we are defending or retreating */
-  unsigned char defend = 1; /* tbd */
-
-  if (defend) {
-    if (netif->autoip->lastconflict > 0) {
-      /* retreat, there was a conflicting ARP in the last
-       * DEFEND_INTERVAL seconds
-       */
-      LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
-        ("autoip_handle_arp_conflict(): we are defending, but in DEFEND_INTERVAL, retreating\n"));
-
-      /* TODO: close all TCP sessions */
-      autoip_restart(netif);
-    } else {
-      LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
-        ("autoip_handle_arp_conflict(): we are defend, send ARP Announce\n"));
-      autoip_arp_announce(netif);
-      netif->autoip->lastconflict = DEFEND_INTERVAL * AUTOIP_TICKS_PER_SECOND;
-    }
-  } else {
-    LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
-      ("autoip_handle_arp_conflict(): we do not defend, retreating\n"));
-    /* TODO: close all TCP sessions */
-    autoip_restart(netif);
-  }
-}
-
-/**
- * Create an IP-Address out of range 169.254.1.0 to 169.254.254.255
- *
- * @param netif network interface on which create the IP-Address
- * @param ipaddr ip address to initialize
- */
-static void
-autoip_create_addr(struct netif *netif, ip_addr_t *ipaddr)
-{
-  /* Here we create an IP-Address out of range 169.254.1.0 to 169.254.254.255
-   * compliant to RFC 3927 Section 2.1
-   * We have 254 * 256 possibilities */
-
-  u32_t addr = ntohl(LWIP_AUTOIP_CREATE_SEED_ADDR(netif));
-  addr += netif->autoip->tried_llipaddr;
-  addr = AUTOIP_NET | (addr & 0xffff);
-  /* Now, 169.254.0.0 <= addr <= 169.254.255.255 */ 
-
-  if (addr < AUTOIP_RANGE_START) {
-    addr += AUTOIP_RANGE_END - AUTOIP_RANGE_START + 1;
-  }
-  if (addr > AUTOIP_RANGE_END) {
-    addr -= AUTOIP_RANGE_END - AUTOIP_RANGE_START + 1;
-  }
-  LWIP_ASSERT("AUTOIP address not in range", (addr >= AUTOIP_RANGE_START) &&
-    (addr <= AUTOIP_RANGE_END));
-  ip4_addr_set_u32(ipaddr, htonl(addr));
-  
-  LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
-    ("autoip_create_addr(): tried_llipaddr=%"U16_F", %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
-    (u16_t)(netif->autoip->tried_llipaddr), ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr),
-    ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr)));
-}
-
-/**
- * Sends an ARP probe from a network interface
- *
- * @param netif network interface used to send the probe
- */
-static err_t
-autoip_arp_probe(struct netif *netif)
-{
-  return etharp_raw(netif, (struct eth_addr *)netif->hwaddr, &ethbroadcast,
-    (struct eth_addr *)netif->hwaddr, IP_ADDR_ANY, &ethzero,
-    &netif->autoip->llipaddr, ARP_REQUEST);
-}
-
-/**
- * Sends an ARP announce from a network interface
- *
- * @param netif network interface used to send the announce
- */
-static err_t
-autoip_arp_announce(struct netif *netif)
-{
-  return etharp_raw(netif, (struct eth_addr *)netif->hwaddr, &ethbroadcast,
-    (struct eth_addr *)netif->hwaddr, &netif->autoip->llipaddr, &ethzero,
-    &netif->autoip->llipaddr, ARP_REQUEST);
-}
-
-/**
- * Configure interface for use with current LL IP-Address
- *
- * @param netif network interface to configure with current LL IP-Address
- */
-static err_t
-autoip_bind(struct netif *netif)
-{
-  struct autoip *autoip = netif->autoip;
-  ip_addr_t sn_mask, gw_addr;
-
-  LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
-    ("autoip_bind(netif=%p) %c%c%"U16_F" %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
-    (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num,
-    ip4_addr1_16(&autoip->llipaddr), ip4_addr2_16(&autoip->llipaddr),
-    ip4_addr3_16(&autoip->llipaddr), ip4_addr4_16(&autoip->llipaddr)));
-
-  IP4_ADDR(&sn_mask, 255, 255, 0, 0);
-  IP4_ADDR(&gw_addr, 0, 0, 0, 0);
-
-  netif_set_ipaddr(netif, &autoip->llipaddr);
-  netif_set_netmask(netif, &sn_mask);
-  netif_set_gw(netif, &gw_addr);  
-
-  /* bring the interface up */
-  netif_set_up(netif);
-
-  return ERR_OK;
-}
-
-/**
- * Start AutoIP client
- *
- * @param netif network interface on which start the AutoIP client
- */
-err_t
-autoip_start(struct netif *netif)
-{
-  struct autoip *autoip = netif->autoip;
-  err_t result = ERR_OK;
-
-  if (netif_is_up(netif)) {
-    netif_set_down(netif);
-  }
-
-  /* Set IP-Address, Netmask and Gateway to 0 to make sure that
-   * ARP Packets are formed correctly
-   */
-  ip_addr_set_zero(&netif->ip_addr);
-  ip_addr_set_zero(&netif->netmask);
-  ip_addr_set_zero(&netif->gw);
-
-  LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
-    ("autoip_start(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0],
-    netif->name[1], (u16_t)netif->num));
-  if (autoip == NULL) {
-    /* no AutoIP client attached yet? */
-    LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
-      ("autoip_start(): starting new AUTOIP client\n"));
-    autoip = (struct autoip *)mem_malloc(sizeof(struct autoip));
-    if (autoip == NULL) {
-      LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
-        ("autoip_start(): could not allocate autoip\n"));
-      return ERR_MEM;
-    }
-    memset(autoip, 0, sizeof(struct autoip));
-    /* store this AutoIP client in the netif */
-    netif->autoip = autoip;
-    LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, ("autoip_start(): allocated autoip"));
-  } else {
-    autoip->state = AUTOIP_STATE_OFF;
-    autoip->ttw = 0;
-    autoip->sent_num = 0;
-    ip_addr_set_zero(&autoip->llipaddr);
-    autoip->lastconflict = 0;
-  }
-
-  autoip_create_addr(netif, &(autoip->llipaddr));
-  autoip_start_probing(netif);
-
-  return result;
-}
-
-static void
-autoip_start_probing(struct netif *netif)
-{
-  struct autoip *autoip = netif->autoip;
-
-  autoip->state = AUTOIP_STATE_PROBING;
-  autoip->sent_num = 0;
-  LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
-     ("autoip_start_probing(): changing state to PROBING: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
-      ip4_addr1_16(&netif->autoip->llipaddr), ip4_addr2_16(&netif->autoip->llipaddr),
-      ip4_addr3_16(&netif->autoip->llipaddr), ip4_addr4_16(&netif->autoip->llipaddr)));
-
-  /* time to wait to first probe, this is randomly
-   * choosen out of 0 to PROBE_WAIT seconds.
-   * compliant to RFC 3927 Section 2.2.1
-   */
-  autoip->ttw = (u16_t)(LWIP_AUTOIP_RAND(netif) % (PROBE_WAIT * AUTOIP_TICKS_PER_SECOND));
-
-  /*
-   * if we tried more then MAX_CONFLICTS we must limit our rate for
-   * accquiring and probing address
-   * compliant to RFC 3927 Section 2.2.1
-   */
-  if (autoip->tried_llipaddr > MAX_CONFLICTS) {
-    autoip->ttw = RATE_LIMIT_INTERVAL * AUTOIP_TICKS_PER_SECOND;
-  }
-}
-
-/**
- * Handle a possible change in the network configuration.
- *
- * If there is an AutoIP address configured, take the interface down
- * and begin probing with the same address.
- */
-void
-autoip_network_changed(struct netif *netif)
-{
-  if (netif->autoip && netif->autoip->state != AUTOIP_STATE_OFF) {
-    netif_set_down(netif);
-    autoip_start_probing(netif);
-  }
-}
-
-/**
- * Stop AutoIP client
- *
- * @param netif network interface on which stop the AutoIP client
- */
-err_t
-autoip_stop(struct netif *netif)
-{
-  netif->autoip->state = AUTOIP_STATE_OFF;
-  netif_set_down(netif);
-  return ERR_OK;
-}
-
-/**
- * Has to be called in loop every AUTOIP_TMR_INTERVAL milliseconds
- */
-void
-autoip_tmr()
-{
-  struct netif *netif = netif_list;
-  /* loop through netif's */
-  while (netif != NULL) {
-    /* only act on AutoIP configured interfaces */
-    if (netif->autoip != NULL) {
-      if (netif->autoip->lastconflict > 0) {
-        netif->autoip->lastconflict--;
-      }
-
-      LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
-        ("autoip_tmr() AutoIP-State: %"U16_F", ttw=%"U16_F"\n",
-        (u16_t)(netif->autoip->state), netif->autoip->ttw));
-
-      switch(netif->autoip->state) {
-        case AUTOIP_STATE_PROBING:
-          if (netif->autoip->ttw > 0) {
-            netif->autoip->ttw--;
-          } else {
-            if (netif->autoip->sent_num >= PROBE_NUM) {
-              netif->autoip->state = AUTOIP_STATE_ANNOUNCING;
-              netif->autoip->sent_num = 0;
-              netif->autoip->ttw = ANNOUNCE_WAIT * AUTOIP_TICKS_PER_SECOND;
-              LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
-                 ("autoip_tmr(): changing state to ANNOUNCING: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
-                  ip4_addr1_16(&netif->autoip->llipaddr), ip4_addr2_16(&netif->autoip->llipaddr),
-                  ip4_addr3_16(&netif->autoip->llipaddr), ip4_addr4_16(&netif->autoip->llipaddr)));
-            } else {
-              autoip_arp_probe(netif);
-              LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
-                ("autoip_tmr() PROBING Sent Probe\n"));
-              netif->autoip->sent_num++;
-              /* calculate time to wait to next probe */
-              netif->autoip->ttw = (u16_t)((LWIP_AUTOIP_RAND(netif) %
-                ((PROBE_MAX - PROBE_MIN) * AUTOIP_TICKS_PER_SECOND) ) +
-                PROBE_MIN * AUTOIP_TICKS_PER_SECOND);
-            }
-          }
-          break;
-
-        case AUTOIP_STATE_ANNOUNCING:
-          if (netif->autoip->ttw > 0) {
-            netif->autoip->ttw--;
-          } else {
-            if (netif->autoip->sent_num == 0) {
-             /* We are here the first time, so we waited ANNOUNCE_WAIT seconds
-              * Now we can bind to an IP address and use it.
-              *
-              * autoip_bind calls netif_set_up. This triggers a gratuitous ARP
-              * which counts as an announcement.
-              */
-              autoip_bind(netif);
-            } else {
-              autoip_arp_announce(netif);
-              LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
-                ("autoip_tmr() ANNOUNCING Sent Announce\n"));
-            }
-            netif->autoip->ttw = ANNOUNCE_INTERVAL * AUTOIP_TICKS_PER_SECOND;
-            netif->autoip->sent_num++;
-
-            if (netif->autoip->sent_num >= ANNOUNCE_NUM) {
-                netif->autoip->state = AUTOIP_STATE_BOUND;
-                netif->autoip->sent_num = 0;
-                netif->autoip->ttw = 0;
-                 LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
-                    ("autoip_tmr(): changing state to BOUND: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
-                     ip4_addr1_16(&netif->autoip->llipaddr), ip4_addr2_16(&netif->autoip->llipaddr),
-                     ip4_addr3_16(&netif->autoip->llipaddr), ip4_addr4_16(&netif->autoip->llipaddr)));
-            }
-          }
-          break;
-      }
-    }
-    /* proceed to next network interface */
-    netif = netif->next;
-  }
-}
-
-/**
- * Handles every incoming ARP Packet, called by etharp_arp_input.
- *
- * @param netif network interface to use for autoip processing
- * @param hdr Incoming ARP packet
- */
-void
-autoip_arp_reply(struct netif *netif, struct etharp_hdr *hdr)
-{
-  LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, ("autoip_arp_reply()\n"));
-  if ((netif->autoip != NULL) && (netif->autoip->state != AUTOIP_STATE_OFF)) {
-   /* when ip.src == llipaddr && hw.src != netif->hwaddr
-    *
-    * when probing  ip.dst == llipaddr && hw.src != netif->hwaddr
-    * we have a conflict and must solve it
-    */
-    ip_addr_t sipaddr, dipaddr;
-    struct eth_addr netifaddr;
-    ETHADDR16_COPY(netifaddr.addr, netif->hwaddr);
-
-    /* Copy struct ip_addr2 to aligned ip_addr, to support compilers without
-     * structure packing (not using structure copy which breaks strict-aliasing rules).
-     */
-    IPADDR2_COPY(&sipaddr, &hdr->sipaddr);
-    IPADDR2_COPY(&dipaddr, &hdr->dipaddr);
-      
-    if ((netif->autoip->state == AUTOIP_STATE_PROBING) ||
-        ((netif->autoip->state == AUTOIP_STATE_ANNOUNCING) &&
-         (netif->autoip->sent_num == 0))) {
-     /* RFC 3927 Section 2.2.1:
-      * from beginning to after ANNOUNCE_WAIT
-      * seconds we have a conflict if
-      * ip.src == llipaddr OR
-      * ip.dst == llipaddr && hw.src != own hwaddr
-      */
-      if ((ip_addr_cmp(&sipaddr, &netif->autoip->llipaddr)) ||
-          (ip_addr_cmp(&dipaddr, &netif->autoip->llipaddr) &&
-           !eth_addr_cmp(&netifaddr, &hdr->shwaddr))) {
-        LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING,
-          ("autoip_arp_reply(): Probe Conflict detected\n"));
-        autoip_restart(netif);
-      }
-    } else {
-     /* RFC 3927 Section 2.5:
-      * in any state we have a conflict if
-      * ip.src == llipaddr && hw.src != own hwaddr
-      */
-      if (ip_addr_cmp(&sipaddr, &netif->autoip->llipaddr) &&
-          !eth_addr_cmp(&netifaddr, &hdr->shwaddr)) {
-        LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING,
-          ("autoip_arp_reply(): Conflicting ARP-Packet detected\n"));
-        autoip_handle_arp_conflict(netif);
-      }
-    }
-  }
-}
-
-#endif /* LWIP_AUTOIP */
+/**
+ * @file
+ * AutoIP Automatic LinkLocal IP Configuration
+ *
+ * This is a AutoIP implementation for the lwIP TCP/IP stack. It aims to conform
+ * with RFC 3927.
+ *
+ * @defgroup autoip AUTOIP
+ * @ingroup ip4
+ * AUTOIP related functions
+ * USAGE:
+ *
+ * define @ref LWIP_AUTOIP 1 in your lwipopts.h
+ * Options:
+ * AUTOIP_TMR_INTERVAL msecs,
+ *   I recommend a value of 100. The value must divide 1000 with a remainder almost 0.
+ *   Possible values are 1000, 500, 333, 250, 200, 166, 142, 125, 111, 100 ....
+ *
+ * Without DHCP:
+ * - Call autoip_start() after netif_add().
+ *
+ * With DHCP:
+ * - define @ref LWIP_DHCP_AUTOIP_COOP 1 in your lwipopts.h.
+ * - Configure your DHCP Client.
+ * 
+ * @see netifapi_autoip
+ */
+
+/*
+ *
+ * Copyright (c) 2007 Dominik Spies <kontakt@dspies.de>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Dominik Spies <kontakt@dspies.de>
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV4 && LWIP_AUTOIP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/mem.h"
+/* #include "lwip/udp.h" */
+#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+#include "lwip/autoip.h"
+#include "lwip/etharp.h"
+#include "lwip/prot/autoip.h"
+
+#include <string.h>
+
+/** Pseudo random macro based on netif informations.
+ * You could use "rand()" from the C Library if you define LWIP_AUTOIP_RAND in lwipopts.h */
+#ifndef LWIP_AUTOIP_RAND
+#define LWIP_AUTOIP_RAND(netif) ( (((u32_t)((netif->hwaddr[5]) & 0xff) << 24) | \
+                                   ((u32_t)((netif->hwaddr[3]) & 0xff) << 16) | \
+                                   ((u32_t)((netif->hwaddr[2]) & 0xff) << 8) | \
+                                   ((u32_t)((netif->hwaddr[4]) & 0xff))) + \
+                                   (netif_autoip_data(netif)? netif_autoip_data(netif)->tried_llipaddr : 0))
+#endif /* LWIP_AUTOIP_RAND */
+
+/**
+ * Macro that generates the initial IP address to be tried by AUTOIP.
+ * If you want to override this, define it to something else in lwipopts.h.
+ */
+#ifndef LWIP_AUTOIP_CREATE_SEED_ADDR
+#define LWIP_AUTOIP_CREATE_SEED_ADDR(netif) \
+  lwip_htonl(AUTOIP_RANGE_START + ((u32_t)(((u8_t)(netif->hwaddr[4])) | \
+                 ((u32_t)((u8_t)(netif->hwaddr[5]))) << 8)))
+#endif /* LWIP_AUTOIP_CREATE_SEED_ADDR */
+
+/* static functions */
+static err_t autoip_arp_announce(struct netif *netif);
+static void autoip_start_probing(struct netif *netif);
+
+/**
+ * @ingroup autoip 
+ * Set a statically allocated struct autoip to work with.
+ * Using this prevents autoip_start to allocate it using mem_malloc.
+ *
+ * @param netif the netif for which to set the struct autoip
+ * @param autoip (uninitialised) autoip struct allocated by the application
+ */
+void
+autoip_set_struct(struct netif *netif, struct autoip *autoip)
+{
+  LWIP_ASSERT("netif != NULL", netif != NULL);
+  LWIP_ASSERT("autoip != NULL", autoip != NULL);
+  LWIP_ASSERT("netif already has a struct autoip set",
+              netif_autoip_data(netif) == NULL);
+
+  /* clear data structure */
+  memset(autoip, 0, sizeof(struct autoip));
+  /* autoip->state = AUTOIP_STATE_OFF; */
+  netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_AUTOIP, autoip);
+}
+
+/** Restart AutoIP client and check the next address (conflict detected)
+ *
+ * @param netif The netif under AutoIP control
+ */
+static void
+autoip_restart(struct netif *netif)
+{
+  struct autoip* autoip = netif_autoip_data(netif);
+  autoip->tried_llipaddr++;
+  autoip_start(netif);
+}
+
+/**
+ * Handle a IP address conflict after an ARP conflict detection
+ */
+static void
+autoip_handle_arp_conflict(struct netif *netif)
+{
+  struct autoip* autoip = netif_autoip_data(netif);
+
+  /* RFC3927, 2.5 "Conflict Detection and Defense" allows two options where
+     a) means retreat on the first conflict and
+     b) allows to keep an already configured address when having only one
+        conflict in 10 seconds
+     We use option b) since it helps to improve the chance that one of the two
+     conflicting hosts may be able to retain its address. */
+
+  if (autoip->lastconflict > 0) {
+    /* retreat, there was a conflicting ARP in the last DEFEND_INTERVAL seconds */
+    LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+      ("autoip_handle_arp_conflict(): we are defending, but in DEFEND_INTERVAL, retreating\n"));
+
+    /* Active TCP sessions are aborted when removing the ip addresss */
+    autoip_restart(netif);
+  } else {
+    LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+      ("autoip_handle_arp_conflict(): we are defend, send ARP Announce\n"));
+    autoip_arp_announce(netif);
+    autoip->lastconflict = DEFEND_INTERVAL * AUTOIP_TICKS_PER_SECOND;
+  }
+}
+
+/**
+ * Create an IP-Address out of range 169.254.1.0 to 169.254.254.255
+ *
+ * @param netif network interface on which create the IP-Address
+ * @param ipaddr ip address to initialize
+ */
+static void
+autoip_create_addr(struct netif *netif, ip4_addr_t *ipaddr)
+{
+  struct autoip* autoip = netif_autoip_data(netif);
+
+  /* Here we create an IP-Address out of range 169.254.1.0 to 169.254.254.255
+   * compliant to RFC 3927 Section 2.1
+   * We have 254 * 256 possibilities */
+
+  u32_t addr = lwip_ntohl(LWIP_AUTOIP_CREATE_SEED_ADDR(netif));
+  addr += autoip->tried_llipaddr;
+  addr = AUTOIP_NET | (addr & 0xffff);
+  /* Now, 169.254.0.0 <= addr <= 169.254.255.255 */
+
+  if (addr < AUTOIP_RANGE_START) {
+    addr += AUTOIP_RANGE_END - AUTOIP_RANGE_START + 1;
+  }
+  if (addr > AUTOIP_RANGE_END) {
+    addr -= AUTOIP_RANGE_END - AUTOIP_RANGE_START + 1;
+  }
+  LWIP_ASSERT("AUTOIP address not in range", (addr >= AUTOIP_RANGE_START) &&
+    (addr <= AUTOIP_RANGE_END));
+  ip4_addr_set_u32(ipaddr, lwip_htonl(addr));
+
+  LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+    ("autoip_create_addr(): tried_llipaddr=%"U16_F", %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+    (u16_t)(autoip->tried_llipaddr), ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr),
+    ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr)));
+}
+
+/**
+ * Sends an ARP probe from a network interface
+ *
+ * @param netif network interface used to send the probe
+ */
+static err_t
+autoip_arp_probe(struct netif *netif)
+{
+  struct autoip* autoip = netif_autoip_data(netif);
+  /* this works because netif->ip_addr is ANY */
+  return etharp_request(netif, &autoip->llipaddr);
+}
+
+/**
+ * Sends an ARP announce from a network interface
+ *
+ * @param netif network interface used to send the announce
+ */
+static err_t
+autoip_arp_announce(struct netif *netif)
+{
+  return etharp_gratuitous(netif);
+}
+
+/**
+ * Configure interface for use with current LL IP-Address
+ *
+ * @param netif network interface to configure with current LL IP-Address
+ */
+static err_t
+autoip_bind(struct netif *netif)
+{
+  struct autoip* autoip = netif_autoip_data(netif);
+  ip4_addr_t sn_mask, gw_addr;
+
+  LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
+    ("autoip_bind(netif=%p) %c%c%"U16_F" %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+    (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num,
+    ip4_addr1_16(&autoip->llipaddr), ip4_addr2_16(&autoip->llipaddr),
+    ip4_addr3_16(&autoip->llipaddr), ip4_addr4_16(&autoip->llipaddr)));
+
+  IP4_ADDR(&sn_mask, 255, 255, 0, 0);
+  IP4_ADDR(&gw_addr, 0, 0, 0, 0);
+
+  netif_set_addr(netif, &autoip->llipaddr, &sn_mask, &gw_addr);
+  /* interface is used by routing now that an address is set */
+
+  return ERR_OK;
+}
+
+/**
+ * @ingroup autoip 
+ * Start AutoIP client
+ *
+ * @param netif network interface on which start the AutoIP client
+ */
+err_t
+autoip_start(struct netif *netif)
+{
+  struct autoip* autoip = netif_autoip_data(netif);
+  err_t result = ERR_OK;
+
+  LWIP_ERROR("netif is not up, old style port?", netif_is_up(netif), return ERR_ARG;);
+
+  /* Set IP-Address, Netmask and Gateway to 0 to make sure that
+   * ARP Packets are formed correctly
+   */
+  netif_set_addr(netif, IP4_ADDR_ANY4, IP4_ADDR_ANY4, IP4_ADDR_ANY4);
+
+  LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+    ("autoip_start(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0],
+    netif->name[1], (u16_t)netif->num));
+  if (autoip == NULL) {
+    /* no AutoIP client attached yet? */
+    LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
+      ("autoip_start(): starting new AUTOIP client\n"));
+    autoip = (struct autoip *)mem_malloc(sizeof(struct autoip));
+    if (autoip == NULL) {
+      LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
+        ("autoip_start(): could not allocate autoip\n"));
+      return ERR_MEM;
+    }
+    memset(autoip, 0, sizeof(struct autoip));
+    /* store this AutoIP client in the netif */
+    netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_AUTOIP, autoip);
+    LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, ("autoip_start(): allocated autoip"));
+  } else {
+    autoip->state = AUTOIP_STATE_OFF;
+    autoip->ttw = 0;
+    autoip->sent_num = 0;
+    ip4_addr_set_zero(&autoip->llipaddr);
+    autoip->lastconflict = 0;
+  }
+
+  autoip_create_addr(netif, &(autoip->llipaddr));
+  autoip_start_probing(netif);
+
+  return result;
+}
+
+static void
+autoip_start_probing(struct netif *netif)
+{
+  struct autoip* autoip = netif_autoip_data(netif);
+
+  autoip->state = AUTOIP_STATE_PROBING;
+  autoip->sent_num = 0;
+  LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+     ("autoip_start_probing(): changing state to PROBING: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+      ip4_addr1_16(&autoip->llipaddr), ip4_addr2_16(&autoip->llipaddr),
+      ip4_addr3_16(&autoip->llipaddr), ip4_addr4_16(&autoip->llipaddr)));
+
+  /* time to wait to first probe, this is randomly
+   * chosen out of 0 to PROBE_WAIT seconds.
+   * compliant to RFC 3927 Section 2.2.1
+   */
+  autoip->ttw = (u16_t)(LWIP_AUTOIP_RAND(netif) % (PROBE_WAIT * AUTOIP_TICKS_PER_SECOND));
+
+  /*
+   * if we tried more then MAX_CONFLICTS we must limit our rate for
+   * acquiring and probing address
+   * compliant to RFC 3927 Section 2.2.1
+   */
+  if (autoip->tried_llipaddr > MAX_CONFLICTS) {
+    autoip->ttw = RATE_LIMIT_INTERVAL * AUTOIP_TICKS_PER_SECOND;
+  }
+}
+
+/**
+ * Handle a possible change in the network configuration.
+ *
+ * If there is an AutoIP address configured, take the interface down
+ * and begin probing with the same address.
+ */
+void
+autoip_network_changed(struct netif *netif)
+{
+  struct autoip* autoip = netif_autoip_data(netif);
+
+  if (autoip && (autoip->state != AUTOIP_STATE_OFF)) {
+    autoip_start_probing(netif);
+  }
+}
+
+/**
+ * @ingroup autoip 
+ * Stop AutoIP client
+ *
+ * @param netif network interface on which stop the AutoIP client
+ */
+err_t
+autoip_stop(struct netif *netif)
+{
+  struct autoip* autoip = netif_autoip_data(netif);
+
+  if (autoip != NULL) {
+    autoip->state = AUTOIP_STATE_OFF;
+    if (ip4_addr_islinklocal(netif_ip4_addr(netif))) {
+      netif_set_addr(netif, IP4_ADDR_ANY4, IP4_ADDR_ANY4, IP4_ADDR_ANY4);
+    }
+  }
+  return ERR_OK;
+}
+
+/**
+ * Has to be called in loop every AUTOIP_TMR_INTERVAL milliseconds
+ */
+void
+autoip_tmr(void)
+{
+  struct netif *netif = netif_list;
+  /* loop through netif's */
+  while (netif != NULL) {
+    struct autoip* autoip = netif_autoip_data(netif);
+    /* only act on AutoIP configured interfaces */
+    if (autoip != NULL) {
+      if (autoip->lastconflict > 0) {
+        autoip->lastconflict--;
+      }
+
+      LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
+        ("autoip_tmr() AutoIP-State: %"U16_F", ttw=%"U16_F"\n",
+        (u16_t)(autoip->state), autoip->ttw));
+
+      if (autoip->ttw > 0) {
+        autoip->ttw--;
+      }
+
+      switch(autoip->state) {
+        case AUTOIP_STATE_PROBING:
+          if (autoip->ttw == 0) {
+            if (autoip->sent_num >= PROBE_NUM) {
+              /* Switch to ANNOUNCING: now we can bind to an IP address and use it */
+              autoip->state = AUTOIP_STATE_ANNOUNCING;
+              autoip_bind(netif);
+              /* autoip_bind() calls netif_set_addr(): this triggers a gratuitous ARP
+                 which counts as an announcement */
+              autoip->sent_num = 1;
+              autoip->ttw = ANNOUNCE_WAIT * AUTOIP_TICKS_PER_SECOND;
+              LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+                 ("autoip_tmr(): changing state to ANNOUNCING: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+                  ip4_addr1_16(&autoip->llipaddr), ip4_addr2_16(&autoip->llipaddr),
+                  ip4_addr3_16(&autoip->llipaddr), ip4_addr4_16(&autoip->llipaddr)));
+            } else {
+              autoip_arp_probe(netif);
+              LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, ("autoip_tmr() PROBING Sent Probe\n"));
+              autoip->sent_num++;
+              if (autoip->sent_num == PROBE_NUM) {
+                /* calculate time to wait to for announce */
+                autoip->ttw = ANNOUNCE_WAIT * AUTOIP_TICKS_PER_SECOND;
+              } else {
+                /* calculate time to wait to next probe */
+                autoip->ttw = (u16_t)((LWIP_AUTOIP_RAND(netif) %
+                  ((PROBE_MAX - PROBE_MIN) * AUTOIP_TICKS_PER_SECOND) ) +
+                  PROBE_MIN * AUTOIP_TICKS_PER_SECOND);
+              }
+            }
+          }
+          break;
+
+        case AUTOIP_STATE_ANNOUNCING:
+          if (autoip->ttw == 0) {
+            autoip_arp_announce(netif);
+            LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, ("autoip_tmr() ANNOUNCING Sent Announce\n"));
+            autoip->ttw = ANNOUNCE_INTERVAL * AUTOIP_TICKS_PER_SECOND;
+            autoip->sent_num++;
+
+            if (autoip->sent_num >= ANNOUNCE_NUM) {
+                autoip->state = AUTOIP_STATE_BOUND;
+                autoip->sent_num = 0;
+                autoip->ttw = 0;
+                 LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+                    ("autoip_tmr(): changing state to BOUND: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+                     ip4_addr1_16(&autoip->llipaddr), ip4_addr2_16(&autoip->llipaddr),
+                     ip4_addr3_16(&autoip->llipaddr), ip4_addr4_16(&autoip->llipaddr)));
+            }
+          }
+          break;
+
+        default:
+          /* nothing to do in other states */
+          break;
+      }
+    }
+    /* proceed to next network interface */
+    netif = netif->next;
+  }
+}
+
+/**
+ * Handles every incoming ARP Packet, called by etharp_input().
+ *
+ * @param netif network interface to use for autoip processing
+ * @param hdr Incoming ARP packet
+ */
+void
+autoip_arp_reply(struct netif *netif, struct etharp_hdr *hdr)
+{
+  struct autoip* autoip = netif_autoip_data(netif);
+
+  LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, ("autoip_arp_reply()\n"));
+  if ((autoip != NULL) && (autoip->state != AUTOIP_STATE_OFF)) {
+   /* when ip.src == llipaddr && hw.src != netif->hwaddr
+    *
+    * when probing  ip.dst == llipaddr && hw.src != netif->hwaddr
+    * we have a conflict and must solve it
+    */
+    ip4_addr_t sipaddr, dipaddr;
+    struct eth_addr netifaddr;
+    ETHADDR16_COPY(netifaddr.addr, netif->hwaddr);
+
+    /* Copy struct ip4_addr2 to aligned ip4_addr, to support compilers without
+     * structure packing (not using structure copy which breaks strict-aliasing rules).
+     */
+    IPADDR2_COPY(&sipaddr, &hdr->sipaddr);
+    IPADDR2_COPY(&dipaddr, &hdr->dipaddr);
+
+    if (autoip->state == AUTOIP_STATE_PROBING) {
+     /* RFC 3927 Section 2.2.1:
+      * from beginning to after ANNOUNCE_WAIT
+      * seconds we have a conflict if
+      * ip.src == llipaddr OR
+      * ip.dst == llipaddr && hw.src != own hwaddr
+      */
+      if ((ip4_addr_cmp(&sipaddr, &autoip->llipaddr)) ||
+          (ip4_addr_isany_val(sipaddr) &&
+           ip4_addr_cmp(&dipaddr, &autoip->llipaddr) &&
+           !eth_addr_cmp(&netifaddr, &hdr->shwaddr))) {
+        LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING,
+          ("autoip_arp_reply(): Probe Conflict detected\n"));
+        autoip_restart(netif);
+      }
+    } else {
+     /* RFC 3927 Section 2.5:
+      * in any state we have a conflict if
+      * ip.src == llipaddr && hw.src != own hwaddr
+      */
+      if (ip4_addr_cmp(&sipaddr, &autoip->llipaddr) &&
+          !eth_addr_cmp(&netifaddr, &hdr->shwaddr)) {
+        LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING,
+          ("autoip_arp_reply(): Conflicting ARP-Packet detected\n"));
+        autoip_handle_arp_conflict(netif);
+      }
+    }
+  }
+}
+
+/** check if AutoIP supplied netif->ip_addr
+ *
+ * @param netif the netif to check
+ * @return 1 if AutoIP supplied netif->ip_addr (state BOUND or ANNOUNCING),
+ *         0 otherwise
+ */
+u8_t
+autoip_supplied_address(const struct netif *netif)
+{
+  if ((netif != NULL) && (netif_autoip_data(netif) != NULL)) {
+    struct autoip* autoip = netif_autoip_data(netif);
+    return (autoip->state == AUTOIP_STATE_BOUND) || (autoip->state == AUTOIP_STATE_ANNOUNCING);
+  }
+  return 0;
+}
+
+u8_t
+autoip_accept_packet(struct netif *netif, const ip4_addr_t *addr)
+{
+  struct autoip* autoip = netif_autoip_data(netif);
+  return (autoip != NULL) && ip4_addr_cmp(addr, &(autoip->llipaddr));
+}
+
+#endif /* LWIP_IPV4 && LWIP_AUTOIP */

+ 1950 - 1770
thirdparty/lwip/src/core/dhcp.c → thirdparty/lwip/src/core/ipv4/dhcp.c

@@ -1,1770 +1,1950 @@
-/**
- * @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:
- * - 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 "lwip/opt.h"
-
-#if LWIP_DHCP /* don't build if not configured for use in lwipopts.h */
-
-#include "lwip/stats.h"
-#include "lwip/mem.h"
-#include "lwip/udp.h"
-#include "lwip/ip_addr.h"
-#include "lwip/netif.h"
-#include "lwip/def.h"
-#include "lwip/dhcp.h"
-#include "lwip/autoip.h"
-#include "lwip/dns.h"
-#include "netif/etharp.h"
-
-#include <string.h>
-
-/** DHCP_CREATE_RAND_XID: if this is set to 1, the xid is created using
- * LWIP_RAND() (this overrides DHCP_GLOBAL_XID)
- */
-#ifndef DHCP_CREATE_RAND_XID
-#define DHCP_CREATE_RAND_XID 1
-#endif
-
-/** Default for DHCP_GLOBAL_XID is 0xABCD0000
- * This can be changed by defining DHCP_GLOBAL_XID and DHCP_GLOBAL_XID_HEADER, e.g.
- *  #define DHCP_GLOBAL_XID_HEADER "stdlib.h"
- *  #define DHCP_GLOBAL_XID rand()
- */
-#ifdef DHCP_GLOBAL_XID_HEADER
-#include DHCP_GLOBAL_XID_HEADER /* include optional starting XID generation prototypes */
-#endif
-
-/** DHCP_OPTION_MAX_MSG_SIZE is set to the MTU
- * MTU is checked to be big enough in dhcp_start */
-#define DHCP_MAX_MSG_LEN(netif)        (netif->mtu)
-#define DHCP_MAX_MSG_LEN_MIN_REQUIRED  576
-/** Minimum length for reply before packet is parsed */
-#define DHCP_MIN_REPLY_LEN             44
-
-#define REBOOT_TRIES 2
-
-/** Option handling: options are parsed in dhcp_parse_reply
- * and saved in an array where other functions can load them from.
- * This might be moved into the struct dhcp (not necessarily since
- * lwIP is single-threaded and the array is only used while in recv
- * callback). */
-#define DHCP_OPTION_IDX_OVERLOAD    0
-#define DHCP_OPTION_IDX_MSG_TYPE    1
-#define DHCP_OPTION_IDX_SERVER_ID   2
-#define DHCP_OPTION_IDX_LEASE_TIME  3
-#define DHCP_OPTION_IDX_T1          4
-#define DHCP_OPTION_IDX_T2          5
-#define DHCP_OPTION_IDX_SUBNET_MASK 6
-#define DHCP_OPTION_IDX_ROUTER      7
-#define DHCP_OPTION_IDX_DNS_SERVER  8
-#define DHCP_OPTION_IDX_MAX         (DHCP_OPTION_IDX_DNS_SERVER + DNS_MAX_SERVERS)
-
-/** Holds the decoded option values, only valid while in dhcp_recv.
-    @todo: move this into struct dhcp? */
-u32_t dhcp_rx_options_val[DHCP_OPTION_IDX_MAX];
-/** Holds a flag which option was received and is contained in dhcp_rx_options_val,
-    only valid while in dhcp_recv.
-    @todo: move this into struct dhcp? */
-u8_t  dhcp_rx_options_given[DHCP_OPTION_IDX_MAX];
-
-#ifdef DHCP_GLOBAL_XID
-static u32_t xid;
-static u8_t xid_initialised;
-#endif /* DHCP_GLOBAL_XID */
-
-#define dhcp_option_given(dhcp, idx)          (dhcp_rx_options_given[idx] != 0)
-#define dhcp_got_option(dhcp, idx)            (dhcp_rx_options_given[idx] = 1)
-#define dhcp_clear_option(dhcp, idx)          (dhcp_rx_options_given[idx] = 0)
-#define dhcp_clear_all_options(dhcp)          (memset(dhcp_rx_options_given, 0, sizeof(dhcp_rx_options_given)))
-#define dhcp_get_option_value(dhcp, idx)      (dhcp_rx_options_val[idx])
-#define dhcp_set_option_value(dhcp, idx, val) (dhcp_rx_options_val[idx] = (val))
-
-
-/* DHCP client state machine functions */
-static err_t dhcp_discover(struct netif *netif);
-static err_t dhcp_select(struct netif *netif);
-static void dhcp_bind(struct netif *netif);
-#if DHCP_DOES_ARP_CHECK
-static err_t dhcp_decline(struct netif *netif);
-#endif /* DHCP_DOES_ARP_CHECK */
-static err_t dhcp_rebind(struct netif *netif);
-static err_t dhcp_reboot(struct netif *netif);
-static void dhcp_set_state(struct dhcp *dhcp, u8_t new_state);
-
-/* receive, unfold, parse and free incoming messages */
-static void dhcp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port);
-
-/* set the DHCP timers */
-static void dhcp_timeout(struct netif *netif);
-static void dhcp_t1_timeout(struct netif *netif);
-static void dhcp_t2_timeout(struct netif *netif);
-
-/* build outgoing messages */
-/* create a DHCP message, fill in common headers */
-static err_t dhcp_create_msg(struct netif *netif, struct dhcp *dhcp, u8_t message_type);
-/* free a DHCP request */
-static void dhcp_delete_msg(struct dhcp *dhcp);
-/* 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);
-#if LWIP_NETIF_HOSTNAME
-static void dhcp_option_hostname(struct dhcp *dhcp, struct netif *netif);
-#endif /* LWIP_NETIF_HOSTNAME */
-/* 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 clear any existing set IP address and restart DHCP negotiation
- * afresh (as per RFC2131 3.2.3).
- *
- * @param netif the netif under DHCP control
- */
-static void
-dhcp_handle_nak(struct netif *netif)
-{
-  struct dhcp *dhcp = netif->dhcp;
-  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_handle_nak(netif=%p) %c%c%"U16_F"\n", 
-    (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num));
-  /* Set the interface down since the address must no longer be used, as per RFC2131 */
-  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); 
-  /* Change to a defined state */
-  dhcp_set_state(dhcp, DHCP_BACKING_OFF);
-  /* We can immediately restart discovery */
-  dhcp_discover(netif);
-}
-
-#if DHCP_DOES_ARP_CHECK
-/**
- * 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.
- *
- * @param netif the netif under DHCP control
- */
-static void
-dhcp_check(struct netif *netif)
-{
-  struct dhcp *dhcp = netif->dhcp;
-  err_t result;
-  u16_t msecs;
-  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_check(netif=%p) %c%c\n", (void *)netif, (s16_t)netif->name[0],
-    (s16_t)netif->name[1]));
-  dhcp_set_state(dhcp, DHCP_CHECKING);
-  /* 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 | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("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 | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_check(): set request timeout %"U16_F" msecs\n", msecs));
-}
-#endif /* DHCP_DOES_ARP_CHECK */
-
-/**
- * Remember the configuration offered by a DHCP server.
- *
- * @param netif the netif under DHCP control
- */
-static void
-dhcp_handle_offer(struct netif *netif)
-{
-  struct dhcp *dhcp = netif->dhcp;
-  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_handle_offer(netif=%p) %c%c%"U16_F"\n",
-    (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num));
-  /* obtain the server address */
-  if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_SERVER_ID)) {
-    ip4_addr_set_u32(&dhcp->server_ip_addr, htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_SERVER_ID)));
-    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_handle_offer(): server 0x%08"X32_F"\n",
-      ip4_addr_get_u32(&dhcp->server_ip_addr)));
-    /* remember offered address */
-    ip_addr_copy(dhcp->offered_ip_addr, dhcp->msg_in->yiaddr);
-    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_handle_offer(): offer for 0x%08"X32_F"\n",
-      ip4_addr_get_u32(&dhcp->offered_ip_addr)));
-
-    dhcp_select(netif);
-  } else {
-    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
-      ("dhcp_handle_offer(netif=%p) did not get server ID!\n", (void*)netif));
-  }
-}
-
-/**
- * Select a DHCP server offer out of all offers.
- *
- * Simply select the first offer received.
- *
- * @param netif the netif under DHCP control
- * @return lwIP specific error (see error.h)
- */
-static err_t
-dhcp_select(struct netif *netif)
-{
-  struct dhcp *dhcp = netif->dhcp;
-  err_t result;
-  u16_t msecs;
-
-  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_select(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num));
-  dhcp_set_state(dhcp, DHCP_REQUESTING);
-
-  /* create and initialize the DHCP message header */
-  result = dhcp_create_msg(netif, dhcp, DHCP_REQUEST);
-  if (result == ERR_OK) {
-    dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
-    dhcp_option_short(dhcp, DHCP_MAX_MSG_LEN(netif));
-
-    /* MUST request the offered IP address */
-    dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4);
-    dhcp_option_long(dhcp, ntohl(ip4_addr_get_u32(&dhcp->offered_ip_addr)));
-
-    dhcp_option(dhcp, DHCP_OPTION_SERVER_ID, 4);
-    dhcp_option_long(dhcp, ntohl(ip4_addr_get_u32(&dhcp->server_ip_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);
-
-#if LWIP_NETIF_HOSTNAME
-    dhcp_option_hostname(dhcp, netif);
-#endif /* LWIP_NETIF_HOSTNAME */
-
-    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);
-
-    /* send broadcast to any DHCP server */
-    udp_sendto_if(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif);
-    dhcp_delete_msg(dhcp);
-    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_select: REQUESTING\n"));
-  } else {
-    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("dhcp_select: could not allocate DHCP request\n"));
-  }
-  dhcp->tries++;
-  msecs = (dhcp->tries < 6 ? 1 << dhcp->tries : 60) * 1000;
-  dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
-  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_select(): set request timeout %"U16_F" msecs\n", msecs));
-  return result;
-}
-
-/**
- * The DHCP timer that checks for lease renewal/rebind timeouts.
- */
-void
-dhcp_coarse_tmr()
-{
-  struct netif *netif = netif_list;
-  LWIP_DEBUGF(DHCP_DEBUG | LWIP_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 | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_coarse_tmr(): t2 timeout\n"));
-        /* this clients' rebind timeout triggered */
-        dhcp_t2_timeout(netif);
-      /* timer is active (non zero), and triggers (zeroes) now */
-      } else if (netif->dhcp->t1_timeout-- == 1) {
-        LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_coarse_tmr(): t1 timeout\n"));
-        /* this clients' renewal timeout triggered */
-        dhcp_t1_timeout(netif);
-      }
-    }
-    /* 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--;
-      }
-      else if (netif->dhcp->request_timeout == 1) {
-        netif->dhcp->request_timeout--;
-        /* { netif->dhcp->request_timeout == 0 } */
-        LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_fine_tmr(): request timeout\n"));
-        /* this client's request timeout triggered */
-        dhcp_timeout(netif);
-      }
-    }
-    /* 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 | LWIP_DBG_TRACE, ("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 | LWIP_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 | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): REQUESTING, DHCP request timed out\n"));
-    if (dhcp->tries <= 5) {
-      dhcp_select(netif);
-    } else {
-      LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): REQUESTING, releasing, restarting\n"));
-      dhcp_release(netif);
-      dhcp_discover(netif);
-    }
-#if DHCP_DOES_ARP_CHECK
-  /* received no ARP reply for the offered address (which is good) */
-  } else if (dhcp->state == DHCP_CHECKING) {
-    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_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);
-    }
-#endif /* DHCP_DOES_ARP_CHECK */
-  }
-  /* did not get response to renew request? */
-  else if (dhcp->state == DHCP_RENEWING) {
-    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_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 | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): REBINDING, DHCP request timed out\n"));
-    if (dhcp->tries <= 8) {
-      dhcp_rebind(netif);
-    } else {
-      LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): RELEASING, DISCOVERING\n"));
-      dhcp_release(netif);
-      dhcp_discover(netif);
-    }
-  } else if (dhcp->state == DHCP_REBOOTING) {
-    if (dhcp->tries < REBOOT_TRIES) {
-      dhcp_reboot(netif);
-    } else {
-      dhcp_discover(netif);
-    }
-  }
-}
-
-/**
- * The renewal period has timed out.
- *
- * @param netif the netif under DHCP control
- */
-static void
-dhcp_t1_timeout(struct netif *netif)
-{
-  struct dhcp *dhcp = netif->dhcp;
-  LWIP_DEBUGF(DHCP_DEBUG | LWIP_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 | LWIP_DBG_TRACE | LWIP_DBG_STATE,
-                ("dhcp_t1_timeout(): must renew\n"));
-    /* This slightly different to RFC2131: DHCPREQUEST will be sent from state
-       DHCP_RENEWING, not DHCP_BOUND */
-    dhcp_renew(netif);
-  }
-}
-
-/**
- * The rebind period has timed out.
- *
- * @param netif the netif under DHCP control
- */
-static void
-dhcp_t2_timeout(struct netif *netif)
-{
-  struct dhcp *dhcp = netif->dhcp;
-  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_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 | LWIP_DBG_TRACE | LWIP_DBG_STATE,
-                ("dhcp_t2_timeout(): must rebind\n"));
-    /* This slightly different to RFC2131: DHCPREQUEST will be sent from state
-       DHCP_REBINDING, not DHCP_BOUND */
-    dhcp_rebind(netif);
-  }
-}
-
-/**
- * Handle a DHCP ACK packet
- *
- * @param netif the netif under DHCP control
- */
-static void
-dhcp_handle_ack(struct netif *netif)
-{
-  struct dhcp *dhcp = netif->dhcp;
-#if LWIP_DNS
-  u8_t n;
-#endif /* LWIP_DNS */
-
-  /* clear options we might not get from the ACK */
-  ip_addr_set_zero(&dhcp->offered_sn_mask);
-  ip_addr_set_zero(&dhcp->offered_gw_addr);
-#if LWIP_DHCP_BOOTP_FILE
-  ip_addr_set_zero(&dhcp->offered_si_addr);
-#endif /* LWIP_DHCP_BOOTP_FILE */
-
-  /* lease time given? */
-  if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_LEASE_TIME)) {
-    /* remember offered lease time */
-    dhcp->offered_t0_lease = dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_LEASE_TIME);
-  }
-  /* renewal period given? */
-  if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_T1)) {
-    /* remember given renewal period */
-    dhcp->offered_t1_renew = dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_T1);
-  } else {
-    /* calculate safe periods for renewal */
-    dhcp->offered_t1_renew = dhcp->offered_t0_lease / 2;
-  }
-
-  /* renewal period given? */
-  if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_T2)) {
-    /* remember given rebind period */
-    dhcp->offered_t2_rebind = dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_T2);
-  } else {
-    /* calculate safe periods for rebinding */
-    dhcp->offered_t2_rebind = dhcp->offered_t0_lease;
-  }
-
-  /* (y)our internet address */
-  ip_addr_copy(dhcp->offered_ip_addr, dhcp->msg_in->yiaddr);
-
-#if LWIP_DHCP_BOOTP_FILE
-  /* copy boot server address,
-     boot file name copied in dhcp_parse_reply if not overloaded */
-  ip_addr_copy(dhcp->offered_si_addr, dhcp->msg_in->siaddr);
-#endif /* LWIP_DHCP_BOOTP_FILE */
-
-  /* subnet mask given? */
-  if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_SUBNET_MASK)) {
-    /* remember given subnet mask */
-    ip4_addr_set_u32(&dhcp->offered_sn_mask, htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_SUBNET_MASK)));
-    dhcp->subnet_mask_given = 1;
-  } else {
-    dhcp->subnet_mask_given = 0;
-  }
-
-  /* gateway router */
-  if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_ROUTER)) {
-    ip4_addr_set_u32(&dhcp->offered_gw_addr, htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_ROUTER)));
-  }
-  
-#if LWIP_DNS
-  /* DNS servers */
-  n = 0;
-  while(dhcp_option_given(dhcp, DHCP_OPTION_IDX_DNS_SERVER + n) && (n < DNS_MAX_SERVERS)) {
-    ip_addr_t dns_addr;
-    ip4_addr_set_u32(&dns_addr, htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_DNS_SERVER + n)));
-    dns_setserver(n, &dns_addr);
-    n++;
-  }
-#endif /* LWIP_DNS */
-}
-
-/** Set a statically allocated struct dhcp to work with.
- * Using this prevents dhcp_start to allocate it using mem_malloc.
- *
- * @param netif the netif for which to set the struct dhcp
- * @param dhcp (uninitialised) dhcp struct allocated by the application
- */
-void
-dhcp_set_struct(struct netif *netif, struct dhcp *dhcp)
-{
-  LWIP_ASSERT("netif != NULL", netif != NULL);
-  LWIP_ASSERT("dhcp != NULL", dhcp != NULL);
-  LWIP_ASSERT("netif already has a struct dhcp set", netif->dhcp == NULL);
-
-  /* clear data structure */
-  memset(dhcp, 0, sizeof(struct dhcp));
-  /* dhcp_set_state(&dhcp, DHCP_OFF); */
-  netif->dhcp = dhcp;
-}
-
-/** Removes a struct dhcp from a netif.
- *
- * ATTENTION: Only use this when not using dhcp_set_struct() to allocate the
- *            struct dhcp since the memory is passed back to the heap.
- *
- * @param netif the netif from which to remove the struct dhcp
- */
-void dhcp_cleanup(struct netif *netif)
-{
-  LWIP_ASSERT("netif != NULL", netif != NULL);
-
-  if (netif->dhcp != NULL) {
-    mem_free(netif->dhcp);
-    netif->dhcp = NULL;
-  }
-}
-
-/**
- * Start DHCP negotiation for a network interface.
- *
- * If no DHCP client instance was attached to this interface,
- * a new client is created first. If a DHCP client instance
- * was already present, it restarts negotiation.
- *
- * @param netif The lwIP network interface
- * @return lwIP error code
- * - ERR_OK - No error
- * - ERR_MEM - Out of memory
- */
-err_t
-dhcp_start(struct netif *netif)
-{
-  struct dhcp *dhcp;
-  err_t result = ERR_OK;
-
-  LWIP_ERROR("netif != NULL", (netif != NULL), return ERR_ARG;);
-  dhcp = netif->dhcp;
-  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_start(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num));
-  /* Remove the flag that says this netif is handled by DHCP,
-     it is set when we succeeded starting. */
-  netif->flags &= ~NETIF_FLAG_DHCP;
-
-  /* check hwtype of the netif */
-  if ((netif->flags & NETIF_FLAG_ETHARP) == 0) {
-    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): No ETHARP netif\n"));
-    return ERR_ARG;
-  }
-
-  /* check MTU of the netif */
-  if (netif->mtu < DHCP_MAX_MSG_LEN_MIN_REQUIRED) {
-    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): Cannot use this netif with DHCP: MTU is too small\n"));
-    return ERR_MEM;
-  }
-
-  /* no DHCP client attached yet? */
-  if (dhcp == NULL) {
-    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): starting new DHCP client\n"));
-    dhcp = (struct dhcp *)mem_malloc(sizeof(struct dhcp));
-    if (dhcp == NULL) {
-      LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): could not allocate dhcp\n"));
-      return ERR_MEM;
-    }
-    /* store this dhcp client in the netif */
-    netif->dhcp = dhcp;
-    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): allocated dhcp"));
-  /* already has DHCP client attached */
-  } else {
-    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_start(): restarting DHCP configuration\n"));
-    if (dhcp->pcb != NULL) {
-      udp_remove(dhcp->pcb);
-    }
-    LWIP_ASSERT("pbuf p_out wasn't freed", dhcp->p_out == NULL);
-    LWIP_ASSERT("reply wasn't freed", dhcp->msg_in == NULL );
-  }
-    
-  /* clear data structure */
-  memset(dhcp, 0, sizeof(struct dhcp));
-  /* dhcp_set_state(&dhcp, DHCP_OFF); */
-  /* allocate UDP PCB */
-  dhcp->pcb = udp_new();
-  if (dhcp->pcb == NULL) {
-    LWIP_DEBUGF(DHCP_DEBUG  | LWIP_DBG_TRACE, ("dhcp_start(): could not obtain pcb\n"));
-    return ERR_MEM;
-  }
-  ip_set_option(dhcp->pcb, SOF_BROADCAST);
-  /* set up local and remote port for the pcb */
-  udp_bind(dhcp->pcb, IP_ADDR_ANY, DHCP_CLIENT_PORT);
-  udp_connect(dhcp->pcb, IP_ADDR_ANY, DHCP_SERVER_PORT);
-  /* set up the recv callback and argument */
-  udp_recv(dhcp->pcb, dhcp_recv, netif);
-  LWIP_DEBUGF(DHCP_DEBUG | LWIP_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;
-  }
-  /* Set the flag that says this netif is handled by DHCP. */
-  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;
-  struct udp_pcb *pcb;
-
-  LWIP_ERROR("netif != NULL", (netif != NULL), return;);
-
-  memset(&dhcp, 0, sizeof(struct dhcp));
-  dhcp_set_state(&dhcp, DHCP_INFORM);
-
-  if ((netif->dhcp != NULL) && (netif->dhcp->pcb != NULL)) {
-    /* re-use existing pcb */
-    pcb = netif->dhcp->pcb;
-  } else {
-    pcb = udp_new();
-    if (pcb == NULL) {
-      LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_inform(): could not obtain pcb"));
-      return;
-    }
-    dhcp.pcb = pcb;
-    ip_set_option(dhcp.pcb, SOF_BROADCAST);
-    udp_bind(dhcp.pcb, IP_ADDR_ANY, DHCP_CLIENT_PORT);
-    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_inform(): created new udp pcb\n"));
-  }
-  /* create and initialize the DHCP message header */
-  result = dhcp_create_msg(netif, &dhcp, DHCP_INFORM);
-  if (result == ERR_OK) {
-    dhcp_option(&dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
-    dhcp_option_short(&dhcp, DHCP_MAX_MSG_LEN(netif));
-
-    dhcp_option_trailer(&dhcp);
-
-    pbuf_realloc(dhcp.p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp.options_out_len);
-
-    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_inform: INFORMING\n"));
-    udp_sendto_if(pcb, dhcp.p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif);
-    dhcp_delete_msg(&dhcp);
-  } else {
-    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_inform: could not allocate DHCP request\n"));
-  }
-
-  if (dhcp.pcb != NULL) {
-    /* otherwise, the existing pcb was used */
-    udp_remove(dhcp.pcb);
-  }
-}
-
-/** Handle a possible change in the network configuration.
- *
- * This enters the REBOOTING state to verify that the currently bound
- * address is still valid.
- */
-void
-dhcp_network_changed(struct netif *netif)
-{
-  struct dhcp *dhcp = netif->dhcp;
-  if (!dhcp)
-    return;
-  switch (dhcp->state) {
-  case DHCP_REBINDING:
-  case DHCP_RENEWING:
-  case DHCP_BOUND:
-  case DHCP_REBOOTING:
-    netif_set_down(netif);
-    dhcp->tries = 0;
-    dhcp_reboot(netif);
-    break;
-  case DHCP_OFF:
-    /* stay off */
-    break;
-  default:
-    dhcp->tries = 0;
-#if LWIP_DHCP_AUTOIP_COOP
-    if(dhcp->autoip_coop_state == DHCP_AUTOIP_COOP_STATE_ON) {
-      autoip_stop(netif);
-      dhcp->autoip_coop_state = DHCP_AUTOIP_COOP_STATE_OFF;
-    }
-#endif /* LWIP_DHCP_AUTOIP_COOP */
-    dhcp_discover(netif);
-    break;
-  }
-}
-
-#if DHCP_DOES_ARP_CHECK
-/**
- * Match an ARP reply with the offered IP address.
- *
- * @param netif the network interface on which the reply was received
- * @param addr The IP address we received a reply from
- */
-void dhcp_arp_reply(struct netif *netif, ip_addr_t *addr)
-{
-  LWIP_ERROR("netif != NULL", (netif != NULL), return;);
-  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("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 | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_arp_reply(): CHECKING, arp reply for 0x%08"X32_F"\n",
-      ip4_addr_get_u32(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 | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING,
-        ("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).
- *
- * @param netif the netif under DHCP control
- */
-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 | LWIP_DBG_TRACE, ("dhcp_decline()\n"));
-  dhcp_set_state(dhcp, DHCP_BACKING_OFF);
-  /* create and initialize the DHCP message header */
-  result = dhcp_create_msg(netif, dhcp, DHCP_DECLINE);
-  if (result == ERR_OK) {
-    dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4);
-    dhcp_option_long(dhcp, ntohl(ip4_addr_get_u32(&dhcp->offered_ip_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);
-
-    /* per section 4.4.4, broadcast DECLINE messages */
-    udp_sendto_if(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif);
-    dhcp_delete_msg(dhcp);
-    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_decline: BACKING OFF\n"));
-  } else {
-    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
-      ("dhcp_decline: could not allocate DHCP request\n"));
-  }
-  dhcp->tries++;
-  msecs = 10*1000;
-  dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
-  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_decline(): set request timeout %"U16_F" msecs\n", msecs));
-  return result;
-}
-#endif /* DHCP_DOES_ARP_CHECK */
-
-
-/**
- * Start the DHCP process, discover a DHCP server.
- *
- * @param netif the netif under DHCP control
- */
-static err_t
-dhcp_discover(struct netif *netif)
-{
-  struct dhcp *dhcp = netif->dhcp;
-  err_t result = ERR_OK;
-  u16_t msecs;
-  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover()\n"));
-  ip_addr_set_any(&dhcp->offered_ip_addr);
-  dhcp_set_state(dhcp, DHCP_SELECTING);
-  /* create and initialize the DHCP message header */
-  result = dhcp_create_msg(netif, dhcp, DHCP_DISCOVER);
-  if (result == ERR_OK) {
-    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: making request\n"));
-
-    dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
-    dhcp_option_short(dhcp, DHCP_MAX_MSG_LEN(netif));
-
-    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 | LWIP_DBG_TRACE, ("dhcp_discover: realloc()ing\n"));
-    pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len);
-
-    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: sendto(DISCOVER, IP_ADDR_BROADCAST, DHCP_SERVER_PORT)\n"));
-    udp_sendto_if(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif);
-    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: deleting()ing\n"));
-    dhcp_delete_msg(dhcp);
-    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_discover: SELECTING\n"));
-  } else {
-    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_discover: could not allocate DHCP request\n"));
-  }
-  dhcp->tries++;
-#if LWIP_DHCP_AUTOIP_COOP
-  if(dhcp->tries >= LWIP_DHCP_AUTOIP_COOP_TRIES && dhcp->autoip_coop_state == DHCP_AUTOIP_COOP_STATE_OFF) {
-    dhcp->autoip_coop_state = DHCP_AUTOIP_COOP_STATE_ON;
-    autoip_start(netif);
-  }
-#endif /* LWIP_DHCP_AUTOIP_COOP */
-  msecs = (dhcp->tries < 6 ? 1 << dhcp->tries : 60) * 1000;
-  dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
-  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_discover(): set request timeout %"U16_F" msecs\n", msecs));
-  return result;
-}
-
-
-/**
- * Bind the interface to the offered IP address.
- *
- * @param netif network interface to bind to the offered address
- */
-static void
-dhcp_bind(struct netif *netif)
-{
-  u32_t timeout;
-  struct dhcp *dhcp;
-  ip_addr_t sn_mask, gw_addr;
-  LWIP_ERROR("dhcp_bind: netif != NULL", (netif != NULL), return;);
-  dhcp = netif->dhcp;
-  LWIP_ERROR("dhcp_bind: dhcp != NULL", (dhcp != NULL), return;);
-  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_bind(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num));
-
-  /* temporary DHCP lease? */
-  if (dhcp->offered_t1_renew != 0xffffffffUL) {
-    /* set renewal period timer */
-    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_bind(): t1 renewal timer %"U32_F" secs\n", dhcp->offered_t1_renew));
-    timeout = (dhcp->offered_t1_renew + DHCP_COARSE_TIMER_SECS / 2) / DHCP_COARSE_TIMER_SECS;
-    if(timeout > 0xffff) {
-      timeout = 0xffff;
-    }
-    dhcp->t1_timeout = (u16_t)timeout;
-    if (dhcp->t1_timeout == 0) {
-      dhcp->t1_timeout = 1;
-    }
-    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_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 | LWIP_DBG_TRACE, ("dhcp_bind(): t2 rebind timer %"U32_F" secs\n", dhcp->offered_t2_rebind));
-    timeout = (dhcp->offered_t2_rebind + DHCP_COARSE_TIMER_SECS / 2) / DHCP_COARSE_TIMER_SECS;
-    if(timeout > 0xffff) {
-      timeout = 0xffff;
-    }
-    dhcp->t2_timeout = (u16_t)timeout;
-    if (dhcp->t2_timeout == 0) {
-      dhcp->t2_timeout = 1;
-    }
-    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_bind(): set request timeout %"U32_F" msecs\n", dhcp->offered_t2_rebind*1000));
-  }
-
-  /* If we have sub 1 minute lease, t2 and t1 will kick in at the same time. */
-  if ((dhcp->t1_timeout >= dhcp->t2_timeout) && (dhcp->t2_timeout > 0)) {
-    dhcp->t1_timeout = 0;
-  }
-
-  if (dhcp->subnet_mask_given) {
-    /* copy offered network mask */
-    ip_addr_copy(sn_mask, dhcp->offered_sn_mask);
-  } else {
-    /* subnet mask not given, choose a safe subnet mask given the network class */
-    u8_t first_octet = ip4_addr1(&dhcp->offered_ip_addr);
-    if (first_octet <= 127) {
-      ip4_addr_set_u32(&sn_mask, PP_HTONL(0xff000000UL));
-    } else if (first_octet >= 192) {
-      ip4_addr_set_u32(&sn_mask, PP_HTONL(0xffffff00UL));
-    } else {
-      ip4_addr_set_u32(&sn_mask, PP_HTONL(0xffff0000UL));
-    }
-  }
-
-  ip_addr_copy(gw_addr, dhcp->offered_gw_addr);
-  /* gateway address not given? */
-  if (ip_addr_isany(&gw_addr)) {
-    /* copy network address */
-    ip_addr_get_network(&gw_addr, &dhcp->offered_ip_addr, &sn_mask);
-    /* use first host address on network as gateway */
-    ip4_addr_set_u32(&gw_addr, ip4_addr_get_u32(&gw_addr) | PP_HTONL(0x00000001UL));
-  }
-
-#if LWIP_DHCP_AUTOIP_COOP
-  if(dhcp->autoip_coop_state == DHCP_AUTOIP_COOP_STATE_ON) {
-    autoip_stop(netif);
-    dhcp->autoip_coop_state = DHCP_AUTOIP_COOP_STATE_OFF;
-  }
-#endif /* LWIP_DHCP_AUTOIP_COOP */
-
-  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_bind(): IP: 0x%08"X32_F"\n",
-    ip4_addr_get_u32(&dhcp->offered_ip_addr)));
-  netif_set_ipaddr(netif, &dhcp->offered_ip_addr);
-  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_bind(): SN: 0x%08"X32_F"\n",
-    ip4_addr_get_u32(&sn_mask)));
-  netif_set_netmask(netif, &sn_mask);
-  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_bind(): GW: 0x%08"X32_F"\n",
-    ip4_addr_get_u32(&gw_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 | LWIP_DBG_TRACE, ("dhcp_renew()\n"));
-  dhcp_set_state(dhcp, DHCP_RENEWING);
-
-  /* create and initialize the DHCP message header */
-  result = dhcp_create_msg(netif, dhcp, DHCP_REQUEST);
-  if (result == ERR_OK) {
-    dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
-    dhcp_option_short(dhcp, DHCP_MAX_MSG_LEN(netif));
-
-#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
-
-#if LWIP_NETIF_HOSTNAME
-    dhcp_option_hostname(dhcp, netif);
-#endif /* LWIP_NETIF_HOSTNAME */
-
-    /* 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_sendto_if(dhcp->pcb, dhcp->p_out, &dhcp->server_ip_addr, DHCP_SERVER_PORT, netif);
-    dhcp_delete_msg(dhcp);
-
-    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_renew: RENEWING\n"));
-  } else {
-    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_renew: could not allocate DHCP request\n"));
-  }
-  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 | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_renew(): set request timeout %"U16_F" msecs\n", msecs));
-  return result;
-}
-
-/**
- * Rebind with a DHCP server for an existing DHCP lease.
- *
- * @param netif network interface which must rebind with a DHCP server
- */
-static err_t
-dhcp_rebind(struct netif *netif)
-{
-  struct dhcp *dhcp = netif->dhcp;
-  err_t result;
-  u16_t msecs;
-  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_rebind()\n"));
-  dhcp_set_state(dhcp, DHCP_REBINDING);
-
-  /* create and initialize the DHCP message header */
-  result = dhcp_create_msg(netif, dhcp, DHCP_REQUEST);
-  if (result == ERR_OK) {
-    dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
-    dhcp_option_short(dhcp, DHCP_MAX_MSG_LEN(netif));
-
-#if LWIP_NETIF_HOSTNAME
-    dhcp_option_hostname(dhcp, netif);
-#endif /* LWIP_NETIF_HOSTNAME */
-
-#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);
-
-    /* broadcast to server */
-    udp_sendto_if(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif);
-    dhcp_delete_msg(dhcp);
-    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_rebind: REBINDING\n"));
-  } else {
-    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_rebind: could not allocate DHCP request\n"));
-  }
-  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 | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_rebind(): set request timeout %"U16_F" msecs\n", msecs));
-  return result;
-}
-
-/**
- * Enter REBOOTING state to verify an existing lease
- *
- * @param netif network interface which must reboot
- */
-static err_t
-dhcp_reboot(struct netif *netif)
-{
-  struct dhcp *dhcp = netif->dhcp;
-  err_t result;
-  u16_t msecs;
-  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_reboot()\n"));
-  dhcp_set_state(dhcp, DHCP_REBOOTING);
-
-  /* create and initialize the DHCP message header */
-  result = dhcp_create_msg(netif, dhcp, DHCP_REQUEST);
-  if (result == ERR_OK) {
-    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(ip4_addr_get_u32(&dhcp->offered_ip_addr)));
-
-    dhcp_option_trailer(dhcp);
-
-    pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len);
-
-    /* broadcast to server */
-    udp_sendto_if(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif);
-    dhcp_delete_msg(dhcp);
-    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_reboot: REBOOTING\n"));
-  } else {
-    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_reboot: could not allocate DHCP request\n"));
-  }
-  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 | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_reboot(): 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 | LWIP_DBG_TRACE, ("dhcp_release()\n"));
-
-  /* idle DHCP client */
-  dhcp_set_state(dhcp, DHCP_OFF);
-  /* clean old DHCP offer */
-  ip_addr_set_zero(&dhcp->server_ip_addr);
-  ip_addr_set_zero(&dhcp->offered_ip_addr);
-  ip_addr_set_zero(&dhcp->offered_sn_mask);
-  ip_addr_set_zero(&dhcp->offered_gw_addr);
-#if LWIP_DHCP_BOOTP_FILE
-  ip_addr_set_zero(&dhcp->offered_si_addr);
-#endif /* LWIP_DHCP_BOOTP_FILE */
-  dhcp->offered_t0_lease = dhcp->offered_t1_renew = dhcp->offered_t2_rebind = 0;
-  
-  /* create and initialize the DHCP message header */
-  result = dhcp_create_msg(netif, dhcp, DHCP_RELEASE);
-  if (result == ERR_OK) {
-    dhcp_option_trailer(dhcp);
-
-    pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len);
-
-    udp_sendto_if(dhcp->pcb, dhcp->p_out, &dhcp->server_ip_addr, DHCP_SERVER_PORT, netif);
-    dhcp_delete_msg(dhcp);
-    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_release: RELEASED, DHCP_OFF\n"));
-  } else {
-    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("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 | LWIP_DBG_TRACE | LWIP_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);
-  
-  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;
-  LWIP_ERROR("dhcp_stop: netif != NULL", (netif != NULL), return;);
-  dhcp = netif->dhcp;
-  /* Remove the flag that says this netif is handled by DHCP. */
-  netif->flags &= ~NETIF_FLAG_DHCP;
-
-  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_stop()\n"));
-  /* netif is DHCP configured? */
-  if (dhcp != NULL) {
-#if LWIP_DHCP_AUTOIP_COOP
-    if(dhcp->autoip_coop_state == DHCP_AUTOIP_COOP_STATE_ON) {
-      autoip_stop(netif);
-      dhcp->autoip_coop_state = DHCP_AUTOIP_COOP_STATE_OFF;
-    }
-#endif /* LWIP_DHCP_AUTOIP_COOP */
-
-    if (dhcp->pcb != NULL) {
-      udp_remove(dhcp->pcb);
-      dhcp->pcb = NULL;
-    }
-    LWIP_ASSERT("reply wasn't freed", dhcp->msg_in == NULL);
-    dhcp_set_state(dhcp, DHCP_OFF);
-  }
-}
-
-/*
- * Set the DHCP state of a DHCP client.
- *
- * If the state changed, reset the number of tries.
- */
-static void
-dhcp_set_state(struct dhcp *dhcp, u8_t new_state)
-{
-  if (new_state != dhcp->state) {
-    dhcp->state = new_state;
-    dhcp->tries = 0;
-    dhcp->request_timeout = 0;
-  }
-}
-
-/*
- * Concatenate an option type and length field to the outgoing
- * DHCP message.
- *
- */
-static void
-dhcp_option(struct dhcp *dhcp, u8_t option_type, u8_t option_len)
-{
-  LWIP_ASSERT("dhcp_option: dhcp->options_out_len + 2 + option_len <= DHCP_OPTIONS_LEN", dhcp->options_out_len + 2U + 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_byte: 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 + 2U <= DHCP_OPTIONS_LEN);
-  dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0xff00U) >> 8);
-  dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t) (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 + 4U <= DHCP_OPTIONS_LEN);
-  dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0xff000000UL) >> 24);
-  dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0x00ff0000UL) >> 16);
-  dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0x0000ff00UL) >> 8);
-  dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0x000000ffUL));
-}
-
-#if LWIP_NETIF_HOSTNAME
-static void
-dhcp_option_hostname(struct dhcp *dhcp, struct netif *netif)
-{
-  if (netif->hostname != NULL) {
-    size_t namelen = strlen(netif->hostname);
-    if (namelen > 0) {
-      u8_t len;
-      const char *p = netif->hostname;
-      /* Shrink len to available bytes (need 2 bytes for OPTION_HOSTNAME
-         and 1 byte for trailer) */
-      size_t available = DHCP_OPTIONS_LEN - dhcp->options_out_len - 3;
-      LWIP_ASSERT("DHCP: hostname is too long!", namelen <= available);
-      len = LWIP_MIN(namelen, available);
-      dhcp_option(dhcp, DHCP_OPTION_HOSTNAME, len);
-      while (len--) {
-        dhcp_option_byte(dhcp, *p++);
-      }
-    }
-  }
-}
-#endif /* LWIP_NETIF_HOSTNAME */
-
-/**
- * Extract the DHCP message and the DHCP options.
- *
- * Extract the DHCP message and the DHCP options, each into a contiguous
- * piece of memory. As a DHCP message is variable sized by its options,
- * and also allows overriding some fields for options, the easy approach
- * is to first unfold the options into a conitguous piece of memory, and
- * use that further on.
- *
- */
-static err_t
-dhcp_parse_reply(struct dhcp *dhcp, struct pbuf *p)
-{
-  u8_t *options;
-  u16_t offset;
-  u16_t offset_max;
-  u16_t options_idx;
-  u16_t options_idx_max;
-  struct pbuf *q;
-  int parse_file_as_options = 0;
-  int parse_sname_as_options = 0;
-
-  /* clear received options */
-  dhcp_clear_all_options(dhcp);
-  /* check that beginning of dhcp_msg (up to and including chaddr) is in first pbuf */
-  if (p->len < DHCP_SNAME_OFS) {
-    return ERR_BUF;
-  }
-  dhcp->msg_in = (struct dhcp_msg *)p->payload;
-#if LWIP_DHCP_BOOTP_FILE
-  /* clear boot file name */
-  dhcp->boot_file_name[0] = 0;
-#endif /* LWIP_DHCP_BOOTP_FILE */
-
-  /* parse options */
-
-  /* start with options field */
-  options_idx = DHCP_OPTIONS_OFS;
-  /* parse options to the end of the received packet */
-  options_idx_max = p->tot_len;
-again:
-  q = p;
-  while((q != NULL) && (options_idx >= q->len)) {
-    options_idx -= q->len;
-    options_idx_max -= q->len;
-    q = q->next;
-  }
-  if (q == NULL) {
-    return ERR_BUF;
-  }
-  offset = options_idx;
-  offset_max = options_idx_max;
-  options = (u8_t*)q->payload;
-  /* at least 1 byte to read and no end marker, then at least 3 bytes to read? */
-  while((q != NULL) && (options[offset] != DHCP_OPTION_END) && (offset < offset_max)) {
-    u8_t op = options[offset];
-    u8_t len;
-    u8_t decode_len = 0;
-    int decode_idx = -1;
-    u16_t val_offset = offset + 2;
-    /* len byte might be in the next pbuf */
-    if (offset + 1 < q->len) {
-      len = options[offset + 1];
-    } else {
-      len = (q->next != NULL ? ((u8_t*)q->next->payload)[0] : 0);
-    }
-    /* LWIP_DEBUGF(DHCP_DEBUG, ("msg_offset=%"U16_F", q->len=%"U16_F, msg_offset, q->len)); */
-    decode_len = len;
-    switch(op) {
-      /* case(DHCP_OPTION_END): handled above */
-      case(DHCP_OPTION_PAD):
-        /* special option: no len encoded */
-        decode_len = len = 0;
-        /* will be increased below */
-        offset--;
-        break;
-      case(DHCP_OPTION_SUBNET_MASK):
-        LWIP_ERROR("len == 4", len == 4, return ERR_VAL;);
-        decode_idx = DHCP_OPTION_IDX_SUBNET_MASK;
-        break;
-      case(DHCP_OPTION_ROUTER):
-        decode_len = 4; /* only copy the first given router */
-        LWIP_ERROR("len >= decode_len", len >= decode_len, return ERR_VAL;);
-        decode_idx = DHCP_OPTION_IDX_ROUTER;
-        break;
-      case(DHCP_OPTION_DNS_SERVER):
-        /* special case: there might be more than one server */
-        LWIP_ERROR("len % 4 == 0", len % 4 == 0, return ERR_VAL;);
-        /* limit number of DNS servers */
-        decode_len = LWIP_MIN(len, 4 * DNS_MAX_SERVERS);
-        LWIP_ERROR("len >= decode_len", len >= decode_len, return ERR_VAL;);
-        decode_idx = DHCP_OPTION_IDX_DNS_SERVER;
-        break;
-      case(DHCP_OPTION_LEASE_TIME):
-        LWIP_ERROR("len == 4", len == 4, return ERR_VAL;);
-        decode_idx = DHCP_OPTION_IDX_LEASE_TIME;
-        break;
-      case(DHCP_OPTION_OVERLOAD):
-        LWIP_ERROR("len == 1", len == 1, return ERR_VAL;);
-        decode_idx = DHCP_OPTION_IDX_OVERLOAD;
-        break;
-      case(DHCP_OPTION_MESSAGE_TYPE):
-        LWIP_ERROR("len == 1", len == 1, return ERR_VAL;);
-        decode_idx = DHCP_OPTION_IDX_MSG_TYPE;
-        break;
-      case(DHCP_OPTION_SERVER_ID):
-        LWIP_ERROR("len == 4", len == 4, return ERR_VAL;);
-        decode_idx = DHCP_OPTION_IDX_SERVER_ID;
-        break;
-      case(DHCP_OPTION_T1):
-        LWIP_ERROR("len == 4", len == 4, return ERR_VAL;);
-        decode_idx = DHCP_OPTION_IDX_T1;
-        break;
-      case(DHCP_OPTION_T2):
-        LWIP_ERROR("len == 4", len == 4, return ERR_VAL;);
-        decode_idx = DHCP_OPTION_IDX_T2;
-        break;
-      default:
-        decode_len = 0;
-        LWIP_DEBUGF(DHCP_DEBUG, ("skipping option %"U16_F" in options\n", op));
-        break;
-    }
-    offset += len + 2;
-    if (decode_len > 0) {
-      u32_t value = 0;
-      u16_t copy_len;
-decode_next:
-      LWIP_ASSERT("check decode_idx", decode_idx >= 0 && decode_idx < DHCP_OPTION_IDX_MAX);
-      if (!dhcp_option_given(dhcp, decode_idx)) {
-        copy_len = LWIP_MIN(decode_len, 4);
-        pbuf_copy_partial(q, &value, copy_len, val_offset);
-        if (decode_len > 4) {
-          /* decode more than one u32_t */
-          LWIP_ERROR("decode_len % 4 == 0", decode_len % 4 == 0, return ERR_VAL;);
-          dhcp_got_option(dhcp, decode_idx);
-          dhcp_set_option_value(dhcp, decode_idx, htonl(value));
-          decode_len -= 4;
-          val_offset += 4;
-          decode_idx++;
-          goto decode_next;
-        } else if (decode_len == 4) {
-          value = ntohl(value);
-        } else {
-          LWIP_ERROR("invalid decode_len", decode_len == 1, return ERR_VAL;);
-          value = ((u8_t*)&value)[0];
-        }
-        dhcp_got_option(dhcp, decode_idx);
-        dhcp_set_option_value(dhcp, decode_idx, value);
-      }
-    }
-    if (offset >= q->len) {
-      offset -= q->len;
-      offset_max -= q->len;
-      if ((offset < offset_max) && offset_max) {
-        q = q->next;
-        LWIP_ASSERT("next pbuf was null", q);
-        options = (u8_t*)q->payload;
-      } else {
-        // We've run out of bytes, probably no end marker. Don't proceed.
-        break;
-      }
-    }
-  }
-  /* is this an overloaded message? */
-  if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_OVERLOAD)) {
-    u32_t overload = dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_OVERLOAD);
-    dhcp_clear_option(dhcp, DHCP_OPTION_IDX_OVERLOAD);
-    if (overload == DHCP_OVERLOAD_FILE) {
-      parse_file_as_options = 1;
-      LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("overloaded file field\n"));
-    } else if (overload == DHCP_OVERLOAD_SNAME) {
-      parse_sname_as_options = 1;
-      LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("overloaded sname field\n"));
-    } else if (overload == DHCP_OVERLOAD_SNAME_FILE) {
-      parse_sname_as_options = 1;
-      parse_file_as_options = 1;
-      LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("overloaded sname and file field\n"));
-    } else {
-      LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("invalid overload option: %d\n", (int)overload));
-    }
-#if LWIP_DHCP_BOOTP_FILE
-    if (!parse_file_as_options) {
-      /* only do this for ACK messages */
-      if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_MSG_TYPE) &&
-        (dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_MSG_TYPE) == DHCP_ACK))
-      /* copy bootp file name, don't care for sname (server hostname) */
-      pbuf_copy_partial(p, dhcp->boot_file_name, DHCP_FILE_LEN-1, DHCP_FILE_OFS);
-      /* make sure the string is really NULL-terminated */
-      dhcp->boot_file_name[DHCP_FILE_LEN-1] = 0;
-    }
-#endif /* LWIP_DHCP_BOOTP_FILE */
-  }
-  if (parse_file_as_options) {
-    /* if both are overloaded, parse file first and then sname (RFC 2131 ch. 4.1) */
-    parse_file_as_options = 0;
-    options_idx = DHCP_FILE_OFS;
-    options_idx_max = DHCP_FILE_OFS + DHCP_FILE_LEN;
-    goto again;
-  } else if (parse_sname_as_options) {
-    parse_sname_as_options = 0;
-    options_idx = DHCP_SNAME_OFS;
-    options_idx_max = DHCP_SNAME_OFS + DHCP_SNAME_LEN;
-    goto again;
-  }
-  return ERR_OK;
-}
-
-/**
- * If an incoming DHCP message is in response to us, then trigger the state machine
- */
-static void
-dhcp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *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 msg_type;
-  u8_t i;
-  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_recv(pbuf = %p) from DHCP server %"U16_F".%"U16_F".%"U16_F".%"U16_F" port %"U16_F"\n", (void*)p,
-    ip4_addr1_16(addr), ip4_addr2_16(addr), ip4_addr3_16(addr), ip4_addr4_16(addr), port));
-  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("pbuf->len = %"U16_F"\n", p->len));
-  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("pbuf->tot_len = %"U16_F"\n", p->tot_len));
-  /* prevent warnings about unused arguments */
-  LWIP_UNUSED_ARG(pcb);
-  LWIP_UNUSED_ARG(addr);
-  LWIP_UNUSED_ARG(port);
-
-  LWIP_ASSERT("reply wasn't freed", dhcp->msg_in == NULL);
-
-  if (p->len < DHCP_MIN_REPLY_LEN) {
-    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("DHCP reply message or pbuf too short\n"));
-    goto free_pbuf_and_return;
-  }
-
-  if (reply_msg->op != DHCP_BOOTREPLY) {
-    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("not a DHCP reply message, but type %"U16_F"\n", (u16_t)reply_msg->op));
-    goto free_pbuf_and_return;
-  }
-  /* iterate through hardware address and match against DHCP message */
-  for (i = 0; i < netif->hwaddr_len; i++) {
-    if (netif->hwaddr[i] != reply_msg->chaddr[i]) {
-      LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
-        ("netif->hwaddr[%"U16_F"]==%02"X16_F" != reply_msg->chaddr[%"U16_F"]==%02"X16_F"\n",
-        (u16_t)i, (u16_t)netif->hwaddr[i], (u16_t)i, (u16_t)reply_msg->chaddr[i]));
-      goto free_pbuf_and_return;
-    }
-  }
-  /* match transaction ID against what we expected */
-  if (ntohl(reply_msg->xid) != dhcp->xid) {
-    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
-      ("transaction id mismatch reply_msg->xid(%"X32_F")!=dhcp->xid(%"X32_F")\n",ntohl(reply_msg->xid),dhcp->xid));
-    goto free_pbuf_and_return;
-  }
-  /* option fields could be unfold? */
-  if (dhcp_parse_reply(dhcp, p) != ERR_OK) {
-    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
-      ("problem unfolding DHCP message - too short on memory?\n"));
-    goto free_pbuf_and_return;
-  }
-
-  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("searching DHCP_OPTION_MESSAGE_TYPE\n"));
-  /* obtain pointer to DHCP message type */
-  if (!dhcp_option_given(dhcp, DHCP_OPTION_IDX_MSG_TYPE)) {
-    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("DHCP_OPTION_MESSAGE_TYPE option not found\n"));
-    goto free_pbuf_and_return;
-  }
-
-  /* read DHCP message type */
-  msg_type = (u8_t)dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_MSG_TYPE);
-  /* message type is DHCP ACK? */
-  if (msg_type == DHCP_ACK) {
-    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("DHCP_ACK received\n"));
-    /* in requesting state? */
-    if (dhcp->state == DHCP_REQUESTING) {
-      dhcp_handle_ack(netif);
-#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_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 | LWIP_DBG_TRACE, ("DHCP_NAK received\n"));
-    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 | LWIP_DBG_TRACE, ("DHCP_OFFER received in DHCP_SELECTING state\n"));
-    dhcp->request_timeout = 0;
-    /* remember offered lease */
-    dhcp_handle_offer(netif);
-  }
-free_pbuf_and_return:
-  dhcp->msg_in = NULL;
-  pbuf_free(p);
-}
-
-/**
- * Create a DHCP request, fill in common headers
- *
- * @param netif the netif under DHCP control
- * @param dhcp dhcp control struct
- * @param message_type message type of the request
- */
-static err_t
-dhcp_create_msg(struct netif *netif, struct dhcp *dhcp, u8_t message_type)
-{
-  u16_t i;
-#ifndef DHCP_GLOBAL_XID
-  /** default global transaction identifier starting value (easy to match
-   *  with a packet analyser). We simply increment for each new request.
-   *  Predefine DHCP_GLOBAL_XID to a better value or a function call to generate one
-   *  at runtime, any supporting function prototypes can be defined in DHCP_GLOBAL_XID_HEADER */
-#if DHCP_CREATE_RAND_XID && defined(LWIP_RAND)
-  static u32_t xid;
-#else /* DHCP_CREATE_RAND_XID && defined(LWIP_RAND) */
-  static u32_t xid = 0xABCD0000;
-#endif /* DHCP_CREATE_RAND_XID && defined(LWIP_RAND) */
-#else
-  if (!xid_initialised) {
-    xid = DHCP_GLOBAL_XID;
-    xid_initialised = !xid_initialised;
-  }
-#endif
-  LWIP_ERROR("dhcp_create_msg: netif != NULL", (netif != NULL), return ERR_ARG;);
-  LWIP_ERROR("dhcp_create_msg: dhcp != NULL", (dhcp != NULL), return ERR_VAL;);
-  LWIP_ASSERT("dhcp_create_msg: dhcp->p_out == NULL", dhcp->p_out == NULL);
-  LWIP_ASSERT("dhcp_create_msg: 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 | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
-      ("dhcp_create_msg(): could not allocate pbuf\n"));
-    return ERR_MEM;
-  }
-  LWIP_ASSERT("dhcp_create_msg: check that first pbuf can hold struct dhcp_msg",
-           (dhcp->p_out->len >= sizeof(struct dhcp_msg)));
-
-  /* reuse transaction identifier in retransmissions */
-  if (dhcp->tries == 0) {
-#if DHCP_CREATE_RAND_XID && defined(LWIP_RAND)
-    xid = LWIP_RAND();
-#else /* DHCP_CREATE_RAND_XID && defined(LWIP_RAND) */
-    xid++;
-#endif /* DHCP_CREATE_RAND_XID && defined(LWIP_RAND) */
-  }
-  dhcp->xid = xid;
-  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE,
-              ("transaction id xid(%"X32_F")\n", xid));
-
-  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;
-  dhcp->msg_out->hlen = netif->hwaddr_len;
-  dhcp->msg_out->hops = 0;
-  dhcp->msg_out->xid = htonl(dhcp->xid);
-  dhcp->msg_out->secs = 0;
-  /* we don't need the broadcast flag since we can receive unicast traffic
-     before being fully configured! */
-  dhcp->msg_out->flags = 0;
-  ip_addr_set_zero(&dhcp->msg_out->ciaddr);
-  /* set ciaddr to netif->ip_addr based on message_type and state */
-  if ((message_type == DHCP_INFORM) || (message_type == DHCP_DECLINE) ||
-      ((message_type == DHCP_REQUEST) && /* DHCP_BOUND not used for sending! */
-       ((dhcp->state==DHCP_RENEWING) || dhcp->state==DHCP_REBINDING))) {
-    ip_addr_copy(dhcp->msg_out->ciaddr, netif->ip_addr);
-  }
-  ip_addr_set_zero(&dhcp->msg_out->yiaddr);
-  ip_addr_set_zero(&dhcp->msg_out->siaddr);
-  ip_addr_set_zero(&dhcp->msg_out->giaddr);
-  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 = PP_HTONL(DHCP_MAGIC_COOKIE);
-  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] = (u8_t)i; /* for debugging only, no matter if truncated */
-  }
-  /* Add option MESSAGE_TYPE */
-  dhcp_option(dhcp, DHCP_OPTION_MESSAGE_TYPE, DHCP_OPTION_MESSAGE_TYPE_LEN);
-  dhcp_option_byte(dhcp, message_type);
-  return ERR_OK;
-}
-
-/**
- * Free previously allocated memory used to send a DHCP request.
- *
- * @param dhcp the dhcp struct to free the request from
- */
-static void
-dhcp_delete_msg(struct dhcp *dhcp)
-{
-  LWIP_ERROR("dhcp_delete_msg: dhcp != NULL", (dhcp != NULL), return;);
-  LWIP_ASSERT("dhcp_delete_msg: dhcp->p_out != NULL", dhcp->p_out != NULL);
-  LWIP_ASSERT("dhcp_delete_msg: dhcp->msg_out != NULL", dhcp->msg_out != NULL);
-  if (dhcp->p_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.
- *
- * @param dhcp DHCP state structure
- */
-static void
-dhcp_option_trailer(struct dhcp *dhcp)
-{
-  LWIP_ERROR("dhcp_option_trailer: dhcp != NULL", (dhcp != NULL), return;);
-  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)) &&
-         (dhcp->options_out_len < DHCP_OPTIONS_LEN)) {
-    /* add a fill/padding byte */
-    dhcp->msg_out->options[dhcp->options_out_len++] = 0;
-  }
-}
-
-#endif /* LWIP_DHCP */
+/**
+ * @file
+ * Dynamic Host Configuration Protocol client
+ *
+ * @defgroup dhcp4 DHCPv4
+ * @ingroup ip4
+ * DHCP (IPv4) related functions
+ * This is a DHCP client for the lwIP TCP/IP stack. It aims to conform
+ * with RFC 2131 and RFC 2132.
+ *
+ * @todo:
+ * - Support for interfaces other than Ethernet (SLIP, PPP, ...)
+ *
+ * Options:
+ * @ref DHCP_COARSE_TIMER_SECS (recommended 60 which is a minute)
+ * @ref DHCP_FINE_TIMER_MSECS (recommended 500 which equals TCP coarse timer)
+ *
+ * dhcp_start() starts a DHCP client instance which
+ * configures the interface by obtaining an IP address lease and maintaining it.
+ *
+ * Use dhcp_release() to end the lease and use dhcp_stop()
+ * to remove the DHCP client.
+ *
+ * @see netifapi_dhcp4
+ */
+
+/*
+ * Copyright (c) 2001-2004 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * Copyright (c) 2001-2004 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ * The Swedish Institute of Computer Science and Adam Dunkels
+ * are specifically granted permission to redistribute this
+ * source code.
+ *
+ * Author: Leon Woestenberg <leon.woestenberg@gmx.net>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV4 && LWIP_DHCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/stats.h"
+#include "lwip/mem.h"
+#include "lwip/udp.h"
+#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+#include "lwip/def.h"
+#include "lwip/dhcp.h"
+#include "lwip/autoip.h"
+#include "lwip/dns.h"
+#include "lwip/etharp.h"
+#include "lwip/prot/dhcp.h"
+
+#include <string.h>
+
+/** DHCP_CREATE_RAND_XID: if this is set to 1, the xid is created using
+ * LWIP_RAND() (this overrides DHCP_GLOBAL_XID)
+ */
+#ifndef DHCP_CREATE_RAND_XID
+#define DHCP_CREATE_RAND_XID        1
+#endif
+
+/** Default for DHCP_GLOBAL_XID is 0xABCD0000
+ * This can be changed by defining DHCP_GLOBAL_XID and DHCP_GLOBAL_XID_HEADER, e.g.
+ *  \#define DHCP_GLOBAL_XID_HEADER "stdlib.h"
+ *  \#define DHCP_GLOBAL_XID rand()
+ */
+#ifdef DHCP_GLOBAL_XID_HEADER
+#include DHCP_GLOBAL_XID_HEADER /* include optional starting XID generation prototypes */
+#endif
+
+/** DHCP_OPTION_MAX_MSG_SIZE is set to the MTU
+ * MTU is checked to be big enough in dhcp_start */
+#define DHCP_MAX_MSG_LEN(netif)        (netif->mtu)
+#define DHCP_MAX_MSG_LEN_MIN_REQUIRED  576
+/** Minimum length for reply before packet is parsed */
+#define DHCP_MIN_REPLY_LEN             44
+
+#define REBOOT_TRIES                2
+
+#if LWIP_DNS && LWIP_DHCP_MAX_DNS_SERVERS
+#if DNS_MAX_SERVERS > LWIP_DHCP_MAX_DNS_SERVERS
+#define LWIP_DHCP_PROVIDE_DNS_SERVERS LWIP_DHCP_MAX_DNS_SERVERS
+#else
+#define LWIP_DHCP_PROVIDE_DNS_SERVERS DNS_MAX_SERVERS
+#endif
+#else
+#define LWIP_DHCP_PROVIDE_DNS_SERVERS 0
+#endif
+
+/** Option handling: options are parsed in dhcp_parse_reply
+ * and saved in an array where other functions can load them from.
+ * This might be moved into the struct dhcp (not necessarily since
+ * lwIP is single-threaded and the array is only used while in recv
+ * callback). */
+enum dhcp_option_idx {
+  DHCP_OPTION_IDX_OVERLOAD = 0,
+  DHCP_OPTION_IDX_MSG_TYPE,
+  DHCP_OPTION_IDX_SERVER_ID,
+  DHCP_OPTION_IDX_LEASE_TIME,
+  DHCP_OPTION_IDX_T1,
+  DHCP_OPTION_IDX_T2,
+  DHCP_OPTION_IDX_SUBNET_MASK,
+  DHCP_OPTION_IDX_ROUTER,
+#if LWIP_DHCP_PROVIDE_DNS_SERVERS
+  DHCP_OPTION_IDX_DNS_SERVER,
+  DHCP_OPTION_IDX_DNS_SERVER_LAST = DHCP_OPTION_IDX_DNS_SERVER + LWIP_DHCP_PROVIDE_DNS_SERVERS - 1,
+#endif /* LWIP_DHCP_PROVIDE_DNS_SERVERS */
+#if LWIP_DHCP_GET_NTP_SRV
+  DHCP_OPTION_IDX_NTP_SERVER,
+  DHCP_OPTION_IDX_NTP_SERVER_LAST = DHCP_OPTION_IDX_NTP_SERVER + LWIP_DHCP_MAX_NTP_SERVERS - 1,
+#endif /* LWIP_DHCP_GET_NTP_SRV */
+  DHCP_OPTION_IDX_MAX
+};
+
+/** Holds the decoded option values, only valid while in dhcp_recv.
+    @todo: move this into struct dhcp? */
+u32_t dhcp_rx_options_val[DHCP_OPTION_IDX_MAX];
+/** Holds a flag which option was received and is contained in dhcp_rx_options_val,
+    only valid while in dhcp_recv.
+    @todo: move this into struct dhcp? */
+u8_t  dhcp_rx_options_given[DHCP_OPTION_IDX_MAX];
+
+static u8_t dhcp_discover_request_options[] = {
+  DHCP_OPTION_SUBNET_MASK,
+  DHCP_OPTION_ROUTER,
+  DHCP_OPTION_BROADCAST
+#if LWIP_DHCP_PROVIDE_DNS_SERVERS
+  , DHCP_OPTION_DNS_SERVER
+#endif /* LWIP_DHCP_PROVIDE_DNS_SERVERS */
+#if LWIP_DHCP_GET_NTP_SRV
+  , DHCP_OPTION_NTP
+#endif /* LWIP_DHCP_GET_NTP_SRV */
+  };
+
+#ifdef DHCP_GLOBAL_XID
+static u32_t xid;
+static u8_t xid_initialised;
+#endif /* DHCP_GLOBAL_XID */
+
+#define dhcp_option_given(dhcp, idx)          (dhcp_rx_options_given[idx] != 0)
+#define dhcp_got_option(dhcp, idx)            (dhcp_rx_options_given[idx] = 1)
+#define dhcp_clear_option(dhcp, idx)          (dhcp_rx_options_given[idx] = 0)
+#define dhcp_clear_all_options(dhcp)          (memset(dhcp_rx_options_given, 0, sizeof(dhcp_rx_options_given)))
+#define dhcp_get_option_value(dhcp, idx)      (dhcp_rx_options_val[idx])
+#define dhcp_set_option_value(dhcp, idx, val) (dhcp_rx_options_val[idx] = (val))
+
+static struct udp_pcb *dhcp_pcb;
+static u8_t dhcp_pcb_refcount;
+
+/* DHCP client state machine functions */
+static err_t dhcp_discover(struct netif *netif);
+static err_t dhcp_select(struct netif *netif);
+static void dhcp_bind(struct netif *netif);
+#if DHCP_DOES_ARP_CHECK
+static err_t dhcp_decline(struct netif *netif);
+#endif /* DHCP_DOES_ARP_CHECK */
+static err_t dhcp_rebind(struct netif *netif);
+static err_t dhcp_reboot(struct netif *netif);
+static void dhcp_set_state(struct dhcp *dhcp, u8_t new_state);
+
+/* receive, unfold, parse and free incoming messages */
+static void dhcp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port);
+
+/* set the DHCP timers */
+static void dhcp_timeout(struct netif *netif);
+static void dhcp_t1_timeout(struct netif *netif);
+static void dhcp_t2_timeout(struct netif *netif);
+
+/* build outgoing messages */
+/* create a DHCP message, fill in common headers */
+static err_t dhcp_create_msg(struct netif *netif, struct dhcp *dhcp, u8_t message_type);
+/* free a DHCP request */
+static void dhcp_delete_msg(struct dhcp *dhcp);
+/* 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);
+#if LWIP_NETIF_HOSTNAME
+static void dhcp_option_hostname(struct dhcp *dhcp, struct netif *netif);
+#endif /* LWIP_NETIF_HOSTNAME */
+/* always add the DHCP options trailer to end and pad */
+static void dhcp_option_trailer(struct dhcp *dhcp);
+
+/** Ensure DHCP PCB is allocated and bound */
+static err_t
+dhcp_inc_pcb_refcount(void)
+{
+  if (dhcp_pcb_refcount == 0) {
+    LWIP_ASSERT("dhcp_inc_pcb_refcount(): memory leak", dhcp_pcb == NULL);
+
+    /* allocate UDP PCB */
+    dhcp_pcb = udp_new();
+
+    if (dhcp_pcb == NULL) {
+      return ERR_MEM;
+    }
+
+    ip_set_option(dhcp_pcb, SOF_BROADCAST);
+
+    /* set up local and remote port for the pcb -> listen on all interfaces on all src/dest IPs */
+    udp_bind(dhcp_pcb, IP4_ADDR_ANY, DHCP_CLIENT_PORT);
+    udp_connect(dhcp_pcb, IP4_ADDR_ANY, DHCP_SERVER_PORT);
+    udp_recv(dhcp_pcb, dhcp_recv, NULL);
+  }
+
+  dhcp_pcb_refcount++;
+
+  return ERR_OK;
+}
+
+/** Free DHCP PCB if the last netif stops using it */
+static void
+dhcp_dec_pcb_refcount(void)
+{
+  LWIP_ASSERT("dhcp_pcb_refcount(): refcount error", (dhcp_pcb_refcount > 0));
+  dhcp_pcb_refcount--;
+
+  if (dhcp_pcb_refcount == 0) {
+    udp_remove(dhcp_pcb);
+    dhcp_pcb = NULL;
+  }
+}
+
+/**
+ * Back-off the DHCP client (because of a received NAK response).
+ *
+ * Back-off the DHCP client because of a received NAK. Receiving a
+ * NAK means the client asked for something non-sensible, for
+ * example when it tries to renew a lease obtained on another network.
+ *
+ * We clear any existing set IP address and restart DHCP negotiation
+ * afresh (as per RFC2131 3.2.3).
+ *
+ * @param netif the netif under DHCP control
+ */
+static void
+dhcp_handle_nak(struct netif *netif)
+{
+  struct dhcp *dhcp = netif_dhcp_data(netif);
+
+  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_handle_nak(netif=%p) %c%c%"U16_F"\n",
+    (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num));
+  /* Change to a defined state - set this before assigning the address
+     to ensure the callback can use dhcp_supplied_address() */
+  dhcp_set_state(dhcp, DHCP_STATE_BACKING_OFF);
+  /* remove IP address from interface (must no longer be used, as per RFC2131) */
+  netif_set_addr(netif, IP4_ADDR_ANY4, IP4_ADDR_ANY4, IP4_ADDR_ANY4);
+  /* We can immediately restart discovery */
+  dhcp_discover(netif);
+}
+
+#if DHCP_DOES_ARP_CHECK
+/**
+ * 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.
+ *
+ * @param netif the netif under DHCP control
+ */
+static void
+dhcp_check(struct netif *netif)
+{
+  struct dhcp *dhcp = netif_dhcp_data(netif);
+  err_t result;
+  u16_t msecs;
+  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_check(netif=%p) %c%c\n", (void *)netif, (s16_t)netif->name[0],
+    (s16_t)netif->name[1]));
+  dhcp_set_state(dhcp, DHCP_STATE_CHECKING);
+  /* 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 | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("dhcp_check: could not perform ARP query\n"));
+  }
+  if (dhcp->tries < 255) {
+    dhcp->tries++;
+  }
+  msecs = 500;
+  dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
+  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_check(): set request timeout %"U16_F" msecs\n", msecs));
+}
+#endif /* DHCP_DOES_ARP_CHECK */
+
+/**
+ * Remember the configuration offered by a DHCP server.
+ *
+ * @param netif the netif under DHCP control
+ */
+static void
+dhcp_handle_offer(struct netif *netif)
+{
+  struct dhcp *dhcp = netif_dhcp_data(netif);
+
+  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_handle_offer(netif=%p) %c%c%"U16_F"\n",
+    (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num));
+  /* obtain the server address */
+  if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_SERVER_ID)) {
+    ip_addr_set_ip4_u32(&dhcp->server_ip_addr, lwip_htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_SERVER_ID)));
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_handle_offer(): server 0x%08"X32_F"\n",
+      ip4_addr_get_u32(ip_2_ip4(&dhcp->server_ip_addr))));
+    /* remember offered address */
+    ip4_addr_copy(dhcp->offered_ip_addr, dhcp->msg_in->yiaddr);
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_handle_offer(): offer for 0x%08"X32_F"\n",
+      ip4_addr_get_u32(&dhcp->offered_ip_addr)));
+
+    dhcp_select(netif);
+  } else {
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
+      ("dhcp_handle_offer(netif=%p) did not get server ID!\n", (void*)netif));
+  }
+}
+
+/**
+ * Select a DHCP server offer out of all offers.
+ *
+ * Simply select the first offer received.
+ *
+ * @param netif the netif under DHCP control
+ * @return lwIP specific error (see error.h)
+ */
+static err_t
+dhcp_select(struct netif *netif)
+{
+  struct dhcp *dhcp = netif_dhcp_data(netif);
+  err_t result;
+  u16_t msecs;
+  u8_t i;
+
+  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_select(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num));
+  dhcp_set_state(dhcp, DHCP_STATE_REQUESTING);
+
+  /* create and initialize the DHCP message header */
+  result = dhcp_create_msg(netif, dhcp, DHCP_REQUEST);
+  if (result == ERR_OK) {
+    dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
+    dhcp_option_short(dhcp, DHCP_MAX_MSG_LEN(netif));
+
+    /* MUST request the offered IP address */
+    dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4);
+    dhcp_option_long(dhcp, lwip_ntohl(ip4_addr_get_u32(&dhcp->offered_ip_addr)));
+
+    dhcp_option(dhcp, DHCP_OPTION_SERVER_ID, 4);
+    dhcp_option_long(dhcp, lwip_ntohl(ip4_addr_get_u32(ip_2_ip4(&dhcp->server_ip_addr))));
+
+    dhcp_option(dhcp, DHCP_OPTION_PARAMETER_REQUEST_LIST, LWIP_ARRAYSIZE(dhcp_discover_request_options));
+    for (i = 0; i < LWIP_ARRAYSIZE(dhcp_discover_request_options); i++) {
+      dhcp_option_byte(dhcp, dhcp_discover_request_options[i]);
+    }
+
+#if LWIP_NETIF_HOSTNAME
+    dhcp_option_hostname(dhcp, netif);
+#endif /* LWIP_NETIF_HOSTNAME */
+
+    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);
+
+    /* send broadcast to any DHCP server */
+    udp_sendto_if_src(dhcp_pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif, IP4_ADDR_ANY);
+    dhcp_delete_msg(dhcp);
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_select: REQUESTING\n"));
+  } else {
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("dhcp_select: could not allocate DHCP request\n"));
+  }
+  if (dhcp->tries < 255) {
+    dhcp->tries++;
+  }
+  msecs = (dhcp->tries < 6 ? 1 << dhcp->tries : 60) * 1000;
+  dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
+  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_select(): set request timeout %"U16_F" msecs\n", msecs));
+  return result;
+}
+
+/**
+ * The DHCP timer that checks for lease renewal/rebind timeouts.
+ * Must be called once a minute (see @ref DHCP_COARSE_TIMER_SECS).
+ */
+void
+dhcp_coarse_tmr(void)
+{
+  struct netif *netif = netif_list;
+  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_coarse_tmr()\n"));
+  /* iterate through all network interfaces */
+  while (netif != NULL) {
+    /* only act on DHCP configured interfaces */
+    struct dhcp *dhcp = netif_dhcp_data(netif);
+    if ((dhcp != NULL) && (dhcp->state != DHCP_STATE_OFF)) {
+      /* compare lease time to expire timeout */
+      if (dhcp->t0_timeout && (++dhcp->lease_used == dhcp->t0_timeout)) {
+        LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_coarse_tmr(): t0 timeout\n"));
+        /* this clients' lease time has expired */
+        dhcp_release(netif);
+        dhcp_discover(netif);
+      /* timer is active (non zero), and triggers (zeroes) now? */
+      } else if (dhcp->t2_rebind_time && (dhcp->t2_rebind_time-- == 1)) {
+        LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_coarse_tmr(): t2 timeout\n"));
+        /* this clients' rebind timeout triggered */
+        dhcp_t2_timeout(netif);
+      /* timer is active (non zero), and triggers (zeroes) now */
+      } else if (dhcp->t1_renew_time && (dhcp->t1_renew_time-- == 1)) {
+        LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_coarse_tmr(): t1 timeout\n"));
+        /* this clients' renewal timeout triggered */
+        dhcp_t1_timeout(netif);
+      }
+    }
+    /* proceed to next netif */
+    netif = netif->next;
+  }
+}
+
+/**
+ * DHCP transaction timeout handling (this function must be called every 500ms,
+ * see @ref DHCP_FINE_TIMER_MSECS).
+ *
+ * A DHCP server is expected to respond within a short period of time.
+ * This timer checks whether an outstanding DHCP request is timed out.
+ */
+void
+dhcp_fine_tmr(void)
+{
+  struct netif *netif = netif_list;
+  /* loop through netif's */
+  while (netif != NULL) {
+    struct dhcp *dhcp = netif_dhcp_data(netif);
+    /* only act on DHCP configured interfaces */
+    if (dhcp != NULL) {
+      /* timer is active (non zero), and is about to trigger now */
+      if (dhcp->request_timeout > 1) {
+        dhcp->request_timeout--;
+      }
+      else if (dhcp->request_timeout == 1) {
+        dhcp->request_timeout--;
+        /* { netif->dhcp->request_timeout == 0 } */
+        LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_fine_tmr(): request timeout\n"));
+        /* this client's request timeout triggered */
+        dhcp_timeout(netif);
+      }
+    }
+    /* 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_data(netif);
+
+  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_timeout()\n"));
+  /* back-off period has passed, or server selection timed out */
+  if ((dhcp->state == DHCP_STATE_BACKING_OFF) || (dhcp->state == DHCP_STATE_SELECTING)) {
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_timeout(): restarting discovery\n"));
+    dhcp_discover(netif);
+  /* receiving the requested lease timed out */
+  } else if (dhcp->state == DHCP_STATE_REQUESTING) {
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): REQUESTING, DHCP request timed out\n"));
+    if (dhcp->tries <= 5) {
+      dhcp_select(netif);
+    } else {
+      LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): REQUESTING, releasing, restarting\n"));
+      dhcp_release(netif);
+      dhcp_discover(netif);
+    }
+#if DHCP_DOES_ARP_CHECK
+  /* received no ARP reply for the offered address (which is good) */
+  } else if (dhcp->state == DHCP_STATE_CHECKING) {
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_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);
+    }
+#endif /* DHCP_DOES_ARP_CHECK */
+  } else if (dhcp->state == DHCP_STATE_REBOOTING) {
+    if (dhcp->tries < REBOOT_TRIES) {
+      dhcp_reboot(netif);
+    } else {
+      dhcp_discover(netif);
+    }
+  }
+}
+
+/**
+ * The renewal period has timed out.
+ *
+ * @param netif the netif under DHCP control
+ */
+static void
+dhcp_t1_timeout(struct netif *netif)
+{
+  struct dhcp *dhcp = netif_dhcp_data(netif);
+
+  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_t1_timeout()\n"));
+  if ((dhcp->state == DHCP_STATE_REQUESTING) || (dhcp->state == DHCP_STATE_BOUND) ||
+      (dhcp->state == DHCP_STATE_RENEWING)) {
+    /* just retry to renew - note that the rebind timer (t2) will
+     * eventually time-out if renew tries fail. */
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+                ("dhcp_t1_timeout(): must renew\n"));
+    /* This slightly different to RFC2131: DHCPREQUEST will be sent from state
+       DHCP_STATE_RENEWING, not DHCP_STATE_BOUND */
+    dhcp_renew(netif);
+    /* Calculate next timeout */
+    if (((dhcp->t2_timeout - dhcp->lease_used) / 2) >= ((60 + DHCP_COARSE_TIMER_SECS / 2) / DHCP_COARSE_TIMER_SECS))
+    {
+       dhcp->t1_renew_time = ((dhcp->t2_timeout - dhcp->lease_used) / 2);
+    }
+  }
+}
+
+/**
+ * The rebind period has timed out.
+ *
+ * @param netif the netif under DHCP control
+ */
+static void
+dhcp_t2_timeout(struct netif *netif)
+{
+  struct dhcp *dhcp = netif_dhcp_data(netif);
+
+  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_t2_timeout()\n"));
+  if ((dhcp->state == DHCP_STATE_REQUESTING) || (dhcp->state == DHCP_STATE_BOUND) ||
+      (dhcp->state == DHCP_STATE_RENEWING) || (dhcp->state == DHCP_STATE_REBINDING)) {
+    /* just retry to rebind */
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+                ("dhcp_t2_timeout(): must rebind\n"));
+    /* This slightly different to RFC2131: DHCPREQUEST will be sent from state
+       DHCP_STATE_REBINDING, not DHCP_STATE_BOUND */
+    dhcp_rebind(netif);
+    /* Calculate next timeout */
+    if (((dhcp->t0_timeout - dhcp->lease_used) / 2) >= ((60 + DHCP_COARSE_TIMER_SECS / 2) / DHCP_COARSE_TIMER_SECS))
+    {
+       dhcp->t2_rebind_time = ((dhcp->t0_timeout - dhcp->lease_used) / 2);
+    }
+  }
+}
+
+/**
+ * Handle a DHCP ACK packet
+ *
+ * @param netif the netif under DHCP control
+ */
+static void
+dhcp_handle_ack(struct netif *netif)
+{
+  struct dhcp *dhcp = netif_dhcp_data(netif);
+
+#if LWIP_DHCP_PROVIDE_DNS_SERVERS || LWIP_DHCP_GET_NTP_SRV
+  u8_t n;
+#endif /* LWIP_DHCP_PROVIDE_DNS_SERVERS || LWIP_DHCP_GET_NTP_SRV */
+#if LWIP_DHCP_GET_NTP_SRV
+  ip4_addr_t ntp_server_addrs[LWIP_DHCP_MAX_NTP_SERVERS];
+#endif
+
+  /* clear options we might not get from the ACK */
+  ip4_addr_set_zero(&dhcp->offered_sn_mask);
+  ip4_addr_set_zero(&dhcp->offered_gw_addr);
+#if LWIP_DHCP_BOOTP_FILE
+  ip4_addr_set_zero(&dhcp->offered_si_addr);
+#endif /* LWIP_DHCP_BOOTP_FILE */
+
+  /* lease time given? */
+  if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_LEASE_TIME)) {
+    /* remember offered lease time */
+    dhcp->offered_t0_lease = dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_LEASE_TIME);
+  }
+  /* renewal period given? */
+  if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_T1)) {
+    /* remember given renewal period */
+    dhcp->offered_t1_renew = dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_T1);
+  } else {
+    /* calculate safe periods for renewal */
+    dhcp->offered_t1_renew = dhcp->offered_t0_lease / 2;
+  }
+
+  /* renewal period given? */
+  if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_T2)) {
+    /* remember given rebind period */
+    dhcp->offered_t2_rebind = dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_T2);
+  } else {
+    /* calculate safe periods for rebinding (offered_t0_lease * 0.875 -> 87.5%)*/
+    dhcp->offered_t2_rebind = (dhcp->offered_t0_lease * 7U) / 8U;
+  }
+
+  /* (y)our internet address */
+  ip4_addr_copy(dhcp->offered_ip_addr, dhcp->msg_in->yiaddr);
+
+#if LWIP_DHCP_BOOTP_FILE
+  /* copy boot server address,
+     boot file name copied in dhcp_parse_reply if not overloaded */
+  ip4_addr_copy(dhcp->offered_si_addr, dhcp->msg_in->siaddr);
+#endif /* LWIP_DHCP_BOOTP_FILE */
+
+  /* subnet mask given? */
+  if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_SUBNET_MASK)) {
+    /* remember given subnet mask */
+    ip4_addr_set_u32(&dhcp->offered_sn_mask, lwip_htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_SUBNET_MASK)));
+    dhcp->subnet_mask_given = 1;
+  } else {
+    dhcp->subnet_mask_given = 0;
+  }
+
+  /* gateway router */
+  if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_ROUTER)) {
+    ip4_addr_set_u32(&dhcp->offered_gw_addr, lwip_htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_ROUTER)));
+  }
+
+#if LWIP_DHCP_GET_NTP_SRV
+  /* NTP servers */
+  for (n = 0; (n < LWIP_DHCP_MAX_NTP_SERVERS) && dhcp_option_given(dhcp, DHCP_OPTION_IDX_NTP_SERVER + n); n++) {
+    ip4_addr_set_u32(&ntp_server_addrs[n], lwip_htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_NTP_SERVER + n)));
+  }
+  dhcp_set_ntp_servers(n, ntp_server_addrs);
+#endif /* LWIP_DHCP_GET_NTP_SRV */
+
+#if LWIP_DHCP_PROVIDE_DNS_SERVERS
+  /* DNS servers */
+  for (n = 0; (n < LWIP_DHCP_PROVIDE_DNS_SERVERS) && dhcp_option_given(dhcp, DHCP_OPTION_IDX_DNS_SERVER + n); n++) {
+    ip_addr_t dns_addr;
+    ip_addr_set_ip4_u32(&dns_addr, lwip_htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_DNS_SERVER + n)));
+    dns_setserver(n, &dns_addr);
+  }
+#endif /* LWIP_DHCP_PROVIDE_DNS_SERVERS */
+}
+
+/**
+ * @ingroup dhcp4
+ * Set a statically allocated struct dhcp to work with.
+ * Using this prevents dhcp_start to allocate it using mem_malloc.
+ *
+ * @param netif the netif for which to set the struct dhcp
+ * @param dhcp (uninitialised) dhcp struct allocated by the application
+ */
+void
+dhcp_set_struct(struct netif *netif, struct dhcp *dhcp)
+{
+  LWIP_ASSERT("netif != NULL", netif != NULL);
+  LWIP_ASSERT("dhcp != NULL", dhcp != NULL);
+  LWIP_ASSERT("netif already has a struct dhcp set", netif_dhcp_data(netif) == NULL);
+
+  /* clear data structure */
+  memset(dhcp, 0, sizeof(struct dhcp));
+  /* dhcp_set_state(&dhcp, DHCP_STATE_OFF); */
+  netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP, dhcp);
+}
+
+/**
+ * @ingroup dhcp4
+ * Removes a struct dhcp from a netif.
+ *
+ * ATTENTION: Only use this when not using dhcp_set_struct() to allocate the
+ *            struct dhcp since the memory is passed back to the heap.
+ *
+ * @param netif the netif from which to remove the struct dhcp
+ */
+void dhcp_cleanup(struct netif *netif)
+{
+  LWIP_ASSERT("netif != NULL", netif != NULL);
+
+  if (netif_dhcp_data(netif) != NULL) {
+    mem_free(netif_dhcp_data(netif));
+    netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP, NULL);
+  }
+}
+
+/**
+ * @ingroup dhcp4
+ * Start DHCP negotiation for a network interface.
+ *
+ * If no DHCP client instance was attached to this interface,
+ * a new client is created first. If a DHCP client instance
+ * was already present, it restarts negotiation.
+ *
+ * @param netif The lwIP network interface
+ * @return lwIP error code
+ * - ERR_OK - No error
+ * - ERR_MEM - Out of memory
+ */
+err_t
+dhcp_start(struct netif *netif)
+{
+  struct dhcp *dhcp;
+  err_t result;
+
+  LWIP_ERROR("netif != NULL", (netif != NULL), return ERR_ARG;);
+  LWIP_ERROR("netif is not up, old style port?", netif_is_up(netif), return ERR_ARG;);
+  dhcp = netif_dhcp_data(netif);
+  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_start(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num));
+
+  /* check MTU of the netif */
+  if (netif->mtu < DHCP_MAX_MSG_LEN_MIN_REQUIRED) {
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): Cannot use this netif with DHCP: MTU is too small\n"));
+    return ERR_MEM;
+  }
+
+  /* no DHCP client attached yet? */
+  if (dhcp == NULL) {
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): starting new DHCP client\n"));
+    dhcp = (struct dhcp *)mem_malloc(sizeof(struct dhcp));
+    if (dhcp == NULL) {
+      LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): could not allocate dhcp\n"));
+      return ERR_MEM;
+    }
+
+    /* store this dhcp client in the netif */
+    netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP, dhcp);
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): allocated dhcp"));
+  /* already has DHCP client attached */
+  } else {
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_start(): restarting DHCP configuration\n"));
+    LWIP_ASSERT("pbuf p_out wasn't freed", dhcp->p_out == NULL);
+    LWIP_ASSERT("reply wasn't freed", dhcp->msg_in == NULL );
+
+    if (dhcp->pcb_allocated != 0) {
+      dhcp_dec_pcb_refcount(); /* free DHCP PCB if not needed any more */
+    }
+    /* dhcp is cleared below, no need to reset flag*/
+  }
+
+  /* clear data structure */
+  memset(dhcp, 0, sizeof(struct dhcp));
+  /* dhcp_set_state(&dhcp, DHCP_STATE_OFF); */
+
+  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): starting DHCP configuration\n"));
+
+  if (dhcp_inc_pcb_refcount() != ERR_OK) { /* ensure DHCP PCB is allocated */
+    return ERR_MEM;
+  }
+  dhcp->pcb_allocated = 1;
+
+#if LWIP_DHCP_CHECK_LINK_UP
+  if (!netif_is_link_up(netif)) {
+    /* set state INIT and wait for dhcp_network_changed() to call dhcp_discover() */
+    dhcp_set_state(dhcp, DHCP_STATE_INIT);
+    return ERR_OK;
+  }
+#endif /* LWIP_DHCP_CHECK_LINK_UP */
+
+
+  /* (re)start the DHCP negotiation */
+  result = dhcp_discover(netif);
+  if (result != ERR_OK) {
+    /* free resources allocated above */
+    dhcp_stop(netif);
+    return ERR_MEM;
+  }
+  return result;
+}
+
+/**
+ * @ingroup dhcp4
+ * Inform a DHCP server of our manual configuration.
+ *
+ * This informs DHCP servers of our fixed IP address configuration
+ * by sending an INFORM message. It does not involve DHCP address
+ * configuration, it is just here to be nice to the network.
+ *
+ * @param netif The lwIP network interface
+ */
+void
+dhcp_inform(struct netif *netif)
+{
+  struct dhcp dhcp;
+  err_t result = ERR_OK;
+
+  LWIP_ERROR("netif != NULL", (netif != NULL), return;);
+
+  if (dhcp_inc_pcb_refcount() != ERR_OK) { /* ensure DHCP PCB is allocated */
+    return;
+  }
+
+  memset(&dhcp, 0, sizeof(struct dhcp));
+  dhcp_set_state(&dhcp, DHCP_STATE_INFORMING);
+
+  /* create and initialize the DHCP message header */
+  result = dhcp_create_msg(netif, &dhcp, DHCP_INFORM);
+  if (result == ERR_OK) {
+    dhcp_option(&dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
+    dhcp_option_short(&dhcp, DHCP_MAX_MSG_LEN(netif));
+
+    dhcp_option_trailer(&dhcp);
+
+    pbuf_realloc(dhcp.p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp.options_out_len);
+
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_inform: INFORMING\n"));
+
+    udp_sendto_if(dhcp_pcb, dhcp.p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif);
+
+    dhcp_delete_msg(&dhcp);
+  } else {
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_inform: could not allocate DHCP request\n"));
+  }
+
+  dhcp_dec_pcb_refcount(); /* delete DHCP PCB if not needed any more */
+}
+
+/** Handle a possible change in the network configuration.
+ *
+ * This enters the REBOOTING state to verify that the currently bound
+ * address is still valid.
+ */
+void
+dhcp_network_changed(struct netif *netif)
+{
+  struct dhcp *dhcp = netif_dhcp_data(netif);
+
+  if (!dhcp)
+    return;
+  switch (dhcp->state) {
+  case DHCP_STATE_REBINDING:
+  case DHCP_STATE_RENEWING:
+  case DHCP_STATE_BOUND:
+  case DHCP_STATE_REBOOTING:
+    dhcp->tries = 0;
+    dhcp_reboot(netif);
+    break;
+  case DHCP_STATE_OFF:
+    /* stay off */
+    break;
+  default:
+    /* INIT/REQUESTING/CHECKING/BACKING_OFF restart with new 'rid' because the
+       state changes, SELECTING: continue with current 'rid' as we stay in the
+       same state */
+#if LWIP_DHCP_AUTOIP_COOP
+    if (dhcp->autoip_coop_state == DHCP_AUTOIP_COOP_STATE_ON) {
+      autoip_stop(netif);
+      dhcp->autoip_coop_state = DHCP_AUTOIP_COOP_STATE_OFF;
+    }
+#endif /* LWIP_DHCP_AUTOIP_COOP */
+    /* ensure we start with short timeouts, even if already discovering */
+    dhcp->tries = 0;
+    dhcp_discover(netif);
+    break;
+  }
+}
+
+#if DHCP_DOES_ARP_CHECK
+/**
+ * Match an ARP reply with the offered IP address:
+ * check whether the offered IP address is not in use using ARP
+ *
+ * @param netif the network interface on which the reply was received
+ * @param addr The IP address we received a reply from
+ */
+void
+dhcp_arp_reply(struct netif *netif, const ip4_addr_t *addr)
+{
+  struct dhcp *dhcp;
+
+  LWIP_ERROR("netif != NULL", (netif != NULL), return;);
+  dhcp = netif_dhcp_data(netif);
+  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_arp_reply()\n"));
+  /* is a DHCP client doing an ARP check? */
+  if ((dhcp != NULL) && (dhcp->state == DHCP_STATE_CHECKING)) {
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_arp_reply(): CHECKING, arp reply for 0x%08"X32_F"\n",
+      ip4_addr_get_u32(addr)));
+    /* did a host respond with the address we
+       were offered by the DHCP server? */
+    if (ip4_addr_cmp(addr, &dhcp->offered_ip_addr)) {
+      /* we will not accept the offered address */
+      LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING,
+        ("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).
+ *
+ * @param netif the netif under DHCP control
+ */
+static err_t
+dhcp_decline(struct netif *netif)
+{
+  struct dhcp *dhcp = netif_dhcp_data(netif);
+  err_t result = ERR_OK;
+  u16_t msecs;
+  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_decline()\n"));
+  dhcp_set_state(dhcp, DHCP_STATE_BACKING_OFF);
+  /* create and initialize the DHCP message header */
+  result = dhcp_create_msg(netif, dhcp, DHCP_DECLINE);
+  if (result == ERR_OK) {
+    dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4);
+    dhcp_option_long(dhcp, lwip_ntohl(ip4_addr_get_u32(&dhcp->offered_ip_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);
+
+    /* per section 4.4.4, broadcast DECLINE messages */
+    udp_sendto_if_src(dhcp_pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif, IP4_ADDR_ANY);
+    dhcp_delete_msg(dhcp);
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_decline: BACKING OFF\n"));
+  } else {
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
+      ("dhcp_decline: could not allocate DHCP request\n"));
+  }
+  if (dhcp->tries < 255) {
+    dhcp->tries++;
+  }
+  msecs = 10*1000;
+  dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
+  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_decline(): set request timeout %"U16_F" msecs\n", msecs));
+  return result;
+}
+#endif /* DHCP_DOES_ARP_CHECK */
+
+
+/**
+ * Start the DHCP process, discover a DHCP server.
+ *
+ * @param netif the netif under DHCP control
+ */
+static err_t
+dhcp_discover(struct netif *netif)
+{
+  struct dhcp *dhcp = netif_dhcp_data(netif);
+  err_t result = ERR_OK;
+  u16_t msecs;
+  u8_t i;
+  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover()\n"));
+  ip4_addr_set_any(&dhcp->offered_ip_addr);
+  dhcp_set_state(dhcp, DHCP_STATE_SELECTING);
+  /* create and initialize the DHCP message header */
+  result = dhcp_create_msg(netif, dhcp, DHCP_DISCOVER);
+  if (result == ERR_OK) {
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: making request\n"));
+
+    dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
+    dhcp_option_short(dhcp, DHCP_MAX_MSG_LEN(netif));
+
+    dhcp_option(dhcp, DHCP_OPTION_PARAMETER_REQUEST_LIST, LWIP_ARRAYSIZE(dhcp_discover_request_options));
+    for (i = 0; i < LWIP_ARRAYSIZE(dhcp_discover_request_options); i++) {
+      dhcp_option_byte(dhcp, dhcp_discover_request_options[i]);
+    }
+    dhcp_option_trailer(dhcp);
+
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: realloc()ing\n"));
+    pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len);
+
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: sendto(DISCOVER, IP_ADDR_BROADCAST, DHCP_SERVER_PORT)\n"));
+    udp_sendto_if_src(dhcp_pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif, IP4_ADDR_ANY);
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: deleting()ing\n"));
+    dhcp_delete_msg(dhcp);
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_discover: SELECTING\n"));
+  } else {
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_discover: could not allocate DHCP request\n"));
+  }
+  if (dhcp->tries < 255) {
+    dhcp->tries++;
+  }
+#if LWIP_DHCP_AUTOIP_COOP
+  if (dhcp->tries >= LWIP_DHCP_AUTOIP_COOP_TRIES && dhcp->autoip_coop_state == DHCP_AUTOIP_COOP_STATE_OFF) {
+    dhcp->autoip_coop_state = DHCP_AUTOIP_COOP_STATE_ON;
+    autoip_start(netif);
+  }
+#endif /* LWIP_DHCP_AUTOIP_COOP */
+  msecs = (dhcp->tries < 6 ? 1 << dhcp->tries : 60) * 1000;
+  dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
+  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_discover(): set request timeout %"U16_F" msecs\n", msecs));
+  return result;
+}
+
+
+/**
+ * Bind the interface to the offered IP address.
+ *
+ * @param netif network interface to bind to the offered address
+ */
+static void
+dhcp_bind(struct netif *netif)
+{
+  u32_t timeout;
+  struct dhcp *dhcp;
+  ip4_addr_t sn_mask, gw_addr;
+  LWIP_ERROR("dhcp_bind: netif != NULL", (netif != NULL), return;);
+  dhcp = netif_dhcp_data(netif);
+  LWIP_ERROR("dhcp_bind: dhcp != NULL", (dhcp != NULL), return;);
+  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_bind(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num));
+
+  /* reset time used of lease */
+  dhcp->lease_used = 0;
+
+  if (dhcp->offered_t0_lease != 0xffffffffUL) {
+     /* set renewal period timer */
+     LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_bind(): t0 renewal timer %"U32_F" secs\n", dhcp->offered_t0_lease));
+     timeout = (dhcp->offered_t0_lease + DHCP_COARSE_TIMER_SECS / 2) / DHCP_COARSE_TIMER_SECS;
+     if (timeout > 0xffff) {
+       timeout = 0xffff;
+     }
+     dhcp->t0_timeout = (u16_t)timeout;
+     if (dhcp->t0_timeout == 0) {
+       dhcp->t0_timeout = 1;
+     }
+     LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_bind(): set request timeout %"U32_F" msecs\n", dhcp->offered_t0_lease*1000));
+  }
+
+  /* temporary DHCP lease? */
+  if (dhcp->offered_t1_renew != 0xffffffffUL) {
+    /* set renewal period timer */
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_bind(): t1 renewal timer %"U32_F" secs\n", dhcp->offered_t1_renew));
+    timeout = (dhcp->offered_t1_renew + DHCP_COARSE_TIMER_SECS / 2) / DHCP_COARSE_TIMER_SECS;
+    if (timeout > 0xffff) {
+      timeout = 0xffff;
+    }
+    dhcp->t1_timeout = (u16_t)timeout;
+    if (dhcp->t1_timeout == 0) {
+      dhcp->t1_timeout = 1;
+    }
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_bind(): set request timeout %"U32_F" msecs\n", dhcp->offered_t1_renew*1000));
+    dhcp->t1_renew_time = dhcp->t1_timeout;
+  }
+  /* set renewal period timer */
+  if (dhcp->offered_t2_rebind != 0xffffffffUL) {
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_bind(): t2 rebind timer %"U32_F" secs\n", dhcp->offered_t2_rebind));
+    timeout = (dhcp->offered_t2_rebind + DHCP_COARSE_TIMER_SECS / 2) / DHCP_COARSE_TIMER_SECS;
+    if (timeout > 0xffff) {
+      timeout = 0xffff;
+    }
+    dhcp->t2_timeout = (u16_t)timeout;
+    if (dhcp->t2_timeout == 0) {
+      dhcp->t2_timeout = 1;
+    }
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_bind(): set request timeout %"U32_F" msecs\n", dhcp->offered_t2_rebind*1000));
+    dhcp->t2_rebind_time = dhcp->t2_timeout;
+  }
+
+  /* If we have sub 1 minute lease, t2 and t1 will kick in at the same time. */
+  if ((dhcp->t1_timeout >= dhcp->t2_timeout) && (dhcp->t2_timeout > 0)) {
+    dhcp->t1_timeout = 0;
+  }
+
+  if (dhcp->subnet_mask_given) {
+    /* copy offered network mask */
+    ip4_addr_copy(sn_mask, dhcp->offered_sn_mask);
+  } else {
+    /* subnet mask not given, choose a safe subnet mask given the network class */
+    u8_t first_octet = ip4_addr1(&dhcp->offered_ip_addr);
+    if (first_octet <= 127) {
+      ip4_addr_set_u32(&sn_mask, PP_HTONL(0xff000000UL));
+    } else if (first_octet >= 192) {
+      ip4_addr_set_u32(&sn_mask, PP_HTONL(0xffffff00UL));
+    } else {
+      ip4_addr_set_u32(&sn_mask, PP_HTONL(0xffff0000UL));
+    }
+  }
+
+  ip4_addr_copy(gw_addr, dhcp->offered_gw_addr);
+  /* gateway address not given? */
+  if (ip4_addr_isany_val(gw_addr)) {
+    /* copy network address */
+    ip4_addr_get_network(&gw_addr, &dhcp->offered_ip_addr, &sn_mask);
+    /* use first host address on network as gateway */
+    ip4_addr_set_u32(&gw_addr, ip4_addr_get_u32(&gw_addr) | PP_HTONL(0x00000001UL));
+  }
+
+#if LWIP_DHCP_AUTOIP_COOP
+  if (dhcp->autoip_coop_state == DHCP_AUTOIP_COOP_STATE_ON) {
+    autoip_stop(netif);
+    dhcp->autoip_coop_state = DHCP_AUTOIP_COOP_STATE_OFF;
+  }
+#endif /* LWIP_DHCP_AUTOIP_COOP */
+
+  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_bind(): IP: 0x%08"X32_F" SN: 0x%08"X32_F" GW: 0x%08"X32_F"\n",
+    ip4_addr_get_u32(&dhcp->offered_ip_addr), ip4_addr_get_u32(&sn_mask), ip4_addr_get_u32(&gw_addr)));
+  /* netif is now bound to DHCP leased address - set this before assigning the address
+     to ensure the callback can use dhcp_supplied_address() */
+  dhcp_set_state(dhcp, DHCP_STATE_BOUND);
+
+  netif_set_addr(netif, &dhcp->offered_ip_addr, &sn_mask, &gw_addr);
+  /* interface is used by routing now that an address is set */
+}
+
+/**
+ * @ingroup dhcp4
+ * Renew an existing DHCP lease at the involved DHCP server.
+ *
+ * @param netif network interface which must renew its lease
+ */
+err_t
+dhcp_renew(struct netif *netif)
+{
+  struct dhcp *dhcp = netif_dhcp_data(netif);
+  err_t result;
+  u16_t msecs;
+  u8_t i;
+  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_renew()\n"));
+  dhcp_set_state(dhcp, DHCP_STATE_RENEWING);
+
+  /* create and initialize the DHCP message header */
+  result = dhcp_create_msg(netif, dhcp, DHCP_REQUEST);
+  if (result == ERR_OK) {
+    dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
+    dhcp_option_short(dhcp, DHCP_MAX_MSG_LEN(netif));
+
+    dhcp_option(dhcp, DHCP_OPTION_PARAMETER_REQUEST_LIST, LWIP_ARRAYSIZE(dhcp_discover_request_options));
+    for (i = 0; i < LWIP_ARRAYSIZE(dhcp_discover_request_options); i++) {
+      dhcp_option_byte(dhcp, dhcp_discover_request_options[i]);
+    }
+
+#if LWIP_NETIF_HOSTNAME
+    dhcp_option_hostname(dhcp, netif);
+#endif /* LWIP_NETIF_HOSTNAME */
+
+    /* 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_sendto_if(dhcp_pcb, dhcp->p_out, &dhcp->server_ip_addr, DHCP_SERVER_PORT, netif);
+    dhcp_delete_msg(dhcp);
+
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_renew: RENEWING\n"));
+  } else {
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_renew: could not allocate DHCP request\n"));
+  }
+  if (dhcp->tries < 255) {
+    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 | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_renew(): set request timeout %"U16_F" msecs\n", msecs));
+  return result;
+}
+
+/**
+ * Rebind with a DHCP server for an existing DHCP lease.
+ *
+ * @param netif network interface which must rebind with a DHCP server
+ */
+static err_t
+dhcp_rebind(struct netif *netif)
+{
+  struct dhcp *dhcp = netif_dhcp_data(netif);
+  err_t result;
+  u16_t msecs;
+  u8_t i;
+  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_rebind()\n"));
+  dhcp_set_state(dhcp, DHCP_STATE_REBINDING);
+
+  /* create and initialize the DHCP message header */
+  result = dhcp_create_msg(netif, dhcp, DHCP_REQUEST);
+  if (result == ERR_OK) {
+    dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
+    dhcp_option_short(dhcp, DHCP_MAX_MSG_LEN(netif));
+
+    dhcp_option(dhcp, DHCP_OPTION_PARAMETER_REQUEST_LIST, LWIP_ARRAYSIZE(dhcp_discover_request_options));
+    for (i = 0; i < LWIP_ARRAYSIZE(dhcp_discover_request_options); i++) {
+      dhcp_option_byte(dhcp, dhcp_discover_request_options[i]);
+    }
+
+#if LWIP_NETIF_HOSTNAME
+    dhcp_option_hostname(dhcp, netif);
+#endif /* LWIP_NETIF_HOSTNAME */
+
+    dhcp_option_trailer(dhcp);
+
+    pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len);
+
+    /* broadcast to server */
+    udp_sendto_if(dhcp_pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif);
+    dhcp_delete_msg(dhcp);
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_rebind: REBINDING\n"));
+  } else {
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_rebind: could not allocate DHCP request\n"));
+  }
+  if (dhcp->tries < 255) {
+    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 | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_rebind(): set request timeout %"U16_F" msecs\n", msecs));
+  return result;
+}
+
+/**
+ * Enter REBOOTING state to verify an existing lease
+ *
+ * @param netif network interface which must reboot
+ */
+static err_t
+dhcp_reboot(struct netif *netif)
+{
+  struct dhcp *dhcp = netif_dhcp_data(netif);
+  err_t result;
+  u16_t msecs;
+  u8_t i;
+  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_reboot()\n"));
+  dhcp_set_state(dhcp, DHCP_STATE_REBOOTING);
+
+  /* create and initialize the DHCP message header */
+  result = dhcp_create_msg(netif, dhcp, DHCP_REQUEST);
+  if (result == ERR_OK) {
+    dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
+    dhcp_option_short(dhcp, DHCP_MAX_MSG_LEN_MIN_REQUIRED);
+
+    dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4);
+    dhcp_option_long(dhcp, lwip_ntohl(ip4_addr_get_u32(&dhcp->offered_ip_addr)));
+
+    dhcp_option(dhcp, DHCP_OPTION_PARAMETER_REQUEST_LIST, LWIP_ARRAYSIZE(dhcp_discover_request_options));
+    for (i = 0; i < LWIP_ARRAYSIZE(dhcp_discover_request_options); i++) {
+      dhcp_option_byte(dhcp, dhcp_discover_request_options[i]);
+    }
+
+    dhcp_option_trailer(dhcp);
+
+    pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len);
+
+    /* broadcast to server */
+    udp_sendto_if(dhcp_pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif);
+    dhcp_delete_msg(dhcp);
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_reboot: REBOOTING\n"));
+  } else {
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_reboot: could not allocate DHCP request\n"));
+  }
+  if (dhcp->tries < 255) {
+    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 | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_reboot(): set request timeout %"U16_F" msecs\n", msecs));
+  return result;
+}
+
+
+/**
+ * @ingroup dhcp4
+ * Release a DHCP lease (usually called before @ref dhcp_stop).
+ *
+ * @param netif network interface which must release its lease
+ */
+err_t
+dhcp_release(struct netif *netif)
+{
+  struct dhcp *dhcp = netif_dhcp_data(netif);
+  err_t result;
+  ip_addr_t server_ip_addr;
+  u8_t is_dhcp_supplied_address;
+
+  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_release()\n"));
+  if (dhcp == NULL) {
+    return ERR_ARG;
+  }
+  ip_addr_copy(server_ip_addr, dhcp->server_ip_addr);
+
+  is_dhcp_supplied_address = dhcp_supplied_address(netif);
+
+  /* idle DHCP client */
+  dhcp_set_state(dhcp, DHCP_STATE_OFF);
+  /* clean old DHCP offer */
+  ip_addr_set_zero_ip4(&dhcp->server_ip_addr);
+  ip4_addr_set_zero(&dhcp->offered_ip_addr);
+  ip4_addr_set_zero(&dhcp->offered_sn_mask);
+  ip4_addr_set_zero(&dhcp->offered_gw_addr);
+#if LWIP_DHCP_BOOTP_FILE
+  ip4_addr_set_zero(&dhcp->offered_si_addr);
+#endif /* LWIP_DHCP_BOOTP_FILE */
+  dhcp->offered_t0_lease = dhcp->offered_t1_renew = dhcp->offered_t2_rebind = 0;
+  dhcp->t1_renew_time = dhcp->t2_rebind_time = dhcp->lease_used = dhcp->t0_timeout = 0;
+
+  if (!is_dhcp_supplied_address) {
+    /* don't issue release message when address is not dhcp-assigned */
+    return ERR_OK;
+  }
+
+  /* create and initialize the DHCP message header */
+  result = dhcp_create_msg(netif, dhcp, DHCP_RELEASE);
+  if (result == ERR_OK) {
+    dhcp_option(dhcp, DHCP_OPTION_SERVER_ID, 4);
+    dhcp_option_long(dhcp, lwip_ntohl(ip4_addr_get_u32(ip_2_ip4(&server_ip_addr))));
+
+    dhcp_option_trailer(dhcp);
+
+    pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len);
+
+    udp_sendto_if(dhcp_pcb, dhcp->p_out, &server_ip_addr, DHCP_SERVER_PORT, netif);
+    dhcp_delete_msg(dhcp);
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_release: RELEASED, DHCP_STATE_OFF\n"));
+  } else {
+    /* sending release failed, but that's not a problem since the correct behaviour of dhcp does not rely on release */
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_release: could not allocate DHCP request\n"));
+  }
+  /* remove IP address from interface (prevents routing from selecting this interface) */
+  netif_set_addr(netif, IP4_ADDR_ANY4, IP4_ADDR_ANY4, IP4_ADDR_ANY4);
+
+  return result;
+}
+
+/**
+ * @ingroup dhcp4
+ * 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;
+  LWIP_ERROR("dhcp_stop: netif != NULL", (netif != NULL), return;);
+  dhcp = netif_dhcp_data(netif);
+
+  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_stop()\n"));
+  /* netif is DHCP configured? */
+  if (dhcp != NULL) {
+#if LWIP_DHCP_AUTOIP_COOP
+    if (dhcp->autoip_coop_state == DHCP_AUTOIP_COOP_STATE_ON) {
+      autoip_stop(netif);
+      dhcp->autoip_coop_state = DHCP_AUTOIP_COOP_STATE_OFF;
+    }
+#endif /* LWIP_DHCP_AUTOIP_COOP */
+
+    LWIP_ASSERT("reply wasn't freed", dhcp->msg_in == NULL);
+    dhcp_set_state(dhcp, DHCP_STATE_OFF);
+
+    if (dhcp->pcb_allocated != 0) {
+      dhcp_dec_pcb_refcount(); /* free DHCP PCB if not needed any more */
+      dhcp->pcb_allocated = 0;
+    }
+  }
+}
+
+/*
+ * Set the DHCP state of a DHCP client.
+ *
+ * If the state changed, reset the number of tries.
+ */
+static void
+dhcp_set_state(struct dhcp *dhcp, u8_t new_state)
+{
+  if (new_state != dhcp->state) {
+    dhcp->state = new_state;
+    dhcp->tries = 0;
+    dhcp->request_timeout = 0;
+  }
+}
+
+/*
+ * Concatenate an option type and length field to the outgoing
+ * DHCP message.
+ *
+ */
+static void
+dhcp_option(struct dhcp *dhcp, u8_t option_type, u8_t option_len)
+{
+  LWIP_ASSERT("dhcp_option: dhcp->options_out_len + 2 + option_len <= DHCP_OPTIONS_LEN", dhcp->options_out_len + 2U + 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_byte: 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 + 2U <= DHCP_OPTIONS_LEN);
+  dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0xff00U) >> 8);
+  dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t) (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 + 4U <= DHCP_OPTIONS_LEN);
+  dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0xff000000UL) >> 24);
+  dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0x00ff0000UL) >> 16);
+  dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0x0000ff00UL) >> 8);
+  dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0x000000ffUL));
+}
+
+#if LWIP_NETIF_HOSTNAME
+static void
+dhcp_option_hostname(struct dhcp *dhcp, struct netif *netif)
+{
+  if (netif->hostname != NULL) {
+    size_t namelen = strlen(netif->hostname);
+    if (namelen > 0) {
+      size_t len;
+      const char *p = netif->hostname;
+      /* Shrink len to available bytes (need 2 bytes for OPTION_HOSTNAME
+         and 1 byte for trailer) */
+      size_t available = DHCP_OPTIONS_LEN - dhcp->options_out_len - 3;
+      LWIP_ASSERT("DHCP: hostname is too long!", namelen <= available);
+      len = LWIP_MIN(namelen, available);
+      LWIP_ASSERT("DHCP: hostname is too long!", len <= 0xFF);
+      dhcp_option(dhcp, DHCP_OPTION_HOSTNAME, (u8_t)len);
+      while (len--) {
+        dhcp_option_byte(dhcp, *p++);
+      }
+    }
+  }
+}
+#endif /* LWIP_NETIF_HOSTNAME */
+
+/**
+ * Extract the DHCP message and the DHCP options.
+ *
+ * Extract the DHCP message and the DHCP options, each into a contiguous
+ * piece of memory. As a DHCP message is variable sized by its options,
+ * and also allows overriding some fields for options, the easy approach
+ * is to first unfold the options into a contiguous piece of memory, and
+ * use that further on.
+ *
+ */
+static err_t
+dhcp_parse_reply(struct dhcp *dhcp, struct pbuf *p)
+{
+  u8_t *options;
+  u16_t offset;
+  u16_t offset_max;
+  u16_t options_idx;
+  u16_t options_idx_max;
+  struct pbuf *q;
+  int parse_file_as_options = 0;
+  int parse_sname_as_options = 0;
+
+  /* clear received options */
+  dhcp_clear_all_options(dhcp);
+  /* check that beginning of dhcp_msg (up to and including chaddr) is in first pbuf */
+  if (p->len < DHCP_SNAME_OFS) {
+    return ERR_BUF;
+  }
+  dhcp->msg_in = (struct dhcp_msg *)p->payload;
+#if LWIP_DHCP_BOOTP_FILE
+  /* clear boot file name */
+  dhcp->boot_file_name[0] = 0;
+#endif /* LWIP_DHCP_BOOTP_FILE */
+
+  /* parse options */
+
+  /* start with options field */
+  options_idx = DHCP_OPTIONS_OFS;
+  /* parse options to the end of the received packet */
+  options_idx_max = p->tot_len;
+again:
+  q = p;
+  while ((q != NULL) && (options_idx >= q->len)) {
+    options_idx -= q->len;
+    options_idx_max -= q->len;
+    q = q->next;
+  }
+  if (q == NULL) {
+    return ERR_BUF;
+  }
+  offset = options_idx;
+  offset_max = options_idx_max;
+  options = (u8_t*)q->payload;
+  /* at least 1 byte to read and no end marker, then at least 3 bytes to read? */
+  while ((q != NULL) && (options[offset] != DHCP_OPTION_END) && (offset < offset_max)) {
+    u8_t op = options[offset];
+    u8_t len;
+    u8_t decode_len = 0;
+    int decode_idx = -1;
+    u16_t val_offset = offset + 2;
+    /* len byte might be in the next pbuf */
+    if ((offset + 1) < q->len) {
+      len = options[offset + 1];
+    } else {
+      len = (q->next != NULL ? ((u8_t*)q->next->payload)[0] : 0);
+    }
+    /* LWIP_DEBUGF(DHCP_DEBUG, ("msg_offset=%"U16_F", q->len=%"U16_F, msg_offset, q->len)); */
+    decode_len = len;
+    switch(op) {
+      /* case(DHCP_OPTION_END): handled above */
+      case(DHCP_OPTION_PAD):
+        /* special option: no len encoded */
+        decode_len = len = 0;
+        /* will be increased below */
+        offset--;
+        break;
+      case(DHCP_OPTION_SUBNET_MASK):
+        LWIP_ERROR("len == 4", len == 4, return ERR_VAL;);
+        decode_idx = DHCP_OPTION_IDX_SUBNET_MASK;
+        break;
+      case(DHCP_OPTION_ROUTER):
+        decode_len = 4; /* only copy the first given router */
+        LWIP_ERROR("len >= decode_len", len >= decode_len, return ERR_VAL;);
+        decode_idx = DHCP_OPTION_IDX_ROUTER;
+        break;
+#if LWIP_DHCP_PROVIDE_DNS_SERVERS
+      case(DHCP_OPTION_DNS_SERVER):
+        /* special case: there might be more than one server */
+        LWIP_ERROR("len %% 4 == 0", len % 4 == 0, return ERR_VAL;);
+        /* limit number of DNS servers */
+        decode_len = LWIP_MIN(len, 4 * DNS_MAX_SERVERS);
+        LWIP_ERROR("len >= decode_len", len >= decode_len, return ERR_VAL;);
+        decode_idx = DHCP_OPTION_IDX_DNS_SERVER;
+        break;
+#endif /* LWIP_DHCP_PROVIDE_DNS_SERVERS */
+      case(DHCP_OPTION_LEASE_TIME):
+        LWIP_ERROR("len == 4", len == 4, return ERR_VAL;);
+        decode_idx = DHCP_OPTION_IDX_LEASE_TIME;
+        break;
+#if LWIP_DHCP_GET_NTP_SRV
+      case(DHCP_OPTION_NTP):
+        /* special case: there might be more than one server */
+        LWIP_ERROR("len %% 4 == 0", len % 4 == 0, return ERR_VAL;);
+        /* limit number of NTP servers */
+        decode_len = LWIP_MIN(len, 4 * LWIP_DHCP_MAX_NTP_SERVERS);
+        LWIP_ERROR("len >= decode_len", len >= decode_len, return ERR_VAL;);
+        decode_idx = DHCP_OPTION_IDX_NTP_SERVER;
+        break;
+#endif /* LWIP_DHCP_GET_NTP_SRV*/
+      case(DHCP_OPTION_OVERLOAD):
+        LWIP_ERROR("len == 1", len == 1, return ERR_VAL;);
+        /* decode overload only in options, not in file/sname: invalid packet */
+        LWIP_ERROR("overload in file/sname", options_idx == DHCP_OPTIONS_OFS, return ERR_VAL;);
+        decode_idx = DHCP_OPTION_IDX_OVERLOAD;
+        break;
+      case(DHCP_OPTION_MESSAGE_TYPE):
+        LWIP_ERROR("len == 1", len == 1, return ERR_VAL;);
+        decode_idx = DHCP_OPTION_IDX_MSG_TYPE;
+        break;
+      case(DHCP_OPTION_SERVER_ID):
+        LWIP_ERROR("len == 4", len == 4, return ERR_VAL;);
+        decode_idx = DHCP_OPTION_IDX_SERVER_ID;
+        break;
+      case(DHCP_OPTION_T1):
+        LWIP_ERROR("len == 4", len == 4, return ERR_VAL;);
+        decode_idx = DHCP_OPTION_IDX_T1;
+        break;
+      case(DHCP_OPTION_T2):
+        LWIP_ERROR("len == 4", len == 4, return ERR_VAL;);
+        decode_idx = DHCP_OPTION_IDX_T2;
+        break;
+      default:
+        decode_len = 0;
+        LWIP_DEBUGF(DHCP_DEBUG, ("skipping option %"U16_F" in options\n", (u16_t)op));
+        break;
+    }
+    offset += len + 2;
+    if (decode_len > 0) {
+      u32_t value = 0;
+      u16_t copy_len;
+decode_next:
+      LWIP_ASSERT("check decode_idx", decode_idx >= 0 && decode_idx < DHCP_OPTION_IDX_MAX);
+      if (!dhcp_option_given(dhcp, decode_idx)) {
+        copy_len = LWIP_MIN(decode_len, 4);
+        if (pbuf_copy_partial(q, &value, copy_len, val_offset) != copy_len) {
+          return ERR_BUF;
+        }
+        if (decode_len > 4) {
+          /* decode more than one u32_t */
+          LWIP_ERROR("decode_len %% 4 == 0", decode_len % 4 == 0, return ERR_VAL;);
+          dhcp_got_option(dhcp, decode_idx);
+          dhcp_set_option_value(dhcp, decode_idx, lwip_htonl(value));
+          decode_len -= 4;
+          val_offset += 4;
+          decode_idx++;
+          goto decode_next;
+        } else if (decode_len == 4) {
+          value = lwip_ntohl(value);
+        } else {
+          LWIP_ERROR("invalid decode_len", decode_len == 1, return ERR_VAL;);
+          value = ((u8_t*)&value)[0];
+        }
+        dhcp_got_option(dhcp, decode_idx);
+        dhcp_set_option_value(dhcp, decode_idx, value);
+      }
+    }
+    if (offset >= q->len) {
+      offset -= q->len;
+      offset_max -= q->len;
+      if ((offset < offset_max) && offset_max) {
+        q = q->next;
+        LWIP_ASSERT("next pbuf was null", q);
+        options = (u8_t*)q->payload;
+      } else {
+        /* We've run out of bytes, probably no end marker. Don't proceed. */
+        break;
+      }
+    }
+  }
+  /* is this an overloaded message? */
+  if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_OVERLOAD)) {
+    u32_t overload = dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_OVERLOAD);
+    dhcp_clear_option(dhcp, DHCP_OPTION_IDX_OVERLOAD);
+    if (overload == DHCP_OVERLOAD_FILE) {
+      parse_file_as_options = 1;
+      LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("overloaded file field\n"));
+    } else if (overload == DHCP_OVERLOAD_SNAME) {
+      parse_sname_as_options = 1;
+      LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("overloaded sname field\n"));
+    } else if (overload == DHCP_OVERLOAD_SNAME_FILE) {
+      parse_sname_as_options = 1;
+      parse_file_as_options = 1;
+      LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("overloaded sname and file field\n"));
+    } else {
+      LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("invalid overload option: %d\n", (int)overload));
+    }
+#if LWIP_DHCP_BOOTP_FILE
+    if (!parse_file_as_options) {
+      /* only do this for ACK messages */
+      if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_MSG_TYPE) &&
+        (dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_MSG_TYPE) == DHCP_ACK))
+      /* copy bootp file name, don't care for sname (server hostname) */
+      if (pbuf_copy_partial(p, dhcp->boot_file_name, DHCP_FILE_LEN-1, DHCP_FILE_OFS) != (DHCP_FILE_LEN-1)) {
+        return ERR_BUF;
+      }
+      /* make sure the string is really NULL-terminated */
+      dhcp->boot_file_name[DHCP_FILE_LEN-1] = 0;
+    }
+#endif /* LWIP_DHCP_BOOTP_FILE */
+  }
+  if (parse_file_as_options) {
+    /* if both are overloaded, parse file first and then sname (RFC 2131 ch. 4.1) */
+    parse_file_as_options = 0;
+    options_idx = DHCP_FILE_OFS;
+    options_idx_max = DHCP_FILE_OFS + DHCP_FILE_LEN;
+    goto again;
+  } else if (parse_sname_as_options) {
+    parse_sname_as_options = 0;
+    options_idx = DHCP_SNAME_OFS;
+    options_idx_max = DHCP_SNAME_OFS + DHCP_SNAME_LEN;
+    goto again;
+  }
+  return ERR_OK;
+}
+
+/**
+ * If an incoming DHCP message is in response to us, then trigger the state machine
+ */
+static void
+dhcp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
+{
+  struct netif *netif = ip_current_input_netif();
+  struct dhcp *dhcp = netif_dhcp_data(netif);
+  struct dhcp_msg *reply_msg = (struct dhcp_msg *)p->payload;
+  u8_t msg_type;
+  u8_t i;
+
+  LWIP_UNUSED_ARG(arg);
+
+  /* Caught DHCP message from netif that does not have DHCP enabled? -> not interested */
+  if ((dhcp == NULL) || (dhcp->pcb_allocated == 0)) {
+    goto free_pbuf_and_return;
+  }
+
+  LWIP_ASSERT("invalid server address type", IP_IS_V4(addr));
+
+  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_recv(pbuf = %p) from DHCP server %"U16_F".%"U16_F".%"U16_F".%"U16_F" port %"U16_F"\n", (void*)p,
+    ip4_addr1_16(ip_2_ip4(addr)), ip4_addr2_16(ip_2_ip4(addr)), ip4_addr3_16(ip_2_ip4(addr)), ip4_addr4_16(ip_2_ip4(addr)), port));
+  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("pbuf->len = %"U16_F"\n", p->len));
+  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("pbuf->tot_len = %"U16_F"\n", p->tot_len));
+  /* prevent warnings about unused arguments */
+  LWIP_UNUSED_ARG(pcb);
+  LWIP_UNUSED_ARG(addr);
+  LWIP_UNUSED_ARG(port);
+
+  LWIP_ASSERT("reply wasn't freed", dhcp->msg_in == NULL);
+
+  if (p->len < DHCP_MIN_REPLY_LEN) {
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("DHCP reply message or pbuf too short\n"));
+    goto free_pbuf_and_return;
+  }
+
+  if (reply_msg->op != DHCP_BOOTREPLY) {
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("not a DHCP reply message, but type %"U16_F"\n", (u16_t)reply_msg->op));
+    goto free_pbuf_and_return;
+  }
+  /* iterate through hardware address and match against DHCP message */
+  for (i = 0; i < netif->hwaddr_len && i < NETIF_MAX_HWADDR_LEN && i < DHCP_CHADDR_LEN; i++) {
+    if (netif->hwaddr[i] != reply_msg->chaddr[i]) {
+      LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
+        ("netif->hwaddr[%"U16_F"]==%02"X16_F" != reply_msg->chaddr[%"U16_F"]==%02"X16_F"\n",
+        (u16_t)i, (u16_t)netif->hwaddr[i], (u16_t)i, (u16_t)reply_msg->chaddr[i]));
+      goto free_pbuf_and_return;
+    }
+  }
+  /* match transaction ID against what we expected */
+  if (lwip_ntohl(reply_msg->xid) != dhcp->xid) {
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
+      ("transaction id mismatch reply_msg->xid(%"X32_F")!=dhcp->xid(%"X32_F")\n",lwip_ntohl(reply_msg->xid),dhcp->xid));
+    goto free_pbuf_and_return;
+  }
+  /* option fields could be unfold? */
+  if (dhcp_parse_reply(dhcp, p) != ERR_OK) {
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
+      ("problem unfolding DHCP message - too short on memory?\n"));
+    goto free_pbuf_and_return;
+  }
+
+  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("searching DHCP_OPTION_MESSAGE_TYPE\n"));
+  /* obtain pointer to DHCP message type */
+  if (!dhcp_option_given(dhcp, DHCP_OPTION_IDX_MSG_TYPE)) {
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("DHCP_OPTION_MESSAGE_TYPE option not found\n"));
+    goto free_pbuf_and_return;
+  }
+
+  /* read DHCP message type */
+  msg_type = (u8_t)dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_MSG_TYPE);
+  /* message type is DHCP ACK? */
+  if (msg_type == DHCP_ACK) {
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("DHCP_ACK received\n"));
+    /* in requesting state? */
+    if (dhcp->state == DHCP_STATE_REQUESTING) {
+      dhcp_handle_ack(netif);
+#if DHCP_DOES_ARP_CHECK
+      if ((netif->flags & NETIF_FLAG_ETHARP) != 0) {
+        /* check if the acknowledged lease address is already in use */
+        dhcp_check(netif);
+      } else {
+        /* bind interface to the acknowledged lease address */
+        dhcp_bind(netif);
+      }
+#else
+      /* bind interface to the acknowledged lease address */
+      dhcp_bind(netif);
+#endif
+    }
+    /* already bound to the given lease address? */
+    else if ((dhcp->state == DHCP_STATE_REBOOTING) || (dhcp->state == DHCP_STATE_REBINDING) ||
+             (dhcp->state == DHCP_STATE_RENEWING)) {
+      dhcp_handle_ack(netif);
+      dhcp_bind(netif);
+    }
+  }
+  /* received a DHCP_NAK in appropriate state? */
+  else if ((msg_type == DHCP_NAK) &&
+    ((dhcp->state == DHCP_STATE_REBOOTING) || (dhcp->state == DHCP_STATE_REQUESTING) ||
+     (dhcp->state == DHCP_STATE_REBINDING) || (dhcp->state == DHCP_STATE_RENEWING  ))) {
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("DHCP_NAK received\n"));
+    dhcp_handle_nak(netif);
+  }
+  /* received a DHCP_OFFER in DHCP_STATE_SELECTING state? */
+  else if ((msg_type == DHCP_OFFER) && (dhcp->state == DHCP_STATE_SELECTING)) {
+    LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("DHCP_OFFER received in DHCP_STATE_SELECTING state\n"));
+    dhcp->request_timeout = 0;
+    /* remember offered lease */
+    dhcp_handle_offer(netif);
+  }
+
+free_pbuf_and_return:
+  if (dhcp != NULL) {
+    dhcp->msg_in = NULL;
+  }
+  pbuf_free(p);
+}
+
+/**
+ * Create a DHCP request, fill in common headers
+ *
+ * @param netif the netif under DHCP control
+ * @param dhcp dhcp control struct
+ * @param message_type message type of the request
+ */
+static err_t
+dhcp_create_msg(struct netif *netif, struct dhcp *dhcp, u8_t message_type)
+{
+  u16_t i;
+#ifndef DHCP_GLOBAL_XID
+  /** default global transaction identifier starting value (easy to match
+   *  with a packet analyser). We simply increment for each new request.
+   *  Predefine DHCP_GLOBAL_XID to a better value or a function call to generate one
+   *  at runtime, any supporting function prototypes can be defined in DHCP_GLOBAL_XID_HEADER */
+#if DHCP_CREATE_RAND_XID && defined(LWIP_RAND)
+  static u32_t xid;
+#else /* DHCP_CREATE_RAND_XID && defined(LWIP_RAND) */
+  static u32_t xid = 0xABCD0000;
+#endif /* DHCP_CREATE_RAND_XID && defined(LWIP_RAND) */
+#else
+  if (!xid_initialised) {
+    xid = DHCP_GLOBAL_XID;
+    xid_initialised = !xid_initialised;
+  }
+#endif
+  LWIP_ERROR("dhcp_create_msg: netif != NULL", (netif != NULL), return ERR_ARG;);
+  LWIP_ERROR("dhcp_create_msg: dhcp != NULL", (dhcp != NULL), return ERR_VAL;);
+  LWIP_ASSERT("dhcp_create_msg: dhcp->p_out == NULL", dhcp->p_out == NULL);
+  LWIP_ASSERT("dhcp_create_msg: 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 | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
+      ("dhcp_create_msg(): could not allocate pbuf\n"));
+    return ERR_MEM;
+  }
+  LWIP_ASSERT("dhcp_create_msg: check that first pbuf can hold struct dhcp_msg",
+           (dhcp->p_out->len >= sizeof(struct dhcp_msg)));
+
+  /* DHCP_REQUEST should reuse 'xid' from DHCPOFFER */
+  if (message_type != DHCP_REQUEST) {
+    /* reuse transaction identifier in retransmissions */
+    if (dhcp->tries == 0) {
+#if DHCP_CREATE_RAND_XID && defined(LWIP_RAND)
+      xid = LWIP_RAND();
+#else /* DHCP_CREATE_RAND_XID && defined(LWIP_RAND) */
+      xid++;
+#endif /* DHCP_CREATE_RAND_XID && defined(LWIP_RAND) */
+    }
+    dhcp->xid = xid;
+  }
+  LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE,
+              ("transaction id xid(%"X32_F")\n", xid));
+
+  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;
+  dhcp->msg_out->hlen = netif->hwaddr_len;
+  dhcp->msg_out->hops = 0;
+  dhcp->msg_out->xid = lwip_htonl(dhcp->xid);
+  dhcp->msg_out->secs = 0;
+  /* we don't need the broadcast flag since we can receive unicast traffic
+     before being fully configured! */
+  dhcp->msg_out->flags = 0;
+  ip4_addr_set_zero(&dhcp->msg_out->ciaddr);
+  /* set ciaddr to netif->ip_addr based on message_type and state */
+  if ((message_type == DHCP_INFORM) || (message_type == DHCP_DECLINE) || (message_type == DHCP_RELEASE) ||
+      ((message_type == DHCP_REQUEST) && /* DHCP_STATE_BOUND not used for sending! */
+       ((dhcp->state== DHCP_STATE_RENEWING) || dhcp->state== DHCP_STATE_REBINDING))) {
+    ip4_addr_copy(dhcp->msg_out->ciaddr, *netif_ip4_addr(netif));
+  }
+  ip4_addr_set_zero(&dhcp->msg_out->yiaddr);
+  ip4_addr_set_zero(&dhcp->msg_out->siaddr);
+  ip4_addr_set_zero(&dhcp->msg_out->giaddr);
+  for (i = 0; i < DHCP_CHADDR_LEN; i++) {
+    /* copy netif hardware address, pad with zeroes */
+    dhcp->msg_out->chaddr[i] = (i < netif->hwaddr_len && i < NETIF_MAX_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 = PP_HTONL(DHCP_MAGIC_COOKIE);
+  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] = (u8_t)i; /* for debugging only, no matter if truncated */
+  }
+  /* Add option MESSAGE_TYPE */
+  dhcp_option(dhcp, DHCP_OPTION_MESSAGE_TYPE, DHCP_OPTION_MESSAGE_TYPE_LEN);
+  dhcp_option_byte(dhcp, message_type);
+  return ERR_OK;
+}
+
+/**
+ * Free previously allocated memory used to send a DHCP request.
+ *
+ * @param dhcp the dhcp struct to free the request from
+ */
+static void
+dhcp_delete_msg(struct dhcp *dhcp)
+{
+  LWIP_ERROR("dhcp_delete_msg: dhcp != NULL", (dhcp != NULL), return;);
+  LWIP_ASSERT("dhcp_delete_msg: dhcp->p_out != NULL", dhcp->p_out != NULL);
+  LWIP_ASSERT("dhcp_delete_msg: dhcp->msg_out != NULL", dhcp->msg_out != NULL);
+  if (dhcp->p_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.
+ *
+ * @param dhcp DHCP state structure
+ */
+static void
+dhcp_option_trailer(struct dhcp *dhcp)
+{
+  LWIP_ERROR("dhcp_option_trailer: dhcp != NULL", (dhcp != NULL), return;);
+  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)) &&
+         (dhcp->options_out_len < DHCP_OPTIONS_LEN)) {
+    /* add a fill/padding byte */
+    dhcp->msg_out->options[dhcp->options_out_len++] = 0;
+  }
+}
+
+/** check if DHCP supplied netif->ip_addr
+ *
+ * @param netif the netif to check
+ * @return 1 if DHCP supplied netif->ip_addr (states BOUND or RENEWING),
+ *         0 otherwise
+ */
+u8_t
+dhcp_supplied_address(const struct netif *netif)
+{
+  if ((netif != NULL) && (netif_dhcp_data(netif) != NULL)) {
+    struct dhcp* dhcp = netif_dhcp_data(netif);
+    return (dhcp->state == DHCP_STATE_BOUND) || (dhcp->state == DHCP_STATE_RENEWING);
+  }
+  return 0;
+}
+
+#endif /* LWIP_IPV4 && LWIP_DHCP */

+ 1206 - 1399
thirdparty/lwip/src/netif/etharp.c → thirdparty/lwip/src/core/ipv4/etharp.c

@@ -1,1399 +1,1206 @@
-/**
- * @file
- * Address Resolution Protocol module for IP over Ethernet
- *
- * Functionally, ARP is divided into two parts. The first maps an IP address
- * to a physical address when sending a packet, and the second part answers
- * requests from other machines for our physical address.
- *
- * This implementation complies with RFC 826 (Ethernet ARP). It supports
- * Gratuitious ARP from RFC3220 (IP Mobility Support for IPv4) section 4.6
- * if an interface calls etharp_gratuitous(our_netif) upon address change.
- */
-
-/*
- * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
- * Copyright (c) 2003-2004 Leon Woestenberg <leon.woestenberg@axon.tv>
- * Copyright (c) 2003-2004 Axon Digital Design B.V., The Netherlands.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * This file is part of the lwIP TCP/IP stack.
- *
- */
- 
-#include "lwip/opt.h"
-
-#if LWIP_ARP || LWIP_ETHERNET
-
-#include "lwip/ip_addr.h"
-#include "lwip/def.h"
-#include "lwip/ip.h"
-#include "lwip/stats.h"
-#include "lwip/snmp.h"
-#include "lwip/dhcp.h"
-#include "lwip/autoip.h"
-#include "netif/etharp.h"
-
-#if PPPOE_SUPPORT
-#include "netif/ppp_oe.h"
-#endif /* PPPOE_SUPPORT */
-
-#include <string.h>
-
-const struct eth_addr ethbroadcast = {{0xff,0xff,0xff,0xff,0xff,0xff}};
-const struct eth_addr ethzero = {{0,0,0,0,0,0}};
-
-/** The 24-bit IANA multicast OUI is 01-00-5e: */
-#define LL_MULTICAST_ADDR_0 0x01
-#define LL_MULTICAST_ADDR_1 0x00
-#define LL_MULTICAST_ADDR_2 0x5e
-
-#if LWIP_ARP /* don't build if not configured for use in lwipopts.h */
-
-/** the time an ARP entry stays valid after its last update,
- *  for ARP_TMR_INTERVAL = 5000, this is
- *  (240 * 5) seconds = 20 minutes.
- */
-#define ARP_MAXAGE              240
-/** Re-request a used ARP entry 1 minute before it would expire to prevent
- *  breaking a steadily used connection because the ARP entry timed out. */
-#define ARP_AGE_REREQUEST_USED  (ARP_MAXAGE - 12)
-
-/** the time an ARP entry stays pending after first request,
- *  for ARP_TMR_INTERVAL = 5000, this is
- *  (2 * 5) seconds = 10 seconds.
- * 
- *  @internal Keep this number at least 2, otherwise it might
- *  run out instantly if the timeout occurs directly after a request.
- */
-#define ARP_MAXPENDING 2
-
-#define HWTYPE_ETHERNET 1
-
-enum etharp_state {
-  ETHARP_STATE_EMPTY = 0,
-  ETHARP_STATE_PENDING,
-  ETHARP_STATE_STABLE,
-  ETHARP_STATE_STABLE_REREQUESTING
-#if ETHARP_SUPPORT_STATIC_ENTRIES
-  ,ETHARP_STATE_STATIC
-#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
-};
-
-struct etharp_entry {
-#if ARP_QUEUEING
-  /** Pointer to queue of pending outgoing packets on this ARP entry. */
-  struct etharp_q_entry *q;
-#else /* ARP_QUEUEING */
-  /** Pointer to a single pending outgoing packet on this ARP entry. */
-  struct pbuf *q;
-#endif /* ARP_QUEUEING */
-  ip_addr_t ipaddr;
-  struct netif *netif;
-  struct eth_addr ethaddr;
-  u8_t state;
-  u8_t ctime;
-};
-
-static struct etharp_entry arp_table[ARP_TABLE_SIZE];
-
-#if !LWIP_NETIF_HWADDRHINT
-static u8_t etharp_cached_entry;
-#endif /* !LWIP_NETIF_HWADDRHINT */
-
-/** Try hard to create a new entry - we want the IP address to appear in
-    the cache (even if this means removing an active entry or so). */
-#define ETHARP_FLAG_TRY_HARD     1
-#define ETHARP_FLAG_FIND_ONLY    2
-#if ETHARP_SUPPORT_STATIC_ENTRIES
-#define ETHARP_FLAG_STATIC_ENTRY 4
-#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
-
-#if LWIP_NETIF_HWADDRHINT
-#define ETHARP_SET_HINT(netif, hint)  if (((netif) != NULL) && ((netif)->addr_hint != NULL))  \
-                                      *((netif)->addr_hint) = (hint);
-#else /* LWIP_NETIF_HWADDRHINT */
-#define ETHARP_SET_HINT(netif, hint)  (etharp_cached_entry = (hint))
-#endif /* LWIP_NETIF_HWADDRHINT */
-
-
-/* Some checks, instead of etharp_init(): */
-#if (LWIP_ARP && (ARP_TABLE_SIZE > 0x7f))
-  #error "ARP_TABLE_SIZE must fit in an s8_t, you have to reduce it in your lwipopts.h"
-#endif
-
-
-#if ARP_QUEUEING
-/**
- * Free a complete queue of etharp entries
- *
- * @param q a qeueue of etharp_q_entry's to free
- */
-static void
-free_etharp_q(struct etharp_q_entry *q)
-{
-  struct etharp_q_entry *r;
-  LWIP_ASSERT("q != NULL", q != NULL);
-  LWIP_ASSERT("q->p != NULL", q->p != NULL);
-  while (q) {
-    r = q;
-    q = q->next;
-    LWIP_ASSERT("r->p != NULL", (r->p != NULL));
-    pbuf_free(r->p);
-    memp_free(MEMP_ARP_QUEUE, r);
-  }
-}
-#else /* ARP_QUEUEING */
-
-/** Compatibility define: free the queued pbuf */
-#define free_etharp_q(q) pbuf_free(q)
-
-#endif /* ARP_QUEUEING */
-
-/** Clean up ARP table entries */
-static void
-etharp_free_entry(int i)
-{
-  /* remove from SNMP ARP index tree */
-  snmp_delete_arpidx_tree(arp_table[i].netif, &arp_table[i].ipaddr);
-  /* and empty packet queue */
-  if (arp_table[i].q != NULL) {
-    /* remove all queued packets */
-    LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_free_entry: freeing entry %"U16_F", packet queue %p.\n", (u16_t)i, (void *)(arp_table[i].q)));
-    free_etharp_q(arp_table[i].q);
-    arp_table[i].q = NULL;
-  }
-  /* recycle entry for re-use */
-  arp_table[i].state = ETHARP_STATE_EMPTY;
-#ifdef LWIP_DEBUG
-  /* for debugging, clean out the complete entry */
-  arp_table[i].ctime = 0;
-  arp_table[i].netif = NULL;
-  ip_addr_set_zero(&arp_table[i].ipaddr);
-  arp_table[i].ethaddr = ethzero;
-#endif /* LWIP_DEBUG */
-}
-
-/**
- * Clears expired entries in the ARP table.
- *
- * This function should be called every ETHARP_TMR_INTERVAL milliseconds (5 seconds),
- * in order to expire entries in the ARP table.
- */
-void
-etharp_tmr(void)
-{
-  u8_t i;
-
-  LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_timer\n"));
-  /* remove expired entries from the ARP table */
-  for (i = 0; i < ARP_TABLE_SIZE; ++i) {
-    u8_t state = arp_table[i].state;
-    if (state != ETHARP_STATE_EMPTY
-#if ETHARP_SUPPORT_STATIC_ENTRIES
-      && (state != ETHARP_STATE_STATIC)
-#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
-      ) {
-      arp_table[i].ctime++;
-      if ((arp_table[i].ctime >= ARP_MAXAGE) ||
-          ((arp_table[i].state == ETHARP_STATE_PENDING)  &&
-           (arp_table[i].ctime >= ARP_MAXPENDING))) {
-        /* pending or stable entry has become old! */
-        LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_timer: expired %s entry %"U16_F".\n",
-             arp_table[i].state >= ETHARP_STATE_STABLE ? "stable" : "pending", (u16_t)i));
-        /* clean up entries that have just been expired */
-        etharp_free_entry(i);
-      }
-      else if (arp_table[i].state == ETHARP_STATE_STABLE_REREQUESTING) {
-        /* Reset state to stable, so that the next transmitted packet will
-           re-send an ARP request. */
-        arp_table[i].state = ETHARP_STATE_STABLE;
-      }
-#if ARP_QUEUEING
-      /* still pending entry? (not expired) */
-      if (arp_table[i].state == ETHARP_STATE_PENDING) {
-        /* resend an ARP query here? */
-      }
-#endif /* ARP_QUEUEING */
-    }
-  }
-}
-
-/**
- * Search the ARP table for a matching or new entry.
- * 
- * If an IP address is given, return a pending or stable ARP entry that matches
- * the address. If no match is found, create a new entry with this address set,
- * but in state ETHARP_EMPTY. The caller must check and possibly change the
- * state of the returned entry.
- * 
- * If ipaddr is NULL, return a initialized new entry in state ETHARP_EMPTY.
- * 
- * In all cases, attempt to create new entries from an empty entry. If no
- * empty entries are available and ETHARP_FLAG_TRY_HARD flag is set, recycle
- * old entries. Heuristic choose the least important entry for recycling.
- *
- * @param ipaddr IP address to find in ARP cache, or to add if not found.
- * @param flags @see definition of ETHARP_FLAG_*
- * @param netif netif related to this address (used for NETIF_HWADDRHINT)
- *  
- * @return The ARP entry index that matched or is created, ERR_MEM if no
- * entry is found or could be recycled.
- */
-static s8_t
-etharp_find_entry(ip_addr_t *ipaddr, u8_t flags)
-{
-  s8_t old_pending = ARP_TABLE_SIZE, old_stable = ARP_TABLE_SIZE;
-  s8_t empty = ARP_TABLE_SIZE;
-  u8_t i = 0, age_pending = 0, age_stable = 0;
-  /* oldest entry with packets on queue */
-  s8_t old_queue = ARP_TABLE_SIZE;
-  /* its age */
-  u8_t age_queue = 0;
-
-  /**
-   * a) do a search through the cache, remember candidates
-   * b) select candidate entry
-   * c) create new entry
-   */
-
-  /* a) in a single search sweep, do all of this
-   * 1) remember the first empty entry (if any)
-   * 2) remember the oldest stable entry (if any)
-   * 3) remember the oldest pending entry without queued packets (if any)
-   * 4) remember the oldest pending entry with queued packets (if any)
-   * 5) search for a matching IP entry, either pending or stable
-   *    until 5 matches, or all entries are searched for.
-   */
-
-  for (i = 0; i < ARP_TABLE_SIZE; ++i) {
-    u8_t state = arp_table[i].state;
-    /* no empty entry found yet and now we do find one? */
-    if ((empty == ARP_TABLE_SIZE) && (state == ETHARP_STATE_EMPTY)) {
-      LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_find_entry: found empty entry %"U16_F"\n", (u16_t)i));
-      /* remember first empty entry */
-      empty = i;
-    } else if (state != ETHARP_STATE_EMPTY) {
-      LWIP_ASSERT("state == ETHARP_STATE_PENDING || state >= ETHARP_STATE_STABLE",
-        state == ETHARP_STATE_PENDING || state >= ETHARP_STATE_STABLE);
-      /* if given, does IP address match IP address in ARP entry? */
-      if (ipaddr && ip_addr_cmp(ipaddr, &arp_table[i].ipaddr)) {
-        LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: found matching entry %"U16_F"\n", (u16_t)i));
-        /* found exact IP address match, simply bail out */
-        return i;
-      }
-      /* pending entry? */
-      if (state == ETHARP_STATE_PENDING) {
-        /* pending with queued packets? */
-        if (arp_table[i].q != NULL) {
-          if (arp_table[i].ctime >= age_queue) {
-            old_queue = i;
-            age_queue = arp_table[i].ctime;
-          }
-        } else
-        /* pending without queued packets? */
-        {
-          if (arp_table[i].ctime >= age_pending) {
-            old_pending = i;
-            age_pending = arp_table[i].ctime;
-          }
-        }
-      /* stable entry? */
-      } else if (state >= ETHARP_STATE_STABLE) {
-#if ETHARP_SUPPORT_STATIC_ENTRIES
-        /* don't record old_stable for static entries since they never expire */
-        if (state < ETHARP_STATE_STATIC)
-#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
-        {
-          /* remember entry with oldest stable entry in oldest, its age in maxtime */
-          if (arp_table[i].ctime >= age_stable) {
-            old_stable = i;
-            age_stable = arp_table[i].ctime;
-          }
-        }
-      }
-    }
-  }
-  /* { we have no match } => try to create a new entry */
-   
-  /* don't create new entry, only search? */
-  if (((flags & ETHARP_FLAG_FIND_ONLY) != 0) ||
-      /* or no empty entry found and not allowed to recycle? */
-      ((empty == ARP_TABLE_SIZE) && ((flags & ETHARP_FLAG_TRY_HARD) == 0))) {
-    LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: no empty entry found and not allowed to recycle\n"));
-    return (s8_t)ERR_MEM;
-  }
-  
-  /* b) choose the least destructive entry to recycle:
-   * 1) empty entry
-   * 2) oldest stable entry
-   * 3) oldest pending entry without queued packets
-   * 4) oldest pending entry with queued packets
-   * 
-   * { ETHARP_FLAG_TRY_HARD is set at this point }
-   */ 
-
-  /* 1) empty entry available? */
-  if (empty < ARP_TABLE_SIZE) {
-    i = empty;
-    LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: selecting empty entry %"U16_F"\n", (u16_t)i));
-  } else {
-    /* 2) found recyclable stable entry? */
-    if (old_stable < ARP_TABLE_SIZE) {
-      /* recycle oldest stable*/
-      i = old_stable;
-      LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: selecting oldest stable entry %"U16_F"\n", (u16_t)i));
-      /* no queued packets should exist on stable entries */
-      LWIP_ASSERT("arp_table[i].q == NULL", arp_table[i].q == NULL);
-    /* 3) found recyclable pending entry without queued packets? */
-    } else if (old_pending < ARP_TABLE_SIZE) {
-      /* recycle oldest pending */
-      i = old_pending;
-      LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: selecting oldest pending entry %"U16_F" (without queue)\n", (u16_t)i));
-    /* 4) found recyclable pending entry with queued packets? */
-    } else if (old_queue < ARP_TABLE_SIZE) {
-      /* recycle oldest pending (queued packets are free in etharp_free_entry) */
-      i = old_queue;
-      LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: selecting oldest pending entry %"U16_F", freeing packet queue %p\n", (u16_t)i, (void *)(arp_table[i].q)));
-      /* no empty or recyclable entries found */
-    } else {
-      LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: no empty or recyclable entries found\n"));
-      return (s8_t)ERR_MEM;
-    }
-
-    /* { empty or recyclable entry found } */
-    LWIP_ASSERT("i < ARP_TABLE_SIZE", i < ARP_TABLE_SIZE);
-    etharp_free_entry(i);
-  }
-
-  LWIP_ASSERT("i < ARP_TABLE_SIZE", i < ARP_TABLE_SIZE);
-  LWIP_ASSERT("arp_table[i].state == ETHARP_STATE_EMPTY",
-    arp_table[i].state == ETHARP_STATE_EMPTY);
-
-  /* IP address given? */
-  if (ipaddr != NULL) {
-    /* set IP address */
-    ip_addr_copy(arp_table[i].ipaddr, *ipaddr);
-  }
-  arp_table[i].ctime = 0;
-  return (err_t)i;
-}
-
-/**
- * Send an IP packet on the network using netif->linkoutput
- * The ethernet header is filled in before sending.
- *
- * @params netif the lwIP network interface on which to send the packet
- * @params p the packet to send, p->payload pointing to the (uninitialized) ethernet header
- * @params src the source MAC address to be copied into the ethernet header
- * @params dst the destination MAC address to be copied into the ethernet header
- * @return ERR_OK if the packet was sent, any other err_t on failure
- */
-static err_t
-etharp_send_ip(struct netif *netif, struct pbuf *p, struct eth_addr *src, struct eth_addr *dst)
-{
-  struct eth_hdr *ethhdr = (struct eth_hdr *)p->payload;
-
-  LWIP_ASSERT("netif->hwaddr_len must be the same as ETHARP_HWADDR_LEN for etharp!",
-              (netif->hwaddr_len == ETHARP_HWADDR_LEN));
-  ETHADDR32_COPY(&ethhdr->dest, dst);
-  ETHADDR16_COPY(&ethhdr->src, src);
-  ethhdr->type = PP_HTONS(ETHTYPE_IP);
-  LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_send_ip: sending packet %p\n", (void *)p));
-  /* send the packet */
-  return netif->linkoutput(netif, p);
-}
-
-/**
- * Update (or insert) a IP/MAC address pair in the ARP cache.
- *
- * If a pending entry is resolved, any queued packets will be sent
- * at this point.
- * 
- * @param netif netif related to this entry (used for NETIF_ADDRHINT)
- * @param ipaddr IP address of the inserted ARP entry.
- * @param ethaddr Ethernet address of the inserted ARP entry.
- * @param flags @see definition of ETHARP_FLAG_*
- *
- * @return
- * - ERR_OK Succesfully updated ARP cache.
- * - ERR_MEM If we could not add a new ARP entry when ETHARP_FLAG_TRY_HARD was set.
- * - ERR_ARG Non-unicast address given, those will not appear in ARP cache.
- *
- * @see pbuf_free()
- */
-static err_t
-etharp_update_arp_entry(struct netif *netif, ip_addr_t *ipaddr, struct eth_addr *ethaddr, u8_t flags)
-{
-  s8_t i;
-  LWIP_ASSERT("netif->hwaddr_len == ETHARP_HWADDR_LEN", netif->hwaddr_len == ETHARP_HWADDR_LEN);
-  LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_update_arp_entry: %"U16_F".%"U16_F".%"U16_F".%"U16_F" - %02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F"\n",
-    ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr),
-    ethaddr->addr[0], ethaddr->addr[1], ethaddr->addr[2],
-    ethaddr->addr[3], ethaddr->addr[4], ethaddr->addr[5]));
-  /* non-unicast address? */
-  if (ip_addr_isany(ipaddr) ||
-      ip_addr_isbroadcast(ipaddr, netif) ||
-      ip_addr_ismulticast(ipaddr)) {
-    LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_update_arp_entry: will not add non-unicast IP address to ARP cache\n"));
-    return ERR_ARG;
-  }
-  /* find or create ARP entry */
-  i = etharp_find_entry(ipaddr, flags);
-  /* bail out if no entry could be found */
-  if (i < 0) {
-    return (err_t)i;
-  }
-
-#if ETHARP_SUPPORT_STATIC_ENTRIES
-  if (flags & ETHARP_FLAG_STATIC_ENTRY) {
-    /* record static type */
-    arp_table[i].state = ETHARP_STATE_STATIC;
-  } else
-#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
-  {
-    /* mark it stable */
-    arp_table[i].state = ETHARP_STATE_STABLE;
-  }
-
-  /* record network interface */
-  arp_table[i].netif = netif;
-  /* insert in SNMP ARP index tree */
-  snmp_insert_arpidx_tree(netif, &arp_table[i].ipaddr);
-
-  LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_update_arp_entry: updating stable entry %"S16_F"\n", (s16_t)i));
-  /* update address */
-  ETHADDR32_COPY(&arp_table[i].ethaddr, ethaddr);
-  /* reset time stamp */
-  arp_table[i].ctime = 0;
-  /* this is where we will send out queued packets! */
-#if ARP_QUEUEING
-  while (arp_table[i].q != NULL) {
-    struct pbuf *p;
-    /* remember remainder of queue */
-    struct etharp_q_entry *q = arp_table[i].q;
-    /* pop first item off the queue */
-    arp_table[i].q = q->next;
-    /* get the packet pointer */
-    p = q->p;
-    /* now queue entry can be freed */
-    memp_free(MEMP_ARP_QUEUE, q);
-#else /* ARP_QUEUEING */
-  if (arp_table[i].q != NULL) {
-    struct pbuf *p = arp_table[i].q;
-    arp_table[i].q = NULL;
-#endif /* ARP_QUEUEING */
-    /* send the queued IP packet */
-    etharp_send_ip(netif, p, (struct eth_addr*)(netif->hwaddr), ethaddr);
-    /* free the queued IP packet */
-    pbuf_free(p);
-  }
-  return ERR_OK;
-}
-
-#if ETHARP_SUPPORT_STATIC_ENTRIES
-/** Add a new static entry to the ARP table. If an entry exists for the
- * specified IP address, this entry is overwritten.
- * If packets are queued for the specified IP address, they are sent out.
- *
- * @param ipaddr IP address for the new static entry
- * @param ethaddr ethernet address for the new static entry
- * @return @see return values of etharp_add_static_entry
- */
-err_t
-etharp_add_static_entry(ip_addr_t *ipaddr, struct eth_addr *ethaddr)
-{
-  struct netif *netif;
-  LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_add_static_entry: %"U16_F".%"U16_F".%"U16_F".%"U16_F" - %02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F"\n",
-    ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr),
-    ethaddr->addr[0], ethaddr->addr[1], ethaddr->addr[2],
-    ethaddr->addr[3], ethaddr->addr[4], ethaddr->addr[5]));
-
-  netif = ip_route(ipaddr);
-  if (netif == NULL) {
-    return ERR_RTE;
-  }
-
-  return etharp_update_arp_entry(netif, ipaddr, ethaddr, ETHARP_FLAG_TRY_HARD | ETHARP_FLAG_STATIC_ENTRY);
-}
-
-/** Remove a static entry from the ARP table previously added with a call to
- * etharp_add_static_entry.
- *
- * @param ipaddr IP address of the static entry to remove
- * @return ERR_OK: entry removed
- *         ERR_MEM: entry wasn't found
- *         ERR_ARG: entry wasn't a static entry but a dynamic one
- */
-err_t
-etharp_remove_static_entry(ip_addr_t *ipaddr)
-{
-  s8_t i;
-  LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_remove_static_entry: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
-    ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr)));
-
-  /* find or create ARP entry */
-  i = etharp_find_entry(ipaddr, ETHARP_FLAG_FIND_ONLY);
-  /* bail out if no entry could be found */
-  if (i < 0) {
-    return (err_t)i;
-  }
-
-  if (arp_table[i].state != ETHARP_STATE_STATIC) {
-    /* entry wasn't a static entry, cannot remove it */
-    return ERR_ARG;
-  }
-  /* entry found, free it */
-  etharp_free_entry(i);
-  return ERR_OK;
-}
-#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
-
-/**
- * Remove all ARP table entries of the specified netif.
- *
- * @param netif points to a network interface
- */
-void etharp_cleanup_netif(struct netif *netif)
-{
-  u8_t i;
-
-  for (i = 0; i < ARP_TABLE_SIZE; ++i) {
-    u8_t state = arp_table[i].state;
-    if ((state != ETHARP_STATE_EMPTY) && (arp_table[i].netif == netif)) {
-      etharp_free_entry(i);
-    }
-  }
-}
-
-/**
- * Finds (stable) ethernet/IP address pair from ARP table
- * using interface and IP address index.
- * @note the addresses in the ARP table are in network order!
- *
- * @param netif points to interface index
- * @param ipaddr points to the (network order) IP address index
- * @param eth_ret points to return pointer
- * @param ip_ret points to return pointer
- * @return table index if found, -1 otherwise
- */
-s8_t
-etharp_find_addr(struct netif *netif, ip_addr_t *ipaddr,
-         struct eth_addr **eth_ret, ip_addr_t **ip_ret)
-{
-  s8_t i;
-
-  LWIP_ASSERT("eth_ret != NULL && ip_ret != NULL",
-    eth_ret != NULL && ip_ret != NULL);
-
-  LWIP_UNUSED_ARG(netif);
-
-  i = etharp_find_entry(ipaddr, ETHARP_FLAG_FIND_ONLY);
-  if((i >= 0) && (arp_table[i].state >= ETHARP_STATE_STABLE)) {
-      *eth_ret = &arp_table[i].ethaddr;
-      *ip_ret = &arp_table[i].ipaddr;
-      return i;
-  }
-  return -1;
-}
-
-#if ETHARP_TRUST_IP_MAC
-/**
- * Updates the ARP table using the given IP packet.
- *
- * Uses the incoming IP packet's source address to update the
- * ARP cache for the local network. The function does not alter
- * or free the packet. This function must be called before the
- * packet p is passed to the IP layer.
- *
- * @param netif The lwIP network interface on which the IP packet pbuf arrived.
- * @param p The IP packet that arrived on netif.
- *
- * @return NULL
- *
- * @see pbuf_free()
- */
-static void
-etharp_ip_input(struct netif *netif, struct pbuf *p)
-{
-  struct eth_hdr *ethhdr;
-  struct ip_hdr *iphdr;
-  ip_addr_t iphdr_src;
-  LWIP_ERROR("netif != NULL", (netif != NULL), return;);
-
-  /* Only insert an entry if the source IP address of the
-     incoming IP packet comes from a host on the local network. */
-  ethhdr = (struct eth_hdr *)p->payload;
-  iphdr = (struct ip_hdr *)((u8_t*)ethhdr + SIZEOF_ETH_HDR);
-#if ETHARP_SUPPORT_VLAN
-  if (ethhdr->type == PP_HTONS(ETHTYPE_VLAN)) {
-    iphdr = (struct ip_hdr *)((u8_t*)ethhdr + SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR);
-  }
-#endif /* ETHARP_SUPPORT_VLAN */
-
-  ip_addr_copy(iphdr_src, iphdr->src);
-
-  /* source is not on the local network? */
-  if (!ip_addr_netcmp(&iphdr_src, &(netif->ip_addr), &(netif->netmask))) {
-    /* do nothing */
-    return;
-  }
-
-  LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_ip_input: updating ETHARP table.\n"));
-  /* update the source IP address in the cache, if present */
-  /* @todo We could use ETHARP_FLAG_TRY_HARD if we think we are going to talk
-   * back soon (for example, if the destination IP address is ours. */
-  etharp_update_arp_entry(netif, &iphdr_src, &(ethhdr->src), ETHARP_FLAG_FIND_ONLY);
-}
-#endif /* ETHARP_TRUST_IP_MAC */
-
-/**
- * Responds to ARP requests to us. Upon ARP replies to us, add entry to cache  
- * send out queued IP packets. Updates cache with snooped address pairs.
- *
- * Should be called for incoming ARP packets. The pbuf in the argument
- * is freed by this function.
- *
- * @param netif The lwIP network interface on which the ARP packet pbuf arrived.
- * @param ethaddr Ethernet address of netif.
- * @param p The ARP packet that arrived on netif. Is freed by this function.
- *
- * @return NULL
- *
- * @see pbuf_free()
- */
-static void
-etharp_arp_input(struct netif *netif, struct eth_addr *ethaddr, struct pbuf *p)
-{
-  struct etharp_hdr *hdr;
-  struct eth_hdr *ethhdr;
-  /* these are aligned properly, whereas the ARP header fields might not be */
-  ip_addr_t sipaddr, dipaddr;
-  u8_t for_us;
-#if LWIP_AUTOIP
-  const u8_t * ethdst_hwaddr;
-#endif /* LWIP_AUTOIP */
-
-  LWIP_ERROR("netif != NULL", (netif != NULL), return;);
-
-  /* drop short ARP packets: we have to check for p->len instead of p->tot_len here
-     since a struct etharp_hdr is pointed to p->payload, so it musn't be chained! */
-  if (p->len < SIZEOF_ETHARP_PACKET) {
-    LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
-      ("etharp_arp_input: packet dropped, too short (%"S16_F"/%"S16_F")\n", p->tot_len,
-      (s16_t)SIZEOF_ETHARP_PACKET));
-    ETHARP_STATS_INC(etharp.lenerr);
-    ETHARP_STATS_INC(etharp.drop);
-    pbuf_free(p);
-    return;
-  }
-
-  ethhdr = (struct eth_hdr *)p->payload;
-  hdr = (struct etharp_hdr *)((u8_t*)ethhdr + SIZEOF_ETH_HDR);
-#if ETHARP_SUPPORT_VLAN
-  if (ethhdr->type == PP_HTONS(ETHTYPE_VLAN)) {
-    hdr = (struct etharp_hdr *)(((u8_t*)ethhdr) + SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR);
-  }
-#endif /* ETHARP_SUPPORT_VLAN */
-
-  /* RFC 826 "Packet Reception": */
-  if ((hdr->hwtype != PP_HTONS(HWTYPE_ETHERNET)) ||
-      (hdr->hwlen != ETHARP_HWADDR_LEN) ||
-      (hdr->protolen != sizeof(ip_addr_t)) ||
-      (hdr->proto != PP_HTONS(ETHTYPE_IP)))  {
-    LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
-      ("etharp_arp_input: packet dropped, wrong hw type, hwlen, proto, protolen or ethernet type (%"U16_F"/%"U16_F"/%"U16_F"/%"U16_F")\n",
-      hdr->hwtype, hdr->hwlen, hdr->proto, hdr->protolen));
-    ETHARP_STATS_INC(etharp.proterr);
-    ETHARP_STATS_INC(etharp.drop);
-    pbuf_free(p);
-    return;
-  }
-  ETHARP_STATS_INC(etharp.recv);
-
-#if LWIP_AUTOIP
-  /* We have to check if a host already has configured our random
-   * created link local address and continously check if there is
-   * a host with this IP-address so we can detect collisions */
-  autoip_arp_reply(netif, hdr);
-#endif /* LWIP_AUTOIP */
-
-  /* Copy struct ip_addr2 to aligned ip_addr, to support compilers without
-   * structure packing (not using structure copy which breaks strict-aliasing rules). */
-  IPADDR2_COPY(&sipaddr, &hdr->sipaddr);
-  IPADDR2_COPY(&dipaddr, &hdr->dipaddr);
-
-  /* this interface is not configured? */
-  if (ip_addr_isany(&netif->ip_addr)) {
-    for_us = 0;
-  } else {
-    /* ARP packet directed to us? */
-    for_us = (u8_t)ip_addr_cmp(&dipaddr, &(netif->ip_addr));
-  }
-
-  /* ARP message directed to us?
-      -> add IP address in ARP cache; assume requester wants to talk to us,
-         can result in directly sending the queued packets for this host.
-     ARP message not directed to us?
-      ->  update the source IP address in the cache, if present */
-  etharp_update_arp_entry(netif, &sipaddr, &(hdr->shwaddr),
-                   for_us ? ETHARP_FLAG_TRY_HARD : ETHARP_FLAG_FIND_ONLY);
-
-  /* now act on the message itself */
-  switch (hdr->opcode) {
-  /* ARP request? */
-  case PP_HTONS(ARP_REQUEST):
-    /* ARP request. If it asked for our address, we send out a
-     * reply. In any case, we time-stamp any existing ARP entry,
-     * and possiby send out an IP packet that was queued on it. */
-
-    LWIP_DEBUGF (ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: incoming ARP request\n"));
-    /* ARP request for our address? */
-    if (for_us) {
-
-      LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: replying to ARP request for our IP address\n"));
-      /* Re-use pbuf to send ARP reply.
-         Since we are re-using an existing pbuf, we can't call etharp_raw since
-         that would allocate a new pbuf. */
-      hdr->opcode = htons(ARP_REPLY);
-
-      IPADDR2_COPY(&hdr->dipaddr, &hdr->sipaddr);
-      IPADDR2_COPY(&hdr->sipaddr, &netif->ip_addr);
-
-      LWIP_ASSERT("netif->hwaddr_len must be the same as ETHARP_HWADDR_LEN for etharp!",
-                  (netif->hwaddr_len == ETHARP_HWADDR_LEN));
-#if LWIP_AUTOIP
-      /* If we are using Link-Local, all ARP packets that contain a Link-Local
-       * 'sender IP address' MUST be sent using link-layer broadcast instead of
-       * link-layer unicast. (See RFC3927 Section 2.5, last paragraph) */
-      ethdst_hwaddr = ip_addr_islinklocal(&netif->ip_addr) ? (u8_t*)(ethbroadcast.addr) : hdr->shwaddr.addr;
-#endif /* LWIP_AUTOIP */
-
-      ETHADDR16_COPY(&hdr->dhwaddr, &hdr->shwaddr);
-#if LWIP_AUTOIP
-      ETHADDR16_COPY(&ethhdr->dest, ethdst_hwaddr);
-#else  /* LWIP_AUTOIP */
-      ETHADDR16_COPY(&ethhdr->dest, &hdr->shwaddr);
-#endif /* LWIP_AUTOIP */
-      ETHADDR16_COPY(&hdr->shwaddr, ethaddr);
-      ETHADDR16_COPY(&ethhdr->src, ethaddr);
-
-      /* hwtype, hwaddr_len, proto, protolen and the type in the ethernet header
-         are already correct, we tested that before */
-
-      /* return ARP reply */
-      netif->linkoutput(netif, p);
-    /* we are not configured? */
-    } else if (ip_addr_isany(&netif->ip_addr)) {
-      /* { for_us == 0 and netif->ip_addr.addr == 0 } */
-      LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: we are unconfigured, ARP request ignored.\n"));
-    /* request was not directed to us */
-    } else {
-      /* { for_us == 0 and netif->ip_addr.addr != 0 } */
-      LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: ARP request was not for us.\n"));
-    }
-    break;
-  case PP_HTONS(ARP_REPLY):
-    /* ARP reply. We already updated the ARP cache earlier. */
-    LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: incoming ARP reply\n"));
-#if (LWIP_DHCP && DHCP_DOES_ARP_CHECK)
-    /* DHCP wants to know about ARP replies from any host with an
-     * IP address also offered to us by the DHCP server. We do not
-     * want to take a duplicate IP address on a single network.
-     * @todo How should we handle redundant (fail-over) interfaces? */
-    dhcp_arp_reply(netif, &sipaddr);
-#endif /* (LWIP_DHCP && DHCP_DOES_ARP_CHECK) */
-    break;
-  default:
-    LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: ARP unknown opcode type %"S16_F"\n", htons(hdr->opcode)));
-    ETHARP_STATS_INC(etharp.err);
-    break;
-  }
-  /* free ARP packet */
-  pbuf_free(p);
-}
-
-/** Just a small helper function that sends a pbuf to an ethernet address
- * in the arp_table specified by the index 'arp_idx'.
- */
-static err_t
-etharp_output_to_arp_index(struct netif *netif, struct pbuf *q, u8_t arp_idx)
-{
-  LWIP_ASSERT("arp_table[arp_idx].state >= ETHARP_STATE_STABLE",
-              arp_table[arp_idx].state >= ETHARP_STATE_STABLE);
-  /* if arp table entry is about to expire: re-request it,
-     but only if its state is ETHARP_STATE_STABLE to prevent flooding the
-     network with ARP requests if this address is used frequently. */
-  if ((arp_table[arp_idx].state == ETHARP_STATE_STABLE) && 
-      (arp_table[arp_idx].ctime >= ARP_AGE_REREQUEST_USED)) {
-    if (etharp_request(netif, &arp_table[arp_idx].ipaddr) == ERR_OK) {
-      arp_table[arp_idx].state = ETHARP_STATE_STABLE_REREQUESTING;
-    }
-  }
-  
-  return etharp_send_ip(netif, q, (struct eth_addr*)(netif->hwaddr),
-    &arp_table[arp_idx].ethaddr);
-}
-
-/**
- * Resolve and fill-in Ethernet address header for outgoing IP packet.
- *
- * For IP multicast and broadcast, corresponding Ethernet addresses
- * are selected and the packet is transmitted on the link.
- *
- * For unicast addresses, the packet is submitted to etharp_query(). In
- * case the IP address is outside the local network, the IP address of
- * the gateway is used.
- *
- * @param netif The lwIP network interface which the IP packet will be sent on.
- * @param q The pbuf(s) containing the IP packet to be sent.
- * @param ipaddr The IP address of the packet destination.
- *
- * @return
- * - ERR_RTE No route to destination (no gateway to external networks),
- * or the return type of either etharp_query() or etharp_send_ip().
- */
-err_t
-etharp_output(struct netif *netif, struct pbuf *q, ip_addr_t *ipaddr)
-{
-  struct eth_addr *dest;
-  struct eth_addr mcastaddr;
-  ip_addr_t *dst_addr = ipaddr;
-
-  LWIP_ASSERT("netif != NULL", netif != NULL);
-  LWIP_ASSERT("q != NULL", q != NULL);
-  LWIP_ASSERT("ipaddr != NULL", ipaddr != NULL);
-
-  /* make room for Ethernet header - should not fail */
-  if (pbuf_header(q, sizeof(struct eth_hdr)) != 0) {
-    /* bail out */
-    LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
-      ("etharp_output: could not allocate room for header.\n"));
-    LINK_STATS_INC(link.lenerr);
-    return ERR_BUF;
-  }
-
-  /* Determine on destination hardware address. Broadcasts and multicasts
-   * are special, other IP addresses are looked up in the ARP table. */
-
-  /* broadcast destination IP address? */
-  if (ip_addr_isbroadcast(ipaddr, netif)) {
-    /* broadcast on Ethernet also */
-    dest = (struct eth_addr *)&ethbroadcast;
-  /* multicast destination IP address? */
-  } else if (ip_addr_ismulticast(ipaddr)) {
-    /* Hash IP multicast address to MAC address.*/
-    mcastaddr.addr[0] = LL_MULTICAST_ADDR_0;
-    mcastaddr.addr[1] = LL_MULTICAST_ADDR_1;
-    mcastaddr.addr[2] = LL_MULTICAST_ADDR_2;
-    mcastaddr.addr[3] = ip4_addr2(ipaddr) & 0x7f;
-    mcastaddr.addr[4] = ip4_addr3(ipaddr);
-    mcastaddr.addr[5] = ip4_addr4(ipaddr);
-    /* destination Ethernet address is multicast */
-    dest = &mcastaddr;
-  /* unicast destination IP address? */
-  } else {
-    s8_t i;
-    /* outside local network? if so, this can neither be a global broadcast nor
-       a subnet broadcast. */
-    if (!ip_addr_netcmp(ipaddr, &(netif->ip_addr), &(netif->netmask)) &&
-        !ip_addr_islinklocal(ipaddr)) {
-#if LWIP_AUTOIP
-      struct ip_hdr *iphdr = (struct ip_hdr*)((u8_t*)q->payload +
-        sizeof(struct eth_hdr));
-      /* According to RFC 3297, chapter 2.6.2 (Forwarding Rules), a packet with
-         a link-local source address must always be "directly to its destination
-         on the same physical link. The host MUST NOT send the packet to any
-         router for forwarding". */
-      if (!ip_addr_islinklocal(&iphdr->src))
-#endif /* LWIP_AUTOIP */
-      {
-        /* interface has default gateway? */
-        if (!ip_addr_isany(&netif->gw)) {
-          /* send to hardware address of default gateway IP address */
-          dst_addr = &(netif->gw);
-        /* no default gateway available */
-        } else {
-          /* no route to destination error (default gateway missing) */
-          return ERR_RTE;
-        }
-      }
-    }
-#if LWIP_NETIF_HWADDRHINT
-    if (netif->addr_hint != NULL) {
-      /* per-pcb cached entry was given */
-      u8_t etharp_cached_entry = *(netif->addr_hint);
-      if (etharp_cached_entry < ARP_TABLE_SIZE) {
-#endif /* LWIP_NETIF_HWADDRHINT */
-        if ((arp_table[etharp_cached_entry].state >= ETHARP_STATE_STABLE) &&
-            (ip_addr_cmp(dst_addr, &arp_table[etharp_cached_entry].ipaddr))) {
-          /* the per-pcb-cached entry is stable and the right one! */
-          ETHARP_STATS_INC(etharp.cachehit);
-          return etharp_output_to_arp_index(netif, q, etharp_cached_entry);
-        }
-#if LWIP_NETIF_HWADDRHINT
-      }
-    }
-#endif /* LWIP_NETIF_HWADDRHINT */
-
-    /* find stable entry: do this here since this is a critical path for
-       throughput and etharp_find_entry() is kind of slow */
-    for (i = 0; i < ARP_TABLE_SIZE; i++) {
-      if ((arp_table[i].state >= ETHARP_STATE_STABLE) &&
-          (ip_addr_cmp(dst_addr, &arp_table[i].ipaddr))) {
-        /* found an existing, stable entry */
-        ETHARP_SET_HINT(netif, i);
-        return etharp_output_to_arp_index(netif, q, i);
-      }
-    }
-    /* no stable entry found, use the (slower) query function:
-       queue on destination Ethernet address belonging to ipaddr */
-    return etharp_query(netif, dst_addr, q);
-  }
-
-  /* continuation for multicast/broadcast destinations */
-  /* obtain source Ethernet address of the given interface */
-  /* send packet directly on the link */
-  return etharp_send_ip(netif, q, (struct eth_addr*)(netif->hwaddr), dest);
-}
-
-/**
- * Send an ARP request for the given IP address and/or queue a packet.
- *
- * If the IP address was not yet in the cache, a pending ARP cache entry
- * is added and an ARP request is sent for the given address. The packet
- * is queued on this entry.
- *
- * If the IP address was already pending in the cache, a new ARP request
- * is sent for the given address. The packet is queued on this entry.
- *
- * If the IP address was already stable in the cache, and a packet is
- * given, it is directly sent and no ARP request is sent out. 
- * 
- * If the IP address was already stable in the cache, and no packet is
- * given, an ARP request is sent out.
- * 
- * @param netif The lwIP network interface on which ipaddr
- * must be queried for.
- * @param ipaddr The IP address to be resolved.
- * @param q If non-NULL, a pbuf that must be delivered to the IP address.
- * q is not freed by this function.
- *
- * @note q must only be ONE packet, not a packet queue!
- *
- * @return
- * - ERR_BUF Could not make room for Ethernet header.
- * - ERR_MEM Hardware address unknown, and no more ARP entries available
- *   to query for address or queue the packet.
- * - ERR_MEM Could not queue packet due to memory shortage.
- * - ERR_RTE No route to destination (no gateway to external networks).
- * - ERR_ARG Non-unicast address given, those will not appear in ARP cache.
- *
- */
-err_t
-etharp_query(struct netif *netif, ip_addr_t *ipaddr, struct pbuf *q)
-{
-  struct eth_addr * srcaddr = (struct eth_addr *)netif->hwaddr;
-  err_t result = ERR_MEM;
-  s8_t i; /* ARP entry index */
-
-  /* non-unicast address? */
-  if (ip_addr_isbroadcast(ipaddr, netif) ||
-      ip_addr_ismulticast(ipaddr) ||
-      ip_addr_isany(ipaddr)) {
-    LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: will not add non-unicast IP address to ARP cache\n"));
-    return ERR_ARG;
-  }
-
-  /* find entry in ARP cache, ask to create entry if queueing packet */
-  i = etharp_find_entry(ipaddr, ETHARP_FLAG_TRY_HARD);
-
-  /* could not find or create entry? */
-  if (i < 0) {
-    LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: could not create ARP entry\n"));
-    if (q) {
-      LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: packet dropped\n"));
-      ETHARP_STATS_INC(etharp.memerr);
-    }
-    return (err_t)i;
-  }
-
-  /* mark a fresh entry as pending (we just sent a request) */
-  if (arp_table[i].state == ETHARP_STATE_EMPTY) {
-    arp_table[i].state = ETHARP_STATE_PENDING;
-  }
-
-  /* { i is either a STABLE or (new or existing) PENDING entry } */
-  LWIP_ASSERT("arp_table[i].state == PENDING or STABLE",
-  ((arp_table[i].state == ETHARP_STATE_PENDING) ||
-   (arp_table[i].state >= ETHARP_STATE_STABLE)));
-
-  /* do we have a pending entry? or an implicit query request? */
-  if ((arp_table[i].state == ETHARP_STATE_PENDING) || (q == NULL)) {
-    /* try to resolve it; send out ARP request */
-    result = etharp_request(netif, ipaddr);
-    if (result != ERR_OK) {
-      /* ARP request couldn't be sent */
-      /* We don't re-send arp request in etharp_tmr, but we still queue packets,
-         since this failure could be temporary, and the next packet calling
-         etharp_query again could lead to sending the queued packets. */
-    }
-    if (q == NULL) {
-      return result;
-    }
-  }
-
-  /* packet given? */
-  LWIP_ASSERT("q != NULL", q != NULL);
-  /* stable entry? */
-  if (arp_table[i].state >= ETHARP_STATE_STABLE) {
-    /* we have a valid IP->Ethernet address mapping */
-    ETHARP_SET_HINT(netif, i);
-    /* send the packet */
-    result = etharp_send_ip(netif, q, srcaddr, &(arp_table[i].ethaddr));
-  /* pending entry? (either just created or already pending */
-  } else if (arp_table[i].state == ETHARP_STATE_PENDING) {
-    /* entry is still pending, queue the given packet 'q' */
-    struct pbuf *p;
-    int copy_needed = 0;
-    /* IF q includes a PBUF_REF, PBUF_POOL or PBUF_RAM, we have no choice but
-     * to copy the whole queue into a new PBUF_RAM (see bug #11400) 
-     * PBUF_ROMs can be left as they are, since ROM must not get changed. */
-    p = q;
-    while (p) {
-      LWIP_ASSERT("no packet queues allowed!", (p->len != p->tot_len) || (p->next == 0));
-      if(p->type != PBUF_ROM) {
-        copy_needed = 1;
-        break;
-      }
-      p = p->next;
-    }
-    if(copy_needed) {
-      /* copy the whole packet into new pbufs */
-      p = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM);
-      if(p != NULL) {
-        if (pbuf_copy(p, q) != ERR_OK) {
-          pbuf_free(p);
-          p = NULL;
-        }
-      }
-    } else {
-      /* referencing the old pbuf is enough */
-      p = q;
-      pbuf_ref(p);
-    }
-    /* packet could be taken over? */
-    if (p != NULL) {
-      /* queue packet ... */
-#if ARP_QUEUEING
-      struct etharp_q_entry *new_entry;
-      /* allocate a new arp queue entry */
-      new_entry = (struct etharp_q_entry *)memp_malloc(MEMP_ARP_QUEUE);
-      if (new_entry != NULL) {
-        new_entry->next = 0;
-        new_entry->p = p;
-        if(arp_table[i].q != NULL) {
-          /* queue was already existent, append the new entry to the end */
-          struct etharp_q_entry *r;
-          r = arp_table[i].q;
-          while (r->next != NULL) {
-            r = r->next;
-          }
-          r->next = new_entry;
-        } else {
-          /* queue did not exist, first item in queue */
-          arp_table[i].q = new_entry;
-        }
-        LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: queued packet %p on ARP entry %"S16_F"\n", (void *)q, (s16_t)i));
-        result = ERR_OK;
-      } else {
-        /* the pool MEMP_ARP_QUEUE is empty */
-        pbuf_free(p);
-        LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: could not queue a copy of PBUF_REF packet %p (out of memory)\n", (void *)q));
-        result = ERR_MEM;
-      }
-#else /* ARP_QUEUEING */
-      /* always queue one packet per ARP request only, freeing a previously queued packet */
-      if (arp_table[i].q != NULL) {
-        LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: dropped previously queued packet %p for ARP entry %"S16_F"\n", (void *)q, (s16_t)i));
-        pbuf_free(arp_table[i].q);
-      }
-      arp_table[i].q = p;
-      result = ERR_OK;
-      LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: queued packet %p on ARP entry %"S16_F"\n", (void *)q, (s16_t)i));
-#endif /* ARP_QUEUEING */
-    } else {
-      ETHARP_STATS_INC(etharp.memerr);
-      LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: could not queue a copy of PBUF_REF packet %p (out of memory)\n", (void *)q));
-      result = ERR_MEM;
-    }
-  }
-  return result;
-}
-
-/**
- * Send a raw ARP packet (opcode and all addresses can be modified)
- *
- * @param netif the lwip network interface on which to send the ARP packet
- * @param ethsrc_addr the source MAC address for the ethernet header
- * @param ethdst_addr the destination MAC address for the ethernet header
- * @param hwsrc_addr the source MAC address for the ARP protocol header
- * @param ipsrc_addr the source IP address for the ARP protocol header
- * @param hwdst_addr the destination MAC address for the ARP protocol header
- * @param ipdst_addr the destination IP address for the ARP protocol header
- * @param opcode the type of the ARP packet
- * @return ERR_OK if the ARP packet has been sent
- *         ERR_MEM if the ARP packet couldn't be allocated
- *         any other err_t on failure
- */
-#if !LWIP_AUTOIP
-static
-#endif /* LWIP_AUTOIP */
-err_t
-etharp_raw(struct netif *netif, const struct eth_addr *ethsrc_addr,
-           const struct eth_addr *ethdst_addr,
-           const struct eth_addr *hwsrc_addr, const ip_addr_t *ipsrc_addr,
-           const struct eth_addr *hwdst_addr, const ip_addr_t *ipdst_addr,
-           const u16_t opcode)
-{
-  struct pbuf *p;
-  err_t result = ERR_OK;
-  struct eth_hdr *ethhdr;
-  struct etharp_hdr *hdr;
-#if LWIP_AUTOIP
-  const u8_t * ethdst_hwaddr;
-#endif /* LWIP_AUTOIP */
-
-  LWIP_ASSERT("netif != NULL", netif != NULL);
-
-  /* allocate a pbuf for the outgoing ARP request packet */
-  p = pbuf_alloc(PBUF_RAW, SIZEOF_ETHARP_PACKET, PBUF_RAM);
-  /* could allocate a pbuf for an ARP request? */
-  if (p == NULL) {
-    LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
-      ("etharp_raw: could not allocate pbuf for ARP request.\n"));
-    ETHARP_STATS_INC(etharp.memerr);
-    return ERR_MEM;
-  }
-  LWIP_ASSERT("check that first pbuf can hold struct etharp_hdr",
-              (p->len >= SIZEOF_ETHARP_PACKET));
-
-  ethhdr = (struct eth_hdr *)p->payload;
-  hdr = (struct etharp_hdr *)((u8_t*)ethhdr + SIZEOF_ETH_HDR);
-  LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_raw: sending raw ARP packet.\n"));
-  hdr->opcode = htons(opcode);
-
-  LWIP_ASSERT("netif->hwaddr_len must be the same as ETHARP_HWADDR_LEN for etharp!",
-              (netif->hwaddr_len == ETHARP_HWADDR_LEN));
-#if LWIP_AUTOIP
-  /* If we are using Link-Local, all ARP packets that contain a Link-Local
-   * 'sender IP address' MUST be sent using link-layer broadcast instead of
-   * link-layer unicast. (See RFC3927 Section 2.5, last paragraph) */
-  ethdst_hwaddr = ip_addr_islinklocal(ipsrc_addr) ? (u8_t*)(ethbroadcast.addr) : ethdst_addr->addr;
-#endif /* LWIP_AUTOIP */
-  /* Write the ARP MAC-Addresses */
-  ETHADDR16_COPY(&hdr->shwaddr, hwsrc_addr);
-  ETHADDR16_COPY(&hdr->dhwaddr, hwdst_addr);
-  /* Write the Ethernet MAC-Addresses */
-#if LWIP_AUTOIP
-  ETHADDR16_COPY(&ethhdr->dest, ethdst_hwaddr);
-#else  /* LWIP_AUTOIP */
-  ETHADDR16_COPY(&ethhdr->dest, ethdst_addr);
-#endif /* LWIP_AUTOIP */
-  ETHADDR16_COPY(&ethhdr->src, ethsrc_addr);
-  /* Copy struct ip_addr2 to aligned ip_addr, to support compilers without
-   * structure packing. */ 
-  IPADDR2_COPY(&hdr->sipaddr, ipsrc_addr);
-  IPADDR2_COPY(&hdr->dipaddr, ipdst_addr);
-
-  hdr->hwtype = PP_HTONS(HWTYPE_ETHERNET);
-  hdr->proto = PP_HTONS(ETHTYPE_IP);
-  /* set hwlen and protolen */
-  hdr->hwlen = ETHARP_HWADDR_LEN;
-  hdr->protolen = sizeof(ip_addr_t);
-
-  ethhdr->type = PP_HTONS(ETHTYPE_ARP);
-  /* send ARP query */
-  result = netif->linkoutput(netif, p);
-  ETHARP_STATS_INC(etharp.xmit);
-  /* free ARP query packet */
-  pbuf_free(p);
-  p = NULL;
-  /* could not allocate pbuf for ARP request */
-
-  return result;
-}
-
-/**
- * Send an ARP request packet asking for ipaddr.
- *
- * @param netif the lwip network interface on which to send the request
- * @param ipaddr the IP address for which to ask
- * @return ERR_OK if the request has been sent
- *         ERR_MEM if the ARP packet couldn't be allocated
- *         any other err_t on failure
- */
-err_t
-etharp_request(struct netif *netif, ip_addr_t *ipaddr)
-{
-  LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_request: sending ARP request.\n"));
-  return etharp_raw(netif, (struct eth_addr *)netif->hwaddr, &ethbroadcast,
-                    (struct eth_addr *)netif->hwaddr, &netif->ip_addr, &ethzero,
-                    ipaddr, ARP_REQUEST);
-}
-#endif /* LWIP_ARP */
-
-/**
- * Process received ethernet frames. Using this function instead of directly
- * calling ip_input and passing ARP frames through etharp in ethernetif_input,
- * the ARP cache is protected from concurrent access.
- *
- * @param p the recevied packet, p->payload pointing to the ethernet header
- * @param netif the network interface on which the packet was received
- */
-err_t
-ethernet_input(struct pbuf *p, struct netif *netif)
-{
-  struct eth_hdr* ethhdr;
-  u16_t type;
-#if LWIP_ARP || ETHARP_SUPPORT_VLAN
-  s16_t ip_hdr_offset = SIZEOF_ETH_HDR;
-#endif /* LWIP_ARP || ETHARP_SUPPORT_VLAN */
-
-  if (p->len <= SIZEOF_ETH_HDR) {
-    /* a packet with only an ethernet header (or less) is not valid for us */
-    ETHARP_STATS_INC(etharp.proterr);
-    ETHARP_STATS_INC(etharp.drop);
-    goto free_and_return;
-  }
-
-  /* points to packet payload, which starts with an Ethernet header */
-  ethhdr = (struct eth_hdr *)p->payload;
-  LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE,
-    ("ethernet_input: dest:%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F", src:%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F", type:%"X16_F"\n",
-     (unsigned)ethhdr->dest.addr[0], (unsigned)ethhdr->dest.addr[1], (unsigned)ethhdr->dest.addr[2],
-     (unsigned)ethhdr->dest.addr[3], (unsigned)ethhdr->dest.addr[4], (unsigned)ethhdr->dest.addr[5],
-     (unsigned)ethhdr->src.addr[0], (unsigned)ethhdr->src.addr[1], (unsigned)ethhdr->src.addr[2],
-     (unsigned)ethhdr->src.addr[3], (unsigned)ethhdr->src.addr[4], (unsigned)ethhdr->src.addr[5],
-     (unsigned)htons(ethhdr->type)));
-
-  type = ethhdr->type;
-#if ETHARP_SUPPORT_VLAN
-  if (type == PP_HTONS(ETHTYPE_VLAN)) {
-    struct eth_vlan_hdr *vlan = (struct eth_vlan_hdr*)(((char*)ethhdr) + SIZEOF_ETH_HDR);
-    if (p->len <= SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR) {
-      /* a packet with only an ethernet/vlan header (or less) is not valid for us */
-      ETHARP_STATS_INC(etharp.proterr);
-      ETHARP_STATS_INC(etharp.drop);
-      goto free_and_return;
-    }
-#if defined(ETHARP_VLAN_CHECK) || defined(ETHARP_VLAN_CHECK_FN) /* if not, allow all VLANs */
-#ifdef ETHARP_VLAN_CHECK_FN
-    if (!ETHARP_VLAN_CHECK_FN(ethhdr, vlan)) {
-#elif defined(ETHARP_VLAN_CHECK)
-    if (VLAN_ID(vlan) != ETHARP_VLAN_CHECK) {
-#endif
-      /* silently ignore this packet: not for our VLAN */
-      pbuf_free(p);
-      return ERR_OK;
-    }
-#endif /* defined(ETHARP_VLAN_CHECK) || defined(ETHARP_VLAN_CHECK_FN) */
-    type = vlan->tpid;
-    ip_hdr_offset = SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR;
-  }
-#endif /* ETHARP_SUPPORT_VLAN */
-
-#if LWIP_ARP_FILTER_NETIF
-  netif = LWIP_ARP_FILTER_NETIF_FN(p, netif, htons(type));
-#endif /* LWIP_ARP_FILTER_NETIF*/
-
-  if (ethhdr->dest.addr[0] & 1) {
-    /* this might be a multicast or broadcast packet */
-    if (ethhdr->dest.addr[0] == LL_MULTICAST_ADDR_0) {
-      if ((ethhdr->dest.addr[1] == LL_MULTICAST_ADDR_1) &&
-          (ethhdr->dest.addr[2] == LL_MULTICAST_ADDR_2)) {
-        /* mark the pbuf as link-layer multicast */
-        p->flags |= PBUF_FLAG_LLMCAST;
-      }
-    } else if (eth_addr_cmp(&ethhdr->dest, &ethbroadcast)) {
-      /* mark the pbuf as link-layer broadcast */
-      p->flags |= PBUF_FLAG_LLBCAST;
-    }
-  }
-
-  switch (type) {
-#if LWIP_ARP
-    /* IP packet? */
-    case PP_HTONS(ETHTYPE_IP):
-      if (!(netif->flags & NETIF_FLAG_ETHARP)) {
-        goto free_and_return;
-      }
-#if ETHARP_TRUST_IP_MAC
-      /* update ARP table */
-      etharp_ip_input(netif, p);
-#endif /* ETHARP_TRUST_IP_MAC */
-      /* skip Ethernet header */
-      if(pbuf_header(p, -ip_hdr_offset)) {
-        LWIP_ASSERT("Can't move over header in packet", 0);
-        goto free_and_return;
-      } else {
-        /* pass to IP layer */
-        ip_input(p, netif);
-      }
-      break;
-      
-    case PP_HTONS(ETHTYPE_ARP):
-      if (!(netif->flags & NETIF_FLAG_ETHARP)) {
-        goto free_and_return;
-      }
-      /* pass p to ARP module */
-      etharp_arp_input(netif, (struct eth_addr*)(netif->hwaddr), p);
-      break;
-#endif /* LWIP_ARP */
-#if PPPOE_SUPPORT
-    case PP_HTONS(ETHTYPE_PPPOEDISC): /* PPP Over Ethernet Discovery Stage */
-      pppoe_disc_input(netif, p);
-      break;
-
-    case PP_HTONS(ETHTYPE_PPPOE): /* PPP Over Ethernet Session Stage */
-      pppoe_data_input(netif, p);
-      break;
-#endif /* PPPOE_SUPPORT */
-
-    default:
-      ETHARP_STATS_INC(etharp.proterr);
-      ETHARP_STATS_INC(etharp.drop);
-      goto free_and_return;
-  }
-
-  /* This means the pbuf is freed or consumed,
-     so the caller doesn't have to free it again */
-  return ERR_OK;
-
-free_and_return:
-  pbuf_free(p);
-  return ERR_OK;
-}
-#endif /* LWIP_ARP || LWIP_ETHERNET */
+/**
+ * @file
+ * Address Resolution Protocol module for IP over Ethernet
+ *
+ * Functionally, ARP is divided into two parts. The first maps an IP address
+ * to a physical address when sending a packet, and the second part answers
+ * requests from other machines for our physical address.
+ *
+ * This implementation complies with RFC 826 (Ethernet ARP). It supports
+ * Gratuitious ARP from RFC3220 (IP Mobility Support for IPv4) section 4.6
+ * if an interface calls etharp_gratuitous(our_netif) upon address change.
+ */
+
+/*
+ * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
+ * Copyright (c) 2003-2004 Leon Woestenberg <leon.woestenberg@axon.tv>
+ * Copyright (c) 2003-2004 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_ARP || LWIP_ETHERNET
+
+#include "lwip/etharp.h"
+#include "lwip/stats.h"
+#include "lwip/snmp.h"
+#include "lwip/dhcp.h"
+#include "lwip/autoip.h"
+#include "netif/ethernet.h"
+
+#include <string.h>
+
+#ifdef LWIP_HOOK_FILENAME
+#include LWIP_HOOK_FILENAME
+#endif
+
+#if LWIP_IPV4 && LWIP_ARP /* don't build if not configured for use in lwipopts.h */
+
+/** Re-request a used ARP entry 1 minute before it would expire to prevent
+ *  breaking a steadily used connection because the ARP entry timed out. */
+#define ARP_AGE_REREQUEST_USED_UNICAST   (ARP_MAXAGE - 30)
+#define ARP_AGE_REREQUEST_USED_BROADCAST (ARP_MAXAGE - 15)
+
+/** the time an ARP entry stays pending after first request,
+ *  for ARP_TMR_INTERVAL = 1000, this is
+ *  10 seconds.
+ *
+ *  @internal Keep this number at least 2, otherwise it might
+ *  run out instantly if the timeout occurs directly after a request.
+ */
+#define ARP_MAXPENDING 5
+
+/** ARP states */
+enum etharp_state {
+  ETHARP_STATE_EMPTY = 0,
+  ETHARP_STATE_PENDING,
+  ETHARP_STATE_STABLE,
+  ETHARP_STATE_STABLE_REREQUESTING_1,
+  ETHARP_STATE_STABLE_REREQUESTING_2
+#if ETHARP_SUPPORT_STATIC_ENTRIES
+  ,ETHARP_STATE_STATIC
+#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
+};
+
+struct etharp_entry {
+#if ARP_QUEUEING
+  /** Pointer to queue of pending outgoing packets on this ARP entry. */
+  struct etharp_q_entry *q;
+#else /* ARP_QUEUEING */
+  /** Pointer to a single pending outgoing packet on this ARP entry. */
+  struct pbuf *q;
+#endif /* ARP_QUEUEING */
+  ip4_addr_t ipaddr;
+  struct netif *netif;
+  struct eth_addr ethaddr;
+  u16_t ctime;
+  u8_t state;
+};
+
+static struct etharp_entry arp_table[ARP_TABLE_SIZE];
+
+#if !LWIP_NETIF_HWADDRHINT
+static u8_t etharp_cached_entry;
+#endif /* !LWIP_NETIF_HWADDRHINT */
+
+/** Try hard to create a new entry - we want the IP address to appear in
+    the cache (even if this means removing an active entry or so). */
+#define ETHARP_FLAG_TRY_HARD     1
+#define ETHARP_FLAG_FIND_ONLY    2
+#if ETHARP_SUPPORT_STATIC_ENTRIES
+#define ETHARP_FLAG_STATIC_ENTRY 4
+#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
+
+#if LWIP_NETIF_HWADDRHINT
+#define ETHARP_SET_HINT(netif, hint)  if (((netif) != NULL) && ((netif)->addr_hint != NULL))  \
+                                      *((netif)->addr_hint) = (hint);
+#else /* LWIP_NETIF_HWADDRHINT */
+#define ETHARP_SET_HINT(netif, hint)  (etharp_cached_entry = (hint))
+#endif /* LWIP_NETIF_HWADDRHINT */
+
+
+/* Some checks, instead of etharp_init(): */
+#if (LWIP_ARP && (ARP_TABLE_SIZE > 0x7f))
+  #error "ARP_TABLE_SIZE must fit in an s8_t, you have to reduce it in your lwipopts.h"
+#endif
+
+
+static err_t etharp_request_dst(struct netif *netif, const ip4_addr_t *ipaddr, const struct eth_addr* hw_dst_addr);
+static err_t etharp_raw(struct netif *netif,
+                        const struct eth_addr *ethsrc_addr, const struct eth_addr *ethdst_addr,
+                        const struct eth_addr *hwsrc_addr, const ip4_addr_t *ipsrc_addr,
+                        const struct eth_addr *hwdst_addr, const ip4_addr_t *ipdst_addr,
+                        const u16_t opcode);
+
+#if ARP_QUEUEING
+/**
+ * Free a complete queue of etharp entries
+ *
+ * @param q a qeueue of etharp_q_entry's to free
+ */
+static void
+free_etharp_q(struct etharp_q_entry *q)
+{
+  struct etharp_q_entry *r;
+  LWIP_ASSERT("q != NULL", q != NULL);
+  LWIP_ASSERT("q->p != NULL", q->p != NULL);
+  while (q) {
+    r = q;
+    q = q->next;
+    LWIP_ASSERT("r->p != NULL", (r->p != NULL));
+    pbuf_free(r->p);
+    memp_free(MEMP_ARP_QUEUE, r);
+  }
+}
+#else /* ARP_QUEUEING */
+
+/** Compatibility define: free the queued pbuf */
+#define free_etharp_q(q) pbuf_free(q)
+
+#endif /* ARP_QUEUEING */
+
+/** Clean up ARP table entries */
+static void
+etharp_free_entry(int i)
+{
+  /* remove from SNMP ARP index tree */
+  mib2_remove_arp_entry(arp_table[i].netif, &arp_table[i].ipaddr);
+  /* and empty packet queue */
+  if (arp_table[i].q != NULL) {
+    /* remove all queued packets */
+    LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_free_entry: freeing entry %"U16_F", packet queue %p.\n", (u16_t)i, (void *)(arp_table[i].q)));
+    free_etharp_q(arp_table[i].q);
+    arp_table[i].q = NULL;
+  }
+  /* recycle entry for re-use */
+  arp_table[i].state = ETHARP_STATE_EMPTY;
+#ifdef LWIP_DEBUG
+  /* for debugging, clean out the complete entry */
+  arp_table[i].ctime = 0;
+  arp_table[i].netif = NULL;
+  ip4_addr_set_zero(&arp_table[i].ipaddr);
+  arp_table[i].ethaddr = ethzero;
+#endif /* LWIP_DEBUG */
+}
+
+/**
+ * Clears expired entries in the ARP table.
+ *
+ * This function should be called every ARP_TMR_INTERVAL milliseconds (1 second),
+ * in order to expire entries in the ARP table.
+ */
+void
+etharp_tmr(void)
+{
+  u8_t i;
+
+  LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_timer\n"));
+  /* remove expired entries from the ARP table */
+  for (i = 0; i < ARP_TABLE_SIZE; ++i) {
+    u8_t state = arp_table[i].state;
+    if (state != ETHARP_STATE_EMPTY
+#if ETHARP_SUPPORT_STATIC_ENTRIES
+      && (state != ETHARP_STATE_STATIC)
+#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
+      ) {
+      arp_table[i].ctime++;
+      if ((arp_table[i].ctime >= ARP_MAXAGE) ||
+          ((arp_table[i].state == ETHARP_STATE_PENDING)  &&
+           (arp_table[i].ctime >= ARP_MAXPENDING))) {
+        /* pending or stable entry has become old! */
+        LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_timer: expired %s entry %"U16_F".\n",
+             arp_table[i].state >= ETHARP_STATE_STABLE ? "stable" : "pending", (u16_t)i));
+        /* clean up entries that have just been expired */
+        etharp_free_entry(i);
+      } else if (arp_table[i].state == ETHARP_STATE_STABLE_REREQUESTING_1) {
+        /* Don't send more than one request every 2 seconds. */
+        arp_table[i].state = ETHARP_STATE_STABLE_REREQUESTING_2;
+      } else if (arp_table[i].state == ETHARP_STATE_STABLE_REREQUESTING_2) {
+        /* Reset state to stable, so that the next transmitted packet will
+           re-send an ARP request. */
+        arp_table[i].state = ETHARP_STATE_STABLE;
+      } else if (arp_table[i].state == ETHARP_STATE_PENDING) {
+        /* still pending, resend an ARP query */
+        etharp_request(arp_table[i].netif, &arp_table[i].ipaddr);
+      }
+    }
+  }
+}
+
+/**
+ * Search the ARP table for a matching or new entry.
+ *
+ * If an IP address is given, return a pending or stable ARP entry that matches
+ * the address. If no match is found, create a new entry with this address set,
+ * but in state ETHARP_EMPTY. The caller must check and possibly change the
+ * state of the returned entry.
+ *
+ * If ipaddr is NULL, return a initialized new entry in state ETHARP_EMPTY.
+ *
+ * In all cases, attempt to create new entries from an empty entry. If no
+ * empty entries are available and ETHARP_FLAG_TRY_HARD flag is set, recycle
+ * old entries. Heuristic choose the least important entry for recycling.
+ *
+ * @param ipaddr IP address to find in ARP cache, or to add if not found.
+ * @param flags See @ref etharp_state
+ * @param netif netif related to this address (used for NETIF_HWADDRHINT)
+ *
+ * @return The ARP entry index that matched or is created, ERR_MEM if no
+ * entry is found or could be recycled.
+ */
+static s8_t
+etharp_find_entry(const ip4_addr_t *ipaddr, u8_t flags, struct netif* netif)
+{
+  s8_t old_pending = ARP_TABLE_SIZE, old_stable = ARP_TABLE_SIZE;
+  s8_t empty = ARP_TABLE_SIZE;
+  u8_t i = 0;
+  /* oldest entry with packets on queue */
+  s8_t old_queue = ARP_TABLE_SIZE;
+  /* its age */
+  u16_t age_queue = 0, age_pending = 0, age_stable = 0;
+
+  LWIP_UNUSED_ARG(netif);
+
+  /**
+   * a) do a search through the cache, remember candidates
+   * b) select candidate entry
+   * c) create new entry
+   */
+
+  /* a) in a single search sweep, do all of this
+   * 1) remember the first empty entry (if any)
+   * 2) remember the oldest stable entry (if any)
+   * 3) remember the oldest pending entry without queued packets (if any)
+   * 4) remember the oldest pending entry with queued packets (if any)
+   * 5) search for a matching IP entry, either pending or stable
+   *    until 5 matches, or all entries are searched for.
+   */
+
+  for (i = 0; i < ARP_TABLE_SIZE; ++i) {
+    u8_t state = arp_table[i].state;
+    /* no empty entry found yet and now we do find one? */
+    if ((empty == ARP_TABLE_SIZE) && (state == ETHARP_STATE_EMPTY)) {
+      LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_find_entry: found empty entry %"U16_F"\n", (u16_t)i));
+      /* remember first empty entry */
+      empty = i;
+    } else if (state != ETHARP_STATE_EMPTY) {
+      LWIP_ASSERT("state == ETHARP_STATE_PENDING || state >= ETHARP_STATE_STABLE",
+        state == ETHARP_STATE_PENDING || state >= ETHARP_STATE_STABLE);
+      /* if given, does IP address match IP address in ARP entry? */
+      if (ipaddr && ip4_addr_cmp(ipaddr, &arp_table[i].ipaddr)
+#if ETHARP_TABLE_MATCH_NETIF
+          && ((netif == NULL) || (netif == arp_table[i].netif))
+#endif /* ETHARP_TABLE_MATCH_NETIF */
+        ) {
+        LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: found matching entry %"U16_F"\n", (u16_t)i));
+        /* found exact IP address match, simply bail out */
+        return i;
+      }
+      /* pending entry? */
+      if (state == ETHARP_STATE_PENDING) {
+        /* pending with queued packets? */
+        if (arp_table[i].q != NULL) {
+          if (arp_table[i].ctime >= age_queue) {
+            old_queue = i;
+            age_queue = arp_table[i].ctime;
+          }
+        } else
+        /* pending without queued packets? */
+        {
+          if (arp_table[i].ctime >= age_pending) {
+            old_pending = i;
+            age_pending = arp_table[i].ctime;
+          }
+        }
+      /* stable entry? */
+      } else if (state >= ETHARP_STATE_STABLE) {
+#if ETHARP_SUPPORT_STATIC_ENTRIES
+        /* don't record old_stable for static entries since they never expire */
+        if (state < ETHARP_STATE_STATIC)
+#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
+        {
+          /* remember entry with oldest stable entry in oldest, its age in maxtime */
+          if (arp_table[i].ctime >= age_stable) {
+            old_stable = i;
+            age_stable = arp_table[i].ctime;
+          }
+        }
+      }
+    }
+  }
+  /* { we have no match } => try to create a new entry */
+
+  /* don't create new entry, only search? */
+  if (((flags & ETHARP_FLAG_FIND_ONLY) != 0) ||
+      /* or no empty entry found and not allowed to recycle? */
+      ((empty == ARP_TABLE_SIZE) && ((flags & ETHARP_FLAG_TRY_HARD) == 0))) {
+    LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: no empty entry found and not allowed to recycle\n"));
+    return (s8_t)ERR_MEM;
+  }
+
+  /* b) choose the least destructive entry to recycle:
+   * 1) empty entry
+   * 2) oldest stable entry
+   * 3) oldest pending entry without queued packets
+   * 4) oldest pending entry with queued packets
+   *
+   * { ETHARP_FLAG_TRY_HARD is set at this point }
+   */
+
+  /* 1) empty entry available? */
+  if (empty < ARP_TABLE_SIZE) {
+    i = empty;
+    LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: selecting empty entry %"U16_F"\n", (u16_t)i));
+  } else {
+    /* 2) found recyclable stable entry? */
+    if (old_stable < ARP_TABLE_SIZE) {
+      /* recycle oldest stable*/
+      i = old_stable;
+      LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: selecting oldest stable entry %"U16_F"\n", (u16_t)i));
+      /* no queued packets should exist on stable entries */
+      LWIP_ASSERT("arp_table[i].q == NULL", arp_table[i].q == NULL);
+    /* 3) found recyclable pending entry without queued packets? */
+    } else if (old_pending < ARP_TABLE_SIZE) {
+      /* recycle oldest pending */
+      i = old_pending;
+      LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: selecting oldest pending entry %"U16_F" (without queue)\n", (u16_t)i));
+    /* 4) found recyclable pending entry with queued packets? */
+    } else if (old_queue < ARP_TABLE_SIZE) {
+      /* recycle oldest pending (queued packets are free in etharp_free_entry) */
+      i = old_queue;
+      LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: selecting oldest pending entry %"U16_F", freeing packet queue %p\n", (u16_t)i, (void *)(arp_table[i].q)));
+      /* no empty or recyclable entries found */
+    } else {
+      LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: no empty or recyclable entries found\n"));
+      return (s8_t)ERR_MEM;
+    }
+
+    /* { empty or recyclable entry found } */
+    LWIP_ASSERT("i < ARP_TABLE_SIZE", i < ARP_TABLE_SIZE);
+    etharp_free_entry(i);
+  }
+
+  LWIP_ASSERT("i < ARP_TABLE_SIZE", i < ARP_TABLE_SIZE);
+  LWIP_ASSERT("arp_table[i].state == ETHARP_STATE_EMPTY",
+    arp_table[i].state == ETHARP_STATE_EMPTY);
+
+  /* IP address given? */
+  if (ipaddr != NULL) {
+    /* set IP address */
+    ip4_addr_copy(arp_table[i].ipaddr, *ipaddr);
+  }
+  arp_table[i].ctime = 0;
+#if ETHARP_TABLE_MATCH_NETIF
+  arp_table[i].netif = netif;
+#endif /* ETHARP_TABLE_MATCH_NETIF*/
+  return (err_t)i;
+}
+
+/**
+ * Update (or insert) a IP/MAC address pair in the ARP cache.
+ *
+ * If a pending entry is resolved, any queued packets will be sent
+ * at this point.
+ *
+ * @param netif netif related to this entry (used for NETIF_ADDRHINT)
+ * @param ipaddr IP address of the inserted ARP entry.
+ * @param ethaddr Ethernet address of the inserted ARP entry.
+ * @param flags See @ref etharp_state
+ *
+ * @return
+ * - ERR_OK Successfully updated ARP cache.
+ * - ERR_MEM If we could not add a new ARP entry when ETHARP_FLAG_TRY_HARD was set.
+ * - ERR_ARG Non-unicast address given, those will not appear in ARP cache.
+ *
+ * @see pbuf_free()
+ */
+static err_t
+etharp_update_arp_entry(struct netif *netif, const ip4_addr_t *ipaddr, struct eth_addr *ethaddr, u8_t flags)
+{
+  s8_t i;
+  LWIP_ASSERT("netif->hwaddr_len == ETH_HWADDR_LEN", netif->hwaddr_len == ETH_HWADDR_LEN);
+  LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_update_arp_entry: %"U16_F".%"U16_F".%"U16_F".%"U16_F" - %02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F"\n",
+    ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr),
+    (u16_t)ethaddr->addr[0], (u16_t)ethaddr->addr[1], (u16_t)ethaddr->addr[2],
+    (u16_t)ethaddr->addr[3], (u16_t)ethaddr->addr[4], (u16_t)ethaddr->addr[5]));
+  /* non-unicast address? */
+  if (ip4_addr_isany(ipaddr) ||
+      ip4_addr_isbroadcast(ipaddr, netif) ||
+      ip4_addr_ismulticast(ipaddr)) {
+    LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_update_arp_entry: will not add non-unicast IP address to ARP cache\n"));
+    return ERR_ARG;
+  }
+  /* find or create ARP entry */
+  i = etharp_find_entry(ipaddr, flags, netif);
+  /* bail out if no entry could be found */
+  if (i < 0) {
+    return (err_t)i;
+  }
+
+#if ETHARP_SUPPORT_STATIC_ENTRIES
+  if (flags & ETHARP_FLAG_STATIC_ENTRY) {
+    /* record static type */
+    arp_table[i].state = ETHARP_STATE_STATIC;
+  } else if (arp_table[i].state == ETHARP_STATE_STATIC) {
+    /* found entry is a static type, don't overwrite it */
+    return ERR_VAL;
+  } else
+#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
+  {
+    /* mark it stable */
+    arp_table[i].state = ETHARP_STATE_STABLE;
+  }
+
+  /* record network interface */
+  arp_table[i].netif = netif;
+  /* insert in SNMP ARP index tree */
+  mib2_add_arp_entry(netif, &arp_table[i].ipaddr);
+
+  LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_update_arp_entry: updating stable entry %"S16_F"\n", (s16_t)i));
+  /* update address */
+  ETHADDR32_COPY(&arp_table[i].ethaddr, ethaddr);
+  /* reset time stamp */
+  arp_table[i].ctime = 0;
+  /* this is where we will send out queued packets! */
+#if ARP_QUEUEING
+  while (arp_table[i].q != NULL) {
+    struct pbuf *p;
+    /* remember remainder of queue */
+    struct etharp_q_entry *q = arp_table[i].q;
+    /* pop first item off the queue */
+    arp_table[i].q = q->next;
+    /* get the packet pointer */
+    p = q->p;
+    /* now queue entry can be freed */
+    memp_free(MEMP_ARP_QUEUE, q);
+#else /* ARP_QUEUEING */
+  if (arp_table[i].q != NULL) {
+    struct pbuf *p = arp_table[i].q;
+    arp_table[i].q = NULL;
+#endif /* ARP_QUEUEING */
+    /* send the queued IP packet */
+    ethernet_output(netif, p, (struct eth_addr*)(netif->hwaddr), ethaddr, ETHTYPE_IP);
+    /* free the queued IP packet */
+    pbuf_free(p);
+  }
+  return ERR_OK;
+}
+
+#if ETHARP_SUPPORT_STATIC_ENTRIES
+/** Add a new static entry to the ARP table. If an entry exists for the
+ * specified IP address, this entry is overwritten.
+ * If packets are queued for the specified IP address, they are sent out.
+ *
+ * @param ipaddr IP address for the new static entry
+ * @param ethaddr ethernet address for the new static entry
+ * @return See return values of etharp_add_static_entry
+ */
+err_t
+etharp_add_static_entry(const ip4_addr_t *ipaddr, struct eth_addr *ethaddr)
+{
+  struct netif *netif;
+  LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_add_static_entry: %"U16_F".%"U16_F".%"U16_F".%"U16_F" - %02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F"\n",
+    ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr),
+    (u16_t)ethaddr->addr[0], (u16_t)ethaddr->addr[1], (u16_t)ethaddr->addr[2],
+    (u16_t)ethaddr->addr[3], (u16_t)ethaddr->addr[4], (u16_t)ethaddr->addr[5]));
+
+  netif = ip4_route(ipaddr);
+  if (netif == NULL) {
+    return ERR_RTE;
+  }
+
+  return etharp_update_arp_entry(netif, ipaddr, ethaddr, ETHARP_FLAG_TRY_HARD | ETHARP_FLAG_STATIC_ENTRY);
+}
+
+/** Remove a static entry from the ARP table previously added with a call to
+ * etharp_add_static_entry.
+ *
+ * @param ipaddr IP address of the static entry to remove
+ * @return ERR_OK: entry removed
+ *         ERR_MEM: entry wasn't found
+ *         ERR_ARG: entry wasn't a static entry but a dynamic one
+ */
+err_t
+etharp_remove_static_entry(const ip4_addr_t *ipaddr)
+{
+  s8_t i;
+  LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_remove_static_entry: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+    ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr)));
+
+  /* find or create ARP entry */
+  i = etharp_find_entry(ipaddr, ETHARP_FLAG_FIND_ONLY, NULL);
+  /* bail out if no entry could be found */
+  if (i < 0) {
+    return (err_t)i;
+  }
+
+  if (arp_table[i].state != ETHARP_STATE_STATIC) {
+    /* entry wasn't a static entry, cannot remove it */
+    return ERR_ARG;
+  }
+  /* entry found, free it */
+  etharp_free_entry(i);
+  return ERR_OK;
+}
+#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
+
+/**
+ * Remove all ARP table entries of the specified netif.
+ *
+ * @param netif points to a network interface
+ */
+void
+etharp_cleanup_netif(struct netif *netif)
+{
+  u8_t i;
+
+  for (i = 0; i < ARP_TABLE_SIZE; ++i) {
+    u8_t state = arp_table[i].state;
+    if ((state != ETHARP_STATE_EMPTY) && (arp_table[i].netif == netif)) {
+      etharp_free_entry(i);
+    }
+  }
+}
+
+/**
+ * Finds (stable) ethernet/IP address pair from ARP table
+ * using interface and IP address index.
+ * @note the addresses in the ARP table are in network order!
+ *
+ * @param netif points to interface index
+ * @param ipaddr points to the (network order) IP address index
+ * @param eth_ret points to return pointer
+ * @param ip_ret points to return pointer
+ * @return table index if found, -1 otherwise
+ */
+s8_t
+etharp_find_addr(struct netif *netif, const ip4_addr_t *ipaddr,
+         struct eth_addr **eth_ret, const ip4_addr_t **ip_ret)
+{
+  s8_t i;
+
+  LWIP_ASSERT("eth_ret != NULL && ip_ret != NULL",
+    eth_ret != NULL && ip_ret != NULL);
+
+  LWIP_UNUSED_ARG(netif);
+
+  i = etharp_find_entry(ipaddr, ETHARP_FLAG_FIND_ONLY, netif);
+  if ((i >= 0) && (arp_table[i].state >= ETHARP_STATE_STABLE)) {
+      *eth_ret = &arp_table[i].ethaddr;
+      *ip_ret = &arp_table[i].ipaddr;
+      return i;
+  }
+  return -1;
+}
+
+/**
+ * Possibility to iterate over stable ARP table entries
+ *
+ * @param i entry number, 0 to ARP_TABLE_SIZE
+ * @param ipaddr return value: IP address
+ * @param netif return value: points to interface
+ * @param eth_ret return value: ETH address
+ * @return 1 on valid index, 0 otherwise
+ */
+u8_t
+etharp_get_entry(u8_t i, ip4_addr_t **ipaddr, struct netif **netif, struct eth_addr **eth_ret)
+{
+  LWIP_ASSERT("ipaddr != NULL", ipaddr != NULL);
+  LWIP_ASSERT("netif != NULL", netif != NULL);
+  LWIP_ASSERT("eth_ret != NULL", eth_ret != NULL);
+
+  if((i < ARP_TABLE_SIZE) && (arp_table[i].state >= ETHARP_STATE_STABLE)) {
+    *ipaddr  = &arp_table[i].ipaddr;
+    *netif   = arp_table[i].netif;
+    *eth_ret = &arp_table[i].ethaddr;
+    return 1;
+  } else {
+    return 0;
+  }
+}
+
+/**
+ * Responds to ARP requests to us. Upon ARP replies to us, add entry to cache
+ * send out queued IP packets. Updates cache with snooped address pairs.
+ *
+ * Should be called for incoming ARP packets. The pbuf in the argument
+ * is freed by this function.
+ *
+ * @param p The ARP packet that arrived on netif. Is freed by this function.
+ * @param netif The lwIP network interface on which the ARP packet pbuf arrived.
+ *
+ * @see pbuf_free()
+ */
+void
+etharp_input(struct pbuf *p, struct netif *netif)
+{
+  struct etharp_hdr *hdr;
+  /* these are aligned properly, whereas the ARP header fields might not be */
+  ip4_addr_t sipaddr, dipaddr;
+  u8_t for_us;
+
+  LWIP_ERROR("netif != NULL", (netif != NULL), return;);
+
+  hdr = (struct etharp_hdr *)p->payload;
+
+  /* RFC 826 "Packet Reception": */
+  if ((hdr->hwtype != PP_HTONS(HWTYPE_ETHERNET)) ||
+      (hdr->hwlen != ETH_HWADDR_LEN) ||
+      (hdr->protolen != sizeof(ip4_addr_t)) ||
+      (hdr->proto != PP_HTONS(ETHTYPE_IP)))  {
+    LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
+      ("etharp_input: packet dropped, wrong hw type, hwlen, proto, protolen or ethernet type (%"U16_F"/%"U16_F"/%"U16_F"/%"U16_F")\n",
+      hdr->hwtype, (u16_t)hdr->hwlen, hdr->proto, (u16_t)hdr->protolen));
+    ETHARP_STATS_INC(etharp.proterr);
+    ETHARP_STATS_INC(etharp.drop);
+    pbuf_free(p);
+    return;
+  }
+  ETHARP_STATS_INC(etharp.recv);
+
+#if LWIP_AUTOIP
+  /* We have to check if a host already has configured our random
+   * created link local address and continuously check if there is
+   * a host with this IP-address so we can detect collisions */
+  autoip_arp_reply(netif, hdr);
+#endif /* LWIP_AUTOIP */
+
+  /* Copy struct ip4_addr2 to aligned ip4_addr, to support compilers without
+   * structure packing (not using structure copy which breaks strict-aliasing rules). */
+  IPADDR2_COPY(&sipaddr, &hdr->sipaddr);
+  IPADDR2_COPY(&dipaddr, &hdr->dipaddr);
+
+  /* this interface is not configured? */
+  if (ip4_addr_isany_val(*netif_ip4_addr(netif))) {
+    for_us = 0;
+  } else {
+    /* ARP packet directed to us? */
+    for_us = (u8_t)ip4_addr_cmp(&dipaddr, netif_ip4_addr(netif));
+  }
+
+  /* ARP message directed to us?
+      -> add IP address in ARP cache; assume requester wants to talk to us,
+         can result in directly sending the queued packets for this host.
+     ARP message not directed to us?
+      ->  update the source IP address in the cache, if present */
+  etharp_update_arp_entry(netif, &sipaddr, &(hdr->shwaddr),
+                   for_us ? ETHARP_FLAG_TRY_HARD : ETHARP_FLAG_FIND_ONLY);
+
+  /* now act on the message itself */
+  switch (hdr->opcode) {
+  /* ARP request? */
+  case PP_HTONS(ARP_REQUEST):
+    /* ARP request. If it asked for our address, we send out a
+     * reply. In any case, we time-stamp any existing ARP entry,
+     * and possibly send out an IP packet that was queued on it. */
+
+    LWIP_DEBUGF (ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_input: incoming ARP request\n"));
+    /* ARP request for our address? */
+    if (for_us) {
+      /* send ARP response */
+      etharp_raw(netif,
+                 (struct eth_addr *)netif->hwaddr, &hdr->shwaddr,
+                 (struct eth_addr *)netif->hwaddr, netif_ip4_addr(netif),
+                 &hdr->shwaddr, &sipaddr,
+                 ARP_REPLY);
+    /* we are not configured? */
+    } else if (ip4_addr_isany_val(*netif_ip4_addr(netif))) {
+      /* { for_us == 0 and netif->ip_addr.addr == 0 } */
+      LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_input: we are unconfigured, ARP request ignored.\n"));
+    /* request was not directed to us */
+    } else {
+      /* { for_us == 0 and netif->ip_addr.addr != 0 } */
+      LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_input: ARP request was not for us.\n"));
+    }
+    break;
+  case PP_HTONS(ARP_REPLY):
+    /* ARP reply. We already updated the ARP cache earlier. */
+    LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_input: incoming ARP reply\n"));
+#if (LWIP_DHCP && DHCP_DOES_ARP_CHECK)
+    /* DHCP wants to know about ARP replies from any host with an
+     * IP address also offered to us by the DHCP server. We do not
+     * want to take a duplicate IP address on a single network.
+     * @todo How should we handle redundant (fail-over) interfaces? */
+    dhcp_arp_reply(netif, &sipaddr);
+#endif /* (LWIP_DHCP && DHCP_DOES_ARP_CHECK) */
+    break;
+  default:
+    LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_input: ARP unknown opcode type %"S16_F"\n", lwip_htons(hdr->opcode)));
+    ETHARP_STATS_INC(etharp.err);
+    break;
+  }
+  /* free ARP packet */
+  pbuf_free(p);
+}
+
+/** Just a small helper function that sends a pbuf to an ethernet address
+ * in the arp_table specified by the index 'arp_idx'.
+ */
+static err_t
+etharp_output_to_arp_index(struct netif *netif, struct pbuf *q, u8_t arp_idx)
+{
+  LWIP_ASSERT("arp_table[arp_idx].state >= ETHARP_STATE_STABLE",
+              arp_table[arp_idx].state >= ETHARP_STATE_STABLE);
+  /* if arp table entry is about to expire: re-request it,
+     but only if its state is ETHARP_STATE_STABLE to prevent flooding the
+     network with ARP requests if this address is used frequently. */
+  if (arp_table[arp_idx].state == ETHARP_STATE_STABLE) {
+    if (arp_table[arp_idx].ctime >= ARP_AGE_REREQUEST_USED_BROADCAST) {
+      /* issue a standard request using broadcast */
+      if (etharp_request(netif, &arp_table[arp_idx].ipaddr) == ERR_OK) {
+        arp_table[arp_idx].state = ETHARP_STATE_STABLE_REREQUESTING_1;
+      }
+    } else if (arp_table[arp_idx].ctime >= ARP_AGE_REREQUEST_USED_UNICAST) {
+      /* issue a unicast request (for 15 seconds) to prevent unnecessary broadcast */
+      if (etharp_request_dst(netif, &arp_table[arp_idx].ipaddr, &arp_table[arp_idx].ethaddr) == ERR_OK) {
+        arp_table[arp_idx].state = ETHARP_STATE_STABLE_REREQUESTING_1;
+      }
+    }
+  }
+
+  return ethernet_output(netif, q, (struct eth_addr*)(netif->hwaddr), &arp_table[arp_idx].ethaddr, ETHTYPE_IP);
+}
+
+/**
+ * Resolve and fill-in Ethernet address header for outgoing IP packet.
+ *
+ * For IP multicast and broadcast, corresponding Ethernet addresses
+ * are selected and the packet is transmitted on the link.
+ *
+ * For unicast addresses, the packet is submitted to etharp_query(). In
+ * case the IP address is outside the local network, the IP address of
+ * the gateway is used.
+ *
+ * @param netif The lwIP network interface which the IP packet will be sent on.
+ * @param q The pbuf(s) containing the IP packet to be sent.
+ * @param ipaddr The IP address of the packet destination.
+ *
+ * @return
+ * - ERR_RTE No route to destination (no gateway to external networks),
+ * or the return type of either etharp_query() or ethernet_output().
+ */
+err_t
+etharp_output(struct netif *netif, struct pbuf *q, const ip4_addr_t *ipaddr)
+{
+  const struct eth_addr *dest;
+  struct eth_addr mcastaddr;
+  const ip4_addr_t *dst_addr = ipaddr;
+
+  LWIP_ASSERT("netif != NULL", netif != NULL);
+  LWIP_ASSERT("q != NULL", q != NULL);
+  LWIP_ASSERT("ipaddr != NULL", ipaddr != NULL);
+
+  /* Determine on destination hardware address. Broadcasts and multicasts
+   * are special, other IP addresses are looked up in the ARP table. */
+
+  /* broadcast destination IP address? */
+  if (ip4_addr_isbroadcast(ipaddr, netif)) {
+    /* broadcast on Ethernet also */
+    dest = (const struct eth_addr *)&ethbroadcast;
+  /* multicast destination IP address? */
+  } else if (ip4_addr_ismulticast(ipaddr)) {
+    /* Hash IP multicast address to MAC address.*/
+    mcastaddr.addr[0] = LL_IP4_MULTICAST_ADDR_0;
+    mcastaddr.addr[1] = LL_IP4_MULTICAST_ADDR_1;
+    mcastaddr.addr[2] = LL_IP4_MULTICAST_ADDR_2;
+    mcastaddr.addr[3] = ip4_addr2(ipaddr) & 0x7f;
+    mcastaddr.addr[4] = ip4_addr3(ipaddr);
+    mcastaddr.addr[5] = ip4_addr4(ipaddr);
+    /* destination Ethernet address is multicast */
+    dest = &mcastaddr;
+  /* unicast destination IP address? */
+  } else {
+    s8_t i;
+    /* outside local network? if so, this can neither be a global broadcast nor
+       a subnet broadcast. */
+    if (!ip4_addr_netcmp(ipaddr, netif_ip4_addr(netif), netif_ip4_netmask(netif)) &&
+        !ip4_addr_islinklocal(ipaddr)) {
+#if LWIP_AUTOIP
+      struct ip_hdr *iphdr = LWIP_ALIGNMENT_CAST(struct ip_hdr*, q->payload);
+      /* According to RFC 3297, chapter 2.6.2 (Forwarding Rules), a packet with
+         a link-local source address must always be "directly to its destination
+         on the same physical link. The host MUST NOT send the packet to any
+         router for forwarding". */
+      if (!ip4_addr_islinklocal(&iphdr->src))
+#endif /* LWIP_AUTOIP */
+      {
+#ifdef LWIP_HOOK_ETHARP_GET_GW
+        /* For advanced routing, a single default gateway might not be enough, so get
+           the IP address of the gateway to handle the current destination address. */
+        dst_addr = LWIP_HOOK_ETHARP_GET_GW(netif, ipaddr);
+        if (dst_addr == NULL)
+#endif /* LWIP_HOOK_ETHARP_GET_GW */
+        {
+          /* interface has default gateway? */
+          if (!ip4_addr_isany_val(*netif_ip4_gw(netif))) {
+            /* send to hardware address of default gateway IP address */
+            dst_addr = netif_ip4_gw(netif);
+          /* no default gateway available */
+          } else {
+            /* no route to destination error (default gateway missing) */
+            return ERR_RTE;
+          }
+        }
+      }
+    }
+#if LWIP_NETIF_HWADDRHINT
+    if (netif->addr_hint != NULL) {
+      /* per-pcb cached entry was given */
+      u8_t etharp_cached_entry = *(netif->addr_hint);
+      if (etharp_cached_entry < ARP_TABLE_SIZE) {
+#endif /* LWIP_NETIF_HWADDRHINT */
+        if ((arp_table[etharp_cached_entry].state >= ETHARP_STATE_STABLE) &&
+#if ETHARP_TABLE_MATCH_NETIF
+            (arp_table[etharp_cached_entry].netif == netif) &&
+#endif
+            (ip4_addr_cmp(dst_addr, &arp_table[etharp_cached_entry].ipaddr))) {
+          /* the per-pcb-cached entry is stable and the right one! */
+          ETHARP_STATS_INC(etharp.cachehit);
+          return etharp_output_to_arp_index(netif, q, etharp_cached_entry);
+        }
+#if LWIP_NETIF_HWADDRHINT
+      }
+    }
+#endif /* LWIP_NETIF_HWADDRHINT */
+
+    /* find stable entry: do this here since this is a critical path for
+       throughput and etharp_find_entry() is kind of slow */
+    for (i = 0; i < ARP_TABLE_SIZE; i++) {
+      if ((arp_table[i].state >= ETHARP_STATE_STABLE) &&
+#if ETHARP_TABLE_MATCH_NETIF
+          (arp_table[i].netif == netif) &&
+#endif
+          (ip4_addr_cmp(dst_addr, &arp_table[i].ipaddr))) {
+        /* found an existing, stable entry */
+        ETHARP_SET_HINT(netif, i);
+        return etharp_output_to_arp_index(netif, q, i);
+      }
+    }
+    /* no stable entry found, use the (slower) query function:
+       queue on destination Ethernet address belonging to ipaddr */
+    return etharp_query(netif, dst_addr, q);
+  }
+
+  /* continuation for multicast/broadcast destinations */
+  /* obtain source Ethernet address of the given interface */
+  /* send packet directly on the link */
+  return ethernet_output(netif, q, (struct eth_addr*)(netif->hwaddr), dest, ETHTYPE_IP);
+}
+
+/**
+ * Send an ARP request for the given IP address and/or queue a packet.
+ *
+ * If the IP address was not yet in the cache, a pending ARP cache entry
+ * is added and an ARP request is sent for the given address. The packet
+ * is queued on this entry.
+ *
+ * If the IP address was already pending in the cache, a new ARP request
+ * is sent for the given address. The packet is queued on this entry.
+ *
+ * If the IP address was already stable in the cache, and a packet is
+ * given, it is directly sent and no ARP request is sent out.
+ *
+ * If the IP address was already stable in the cache, and no packet is
+ * given, an ARP request is sent out.
+ *
+ * @param netif The lwIP network interface on which ipaddr
+ * must be queried for.
+ * @param ipaddr The IP address to be resolved.
+ * @param q If non-NULL, a pbuf that must be delivered to the IP address.
+ * q is not freed by this function.
+ *
+ * @note q must only be ONE packet, not a packet queue!
+ *
+ * @return
+ * - ERR_BUF Could not make room for Ethernet header.
+ * - ERR_MEM Hardware address unknown, and no more ARP entries available
+ *   to query for address or queue the packet.
+ * - ERR_MEM Could not queue packet due to memory shortage.
+ * - ERR_RTE No route to destination (no gateway to external networks).
+ * - ERR_ARG Non-unicast address given, those will not appear in ARP cache.
+ *
+ */
+err_t
+etharp_query(struct netif *netif, const ip4_addr_t *ipaddr, struct pbuf *q)
+{
+  struct eth_addr * srcaddr = (struct eth_addr *)netif->hwaddr;
+  err_t result = ERR_MEM;
+  int is_new_entry = 0;
+  s8_t i; /* ARP entry index */
+
+  /* non-unicast address? */
+  if (ip4_addr_isbroadcast(ipaddr, netif) ||
+      ip4_addr_ismulticast(ipaddr) ||
+      ip4_addr_isany(ipaddr)) {
+    LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: will not add non-unicast IP address to ARP cache\n"));
+    return ERR_ARG;
+  }
+
+  /* find entry in ARP cache, ask to create entry if queueing packet */
+  i = etharp_find_entry(ipaddr, ETHARP_FLAG_TRY_HARD, netif);
+
+  /* could not find or create entry? */
+  if (i < 0) {
+    LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: could not create ARP entry\n"));
+    if (q) {
+      LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: packet dropped\n"));
+      ETHARP_STATS_INC(etharp.memerr);
+    }
+    return (err_t)i;
+  }
+
+  /* mark a fresh entry as pending (we just sent a request) */
+  if (arp_table[i].state == ETHARP_STATE_EMPTY) {
+    is_new_entry = 1;
+    arp_table[i].state = ETHARP_STATE_PENDING;
+    /* record network interface for re-sending arp request in etharp_tmr */
+    arp_table[i].netif = netif;
+  }
+
+  /* { i is either a STABLE or (new or existing) PENDING entry } */
+  LWIP_ASSERT("arp_table[i].state == PENDING or STABLE",
+  ((arp_table[i].state == ETHARP_STATE_PENDING) ||
+   (arp_table[i].state >= ETHARP_STATE_STABLE)));
+
+  /* do we have a new entry? or an implicit query request? */
+  if (is_new_entry || (q == NULL)) {
+    /* try to resolve it; send out ARP request */
+    result = etharp_request(netif, ipaddr);
+    if (result != ERR_OK) {
+      /* ARP request couldn't be sent */
+      /* We don't re-send arp request in etharp_tmr, but we still queue packets,
+         since this failure could be temporary, and the next packet calling
+         etharp_query again could lead to sending the queued packets. */
+    }
+    if (q == NULL) {
+      return result;
+    }
+  }
+
+  /* packet given? */
+  LWIP_ASSERT("q != NULL", q != NULL);
+  /* stable entry? */
+  if (arp_table[i].state >= ETHARP_STATE_STABLE) {
+    /* we have a valid IP->Ethernet address mapping */
+    ETHARP_SET_HINT(netif, i);
+    /* send the packet */
+    result = ethernet_output(netif, q, srcaddr, &(arp_table[i].ethaddr), ETHTYPE_IP);
+  /* pending entry? (either just created or already pending */
+  } else if (arp_table[i].state == ETHARP_STATE_PENDING) {
+    /* entry is still pending, queue the given packet 'q' */
+    struct pbuf *p;
+    int copy_needed = 0;
+    /* IF q includes a PBUF_REF, PBUF_POOL or PBUF_RAM, we have no choice but
+     * to copy the whole queue into a new PBUF_RAM (see bug #11400)
+     * PBUF_ROMs can be left as they are, since ROM must not get changed. */
+    p = q;
+    while (p) {
+      LWIP_ASSERT("no packet queues allowed!", (p->len != p->tot_len) || (p->next == 0));
+      if (p->type != PBUF_ROM) {
+        copy_needed = 1;
+        break;
+      }
+      p = p->next;
+    }
+    if (copy_needed) {
+      /* copy the whole packet into new pbufs */
+      p = pbuf_alloc(PBUF_LINK, p->tot_len, PBUF_RAM);
+      if (p != NULL) {
+        if (pbuf_copy(p, q) != ERR_OK) {
+          pbuf_free(p);
+          p = NULL;
+        }
+      }
+    } else {
+      /* referencing the old pbuf is enough */
+      p = q;
+      pbuf_ref(p);
+    }
+    /* packet could be taken over? */
+    if (p != NULL) {
+      /* queue packet ... */
+#if ARP_QUEUEING
+      struct etharp_q_entry *new_entry;
+      /* allocate a new arp queue entry */
+      new_entry = (struct etharp_q_entry *)memp_malloc(MEMP_ARP_QUEUE);
+      if (new_entry != NULL) {
+        unsigned int qlen = 0;
+        new_entry->next = 0;
+        new_entry->p = p;
+        if (arp_table[i].q != NULL) {
+          /* queue was already existent, append the new entry to the end */
+          struct etharp_q_entry *r;
+          r = arp_table[i].q;
+          qlen++;
+          while (r->next != NULL) {
+            r = r->next;
+            qlen++;
+          }
+          r->next = new_entry;
+        } else {
+          /* queue did not exist, first item in queue */
+          arp_table[i].q = new_entry;
+        }
+#if ARP_QUEUE_LEN
+        if (qlen >= ARP_QUEUE_LEN) {
+          struct etharp_q_entry *old;
+          old = arp_table[i].q;
+          arp_table[i].q = arp_table[i].q->next;
+          pbuf_free(old->p);
+          memp_free(MEMP_ARP_QUEUE, old);
+        }
+#endif
+        LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: queued packet %p on ARP entry %"S16_F"\n", (void *)q, (s16_t)i));
+        result = ERR_OK;
+      } else {
+        /* the pool MEMP_ARP_QUEUE is empty */
+        pbuf_free(p);
+        LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: could not queue a copy of PBUF_REF packet %p (out of memory)\n", (void *)q));
+        result = ERR_MEM;
+      }
+#else /* ARP_QUEUEING */
+      /* always queue one packet per ARP request only, freeing a previously queued packet */
+      if (arp_table[i].q != NULL) {
+        LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: dropped previously queued packet %p for ARP entry %"S16_F"\n", (void *)q, (s16_t)i));
+        pbuf_free(arp_table[i].q);
+      }
+      arp_table[i].q = p;
+      result = ERR_OK;
+      LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: queued packet %p on ARP entry %"S16_F"\n", (void *)q, (s16_t)i));
+#endif /* ARP_QUEUEING */
+    } else {
+      ETHARP_STATS_INC(etharp.memerr);
+      LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: could not queue a copy of PBUF_REF packet %p (out of memory)\n", (void *)q));
+      result = ERR_MEM;
+    }
+  }
+  return result;
+}
+
+/**
+ * Send a raw ARP packet (opcode and all addresses can be modified)
+ *
+ * @param netif the lwip network interface on which to send the ARP packet
+ * @param ethsrc_addr the source MAC address for the ethernet header
+ * @param ethdst_addr the destination MAC address for the ethernet header
+ * @param hwsrc_addr the source MAC address for the ARP protocol header
+ * @param ipsrc_addr the source IP address for the ARP protocol header
+ * @param hwdst_addr the destination MAC address for the ARP protocol header
+ * @param ipdst_addr the destination IP address for the ARP protocol header
+ * @param opcode the type of the ARP packet
+ * @return ERR_OK if the ARP packet has been sent
+ *         ERR_MEM if the ARP packet couldn't be allocated
+ *         any other err_t on failure
+ */
+static err_t
+etharp_raw(struct netif *netif, const struct eth_addr *ethsrc_addr,
+           const struct eth_addr *ethdst_addr,
+           const struct eth_addr *hwsrc_addr, const ip4_addr_t *ipsrc_addr,
+           const struct eth_addr *hwdst_addr, const ip4_addr_t *ipdst_addr,
+           const u16_t opcode)
+{
+  struct pbuf *p;
+  err_t result = ERR_OK;
+  struct etharp_hdr *hdr;
+
+  LWIP_ASSERT("netif != NULL", netif != NULL);
+
+  /* allocate a pbuf for the outgoing ARP request packet */
+  p = pbuf_alloc(PBUF_LINK, SIZEOF_ETHARP_HDR, PBUF_RAM);
+  /* could allocate a pbuf for an ARP request? */
+  if (p == NULL) {
+    LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
+      ("etharp_raw: could not allocate pbuf for ARP request.\n"));
+    ETHARP_STATS_INC(etharp.memerr);
+    return ERR_MEM;
+  }
+  LWIP_ASSERT("check that first pbuf can hold struct etharp_hdr",
+              (p->len >= SIZEOF_ETHARP_HDR));
+
+  hdr = (struct etharp_hdr *)p->payload;
+  LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_raw: sending raw ARP packet.\n"));
+  hdr->opcode = lwip_htons(opcode);
+
+  LWIP_ASSERT("netif->hwaddr_len must be the same as ETH_HWADDR_LEN for etharp!",
+              (netif->hwaddr_len == ETH_HWADDR_LEN));
+
+  /* Write the ARP MAC-Addresses */
+  ETHADDR16_COPY(&hdr->shwaddr, hwsrc_addr);
+  ETHADDR16_COPY(&hdr->dhwaddr, hwdst_addr);
+  /* Copy struct ip4_addr2 to aligned ip4_addr, to support compilers without
+   * structure packing. */
+  IPADDR2_COPY(&hdr->sipaddr, ipsrc_addr);
+  IPADDR2_COPY(&hdr->dipaddr, ipdst_addr);
+
+  hdr->hwtype = PP_HTONS(HWTYPE_ETHERNET);
+  hdr->proto = PP_HTONS(ETHTYPE_IP);
+  /* set hwlen and protolen */
+  hdr->hwlen = ETH_HWADDR_LEN;
+  hdr->protolen = sizeof(ip4_addr_t);
+
+  /* send ARP query */
+#if LWIP_AUTOIP
+  /* If we are using Link-Local, all ARP packets that contain a Link-Local
+   * 'sender IP address' MUST be sent using link-layer broadcast instead of
+   * link-layer unicast. (See RFC3927 Section 2.5, last paragraph) */
+  if(ip4_addr_islinklocal(ipsrc_addr)) {
+    ethernet_output(netif, p, ethsrc_addr, &ethbroadcast, ETHTYPE_ARP);
+  } else
+#endif /* LWIP_AUTOIP */
+  {
+    ethernet_output(netif, p, ethsrc_addr, ethdst_addr, ETHTYPE_ARP);
+  }
+
+  ETHARP_STATS_INC(etharp.xmit);
+  /* free ARP query packet */
+  pbuf_free(p);
+  p = NULL;
+  /* could not allocate pbuf for ARP request */
+
+  return result;
+}
+
+/**
+ * Send an ARP request packet asking for ipaddr to a specific eth address.
+ * Used to send unicast request to refresh the ARP table just before an entry
+ * times out
+ *
+ * @param netif the lwip network interface on which to send the request
+ * @param ipaddr the IP address for which to ask
+ * @param hw_dst_addr the ethernet address to send this packet to
+ * @return ERR_OK if the request has been sent
+ *         ERR_MEM if the ARP packet couldn't be allocated
+ *         any other err_t on failure
+ */
+static err_t
+etharp_request_dst(struct netif *netif, const ip4_addr_t *ipaddr, const struct eth_addr* hw_dst_addr)
+{
+  return etharp_raw(netif, (struct eth_addr *)netif->hwaddr, hw_dst_addr,
+                    (struct eth_addr *)netif->hwaddr, netif_ip4_addr(netif), &ethzero,
+                    ipaddr, ARP_REQUEST);
+}
+
+/**
+ * Send an ARP request packet asking for ipaddr.
+ *
+ * @param netif the lwip network interface on which to send the request
+ * @param ipaddr the IP address for which to ask
+ * @return ERR_OK if the request has been sent
+ *         ERR_MEM if the ARP packet couldn't be allocated
+ *         any other err_t on failure
+ */
+err_t
+etharp_request(struct netif *netif, const ip4_addr_t *ipaddr)
+{
+  LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_request: sending ARP request.\n"));
+  return etharp_request_dst(netif, ipaddr, &ethbroadcast);
+}
+#endif /* LWIP_IPV4 && LWIP_ARP */
+
+#endif /* LWIP_ARP || LWIP_ETHERNET */

+ 397 - 355
thirdparty/lwip/src/core/ipv4/icmp.c

@@ -1,355 +1,397 @@
-/**
- * @file
- * ICMP - Internet Control Message Protocol
- *
- */
-
-/*
- * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * This file is part of the lwIP TCP/IP stack.
- *
- * Author: Adam Dunkels <adam@sics.se>
- *
- */
-
-/* Some ICMP messages should be passed to the transport protocols. This
-   is not implemented. */
-
-#include "lwip/opt.h"
-
-#if LWIP_ICMP /* don't build if not configured for use in lwipopts.h */
-
-#include "lwip/icmp.h"
-#include "lwip/inet_chksum.h"
-#include "lwip/ip.h"
-#include "lwip/def.h"
-#include "lwip/stats.h"
-#include "lwip/snmp.h"
-
-#include <string.h>
-
-/** Small optimization: set to 0 if incoming PBUF_POOL pbuf always can be
- * used to modify and send a response packet (and to 1 if this is not the case,
- * e.g. when link header is stripped of when receiving) */
-#ifndef LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN
-#define LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN 1
-#endif /* LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN */
-
-/* The amount of data from the original packet to return in a dest-unreachable */
-#define ICMP_DEST_UNREACH_DATASIZE 8
-
-static void icmp_send_response(struct pbuf *p, u8_t type, u8_t code);
-
-/**
- * Processes ICMP input packets, called from ip_input().
- *
- * Currently only processes icmp echo requests and sends
- * out the echo response.
- *
- * @param p the icmp echo request packet, p->payload pointing to the ip header
- * @param inp the netif on which this packet was received
- */
-void
-icmp_input(struct pbuf *p, struct netif *inp)
-{
-  u8_t type;
-#ifdef LWIP_DEBUG
-  u8_t code;
-#endif /* LWIP_DEBUG */
-  struct icmp_echo_hdr *iecho;
-  struct ip_hdr *iphdr;
-  s16_t hlen;
-
-  ICMP_STATS_INC(icmp.recv);
-  snmp_inc_icmpinmsgs();
-
-
-  iphdr = (struct ip_hdr *)p->payload;
-  hlen = IPH_HL(iphdr) * 4;
-  if (pbuf_header(p, -hlen) || (p->tot_len < sizeof(u16_t)*2)) {
-    LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: short ICMP (%"U16_F" bytes) received\n", p->tot_len));
-    goto lenerr;
-  }
-
-  type = *((u8_t *)p->payload);
-#ifdef LWIP_DEBUG
-  code = *(((u8_t *)p->payload)+1);
-#endif /* LWIP_DEBUG */
-  switch (type) {
-  case ICMP_ER:
-    /* This is OK, echo reply might have been parsed by a raw PCB
-       (as obviously, an echo request has been sent, too). */
-    break; 
-  case ICMP_ECHO:
-#if !LWIP_MULTICAST_PING || !LWIP_BROADCAST_PING
-    {
-      int accepted = 1;
-#if !LWIP_MULTICAST_PING
-      /* multicast destination address? */
-      if (ip_addr_ismulticast(&current_iphdr_dest)) {
-        accepted = 0;
-      }
-#endif /* LWIP_MULTICAST_PING */
-#if !LWIP_BROADCAST_PING
-      /* broadcast destination address? */
-      if (ip_addr_isbroadcast(&current_iphdr_dest, inp)) {
-        accepted = 0;
-      }
-#endif /* LWIP_BROADCAST_PING */
-      /* broadcast or multicast destination address not acceptd? */
-      if (!accepted) {
-        LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: Not echoing to multicast or broadcast pings\n"));
-        ICMP_STATS_INC(icmp.err);
-        pbuf_free(p);
-        return;
-      }
-    }
-#endif /* !LWIP_MULTICAST_PING || !LWIP_BROADCAST_PING */
-    LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ping\n"));
-    if (p->tot_len < sizeof(struct icmp_echo_hdr)) {
-      LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: bad ICMP echo received\n"));
-      goto lenerr;
-    }
-    if (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;
-    }
-#if LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN
-    if (pbuf_header(p, (PBUF_IP_HLEN + PBUF_LINK_HLEN))) {
-      /* p is not big enough to contain link headers
-       * allocate a new one and copy p into it
-       */
-      struct pbuf *r;
-      /* switch p->payload to ip header */
-      if (pbuf_header(p, hlen)) {
-        LWIP_ASSERT("icmp_input: moving p->payload to ip header failed\n", 0);
-        goto memerr;
-      }
-      /* allocate new packet buffer with space for link headers */
-      r = pbuf_alloc(PBUF_LINK, p->tot_len, PBUF_RAM);
-      if (r == NULL) {
-        LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: allocating new pbuf failed\n"));
-        goto memerr;
-      }
-      LWIP_ASSERT("check that first pbuf can hold struct the ICMP header",
-                  (r->len >= hlen + sizeof(struct icmp_echo_hdr)));
-      /* copy the whole packet including ip header */
-      if (pbuf_copy(r, p) != ERR_OK) {
-        LWIP_ASSERT("icmp_input: copying to new pbuf failed\n", 0);
-        goto memerr;
-      }
-      iphdr = (struct ip_hdr *)r->payload;
-      /* switch r->payload back to icmp header */
-      if (pbuf_header(r, -hlen)) {
-        LWIP_ASSERT("icmp_input: restoring original p->payload failed\n", 0);
-        goto memerr;
-      }
-      /* free the original p */
-      pbuf_free(p);
-      /* we now have an identical copy of p that has room for link headers */
-      p = r;
-    } else {
-      /* restore p->payload to point to icmp header */
-      if (pbuf_header(p, -(s16_t)(PBUF_IP_HLEN + PBUF_LINK_HLEN))) {
-        LWIP_ASSERT("icmp_input: restoring original p->payload failed\n", 0);
-        goto memerr;
-      }
-    }
-#endif /* LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN */
-    /* At this point, all checks are OK. */
-    /* We generate an answer by switching the dest and src ip addresses,
-     * setting the icmp type to ECHO_RESPONSE and updating the checksum. */
-    iecho = (struct icmp_echo_hdr *)p->payload;
-    ip_addr_copy(iphdr->src, *ip_current_dest_addr());
-    ip_addr_copy(iphdr->dest, *ip_current_src_addr());
-    ICMPH_TYPE_SET(iecho, ICMP_ER);
-//-----------------------------------------------------------    
-// Добавил фикс 15.08.15.
-// Фикс исправляет баг с пингом.    
-#ifdef CHECKSUM_BY_HARDWARE
-    iecho->chksum = 0;
-#else
-        /* 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);
-    }
-#endif
-//-----------------------------------------------------------    
-    
-/*
-#if CHECKSUM_GEN_ICMP
-    // adjust the checksum 
-    if (iecho->chksum >= PP_HTONS(0xffffU - (ICMP_ECHO << 8))) {
-      iecho->chksum += PP_HTONS(ICMP_ECHO << 8) + 1;
-    } else {
-      iecho->chksum += PP_HTONS(ICMP_ECHO << 8);
-    }
-#else // CHECKSUM_GEN_ICMP 
-    iecho->chksum = 0;
-#endif // CHECKSUM_GEN_ICMP 
-*/
-    /* Set the correct TTL and recalculate the header checksum. */
-    IPH_TTL_SET(iphdr, ICMP_TTL);
-    IPH_CHKSUM_SET(iphdr, 0);
-#if CHECKSUM_GEN_IP
-    IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN));
-#endif /* CHECKSUM_GEN_IP */
-
-    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();
-
-    if(pbuf_header(p, hlen)) {
-      LWIP_ASSERT("Can't move over header in packet", 0);
-    } else {
-      err_t ret;
-      /* send an ICMP packet, src addr is the dest addr of the curren packet */
-      ret = ip_output_if(p, ip_current_dest_addr(), IP_HDRINCL,
-                   ICMP_TTL, 0, IP_PROTO_ICMP, inp);
-      if (ret != ERR_OK) {
-        LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ip_output_if returned an error: %c.\n", ret));
-      }
-    }
-    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);
-  return;
-lenerr:
-  pbuf_free(p);
-  ICMP_STATS_INC(icmp.lenerr);
-  snmp_inc_icmpinerrors();
-  return;
-#if LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN
-memerr:
-  pbuf_free(p);
-  ICMP_STATS_INC(icmp.err);
-  snmp_inc_icmpinerrors();
-  return;
-#endif /* LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN */
-}
-
-/**
- * Send an icmp 'destination unreachable' packet, called from ip_input() if
- * the transport layer protocol is unknown and from udp_input() if the local
- * port is not bound.
- *
- * @param p the input packet for which the 'unreachable' should be sent,
- *          p->payload pointing to the IP header
- * @param t type of the 'unreachable' packet
- */
-void
-icmp_dest_unreach(struct pbuf *p, enum icmp_dur_type t)
-{
-  icmp_send_response(p, ICMP_DUR, t);
-}
-
-#if IP_FORWARD || IP_REASSEMBLY
-/**
- * Send a 'time exceeded' packet, called from ip_forward() if TTL is 0.
- *
- * @param p the input packet for which the 'time exceeded' should be sent,
- *          p->payload pointing to the IP header
- * @param t type of the 'time exceeded' packet
- */
-void
-icmp_time_exceeded(struct pbuf *p, enum icmp_te_type t)
-{
-  icmp_send_response(p, ICMP_TE, t);
-}
-
-#endif /* IP_FORWARD || IP_REASSEMBLY */
-
-/**
- * Send an icmp packet in response to an incoming packet.
- *
- * @param p the input packet for which the 'unreachable' should be sent,
- *          p->payload pointing to the IP header
- * @param type Type of the ICMP header
- * @param code Code of the ICMP header
- */
-static void
-icmp_send_response(struct pbuf *p, u8_t type, u8_t code)
-{
-  struct pbuf *q;
-  struct ip_hdr *iphdr;
-  /* we can use the echo header here */
-  struct icmp_echo_hdr *icmphdr;
-  ip_addr_t iphdr_src;
-
-  /* ICMP header + IP header + 8 bytes of data */
-  q = pbuf_alloc(PBUF_IP, sizeof(struct icmp_echo_hdr) + IP_HLEN + ICMP_DEST_UNREACH_DATASIZE,
-                 PBUF_RAM);
-  if (q == NULL) {
-    LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded: failed to allocate pbuf for ICMP packet.\n"));
-    return;
-  }
-  LWIP_ASSERT("check that first pbuf can hold icmp message",
-             (q->len >= (sizeof(struct icmp_echo_hdr) + IP_HLEN + ICMP_DEST_UNREACH_DATASIZE)));
-
-  iphdr = (struct ip_hdr *)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"));
-
-  icmphdr = (struct icmp_echo_hdr *)q->payload;
-  icmphdr->type = type;
-  icmphdr->code = code;
-  icmphdr->id = 0;
-  icmphdr->seqno = 0;
-
-  /* copy fields from original packet */
-  SMEMCPY((u8_t *)q->payload + sizeof(struct icmp_echo_hdr), (u8_t *)p->payload,
-          IP_HLEN + ICMP_DEST_UNREACH_DATASIZE);
-
-  /* calculate checksum */
-  icmphdr->chksum = 0;
-  icmphdr->chksum = inet_chksum(icmphdr, 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_addr_copy(iphdr_src, iphdr->src);
-  ip_output(q, NULL, &iphdr_src, ICMP_TTL, 0, IP_PROTO_ICMP);
-  pbuf_free(q);
-}
-
-#endif /* LWIP_ICMP */
+/**
+ * @file
+ * ICMP - Internet Control Message Protocol
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+/* Some ICMP messages should be passed to the transport protocols. This
+   is not implemented. */
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV4 && LWIP_ICMP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/icmp.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/ip.h"
+#include "lwip/def.h"
+#include "lwip/stats.h"
+
+#include <string.h>
+
+#ifdef LWIP_HOOK_FILENAME
+#include LWIP_HOOK_FILENAME
+#endif
+
+/** Small optimization: set to 0 if incoming PBUF_POOL pbuf always can be
+ * used to modify and send a response packet (and to 1 if this is not the case,
+ * e.g. when link header is stripped of when receiving) */
+#ifndef LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN
+#define LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN 1
+#endif /* LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN */
+
+/* The amount of data from the original packet to return in a dest-unreachable */
+#define ICMP_DEST_UNREACH_DATASIZE 8
+
+static void icmp_send_response(struct pbuf *p, u8_t type, u8_t code);
+
+/**
+ * Processes ICMP input packets, called from ip_input().
+ *
+ * Currently only processes icmp echo requests and sends
+ * out the echo response.
+ *
+ * @param p the icmp echo request packet, p->payload pointing to the icmp header
+ * @param inp the netif on which this packet was received
+ */
+void
+icmp_input(struct pbuf *p, struct netif *inp)
+{
+  u8_t type;
+#ifdef LWIP_DEBUG
+  u8_t code;
+#endif /* LWIP_DEBUG */
+  struct icmp_echo_hdr *iecho;
+  const struct ip_hdr *iphdr_in;
+  u16_t hlen;
+  const ip4_addr_t* src;
+
+  ICMP_STATS_INC(icmp.recv);
+  MIB2_STATS_INC(mib2.icmpinmsgs);
+
+  iphdr_in = ip4_current_header();
+  hlen = IPH_HL(iphdr_in) * 4;
+  if (hlen < IP_HLEN) {
+    LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: short IP header (%"S16_F" bytes) received\n", hlen));
+    goto lenerr;
+  }
+  if (p->len < sizeof(u16_t)*2) {
+    LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: short ICMP (%"U16_F" bytes) received\n", p->tot_len));
+    goto lenerr;
+  }
+
+  type = *((u8_t *)p->payload);
+#ifdef LWIP_DEBUG
+  code = *(((u8_t *)p->payload)+1);
+#endif /* LWIP_DEBUG */
+  switch (type) {
+  case ICMP_ER:
+    /* This is OK, echo reply might have been parsed by a raw PCB
+       (as obviously, an echo request has been sent, too). */
+    MIB2_STATS_INC(mib2.icmpinechoreps);
+    break;
+  case ICMP_ECHO:
+    MIB2_STATS_INC(mib2.icmpinechos);
+    src = ip4_current_dest_addr();
+    /* multicast destination address? */
+    if (ip4_addr_ismulticast(ip4_current_dest_addr())) {
+#if LWIP_MULTICAST_PING
+      /* For multicast, use address of receiving interface as source address */
+      src = netif_ip4_addr(inp);
+#else /* LWIP_MULTICAST_PING */
+      LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: Not echoing to multicast pings\n"));
+      goto icmperr;
+#endif /* LWIP_MULTICAST_PING */
+    }
+    /* broadcast destination address? */
+    if (ip4_addr_isbroadcast(ip4_current_dest_addr(), ip_current_netif())) {
+#if LWIP_BROADCAST_PING
+      /* For broadcast, use address of receiving interface as source address */
+      src = netif_ip4_addr(inp);
+#else /* LWIP_BROADCAST_PING */
+      LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: Not echoing to broadcast pings\n"));
+      goto icmperr;
+#endif /* LWIP_BROADCAST_PING */
+    }
+    LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ping\n"));
+    if (p->tot_len < sizeof(struct icmp_echo_hdr)) {
+      LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: bad ICMP echo received\n"));
+      goto lenerr;
+    }
+#if CHECKSUM_CHECK_ICMP
+    IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_CHECK_ICMP) {
+      if (inet_chksum_pbuf(p) != 0) {
+        LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: checksum failed for received ICMP echo\n"));
+        pbuf_free(p);
+        ICMP_STATS_INC(icmp.chkerr);
+        MIB2_STATS_INC(mib2.icmpinerrors);
+        return;
+      }
+    }
+#endif
+#if LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN
+    if (pbuf_header(p, (s16_t)(hlen + PBUF_LINK_HLEN + PBUF_LINK_ENCAPSULATION_HLEN))) {
+      /* p is not big enough to contain link headers
+       * allocate a new one and copy p into it
+       */
+      struct pbuf *r;
+      /* allocate new packet buffer with space for link headers */
+      r = pbuf_alloc(PBUF_LINK, p->tot_len + hlen, PBUF_RAM);
+      if (r == NULL) {
+        LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: allocating new pbuf failed\n"));
+        goto icmperr;
+      }
+      if (r->len < hlen + sizeof(struct icmp_echo_hdr)) {
+        LWIP_DEBUGF(ICMP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("first pbuf cannot hold the ICMP header"));
+        pbuf_free(r);
+        goto icmperr;
+      }
+      /* copy the ip header */
+      MEMCPY(r->payload, iphdr_in, hlen);
+      /* switch r->payload back to icmp header (cannot fail) */
+      if (pbuf_header(r, (s16_t)-hlen)) {
+        LWIP_ASSERT("icmp_input: moving r->payload to icmp header failed\n", 0);
+        pbuf_free(r);
+        goto icmperr;
+      }
+      /* copy the rest of the packet without ip header */
+      if (pbuf_copy(r, p) != ERR_OK) {
+        LWIP_DEBUGF(ICMP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("icmp_input: copying to new pbuf failed"));
+        pbuf_free(r);
+        goto icmperr;
+      }
+      /* free the original p */
+      pbuf_free(p);
+      /* we now have an identical copy of p that has room for link headers */
+      p = r;
+    } else {
+      /* restore p->payload to point to icmp header (cannot fail) */
+      if (pbuf_header(p, -(s16_t)(hlen + PBUF_LINK_HLEN + PBUF_LINK_ENCAPSULATION_HLEN))) {
+        LWIP_ASSERT("icmp_input: restoring original p->payload failed\n", 0);
+        goto icmperr;
+      }
+    }
+#endif /* LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN */
+    /* At this point, all checks are OK. */
+    /* We generate an answer by switching the dest and src ip addresses,
+     * setting the icmp type to ECHO_RESPONSE and updating the checksum. */
+    iecho = (struct icmp_echo_hdr *)p->payload;
+    if (pbuf_header(p, (s16_t)hlen)) {
+      LWIP_DEBUGF(ICMP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("Can't move over header in packet"));
+    } else {
+      err_t ret;
+      struct ip_hdr *iphdr = (struct ip_hdr*)p->payload;
+      ip4_addr_copy(iphdr->src, *src);
+      ip4_addr_copy(iphdr->dest, *ip4_current_src_addr());
+      ICMPH_TYPE_SET(iecho, ICMP_ER);
+#if CHECKSUM_GEN_ICMP
+      IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_GEN_ICMP) {
+        /* adjust the checksum */
+        if (iecho->chksum > PP_HTONS(0xffffU - (ICMP_ECHO << 8))) {
+          iecho->chksum += PP_HTONS(ICMP_ECHO << 8) + 1;
+        } else {
+          iecho->chksum += PP_HTONS(ICMP_ECHO << 8);
+        }
+      }
+#if LWIP_CHECKSUM_CTRL_PER_NETIF
+      else {
+        iecho->chksum = 0;
+      }
+#endif /* LWIP_CHECKSUM_CTRL_PER_NETIF */
+#else /* CHECKSUM_GEN_ICMP */
+      iecho->chksum = 0;
+#endif /* CHECKSUM_GEN_ICMP */
+
+      /* Set the correct TTL and recalculate the header checksum. */
+      IPH_TTL_SET(iphdr, ICMP_TTL);
+      IPH_CHKSUM_SET(iphdr, 0);
+#if CHECKSUM_GEN_IP
+      IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_GEN_IP) {
+        IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, hlen));
+      }
+#endif /* CHECKSUM_GEN_IP */
+
+      ICMP_STATS_INC(icmp.xmit);
+      /* increase number of messages attempted to send */
+      MIB2_STATS_INC(mib2.icmpoutmsgs);
+      /* increase number of echo replies attempted to send */
+      MIB2_STATS_INC(mib2.icmpoutechoreps);
+
+      /* send an ICMP packet */
+      ret = ip4_output_if(p, src, LWIP_IP_HDRINCL,
+                   ICMP_TTL, 0, IP_PROTO_ICMP, inp);
+      if (ret != ERR_OK) {
+        LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ip_output_if returned an error: %s\n", lwip_strerr(ret)));
+      }
+    }
+    break;
+  default:
+    if (type == ICMP_DUR) {
+      MIB2_STATS_INC(mib2.icmpindestunreachs);
+    } else if (type == ICMP_TE) {
+      MIB2_STATS_INC(mib2.icmpintimeexcds);
+    } else if (type == ICMP_PP) {
+      MIB2_STATS_INC(mib2.icmpinparmprobs);
+    } else if (type == ICMP_SQ) {
+      MIB2_STATS_INC(mib2.icmpinsrcquenchs);
+    } else if (type == ICMP_RD) {
+      MIB2_STATS_INC(mib2.icmpinredirects);
+    } else if (type == ICMP_TS) {
+      MIB2_STATS_INC(mib2.icmpintimestamps);
+    } else if (type == ICMP_TSR) {
+      MIB2_STATS_INC(mib2.icmpintimestampreps);
+    } else if (type == ICMP_AM) {
+      MIB2_STATS_INC(mib2.icmpinaddrmasks);
+    } else if (type == ICMP_AMR) {
+      MIB2_STATS_INC(mib2.icmpinaddrmaskreps);
+    }
+    LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ICMP type %"S16_F" code %"S16_F" not supported.\n",
+                (s16_t)type, (s16_t)code));
+    ICMP_STATS_INC(icmp.proterr);
+    ICMP_STATS_INC(icmp.drop);
+  }
+  pbuf_free(p);
+  return;
+lenerr:
+  pbuf_free(p);
+  ICMP_STATS_INC(icmp.lenerr);
+  MIB2_STATS_INC(mib2.icmpinerrors);
+  return;
+#if LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN || !LWIP_MULTICAST_PING || !LWIP_BROADCAST_PING
+icmperr:
+  pbuf_free(p);
+  ICMP_STATS_INC(icmp.err);
+  MIB2_STATS_INC(mib2.icmpinerrors);
+  return;
+#endif /* LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN || !LWIP_MULTICAST_PING || !LWIP_BROADCAST_PING */
+}
+
+/**
+ * Send an icmp 'destination unreachable' packet, called from ip_input() if
+ * the transport layer protocol is unknown and from udp_input() if the local
+ * port is not bound.
+ *
+ * @param p the input packet for which the 'unreachable' should be sent,
+ *          p->payload pointing to the IP header
+ * @param t type of the 'unreachable' packet
+ */
+void
+icmp_dest_unreach(struct pbuf *p, enum icmp_dur_type t)
+{
+  MIB2_STATS_INC(mib2.icmpoutdestunreachs);
+  icmp_send_response(p, ICMP_DUR, t);
+}
+
+#if IP_FORWARD || IP_REASSEMBLY
+/**
+ * Send a 'time exceeded' packet, called from ip_forward() if TTL is 0.
+ *
+ * @param p the input packet for which the 'time exceeded' should be sent,
+ *          p->payload pointing to the IP header
+ * @param t type of the 'time exceeded' packet
+ */
+void
+icmp_time_exceeded(struct pbuf *p, enum icmp_te_type t)
+{
+  MIB2_STATS_INC(mib2.icmpouttimeexcds);
+  icmp_send_response(p, ICMP_TE, t);
+}
+
+#endif /* IP_FORWARD || IP_REASSEMBLY */
+
+/**
+ * Send an icmp packet in response to an incoming packet.
+ *
+ * @param p the input packet for which the 'unreachable' should be sent,
+ *          p->payload pointing to the IP header
+ * @param type Type of the ICMP header
+ * @param code Code of the ICMP header
+ */
+static void
+icmp_send_response(struct pbuf *p, u8_t type, u8_t code)
+{
+  struct pbuf *q;
+  struct ip_hdr *iphdr;
+  /* we can use the echo header here */
+  struct icmp_echo_hdr *icmphdr;
+  ip4_addr_t iphdr_src;
+  struct netif *netif;
+
+  /* increase number of messages attempted to send */
+  MIB2_STATS_INC(mib2.icmpoutmsgs);
+
+  /* ICMP header + IP header + 8 bytes of data */
+  q = pbuf_alloc(PBUF_IP, sizeof(struct icmp_echo_hdr) + IP_HLEN + ICMP_DEST_UNREACH_DATASIZE,
+                 PBUF_RAM);
+  if (q == NULL) {
+    LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded: failed to allocate pbuf for ICMP packet.\n"));
+    MIB2_STATS_INC(mib2.icmpouterrors);
+    return;
+  }
+  LWIP_ASSERT("check that first pbuf can hold icmp message",
+             (q->len >= (sizeof(struct icmp_echo_hdr) + IP_HLEN + ICMP_DEST_UNREACH_DATASIZE)));
+
+  iphdr = (struct ip_hdr *)p->payload;
+  LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded from "));
+  ip4_addr_debug_print_val(ICMP_DEBUG, iphdr->src);
+  LWIP_DEBUGF(ICMP_DEBUG, (" to "));
+  ip4_addr_debug_print_val(ICMP_DEBUG, iphdr->dest);
+  LWIP_DEBUGF(ICMP_DEBUG, ("\n"));
+
+  icmphdr = (struct icmp_echo_hdr *)q->payload;
+  icmphdr->type = type;
+  icmphdr->code = code;
+  icmphdr->id = 0;
+  icmphdr->seqno = 0;
+
+  /* copy fields from original packet */
+  SMEMCPY((u8_t *)q->payload + sizeof(struct icmp_echo_hdr), (u8_t *)p->payload,
+          IP_HLEN + ICMP_DEST_UNREACH_DATASIZE);
+
+  ip4_addr_copy(iphdr_src, iphdr->src);
+#ifdef LWIP_HOOK_IP4_ROUTE_SRC
+  {
+    ip4_addr_t iphdr_dst;
+    ip4_addr_copy(iphdr_dst, iphdr->dest);
+    netif = ip4_route_src(&iphdr_src, &iphdr_dst);
+  }
+#else
+  netif = ip4_route(&iphdr_src);
+#endif
+  if (netif != NULL) {
+    /* calculate checksum */
+    icmphdr->chksum = 0;
+#if CHECKSUM_GEN_ICMP
+    IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_ICMP) {
+      icmphdr->chksum = inet_chksum(icmphdr, q->len);
+    }
+#endif
+    ICMP_STATS_INC(icmp.xmit);
+    ip4_output_if(q, NULL, &iphdr_src, ICMP_TTL, 0, IP_PROTO_ICMP, netif);
+  }
+  pbuf_free(q);
+}
+
+#endif /* LWIP_IPV4 && LWIP_ICMP */

+ 800 - 805
thirdparty/lwip/src/core/ipv4/igmp.c

@@ -1,805 +1,800 @@
-/**
- * @file
- * IGMP - Internet Group Management Protocol
- *
- */
-
-/*
- * Copyright (c) 2002 CITEL Technologies Ltd.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without 
- * modification, are permitted provided that the following conditions 
- * are met: 
- * 1. Redistributions of source code must retain the above copyright 
- *    notice, this list of conditions and the following disclaimer. 
- * 2. Redistributions in binary form must reproduce the above copyright 
- *    notice, this list of conditions and the following disclaimer in the 
- *    documentation and/or other materials provided with the distribution. 
- * 3. Neither the name of CITEL Technologies Ltd nor the names of its contributors 
- *    may be used to endorse or promote products derived from this software 
- *    without specific prior written permission. 
- *
- * THIS SOFTWARE IS PROVIDED BY CITEL TECHNOLOGIES AND CONTRIBUTORS ``AS IS''
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
- * ARE DISCLAIMED.  IN NO EVENT SHALL CITEL TECHNOLOGIES OR CONTRIBUTORS BE LIABLE 
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
- * SUCH DAMAGE. 
- *
- * This file is a contribution to the lwIP TCP/IP stack.
- * The Swedish Institute of Computer Science and Adam Dunkels
- * are specifically granted permission to redistribute this
- * source code.
-*/
-
-/*-------------------------------------------------------------
-Note 1)
-Although the rfc requires V1 AND V2 capability
-we will only support v2 since now V1 is very old (August 1989)
-V1 can be added if required
-
-a debug print and statistic have been implemented to
-show this up.
--------------------------------------------------------------
--------------------------------------------------------------
-Note 2)
-A query for a specific group address (as opposed to ALLHOSTS)
-has now been implemented as I am unsure if it is required
-
-a debug print and statistic have been implemented to
-show this up.
--------------------------------------------------------------
--------------------------------------------------------------
-Note 3)
-The router alert rfc 2113 is implemented in outgoing packets
-but not checked rigorously incoming
--------------------------------------------------------------
-Steve Reynolds
-------------------------------------------------------------*/
-
-/*-----------------------------------------------------------------------------
- * RFC 988  - Host extensions for IP multicasting                         - V0
- * RFC 1054 - Host extensions for IP multicasting                         -
- * RFC 1112 - Host extensions for IP multicasting                         - V1
- * RFC 2236 - Internet Group Management Protocol, Version 2               - V2  <- this code is based on this RFC (it's the "de facto" standard)
- * RFC 3376 - Internet Group Management Protocol, Version 3               - V3
- * RFC 4604 - Using Internet Group Management Protocol Version 3...       - V3+
- * RFC 2113 - IP Router Alert Option                                      - 
- *----------------------------------------------------------------------------*/
-
-/*-----------------------------------------------------------------------------
- * Includes
- *----------------------------------------------------------------------------*/
-
-#include "lwip/opt.h"
-
-#if LWIP_IGMP /* don't build if not configured for use in lwipopts.h */
-
-#include "lwip/igmp.h"
-#include "lwip/debug.h"
-#include "lwip/def.h"
-#include "lwip/mem.h"
-#include "lwip/ip.h"
-#include "lwip/inet_chksum.h"
-#include "lwip/netif.h"
-#include "lwip/icmp.h"
-#include "lwip/udp.h"
-#include "lwip/tcp.h"
-#include "lwip/stats.h"
-
-#include "string.h"
-
-/* 
- * IGMP constants
- */
-#define IGMP_TTL                       1
-#define IGMP_MINLEN                    8
-#define ROUTER_ALERT                   0x9404U
-#define ROUTER_ALERTLEN                4
-
-/*
- * IGMP message types, including version number.
- */
-#define IGMP_MEMB_QUERY                0x11 /* Membership query         */
-#define IGMP_V1_MEMB_REPORT            0x12 /* Ver. 1 membership report */
-#define IGMP_V2_MEMB_REPORT            0x16 /* Ver. 2 membership report */
-#define IGMP_LEAVE_GROUP               0x17 /* Leave-group message      */
-
-/* Group  membership states */
-#define IGMP_GROUP_NON_MEMBER          0
-#define IGMP_GROUP_DELAYING_MEMBER     1
-#define IGMP_GROUP_IDLE_MEMBER         2
-
-/**
- * IGMP packet format.
- */
-#ifdef PACK_STRUCT_USE_INCLUDES
-#  include "arch/bpstruct.h"
-#endif
-PACK_STRUCT_BEGIN
-struct igmp_msg {
- PACK_STRUCT_FIELD(u8_t           igmp_msgtype);
- PACK_STRUCT_FIELD(u8_t           igmp_maxresp);
- PACK_STRUCT_FIELD(u16_t          igmp_checksum);
- PACK_STRUCT_FIELD(ip_addr_p_t    igmp_group_address);
-} PACK_STRUCT_STRUCT;
-PACK_STRUCT_END
-#ifdef PACK_STRUCT_USE_INCLUDES
-#  include "arch/epstruct.h"
-#endif
-
-
-static struct igmp_group *igmp_lookup_group(struct netif *ifp, ip_addr_t *addr);
-static err_t  igmp_remove_group(struct igmp_group *group);
-static void   igmp_timeout( struct igmp_group *group);
-static void   igmp_start_timer(struct igmp_group *group, u8_t max_time);
-static void   igmp_delaying_member(struct igmp_group *group, u8_t maxresp);
-static err_t  igmp_ip_output_if(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, struct netif *netif);
-static void   igmp_send(struct igmp_group *group, u8_t type);
-
-
-static struct igmp_group* igmp_group_list;
-static ip_addr_t     allsystems;
-static ip_addr_t     allrouters;
-
-
-/**
- * Initialize the IGMP module
- */
-void
-igmp_init(void)
-{
-  LWIP_DEBUGF(IGMP_DEBUG, ("igmp_init: initializing\n"));
-
-  IP4_ADDR(&allsystems, 224, 0, 0, 1);
-  IP4_ADDR(&allrouters, 224, 0, 0, 2);
-}
-
-#ifdef LWIP_DEBUG
-/**
- * Dump global IGMP groups list
- */
-void
-igmp_dump_group_list()
-{ 
-  struct igmp_group *group = igmp_group_list;
-
-  while (group != NULL) {
-    LWIP_DEBUGF(IGMP_DEBUG, ("igmp_dump_group_list: [%"U32_F"] ", (u32_t)(group->group_state)));
-    ip_addr_debug_print(IGMP_DEBUG, &group->group_address);
-    LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", group->netif));
-    group = group->next;
-  }
-  LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
-}
-#else
-#define igmp_dump_group_list()
-#endif /* LWIP_DEBUG */
-
-/**
- * Start IGMP processing on interface
- *
- * @param netif network interface on which start IGMP processing
- */
-err_t
-igmp_start(struct netif *netif)
-{
-  struct igmp_group* group;
-
-  LWIP_DEBUGF(IGMP_DEBUG, ("igmp_start: starting IGMP processing on if %p\n", netif));
-
-  group = igmp_lookup_group(netif, &allsystems);
-
-  if (group != NULL) {
-    group->group_state = IGMP_GROUP_IDLE_MEMBER;
-    group->use++;
-
-    /* Allow the igmp messages at the MAC level */
-    if (netif->igmp_mac_filter != NULL) {
-      LWIP_DEBUGF(IGMP_DEBUG, ("igmp_start: igmp_mac_filter(ADD "));
-      ip_addr_debug_print(IGMP_DEBUG, &allsystems);
-      LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif));
-      netif->igmp_mac_filter(netif, &allsystems, IGMP_ADD_MAC_FILTER);
-    }
-
-    return ERR_OK;
-  }
-
-  return ERR_MEM;
-}
-
-/**
- * Stop IGMP processing on interface
- *
- * @param netif network interface on which stop IGMP processing
- */
-err_t
-igmp_stop(struct netif *netif)
-{
-  struct igmp_group *group = igmp_group_list;
-  struct igmp_group *prev  = NULL;
-  struct igmp_group *next;
-
-  /* look for groups joined on this interface further down the list */
-  while (group != NULL) {
-    next = group->next;
-    /* is it a group joined on this interface? */
-    if (group->netif == netif) {
-      /* is it the first group of the list? */
-      if (group == igmp_group_list) {
-        igmp_group_list = next;
-      }
-      /* is there a "previous" group defined? */
-      if (prev != NULL) {
-        prev->next = next;
-      }
-      /* disable the group at the MAC level */
-      if (netif->igmp_mac_filter != NULL) {
-        LWIP_DEBUGF(IGMP_DEBUG, ("igmp_stop: igmp_mac_filter(DEL "));
-        ip_addr_debug_print(IGMP_DEBUG, &group->group_address);
-        LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif));
-        netif->igmp_mac_filter(netif, &(group->group_address), IGMP_DEL_MAC_FILTER);
-      }
-      /* free group */
-      memp_free(MEMP_IGMP_GROUP, group);
-    } else {
-      /* change the "previous" */
-      prev = group;
-    }
-    /* move to "next" */
-    group = next;
-  }
-  return ERR_OK;
-}
-
-/**
- * Report IGMP memberships for this interface
- *
- * @param netif network interface on which report IGMP memberships
- */
-void
-igmp_report_groups(struct netif *netif)
-{
-  struct igmp_group *group = igmp_group_list;
-
-  LWIP_DEBUGF(IGMP_DEBUG, ("igmp_report_groups: sending IGMP reports on if %p\n", netif));
-
-  while (group != NULL) {
-    if (group->netif == netif) {
-      igmp_delaying_member(group, IGMP_JOIN_DELAYING_MEMBER_TMR);
-    }
-    group = group->next;
-  }
-}
-
-/**
- * Search for a group in the global igmp_group_list
- *
- * @param ifp the network interface for which to look
- * @param addr the group ip address to search for
- * @return a struct igmp_group* if the group has been found,
- *         NULL if the group wasn't found.
- */
-struct igmp_group *
-igmp_lookfor_group(struct netif *ifp, ip_addr_t *addr)
-{
-  struct igmp_group *group = igmp_group_list;
-
-  while (group != NULL) {
-    if ((group->netif == ifp) && (ip_addr_cmp(&(group->group_address), addr))) {
-      return group;
-    }
-    group = group->next;
-  }
-
-  /* to be clearer, we return NULL here instead of
-   * 'group' (which is also NULL at this point).
-   */
-  return NULL;
-}
-
-/**
- * Search for a specific igmp group and create a new one if not found-
- *
- * @param ifp the network interface for which to look
- * @param addr the group ip address to search
- * @return a struct igmp_group*,
- *         NULL on memory error.
- */
-struct igmp_group *
-igmp_lookup_group(struct netif *ifp, ip_addr_t *addr)
-{
-  struct igmp_group *group = igmp_group_list;
-  
-  /* Search if the group already exists */
-  group = igmp_lookfor_group(ifp, addr);
-  if (group != NULL) {
-    /* Group already exists. */
-    return group;
-  }
-
-  /* Group doesn't exist yet, create a new one */
-  group = (struct igmp_group *)memp_malloc(MEMP_IGMP_GROUP);
-  if (group != NULL) {
-    group->netif              = ifp;
-    ip_addr_set(&(group->group_address), addr);
-    group->timer              = 0; /* Not running */
-    group->group_state        = IGMP_GROUP_NON_MEMBER;
-    group->last_reporter_flag = 0;
-    group->use                = 0;
-    group->next               = igmp_group_list;
-    
-    igmp_group_list = group;
-  }
-
-  LWIP_DEBUGF(IGMP_DEBUG, ("igmp_lookup_group: %sallocated a new group with address ", (group?"":"impossible to ")));
-  ip_addr_debug_print(IGMP_DEBUG, addr);
-  LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", ifp));
-
-  return group;
-}
-
-/**
- * Remove a group in the global igmp_group_list
- *
- * @param group the group to remove from the global igmp_group_list
- * @return ERR_OK if group was removed from the list, an err_t otherwise
- */
-static err_t
-igmp_remove_group(struct igmp_group *group)
-{
-  err_t err = ERR_OK;
-
-  /* Is it the first group? */
-  if (igmp_group_list == group) {
-    igmp_group_list = group->next;
-  } else {
-    /* look for group further down the list */
-    struct igmp_group *tmpGroup;
-    for (tmpGroup = igmp_group_list; tmpGroup != NULL; tmpGroup = tmpGroup->next) {
-      if (tmpGroup->next == group) {
-        tmpGroup->next = group->next;
-        break;
-      }
-    }
-    /* Group not found in the global igmp_group_list */
-    if (tmpGroup == NULL)
-      err = ERR_ARG;
-  }
-  /* free group */
-  memp_free(MEMP_IGMP_GROUP, group);
-
-  return err;
-}
-
-/**
- * Called from ip_input() if a new IGMP packet is received.
- *
- * @param p received igmp packet, p->payload pointing to the ip header
- * @param inp network interface on which the packet was received
- * @param dest destination ip address of the igmp packet
- */
-void
-igmp_input(struct pbuf *p, struct netif *inp, ip_addr_t *dest)
-{
-  struct ip_hdr *    iphdr;
-  struct igmp_msg*   igmp;
-  struct igmp_group* group;
-  struct igmp_group* groupref;
-
-  IGMP_STATS_INC(igmp.recv);
-
-  /* Note that the length CAN be greater than 8 but only 8 are used - All are included in the checksum */    
-  iphdr = (struct ip_hdr *)p->payload;
-  if (pbuf_header(p, -(s16_t)(IPH_HL(iphdr) * 4)) || (p->len < IGMP_MINLEN)) {
-    pbuf_free(p);
-    IGMP_STATS_INC(igmp.lenerr);
-    LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: length error\n"));
-    return;
-  }
-
-  LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: message from "));
-  ip_addr_debug_print(IGMP_DEBUG, &(iphdr->src));
-  LWIP_DEBUGF(IGMP_DEBUG, (" to address "));
-  ip_addr_debug_print(IGMP_DEBUG, &(iphdr->dest));
-  LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", inp));
-
-  /* Now calculate and check the checksum */
-  igmp = (struct igmp_msg *)p->payload;
-  if (inet_chksum(igmp, p->len)) {
-    pbuf_free(p);
-    IGMP_STATS_INC(igmp.chkerr);
-    LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: checksum error\n"));
-    return;
-  }
-
-  /* Packet is ok so find an existing group */
-  group = igmp_lookfor_group(inp, dest); /* use the destination IP address of incoming packet */
-  
-  /* If group can be found or create... */
-  if (!group) {
-    pbuf_free(p);
-    IGMP_STATS_INC(igmp.drop);
-    LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP frame not for us\n"));
-    return;
-  }
-
-  /* NOW ACT ON THE INCOMING MESSAGE TYPE... */
-  switch (igmp->igmp_msgtype) {
-   case IGMP_MEMB_QUERY: {
-     /* IGMP_MEMB_QUERY to the "all systems" address ? */
-     if ((ip_addr_cmp(dest, &allsystems)) && ip_addr_isany(&igmp->igmp_group_address)) {
-       /* THIS IS THE GENERAL QUERY */
-       LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: General IGMP_MEMB_QUERY on \"ALL SYSTEMS\" address (224.0.0.1) [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp)));
-
-       if (igmp->igmp_maxresp == 0) {
-         IGMP_STATS_INC(igmp.rx_v1);
-         LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: got an all hosts query with time== 0 - this is V1 and not implemented - treat as v2\n"));
-         igmp->igmp_maxresp = IGMP_V1_DELAYING_MEMBER_TMR;
-       } else {
-         IGMP_STATS_INC(igmp.rx_general);
-       }
-
-       groupref = igmp_group_list;
-       while (groupref) {
-         /* Do not send messages on the all systems group address! */
-         if ((groupref->netif == inp) && (!(ip_addr_cmp(&(groupref->group_address), &allsystems)))) {
-           igmp_delaying_member(groupref, igmp->igmp_maxresp);
-         }
-         groupref = groupref->next;
-       }
-     } else {
-       /* IGMP_MEMB_QUERY to a specific group ? */
-       if (!ip_addr_isany(&igmp->igmp_group_address)) {
-         LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_MEMB_QUERY to a specific group "));
-         ip_addr_debug_print(IGMP_DEBUG, &igmp->igmp_group_address);
-         if (ip_addr_cmp(dest, &allsystems)) {
-           ip_addr_t groupaddr;
-           LWIP_DEBUGF(IGMP_DEBUG, (" using \"ALL SYSTEMS\" address (224.0.0.1) [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp)));
-           /* we first need to re-look for the group since we used dest last time */
-           ip_addr_copy(groupaddr, igmp->igmp_group_address);
-           group = igmp_lookfor_group(inp, &groupaddr);
-         } else {
-           LWIP_DEBUGF(IGMP_DEBUG, (" with the group address as destination [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp)));
-         }
-
-         if (group != NULL) {
-           IGMP_STATS_INC(igmp.rx_group);
-           igmp_delaying_member(group, igmp->igmp_maxresp);
-         } else {
-           IGMP_STATS_INC(igmp.drop);
-         }
-       } else {
-         IGMP_STATS_INC(igmp.proterr);
-       }
-     }
-     break;
-   }
-   case IGMP_V2_MEMB_REPORT: {
-     LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_V2_MEMB_REPORT\n"));
-     IGMP_STATS_INC(igmp.rx_report);
-     if (group->group_state == IGMP_GROUP_DELAYING_MEMBER) {
-       /* This is on a specific group we have already looked up */
-       group->timer = 0; /* stopped */
-       group->group_state = IGMP_GROUP_IDLE_MEMBER;
-       group->last_reporter_flag = 0;
-     }
-     break;
-   }
-   default: {
-     LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: unexpected msg %d in state %d on group %p on if %p\n",
-       igmp->igmp_msgtype, group->group_state, &group, group->netif));
-     IGMP_STATS_INC(igmp.proterr);
-     break;
-   }
-  }
-
-  pbuf_free(p);
-  return;
-}
-
-/**
- * Join a group on one network interface.
- *
- * @param ifaddr ip address of the network interface which should join a new group
- * @param groupaddr the ip address of the group which to join
- * @return ERR_OK if group was joined on the netif(s), an err_t otherwise
- */
-err_t
-igmp_joingroup(ip_addr_t *ifaddr, ip_addr_t *groupaddr)
-{
-  err_t              err = ERR_VAL; /* no matching interface */
-  struct igmp_group *group;
-  struct netif      *netif;
-
-  /* make sure it is multicast address */
-  LWIP_ERROR("igmp_joingroup: attempt to join non-multicast address", ip_addr_ismulticast(groupaddr), return ERR_VAL;);
-  LWIP_ERROR("igmp_joingroup: attempt to join allsystems address", (!ip_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;);
-
-  /* loop through netif's */
-  netif = netif_list;
-  while (netif != NULL) {
-    /* Should we join this interface ? */
-    if ((netif->flags & NETIF_FLAG_IGMP) && ((ip_addr_isany(ifaddr) || ip_addr_cmp(&(netif->ip_addr), ifaddr)))) {
-      /* find group or create a new one if not found */
-      group = igmp_lookup_group(netif, groupaddr);
-
-      if (group != NULL) {
-        /* This should create a new group, check the state to make sure */
-        if (group->group_state != IGMP_GROUP_NON_MEMBER) {
-          LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: join to group not in state IGMP_GROUP_NON_MEMBER\n"));
-        } else {
-          /* OK - it was new group */
-          LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: join to new group: "));
-          ip_addr_debug_print(IGMP_DEBUG, groupaddr);
-          LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
-
-          /* If first use of the group, allow the group at the MAC level */
-          if ((group->use==0) && (netif->igmp_mac_filter != NULL)) {
-            LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: igmp_mac_filter(ADD "));
-            ip_addr_debug_print(IGMP_DEBUG, groupaddr);
-            LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif));
-            netif->igmp_mac_filter(netif, groupaddr, IGMP_ADD_MAC_FILTER);
-          }
-
-          IGMP_STATS_INC(igmp.tx_join);
-          igmp_send(group, IGMP_V2_MEMB_REPORT);
-
-          igmp_start_timer(group, IGMP_JOIN_DELAYING_MEMBER_TMR);
-
-          /* Need to work out where this timer comes from */
-          group->group_state = IGMP_GROUP_DELAYING_MEMBER;
-        }
-        /* Increment group use */
-        group->use++;
-        /* Join on this interface */
-        err = ERR_OK;
-      } else {
-        /* Return an error even if some network interfaces are joined */
-        /** @todo undo any other netif already joined */
-        LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: Not enought memory to join to group\n"));
-        return ERR_MEM;
-      }
-    }
-    /* proceed to next network interface */
-    netif = netif->next;
-  }
-
-  return err;
-}
-
-/**
- * Leave a group on one network interface.
- *
- * @param ifaddr ip address of the network interface which should leave a group
- * @param groupaddr the ip address of the group which to leave
- * @return ERR_OK if group was left on the netif(s), an err_t otherwise
- */
-err_t
-igmp_leavegroup(ip_addr_t *ifaddr, ip_addr_t *groupaddr)
-{
-  err_t              err = ERR_VAL; /* no matching interface */
-  struct igmp_group *group;
-  struct netif      *netif;
-
-  /* make sure it is multicast address */
-  LWIP_ERROR("igmp_leavegroup: attempt to leave non-multicast address", ip_addr_ismulticast(groupaddr), return ERR_VAL;);
-  LWIP_ERROR("igmp_leavegroup: attempt to leave allsystems address", (!ip_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;);
-
-  /* loop through netif's */
-  netif = netif_list;
-  while (netif != NULL) {
-    /* Should we leave this interface ? */
-    if ((netif->flags & NETIF_FLAG_IGMP) && ((ip_addr_isany(ifaddr) || ip_addr_cmp(&(netif->ip_addr), ifaddr)))) {
-      /* find group */
-      group = igmp_lookfor_group(netif, groupaddr);
-
-      if (group != NULL) {
-        /* Only send a leave if the flag is set according to the state diagram */
-        LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: Leaving group: "));
-        ip_addr_debug_print(IGMP_DEBUG, groupaddr);
-        LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
-
-        /* If there is no other use of the group */
-        if (group->use <= 1) {
-          /* If we are the last reporter for this group */
-          if (group->last_reporter_flag) {
-            LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: sending leaving group\n"));
-            IGMP_STATS_INC(igmp.tx_leave);
-            igmp_send(group, IGMP_LEAVE_GROUP);
-          }
-          
-          /* Disable the group at the MAC level */
-          if (netif->igmp_mac_filter != NULL) {
-            LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: igmp_mac_filter(DEL "));
-            ip_addr_debug_print(IGMP_DEBUG, groupaddr);
-            LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif));
-            netif->igmp_mac_filter(netif, groupaddr, IGMP_DEL_MAC_FILTER);
-          }
-          
-          LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: remove group: "));
-          ip_addr_debug_print(IGMP_DEBUG, groupaddr);
-          LWIP_DEBUGF(IGMP_DEBUG, ("\n"));          
-          
-          /* Free the group */
-          igmp_remove_group(group);
-        } else {
-          /* Decrement group use */
-          group->use--;
-        }
-        /* Leave on this interface */
-        err = ERR_OK;
-      } else {
-        /* It's not a fatal error on "leavegroup" */
-        LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: not member of group\n"));
-      }
-    }
-    /* proceed to next network interface */
-    netif = netif->next;
-  }
-
-  return err;
-}
-
-/**
- * The igmp timer function (both for NO_SYS=1 and =0)
- * Should be called every IGMP_TMR_INTERVAL milliseconds (100 ms is default).
- */
-void
-igmp_tmr(void)
-{
-  struct igmp_group *group = igmp_group_list;
-
-  while (group != NULL) {
-    if (group->timer > 0) {
-      group->timer--;
-      if (group->timer == 0) {
-        igmp_timeout(group);
-      }
-    }
-    group = group->next;
-  }
-}
-
-/**
- * Called if a timeout for one group is reached.
- * Sends a report for this group.
- *
- * @param group an igmp_group for which a timeout is reached
- */
-static void
-igmp_timeout(struct igmp_group *group)
-{
-  /* If the state is IGMP_GROUP_DELAYING_MEMBER then we send a report for this group */
-  if (group->group_state == IGMP_GROUP_DELAYING_MEMBER) {
-    LWIP_DEBUGF(IGMP_DEBUG, ("igmp_timeout: report membership for group with address "));
-    ip_addr_debug_print(IGMP_DEBUG, &(group->group_address));
-    LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", group->netif));
-
-    IGMP_STATS_INC(igmp.tx_report);
-    igmp_send(group, IGMP_V2_MEMB_REPORT);
-  }
-}
-
-/**
- * Start a timer for an igmp group
- *
- * @param group the igmp_group for which to start a timer
- * @param max_time the time in multiples of IGMP_TMR_INTERVAL (decrease with
- *        every call to igmp_tmr())
- */
-static void
-igmp_start_timer(struct igmp_group *group, u8_t max_time)
-{
-  /* ensure the input value is > 0 */
-  if (max_time == 0) {
-    max_time = 1;
-  }
-  /* ensure the random value is > 0 */
-  group->timer = (LWIP_RAND() % (max_time - 1)) + 1;
-}
-
-/**
- * Delaying membership report for a group if necessary
- *
- * @param group the igmp_group for which "delaying" membership report
- * @param maxresp query delay
- */
-static void
-igmp_delaying_member(struct igmp_group *group, u8_t maxresp)
-{
-  if ((group->group_state == IGMP_GROUP_IDLE_MEMBER) ||
-     ((group->group_state == IGMP_GROUP_DELAYING_MEMBER) &&
-      ((group->timer == 0) || (maxresp < group->timer)))) {
-    igmp_start_timer(group, maxresp);
-    group->group_state = IGMP_GROUP_DELAYING_MEMBER;
-  }
-}
-
-
-/**
- * Sends an IP packet on a network interface. This function constructs the IP header
- * and calculates the IP header checksum. If the source IP address is NULL,
- * the IP address of the outgoing network interface is filled in as source address.
- *
- * @param p the packet to send (p->payload points to the data, e.g. next
-            protocol header; if dest == IP_HDRINCL, p already includes an IP
-            header and p->payload points to that IP header)
- * @param src the source IP address to send from (if src == IP_ADDR_ANY, the
- *         IP  address of the netif used to send is used as source address)
- * @param dest the destination IP address to send the packet to
- * @param ttl the TTL value to be set in the IP header
- * @param proto the PROTOCOL to be set in the IP header
- * @param netif the netif on which to send this packet
- * @return ERR_OK if the packet was sent OK
- *         ERR_BUF if p doesn't have enough space for IP/LINK headers
- *         returns errors returned by netif->output
- */
-static err_t
-igmp_ip_output_if(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, struct netif *netif)
-{
-  /* This is the "router alert" option */
-  u16_t ra[2];
-  ra[0] = PP_HTONS(ROUTER_ALERT);
-  ra[1] = 0x0000; /* Router shall examine packet */
-  IGMP_STATS_INC(igmp.xmit);
-  return ip_output_if_opt(p, src, dest, IGMP_TTL, 0, IP_PROTO_IGMP, netif, ra, ROUTER_ALERTLEN);
-}
-
-/**
- * Send an igmp packet to a specific group.
- *
- * @param group the group to which to send the packet
- * @param type the type of igmp packet to send
- */
-static void
-igmp_send(struct igmp_group *group, u8_t type)
-{
-  struct pbuf*     p    = NULL;
-  struct igmp_msg* igmp = NULL;
-  ip_addr_t   src  = *IP_ADDR_ANY;
-  ip_addr_t*  dest = NULL;
-
-  /* IP header + "router alert" option + IGMP header */
-  p = pbuf_alloc(PBUF_TRANSPORT, IGMP_MINLEN, PBUF_RAM);
-  
-  if (p) {
-    igmp = (struct igmp_msg *)p->payload;
-    LWIP_ASSERT("igmp_send: check that first pbuf can hold struct igmp_msg",
-               (p->len >= sizeof(struct igmp_msg)));
-    ip_addr_copy(src, group->netif->ip_addr);
-     
-    if (type == IGMP_V2_MEMB_REPORT) {
-      dest = &(group->group_address);
-      ip_addr_copy(igmp->igmp_group_address, group->group_address);
-      group->last_reporter_flag = 1; /* Remember we were the last to report */
-    } else {
-      if (type == IGMP_LEAVE_GROUP) {
-        dest = &allrouters;
-        ip_addr_copy(igmp->igmp_group_address, group->group_address);
-      }
-    }
-
-    if ((type == IGMP_V2_MEMB_REPORT) || (type == IGMP_LEAVE_GROUP)) {
-      igmp->igmp_msgtype  = type;
-      igmp->igmp_maxresp  = 0;
-      igmp->igmp_checksum = 0;
-      igmp->igmp_checksum = inet_chksum(igmp, IGMP_MINLEN);
-
-      igmp_ip_output_if(p, &src, dest, group->netif);
-    }
-
-    pbuf_free(p);
-  } else {
-    LWIP_DEBUGF(IGMP_DEBUG, ("igmp_send: not enough memory for igmp_send\n"));
-    IGMP_STATS_INC(igmp.memerr);
-  }
-}
-
-#endif /* LWIP_IGMP */
+/**
+ * @file
+ * IGMP - Internet Group Management Protocol
+ *
+ * @defgroup igmp IGMP
+ * @ingroup ip4
+ * To be called from TCPIP thread
+ */
+
+/*
+ * Copyright (c) 2002 CITEL Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of CITEL Technologies Ltd nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY CITEL TECHNOLOGIES AND CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL CITEL TECHNOLOGIES OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This file is a contribution to the lwIP TCP/IP stack.
+ * The Swedish Institute of Computer Science and Adam Dunkels
+ * are specifically granted permission to redistribute this
+ * source code.
+*/
+
+/*-------------------------------------------------------------
+Note 1)
+Although the rfc requires V1 AND V2 capability
+we will only support v2 since now V1 is very old (August 1989)
+V1 can be added if required
+
+a debug print and statistic have been implemented to
+show this up.
+-------------------------------------------------------------
+-------------------------------------------------------------
+Note 2)
+A query for a specific group address (as opposed to ALLHOSTS)
+has now been implemented as I am unsure if it is required
+
+a debug print and statistic have been implemented to
+show this up.
+-------------------------------------------------------------
+-------------------------------------------------------------
+Note 3)
+The router alert rfc 2113 is implemented in outgoing packets
+but not checked rigorously incoming
+-------------------------------------------------------------
+Steve Reynolds
+------------------------------------------------------------*/
+
+/*-----------------------------------------------------------------------------
+ * RFC 988  - Host extensions for IP multicasting                         - V0
+ * RFC 1054 - Host extensions for IP multicasting                         -
+ * RFC 1112 - Host extensions for IP multicasting                         - V1
+ * RFC 2236 - Internet Group Management Protocol, Version 2               - V2  <- this code is based on this RFC (it's the "de facto" standard)
+ * RFC 3376 - Internet Group Management Protocol, Version 3               - V3
+ * RFC 4604 - Using Internet Group Management Protocol Version 3...       - V3+
+ * RFC 2113 - IP Router Alert Option                                      -
+ *----------------------------------------------------------------------------*/
+
+/*-----------------------------------------------------------------------------
+ * Includes
+ *----------------------------------------------------------------------------*/
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV4 && LWIP_IGMP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/igmp.h"
+#include "lwip/debug.h"
+#include "lwip/def.h"
+#include "lwip/mem.h"
+#include "lwip/ip.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/netif.h"
+#include "lwip/stats.h"
+#include "lwip/prot/igmp.h"
+
+#include "string.h"
+
+static struct igmp_group *igmp_lookup_group(struct netif *ifp, const ip4_addr_t *addr);
+static err_t  igmp_remove_group(struct netif* netif, struct igmp_group *group);
+static void   igmp_timeout(struct netif *netif, struct igmp_group *group);
+static void   igmp_start_timer(struct igmp_group *group, u8_t max_time);
+static void   igmp_delaying_member(struct igmp_group *group, u8_t maxresp);
+static err_t  igmp_ip_output_if(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest, struct netif *netif);
+static void   igmp_send(struct netif *netif, struct igmp_group *group, u8_t type);
+
+static ip4_addr_t     allsystems;
+static ip4_addr_t     allrouters;
+
+/**
+ * Initialize the IGMP module
+ */
+void
+igmp_init(void)
+{
+  LWIP_DEBUGF(IGMP_DEBUG, ("igmp_init: initializing\n"));
+
+  IP4_ADDR(&allsystems, 224, 0, 0, 1);
+  IP4_ADDR(&allrouters, 224, 0, 0, 2);
+}
+
+/**
+ * Start IGMP processing on interface
+ *
+ * @param netif network interface on which start IGMP processing
+ */
+err_t
+igmp_start(struct netif *netif)
+{
+  struct igmp_group* group;
+
+  LWIP_DEBUGF(IGMP_DEBUG, ("igmp_start: starting IGMP processing on if %p\n", (void*)netif));
+
+  group = igmp_lookup_group(netif, &allsystems);
+
+  if (group != NULL) {
+    group->group_state = IGMP_GROUP_IDLE_MEMBER;
+    group->use++;
+
+    /* Allow the igmp messages at the MAC level */
+    if (netif->igmp_mac_filter != NULL) {
+      LWIP_DEBUGF(IGMP_DEBUG, ("igmp_start: igmp_mac_filter(ADD "));
+      ip4_addr_debug_print_val(IGMP_DEBUG, allsystems);
+      LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", (void*)netif));
+      netif->igmp_mac_filter(netif, &allsystems, NETIF_ADD_MAC_FILTER);
+    }
+
+    return ERR_OK;
+  }
+
+  return ERR_MEM;
+}
+
+/**
+ * Stop IGMP processing on interface
+ *
+ * @param netif network interface on which stop IGMP processing
+ */
+err_t
+igmp_stop(struct netif *netif)
+{
+  struct igmp_group *group = netif_igmp_data(netif);
+
+  netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_IGMP, NULL);
+
+  while (group != NULL) {
+    struct igmp_group *next = group->next; /* avoid use-after-free below */
+
+    /* disable the group at the MAC level */
+    if (netif->igmp_mac_filter != NULL) {
+      LWIP_DEBUGF(IGMP_DEBUG, ("igmp_stop: igmp_mac_filter(DEL "));
+      ip4_addr_debug_print(IGMP_DEBUG, &group->group_address);
+      LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", (void*)netif));
+      netif->igmp_mac_filter(netif, &(group->group_address), NETIF_DEL_MAC_FILTER);
+    }
+
+    /* free group */
+    memp_free(MEMP_IGMP_GROUP, group);
+
+    /* move to "next" */
+    group = next;
+  }
+  return ERR_OK;
+}
+
+/**
+ * Report IGMP memberships for this interface
+ *
+ * @param netif network interface on which report IGMP memberships
+ */
+void
+igmp_report_groups(struct netif *netif)
+{
+  struct igmp_group *group = netif_igmp_data(netif);
+
+  LWIP_DEBUGF(IGMP_DEBUG, ("igmp_report_groups: sending IGMP reports on if %p\n", (void*)netif));
+
+  /* Skip the first group in the list, it is always the allsystems group added in igmp_start() */
+  if(group != NULL) {
+    group = group->next;
+  }
+  
+  while (group != NULL) {
+    igmp_delaying_member(group, IGMP_JOIN_DELAYING_MEMBER_TMR);
+    group = group->next;
+  }
+}
+
+/**
+ * Search for a group in the global igmp_group_list
+ *
+ * @param ifp the network interface for which to look
+ * @param addr the group ip address to search for
+ * @return a struct igmp_group* if the group has been found,
+ *         NULL if the group wasn't found.
+ */
+struct igmp_group *
+igmp_lookfor_group(struct netif *ifp, const ip4_addr_t *addr)
+{
+  struct igmp_group *group = netif_igmp_data(ifp);
+
+  while (group != NULL) {
+    if (ip4_addr_cmp(&(group->group_address), addr)) {
+      return group;
+    }
+    group = group->next;
+  }
+
+  /* to be clearer, we return NULL here instead of
+   * 'group' (which is also NULL at this point).
+   */
+  return NULL;
+}
+
+/**
+ * Search for a specific igmp group and create a new one if not found-
+ *
+ * @param ifp the network interface for which to look
+ * @param addr the group ip address to search
+ * @return a struct igmp_group*,
+ *         NULL on memory error.
+ */
+static struct igmp_group *
+igmp_lookup_group(struct netif *ifp, const ip4_addr_t *addr)
+{
+  struct igmp_group *group;
+  struct igmp_group *list_head = netif_igmp_data(ifp);
+
+  /* Search if the group already exists */
+  group = igmp_lookfor_group(ifp, addr);
+  if (group != NULL) {
+    /* Group already exists. */
+    return group;
+  }
+  
+  /* Group doesn't exist yet, create a new one */
+  group = (struct igmp_group *)memp_malloc(MEMP_IGMP_GROUP);
+  if (group != NULL) {
+    ip4_addr_set(&(group->group_address), addr);
+    group->timer              = 0; /* Not running */
+    group->group_state        = IGMP_GROUP_NON_MEMBER;
+    group->last_reporter_flag = 0;
+    group->use                = 0;
+
+    /* Ensure allsystems group is always first in list */    
+    if (list_head == NULL) {
+      /* this is the first entry in linked list */
+      LWIP_ASSERT("igmp_lookup_group: first group must be allsystems",
+        (ip4_addr_cmp(addr, &allsystems) != 0));
+      group->next = NULL;
+      netif_set_client_data(ifp, LWIP_NETIF_CLIENT_DATA_INDEX_IGMP, group);
+    } else {
+      /* append _after_ first entry */
+      LWIP_ASSERT("igmp_lookup_group: all except first group must not be allsystems",
+        (ip4_addr_cmp(addr, &allsystems) == 0));
+      group->next = list_head->next;
+      list_head->next = group;
+    }
+  }
+
+  LWIP_DEBUGF(IGMP_DEBUG, ("igmp_lookup_group: %sallocated a new group with address ", (group?"":"impossible to ")));
+  ip4_addr_debug_print(IGMP_DEBUG, addr);
+  LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", (void*)ifp));
+
+  return group;
+}
+
+/**
+ * Remove a group in the global igmp_group_list, but don't free it yet
+ *
+ * @param group the group to remove from the global igmp_group_list
+ * @return ERR_OK if group was removed from the list, an err_t otherwise
+ */
+static err_t
+igmp_remove_group(struct netif* netif, struct igmp_group *group)
+{
+  err_t err = ERR_OK;
+  struct igmp_group *tmp_group;
+
+  /* Skip the first group in the list, it is always the allsystems group added in igmp_start() */
+  for (tmp_group = netif_igmp_data(netif); tmp_group != NULL; tmp_group = tmp_group->next) {
+    if (tmp_group->next == group) {
+      tmp_group->next = group->next;
+      break;
+    }
+  }
+  /* Group not found in the global igmp_group_list */
+  if (tmp_group == NULL) {
+    err = ERR_ARG;
+  }
+
+  return err;
+}
+
+/**
+ * Called from ip_input() if a new IGMP packet is received.
+ *
+ * @param p received igmp packet, p->payload pointing to the igmp header
+ * @param inp network interface on which the packet was received
+ * @param dest destination ip address of the igmp packet
+ */
+void
+igmp_input(struct pbuf *p, struct netif *inp, const ip4_addr_t *dest)
+{
+  struct igmp_msg*   igmp;
+  struct igmp_group* group;
+  struct igmp_group* groupref;
+
+  IGMP_STATS_INC(igmp.recv);
+
+  /* Note that the length CAN be greater than 8 but only 8 are used - All are included in the checksum */
+  if (p->len < IGMP_MINLEN) {
+    pbuf_free(p);
+    IGMP_STATS_INC(igmp.lenerr);
+    LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: length error\n"));
+    return;
+  }
+
+  LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: message from "));
+  ip4_addr_debug_print(IGMP_DEBUG, &(ip4_current_header()->src));
+  LWIP_DEBUGF(IGMP_DEBUG, (" to address "));
+  ip4_addr_debug_print(IGMP_DEBUG, &(ip4_current_header()->dest));
+  LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", (void*)inp));
+
+  /* Now calculate and check the checksum */
+  igmp = (struct igmp_msg *)p->payload;
+  if (inet_chksum(igmp, p->len)) {
+    pbuf_free(p);
+    IGMP_STATS_INC(igmp.chkerr);
+    LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: checksum error\n"));
+    return;
+  }
+
+  /* Packet is ok so find an existing group */
+  group = igmp_lookfor_group(inp, dest); /* use the destination IP address of incoming packet */
+
+  /* If group can be found or create... */
+  if (!group) {
+    pbuf_free(p);
+    IGMP_STATS_INC(igmp.drop);
+    LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP frame not for us\n"));
+    return;
+  }
+
+  /* NOW ACT ON THE INCOMING MESSAGE TYPE... */
+  switch (igmp->igmp_msgtype) {
+  case IGMP_MEMB_QUERY:
+    /* IGMP_MEMB_QUERY to the "all systems" address ? */
+    if ((ip4_addr_cmp(dest, &allsystems)) && ip4_addr_isany(&igmp->igmp_group_address)) {
+      /* THIS IS THE GENERAL QUERY */
+      LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: General IGMP_MEMB_QUERY on \"ALL SYSTEMS\" address (224.0.0.1) [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp)));
+
+      if (igmp->igmp_maxresp == 0) {
+        IGMP_STATS_INC(igmp.rx_v1);
+        LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: got an all hosts query with time== 0 - this is V1 and not implemented - treat as v2\n"));
+        igmp->igmp_maxresp = IGMP_V1_DELAYING_MEMBER_TMR;
+      } else {
+        IGMP_STATS_INC(igmp.rx_general);
+      }
+
+      groupref = netif_igmp_data(inp);
+      
+      /* Do not send messages on the all systems group address! */
+      /* Skip the first group in the list, it is always the allsystems group added in igmp_start() */
+      if(groupref != NULL) {
+        groupref = groupref->next;
+      }
+
+      while (groupref) {
+        igmp_delaying_member(groupref, igmp->igmp_maxresp);
+        groupref = groupref->next;
+      }
+    } else {
+      /* IGMP_MEMB_QUERY to a specific group ? */
+      if (!ip4_addr_isany(&igmp->igmp_group_address)) {
+        LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_MEMB_QUERY to a specific group "));
+        ip4_addr_debug_print(IGMP_DEBUG, &igmp->igmp_group_address);
+        if (ip4_addr_cmp(dest, &allsystems)) {
+          ip4_addr_t groupaddr;
+          LWIP_DEBUGF(IGMP_DEBUG, (" using \"ALL SYSTEMS\" address (224.0.0.1) [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp)));
+          /* we first need to re-look for the group since we used dest last time */
+          ip4_addr_copy(groupaddr, igmp->igmp_group_address);
+          group = igmp_lookfor_group(inp, &groupaddr);
+        } else {
+          LWIP_DEBUGF(IGMP_DEBUG, (" with the group address as destination [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp)));
+        }
+
+        if (group != NULL) {
+          IGMP_STATS_INC(igmp.rx_group);
+          igmp_delaying_member(group, igmp->igmp_maxresp);
+        } else {
+          IGMP_STATS_INC(igmp.drop);
+        }
+      } else {
+        IGMP_STATS_INC(igmp.proterr);
+      }
+    }
+    break;
+  case IGMP_V2_MEMB_REPORT:
+    LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_V2_MEMB_REPORT\n"));
+    IGMP_STATS_INC(igmp.rx_report);
+    if (group->group_state == IGMP_GROUP_DELAYING_MEMBER) {
+      /* This is on a specific group we have already looked up */
+      group->timer = 0; /* stopped */
+      group->group_state = IGMP_GROUP_IDLE_MEMBER;
+      group->last_reporter_flag = 0;
+    }
+    break;
+  default:
+    LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: unexpected msg %d in state %d on group %p on if %p\n",
+      igmp->igmp_msgtype, group->group_state, (void*)&group, (void*)inp));
+    IGMP_STATS_INC(igmp.proterr);
+    break;
+  }
+
+  pbuf_free(p);
+  return;
+}
+
+/**
+ * @ingroup igmp
+ * Join a group on one network interface.
+ *
+ * @param ifaddr ip address of the network interface which should join a new group
+ * @param groupaddr the ip address of the group which to join
+ * @return ERR_OK if group was joined on the netif(s), an err_t otherwise
+ */
+err_t
+igmp_joingroup(const ip4_addr_t *ifaddr, const ip4_addr_t *groupaddr)
+{
+  err_t err = ERR_VAL; /* no matching interface */
+  struct netif *netif;
+
+  /* make sure it is multicast address */
+  LWIP_ERROR("igmp_joingroup: attempt to join non-multicast address", ip4_addr_ismulticast(groupaddr), return ERR_VAL;);
+  LWIP_ERROR("igmp_joingroup: attempt to join allsystems address", (!ip4_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;);
+
+  /* loop through netif's */
+  netif = netif_list;
+  while (netif != NULL) {
+    /* Should we join this interface ? */
+    if ((netif->flags & NETIF_FLAG_IGMP) && ((ip4_addr_isany(ifaddr) || ip4_addr_cmp(netif_ip4_addr(netif), ifaddr)))) {
+      err = igmp_joingroup_netif(netif, groupaddr);
+      if (err != ERR_OK) {
+        /* Return an error even if some network interfaces are joined */
+        /** @todo undo any other netif already joined */
+        return err;
+      }
+    }
+    /* proceed to next network interface */
+    netif = netif->next;
+  }
+
+  return err;
+}
+
+/**
+ * @ingroup igmp
+ * Join a group on one network interface.
+ *
+ * @param netif the network interface which should join a new group
+ * @param groupaddr the ip address of the group which to join
+ * @return ERR_OK if group was joined on the netif, an err_t otherwise
+ */
+err_t
+igmp_joingroup_netif(struct netif *netif, const ip4_addr_t *groupaddr)
+{
+  struct igmp_group *group;
+
+  /* make sure it is multicast address */
+  LWIP_ERROR("igmp_joingroup_netif: attempt to join non-multicast address", ip4_addr_ismulticast(groupaddr), return ERR_VAL;);
+  LWIP_ERROR("igmp_joingroup_netif: attempt to join allsystems address", (!ip4_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;);
+
+  /* make sure it is an igmp-enabled netif */
+  LWIP_ERROR("igmp_joingroup_netif: attempt to join on non-IGMP netif", netif->flags & NETIF_FLAG_IGMP, return ERR_VAL;);
+
+  /* find group or create a new one if not found */
+  group = igmp_lookup_group(netif, groupaddr);
+
+  if (group != NULL) {
+    /* This should create a new group, check the state to make sure */
+    if (group->group_state != IGMP_GROUP_NON_MEMBER) {
+      LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup_netif: join to group not in state IGMP_GROUP_NON_MEMBER\n"));
+    } else {
+      /* OK - it was new group */
+      LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup_netif: join to new group: "));
+      ip4_addr_debug_print(IGMP_DEBUG, groupaddr);
+      LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
+
+      /* If first use of the group, allow the group at the MAC level */
+      if ((group->use==0) && (netif->igmp_mac_filter != NULL)) {
+        LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup_netif: igmp_mac_filter(ADD "));
+        ip4_addr_debug_print(IGMP_DEBUG, groupaddr);
+        LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", (void*)netif));
+        netif->igmp_mac_filter(netif, groupaddr, NETIF_ADD_MAC_FILTER);
+      }
+
+      IGMP_STATS_INC(igmp.tx_join);
+      igmp_send(netif, group, IGMP_V2_MEMB_REPORT);
+
+      igmp_start_timer(group, IGMP_JOIN_DELAYING_MEMBER_TMR);
+
+      /* Need to work out where this timer comes from */
+      group->group_state = IGMP_GROUP_DELAYING_MEMBER;
+    }
+    /* Increment group use */
+    group->use++;
+    /* Join on this interface */
+    return ERR_OK;
+  } else {
+    LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup_netif: Not enough memory to join to group\n"));
+    return ERR_MEM;
+  }
+}
+
+/**
+ * @ingroup igmp
+ * Leave a group on one network interface.
+ *
+ * @param ifaddr ip address of the network interface which should leave a group
+ * @param groupaddr the ip address of the group which to leave
+ * @return ERR_OK if group was left on the netif(s), an err_t otherwise
+ */
+err_t
+igmp_leavegroup(const ip4_addr_t *ifaddr, const ip4_addr_t *groupaddr)
+{
+  err_t err = ERR_VAL; /* no matching interface */
+  struct netif *netif;
+
+  /* make sure it is multicast address */
+  LWIP_ERROR("igmp_leavegroup: attempt to leave non-multicast address", ip4_addr_ismulticast(groupaddr), return ERR_VAL;);
+  LWIP_ERROR("igmp_leavegroup: attempt to leave allsystems address", (!ip4_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;);
+
+  /* loop through netif's */
+  netif = netif_list;
+  while (netif != NULL) {
+    /* Should we leave this interface ? */
+    if ((netif->flags & NETIF_FLAG_IGMP) && ((ip4_addr_isany(ifaddr) || ip4_addr_cmp(netif_ip4_addr(netif), ifaddr)))) {
+      err_t res = igmp_leavegroup_netif(netif, groupaddr);
+      if (err != ERR_OK) {
+        /* Store this result if we have not yet gotten a success */
+        err = res;
+      }
+    }
+    /* proceed to next network interface */
+    netif = netif->next;
+  }
+
+  return err;
+}
+
+/**
+ * @ingroup igmp
+ * Leave a group on one network interface.
+ *
+ * @param netif the network interface which should leave a group
+ * @param groupaddr the ip address of the group which to leave
+ * @return ERR_OK if group was left on the netif, an err_t otherwise
+ */
+err_t
+igmp_leavegroup_netif(struct netif *netif, const ip4_addr_t *groupaddr)
+{
+  struct igmp_group *group;
+
+  /* make sure it is multicast address */
+  LWIP_ERROR("igmp_leavegroup_netif: attempt to leave non-multicast address", ip4_addr_ismulticast(groupaddr), return ERR_VAL;);
+  LWIP_ERROR("igmp_leavegroup_netif: attempt to leave allsystems address", (!ip4_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;);
+
+  /* make sure it is an igmp-enabled netif */
+  LWIP_ERROR("igmp_leavegroup_netif: attempt to leave on non-IGMP netif", netif->flags & NETIF_FLAG_IGMP, return ERR_VAL;);
+
+  /* find group */
+  group = igmp_lookfor_group(netif, groupaddr);
+
+  if (group != NULL) {
+    /* Only send a leave if the flag is set according to the state diagram */
+    LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup_netif: Leaving group: "));
+    ip4_addr_debug_print(IGMP_DEBUG, groupaddr);
+    LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
+
+    /* If there is no other use of the group */
+    if (group->use <= 1) {
+      /* Remove the group from the list */
+      igmp_remove_group(netif, group);
+
+      /* If we are the last reporter for this group */
+      if (group->last_reporter_flag) {
+        LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup_netif: sending leaving group\n"));
+        IGMP_STATS_INC(igmp.tx_leave);
+        igmp_send(netif, group, IGMP_LEAVE_GROUP);
+      }
+
+      /* Disable the group at the MAC level */
+      if (netif->igmp_mac_filter != NULL) {
+        LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup_netif: igmp_mac_filter(DEL "));
+        ip4_addr_debug_print(IGMP_DEBUG, groupaddr);
+        LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", (void*)netif));
+        netif->igmp_mac_filter(netif, groupaddr, NETIF_DEL_MAC_FILTER);
+      }
+
+      /* Free group struct */
+      memp_free(MEMP_IGMP_GROUP, group);
+    } else {
+      /* Decrement group use */
+      group->use--;
+    }
+    return ERR_OK;
+  } else {
+    LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup_netif: not member of group\n"));
+    return ERR_VAL;
+  }
+}
+
+/**
+ * The igmp timer function (both for NO_SYS=1 and =0)
+ * Should be called every IGMP_TMR_INTERVAL milliseconds (100 ms is default).
+ */
+void
+igmp_tmr(void)
+{
+  struct netif *netif = netif_list;
+
+  while (netif != NULL) {
+    struct igmp_group *group = netif_igmp_data(netif);
+
+    while (group != NULL) {
+      if (group->timer > 0) {
+        group->timer--;
+        if (group->timer == 0) {
+          igmp_timeout(netif, group);
+        }
+      }
+      group = group->next;
+    }
+    netif = netif->next;
+  }
+}
+
+/**
+ * Called if a timeout for one group is reached.
+ * Sends a report for this group.
+ *
+ * @param group an igmp_group for which a timeout is reached
+ */
+static void
+igmp_timeout(struct netif *netif, struct igmp_group *group)
+{
+  /* If the state is IGMP_GROUP_DELAYING_MEMBER then we send a report for this group
+     (unless it is the allsystems group) */
+  if ((group->group_state == IGMP_GROUP_DELAYING_MEMBER) &&
+      (!(ip4_addr_cmp(&(group->group_address), &allsystems)))) {
+    LWIP_DEBUGF(IGMP_DEBUG, ("igmp_timeout: report membership for group with address "));
+    ip4_addr_debug_print(IGMP_DEBUG, &(group->group_address));
+    LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", (void*)netif));
+
+    group->group_state = IGMP_GROUP_IDLE_MEMBER;
+    
+    IGMP_STATS_INC(igmp.tx_report);
+    igmp_send(netif, group, IGMP_V2_MEMB_REPORT);
+  }
+}
+
+/**
+ * Start a timer for an igmp group
+ *
+ * @param group the igmp_group for which to start a timer
+ * @param max_time the time in multiples of IGMP_TMR_INTERVAL (decrease with
+ *        every call to igmp_tmr())
+ */
+static void
+igmp_start_timer(struct igmp_group *group, u8_t max_time)
+{
+#ifdef LWIP_RAND
+  group->timer = max_time > 2 ? (LWIP_RAND() % max_time) : 1;
+#else /* LWIP_RAND */
+  /* ATTENTION: use this only if absolutely necessary! */
+  group->timer = max_time / 2;
+#endif /* LWIP_RAND */
+
+  if (group->timer == 0) {
+    group->timer = 1;
+  }
+}
+
+/**
+ * Delaying membership report for a group if necessary
+ *
+ * @param group the igmp_group for which "delaying" membership report
+ * @param maxresp query delay
+ */
+static void
+igmp_delaying_member(struct igmp_group *group, u8_t maxresp)
+{
+  if ((group->group_state == IGMP_GROUP_IDLE_MEMBER) ||
+     ((group->group_state == IGMP_GROUP_DELAYING_MEMBER) &&
+      ((group->timer == 0) || (maxresp < group->timer)))) {
+    igmp_start_timer(group, maxresp);
+    group->group_state = IGMP_GROUP_DELAYING_MEMBER;
+  }
+}
+
+
+/**
+ * Sends an IP packet on a network interface. This function constructs the IP header
+ * and calculates the IP header checksum. If the source IP address is NULL,
+ * the IP address of the outgoing network interface is filled in as source address.
+ *
+ * @param p the packet to send (p->payload points to the data, e.g. next
+            protocol header; if dest == LWIP_IP_HDRINCL, p already includes an
+            IP header and p->payload points to that IP header)
+ * @param src the source IP address to send from (if src == IP4_ADDR_ANY, the
+ *         IP  address of the netif used to send is used as source address)
+ * @param dest the destination IP address to send the packet to
+ * @param netif the netif on which to send this packet
+ * @return ERR_OK if the packet was sent OK
+ *         ERR_BUF if p doesn't have enough space for IP/LINK headers
+ *         returns errors returned by netif->output
+ */
+static err_t
+igmp_ip_output_if(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest, struct netif *netif)
+{
+  /* This is the "router alert" option */
+  u16_t ra[2];
+  ra[0] = PP_HTONS(ROUTER_ALERT);
+  ra[1] = 0x0000; /* Router shall examine packet */
+  IGMP_STATS_INC(igmp.xmit);
+  return ip4_output_if_opt(p, src, dest, IGMP_TTL, 0, IP_PROTO_IGMP, netif, ra, ROUTER_ALERTLEN);
+}
+
+/**
+ * Send an igmp packet to a specific group.
+ *
+ * @param group the group to which to send the packet
+ * @param type the type of igmp packet to send
+ */
+static void
+igmp_send(struct netif *netif, struct igmp_group *group, u8_t type)
+{
+  struct pbuf*     p    = NULL;
+  struct igmp_msg* igmp = NULL;
+  ip4_addr_t   src  = *IP4_ADDR_ANY4;
+  ip4_addr_t*  dest = NULL;
+
+  /* IP header + "router alert" option + IGMP header */
+  p = pbuf_alloc(PBUF_TRANSPORT, IGMP_MINLEN, PBUF_RAM);
+
+  if (p) {
+    igmp = (struct igmp_msg *)p->payload;
+    LWIP_ASSERT("igmp_send: check that first pbuf can hold struct igmp_msg",
+               (p->len >= sizeof(struct igmp_msg)));
+    ip4_addr_copy(src, *netif_ip4_addr(netif));
+
+    if (type == IGMP_V2_MEMB_REPORT) {
+      dest = &(group->group_address);
+      ip4_addr_copy(igmp->igmp_group_address, group->group_address);
+      group->last_reporter_flag = 1; /* Remember we were the last to report */
+    } else {
+      if (type == IGMP_LEAVE_GROUP) {
+        dest = &allrouters;
+        ip4_addr_copy(igmp->igmp_group_address, group->group_address);
+      }
+    }
+
+    if ((type == IGMP_V2_MEMB_REPORT) || (type == IGMP_LEAVE_GROUP)) {
+      igmp->igmp_msgtype  = type;
+      igmp->igmp_maxresp  = 0;
+      igmp->igmp_checksum = 0;
+      igmp->igmp_checksum = inet_chksum(igmp, IGMP_MINLEN);
+
+      igmp_ip_output_if(p, &src, dest, netif);
+    }
+
+    pbuf_free(p);
+  } else {
+    LWIP_DEBUGF(IGMP_DEBUG, ("igmp_send: not enough memory for igmp_send\n"));
+    IGMP_STATS_INC(igmp.memerr);
+  }
+}
+
+#endif /* LWIP_IPV4 && LWIP_IGMP */

+ 1086 - 924
thirdparty/lwip/src/core/ipv4/ip.c → thirdparty/lwip/src/core/ipv4/ip4.c

@@ -1,924 +1,1086 @@
-/** 
- * @file
- * This is the IPv4 layer implementation for incoming and outgoing IP traffic.
- * 
- * @see ip_frag.c
- *
- */
-
-/*
- * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * This file is part of the lwIP TCP/IP stack.
- *
- * Author: Adam Dunkels <adam@sics.se>
- *
- */
-
-#include "lwip/opt.h"
-#include "lwip/ip.h"
-#include "lwip/def.h"
-#include "lwip/mem.h"
-#include "lwip/ip_frag.h"
-#include "lwip/inet_chksum.h"
-#include "lwip/netif.h"
-#include "lwip/icmp.h"
-#include "lwip/igmp.h"
-#include "lwip/raw.h"
-#include "lwip/udp.h"
-#include "lwip/tcp_impl.h"
-#include "lwip/snmp.h"
-#include "lwip/dhcp.h"
-#include "lwip/autoip.h"
-#include "lwip/stats.h"
-#include "arch/perf.h"
-
-#include <string.h>
-
-/** Set this to 0 in the rare case of wanting to call an extra function to
- * generate the IP checksum (in contrast to calculating it on-the-fly). */
-#ifndef LWIP_INLINE_IP_CHKSUM
-#define LWIP_INLINE_IP_CHKSUM   1
-#endif
-#if LWIP_INLINE_IP_CHKSUM && CHECKSUM_GEN_IP
-#define CHECKSUM_GEN_IP_INLINE  1
-#else
-#define CHECKSUM_GEN_IP_INLINE  0
-#endif
-
-#if LWIP_DHCP || defined(LWIP_IP_ACCEPT_UDP_PORT)
-#define IP_ACCEPT_LINK_LAYER_ADDRESSING 1
-
-/** Some defines for DHCP to let link-layer-addressed packets through while the
- * netif is down.
- * To use this in your own application/protocol, define LWIP_IP_ACCEPT_UDP_PORT
- * to return 1 if the port is accepted and 0 if the port is not accepted.
- */
-#if LWIP_DHCP && defined(LWIP_IP_ACCEPT_UDP_PORT)
-/* accept DHCP client port and custom port */
-#define IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(port) (((port) == PP_NTOHS(DHCP_CLIENT_PORT)) \
-         || (LWIP_IP_ACCEPT_UDP_PORT(port)))
-#elif defined(LWIP_IP_ACCEPT_UDP_PORT) /* LWIP_DHCP && defined(LWIP_IP_ACCEPT_UDP_PORT) */
-/* accept custom port only */
-#define IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(port) (LWIP_IP_ACCEPT_UDP_PORT(port))
-#else /* LWIP_DHCP && defined(LWIP_IP_ACCEPT_UDP_PORT) */
-/* accept DHCP client port only */
-#define IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(port) ((port) == PP_NTOHS(DHCP_CLIENT_PORT))
-#endif /* LWIP_DHCP && defined(LWIP_IP_ACCEPT_UDP_PORT) */
-
-#else /* LWIP_DHCP */
-#define IP_ACCEPT_LINK_LAYER_ADDRESSING 0
-#endif /* LWIP_DHCP */
-
-/**
- * The interface that provided the packet for the current callback
- * invocation.
- */
-struct netif *current_netif;
-
-/**
- * Header of the input packet currently being processed.
- */
-const struct ip_hdr *current_header;
-/** Source IP address of current_header */
-ip_addr_t current_iphdr_src;
-/** Destination IP address of current_header */
-ip_addr_t current_iphdr_dest;
-
-/** The IP header ID of the next outgoing IP packet */
-static u16_t ip_id;
-
-/**
- * Finds the appropriate network interface for a given IP address. It
- * searches the list of network interfaces linearly. A match is found
- * if the masked IP address of the network interface equals the masked
- * IP address given to the function.
- *
- * @param dest the destination IP address for which to find the route
- * @return the netif on which to send to reach dest
- */
-struct netif *
-ip_route(ip_addr_t *dest)
-{
-  struct netif *netif;
-
-#ifdef LWIP_HOOK_IP4_ROUTE
-  netif = LWIP_HOOK_IP4_ROUTE(dest);
-  if (netif != NULL) {
-    return netif;
-  }
-#endif
-
-  /* iterate through netifs */
-  for (netif = netif_list; netif != NULL; netif = netif->next) {
-    /* network mask matches? */
-    if (netif_is_up(netif)) {
-      if (ip_addr_netcmp(dest, &(netif->ip_addr), &(netif->netmask))) {
-        /* return netif on which to forward IP packet */
-        return netif;
-      }
-    }
-  }
-  if ((netif_default == NULL) || (!netif_is_up(netif_default))) {
-    LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip_route: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
-      ip4_addr1_16(dest), ip4_addr2_16(dest), ip4_addr3_16(dest), ip4_addr4_16(dest)));
-    IP_STATS_INC(ip.rterr);
-    snmp_inc_ipoutnoroutes();
-    return NULL;
-  }
-  /* no matching netif found, use default netif */
-  return netif_default;
-}
-
-#if IP_FORWARD
-/**
- * Determine whether an IP address is in a reserved set of addresses
- * that may not be forwarded, or whether datagrams to that destination
- * may be forwarded.
- * @param p the packet to forward
- * @param dest the destination IP address
- * @return 1: can forward 0: discard
- */
-static int
-ip_canforward(struct pbuf *p)
-{
-  u32_t addr = ip4_addr_get_u32(ip_current_dest_addr());
-
-  if (p->flags & PBUF_FLAG_LLBCAST) {
-    /* don't route link-layer broadcasts */
-    return 0;
-  }
-  if ((p->flags & PBUF_FLAG_LLMCAST) && !IP_MULTICAST(addr)) {
-    /* don't route link-layer multicasts unless the destination address is an IP
-       multicast address */
-    return 0;
-  }
-  if (IP_EXPERIMENTAL(addr)) {
-    return 0;
-  }
-  if (IP_CLASSA(addr)) {
-    u32_t net = addr & IP_CLASSA_NET;
-    if ((net == 0) || (net == (IP_LOOPBACKNET << IP_CLASSA_NSHIFT))) {
-      /* don't route loopback packets */
-      return 0;
-    }
-  }
-  return 1;
-}
-
-/**
- * Forwards an IP packet. It finds an appropriate route for the
- * packet, decrements the TTL value of the packet, adjusts the
- * checksum and outputs the packet on the appropriate interface.
- *
- * @param p the packet to forward (p->payload points to IP header)
- * @param iphdr the IP header of the input packet
- * @param inp the netif on which this packet was received
- */
-static void
-ip_forward(struct pbuf *p, struct ip_hdr *iphdr, struct netif *inp)
-{
-  struct netif *netif;
-
-  PERF_START;
-
-  if (!ip_canforward(p)) {
-    goto return_noroute;
-  }
-
-  /* RFC3927 2.7: do not forward link-local addresses */
-  if (ip_addr_islinklocal(&current_iphdr_dest)) {
-    LWIP_DEBUGF(IP_DEBUG, ("ip_forward: not forwarding LLA %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
-      ip4_addr1_16(&current_iphdr_dest), ip4_addr2_16(&current_iphdr_dest),
-      ip4_addr3_16(&current_iphdr_dest), ip4_addr4_16(&current_iphdr_dest)));
-    goto return_noroute;
-  }
-
-  /* Find network interface where to forward this IP packet to. */
-  netif = ip_route(&current_iphdr_dest);
-  if (netif == NULL) {
-    LWIP_DEBUGF(IP_DEBUG, ("ip_forward: no forwarding route for %"U16_F".%"U16_F".%"U16_F".%"U16_F" found\n",
-      ip4_addr1_16(&current_iphdr_dest), ip4_addr2_16(&current_iphdr_dest),
-      ip4_addr3_16(&current_iphdr_dest), ip4_addr4_16(&current_iphdr_dest)));
-    /* @todo: send ICMP_DUR_NET? */
-    goto return_noroute;
-  }
-#if !IP_FORWARD_ALLOW_TX_ON_RX_NETIF
-  /* Do not forward packets onto the same network interface on which
-   * they arrived. */
-  if (netif == inp) {
-    LWIP_DEBUGF(IP_DEBUG, ("ip_forward: not bouncing packets back on incoming interface.\n"));
-    goto return_noroute;
-  }
-#endif /* IP_FORWARD_ALLOW_TX_ON_RX_NETIF */
-
-  /* decrement TTL */
-  IPH_TTL_SET(iphdr, IPH_TTL(iphdr) - 1);
-  /* send ICMP if TTL == 0 */
-  if (IPH_TTL(iphdr) == 0) {
-    snmp_inc_ipinhdrerrors();
-#if LWIP_ICMP
-    /* Don't send ICMP messages in response to ICMP messages */
-    if (IPH_PROTO(iphdr) != IP_PROTO_ICMP) {
-      icmp_time_exceeded(p, ICMP_TE_TTL);
-    }
-#endif /* LWIP_ICMP */
-    return;
-  }
-
-  /* Incrementally update the IP checksum. */
-  if (IPH_CHKSUM(iphdr) >= PP_HTONS(0xffffU - 0x100)) {
-    IPH_CHKSUM_SET(iphdr, IPH_CHKSUM(iphdr) + PP_HTONS(0x100) + 1);
-  } else {
-    IPH_CHKSUM_SET(iphdr, IPH_CHKSUM(iphdr) + PP_HTONS(0x100));
-  }
-
-  LWIP_DEBUGF(IP_DEBUG, ("ip_forward: forwarding packet to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
-    ip4_addr1_16(&current_iphdr_dest), ip4_addr2_16(&current_iphdr_dest),
-    ip4_addr3_16(&current_iphdr_dest), ip4_addr4_16(&current_iphdr_dest)));
-
-  IP_STATS_INC(ip.fw);
-  IP_STATS_INC(ip.xmit);
-  snmp_inc_ipforwdatagrams();
-
-  PERF_STOP("ip_forward");
-  /* don't fragment if interface has mtu set to 0 [loopif] */
-  if (netif->mtu && (p->tot_len > netif->mtu)) {
-    if ((IPH_OFFSET(iphdr) & PP_NTOHS(IP_DF)) == 0) {
-#if IP_FRAG
-      ip_frag(p, netif, ip_current_dest_addr());
-#else /* IP_FRAG */
-      /* @todo: send ICMP Destination Unreacheable code 13 "Communication administratively prohibited"? */
-#endif /* IP_FRAG */
-    } else {
-      /* send ICMP Destination Unreacheable code 4: "Fragmentation Needed and DF Set" */
-      icmp_dest_unreach(p, ICMP_DUR_FRAG);
-    }
-    return;
-  }
-  /* transmit pbuf on chosen interface */
-  netif->output(netif, p, &current_iphdr_dest);
-  return;
-return_noroute:
-  snmp_inc_ipoutnoroutes();
-}
-#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.
- * 
- * @param p the received IP packet (p->payload points to IP header)
- * @param inp the netif on which this packet was received
- * @return ERR_OK if the packet was processed (could return ERR_* if it wasn't
- *         processed, but currently always returns ERR_OK)
- */
-err_t
-ip_input(struct pbuf *p, struct netif *inp)
-{
-  struct ip_hdr *iphdr;
-  struct netif *netif;
-  u16_t iphdr_hlen;
-  u16_t iphdr_len;
-#if IP_ACCEPT_LINK_LAYER_ADDRESSING
-  int check_ip_src=1;
-#endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING */
-
-  IP_STATS_INC(ip.recv);
-  snmp_inc_ipinreceives();
-
-  /* identify the IP header */
-  iphdr = (struct ip_hdr *)p->payload;
-  if (IPH_V(iphdr) != 4) {
-    LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_WARNING, ("IP packet dropped due to bad version number %"U16_F"\n", IPH_V(iphdr)));
-    ip_debug_print(p);
-    pbuf_free(p);
-    IP_STATS_INC(ip.err);
-    IP_STATS_INC(ip.drop);
-    snmp_inc_ipinhdrerrors();
-    return ERR_OK;
-  }
-
-#ifdef LWIP_HOOK_IP4_INPUT
-  if (LWIP_HOOK_IP4_INPUT(p, inp)) {
-    /* the packet has been eaten */
-    return ERR_OK;
-  }
-#endif
-
-  /* obtain IP header length in number of 32-bit words */
-  iphdr_hlen = IPH_HL(iphdr);
-  /* calculate IP header length in bytes */
-  iphdr_hlen *= 4;
-  /* obtain ip length in bytes */
-  iphdr_len = ntohs(IPH_LEN(iphdr));
-
-  /* header length exceeds first pbuf length, or ip length exceeds total pbuf length? */
-  if ((iphdr_hlen > p->len) || (iphdr_len > p->tot_len)) {
-    if (iphdr_hlen > p->len) {
-      LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
-        ("IP header (len %"U16_F") does not fit in first pbuf (len %"U16_F"), IP packet dropped.\n",
-        iphdr_hlen, p->len));
-    }
-    if (iphdr_len > p->tot_len) {
-      LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
-        ("IP (len %"U16_F") is longer than pbuf (len %"U16_F"), IP packet dropped.\n",
-        iphdr_len, p->tot_len));
-    }
-    /* free (drop) packet pbufs */
-    pbuf_free(p);
-    IP_STATS_INC(ip.lenerr);
-    IP_STATS_INC(ip.drop);
-    snmp_inc_ipindiscards();
-    return ERR_OK;
-  }
-
-  /* verify checksum */
-#if CHECKSUM_CHECK_IP
-  if (inet_chksum(iphdr, iphdr_hlen) != 0) {
-
-    LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
-      ("Checksum (0x%"X16_F") failed, IP packet dropped.\n", inet_chksum(iphdr, iphdr_hlen)));
-    ip_debug_print(p);
-    pbuf_free(p);
-    IP_STATS_INC(ip.chkerr);
-    IP_STATS_INC(ip.drop);
-    snmp_inc_ipinhdrerrors();
-    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, iphdr_len);
-
-  /* copy IP addresses to aligned ip_addr_t */
-  ip_addr_copy(current_iphdr_dest, iphdr->dest);
-  ip_addr_copy(current_iphdr_src, iphdr->src);
-
-  /* match packet against an interface, i.e. is this packet for us? */
-#if LWIP_IGMP
-  if (ip_addr_ismulticast(&current_iphdr_dest)) {
-    if ((inp->flags & NETIF_FLAG_IGMP) && (igmp_lookfor_group(inp, &current_iphdr_dest))) {
-      netif = inp;
-    } else {
-      netif = NULL;
-    }
-  } else
-#endif /* LWIP_IGMP */
-  {
-    /* start trying with inp. if that's not acceptable, start walking the
-       list of configured netifs.
-       'first' is used as a boolean to mark whether we started walking the list */
-    int first = 1;
-    netif = inp;
-    do {
-      LWIP_DEBUGF(IP_DEBUG, ("ip_input: iphdr->dest 0x%"X32_F" netif->ip_addr 0x%"X32_F" (0x%"X32_F", 0x%"X32_F", 0x%"X32_F")\n",
-          ip4_addr_get_u32(&iphdr->dest), ip4_addr_get_u32(&netif->ip_addr),
-          ip4_addr_get_u32(&iphdr->dest) & ip4_addr_get_u32(&netif->netmask),
-          ip4_addr_get_u32(&netif->ip_addr) & ip4_addr_get_u32(&netif->netmask),
-          ip4_addr_get_u32(&iphdr->dest) & ~ip4_addr_get_u32(&netif->netmask)));
-
-      /* 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(&current_iphdr_dest, &(netif->ip_addr)) ||
-            /* or broadcast on this interface network address? */
-            ip_addr_isbroadcast(&current_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_AUTOIP
-        /* connections to link-local addresses must persist after changing
-           the netif's address (RFC3927 ch. 1.9) */
-        if ((netif->autoip != NULL) &&
-            ip_addr_cmp(&current_iphdr_dest, &(netif->autoip->llipaddr))) {
-          LWIP_DEBUGF(IP_DEBUG, ("ip_input: LLA packet accepted on interface %c%c\n",
-              netif->name[0], netif->name[1]));
-          /* break out of for loop */
-          break;
-        }
-#endif /* LWIP_AUTOIP */
-      }
-      if (first) {
-        first = 0;
-        netif = netif_list;
-      } else {
-        netif = netif->next;
-      }
-      if (netif == inp) {
-        netif = netif->next;
-      }
-    } while(netif != NULL);
-  }
-
-#if IP_ACCEPT_LINK_LAYER_ADDRESSING
-  /* Pass DHCP messages regardless of destination address. DHCP traffic is addressed
-   * using link layer addressing (such as Ethernet MAC) so we must not filter on IP.
-   * According to RFC 1542 section 3.1.1, referred by RFC 2131).
-   *
-   * If you want to accept private broadcast communication while a netif is down,
-   * define LWIP_IP_ACCEPT_UDP_PORT(dst_port), e.g.:
-   *
-   * #define LWIP_IP_ACCEPT_UDP_PORT(dst_port) ((dst_port) == PP_NTOHS(12345))
-   */
-  if (netif == NULL) {
-    /* remote port is DHCP server? */
-    if (IPH_PROTO(iphdr) == IP_PROTO_UDP) {
-      struct udp_hdr *udphdr = (struct udp_hdr *)((u8_t *)iphdr + iphdr_hlen);
-      LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE, ("ip_input: UDP packet to DHCP client port %"U16_F"\n",
-        ntohs(udphdr->dest)));
-      if (IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(udphdr->dest)) {
-        LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE, ("ip_input: DHCP packet accepted.\n"));
-        netif = inp;
-        check_ip_src = 0;
-      }
-    }
-  }
-#endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING */
-
-  /* broadcast or multicast packet source address? Compliant with RFC 1122: 3.2.1.3 */
-#if IP_ACCEPT_LINK_LAYER_ADDRESSING
-  /* DHCP servers need 0.0.0.0 to be allowed as source address (RFC 1.1.2.2: 3.2.1.3/a) */
-  if (check_ip_src && !ip_addr_isany(&current_iphdr_src))
-#endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING */
-  {  if ((ip_addr_isbroadcast(&current_iphdr_src, inp)) ||
-         (ip_addr_ismulticast(&current_iphdr_src))) {
-      /* packet source is not valid */
-      LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("ip_input: packet source is not valid.\n"));
-      /* free (drop) packet pbufs */
-      pbuf_free(p);
-      IP_STATS_INC(ip.drop);
-      snmp_inc_ipinaddrerrors();
-      snmp_inc_ipindiscards();
-      return ERR_OK;
-    }
-  }
-
-  /* packet not for us? */
-  if (netif == NULL) {
-    /* packet not for us, route or discard */
-    LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE, ("ip_input: packet not for us.\n"));
-#if IP_FORWARD
-    /* non-broadcast packet? */
-    if (!ip_addr_isbroadcast(&current_iphdr_dest, inp)) {
-      /* try to forward IP packet on (other) interfaces */
-      ip_forward(p, iphdr, inp);
-    } else
-#endif /* IP_FORWARD */
-    {
-      snmp_inc_ipinaddrerrors();
-      snmp_inc_ipindiscards();
-    }
-    pbuf_free(p);
-    return ERR_OK;
-  }
-  /* packet consists of multiple fragments? */
-  if ((IPH_OFFSET(iphdr) & PP_HTONS(IP_OFFMASK | IP_MF)) != 0) {
-#if IP_REASSEMBLY /* packet fragment reassembly code present? */
-    LWIP_DEBUGF(IP_DEBUG, ("IP packet is a fragment (id=0x%04"X16_F" tot_len=%"U16_F" len=%"U16_F" MF=%"U16_F" offset=%"U16_F"), calling ip_reass()\n",
-      ntohs(IPH_ID(iphdr)), p->tot_len, ntohs(IPH_LEN(iphdr)), !!(IPH_OFFSET(iphdr) & PP_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 = (struct ip_hdr *)p->payload;
-#else /* IP_REASSEMBLY == 0, no packet fragment reassembly code present */
-    pbuf_free(p);
-    LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("IP packet dropped since it was fragmented (0x%"X16_F") (while IP_REASSEMBLY == 0).\n",
-      ntohs(IPH_OFFSET(iphdr))));
-    IP_STATS_INC(ip.opterr);
-    IP_STATS_INC(ip.drop);
-    /* unsupported protocol feature */
-    snmp_inc_ipinunknownprotos();
-    return ERR_OK;
-#endif /* IP_REASSEMBLY */
-  }
-
-#if IP_OPTIONS_ALLOWED == 0 /* no support for IP options in the IP header? */
-
-#if LWIP_IGMP
-  /* there is an extra "router alert" option in IGMP messages which we allow for but do not police */
-  if((iphdr_hlen > IP_HLEN) &&  (IPH_PROTO(iphdr) != IP_PROTO_IGMP)) {
-#else
-  if (iphdr_hlen > IP_HLEN) {
-#endif /* LWIP_IGMP */
-    LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("IP packet dropped since there were IP options (while IP_OPTIONS_ALLOWED == 0).\n"));
-    pbuf_free(p);
-    IP_STATS_INC(ip.opterr);
-    IP_STATS_INC(ip.drop);
-    /* unsupported protocol feature */
-    snmp_inc_ipinunknownprotos();
-    return ERR_OK;
-  }
-#endif /* IP_OPTIONS_ALLOWED == 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));
-
-  current_netif = inp;
-  current_header = iphdr;
-
-#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:
-#if LWIP_UDPLITE
-    case IP_PROTO_UDPLITE:
-#endif /* LWIP_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 */
-#if LWIP_ICMP
-    case IP_PROTO_ICMP:
-      snmp_inc_ipindelivers();
-      icmp_input(p, inp);
-      break;
-#endif /* LWIP_ICMP */
-#if LWIP_IGMP
-    case IP_PROTO_IGMP:
-      igmp_input(p, inp, &current_iphdr_dest);
-      break;
-#endif /* LWIP_IGMP */
-    default:
-#if LWIP_ICMP
-      /* send ICMP destination protocol unreachable unless is was a broadcast */
-      if (!ip_addr_isbroadcast(&current_iphdr_dest, inp) &&
-          !ip_addr_ismulticast(&current_iphdr_dest)) {
-        p->payload = iphdr;
-        icmp_dest_unreach(p, ICMP_DUR_PROTO);
-      }
-#endif /* LWIP_ICMP */
-      pbuf_free(p);
-
-      LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("Unsupported transport protocol %"U16_F"\n", IPH_PROTO(iphdr)));
-
-      IP_STATS_INC(ip.proterr);
-      IP_STATS_INC(ip.drop);
-      snmp_inc_ipinunknownprotos();
-    }
-  }
-
-  current_netif = NULL;
-  current_header = NULL;
-  ip_addr_set_any(&current_iphdr_src);
-  ip_addr_set_any(&current_iphdr_dest);
-
-  return ERR_OK;
-}
-
-/**
- * Sends an IP packet on a network interface. This function constructs
- * the IP header and calculates the IP header checksum. If the source
- * IP address is NULL, the IP address of the outgoing network
- * interface is filled in as source address.
- * If the destination IP address is IP_HDRINCL, p is assumed to already
- * include an IP header and p->payload points to it instead of the data.
- *
- * @param p the packet to send (p->payload points to the data, e.g. next
-            protocol header; if dest == IP_HDRINCL, p already includes an IP
-            header and p->payload points to that IP header)
- * @param src the source IP address to send from (if src == IP_ADDR_ANY, the
- *         IP  address of the netif used to send is used as source address)
- * @param dest the destination IP address to send the packet to
- * @param ttl the TTL value to be set in the IP header
- * @param tos the TOS value to be set in the IP header
- * @param proto the PROTOCOL to be set in the IP header
- * @param netif the netif on which to send this packet
- * @return ERR_OK if the packet was sent OK
- *         ERR_BUF if p doesn't have enough space for IP/LINK headers
- *         returns errors returned by netif->output
- *
- * @note ip_id: RFC791 "some host may be able to simply use
- *  unique identifiers independent of destination"
- */
-err_t
-ip_output_if(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest,
-             u8_t ttl, u8_t tos,
-             u8_t proto, struct netif *netif)
-{
-#if IP_OPTIONS_SEND
-  return ip_output_if_opt(p, src, dest, ttl, tos, proto, netif, NULL, 0);
-}
-
-/**
- * Same as ip_output_if() but with the possibility to include IP options:
- *
- * @ param ip_options pointer to the IP options, copied into the IP header
- * @ param optlen length of ip_options
- */
-err_t ip_output_if_opt(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest,
-       u8_t ttl, u8_t tos, u8_t proto, struct netif *netif, void *ip_options,
-       u16_t optlen)
-{
-#endif /* IP_OPTIONS_SEND */
-  struct ip_hdr *iphdr;
-  ip_addr_t dest_addr;
-#if CHECKSUM_GEN_IP_INLINE
-  u32_t chk_sum = 0;
-#endif /* CHECKSUM_GEN_IP_INLINE */
-
-  /* pbufs passed to IP must have a ref-count of 1 as their payload pointer
-     gets altered as the packet is passed down the stack */
-  LWIP_ASSERT("p->ref == 1", p->ref == 1);
-
-  snmp_inc_ipoutrequests();
-
-  /* Should the IP header be generated or is it already included in p? */
-  if (dest != IP_HDRINCL) {
-    u16_t ip_hlen = IP_HLEN;
-#if IP_OPTIONS_SEND
-    u16_t optlen_aligned = 0;
-    if (optlen != 0) {
-#if CHECKSUM_GEN_IP_INLINE
-      int i;
-#endif /* CHECKSUM_GEN_IP_INLINE */
-      /* round up to a multiple of 4 */
-      optlen_aligned = ((optlen + 3) & ~3);
-      ip_hlen += optlen_aligned;
-      /* First write in the IP options */
-      if (pbuf_header(p, optlen_aligned)) {
-        LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip_output_if_opt: not enough room for IP options in pbuf\n"));
-        IP_STATS_INC(ip.err);
-        snmp_inc_ipoutdiscards();
-        return ERR_BUF;
-      }
-      MEMCPY(p->payload, ip_options, optlen);
-      if (optlen < optlen_aligned) {
-        /* zero the remaining bytes */
-        memset(((char*)p->payload) + optlen, 0, optlen_aligned - optlen);
-      }
-#if CHECKSUM_GEN_IP_INLINE
-      for (i = 0; i < optlen_aligned/2; i++) {
-        chk_sum += ((u16_t*)p->payload)[i];
-      }
-#endif /* CHECKSUM_GEN_IP_INLINE */
-    }
-#endif /* IP_OPTIONS_SEND */
-    /* generate IP header */
-    if (pbuf_header(p, IP_HLEN)) {
-      LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip_output: not enough room for IP header in pbuf\n"));
-
-      IP_STATS_INC(ip.err);
-      snmp_inc_ipoutdiscards();
-      return ERR_BUF;
-    }
-
-    iphdr = (struct ip_hdr *)p->payload;
-    LWIP_ASSERT("check that first pbuf can hold struct ip_hdr",
-               (p->len >= sizeof(struct ip_hdr)));
-
-    IPH_TTL_SET(iphdr, ttl);
-    IPH_PROTO_SET(iphdr, proto);
-#if CHECKSUM_GEN_IP_INLINE
-    chk_sum += LWIP_MAKE_U16(proto, ttl);
-#endif /* CHECKSUM_GEN_IP_INLINE */
-
-    /* dest cannot be NULL here */
-    ip_addr_copy(iphdr->dest, *dest);
-#if CHECKSUM_GEN_IP_INLINE
-    chk_sum += ip4_addr_get_u32(&iphdr->dest) & 0xFFFF;
-    chk_sum += ip4_addr_get_u32(&iphdr->dest) >> 16;
-#endif /* CHECKSUM_GEN_IP_INLINE */
-
-    IPH_VHL_SET(iphdr, 4, ip_hlen / 4);
-    IPH_TOS_SET(iphdr, tos);
-#if CHECKSUM_GEN_IP_INLINE
-    chk_sum += LWIP_MAKE_U16(tos, iphdr->_v_hl);
-#endif /* CHECKSUM_GEN_IP_INLINE */
-    IPH_LEN_SET(iphdr, htons(p->tot_len));
-#if CHECKSUM_GEN_IP_INLINE
-    chk_sum += iphdr->_len;
-#endif /* CHECKSUM_GEN_IP_INLINE */
-    IPH_OFFSET_SET(iphdr, 0);
-    IPH_ID_SET(iphdr, htons(ip_id));
-#if CHECKSUM_GEN_IP_INLINE
-    chk_sum += iphdr->_id;
-#endif /* CHECKSUM_GEN_IP_INLINE */
-    ++ip_id;
-
-    if (ip_addr_isany(src)) {
-      ip_addr_copy(iphdr->src, netif->ip_addr);
-    } else {
-      /* src cannot be NULL here */
-      ip_addr_copy(iphdr->src, *src);
-    }
-
-#if CHECKSUM_GEN_IP_INLINE
-    chk_sum += ip4_addr_get_u32(&iphdr->src) & 0xFFFF;
-    chk_sum += ip4_addr_get_u32(&iphdr->src) >> 16;
-    chk_sum = (chk_sum >> 16) + (chk_sum & 0xFFFF);
-    chk_sum = (chk_sum >> 16) + chk_sum;
-    chk_sum = ~chk_sum;
-    iphdr->_chksum = chk_sum; /* network order */
-#else /* CHECKSUM_GEN_IP_INLINE */
-    IPH_CHKSUM_SET(iphdr, 0);
-#if CHECKSUM_GEN_IP
-    IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, ip_hlen));
-#endif
-#endif /* CHECKSUM_GEN_IP_INLINE */
-  } else {
-    /* IP header already included in p */
-    iphdr = (struct ip_hdr *)p->payload;
-    ip_addr_copy(dest_addr, iphdr->dest);
-    dest = &dest_addr;
-  }
-
-  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);
-
-#if ENABLE_LOOPBACK
-  if (ip_addr_cmp(dest, &netif->ip_addr)) {
-    /* Packet to self, enqueue it for loopback */
-    LWIP_DEBUGF(IP_DEBUG, ("netif_loop_output()"));
-    return netif_loop_output(netif, p, dest);
-  }
-#if LWIP_IGMP
-  if ((p->flags & PBUF_FLAG_MCASTLOOP) != 0) {
-    netif_loop_output(netif, p, dest);
-  }
-#endif /* LWIP_IGMP */
-#endif /* ENABLE_LOOPBACK */
-#if IP_FRAG
-  /* don't fragment if interface has mtu set to 0 [loopif] */
-  if (netif->mtu && (p->tot_len > netif->mtu)) {
-    return ip_frag(p, netif, dest);
-  }
-#endif /* IP_FRAG */
-
-  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.
- *
- * @param p the packet to send (p->payload points to the data, e.g. next
-            protocol header; if dest == IP_HDRINCL, p already includes an IP
-            header and p->payload points to that IP header)
- * @param src the source IP address to send from (if src == IP_ADDR_ANY, the
- *         IP  address of the netif used to send is used as source address)
- * @param dest the destination IP address to send the packet to
- * @param ttl the TTL value to be set in the IP header
- * @param tos the TOS value to be set in the IP header
- * @param proto the PROTOCOL to be set in the IP header
- *
- * @return ERR_RTE if no route is found
- *         see ip_output_if() for more return values
- */
-err_t
-ip_output(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest,
-          u8_t ttl, u8_t tos, u8_t proto)
-{
-  struct netif *netif;
-
-  /* pbufs passed to IP must have a ref-count of 1 as their payload pointer
-     gets altered as the packet is passed down the stack */
-  LWIP_ASSERT("p->ref == 1", p->ref == 1);
-
-  if ((netif = ip_route(dest)) == NULL) {
-    LWIP_DEBUGF(IP_DEBUG, ("ip_output: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
-      ip4_addr1_16(dest), ip4_addr2_16(dest), ip4_addr3_16(dest), ip4_addr4_16(dest)));
-    IP_STATS_INC(ip.rterr);
-    return ERR_RTE;
-  }
-
-  return ip_output_if(p, src, dest, ttl, tos, proto, netif);
-}
-
-#if LWIP_NETIF_HWADDRHINT
-/** Like ip_output, but takes and addr_hint pointer that is passed on to netif->addr_hint
- *  before calling ip_output_if.
- *
- * @param p the packet to send (p->payload points to the data, e.g. next
-            protocol header; if dest == IP_HDRINCL, p already includes an IP
-            header and p->payload points to that IP header)
- * @param src the source IP address to send from (if src == IP_ADDR_ANY, the
- *         IP  address of the netif used to send is used as source address)
- * @param dest the destination IP address to send the packet to
- * @param ttl the TTL value to be set in the IP header
- * @param tos the TOS value to be set in the IP header
- * @param proto the PROTOCOL to be set in the IP header
- * @param addr_hint address hint pointer set to netif->addr_hint before
- *        calling ip_output_if()
- *
- * @return ERR_RTE if no route is found
- *         see ip_output_if() for more return values
- */
-err_t
-ip_output_hinted(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest,
-          u8_t ttl, u8_t tos, u8_t proto, u8_t *addr_hint)
-{
-  struct netif *netif;
-  err_t err;
-
-  /* pbufs passed to IP must have a ref-count of 1 as their payload pointer
-     gets altered as the packet is passed down the stack */
-  LWIP_ASSERT("p->ref == 1", p->ref == 1);
-
-  if ((netif = ip_route(dest)) == NULL) {
-    LWIP_DEBUGF(IP_DEBUG, ("ip_output: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
-      ip4_addr1_16(dest), ip4_addr2_16(dest), ip4_addr3_16(dest), ip4_addr4_16(dest)));
-    IP_STATS_INC(ip.rterr);
-    return ERR_RTE;
-  }
-
-  NETIF_SET_HWADDRHINT(netif, addr_hint);
-  err = ip_output_if(p, src, dest, ttl, tos, proto, netif);
-  NETIF_SET_HWADDRHINT(netif, NULL);
-
-  return err;
-}
-#endif /* LWIP_NETIF_HWADDRHINT*/
-
-#if IP_DEBUG
-/* Print an IP header by using LWIP_DEBUGF
- * @param p an IP packet, p->payload pointing to the IP header
- */
-void
-ip_debug_print(struct pbuf *p)
-{
-  struct ip_hdr *iphdr = (struct ip_hdr *)p->payload;
-
-  LWIP_DEBUGF(IP_DEBUG, ("IP header:\n"));
-  LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
-  LWIP_DEBUGF(IP_DEBUG, ("|%2"S16_F" |%2"S16_F" |  0x%02"X16_F" |     %5"U16_F"     | (v, hl, tos, len)\n",
-                    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_16(&iphdr->src),
-                    ip4_addr2_16(&iphdr->src),
-                    ip4_addr3_16(&iphdr->src),
-                    ip4_addr4_16(&iphdr->src)));
-  LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
-  LWIP_DEBUGF(IP_DEBUG, ("|  %3"U16_F"  |  %3"U16_F"  |  %3"U16_F"  |  %3"U16_F"  | (dest)\n",
-                    ip4_addr1_16(&iphdr->dest),
-                    ip4_addr2_16(&iphdr->dest),
-                    ip4_addr3_16(&iphdr->dest),
-                    ip4_addr4_16(&iphdr->dest)));
-  LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
-}
-#endif /* IP_DEBUG */
+/**
+ * @file
+ * This is the IPv4 layer implementation for incoming and outgoing IP traffic.
+ *
+ * @see ip_frag.c
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV4
+
+#include "lwip/ip.h"
+#include "lwip/def.h"
+#include "lwip/mem.h"
+#include "lwip/ip4_frag.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/netif.h"
+#include "lwip/icmp.h"
+#include "lwip/igmp.h"
+#include "lwip/raw.h"
+#include "lwip/udp.h"
+#include "lwip/priv/tcp_priv.h"
+#include "lwip/autoip.h"
+#include "lwip/stats.h"
+#include "lwip/prot/dhcp.h"
+
+#include <string.h>
+
+#ifdef LWIP_HOOK_FILENAME
+#include LWIP_HOOK_FILENAME
+#endif
+
+/** Set this to 0 in the rare case of wanting to call an extra function to
+ * generate the IP checksum (in contrast to calculating it on-the-fly). */
+#ifndef LWIP_INLINE_IP_CHKSUM
+#if LWIP_CHECKSUM_CTRL_PER_NETIF
+#define LWIP_INLINE_IP_CHKSUM   0
+#else /* LWIP_CHECKSUM_CTRL_PER_NETIF */
+#define LWIP_INLINE_IP_CHKSUM   1
+#endif /* LWIP_CHECKSUM_CTRL_PER_NETIF */
+#endif
+
+#if LWIP_INLINE_IP_CHKSUM && CHECKSUM_GEN_IP
+#define CHECKSUM_GEN_IP_INLINE  1
+#else
+#define CHECKSUM_GEN_IP_INLINE  0
+#endif
+
+#if LWIP_DHCP || defined(LWIP_IP_ACCEPT_UDP_PORT)
+#define IP_ACCEPT_LINK_LAYER_ADDRESSING 1
+
+/** Some defines for DHCP to let link-layer-addressed packets through while the
+ * netif is down.
+ * To use this in your own application/protocol, define LWIP_IP_ACCEPT_UDP_PORT(port)
+ * to return 1 if the port is accepted and 0 if the port is not accepted.
+ */
+#if LWIP_DHCP && defined(LWIP_IP_ACCEPT_UDP_PORT)
+/* accept DHCP client port and custom port */
+#define IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(port) (((port) == PP_NTOHS(DHCP_CLIENT_PORT)) \
+         || (LWIP_IP_ACCEPT_UDP_PORT(port)))
+#elif defined(LWIP_IP_ACCEPT_UDP_PORT) /* LWIP_DHCP && defined(LWIP_IP_ACCEPT_UDP_PORT) */
+/* accept custom port only */
+#define IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(port) (LWIP_IP_ACCEPT_UDP_PORT(port))
+#else /* LWIP_DHCP && defined(LWIP_IP_ACCEPT_UDP_PORT) */
+/* accept DHCP client port only */
+#define IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(port) ((port) == PP_NTOHS(DHCP_CLIENT_PORT))
+#endif /* LWIP_DHCP && defined(LWIP_IP_ACCEPT_UDP_PORT) */
+
+#else /* LWIP_DHCP */
+#define IP_ACCEPT_LINK_LAYER_ADDRESSING 0
+#endif /* LWIP_DHCP */
+
+/** The IP header ID of the next outgoing IP packet */
+static u16_t ip_id;
+
+#if LWIP_MULTICAST_TX_OPTIONS
+/** The default netif used for multicast */
+static struct netif* ip4_default_multicast_netif;
+
+/**
+ * @ingroup ip4
+ * Set a default netif for IPv4 multicast. */
+void
+ip4_set_default_multicast_netif(struct netif* default_multicast_netif)
+{
+  ip4_default_multicast_netif = default_multicast_netif;
+}
+#endif /* LWIP_MULTICAST_TX_OPTIONS */
+
+#ifdef LWIP_HOOK_IP4_ROUTE_SRC
+/**
+ * Source based IPv4 routing must be fully implemented in
+ * LWIP_HOOK_IP4_ROUTE_SRC(). This function only provides he parameters.
+ */
+struct netif *
+ip4_route_src(const ip4_addr_t *dest, const ip4_addr_t *src)
+{
+  if (src != NULL) {
+    /* when src==NULL, the hook is called from ip4_route(dest) */
+    struct netif *netif = LWIP_HOOK_IP4_ROUTE_SRC(dest, src);
+    if (netif != NULL) {
+      return netif;
+    }
+  }
+  return ip4_route(dest);
+}
+#endif /* LWIP_HOOK_IP4_ROUTE_SRC */
+
+/**
+ * Finds the appropriate network interface for a given IP address. It
+ * searches the list of network interfaces linearly. A match is found
+ * if the masked IP address of the network interface equals the masked
+ * IP address given to the function.
+ *
+ * @param dest the destination IP address for which to find the route
+ * @return the netif on which to send to reach dest
+ */
+struct netif *
+ip4_route(const ip4_addr_t *dest)
+{
+  struct netif *netif;
+
+#if LWIP_MULTICAST_TX_OPTIONS
+  /* Use administratively selected interface for multicast by default */
+  if (ip4_addr_ismulticast(dest) && ip4_default_multicast_netif) {
+    return ip4_default_multicast_netif;
+  }
+#endif /* LWIP_MULTICAST_TX_OPTIONS */
+
+  /* iterate through netifs */
+  for (netif = netif_list; netif != NULL; netif = netif->next) {
+    /* is the netif up, does it have a link and a valid address? */
+    if (netif_is_up(netif) && netif_is_link_up(netif) && !ip4_addr_isany_val(*netif_ip4_addr(netif))) {
+      /* network mask matches? */
+      if (ip4_addr_netcmp(dest, netif_ip4_addr(netif), netif_ip4_netmask(netif))) {
+        /* return netif on which to forward IP packet */
+        return netif;
+      }
+      /* gateway matches on a non broadcast interface? (i.e. peer in a point to point interface) */
+      if (((netif->flags & NETIF_FLAG_BROADCAST) == 0) && ip4_addr_cmp(dest, netif_ip4_gw(netif))) {
+        /* return netif on which to forward IP packet */
+        return netif;
+      }
+    }
+  }
+
+#if LWIP_NETIF_LOOPBACK && !LWIP_HAVE_LOOPIF
+  /* loopif is disabled, looopback traffic is passed through any netif */
+  if (ip4_addr_isloopback(dest)) {
+    /* don't check for link on loopback traffic */
+    if (netif_default != NULL && netif_is_up(netif_default)) {
+      return netif_default;
+    }
+    /* default netif is not up, just use any netif for loopback traffic */
+    for (netif = netif_list; netif != NULL; netif = netif->next) {
+      if (netif_is_up(netif)) {
+        return netif;
+      }
+    }
+    return NULL;
+  }
+#endif /* LWIP_NETIF_LOOPBACK && !LWIP_HAVE_LOOPIF */
+
+#ifdef LWIP_HOOK_IP4_ROUTE_SRC
+  netif = LWIP_HOOK_IP4_ROUTE_SRC(dest, NULL);
+  if (netif != NULL) {
+    return netif;
+  }
+#elif defined(LWIP_HOOK_IP4_ROUTE)
+  netif = LWIP_HOOK_IP4_ROUTE(dest);
+  if (netif != NULL) {
+    return netif;
+  }
+#endif
+
+  if ((netif_default == NULL) || !netif_is_up(netif_default) || !netif_is_link_up(netif_default) ||
+      ip4_addr_isany_val(*netif_ip4_addr(netif_default))) {
+    /* No matching netif found and default netif is not usable.
+       If this is not good enough for you, use LWIP_HOOK_IP4_ROUTE() */
+    LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip4_route: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+      ip4_addr1_16(dest), ip4_addr2_16(dest), ip4_addr3_16(dest), ip4_addr4_16(dest)));
+    IP_STATS_INC(ip.rterr);
+    MIB2_STATS_INC(mib2.ipoutnoroutes);
+    return NULL;
+  }
+
+  return netif_default;
+}
+
+#if IP_FORWARD
+/**
+ * Determine whether an IP address is in a reserved set of addresses
+ * that may not be forwarded, or whether datagrams to that destination
+ * may be forwarded.
+ * @param p the packet to forward
+ * @return 1: can forward 0: discard
+ */
+static int
+ip4_canforward(struct pbuf *p)
+{
+  u32_t addr = lwip_htonl(ip4_addr_get_u32(ip4_current_dest_addr()));
+
+  if (p->flags & PBUF_FLAG_LLBCAST) {
+    /* don't route link-layer broadcasts */
+    return 0;
+  }
+  if ((p->flags & PBUF_FLAG_LLMCAST) && !IP_MULTICAST(addr)) {
+    /* don't route link-layer multicasts unless the destination address is an IP
+       multicast address */
+    return 0;
+  }
+  if (IP_EXPERIMENTAL(addr)) {
+    return 0;
+  }
+  if (IP_CLASSA(addr)) {
+    u32_t net = addr & IP_CLASSA_NET;
+    if ((net == 0) || (net == ((u32_t)IP_LOOPBACKNET << IP_CLASSA_NSHIFT))) {
+      /* don't route loopback packets */
+      return 0;
+    }
+  }
+  return 1;
+}
+
+/**
+ * Forwards an IP packet. It finds an appropriate route for the
+ * packet, decrements the TTL value of the packet, adjusts the
+ * checksum and outputs the packet on the appropriate interface.
+ *
+ * @param p the packet to forward (p->payload points to IP header)
+ * @param iphdr the IP header of the input packet
+ * @param inp the netif on which this packet was received
+ */
+static void
+ip4_forward(struct pbuf *p, struct ip_hdr *iphdr, struct netif *inp)
+{
+  struct netif *netif;
+
+  PERF_START;
+  LWIP_UNUSED_ARG(inp);
+
+  if (!ip4_canforward(p)) {
+    goto return_noroute;
+  }
+
+  /* RFC3927 2.7: do not forward link-local addresses */
+  if (ip4_addr_islinklocal(ip4_current_dest_addr())) {
+    LWIP_DEBUGF(IP_DEBUG, ("ip4_forward: not forwarding LLA %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+      ip4_addr1_16(ip4_current_dest_addr()), ip4_addr2_16(ip4_current_dest_addr()),
+      ip4_addr3_16(ip4_current_dest_addr()), ip4_addr4_16(ip4_current_dest_addr())));
+    goto return_noroute;
+  }
+
+  /* Find network interface where to forward this IP packet to. */
+  netif = ip4_route_src(ip4_current_dest_addr(), ip4_current_src_addr());
+  if (netif == NULL) {
+    LWIP_DEBUGF(IP_DEBUG, ("ip4_forward: no forwarding route for %"U16_F".%"U16_F".%"U16_F".%"U16_F" found\n",
+      ip4_addr1_16(ip4_current_dest_addr()), ip4_addr2_16(ip4_current_dest_addr()),
+      ip4_addr3_16(ip4_current_dest_addr()), ip4_addr4_16(ip4_current_dest_addr())));
+    /* @todo: send ICMP_DUR_NET? */
+    goto return_noroute;
+  }
+#if !IP_FORWARD_ALLOW_TX_ON_RX_NETIF
+  /* Do not forward packets onto the same network interface on which
+   * they arrived. */
+  if (netif == inp) {
+    LWIP_DEBUGF(IP_DEBUG, ("ip4_forward: not bouncing packets back on incoming interface.\n"));
+    goto return_noroute;
+  }
+#endif /* IP_FORWARD_ALLOW_TX_ON_RX_NETIF */
+
+  /* decrement TTL */
+  IPH_TTL_SET(iphdr, IPH_TTL(iphdr) - 1);
+  /* send ICMP if TTL == 0 */
+  if (IPH_TTL(iphdr) == 0) {
+    MIB2_STATS_INC(mib2.ipinhdrerrors);
+#if LWIP_ICMP
+    /* Don't send ICMP messages in response to ICMP messages */
+    if (IPH_PROTO(iphdr) != IP_PROTO_ICMP) {
+      icmp_time_exceeded(p, ICMP_TE_TTL);
+    }
+#endif /* LWIP_ICMP */
+    return;
+  }
+
+  /* Incrementally update the IP checksum. */
+  if (IPH_CHKSUM(iphdr) >= PP_HTONS(0xffffU - 0x100)) {
+    IPH_CHKSUM_SET(iphdr, IPH_CHKSUM(iphdr) + PP_HTONS(0x100) + 1);
+  } else {
+    IPH_CHKSUM_SET(iphdr, IPH_CHKSUM(iphdr) + PP_HTONS(0x100));
+  }
+
+  LWIP_DEBUGF(IP_DEBUG, ("ip4_forward: forwarding packet to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+    ip4_addr1_16(ip4_current_dest_addr()), ip4_addr2_16(ip4_current_dest_addr()),
+    ip4_addr3_16(ip4_current_dest_addr()), ip4_addr4_16(ip4_current_dest_addr())));
+
+  IP_STATS_INC(ip.fw);
+  MIB2_STATS_INC(mib2.ipforwdatagrams);
+  IP_STATS_INC(ip.xmit);
+
+  PERF_STOP("ip4_forward");
+  /* don't fragment if interface has mtu set to 0 [loopif] */
+  if (netif->mtu && (p->tot_len > netif->mtu)) {
+    if ((IPH_OFFSET(iphdr) & PP_NTOHS(IP_DF)) == 0) {
+#if IP_FRAG
+      ip4_frag(p, netif, ip4_current_dest_addr());
+#else /* IP_FRAG */
+      /* @todo: send ICMP Destination Unreachable code 13 "Communication administratively prohibited"? */
+#endif /* IP_FRAG */
+    } else {
+#if LWIP_ICMP
+      /* send ICMP Destination Unreachable code 4: "Fragmentation Needed and DF Set" */
+      icmp_dest_unreach(p, ICMP_DUR_FRAG);
+#endif /* LWIP_ICMP */
+    }
+    return;
+  }
+  /* transmit pbuf on chosen interface */
+  netif->output(netif, p, ip4_current_dest_addr());
+  return;
+return_noroute:
+  MIB2_STATS_INC(mib2.ipoutnoroutes);
+}
+#endif /* IP_FORWARD */
+
+/**
+ * This function is called by the network interface device driver when
+ * an IP packet is received. The function does the basic checks of the
+ * IP header such as packet size being at least larger than the header
+ * size etc. If the packet was not destined for us, the packet is
+ * forwarded (using ip_forward). The IP checksum is always checked.
+ *
+ * Finally, the packet is sent to the upper layer protocol input function.
+ *
+ * @param p the received IP packet (p->payload points to IP header)
+ * @param inp the netif on which this packet was received
+ * @return ERR_OK if the packet was processed (could return ERR_* if it wasn't
+ *         processed, but currently always returns ERR_OK)
+ */
+err_t
+ip4_input(struct pbuf *p, struct netif *inp)
+{
+  struct ip_hdr *iphdr;
+  struct netif *netif;
+  u16_t iphdr_hlen;
+  u16_t iphdr_len;
+#if IP_ACCEPT_LINK_LAYER_ADDRESSING || LWIP_IGMP
+  int check_ip_src = 1;
+#endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING || LWIP_IGMP */
+
+  IP_STATS_INC(ip.recv);
+  MIB2_STATS_INC(mib2.ipinreceives);
+
+  /* identify the IP header */
+  iphdr = (struct ip_hdr *)p->payload;
+  if (IPH_V(iphdr) != 4) {
+    LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_WARNING, ("IP packet dropped due to bad version number %"U16_F"\n", (u16_t)IPH_V(iphdr)));
+    ip4_debug_print(p);
+    pbuf_free(p);
+    IP_STATS_INC(ip.err);
+    IP_STATS_INC(ip.drop);
+    MIB2_STATS_INC(mib2.ipinhdrerrors);
+    return ERR_OK;
+  }
+
+#ifdef LWIP_HOOK_IP4_INPUT
+  if (LWIP_HOOK_IP4_INPUT(p, inp)) {
+    /* the packet has been eaten */
+    return ERR_OK;
+  }
+#endif
+
+  /* obtain IP header length in number of 32-bit words */
+  iphdr_hlen = IPH_HL(iphdr);
+  /* calculate IP header length in bytes */
+  iphdr_hlen *= 4;
+  /* obtain ip length in bytes */
+  iphdr_len = lwip_ntohs(IPH_LEN(iphdr));
+
+  /* Trim pbuf. This is especially required for packets < 60 bytes. */
+  if (iphdr_len < p->tot_len) {
+    pbuf_realloc(p, iphdr_len);
+  }
+
+  /* header length exceeds first pbuf length, or ip length exceeds total pbuf length? */
+  if ((iphdr_hlen > p->len) || (iphdr_len > p->tot_len) || (iphdr_hlen < IP_HLEN)) {
+    if (iphdr_hlen < IP_HLEN) {
+      LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+        ("ip4_input: short IP header (%"U16_F" bytes) received, IP packet dropped\n", iphdr_hlen));
+    }
+    if (iphdr_hlen > p->len) {
+      LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+        ("IP header (len %"U16_F") does not fit in first pbuf (len %"U16_F"), IP packet dropped.\n",
+        iphdr_hlen, p->len));
+    }
+    if (iphdr_len > p->tot_len) {
+      LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+        ("IP (len %"U16_F") is longer than pbuf (len %"U16_F"), IP packet dropped.\n",
+        iphdr_len, p->tot_len));
+    }
+    /* free (drop) packet pbufs */
+    pbuf_free(p);
+    IP_STATS_INC(ip.lenerr);
+    IP_STATS_INC(ip.drop);
+    MIB2_STATS_INC(mib2.ipindiscards);
+    return ERR_OK;
+  }
+
+  /* verify checksum */
+#if CHECKSUM_CHECK_IP
+  IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_CHECK_IP) {
+    if (inet_chksum(iphdr, iphdr_hlen) != 0) {
+
+      LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+        ("Checksum (0x%"X16_F") failed, IP packet dropped.\n", inet_chksum(iphdr, iphdr_hlen)));
+      ip4_debug_print(p);
+      pbuf_free(p);
+      IP_STATS_INC(ip.chkerr);
+      IP_STATS_INC(ip.drop);
+      MIB2_STATS_INC(mib2.ipinhdrerrors);
+      return ERR_OK;
+    }
+  }
+#endif
+
+  /* copy IP addresses to aligned ip_addr_t */
+  ip_addr_copy_from_ip4(ip_data.current_iphdr_dest, iphdr->dest);
+  ip_addr_copy_from_ip4(ip_data.current_iphdr_src, iphdr->src);
+
+  /* match packet against an interface, i.e. is this packet for us? */
+  if (ip4_addr_ismulticast(ip4_current_dest_addr())) {
+#if LWIP_IGMP
+    if ((inp->flags & NETIF_FLAG_IGMP) && (igmp_lookfor_group(inp, ip4_current_dest_addr()))) {
+      /* IGMP snooping switches need 0.0.0.0 to be allowed as source address (RFC 4541) */
+      ip4_addr_t allsystems;
+      IP4_ADDR(&allsystems, 224, 0, 0, 1);
+      if (ip4_addr_cmp(ip4_current_dest_addr(), &allsystems) &&
+          ip4_addr_isany(ip4_current_src_addr())) {
+        check_ip_src = 0;
+      }
+      netif = inp;
+    } else {
+      netif = NULL;
+    }
+#else /* LWIP_IGMP */
+    if ((netif_is_up(inp)) && (!ip4_addr_isany_val(*netif_ip4_addr(inp)))) {
+      netif = inp;
+    } else {
+      netif = NULL;
+    }
+#endif /* LWIP_IGMP */
+  } else {
+    /* start trying with inp. if that's not acceptable, start walking the
+       list of configured netifs.
+       'first' is used as a boolean to mark whether we started walking the list */
+    int first = 1;
+    netif = inp;
+    do {
+      LWIP_DEBUGF(IP_DEBUG, ("ip_input: iphdr->dest 0x%"X32_F" netif->ip_addr 0x%"X32_F" (0x%"X32_F", 0x%"X32_F", 0x%"X32_F")\n",
+          ip4_addr_get_u32(&iphdr->dest), ip4_addr_get_u32(netif_ip4_addr(netif)),
+          ip4_addr_get_u32(&iphdr->dest) & ip4_addr_get_u32(netif_ip4_netmask(netif)),
+          ip4_addr_get_u32(netif_ip4_addr(netif)) & ip4_addr_get_u32(netif_ip4_netmask(netif)),
+          ip4_addr_get_u32(&iphdr->dest) & ~ip4_addr_get_u32(netif_ip4_netmask(netif))));
+
+      /* interface is up and configured? */
+      if ((netif_is_up(netif)) && (!ip4_addr_isany_val(*netif_ip4_addr(netif)))) {
+        /* unicast to this interface address? */
+        if (ip4_addr_cmp(ip4_current_dest_addr(), netif_ip4_addr(netif)) ||
+            /* or broadcast on this interface network address? */
+            ip4_addr_isbroadcast(ip4_current_dest_addr(), netif)
+#if LWIP_NETIF_LOOPBACK && !LWIP_HAVE_LOOPIF
+            || (ip4_addr_get_u32(ip4_current_dest_addr()) == PP_HTONL(IPADDR_LOOPBACK))
+#endif /* LWIP_NETIF_LOOPBACK && !LWIP_HAVE_LOOPIF */
+            ) {
+          LWIP_DEBUGF(IP_DEBUG, ("ip4_input: packet accepted on interface %c%c\n",
+              netif->name[0], netif->name[1]));
+          /* break out of for loop */
+          break;
+        }
+#if LWIP_AUTOIP
+        /* connections to link-local addresses must persist after changing
+           the netif's address (RFC3927 ch. 1.9) */
+        if (autoip_accept_packet(netif, ip4_current_dest_addr())) {
+          LWIP_DEBUGF(IP_DEBUG, ("ip4_input: LLA packet accepted on interface %c%c\n",
+              netif->name[0], netif->name[1]));
+          /* break out of for loop */
+          break;
+        }
+#endif /* LWIP_AUTOIP */
+      }
+      if (first) {
+#if !LWIP_NETIF_LOOPBACK || LWIP_HAVE_LOOPIF
+        /* Packets sent to the loopback address must not be accepted on an
+         * interface that does not have the loopback address assigned to it,
+         * unless a non-loopback interface is used for loopback traffic. */
+        if (ip4_addr_isloopback(ip4_current_dest_addr())) {
+          netif = NULL;
+          break;
+        }
+#endif /* !LWIP_NETIF_LOOPBACK || LWIP_HAVE_LOOPIF */
+        first = 0;
+        netif = netif_list;
+      } else {
+        netif = netif->next;
+      }
+      if (netif == inp) {
+        netif = netif->next;
+      }
+    } while (netif != NULL);
+  }
+
+#if IP_ACCEPT_LINK_LAYER_ADDRESSING
+  /* Pass DHCP messages regardless of destination address. DHCP traffic is addressed
+   * using link layer addressing (such as Ethernet MAC) so we must not filter on IP.
+   * According to RFC 1542 section 3.1.1, referred by RFC 2131).
+   *
+   * If you want to accept private broadcast communication while a netif is down,
+   * define LWIP_IP_ACCEPT_UDP_PORT(dst_port), e.g.:
+   *
+   * #define LWIP_IP_ACCEPT_UDP_PORT(dst_port) ((dst_port) == PP_NTOHS(12345))
+   */
+  if (netif == NULL) {
+    /* remote port is DHCP server? */
+    if (IPH_PROTO(iphdr) == IP_PROTO_UDP) {
+      struct udp_hdr *udphdr = (struct udp_hdr *)((u8_t *)iphdr + iphdr_hlen);
+      LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE, ("ip4_input: UDP packet to DHCP client port %"U16_F"\n",
+        lwip_ntohs(udphdr->dest)));
+      if (IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(udphdr->dest)) {
+        LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE, ("ip4_input: DHCP packet accepted.\n"));
+        netif = inp;
+        check_ip_src = 0;
+      }
+    }
+  }
+#endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING */
+
+  /* broadcast or multicast packet source address? Compliant with RFC 1122: 3.2.1.3 */
+#if LWIP_IGMP || IP_ACCEPT_LINK_LAYER_ADDRESSING
+  if (check_ip_src
+#if IP_ACCEPT_LINK_LAYER_ADDRESSING
+  /* DHCP servers need 0.0.0.0 to be allowed as source address (RFC 1.1.2.2: 3.2.1.3/a) */
+      && !ip4_addr_isany_val(*ip4_current_src_addr())
+#endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING */
+     )
+#endif /* LWIP_IGMP || IP_ACCEPT_LINK_LAYER_ADDRESSING */
+  {
+    if ((ip4_addr_isbroadcast(ip4_current_src_addr(), inp)) ||
+        (ip4_addr_ismulticast(ip4_current_src_addr()))) {
+      /* packet source is not valid */
+      LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("ip4_input: packet source is not valid.\n"));
+      /* free (drop) packet pbufs */
+      pbuf_free(p);
+      IP_STATS_INC(ip.drop);
+      MIB2_STATS_INC(mib2.ipinaddrerrors);
+      MIB2_STATS_INC(mib2.ipindiscards);
+      return ERR_OK;
+    }
+  }
+
+  /* packet not for us? */
+  if (netif == NULL) {
+    /* packet not for us, route or discard */
+    LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE, ("ip4_input: packet not for us.\n"));
+#if IP_FORWARD
+    /* non-broadcast packet? */
+    if (!ip4_addr_isbroadcast(ip4_current_dest_addr(), inp)) {
+      /* try to forward IP packet on (other) interfaces */
+      ip4_forward(p, iphdr, inp);
+    } else
+#endif /* IP_FORWARD */
+    {
+      IP_STATS_INC(ip.drop);
+      MIB2_STATS_INC(mib2.ipinaddrerrors);
+      MIB2_STATS_INC(mib2.ipindiscards);
+    }
+    pbuf_free(p);
+    return ERR_OK;
+  }
+  /* packet consists of multiple fragments? */
+  if ((IPH_OFFSET(iphdr) & PP_HTONS(IP_OFFMASK | IP_MF)) != 0) {
+#if IP_REASSEMBLY /* packet fragment reassembly code present? */
+    LWIP_DEBUGF(IP_DEBUG, ("IP packet is a fragment (id=0x%04"X16_F" tot_len=%"U16_F" len=%"U16_F" MF=%"U16_F" offset=%"U16_F"), calling ip4_reass()\n",
+      lwip_ntohs(IPH_ID(iphdr)), p->tot_len, lwip_ntohs(IPH_LEN(iphdr)), (u16_t)!!(IPH_OFFSET(iphdr) & PP_HTONS(IP_MF)), (u16_t)((lwip_ntohs(IPH_OFFSET(iphdr)) & IP_OFFMASK)*8)));
+    /* reassemble the packet*/
+    p = ip4_reass(p);
+    /* packet not fully reassembled yet? */
+    if (p == NULL) {
+      return ERR_OK;
+    }
+    iphdr = (struct ip_hdr *)p->payload;
+#else /* IP_REASSEMBLY == 0, no packet fragment reassembly code present */
+    pbuf_free(p);
+    LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("IP packet dropped since it was fragmented (0x%"X16_F") (while IP_REASSEMBLY == 0).\n",
+      lwip_ntohs(IPH_OFFSET(iphdr))));
+    IP_STATS_INC(ip.opterr);
+    IP_STATS_INC(ip.drop);
+    /* unsupported protocol feature */
+    MIB2_STATS_INC(mib2.ipinunknownprotos);
+    return ERR_OK;
+#endif /* IP_REASSEMBLY */
+  }
+
+#if IP_OPTIONS_ALLOWED == 0 /* no support for IP options in the IP header? */
+
+#if LWIP_IGMP
+  /* there is an extra "router alert" option in IGMP messages which we allow for but do not police */
+  if ((iphdr_hlen > IP_HLEN) &&  (IPH_PROTO(iphdr) != IP_PROTO_IGMP)) {
+#else
+  if (iphdr_hlen > IP_HLEN) {
+#endif /* LWIP_IGMP */
+    LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("IP packet dropped since there were IP options (while IP_OPTIONS_ALLOWED == 0).\n"));
+    pbuf_free(p);
+    IP_STATS_INC(ip.opterr);
+    IP_STATS_INC(ip.drop);
+    /* unsupported protocol feature */
+    MIB2_STATS_INC(mib2.ipinunknownprotos);
+    return ERR_OK;
+  }
+#endif /* IP_OPTIONS_ALLOWED == 0 */
+
+  /* send to upper layers */
+  LWIP_DEBUGF(IP_DEBUG, ("ip4_input: \n"));
+  ip4_debug_print(p);
+  LWIP_DEBUGF(IP_DEBUG, ("ip4_input: p->len %"U16_F" p->tot_len %"U16_F"\n", p->len, p->tot_len));
+
+  ip_data.current_netif = netif;
+  ip_data.current_input_netif = inp;
+  ip_data.current_ip4_header = iphdr;
+  ip_data.current_ip_header_tot_len = IPH_HL(iphdr) * 4;
+
+#if LWIP_RAW
+  /* raw input did not eat the packet? */
+  if (raw_input(p, inp) == 0)
+#endif /* LWIP_RAW */
+  {
+    pbuf_header(p, -(s16_t)iphdr_hlen); /* Move to payload, no check necessary. */
+
+    switch (IPH_PROTO(iphdr)) {
+#if LWIP_UDP
+    case IP_PROTO_UDP:
+#if LWIP_UDPLITE
+    case IP_PROTO_UDPLITE:
+#endif /* LWIP_UDPLITE */
+      MIB2_STATS_INC(mib2.ipindelivers);
+      udp_input(p, inp);
+      break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+    case IP_PROTO_TCP:
+      MIB2_STATS_INC(mib2.ipindelivers);
+      tcp_input(p, inp);
+      break;
+#endif /* LWIP_TCP */
+#if LWIP_ICMP
+    case IP_PROTO_ICMP:
+      MIB2_STATS_INC(mib2.ipindelivers);
+      icmp_input(p, inp);
+      break;
+#endif /* LWIP_ICMP */
+#if LWIP_IGMP
+    case IP_PROTO_IGMP:
+      igmp_input(p, inp, ip4_current_dest_addr());
+      break;
+#endif /* LWIP_IGMP */
+    default:
+#if LWIP_ICMP
+      /* send ICMP destination protocol unreachable unless is was a broadcast */
+      if (!ip4_addr_isbroadcast(ip4_current_dest_addr(), netif) &&
+          !ip4_addr_ismulticast(ip4_current_dest_addr())) {
+        pbuf_header_force(p, iphdr_hlen); /* Move to ip header, no check necessary. */
+        p->payload = iphdr;
+        icmp_dest_unreach(p, ICMP_DUR_PROTO);
+      }
+#endif /* LWIP_ICMP */
+      pbuf_free(p);
+
+      LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("Unsupported transport protocol %"U16_F"\n", (u16_t)IPH_PROTO(iphdr)));
+
+      IP_STATS_INC(ip.proterr);
+      IP_STATS_INC(ip.drop);
+      MIB2_STATS_INC(mib2.ipinunknownprotos);
+    }
+  }
+
+  /* @todo: this is not really necessary... */
+  ip_data.current_netif = NULL;
+  ip_data.current_input_netif = NULL;
+  ip_data.current_ip4_header = NULL;
+  ip_data.current_ip_header_tot_len = 0;
+  ip4_addr_set_any(ip4_current_src_addr());
+  ip4_addr_set_any(ip4_current_dest_addr());
+
+  return ERR_OK;
+}
+
+/**
+ * Sends an IP packet on a network interface. This function constructs
+ * the IP header and calculates the IP header checksum. If the source
+ * IP address is NULL, the IP address of the outgoing network
+ * interface is filled in as source address.
+ * If the destination IP address is LWIP_IP_HDRINCL, p is assumed to already
+ * include an IP header and p->payload points to it instead of the data.
+ *
+ * @param p the packet to send (p->payload points to the data, e.g. next
+            protocol header; if dest == LWIP_IP_HDRINCL, p already includes an
+            IP header and p->payload points to that IP header)
+ * @param src the source IP address to send from (if src == IP4_ADDR_ANY, the
+ *         IP  address of the netif used to send is used as source address)
+ * @param dest the destination IP address to send the packet to
+ * @param ttl the TTL value to be set in the IP header
+ * @param tos the TOS value to be set in the IP header
+ * @param proto the PROTOCOL to be set in the IP header
+ * @param netif the netif on which to send this packet
+ * @return ERR_OK if the packet was sent OK
+ *         ERR_BUF if p doesn't have enough space for IP/LINK headers
+ *         returns errors returned by netif->output
+ *
+ * @note ip_id: RFC791 "some host may be able to simply use
+ *  unique identifiers independent of destination"
+ */
+err_t
+ip4_output_if(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest,
+             u8_t ttl, u8_t tos,
+             u8_t proto, struct netif *netif)
+{
+#if IP_OPTIONS_SEND
+  return ip4_output_if_opt(p, src, dest, ttl, tos, proto, netif, NULL, 0);
+}
+
+/**
+ * Same as ip_output_if() but with the possibility to include IP options:
+ *
+ * @ param ip_options pointer to the IP options, copied into the IP header
+ * @ param optlen length of ip_options
+ */
+err_t
+ip4_output_if_opt(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest,
+       u8_t ttl, u8_t tos, u8_t proto, struct netif *netif, void *ip_options,
+       u16_t optlen)
+{
+#endif /* IP_OPTIONS_SEND */
+  const ip4_addr_t *src_used = src;
+  if (dest != LWIP_IP_HDRINCL) {
+    if (ip4_addr_isany(src)) {
+      src_used = netif_ip4_addr(netif);
+    }
+  }
+
+#if IP_OPTIONS_SEND
+  return ip4_output_if_opt_src(p, src_used, dest, ttl, tos, proto, netif,
+    ip_options, optlen);
+#else /* IP_OPTIONS_SEND */
+  return ip4_output_if_src(p, src_used, dest, ttl, tos, proto, netif);
+#endif /* IP_OPTIONS_SEND */
+}
+
+/**
+ * Same as ip_output_if() but 'src' address is not replaced by netif address
+ * when it is 'any'.
+ */
+err_t
+ip4_output_if_src(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest,
+             u8_t ttl, u8_t tos,
+             u8_t proto, struct netif *netif)
+{
+#if IP_OPTIONS_SEND
+  return ip4_output_if_opt_src(p, src, dest, ttl, tos, proto, netif, NULL, 0);
+}
+
+/**
+ * Same as ip_output_if_opt() but 'src' address is not replaced by netif address
+ * when it is 'any'.
+ */
+err_t
+ip4_output_if_opt_src(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest,
+       u8_t ttl, u8_t tos, u8_t proto, struct netif *netif, void *ip_options,
+       u16_t optlen)
+{
+#endif /* IP_OPTIONS_SEND */
+  struct ip_hdr *iphdr;
+  ip4_addr_t dest_addr;
+#if CHECKSUM_GEN_IP_INLINE
+  u32_t chk_sum = 0;
+#endif /* CHECKSUM_GEN_IP_INLINE */
+
+  LWIP_IP_CHECK_PBUF_REF_COUNT_FOR_TX(p);
+
+  MIB2_STATS_INC(mib2.ipoutrequests);
+
+  /* Should the IP header be generated or is it already included in p? */
+  if (dest != LWIP_IP_HDRINCL) {
+    u16_t ip_hlen = IP_HLEN;
+#if IP_OPTIONS_SEND
+    u16_t optlen_aligned = 0;
+    if (optlen != 0) {
+#if CHECKSUM_GEN_IP_INLINE
+      int i;
+#endif /* CHECKSUM_GEN_IP_INLINE */
+      /* round up to a multiple of 4 */
+      optlen_aligned = ((optlen + 3) & ~3);
+      ip_hlen += optlen_aligned;
+      /* First write in the IP options */
+      if (pbuf_header(p, optlen_aligned)) {
+        LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip4_output_if_opt: not enough room for IP options in pbuf\n"));
+        IP_STATS_INC(ip.err);
+        MIB2_STATS_INC(mib2.ipoutdiscards);
+        return ERR_BUF;
+      }
+      MEMCPY(p->payload, ip_options, optlen);
+      if (optlen < optlen_aligned) {
+        /* zero the remaining bytes */
+        memset(((char*)p->payload) + optlen, 0, optlen_aligned - optlen);
+      }
+#if CHECKSUM_GEN_IP_INLINE
+      for (i = 0; i < optlen_aligned/2; i++) {
+        chk_sum += ((u16_t*)p->payload)[i];
+      }
+#endif /* CHECKSUM_GEN_IP_INLINE */
+    }
+#endif /* IP_OPTIONS_SEND */
+    /* generate IP header */
+    if (pbuf_header(p, IP_HLEN)) {
+      LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip4_output: not enough room for IP header in pbuf\n"));
+
+      IP_STATS_INC(ip.err);
+      MIB2_STATS_INC(mib2.ipoutdiscards);
+      return ERR_BUF;
+    }
+
+    iphdr = (struct ip_hdr *)p->payload;
+    LWIP_ASSERT("check that first pbuf can hold struct ip_hdr",
+               (p->len >= sizeof(struct ip_hdr)));
+
+    IPH_TTL_SET(iphdr, ttl);
+    IPH_PROTO_SET(iphdr, proto);
+#if CHECKSUM_GEN_IP_INLINE
+    chk_sum += PP_NTOHS(proto | (ttl << 8));
+#endif /* CHECKSUM_GEN_IP_INLINE */
+
+    /* dest cannot be NULL here */
+    ip4_addr_copy(iphdr->dest, *dest);
+#if CHECKSUM_GEN_IP_INLINE
+    chk_sum += ip4_addr_get_u32(&iphdr->dest) & 0xFFFF;
+    chk_sum += ip4_addr_get_u32(&iphdr->dest) >> 16;
+#endif /* CHECKSUM_GEN_IP_INLINE */
+
+    IPH_VHL_SET(iphdr, 4, ip_hlen / 4);
+    IPH_TOS_SET(iphdr, tos);
+#if CHECKSUM_GEN_IP_INLINE
+    chk_sum += PP_NTOHS(tos | (iphdr->_v_hl << 8));
+#endif /* CHECKSUM_GEN_IP_INLINE */
+    IPH_LEN_SET(iphdr, lwip_htons(p->tot_len));
+#if CHECKSUM_GEN_IP_INLINE
+    chk_sum += iphdr->_len;
+#endif /* CHECKSUM_GEN_IP_INLINE */
+    IPH_OFFSET_SET(iphdr, 0);
+    IPH_ID_SET(iphdr, lwip_htons(ip_id));
+#if CHECKSUM_GEN_IP_INLINE
+    chk_sum += iphdr->_id;
+#endif /* CHECKSUM_GEN_IP_INLINE */
+    ++ip_id;
+
+    if (src == NULL) {
+      ip4_addr_copy(iphdr->src, *IP4_ADDR_ANY4);
+    } else {
+      /* src cannot be NULL here */
+      ip4_addr_copy(iphdr->src, *src);
+    }
+
+#if CHECKSUM_GEN_IP_INLINE
+    chk_sum += ip4_addr_get_u32(&iphdr->src) & 0xFFFF;
+    chk_sum += ip4_addr_get_u32(&iphdr->src) >> 16;
+    chk_sum = (chk_sum >> 16) + (chk_sum & 0xFFFF);
+    chk_sum = (chk_sum >> 16) + chk_sum;
+    chk_sum = ~chk_sum;
+    IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_IP) {
+      iphdr->_chksum = (u16_t)chk_sum; /* network order */
+    }
+#if LWIP_CHECKSUM_CTRL_PER_NETIF
+    else {
+      IPH_CHKSUM_SET(iphdr, 0);
+    }
+#endif /* LWIP_CHECKSUM_CTRL_PER_NETIF*/
+#else /* CHECKSUM_GEN_IP_INLINE */
+    IPH_CHKSUM_SET(iphdr, 0);
+#if CHECKSUM_GEN_IP
+    IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_IP) {
+      IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, ip_hlen));
+    }
+#endif /* CHECKSUM_GEN_IP */
+#endif /* CHECKSUM_GEN_IP_INLINE */
+  } else {
+    /* IP header already included in p */
+    iphdr = (struct ip_hdr *)p->payload;
+    ip4_addr_copy(dest_addr, iphdr->dest);
+    dest = &dest_addr;
+  }
+
+  IP_STATS_INC(ip.xmit);
+
+  LWIP_DEBUGF(IP_DEBUG, ("ip4_output_if: %c%c%"U16_F"\n", netif->name[0], netif->name[1], (u16_t)netif->num));
+  ip4_debug_print(p);
+
+#if ENABLE_LOOPBACK
+  if (ip4_addr_cmp(dest, netif_ip4_addr(netif))
+#if !LWIP_HAVE_LOOPIF
+      || ip4_addr_isloopback(dest)
+#endif /* !LWIP_HAVE_LOOPIF */
+      ) {
+    /* Packet to self, enqueue it for loopback */
+    LWIP_DEBUGF(IP_DEBUG, ("netif_loop_output()"));
+    return netif_loop_output(netif, p);
+  }
+#if LWIP_MULTICAST_TX_OPTIONS
+  if ((p->flags & PBUF_FLAG_MCASTLOOP) != 0) {
+    netif_loop_output(netif, p);
+  }
+#endif /* LWIP_MULTICAST_TX_OPTIONS */
+#endif /* ENABLE_LOOPBACK */
+#if IP_FRAG
+  /* don't fragment if interface has mtu set to 0 [loopif] */
+  if (netif->mtu && (p->tot_len > netif->mtu)) {
+    return ip4_frag(p, netif, dest);
+  }
+#endif /* IP_FRAG */
+
+  LWIP_DEBUGF(IP_DEBUG, ("ip4_output_if: call netif->output()\n"));
+  return netif->output(netif, p, dest);
+}
+
+/**
+ * Simple interface to ip_output_if. It finds the outgoing network
+ * interface and calls upon ip_output_if to do the actual work.
+ *
+ * @param p the packet to send (p->payload points to the data, e.g. next
+            protocol header; if dest == LWIP_IP_HDRINCL, p already includes an
+            IP header and p->payload points to that IP header)
+ * @param src the source IP address to send from (if src == IP4_ADDR_ANY, the
+ *         IP  address of the netif used to send is used as source address)
+ * @param dest the destination IP address to send the packet to
+ * @param ttl the TTL value to be set in the IP header
+ * @param tos the TOS value to be set in the IP header
+ * @param proto the PROTOCOL to be set in the IP header
+ *
+ * @return ERR_RTE if no route is found
+ *         see ip_output_if() for more return values
+ */
+err_t
+ip4_output(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest,
+          u8_t ttl, u8_t tos, u8_t proto)
+{
+  struct netif *netif;
+
+  LWIP_IP_CHECK_PBUF_REF_COUNT_FOR_TX(p);
+
+  if ((netif = ip4_route_src(dest, src)) == NULL) {
+    LWIP_DEBUGF(IP_DEBUG, ("ip4_output: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+      ip4_addr1_16(dest), ip4_addr2_16(dest), ip4_addr3_16(dest), ip4_addr4_16(dest)));
+    IP_STATS_INC(ip.rterr);
+    return ERR_RTE;
+  }
+
+  return ip4_output_if(p, src, dest, ttl, tos, proto, netif);
+}
+
+#if LWIP_NETIF_HWADDRHINT
+/** Like ip_output, but takes and addr_hint pointer that is passed on to netif->addr_hint
+ *  before calling ip_output_if.
+ *
+ * @param p the packet to send (p->payload points to the data, e.g. next
+            protocol header; if dest == LWIP_IP_HDRINCL, p already includes an
+            IP header and p->payload points to that IP header)
+ * @param src the source IP address to send from (if src == IP4_ADDR_ANY, the
+ *         IP  address of the netif used to send is used as source address)
+ * @param dest the destination IP address to send the packet to
+ * @param ttl the TTL value to be set in the IP header
+ * @param tos the TOS value to be set in the IP header
+ * @param proto the PROTOCOL to be set in the IP header
+ * @param addr_hint address hint pointer set to netif->addr_hint before
+ *        calling ip_output_if()
+ *
+ * @return ERR_RTE if no route is found
+ *         see ip_output_if() for more return values
+ */
+err_t
+ip4_output_hinted(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest,
+          u8_t ttl, u8_t tos, u8_t proto, u8_t *addr_hint)
+{
+  struct netif *netif;
+  err_t err;
+
+  LWIP_IP_CHECK_PBUF_REF_COUNT_FOR_TX(p);
+
+  if ((netif = ip4_route_src(dest, src)) == NULL) {
+    LWIP_DEBUGF(IP_DEBUG, ("ip4_output: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+      ip4_addr1_16(dest), ip4_addr2_16(dest), ip4_addr3_16(dest), ip4_addr4_16(dest)));
+    IP_STATS_INC(ip.rterr);
+    return ERR_RTE;
+  }
+
+  NETIF_SET_HWADDRHINT(netif, addr_hint);
+  err = ip4_output_if(p, src, dest, ttl, tos, proto, netif);
+  NETIF_SET_HWADDRHINT(netif, NULL);
+
+  return err;
+}
+#endif /* LWIP_NETIF_HWADDRHINT*/
+
+#if IP_DEBUG
+/* Print an IP header by using LWIP_DEBUGF
+ * @param p an IP packet, p->payload pointing to the IP header
+ */
+void
+ip4_debug_print(struct pbuf *p)
+{
+  struct ip_hdr *iphdr = (struct ip_hdr *)p->payload;
+
+  LWIP_DEBUGF(IP_DEBUG, ("IP header:\n"));
+  LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
+  LWIP_DEBUGF(IP_DEBUG, ("|%2"S16_F" |%2"S16_F" |  0x%02"X16_F" |     %5"U16_F"     | (v, hl, tos, len)\n",
+                    (u16_t)IPH_V(iphdr),
+                    (u16_t)IPH_HL(iphdr),
+                    (u16_t)IPH_TOS(iphdr),
+                    lwip_ntohs(IPH_LEN(iphdr))));
+  LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
+  LWIP_DEBUGF(IP_DEBUG, ("|    %5"U16_F"      |%"U16_F"%"U16_F"%"U16_F"|    %4"U16_F"   | (id, flags, offset)\n",
+                    lwip_ntohs(IPH_ID(iphdr)),
+                    (u16_t)(lwip_ntohs(IPH_OFFSET(iphdr)) >> 15 & 1),
+                    (u16_t)(lwip_ntohs(IPH_OFFSET(iphdr)) >> 14 & 1),
+                    (u16_t)(lwip_ntohs(IPH_OFFSET(iphdr)) >> 13 & 1),
+                    (u16_t)(lwip_ntohs(IPH_OFFSET(iphdr)) & IP_OFFMASK)));
+  LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
+  LWIP_DEBUGF(IP_DEBUG, ("|  %3"U16_F"  |  %3"U16_F"  |    0x%04"X16_F"     | (ttl, proto, chksum)\n",
+                    (u16_t)IPH_TTL(iphdr),
+                    (u16_t)IPH_PROTO(iphdr),
+                    lwip_ntohs(IPH_CHKSUM(iphdr))));
+  LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
+  LWIP_DEBUGF(IP_DEBUG, ("|  %3"U16_F"  |  %3"U16_F"  |  %3"U16_F"  |  %3"U16_F"  | (src)\n",
+                    ip4_addr1_16(&iphdr->src),
+                    ip4_addr2_16(&iphdr->src),
+                    ip4_addr3_16(&iphdr->src),
+                    ip4_addr4_16(&iphdr->src)));
+  LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
+  LWIP_DEBUGF(IP_DEBUG, ("|  %3"U16_F"  |  %3"U16_F"  |  %3"U16_F"  |  %3"U16_F"  | (dest)\n",
+                    ip4_addr1_16(&iphdr->dest),
+                    ip4_addr2_16(&iphdr->dest),
+                    ip4_addr3_16(&iphdr->dest),
+                    ip4_addr4_16(&iphdr->dest)));
+  LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
+}
+#endif /* IP_DEBUG */
+
+#endif /* LWIP_IPV4 */

+ 331 - 312
thirdparty/lwip/src/core/ipv4/ip_addr.c → thirdparty/lwip/src/core/ipv4/ip4_addr.c

@@ -1,312 +1,331 @@
-/**
- * @file
- * This is the IPv4 address tools implementation.
- *
- */
-
-/*
- * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
- * All rights reserved. 
- * 
- * Redistribution and use in source and binary forms, with or without modification, 
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission. 
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
- * OF SUCH DAMAGE.
- *
- * This file is part of the lwIP TCP/IP stack.
- * 
- * Author: Adam Dunkels <adam@sics.se>
- *
- */
-
-#include "lwip/opt.h"
-#include "lwip/ip_addr.h"
-#include "lwip/netif.h"
-
-/* used by IP_ADDR_ANY and IP_ADDR_BROADCAST in ip_addr.h */
-const ip_addr_t ip_addr_any = { IPADDR_ANY };
-const ip_addr_t ip_addr_broadcast = { IPADDR_BROADCAST };
-
-/**
- * Determine if an address is a broadcast address on a network interface 
- * 
- * @param addr address to be checked
- * @param netif the network interface against which the address is checked
- * @return returns non-zero if the address is a broadcast address
- */
-u8_t
-ip4_addr_isbroadcast(u32_t addr, const struct netif *netif)
-{
-  ip_addr_t ipaddr;
-  ip4_addr_set_u32(&ipaddr, addr);
-
-  /* all ones (broadcast) or all zeroes (old skool broadcast) */
-  if ((~addr == IPADDR_ANY) ||
-      (addr == IPADDR_ANY)) {
-    return 1;
-  /* no broadcast support on this network interface? */
-  } else if ((netif->flags & NETIF_FLAG_BROADCAST) == 0) {
-    /* the given address cannot be a broadcast address
-     * nor can we check against any broadcast addresses */
-    return 0;
-  /* address matches network interface address exactly? => no broadcast */
-  } else if (addr == ip4_addr_get_u32(&netif->ip_addr)) {
-    return 0;
-  /*  on the same (sub) network... */
-  } else if (ip_addr_netcmp(&ipaddr, &(netif->ip_addr), &(netif->netmask))
-         /* ...and host identifier bits are all ones? =>... */
-          && ((addr & ~ip4_addr_get_u32(&netif->netmask)) ==
-           (IPADDR_BROADCAST & ~ip4_addr_get_u32(&netif->netmask)))) {
-    /* => network broadcast address */
-    return 1;
-  } else {
-    return 0;
-  }
-}
-
-/** Checks if a netmask is valid (starting with ones, then only zeros)
- *
- * @param netmask the IPv4 netmask to check (in network byte order!)
- * @return 1 if the netmask is valid, 0 if it is not
- */
-u8_t
-ip4_addr_netmask_valid(u32_t netmask)
-{
-  u32_t mask;
-  u32_t nm_hostorder = lwip_htonl(netmask);
-
-  /* first, check for the first zero */
-  for (mask = 1UL << 31 ; mask != 0; mask >>= 1) {
-    if ((nm_hostorder & mask) == 0) {
-      break;
-    }
-  }
-  /* then check that there is no one */
-  for (; mask != 0; mask >>= 1) {
-    if ((nm_hostorder & mask) != 0) {
-      /* there is a one after the first zero -> invalid */
-      return 0;
-    }
-  }
-  /* no one after the first zero -> valid */
-  return 1;
-}
-
-/* Here for now until needed in other places in lwIP */
-#ifndef isprint
-#define in_range(c, lo, up)  ((u8_t)c >= lo && (u8_t)c <= up)
-#define isprint(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.
- *
- * @param cp IP address in ascii represenation (e.g. "127.0.0.1")
- * @return ip address in network order
- */
-u32_t
-ipaddr_addr(const char *cp)
-{
-  ip_addr_t val;
-
-  if (ipaddr_aton(cp, &val)) {
-    return ip4_addr_get_u32(&val);
-  }
-  return (IPADDR_NONE);
-}
-
-/**
- * Check whether "cp" is a valid ascii representation
- * of an Internet address and convert to a binary address.
- * Returns 1 if the address is valid, 0 if not.
- * This replaces inet_addr, the return value from which
- * cannot distinguish between failure and a local broadcast address.
- *
- * @param cp IP address in ascii represenation (e.g. "127.0.0.1")
- * @param addr pointer to which to save the ip address in network order
- * @return 1 if cp could be converted to addr, 0 on failure
- */
-int
-ipaddr_aton(const char *cp, ip_addr_t *addr)
-{
-  u32_t val;
-  u8_t base;
-  char c;
-  u32_t parts[4];
-  u32_t *pp = parts;
-
-  c = *cp;
-  for (;;) {
-    /*
-     * Collect number up to ``.''.
-     * Values are specified as for C:
-     * 0x=hex, 0=octal, 1-9=decimal.
-     */
-    if (!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) + (int)(c - '0');
-        c = *++cp;
-      } else if (base == 16 && isxdigit(c)) {
-        val = (val << 4) | (int)(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' && !isspace(c)) {
-    return (0);
-  }
-  /*
-   * Concoct the address according to
-   * the number of parts specified.
-   */
-  switch (pp - parts + 1) {
-
-  case 0:
-    return (0);       /* initial nondigit */
-
-  case 1:             /* a -- 32 bits */
-    break;
-
-  case 2:             /* a.b -- 8.24 bits */
-    if (val > 0xffffffUL) {
-      return (0);
-    }
-    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;
-  default:
-    LWIP_ASSERT("unhandled", 0);
-    break;
-  }
-  if (addr) {
-    ip4_addr_set_u32(addr, htonl(val));
-  }
-  return (1);
-}
-
-/**
- * Convert numeric IP address into decimal dotted ASCII representation.
- * returns ptr to static buffer; not reentrant!
- *
- * @param addr ip address in network order to convert
- * @return pointer to a global static (!) buffer that holds the ASCII
- *         represenation of addr
- */
-char *
-ipaddr_ntoa(const ip_addr_t *addr)
-{
-  static char str[16];
-  return ipaddr_ntoa_r(addr, str, 16);
-}
-
-/**
- * Same as ipaddr_ntoa, but reentrant since a user-supplied buffer is used.
- *
- * @param addr ip address in network order to convert
- * @param buf target buffer where the string is stored
- * @param buflen length of buf
- * @return either pointer to buf which now holds the ASCII
- *         representation of addr or NULL if buf was too small
- */
-char *ipaddr_ntoa_r(const ip_addr_t *addr, char *buf, int buflen)
-{
-  u32_t s_addr;
-  char inv[3];
-  char *rp;
-  u8_t *ap;
-  u8_t rem;
-  u8_t n;
-  u8_t i;
-  int len = 0;
-
-  s_addr = ip4_addr_get_u32(addr);
-
-  rp = buf;
-  ap = (u8_t *)&s_addr;
-  for(n = 0; n < 4; n++) {
-    i = 0;
-    do {
-      rem = *ap % (u8_t)10;
-      *ap /= (u8_t)10;
-      inv[i++] = '0' + rem;
-    } while(*ap);
-    while(i--) {
-      if (len++ >= buflen) {
-        return NULL;
-      }
-      *rp++ = inv[i];
-    }
-    if (len++ >= buflen) {
-      return NULL;
-    }
-    *rp++ = '.';
-    ap++;
-  }
-  *--rp = 0;
-  return buf;
-}
+/**
+ * @file
+ * This is the IPv4 address tools implementation.
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV4
+
+#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+
+/* used by IP4_ADDR_ANY and IP_ADDR_BROADCAST in ip_addr.h */
+const ip_addr_t ip_addr_any = IPADDR4_INIT(IPADDR_ANY);
+const ip_addr_t ip_addr_broadcast = IPADDR4_INIT(IPADDR_BROADCAST);
+
+/**
+ * Determine if an address is a broadcast address on a network interface
+ *
+ * @param addr address to be checked
+ * @param netif the network interface against which the address is checked
+ * @return returns non-zero if the address is a broadcast address
+ */
+u8_t
+ip4_addr_isbroadcast_u32(u32_t addr, const struct netif *netif)
+{
+  ip4_addr_t ipaddr;
+  ip4_addr_set_u32(&ipaddr, addr);
+
+  /* all ones (broadcast) or all zeroes (old skool broadcast) */
+  if ((~addr == IPADDR_ANY) ||
+      (addr == IPADDR_ANY)) {
+    return 1;
+  /* no broadcast support on this network interface? */
+  } else if ((netif->flags & NETIF_FLAG_BROADCAST) == 0) {
+    /* the given address cannot be a broadcast address
+     * nor can we check against any broadcast addresses */
+    return 0;
+  /* address matches network interface address exactly? => no broadcast */
+  } else if (addr == ip4_addr_get_u32(netif_ip4_addr(netif))) {
+    return 0;
+  /*  on the same (sub) network... */
+  } else if (ip4_addr_netcmp(&ipaddr, netif_ip4_addr(netif), netif_ip4_netmask(netif))
+         /* ...and host identifier bits are all ones? =>... */
+          && ((addr & ~ip4_addr_get_u32(netif_ip4_netmask(netif))) ==
+           (IPADDR_BROADCAST & ~ip4_addr_get_u32(netif_ip4_netmask(netif))))) {
+    /* => network broadcast address */
+    return 1;
+  } else {
+    return 0;
+  }
+}
+
+/** Checks if a netmask is valid (starting with ones, then only zeros)
+ *
+ * @param netmask the IPv4 netmask to check (in network byte order!)
+ * @return 1 if the netmask is valid, 0 if it is not
+ */
+u8_t
+ip4_addr_netmask_valid(u32_t netmask)
+{
+  u32_t mask;
+  u32_t nm_hostorder = lwip_htonl(netmask);
+
+  /* first, check for the first zero */
+  for (mask = 1UL << 31 ; mask != 0; mask >>= 1) {
+    if ((nm_hostorder & mask) == 0) {
+      break;
+    }
+  }
+  /* then check that there is no one */
+  for (; mask != 0; mask >>= 1) {
+    if ((nm_hostorder & mask) != 0) {
+      /* there is a one after the first zero -> invalid */
+      return 0;
+    }
+  }
+  /* no one after the first zero -> valid */
+  return 1;
+}
+
+/* Here for now until needed in other places in lwIP */
+#ifndef isprint
+#define in_range(c, lo, up)  ((u8_t)c >= lo && (u8_t)c <= up)
+#define isprint(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.
+ *
+ * @param cp IP address in ascii representation (e.g. "127.0.0.1")
+ * @return ip address in network order
+ */
+u32_t
+ipaddr_addr(const char *cp)
+{
+  ip4_addr_t val;
+
+  if (ip4addr_aton(cp, &val)) {
+    return ip4_addr_get_u32(&val);
+  }
+  return (IPADDR_NONE);
+}
+
+/**
+ * Check whether "cp" is a valid ascii representation
+ * of an Internet address and convert to a binary address.
+ * Returns 1 if the address is valid, 0 if not.
+ * This replaces inet_addr, the return value from which
+ * cannot distinguish between failure and a local broadcast address.
+ *
+ * @param cp IP address in ascii representation (e.g. "127.0.0.1")
+ * @param addr pointer to which to save the ip address in network order
+ * @return 1 if cp could be converted to addr, 0 on failure
+ */
+int
+ip4addr_aton(const char *cp, ip4_addr_t *addr)
+{
+  u32_t val;
+  u8_t base;
+  char c;
+  u32_t parts[4];
+  u32_t *pp = parts;
+
+  c = *cp;
+  for (;;) {
+    /*
+     * Collect number up to ``.''.
+     * Values are specified as for C:
+     * 0x=hex, 0=octal, 1-9=decimal.
+     */
+    if (!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) + (u32_t)(c - '0');
+        c = *++cp;
+      } else if (base == 16 && isxdigit(c)) {
+        val = (val << 4) | (u32_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' && !isspace(c)) {
+    return 0;
+  }
+  /*
+   * Concoct the address according to
+   * the number of parts specified.
+   */
+  switch (pp - parts + 1) {
+
+  case 0:
+    return 0;       /* initial nondigit */
+
+  case 1:             /* a -- 32 bits */
+    break;
+
+  case 2:             /* a.b -- 8.24 bits */
+    if (val > 0xffffffUL) {
+      return 0;
+    }
+    if (parts[0] > 0xff) {
+      return 0;
+    }
+    val |= parts[0] << 24;
+    break;
+
+  case 3:             /* a.b.c -- 8.8.16 bits */
+    if (val > 0xffff) {
+      return 0;
+    }
+    if ((parts[0] > 0xff) || (parts[1] > 0xff)) {
+      return 0;
+    }
+    val |= (parts[0] << 24) | (parts[1] << 16);
+    break;
+
+  case 4:             /* a.b.c.d -- 8.8.8.8 bits */
+    if (val > 0xff) {
+      return 0;
+    }
+    if ((parts[0] > 0xff) || (parts[1] > 0xff) || (parts[2] > 0xff)) {
+      return 0;
+    }
+    val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8);
+    break;
+  default:
+    LWIP_ASSERT("unhandled", 0);
+    break;
+  }
+  if (addr) {
+    ip4_addr_set_u32(addr, lwip_htonl(val));
+  }
+  return 1;
+}
+
+/**
+ * Convert numeric IP address into decimal dotted ASCII representation.
+ * returns ptr to static buffer; not reentrant!
+ *
+ * @param addr ip address in network order to convert
+ * @return pointer to a global static (!) buffer that holds the ASCII
+ *         representation of addr
+ */
+char*
+ip4addr_ntoa(const ip4_addr_t *addr)
+{
+  static char str[IP4ADDR_STRLEN_MAX];
+  return ip4addr_ntoa_r(addr, str, IP4ADDR_STRLEN_MAX);
+}
+
+/**
+ * Same as ipaddr_ntoa, but reentrant since a user-supplied buffer is used.
+ *
+ * @param addr ip address in network order to convert
+ * @param buf target buffer where the string is stored
+ * @param buflen length of buf
+ * @return either pointer to buf which now holds the ASCII
+ *         representation of addr or NULL if buf was too small
+ */
+char*
+ip4addr_ntoa_r(const ip4_addr_t *addr, char *buf, int buflen)
+{
+  u32_t s_addr;
+  char inv[3];
+  char *rp;
+  u8_t *ap;
+  u8_t rem;
+  u8_t n;
+  u8_t i;
+  int len = 0;
+
+  s_addr = ip4_addr_get_u32(addr);
+
+  rp = buf;
+  ap = (u8_t *)&s_addr;
+  for (n = 0; n < 4; n++) {
+    i = 0;
+    do {
+      rem = *ap % (u8_t)10;
+      *ap /= (u8_t)10;
+      inv[i++] = (char)('0' + rem);
+    } while (*ap);
+    while (i--) {
+      if (len++ >= buflen) {
+        return NULL;
+      }
+      *rp++ = inv[i];
+    }
+    if (len++ >= buflen) {
+      return NULL;
+    }
+    *rp++ = '.';
+    ap++;
+  }
+  *--rp = 0;
+  return buf;
+}
+
+#endif /* LWIP_IPV4 */

+ 831 - 863
thirdparty/lwip/src/core/ipv4/ip_frag.c → thirdparty/lwip/src/core/ipv4/ip4_frag.c

@@ -1,863 +1,831 @@
-/**
- * @file
- * This is the IPv4 packet segmentation and reassembly implementation.
- *
- */
-
-/*
- * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
- * All rights reserved. 
- * 
- * Redistribution and use in source and binary forms, with or without modification, 
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission. 
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
- * OF SUCH DAMAGE.
- *
- * This file is part of the lwIP TCP/IP stack.
- * 
- * Author: Jani Monoses <jani@iv.ro> 
- *         Simon Goldschmidt
- * original reassembly code by Adam Dunkels <adam@sics.se>
- * 
- */
-
-#include "lwip/opt.h"
-#include "lwip/ip_frag.h"
-#include "lwip/def.h"
-#include "lwip/inet_chksum.h"
-#include "lwip/netif.h"
-#include "lwip/snmp.h"
-#include "lwip/stats.h"
-#include "lwip/icmp.h"
-
-#include <string.h>
-
-#if IP_REASSEMBLY
-/**
- * The IP reassembly code currently has the following limitations:
- * - IP header options are not supported
- * - fragments must not overlap (e.g. due to different routes),
- *   currently, overlapping or duplicate fragments are thrown away
- *   if IP_REASS_CHECK_OVERLAP=1 (the default)!
- *
- * @todo: work with IP header options
- */
-
-/** Setting this to 0, you can turn off checking the fragments for overlapping
- * regions. The code gets a little smaller. Only use this if you know that
- * overlapping won't occur on your network! */
-#ifndef IP_REASS_CHECK_OVERLAP
-#define IP_REASS_CHECK_OVERLAP 1
-#endif /* IP_REASS_CHECK_OVERLAP */
-
-/** Set to 0 to prevent freeing the oldest datagram when the reassembly buffer is
- * full (IP_REASS_MAX_PBUFS pbufs are enqueued). The code gets a little smaller.
- * Datagrams will be freed by timeout only. Especially useful when MEMP_NUM_REASSDATA
- * is set to 1, so one datagram can be reassembled at a time, only. */
-#ifndef IP_REASS_FREE_OLDEST
-#define IP_REASS_FREE_OLDEST 1
-#endif /* IP_REASS_FREE_OLDEST */
-
-#define IP_REASS_FLAG_LASTFRAG 0x01
-
-/** This is a helper struct which holds the starting
- * offset and the ending offset of this fragment to
- * easily chain the fragments.
- * It has the same packing requirements as the IP header, since it replaces
- * the IP header in memory in incoming fragments (after copying it) to keep
- * track of the various fragments. (-> If the IP header doesn't need packing,
- * this struct doesn't need packing, too.)
- */
-#ifdef PACK_STRUCT_USE_INCLUDES
-#  include "arch/bpstruct.h"
-#endif
-PACK_STRUCT_BEGIN
-struct ip_reass_helper {
-  PACK_STRUCT_FIELD(struct pbuf *next_pbuf);
-  PACK_STRUCT_FIELD(u16_t start);
-  PACK_STRUCT_FIELD(u16_t end);
-} PACK_STRUCT_STRUCT;
-PACK_STRUCT_END
-#ifdef PACK_STRUCT_USE_INCLUDES
-#  include "arch/epstruct.h"
-#endif
-
-#define IP_ADDRESSES_AND_ID_MATCH(iphdrA, iphdrB)  \
-  (ip_addr_cmp(&(iphdrA)->src, &(iphdrB)->src) && \
-   ip_addr_cmp(&(iphdrA)->dest, &(iphdrB)->dest) && \
-   IPH_ID(iphdrA) == IPH_ID(iphdrB)) ? 1 : 0
-
-/* global variables */
-static struct ip_reassdata *reassdatagrams;
-static u16_t ip_reass_pbufcount;
-
-/* function prototypes */
-static void ip_reass_dequeue_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev);
-static int ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev);
-
-/**
- * Reassembly timer base function
- * for both NO_SYS == 0 and 1 (!).
- *
- * Should be called every 1000 msec (defined by IP_TMR_INTERVAL).
- */
-void
-ip_reass_tmr(void)
-{
-  struct ip_reassdata *r, *prev = NULL;
-
-  r = reassdatagrams;
-  while (r != NULL) {
-    /* Decrement the timer. Once it reaches 0,
-     * clean up the incomplete fragment assembly */
-    if (r->timer > 0) {
-      r->timer--;
-      LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass_tmr: timer dec %"U16_F"\n",(u16_t)r->timer));
-      prev = r;
-      r = r->next;
-    } else {
-      /* reassembly timed out */
-      struct ip_reassdata *tmp;
-      LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass_tmr: timer timed out\n"));
-      tmp = r;
-      /* get the next pointer before freeing */
-      r = r->next;
-      /* free the helper struct and all enqueued pbufs */
-      ip_reass_free_complete_datagram(tmp, prev);
-     }
-   }
-}
-
-/**
- * Free a datagram (struct ip_reassdata) and all its pbufs.
- * Updates the total count of enqueued pbufs (ip_reass_pbufcount),
- * SNMP counters and sends an ICMP time exceeded packet.
- *
- * @param ipr datagram to free
- * @param prev the previous datagram in the linked list
- * @return the number of pbufs freed
- */
-static int
-ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev)
-{
-  u16_t pbufs_freed = 0;
-  u8_t clen;
-  struct pbuf *p;
-  struct ip_reass_helper *iprh;
-
-  LWIP_ASSERT("prev != ipr", prev != ipr);
-  if (prev != NULL) {
-    LWIP_ASSERT("prev->next == ipr", prev->next == ipr);
-  }
-
-  snmp_inc_ipreasmfails();
-#if LWIP_ICMP
-  iprh = (struct ip_reass_helper *)ipr->p->payload;
-  if (iprh->start == 0) {
-    /* The first fragment was received, send ICMP time exceeded. */
-    /* First, de-queue the first pbuf from r->p. */
-    p = ipr->p;
-    ipr->p = iprh->next_pbuf;
-    /* Then, copy the original header into it. */
-    SMEMCPY(p->payload, &ipr->iphdr, IP_HLEN);
-    icmp_time_exceeded(p, ICMP_TE_FRAG);
-    clen = pbuf_clen(p);
-    LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff);
-    pbufs_freed += clen;
-    pbuf_free(p);
-  }
-#endif /* LWIP_ICMP */
-
-  /* First, free all received pbufs.  The individual pbufs need to be released 
-     separately as they have not yet been chained */
-  p = ipr->p;
-  while (p != NULL) {
-    struct pbuf *pcur;
-    iprh = (struct ip_reass_helper *)p->payload;
-    pcur = p;
-    /* get the next pointer before freeing */
-    p = iprh->next_pbuf;
-    clen = pbuf_clen(pcur);
-    LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff);
-    pbufs_freed += clen;
-    pbuf_free(pcur);
-  }
-  /* Then, unchain the struct ip_reassdata from the list and free it. */
-  ip_reass_dequeue_datagram(ipr, prev);
-  LWIP_ASSERT("ip_reass_pbufcount >= clen", ip_reass_pbufcount >= pbufs_freed);
-  ip_reass_pbufcount -= pbufs_freed;
-
-  return pbufs_freed;
-}
-
-#if IP_REASS_FREE_OLDEST
-/**
- * Free the oldest datagram to make room for enqueueing new fragments.
- * The datagram 'fraghdr' belongs to is not freed!
- *
- * @param fraghdr IP header of the current fragment
- * @param pbufs_needed number of pbufs needed to enqueue
- *        (used for freeing other datagrams if not enough space)
- * @return the number of pbufs freed
- */
-static int
-ip_reass_remove_oldest_datagram(struct ip_hdr *fraghdr, int pbufs_needed)
-{
-  /* @todo Can't we simply remove the last datagram in the
-   *       linked list behind reassdatagrams?
-   */
-  struct ip_reassdata *r, *oldest, *prev;
-  int pbufs_freed = 0, pbufs_freed_current;
-  int other_datagrams;
-
-  /* Free datagrams until being allowed to enqueue 'pbufs_needed' pbufs,
-   * but don't free the datagram that 'fraghdr' belongs to! */
-  do {
-    oldest = NULL;
-    prev = NULL;
-    other_datagrams = 0;
-    r = reassdatagrams;
-    while (r != NULL) {
-      if (!IP_ADDRESSES_AND_ID_MATCH(&r->iphdr, fraghdr)) {
-        /* Not the same datagram as fraghdr */
-        other_datagrams++;
-        if (oldest == NULL) {
-          oldest = r;
-        } else if (r->timer <= oldest->timer) {
-          /* older than the previous oldest */
-          oldest = r;
-        }
-      }
-      if (r->next != NULL) {
-        prev = r;
-      }
-      r = r->next;
-    }
-    if (oldest != NULL) {
-      pbufs_freed_current = ip_reass_free_complete_datagram(oldest, prev);
-      pbufs_freed += pbufs_freed_current;
-    }
-  } while ((pbufs_freed < pbufs_needed) && (other_datagrams > 1));
-  return pbufs_freed;
-}
-#endif /* IP_REASS_FREE_OLDEST */
-
-/**
- * Enqueues a new fragment into the fragment queue
- * @param fraghdr points to the new fragments IP hdr
- * @param clen number of pbufs needed to enqueue (used for freeing other datagrams if not enough space)
- * @return A pointer to the queue location into which the fragment was enqueued
- */
-static struct ip_reassdata*
-ip_reass_enqueue_new_datagram(struct ip_hdr *fraghdr, int clen)
-{
-  struct ip_reassdata* ipr;
-  /* No matching previous fragment found, allocate a new reassdata struct */
-  ipr = (struct ip_reassdata *)memp_malloc(MEMP_REASSDATA);
-  if (ipr == NULL) {
-#if IP_REASS_FREE_OLDEST
-    if (ip_reass_remove_oldest_datagram(fraghdr, clen) >= clen) {
-      ipr = (struct ip_reassdata *)memp_malloc(MEMP_REASSDATA);
-    }
-    if (ipr == NULL)
-#endif /* IP_REASS_FREE_OLDEST */
-    {
-      IPFRAG_STATS_INC(ip_frag.memerr);
-      LWIP_DEBUGF(IP_REASS_DEBUG,("Failed to alloc reassdata struct\n"));
-      return NULL;
-    }
-  }
-  memset(ipr, 0, sizeof(struct ip_reassdata));
-  ipr->timer = IP_REASS_MAXAGE;
-
-  /* enqueue the new structure to the front of the list */
-  ipr->next = reassdatagrams;
-  reassdatagrams = ipr;
-  /* copy the ip header for later tests and input */
-  /* @todo: no ip options supported? */
-  SMEMCPY(&(ipr->iphdr), fraghdr, IP_HLEN);
-  return ipr;
-}
-
-/**
- * Dequeues a datagram from the datagram queue. Doesn't deallocate the pbufs.
- * @param ipr points to the queue entry to dequeue
- */
-static void
-ip_reass_dequeue_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev)
-{
-  
-  /* dequeue the reass struct  */
-  if (reassdatagrams == ipr) {
-    /* it was the first in the list */
-    reassdatagrams = ipr->next;
-  } else {
-    /* it wasn't the first, so it must have a valid 'prev' */
-    LWIP_ASSERT("sanity check linked list", prev != NULL);
-    prev->next = ipr->next;
-  }
-
-  /* now we can free the ip_reass struct */
-  memp_free(MEMP_REASSDATA, ipr);
-}
-
-/**
- * Chain a new pbuf into the pbuf list that composes the datagram.  The pbuf list
- * will grow over time as  new pbufs are rx.
- * Also checks that the datagram passes basic continuity checks (if the last
- * fragment was received at least once).
- * @param root_p points to the 'root' pbuf for the current datagram being assembled.
- * @param new_p points to the pbuf for the current fragment
- * @return 0 if invalid, >0 otherwise
- */
-static int
-ip_reass_chain_frag_into_datagram_and_validate(struct ip_reassdata *ipr, struct pbuf *new_p)
-{
-  struct ip_reass_helper *iprh, *iprh_tmp, *iprh_prev=NULL;
-  struct pbuf *q;
-  u16_t offset,len;
-  struct ip_hdr *fraghdr;
-  int valid = 1;
-
-  /* Extract length and fragment offset from current fragment */
-  fraghdr = (struct ip_hdr*)new_p->payload; 
-  len = ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4;
-  offset = (ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8;
-
-  /* overwrite the fragment's ip header from the pbuf with our helper struct,
-   * and setup the embedded helper structure. */
-  /* make sure the struct ip_reass_helper fits into the IP header */
-  LWIP_ASSERT("sizeof(struct ip_reass_helper) <= IP_HLEN",
-              sizeof(struct ip_reass_helper) <= IP_HLEN);
-  iprh = (struct ip_reass_helper*)new_p->payload;
-  iprh->next_pbuf = NULL;
-  iprh->start = offset;
-  iprh->end = offset + len;
-
-  /* Iterate through until we either get to the end of the list (append),
-   * or we find on with a larger offset (insert). */
-  for (q = ipr->p; q != NULL;) {
-    iprh_tmp = (struct ip_reass_helper*)q->payload;
-    if (iprh->start < iprh_tmp->start) {
-      /* the new pbuf should be inserted before this */
-      iprh->next_pbuf = q;
-      if (iprh_prev != NULL) {
-        /* not the fragment with the lowest offset */
-#if IP_REASS_CHECK_OVERLAP
-        if ((iprh->start < iprh_prev->end) || (iprh->end > iprh_tmp->start)) {
-          /* fragment overlaps with previous or following, throw away */
-          goto freepbuf;
-        }
-#endif /* IP_REASS_CHECK_OVERLAP */
-        iprh_prev->next_pbuf = new_p;
-      } else {
-        /* fragment with the lowest offset */
-        ipr->p = new_p;
-      }
-      break;
-    } else if(iprh->start == iprh_tmp->start) {
-      /* received the same datagram twice: no need to keep the datagram */
-      goto freepbuf;
-#if IP_REASS_CHECK_OVERLAP
-    } else if(iprh->start < iprh_tmp->end) {
-      /* overlap: no need to keep the new datagram */
-      goto freepbuf;
-#endif /* IP_REASS_CHECK_OVERLAP */
-    } else {
-      /* Check if the fragments received so far have no wholes. */
-      if (iprh_prev != NULL) {
-        if (iprh_prev->end != iprh_tmp->start) {
-          /* There is a fragment missing between the current
-           * and the previous fragment */
-          valid = 0;
-        }
-      }
-    }
-    q = iprh_tmp->next_pbuf;
-    iprh_prev = iprh_tmp;
-  }
-
-  /* If q is NULL, then we made it to the end of the list. Determine what to do now */
-  if (q == NULL) {
-    if (iprh_prev != NULL) {
-      /* this is (for now), the fragment with the highest offset:
-       * chain it to the last fragment */
-#if IP_REASS_CHECK_OVERLAP
-      LWIP_ASSERT("check fragments don't overlap", iprh_prev->end <= iprh->start);
-#endif /* IP_REASS_CHECK_OVERLAP */
-      iprh_prev->next_pbuf = new_p;
-      if (iprh_prev->end != iprh->start) {
-        valid = 0;
-      }
-    } else {
-#if IP_REASS_CHECK_OVERLAP
-      LWIP_ASSERT("no previous fragment, this must be the first fragment!",
-        ipr->p == NULL);
-#endif /* IP_REASS_CHECK_OVERLAP */
-      /* this is the first fragment we ever received for this ip datagram */
-      ipr->p = new_p;
-    }
-  }
-
-  /* At this point, the validation part begins: */
-  /* If we already received the last fragment */
-  if ((ipr->flags & IP_REASS_FLAG_LASTFRAG) != 0) {
-    /* and had no wholes so far */
-    if (valid) {
-      /* then check if the rest of the fragments is here */
-      /* Check if the queue starts with the first datagram */
-      if (((struct ip_reass_helper*)ipr->p->payload)->start != 0) {
-        valid = 0;
-      } else {
-        /* and check that there are no wholes after this datagram */
-        iprh_prev = iprh;
-        q = iprh->next_pbuf;
-        while (q != NULL) {
-          iprh = (struct ip_reass_helper*)q->payload;
-          if (iprh_prev->end != iprh->start) {
-            valid = 0;
-            break;
-          }
-          iprh_prev = iprh;
-          q = iprh->next_pbuf;
-        }
-        /* if still valid, all fragments are received
-         * (because to the MF==0 already arrived */
-        if (valid) {
-          LWIP_ASSERT("sanity check", ipr->p != NULL);
-          LWIP_ASSERT("sanity check",
-            ((struct ip_reass_helper*)ipr->p->payload) != iprh);
-          LWIP_ASSERT("validate_datagram:next_pbuf!=NULL",
-            iprh->next_pbuf == NULL);
-          LWIP_ASSERT("validate_datagram:datagram end!=datagram len",
-            iprh->end == ipr->datagram_len);
-        }
-      }
-    }
-    /* If valid is 0 here, there are some fragments missing in the middle
-     * (since MF == 0 has already arrived). Such datagrams simply time out if
-     * no more fragments are received... */
-    return valid;
-  }
-  /* If we come here, not all fragments were received, yet! */
-  return 0; /* not yet valid! */
-#if IP_REASS_CHECK_OVERLAP
-freepbuf:
-  ip_reass_pbufcount -= pbuf_clen(new_p);
-  pbuf_free(new_p);
-  return 0;
-#endif /* IP_REASS_CHECK_OVERLAP */
-}
-
-/**
- * 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 *r;
-  struct ip_hdr *fraghdr;
-  struct ip_reassdata *ipr;
-  struct ip_reass_helper *iprh;
-  u16_t offset, len;
-  u8_t clen;
-  struct ip_reassdata *ipr_prev = NULL;
-
-  IPFRAG_STATS_INC(ip_frag.recv);
-  snmp_inc_ipreasmreqds();
-
-  fraghdr = (struct ip_hdr*)p->payload;
-
-  if ((IPH_HL(fraghdr) * 4) != IP_HLEN) {
-    LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: IP options currently not supported!\n"));
-    IPFRAG_STATS_INC(ip_frag.err);
-    goto nullreturn;
-  }
-
-  offset = (ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8;
-  len = ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4;
-
-  /* Check if we are allowed to enqueue more datagrams. */
-  clen = pbuf_clen(p);
-  if ((ip_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS) {
-#if IP_REASS_FREE_OLDEST
-    if (!ip_reass_remove_oldest_datagram(fraghdr, clen) ||
-        ((ip_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS))
-#endif /* IP_REASS_FREE_OLDEST */
-    {
-      /* No datagram could be freed and still too many pbufs enqueued */
-      LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: Overflow condition: pbufct=%d, clen=%d, MAX=%d\n",
-        ip_reass_pbufcount, clen, IP_REASS_MAX_PBUFS));
-      IPFRAG_STATS_INC(ip_frag.memerr);
-      /* @todo: send ICMP time exceeded here? */
-      /* drop this pbuf */
-      goto nullreturn;
-    }
-  }
-
-  /* Look for the datagram the fragment belongs to in the current datagram queue,
-   * remembering the previous in the queue for later dequeueing. */
-  for (ipr = reassdatagrams; ipr != NULL; ipr = ipr->next) {
-    /* Check if the incoming fragment matches the one currently present
-       in the reassembly buffer. If so, we proceed with copying the
-       fragment into the buffer. */
-    if (IP_ADDRESSES_AND_ID_MATCH(&ipr->iphdr, fraghdr)) {
-      LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass: matching previous fragment ID=%"X16_F"\n",
-        ntohs(IPH_ID(fraghdr))));
-      IPFRAG_STATS_INC(ip_frag.cachehit);
-      break;
-    }
-    ipr_prev = ipr;
-  }
-
-  if (ipr == NULL) {
-  /* Enqueue a new datagram into the datagram queue */
-    ipr = ip_reass_enqueue_new_datagram(fraghdr, clen);
-    /* Bail if unable to enqueue */
-    if(ipr == NULL) {
-      goto nullreturn;
-    }
-  } else {
-    if (((ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) == 0) && 
-      ((ntohs(IPH_OFFSET(&ipr->iphdr)) & IP_OFFMASK) != 0)) {
-      /* ipr->iphdr is not the header from the first fragment, but fraghdr is
-       * -> copy fraghdr into ipr->iphdr since we want to have the header
-       * of the first fragment (for ICMP time exceeded and later, for copying
-       * all options, if supported)*/
-      SMEMCPY(&ipr->iphdr, fraghdr, IP_HLEN);
-    }
-  }
-  /* Track the current number of pbufs current 'in-flight', in order to limit 
-  the number of fragments that may be enqueued at any one time */
-  ip_reass_pbufcount += clen;
-
-  /* At this point, we have either created a new entry or pointing 
-   * to an existing one */
-
-  /* check for 'no more fragments', and update queue entry*/
-  if ((IPH_OFFSET(fraghdr) & PP_NTOHS(IP_MF)) == 0) {
-    ipr->flags |= IP_REASS_FLAG_LASTFRAG;
-    ipr->datagram_len = offset + len;
-    LWIP_DEBUGF(IP_REASS_DEBUG,
-     ("ip_reass: last fragment seen, total len %"S16_F"\n",
-      ipr->datagram_len));
-  }
-  /* find the right place to insert this pbuf */
-  /* @todo: trim pbufs if fragments are overlapping */
-  if (ip_reass_chain_frag_into_datagram_and_validate(ipr, p)) {
-    /* the totally last fragment (flag more fragments = 0) was received at least
-     * once AND all fragments are received */
-    ipr->datagram_len += IP_HLEN;
-
-    /* save the second pbuf before copying the header over the pointer */
-    r = ((struct ip_reass_helper*)ipr->p->payload)->next_pbuf;
-
-    /* copy the original ip header back to the first pbuf */
-    fraghdr = (struct ip_hdr*)(ipr->p->payload);
-    SMEMCPY(fraghdr, &ipr->iphdr, IP_HLEN);
-    IPH_LEN_SET(fraghdr, htons(ipr->datagram_len));
-    IPH_OFFSET_SET(fraghdr, 0);
-    IPH_CHKSUM_SET(fraghdr, 0);
-    /* @todo: do we need to set calculate the correct checksum? */
-    IPH_CHKSUM_SET(fraghdr, inet_chksum(fraghdr, IP_HLEN));
-
-    p = ipr->p;
-
-    /* chain together the pbufs contained within the reass_data list. */
-    while(r != NULL) {
-      iprh = (struct ip_reass_helper*)r->payload;
-
-      /* hide the ip header for every succeding fragment */
-      pbuf_header(r, -IP_HLEN);
-      pbuf_cat(p, r);
-      r = iprh->next_pbuf;
-    }
-    /* release the sources allocate for the fragment queue entry */
-    ip_reass_dequeue_datagram(ipr, ipr_prev);
-
-    /* and adjust the number of pbufs currently queued for reassembly. */
-    ip_reass_pbufcount -= pbuf_clen(p);
-
-    /* Return the pbuf chain */
-    return p;
-  }
-  /* the datagram is not (yet?) reassembled completely */
-  LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass_pbufcount: %d out\n", ip_reass_pbufcount));
-  return NULL;
-
-nullreturn:
-  LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: nullreturn\n"));
-  IPFRAG_STATS_INC(ip_frag.drop);
-  pbuf_free(p);
-  return NULL;
-}
-#endif /* IP_REASSEMBLY */
-
-#if IP_FRAG
-#if IP_FRAG_USES_STATIC_BUF
-static u8_t buf[LWIP_MEM_ALIGN_SIZE(IP_FRAG_MAX_MTU + MEM_ALIGNMENT - 1)];
-#else /* IP_FRAG_USES_STATIC_BUF */
-
-#if !LWIP_NETIF_TX_SINGLE_PBUF
-/** Allocate a new struct pbuf_custom_ref */
-static struct pbuf_custom_ref*
-ip_frag_alloc_pbuf_custom_ref(void)
-{
-  return (struct pbuf_custom_ref*)memp_malloc(MEMP_FRAG_PBUF);
-}
-
-/** Free a struct pbuf_custom_ref */
-static void
-ip_frag_free_pbuf_custom_ref(struct pbuf_custom_ref* p)
-{
-  LWIP_ASSERT("p != NULL", p != NULL);
-  memp_free(MEMP_FRAG_PBUF, p);
-}
-
-/** Free-callback function to free a 'struct pbuf_custom_ref', called by
- * pbuf_free. */
-static void
-ipfrag_free_pbuf_custom(struct pbuf *p)
-{
-  struct pbuf_custom_ref *pcr = (struct pbuf_custom_ref*)p;
-  LWIP_ASSERT("pcr != NULL", pcr != NULL);
-  LWIP_ASSERT("pcr == p", (void*)pcr == (void*)p);
-  if (pcr->original != NULL) {
-    pbuf_free(pcr->original);
-  }
-  ip_frag_free_pbuf_custom_ref(pcr);
-}
-#endif /* !LWIP_NETIF_TX_SINGLE_PBUF */
-#endif /* IP_FRAG_USES_STATIC_BUF */
-
-/**
- * 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_REF) or
- * point PBUF_REFs into p (depending on IP_FRAG_USES_STATIC_BUF).
- *
- * @param p ip packet to send
- * @param netif the netif on which to send
- * @param dest destination ip address to which to send
- *
- * @return ERR_OK if sent successfully, err_t otherwise
- */
-err_t 
-ip_frag(struct pbuf *p, struct netif *netif, ip_addr_t *dest)
-{
-  struct pbuf *rambuf;
-#if IP_FRAG_USES_STATIC_BUF
-  struct pbuf *header;
-#else
-#if !LWIP_NETIF_TX_SINGLE_PBUF
-  struct pbuf *newpbuf;
-#endif
-  struct ip_hdr *original_iphdr;
-#endif
-  struct ip_hdr *iphdr;
-  u16_t nfb;
-  u16_t left, cop;
-  u16_t mtu = netif->mtu;
-  u16_t ofo, omf;
-  u16_t last;
-  u16_t poff = IP_HLEN;
-  u16_t tmp;
-#if !IP_FRAG_USES_STATIC_BUF && !LWIP_NETIF_TX_SINGLE_PBUF
-  u16_t newpbuflen = 0;
-  u16_t left_to_copy;
-#endif
-
-  /* Get a RAM based MTU sized pbuf */
-#if IP_FRAG_USES_STATIC_BUF
-  /* When using a static buffer, we use a PBUF_REF, which we will
-   * use to reference the packet (without link header).
-   * Layer and length is irrelevant.
-   */
-  rambuf = pbuf_alloc(PBUF_LINK, 0, PBUF_REF);
-  if (rambuf == NULL) {
-    LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_frag: pbuf_alloc(PBUF_LINK, 0, PBUF_REF) failed\n"));
-    return ERR_MEM;
-  }
-  rambuf->tot_len = rambuf->len = mtu;
-  rambuf->payload = LWIP_MEM_ALIGN((void *)buf);
-
-  /* Copy the IP header in it */
-  iphdr = (struct ip_hdr *)rambuf->payload;
-  SMEMCPY(iphdr, p->payload, IP_HLEN);
-#else /* IP_FRAG_USES_STATIC_BUF */
-  original_iphdr = (struct ip_hdr *)p->payload;
-  iphdr = original_iphdr;
-#endif /* IP_FRAG_USES_STATIC_BUF */
-
-  /* Save original offset */
-  tmp = ntohs(IPH_OFFSET(iphdr));
-  ofo = tmp & IP_OFFMASK;
-  omf = tmp & IP_MF;
-
-  left = p->tot_len - IP_HLEN;
-
-  nfb = (mtu - IP_HLEN) / 8;
-
-  while (left) {
-    last = (left <= mtu - IP_HLEN);
-
-    /* Set new offset and MF flag */
-    tmp = omf | (IP_OFFMASK & (ofo));
-    if (!last) {
-      tmp = tmp | IP_MF;
-    }
-
-    /* Fill this fragment */
-    cop = last ? left : nfb * 8;
-
-#if IP_FRAG_USES_STATIC_BUF
-    poff += pbuf_copy_partial(p, (u8_t*)iphdr + IP_HLEN, cop, poff);
-#else /* IP_FRAG_USES_STATIC_BUF */
-#if LWIP_NETIF_TX_SINGLE_PBUF
-    rambuf = pbuf_alloc(PBUF_IP, cop, PBUF_RAM);
-    if (rambuf == NULL) {
-      return ERR_MEM;
-    }
-    LWIP_ASSERT("this needs a pbuf in one piece!",
-      (rambuf->len == rambuf->tot_len) && (rambuf->next == NULL));
-    poff += pbuf_copy_partial(p, rambuf->payload, cop, poff);
-    /* make room for the IP header */
-    if(pbuf_header(rambuf, IP_HLEN)) {
-      pbuf_free(rambuf);
-      return ERR_MEM;
-    }
-    /* fill in the IP header */
-    SMEMCPY(rambuf->payload, original_iphdr, IP_HLEN);
-    iphdr = rambuf->payload;
-#else /* LWIP_NETIF_TX_SINGLE_PBUF */
-    /* When not using a static buffer, create a chain of pbufs.
-     * The first will be a PBUF_RAM holding the link and IP header.
-     * The rest will be PBUF_REFs mirroring the pbuf chain to be fragged,
-     * but limited to the size of an mtu.
-     */
-    rambuf = pbuf_alloc(PBUF_LINK, IP_HLEN, PBUF_RAM);
-    if (rambuf == NULL) {
-      return ERR_MEM;
-    }
-    LWIP_ASSERT("this needs a pbuf in one piece!",
-                (p->len >= (IP_HLEN)));
-    SMEMCPY(rambuf->payload, original_iphdr, IP_HLEN);
-    iphdr = (struct ip_hdr *)rambuf->payload;
-
-    /* Can just adjust p directly for needed offset. */
-    p->payload = (u8_t *)p->payload + poff;
-    p->len -= poff;
-
-    left_to_copy = cop;
-    while (left_to_copy) {
-      struct pbuf_custom_ref *pcr;
-      newpbuflen = (left_to_copy < p->len) ? left_to_copy : p->len;
-      /* Is this pbuf already empty? */
-      if (!newpbuflen) {
-        p = p->next;
-        continue;
-      }
-      pcr = ip_frag_alloc_pbuf_custom_ref();
-      if (pcr == NULL) {
-        pbuf_free(rambuf);
-        return ERR_MEM;
-      }
-      /* Mirror this pbuf, although we might not need all of it. */
-      newpbuf = pbuf_alloced_custom(PBUF_RAW, newpbuflen, PBUF_REF, &pcr->pc, p->payload, newpbuflen);
-      if (newpbuf == NULL) {
-        ip_frag_free_pbuf_custom_ref(pcr);
-        pbuf_free(rambuf);
-        return ERR_MEM;
-      }
-      pbuf_ref(p);
-      pcr->original = p;
-      pcr->pc.custom_free_function = ipfrag_free_pbuf_custom;
-
-      /* Add it to end of rambuf's chain, but using pbuf_cat, not pbuf_chain
-       * so that it is removed when pbuf_dechain is later called on rambuf.
-       */
-      pbuf_cat(rambuf, newpbuf);
-      left_to_copy -= newpbuflen;
-      if (left_to_copy) {
-        p = p->next;
-      }
-    }
-    poff = newpbuflen;
-#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
-#endif /* IP_FRAG_USES_STATIC_BUF */
-
-    /* Correct header */
-    IPH_OFFSET_SET(iphdr, htons(tmp));
-    IPH_LEN_SET(iphdr, htons(cop + IP_HLEN));
-    IPH_CHKSUM_SET(iphdr, 0);
-    IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN));
-
-#if IP_FRAG_USES_STATIC_BUF
-    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);
-      snmp_inc_ipfragcreates();
-      pbuf_free(header);
-    } else {
-      LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_frag: pbuf_alloc() for header failed\n"));
-      pbuf_free(rambuf);
-      return ERR_MEM;
-    }
-#else /* IP_FRAG_USES_STATIC_BUF */
-    /* No need for separate header pbuf - we allowed room for it in rambuf
-     * when allocated.
-     */
-    netif->output(netif, rambuf, dest);
-    IPFRAG_STATS_INC(ip_frag.xmit);
-
-    /* Unfortunately we can't reuse rambuf - the hardware may still be
-     * using the buffer. Instead we free it (and the ensuing chain) and
-     * recreate it next time round the loop. If we're lucky the hardware
-     * will have already sent the packet, the free will really free, and
-     * there will be zero memory penalty.
-     */
-    
-    pbuf_free(rambuf);
-#endif /* IP_FRAG_USES_STATIC_BUF */
-    left -= cop;
-    ofo += nfb;
-  }
-#if IP_FRAG_USES_STATIC_BUF
-  pbuf_free(rambuf);
-#endif /* IP_FRAG_USES_STATIC_BUF */
-  snmp_inc_ipfragoks();
-  return ERR_OK;
-}
-#endif /* IP_FRAG */
+/**
+ * @file
+ * This is the IPv4 packet segmentation and reassembly implementation.
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Jani Monoses <jani@iv.ro>
+ *         Simon Goldschmidt
+ * original reassembly code by Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV4
+
+#include "lwip/ip4_frag.h"
+#include "lwip/def.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/netif.h"
+#include "lwip/stats.h"
+#include "lwip/icmp.h"
+
+#include <string.h>
+
+#if IP_REASSEMBLY
+/**
+ * The IP reassembly code currently has the following limitations:
+ * - IP header options are not supported
+ * - fragments must not overlap (e.g. due to different routes),
+ *   currently, overlapping or duplicate fragments are thrown away
+ *   if IP_REASS_CHECK_OVERLAP=1 (the default)!
+ *
+ * @todo: work with IP header options
+ */
+
+/** Setting this to 0, you can turn off checking the fragments for overlapping
+ * regions. The code gets a little smaller. Only use this if you know that
+ * overlapping won't occur on your network! */
+#ifndef IP_REASS_CHECK_OVERLAP
+#define IP_REASS_CHECK_OVERLAP 1
+#endif /* IP_REASS_CHECK_OVERLAP */
+
+/** Set to 0 to prevent freeing the oldest datagram when the reassembly buffer is
+ * full (IP_REASS_MAX_PBUFS pbufs are enqueued). The code gets a little smaller.
+ * Datagrams will be freed by timeout only. Especially useful when MEMP_NUM_REASSDATA
+ * is set to 1, so one datagram can be reassembled at a time, only. */
+#ifndef IP_REASS_FREE_OLDEST
+#define IP_REASS_FREE_OLDEST 1
+#endif /* IP_REASS_FREE_OLDEST */
+
+#define IP_REASS_FLAG_LASTFRAG 0x01
+
+/** This is a helper struct which holds the starting
+ * offset and the ending offset of this fragment to
+ * easily chain the fragments.
+ * It has the same packing requirements as the IP header, since it replaces
+ * the IP header in memory in incoming fragments (after copying it) to keep
+ * track of the various fragments. (-> If the IP header doesn't need packing,
+ * this struct doesn't need packing, too.)
+ */
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct ip_reass_helper {
+  PACK_STRUCT_FIELD(struct pbuf *next_pbuf);
+  PACK_STRUCT_FIELD(u16_t start);
+  PACK_STRUCT_FIELD(u16_t end);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
+
+#define IP_ADDRESSES_AND_ID_MATCH(iphdrA, iphdrB)  \
+  (ip4_addr_cmp(&(iphdrA)->src, &(iphdrB)->src) && \
+   ip4_addr_cmp(&(iphdrA)->dest, &(iphdrB)->dest) && \
+   IPH_ID(iphdrA) == IPH_ID(iphdrB)) ? 1 : 0
+
+/* global variables */
+static struct ip_reassdata *reassdatagrams;
+static u16_t ip_reass_pbufcount;
+
+/* function prototypes */
+static void ip_reass_dequeue_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev);
+static int ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev);
+
+/**
+ * Reassembly timer base function
+ * for both NO_SYS == 0 and 1 (!).
+ *
+ * Should be called every 1000 msec (defined by IP_TMR_INTERVAL).
+ */
+void
+ip_reass_tmr(void)
+{
+  struct ip_reassdata *r, *prev = NULL;
+
+  r = reassdatagrams;
+  while (r != NULL) {
+    /* Decrement the timer. Once it reaches 0,
+     * clean up the incomplete fragment assembly */
+    if (r->timer > 0) {
+      r->timer--;
+      LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass_tmr: timer dec %"U16_F"\n",(u16_t)r->timer));
+      prev = r;
+      r = r->next;
+    } else {
+      /* reassembly timed out */
+      struct ip_reassdata *tmp;
+      LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass_tmr: timer timed out\n"));
+      tmp = r;
+      /* get the next pointer before freeing */
+      r = r->next;
+      /* free the helper struct and all enqueued pbufs */
+      ip_reass_free_complete_datagram(tmp, prev);
+     }
+   }
+}
+
+/**
+ * Free a datagram (struct ip_reassdata) and all its pbufs.
+ * Updates the total count of enqueued pbufs (ip_reass_pbufcount),
+ * SNMP counters and sends an ICMP time exceeded packet.
+ *
+ * @param ipr datagram to free
+ * @param prev the previous datagram in the linked list
+ * @return the number of pbufs freed
+ */
+static int
+ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev)
+{
+  u16_t pbufs_freed = 0;
+  u16_t clen;
+  struct pbuf *p;
+  struct ip_reass_helper *iprh;
+
+  LWIP_ASSERT("prev != ipr", prev != ipr);
+  if (prev != NULL) {
+    LWIP_ASSERT("prev->next == ipr", prev->next == ipr);
+  }
+
+  MIB2_STATS_INC(mib2.ipreasmfails);
+#if LWIP_ICMP
+  iprh = (struct ip_reass_helper *)ipr->p->payload;
+  if (iprh->start == 0) {
+    /* The first fragment was received, send ICMP time exceeded. */
+    /* First, de-queue the first pbuf from r->p. */
+    p = ipr->p;
+    ipr->p = iprh->next_pbuf;
+    /* Then, copy the original header into it. */
+    SMEMCPY(p->payload, &ipr->iphdr, IP_HLEN);
+    icmp_time_exceeded(p, ICMP_TE_FRAG);
+    clen = pbuf_clen(p);
+    LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff);
+    pbufs_freed += clen;
+    pbuf_free(p);
+  }
+#endif /* LWIP_ICMP */
+
+  /* First, free all received pbufs.  The individual pbufs need to be released
+     separately as they have not yet been chained */
+  p = ipr->p;
+  while (p != NULL) {
+    struct pbuf *pcur;
+    iprh = (struct ip_reass_helper *)p->payload;
+    pcur = p;
+    /* get the next pointer before freeing */
+    p = iprh->next_pbuf;
+    clen = pbuf_clen(pcur);
+    LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff);
+    pbufs_freed += clen;
+    pbuf_free(pcur);
+  }
+  /* Then, unchain the struct ip_reassdata from the list and free it. */
+  ip_reass_dequeue_datagram(ipr, prev);
+  LWIP_ASSERT("ip_reass_pbufcount >= clen", ip_reass_pbufcount >= pbufs_freed);
+  ip_reass_pbufcount -= pbufs_freed;
+
+  return pbufs_freed;
+}
+
+#if IP_REASS_FREE_OLDEST
+/**
+ * Free the oldest datagram to make room for enqueueing new fragments.
+ * The datagram 'fraghdr' belongs to is not freed!
+ *
+ * @param fraghdr IP header of the current fragment
+ * @param pbufs_needed number of pbufs needed to enqueue
+ *        (used for freeing other datagrams if not enough space)
+ * @return the number of pbufs freed
+ */
+static int
+ip_reass_remove_oldest_datagram(struct ip_hdr *fraghdr, int pbufs_needed)
+{
+  /* @todo Can't we simply remove the last datagram in the
+   *       linked list behind reassdatagrams?
+   */
+  struct ip_reassdata *r, *oldest, *prev, *oldest_prev;
+  int pbufs_freed = 0, pbufs_freed_current;
+  int other_datagrams;
+
+  /* Free datagrams until being allowed to enqueue 'pbufs_needed' pbufs,
+   * but don't free the datagram that 'fraghdr' belongs to! */
+  do {
+    oldest = NULL;
+    prev = NULL;
+    oldest_prev = NULL;
+    other_datagrams = 0;
+    r = reassdatagrams;
+    while (r != NULL) {
+      if (!IP_ADDRESSES_AND_ID_MATCH(&r->iphdr, fraghdr)) {
+        /* Not the same datagram as fraghdr */
+        other_datagrams++;
+        if (oldest == NULL) {
+          oldest = r;
+          oldest_prev = prev;
+        } else if (r->timer <= oldest->timer) {
+          /* older than the previous oldest */
+          oldest = r;
+          oldest_prev = prev;
+        }
+      }
+      if (r->next != NULL) {
+        prev = r;
+      }
+      r = r->next;
+    }
+    if (oldest != NULL) {
+      pbufs_freed_current = ip_reass_free_complete_datagram(oldest, oldest_prev);
+      pbufs_freed += pbufs_freed_current;
+    }
+  } while ((pbufs_freed < pbufs_needed) && (other_datagrams > 1));
+  return pbufs_freed;
+}
+#endif /* IP_REASS_FREE_OLDEST */
+
+/**
+ * Enqueues a new fragment into the fragment queue
+ * @param fraghdr points to the new fragments IP hdr
+ * @param clen number of pbufs needed to enqueue (used for freeing other datagrams if not enough space)
+ * @return A pointer to the queue location into which the fragment was enqueued
+ */
+static struct ip_reassdata*
+ip_reass_enqueue_new_datagram(struct ip_hdr *fraghdr, int clen)
+{
+  struct ip_reassdata* ipr;
+#if ! IP_REASS_FREE_OLDEST
+  LWIP_UNUSED_ARG(clen);
+#endif
+
+  /* No matching previous fragment found, allocate a new reassdata struct */
+  ipr = (struct ip_reassdata *)memp_malloc(MEMP_REASSDATA);
+  if (ipr == NULL) {
+#if IP_REASS_FREE_OLDEST
+    if (ip_reass_remove_oldest_datagram(fraghdr, clen) >= clen) {
+      ipr = (struct ip_reassdata *)memp_malloc(MEMP_REASSDATA);
+    }
+    if (ipr == NULL)
+#endif /* IP_REASS_FREE_OLDEST */
+    {
+      IPFRAG_STATS_INC(ip_frag.memerr);
+      LWIP_DEBUGF(IP_REASS_DEBUG,("Failed to alloc reassdata struct\n"));
+      return NULL;
+    }
+  }
+  memset(ipr, 0, sizeof(struct ip_reassdata));
+  ipr->timer = IP_REASS_MAXAGE;
+
+  /* enqueue the new structure to the front of the list */
+  ipr->next = reassdatagrams;
+  reassdatagrams = ipr;
+  /* copy the ip header for later tests and input */
+  /* @todo: no ip options supported? */
+  SMEMCPY(&(ipr->iphdr), fraghdr, IP_HLEN);
+  return ipr;
+}
+
+/**
+ * Dequeues a datagram from the datagram queue. Doesn't deallocate the pbufs.
+ * @param ipr points to the queue entry to dequeue
+ */
+static void
+ip_reass_dequeue_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev)
+{
+  /* dequeue the reass struct  */
+  if (reassdatagrams == ipr) {
+    /* it was the first in the list */
+    reassdatagrams = ipr->next;
+  } else {
+    /* it wasn't the first, so it must have a valid 'prev' */
+    LWIP_ASSERT("sanity check linked list", prev != NULL);
+    prev->next = ipr->next;
+  }
+
+  /* now we can free the ip_reassdata struct */
+  memp_free(MEMP_REASSDATA, ipr);
+}
+
+/**
+ * Chain a new pbuf into the pbuf list that composes the datagram.  The pbuf list
+ * will grow over time as  new pbufs are rx.
+ * Also checks that the datagram passes basic continuity checks (if the last
+ * fragment was received at least once).
+ * @param ipr points to the reassembly state
+ * @param new_p points to the pbuf for the current fragment
+ * @return 0 if invalid, >0 otherwise
+ */
+static int
+ip_reass_chain_frag_into_datagram_and_validate(struct ip_reassdata *ipr, struct pbuf *new_p)
+{
+  struct ip_reass_helper *iprh, *iprh_tmp, *iprh_prev=NULL;
+  struct pbuf *q;
+  u16_t offset, len;
+  struct ip_hdr *fraghdr;
+  int valid = 1;
+
+  /* Extract length and fragment offset from current fragment */
+  fraghdr = (struct ip_hdr*)new_p->payload;
+  len = lwip_ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4;
+  offset = (lwip_ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8;
+
+  /* overwrite the fragment's ip header from the pbuf with our helper struct,
+   * and setup the embedded helper structure. */
+  /* make sure the struct ip_reass_helper fits into the IP header */
+  LWIP_ASSERT("sizeof(struct ip_reass_helper) <= IP_HLEN",
+              sizeof(struct ip_reass_helper) <= IP_HLEN);
+  iprh = (struct ip_reass_helper*)new_p->payload;
+  iprh->next_pbuf = NULL;
+  iprh->start = offset;
+  iprh->end = offset + len;
+
+  /* Iterate through until we either get to the end of the list (append),
+   * or we find one with a larger offset (insert). */
+  for (q = ipr->p; q != NULL;) {
+    iprh_tmp = (struct ip_reass_helper*)q->payload;
+    if (iprh->start < iprh_tmp->start) {
+      /* the new pbuf should be inserted before this */
+      iprh->next_pbuf = q;
+      if (iprh_prev != NULL) {
+        /* not the fragment with the lowest offset */
+#if IP_REASS_CHECK_OVERLAP
+        if ((iprh->start < iprh_prev->end) || (iprh->end > iprh_tmp->start)) {
+          /* fragment overlaps with previous or following, throw away */
+          goto freepbuf;
+        }
+#endif /* IP_REASS_CHECK_OVERLAP */
+        iprh_prev->next_pbuf = new_p;
+      } else {
+        /* fragment with the lowest offset */
+        ipr->p = new_p;
+      }
+      break;
+    } else if (iprh->start == iprh_tmp->start) {
+      /* received the same datagram twice: no need to keep the datagram */
+      goto freepbuf;
+#if IP_REASS_CHECK_OVERLAP
+    } else if (iprh->start < iprh_tmp->end) {
+      /* overlap: no need to keep the new datagram */
+      goto freepbuf;
+#endif /* IP_REASS_CHECK_OVERLAP */
+    } else {
+      /* Check if the fragments received so far have no holes. */
+      if (iprh_prev != NULL) {
+        if (iprh_prev->end != iprh_tmp->start) {
+          /* There is a fragment missing between the current
+           * and the previous fragment */
+          valid = 0;
+        }
+      }
+    }
+    q = iprh_tmp->next_pbuf;
+    iprh_prev = iprh_tmp;
+  }
+
+  /* If q is NULL, then we made it to the end of the list. Determine what to do now */
+  if (q == NULL) {
+    if (iprh_prev != NULL) {
+      /* this is (for now), the fragment with the highest offset:
+       * chain it to the last fragment */
+#if IP_REASS_CHECK_OVERLAP
+      LWIP_ASSERT("check fragments don't overlap", iprh_prev->end <= iprh->start);
+#endif /* IP_REASS_CHECK_OVERLAP */
+      iprh_prev->next_pbuf = new_p;
+      if (iprh_prev->end != iprh->start) {
+        valid = 0;
+      }
+    } else {
+#if IP_REASS_CHECK_OVERLAP
+      LWIP_ASSERT("no previous fragment, this must be the first fragment!",
+        ipr->p == NULL);
+#endif /* IP_REASS_CHECK_OVERLAP */
+      /* this is the first fragment we ever received for this ip datagram */
+      ipr->p = new_p;
+    }
+  }
+
+  /* At this point, the validation part begins: */
+  /* If we already received the last fragment */
+  if ((ipr->flags & IP_REASS_FLAG_LASTFRAG) != 0) {
+    /* and had no holes so far */
+    if (valid) {
+      /* then check if the rest of the fragments is here */
+      /* Check if the queue starts with the first datagram */
+      if ((ipr->p == NULL) || (((struct ip_reass_helper*)ipr->p->payload)->start != 0)) {
+        valid = 0;
+      } else {
+        /* and check that there are no holes after this datagram */
+        iprh_prev = iprh;
+        q = iprh->next_pbuf;
+        while (q != NULL) {
+          iprh = (struct ip_reass_helper*)q->payload;
+          if (iprh_prev->end != iprh->start) {
+            valid = 0;
+            break;
+          }
+          iprh_prev = iprh;
+          q = iprh->next_pbuf;
+        }
+        /* if still valid, all fragments are received
+         * (because to the MF==0 already arrived */
+        if (valid) {
+          LWIP_ASSERT("sanity check", ipr->p != NULL);
+          LWIP_ASSERT("sanity check",
+            ((struct ip_reass_helper*)ipr->p->payload) != iprh);
+          LWIP_ASSERT("validate_datagram:next_pbuf!=NULL",
+            iprh->next_pbuf == NULL);
+          LWIP_ASSERT("validate_datagram:datagram end!=datagram len",
+            iprh->end == ipr->datagram_len);
+        }
+      }
+    }
+    /* If valid is 0 here, there are some fragments missing in the middle
+     * (since MF == 0 has already arrived). Such datagrams simply time out if
+     * no more fragments are received... */
+    return valid;
+  }
+  /* If we come here, not all fragments were received, yet! */
+  return 0; /* not yet valid! */
+#if IP_REASS_CHECK_OVERLAP
+freepbuf:
+  ip_reass_pbufcount -= pbuf_clen(new_p);
+  pbuf_free(new_p);
+  return 0;
+#endif /* IP_REASS_CHECK_OVERLAP */
+}
+
+/**
+ * Reassembles incoming IP fragments into an IP datagram.
+ *
+ * @param p points to a pbuf chain of the fragment
+ * @return NULL if reassembly is incomplete, ? otherwise
+ */
+struct pbuf *
+ip4_reass(struct pbuf *p)
+{
+  struct pbuf *r;
+  struct ip_hdr *fraghdr;
+  struct ip_reassdata *ipr;
+  struct ip_reass_helper *iprh;
+  u16_t offset, len, clen;
+
+  IPFRAG_STATS_INC(ip_frag.recv);
+  MIB2_STATS_INC(mib2.ipreasmreqds);
+
+  fraghdr = (struct ip_hdr*)p->payload;
+
+  if ((IPH_HL(fraghdr) * 4) != IP_HLEN) {
+    LWIP_DEBUGF(IP_REASS_DEBUG,("ip4_reass: IP options currently not supported!\n"));
+    IPFRAG_STATS_INC(ip_frag.err);
+    goto nullreturn;
+  }
+
+  offset = (lwip_ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8;
+  len = lwip_ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4;
+
+  /* Check if we are allowed to enqueue more datagrams. */
+  clen = pbuf_clen(p);
+  if ((ip_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS) {
+#if IP_REASS_FREE_OLDEST
+    if (!ip_reass_remove_oldest_datagram(fraghdr, clen) ||
+        ((ip_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS))
+#endif /* IP_REASS_FREE_OLDEST */
+    {
+      /* No datagram could be freed and still too many pbufs enqueued */
+      LWIP_DEBUGF(IP_REASS_DEBUG,("ip4_reass: Overflow condition: pbufct=%d, clen=%d, MAX=%d\n",
+        ip_reass_pbufcount, clen, IP_REASS_MAX_PBUFS));
+      IPFRAG_STATS_INC(ip_frag.memerr);
+      /* @todo: send ICMP time exceeded here? */
+      /* drop this pbuf */
+      goto nullreturn;
+    }
+  }
+
+  /* Look for the datagram the fragment belongs to in the current datagram queue,
+   * remembering the previous in the queue for later dequeueing. */
+  for (ipr = reassdatagrams; ipr != NULL; ipr = ipr->next) {
+    /* Check if the incoming fragment matches the one currently present
+       in the reassembly buffer. If so, we proceed with copying the
+       fragment into the buffer. */
+    if (IP_ADDRESSES_AND_ID_MATCH(&ipr->iphdr, fraghdr)) {
+      LWIP_DEBUGF(IP_REASS_DEBUG, ("ip4_reass: matching previous fragment ID=%"X16_F"\n",
+        lwip_ntohs(IPH_ID(fraghdr))));
+      IPFRAG_STATS_INC(ip_frag.cachehit);
+      break;
+    }
+  }
+
+  if (ipr == NULL) {
+  /* Enqueue a new datagram into the datagram queue */
+    ipr = ip_reass_enqueue_new_datagram(fraghdr, clen);
+    /* Bail if unable to enqueue */
+    if (ipr == NULL) {
+      goto nullreturn;
+    }
+  } else {
+    if (((lwip_ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) == 0) &&
+      ((lwip_ntohs(IPH_OFFSET(&ipr->iphdr)) & IP_OFFMASK) != 0)) {
+      /* ipr->iphdr is not the header from the first fragment, but fraghdr is
+       * -> copy fraghdr into ipr->iphdr since we want to have the header
+       * of the first fragment (for ICMP time exceeded and later, for copying
+       * all options, if supported)*/
+      SMEMCPY(&ipr->iphdr, fraghdr, IP_HLEN);
+    }
+  }
+  /* Track the current number of pbufs current 'in-flight', in order to limit
+  the number of fragments that may be enqueued at any one time */
+  ip_reass_pbufcount += clen;
+
+  /* At this point, we have either created a new entry or pointing
+   * to an existing one */
+
+  /* check for 'no more fragments', and update queue entry*/
+  if ((IPH_OFFSET(fraghdr) & PP_NTOHS(IP_MF)) == 0) {
+    ipr->flags |= IP_REASS_FLAG_LASTFRAG;
+    ipr->datagram_len = offset + len;
+    LWIP_DEBUGF(IP_REASS_DEBUG,
+     ("ip4_reass: last fragment seen, total len %"S16_F"\n",
+      ipr->datagram_len));
+  }
+  /* find the right place to insert this pbuf */
+  /* @todo: trim pbufs if fragments are overlapping */
+  if (ip_reass_chain_frag_into_datagram_and_validate(ipr, p)) {
+    struct ip_reassdata *ipr_prev;
+    /* the totally last fragment (flag more fragments = 0) was received at least
+     * once AND all fragments are received */
+    ipr->datagram_len += IP_HLEN;
+
+    /* save the second pbuf before copying the header over the pointer */
+    r = ((struct ip_reass_helper*)ipr->p->payload)->next_pbuf;
+
+    /* copy the original ip header back to the first pbuf */
+    fraghdr = (struct ip_hdr*)(ipr->p->payload);
+    SMEMCPY(fraghdr, &ipr->iphdr, IP_HLEN);
+    IPH_LEN_SET(fraghdr, lwip_htons(ipr->datagram_len));
+    IPH_OFFSET_SET(fraghdr, 0);
+    IPH_CHKSUM_SET(fraghdr, 0);
+    /* @todo: do we need to set/calculate the correct checksum? */
+#if CHECKSUM_GEN_IP
+    IF__NETIF_CHECKSUM_ENABLED(ip_current_input_netif(), NETIF_CHECKSUM_GEN_IP) {
+      IPH_CHKSUM_SET(fraghdr, inet_chksum(fraghdr, IP_HLEN));
+    }
+#endif /* CHECKSUM_GEN_IP */
+
+    p = ipr->p;
+
+    /* chain together the pbufs contained within the reass_data list. */
+    while (r != NULL) {
+      iprh = (struct ip_reass_helper*)r->payload;
+
+      /* hide the ip header for every succeeding fragment */
+      pbuf_header(r, -IP_HLEN);
+      pbuf_cat(p, r);
+      r = iprh->next_pbuf;
+    }
+
+    /* find the previous entry in the linked list */
+    if (ipr == reassdatagrams) {
+      ipr_prev = NULL;
+    } else {
+      for (ipr_prev = reassdatagrams; ipr_prev != NULL; ipr_prev = ipr_prev->next) {
+        if (ipr_prev->next == ipr) {
+          break;
+        }
+      }
+    }
+
+    /* release the sources allocate for the fragment queue entry */
+    ip_reass_dequeue_datagram(ipr, ipr_prev);
+
+    /* and adjust the number of pbufs currently queued for reassembly. */
+    ip_reass_pbufcount -= pbuf_clen(p);
+
+    MIB2_STATS_INC(mib2.ipreasmoks);
+
+    /* Return the pbuf chain */
+    return p;
+  }
+  /* the datagram is not (yet?) reassembled completely */
+  LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass_pbufcount: %d out\n", ip_reass_pbufcount));
+  return NULL;
+
+nullreturn:
+  LWIP_DEBUGF(IP_REASS_DEBUG,("ip4_reass: nullreturn\n"));
+  IPFRAG_STATS_INC(ip_frag.drop);
+  pbuf_free(p);
+  return NULL;
+}
+#endif /* IP_REASSEMBLY */
+
+#if IP_FRAG
+#if !LWIP_NETIF_TX_SINGLE_PBUF
+/** Allocate a new struct pbuf_custom_ref */
+static struct pbuf_custom_ref*
+ip_frag_alloc_pbuf_custom_ref(void)
+{
+  return (struct pbuf_custom_ref*)memp_malloc(MEMP_FRAG_PBUF);
+}
+
+/** Free a struct pbuf_custom_ref */
+static void
+ip_frag_free_pbuf_custom_ref(struct pbuf_custom_ref* p)
+{
+  LWIP_ASSERT("p != NULL", p != NULL);
+  memp_free(MEMP_FRAG_PBUF, p);
+}
+
+/** Free-callback function to free a 'struct pbuf_custom_ref', called by
+ * pbuf_free. */
+static void
+ipfrag_free_pbuf_custom(struct pbuf *p)
+{
+  struct pbuf_custom_ref *pcr = (struct pbuf_custom_ref*)p;
+  LWIP_ASSERT("pcr != NULL", pcr != NULL);
+  LWIP_ASSERT("pcr == p", (void*)pcr == (void*)p);
+  if (pcr->original != NULL) {
+    pbuf_free(pcr->original);
+  }
+  ip_frag_free_pbuf_custom_ref(pcr);
+}
+#endif /* !LWIP_NETIF_TX_SINGLE_PBUF */
+
+/**
+ * Fragment an IP datagram if too large for the netif.
+ *
+ * Chop the datagram in MTU sized chunks and send them in order
+ * by pointing PBUF_REFs into p.
+ *
+ * @param p ip packet to send
+ * @param netif the netif on which to send
+ * @param dest destination ip address to which to send
+ *
+ * @return ERR_OK if sent successfully, err_t otherwise
+ */
+err_t
+ip4_frag(struct pbuf *p, struct netif *netif, const ip4_addr_t *dest)
+{
+  struct pbuf *rambuf;
+#if !LWIP_NETIF_TX_SINGLE_PBUF
+  struct pbuf *newpbuf;
+  u16_t newpbuflen = 0;
+  u16_t left_to_copy;
+#endif
+  struct ip_hdr *original_iphdr;
+  struct ip_hdr *iphdr;
+  const u16_t nfb = (netif->mtu - IP_HLEN) / 8;
+  u16_t left, fragsize;
+  u16_t ofo;
+  int last;
+  u16_t poff = IP_HLEN;
+  u16_t tmp;
+
+  original_iphdr = (struct ip_hdr *)p->payload;
+  iphdr = original_iphdr;
+  LWIP_ERROR("ip4_frag() does not support IP options", IPH_HL(iphdr) * 4 == IP_HLEN, return ERR_VAL);
+
+  /* Save original offset */
+  tmp = lwip_ntohs(IPH_OFFSET(iphdr));
+  ofo = tmp & IP_OFFMASK;
+  LWIP_ERROR("ip_frag(): MF already set", (tmp & IP_MF) == 0, return ERR_VAL);
+
+  left = p->tot_len - IP_HLEN;
+
+  while (left) {
+    /* Fill this fragment */
+    fragsize = LWIP_MIN(left, nfb * 8);
+
+#if LWIP_NETIF_TX_SINGLE_PBUF
+    rambuf = pbuf_alloc(PBUF_IP, fragsize, PBUF_RAM);
+    if (rambuf == NULL) {
+      goto memerr;
+    }
+    LWIP_ASSERT("this needs a pbuf in one piece!",
+      (rambuf->len == rambuf->tot_len) && (rambuf->next == NULL));
+    poff += pbuf_copy_partial(p, rambuf->payload, fragsize, poff);
+    /* make room for the IP header */
+    if (pbuf_header(rambuf, IP_HLEN)) {
+      pbuf_free(rambuf);
+      goto memerr;
+    }
+    /* fill in the IP header */
+    SMEMCPY(rambuf->payload, original_iphdr, IP_HLEN);
+    iphdr = (struct ip_hdr*)rambuf->payload;
+#else /* LWIP_NETIF_TX_SINGLE_PBUF */
+    /* When not using a static buffer, create a chain of pbufs.
+     * The first will be a PBUF_RAM holding the link and IP header.
+     * The rest will be PBUF_REFs mirroring the pbuf chain to be fragged,
+     * but limited to the size of an mtu.
+     */
+    rambuf = pbuf_alloc(PBUF_LINK, IP_HLEN, PBUF_RAM);
+    if (rambuf == NULL) {
+      goto memerr;
+    }
+    LWIP_ASSERT("this needs a pbuf in one piece!",
+                (p->len >= (IP_HLEN)));
+    SMEMCPY(rambuf->payload, original_iphdr, IP_HLEN);
+    iphdr = (struct ip_hdr *)rambuf->payload;
+
+    left_to_copy = fragsize;
+    while (left_to_copy) {
+      struct pbuf_custom_ref *pcr;
+      u16_t plen = p->len - poff;
+      newpbuflen = LWIP_MIN(left_to_copy, plen);
+      /* Is this pbuf already empty? */
+      if (!newpbuflen) {
+        poff = 0;
+        p = p->next;
+        continue;
+      }
+      pcr = ip_frag_alloc_pbuf_custom_ref();
+      if (pcr == NULL) {
+        pbuf_free(rambuf);
+        goto memerr;
+      }
+      /* Mirror this pbuf, although we might not need all of it. */
+      newpbuf = pbuf_alloced_custom(PBUF_RAW, newpbuflen, PBUF_REF, &pcr->pc,
+        (u8_t*)p->payload + poff, newpbuflen);
+      if (newpbuf == NULL) {
+        ip_frag_free_pbuf_custom_ref(pcr);
+        pbuf_free(rambuf);
+        goto memerr;
+      }
+      pbuf_ref(p);
+      pcr->original = p;
+      pcr->pc.custom_free_function = ipfrag_free_pbuf_custom;
+
+      /* Add it to end of rambuf's chain, but using pbuf_cat, not pbuf_chain
+       * so that it is removed when pbuf_dechain is later called on rambuf.
+       */
+      pbuf_cat(rambuf, newpbuf);
+      left_to_copy -= newpbuflen;
+      if (left_to_copy) {
+        poff = 0;
+        p = p->next;
+      }
+    }
+    poff += newpbuflen;
+#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
+
+    /* Correct header */
+    last = (left <= netif->mtu - IP_HLEN);
+
+    /* Set new offset and MF flag */
+    tmp = (IP_OFFMASK & (ofo));
+    if (!last) {
+      tmp = tmp | IP_MF;
+    }
+    IPH_OFFSET_SET(iphdr, lwip_htons(tmp));
+    IPH_LEN_SET(iphdr, lwip_htons(fragsize + IP_HLEN));
+    IPH_CHKSUM_SET(iphdr, 0);
+#if CHECKSUM_GEN_IP
+    IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_IP) {
+      IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN));
+    }
+#endif /* CHECKSUM_GEN_IP */
+
+    /* No need for separate header pbuf - we allowed room for it in rambuf
+     * when allocated.
+     */
+    netif->output(netif, rambuf, dest);
+    IPFRAG_STATS_INC(ip_frag.xmit);
+
+    /* Unfortunately we can't reuse rambuf - the hardware may still be
+     * using the buffer. Instead we free it (and the ensuing chain) and
+     * recreate it next time round the loop. If we're lucky the hardware
+     * will have already sent the packet, the free will really free, and
+     * there will be zero memory penalty.
+     */
+
+    pbuf_free(rambuf);
+    left -= fragsize;
+    ofo += nfb;
+  }
+  MIB2_STATS_INC(mib2.ipfragoks);
+  return ERR_OK;
+memerr:
+  MIB2_STATS_INC(mib2.ipfragfails);
+  return ERR_MEM;
+}
+#endif /* IP_FRAG */
+
+#endif /* LWIP_IPV4 */

+ 0 - 1
thirdparty/lwip/src/core/ipv6/README

@@ -1 +0,0 @@
-IPv6 support in lwIP is very experimental.

+ 50 - 0
thirdparty/lwip/src/core/ipv6/dhcp6.c

@@ -0,0 +1,50 @@
+/**
+ * @file
+ *
+ * DHCPv6.
+ */
+
+/*
+ * Copyright (c) 2010 Inico Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Ivan Delamer <delamer@inicotech.com>
+ *
+ *
+ * Please coordinate changes and requests with Ivan Delamer
+ * <delamer@inicotech.com>
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV6 && LWIP_IPV6_DHCP6 /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/ip6_addr.h"
+#include "lwip/def.h"
+
+
+#endif /* LWIP_IPV6 && LWIP_IPV6_DHCP6 */

+ 118 - 0
thirdparty/lwip/src/core/ipv6/ethip6.c

@@ -0,0 +1,118 @@
+/**
+ * @file
+ *
+ * Ethernet output for IPv6. Uses ND tables for link-layer addressing.
+ */
+
+/*
+ * Copyright (c) 2010 Inico Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Ivan Delamer <delamer@inicotech.com>
+ *
+ *
+ * Please coordinate changes and requests with Ivan Delamer
+ * <delamer@inicotech.com>
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV6 && LWIP_ETHERNET
+
+#include "lwip/ethip6.h"
+#include "lwip/nd6.h"
+#include "lwip/pbuf.h"
+#include "lwip/ip6.h"
+#include "lwip/ip6_addr.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/netif.h"
+#include "lwip/icmp6.h"
+#include "lwip/prot/ethernet.h"
+#include "netif/ethernet.h"
+
+#include <string.h>
+
+/**
+ * Resolve and fill-in Ethernet address header for outgoing IPv6 packet.
+ *
+ * For IPv6 multicast, corresponding Ethernet addresses
+ * are selected and the packet is transmitted on the link.
+ *
+ * For unicast addresses, ask the ND6 module what to do. It will either let us
+ * send the the packet right away, or queue the packet for later itself, unless
+ * an error occurs.
+ *
+ * @todo anycast addresses
+ *
+ * @param netif The lwIP network interface which the IP packet will be sent on.
+ * @param q The pbuf(s) containing the IP packet to be sent.
+ * @param ip6addr The IP address of the packet destination.
+ *
+ * @return
+ * - ERR_OK or the return value of @ref nd6_get_next_hop_addr_or_queue.
+ */
+err_t
+ethip6_output(struct netif *netif, struct pbuf *q, const ip6_addr_t *ip6addr)
+{
+  struct eth_addr dest;
+  const u8_t *hwaddr;
+  err_t result;
+
+  /* multicast destination IP address? */
+  if (ip6_addr_ismulticast(ip6addr)) {
+    /* Hash IP multicast address to MAC address.*/
+    dest.addr[0] = 0x33;
+    dest.addr[1] = 0x33;
+    dest.addr[2] = ((const u8_t *)(&(ip6addr->addr[3])))[0];
+    dest.addr[3] = ((const u8_t *)(&(ip6addr->addr[3])))[1];
+    dest.addr[4] = ((const u8_t *)(&(ip6addr->addr[3])))[2];
+    dest.addr[5] = ((const u8_t *)(&(ip6addr->addr[3])))[3];
+
+    /* Send out. */
+    return ethernet_output(netif, q, (const struct eth_addr*)(netif->hwaddr), &dest, ETHTYPE_IPV6);
+  }
+
+  /* We have a unicast destination IP address */
+  /* @todo anycast? */
+
+  /* Ask ND6 what to do with the packet. */
+  result = nd6_get_next_hop_addr_or_queue(netif, q, ip6addr, &hwaddr);
+  if (result != ERR_OK) {
+    return result;
+  }
+
+  /* If no hardware address is returned, nd6 has queued the packet for later. */
+  if (hwaddr == NULL) {
+    return ERR_OK;
+  }
+
+  /* Send out the packet using the returned hardware address. */
+  SMEMCPY(dest.addr, hwaddr, 6);
+  return ethernet_output(netif, q, (const struct eth_addr*)(netif->hwaddr), &dest, ETHTYPE_IPV6);
+}
+
+#endif /* LWIP_IPV6 && LWIP_ETHERNET */

+ 350 - 179
thirdparty/lwip/src/core/ipv6/icmp6.c

@@ -1,179 +1,350 @@
-/*
- * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * This file is part of the lwIP TCP/IP stack.
- *
- * Author: Adam Dunkels <adam@sics.se>
- *
- */
-
-/* Some ICMP messages should be passed to the transport protocols. This
-   is not implemented. */
-
-#include "lwip/opt.h"
-
-#if LWIP_ICMP /* don't build if not configured for use in lwipopts.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;
-
-  ICMP_STATS_INC(icmp.recv);
-
-  /* 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);
-      ICMP_STATS_INC(icmp.lenerr);
-      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)));
-      ICMP_STATS_INC(icmp.chkerr);
-    /*      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)));
-    ICMP_STATS_INC(icmp.xmit);
-
-    /*    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));
-    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;
-
-  /* @todo: can this be PBUF_LINK instead of PBUF_IP? */
-  q = pbuf_alloc(PBUF_IP, 8 + IP_HLEN + 8, PBUF_RAM);
-  /* ICMP header + IP header + 8 bytes of data */
-  if (q == NULL) {
-    LWIP_DEBUGF(ICMP_DEBUG, ("icmp_dest_unreach: failed to allocate pbuf for ICMP packet.\n"));
-    pbuf_free(p);
-    return;
-  }
-  LWIP_ASSERT("check that first pbuf can hold icmp message",
-             (q->len >= (8 + IP_HLEN + 8)));
-
-  iphdr = p->payload;
-
-  idur = q->payload;
-  idur->type = (u8_t)ICMP6_DUR;
-  idur->icode = (u8_t)t;
-
-  SMEMCPY((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);
-
-  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"));
-
-  /* @todo: can this be PBUF_LINK instead of PBUF_IP? */
-  q = pbuf_alloc(PBUF_IP, 8 + IP_HLEN + 8, PBUF_RAM);
-  /* ICMP header + IP header + 8 bytes of data */
-  if (q == NULL) {
-    LWIP_DEBUGF(ICMP_DEBUG, ("icmp_dest_unreach: failed to allocate pbuf for ICMP packet.\n"));
-    pbuf_free(p);
-    return;
-  }
-  LWIP_ASSERT("check that first pbuf can hold icmp message",
-             (q->len >= (8 + IP_HLEN + 8)));
-
-  iphdr = p->payload;
-
-  tehdr = q->payload;
-  tehdr->type = (u8_t)ICMP6_TE;
-  tehdr->icode = (u8_t)t;
-
-  /* copy fields from original packet */
-  SMEMCPY((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);
-  ip_output(q, NULL,
-      (struct ip_addr *)&(iphdr->src), ICMP_TTL, IP_PROTO_ICMP);
-  pbuf_free(q);
-}
-
-#endif /* LWIP_ICMP */
+/**
+ * @file
+ *
+ * IPv6 version of ICMP, as per RFC 4443.
+ */
+
+/*
+ * Copyright (c) 2010 Inico Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Ivan Delamer <delamer@inicotech.com>
+ *
+ *
+ * Please coordinate changes and requests with Ivan Delamer
+ * <delamer@inicotech.com>
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_ICMP6 && LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/icmp6.h"
+#include "lwip/prot/icmp6.h"
+#include "lwip/ip6.h"
+#include "lwip/ip6_addr.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/pbuf.h"
+#include "lwip/netif.h"
+#include "lwip/nd6.h"
+#include "lwip/mld6.h"
+#include "lwip/ip.h"
+#include "lwip/stats.h"
+
+#include <string.h>
+
+#ifndef LWIP_ICMP6_DATASIZE
+#define LWIP_ICMP6_DATASIZE   8
+#endif
+#if LWIP_ICMP6_DATASIZE == 0
+#define LWIP_ICMP6_DATASIZE   8
+#endif
+
+/* Forward declarations */
+static void icmp6_send_response(struct pbuf *p, u8_t code, u32_t data, u8_t type);
+
+
+/**
+ * Process an input ICMPv6 message. Called by ip6_input.
+ *
+ * Will generate a reply for echo requests. Other messages are forwarded
+ * to nd6_input, or mld6_input.
+ *
+ * @param p the mld packet, p->payload pointing to the icmpv6 header
+ * @param inp the netif on which this packet was received
+ */
+void
+icmp6_input(struct pbuf *p, struct netif *inp)
+{
+  struct icmp6_hdr *icmp6hdr;
+  struct pbuf *r;
+  const ip6_addr_t *reply_src;
+
+  ICMP6_STATS_INC(icmp6.recv);
+
+  /* Check that ICMPv6 header fits in payload */
+  if (p->len < sizeof(struct icmp6_hdr)) {
+    /* drop short packets */
+    pbuf_free(p);
+    ICMP6_STATS_INC(icmp6.lenerr);
+    ICMP6_STATS_INC(icmp6.drop);
+    return;
+  }
+
+  icmp6hdr = (struct icmp6_hdr *)p->payload;
+
+#if CHECKSUM_CHECK_ICMP6
+  IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_CHECK_ICMP6) {
+    if (ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->tot_len, ip6_current_src_addr(),
+                          ip6_current_dest_addr()) != 0) {
+      /* Checksum failed */
+      pbuf_free(p);
+      ICMP6_STATS_INC(icmp6.chkerr);
+      ICMP6_STATS_INC(icmp6.drop);
+      return;
+    }
+  }
+#endif /* CHECKSUM_CHECK_ICMP6 */
+
+  switch (icmp6hdr->type) {
+  case ICMP6_TYPE_NA: /* Neighbor advertisement */
+  case ICMP6_TYPE_NS: /* Neighbor solicitation */
+  case ICMP6_TYPE_RA: /* Router advertisement */
+  case ICMP6_TYPE_RD: /* Redirect */
+  case ICMP6_TYPE_PTB: /* Packet too big */
+    nd6_input(p, inp);
+    return;
+    break;
+  case ICMP6_TYPE_RS:
+#if LWIP_IPV6_FORWARD
+    /* @todo implement router functionality */
+#endif
+    break;
+#if LWIP_IPV6_MLD
+  case ICMP6_TYPE_MLQ:
+  case ICMP6_TYPE_MLR:
+  case ICMP6_TYPE_MLD:
+    mld6_input(p, inp);
+    return;
+    break;
+#endif
+  case ICMP6_TYPE_EREQ:
+#if !LWIP_MULTICAST_PING
+    /* multicast destination address? */
+    if (ip6_addr_ismulticast(ip6_current_dest_addr())) {
+      /* drop */
+      pbuf_free(p);
+      ICMP6_STATS_INC(icmp6.drop);
+      return;
+    }
+#endif /* LWIP_MULTICAST_PING */
+
+    /* Allocate reply. */
+    r = pbuf_alloc(PBUF_IP, p->tot_len, PBUF_RAM);
+    if (r == NULL) {
+      /* drop */
+      pbuf_free(p);
+      ICMP6_STATS_INC(icmp6.memerr);
+      return;
+    }
+
+    /* Copy echo request. */
+    if (pbuf_copy(r, p) != ERR_OK) {
+      /* drop */
+      pbuf_free(p);
+      pbuf_free(r);
+      ICMP6_STATS_INC(icmp6.err);
+      return;
+    }
+
+    /* Determine reply source IPv6 address. */
+#if LWIP_MULTICAST_PING
+    if (ip6_addr_ismulticast(ip6_current_dest_addr())) {
+      reply_src = ip_2_ip6(ip6_select_source_address(inp, ip6_current_src_addr()));
+      if (reply_src == NULL) {
+        /* drop */
+        pbuf_free(p);
+        pbuf_free(r);
+        ICMP6_STATS_INC(icmp6.rterr);
+        return;
+      }
+    }
+    else
+#endif /* LWIP_MULTICAST_PING */
+    {
+      reply_src = ip6_current_dest_addr();
+    }
+
+    /* Set fields in reply. */
+    ((struct icmp6_echo_hdr *)(r->payload))->type = ICMP6_TYPE_EREP;
+    ((struct icmp6_echo_hdr *)(r->payload))->chksum = 0;
+#if CHECKSUM_GEN_ICMP6
+    IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_GEN_ICMP6) {
+      ((struct icmp6_echo_hdr *)(r->payload))->chksum = ip6_chksum_pseudo(r,
+          IP6_NEXTH_ICMP6, r->tot_len, reply_src, ip6_current_src_addr());
+    }
+#endif /* CHECKSUM_GEN_ICMP6 */
+
+    /* Send reply. */
+    ICMP6_STATS_INC(icmp6.xmit);
+    ip6_output_if(r, reply_src, ip6_current_src_addr(),
+        LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, inp);
+    pbuf_free(r);
+
+    break;
+  default:
+    ICMP6_STATS_INC(icmp6.proterr);
+    ICMP6_STATS_INC(icmp6.drop);
+    break;
+  }
+
+  pbuf_free(p);
+}
+
+
+/**
+ * Send an icmpv6 'destination unreachable' packet.
+ *
+ * @param p the input packet for which the 'unreachable' should be sent,
+ *          p->payload pointing to the IPv6 header
+ * @param c ICMPv6 code for the unreachable type
+ */
+void
+icmp6_dest_unreach(struct pbuf *p, enum icmp6_dur_code c)
+{
+  icmp6_send_response(p, c, 0, ICMP6_TYPE_DUR);
+}
+
+/**
+ * Send an icmpv6 'packet too big' packet.
+ *
+ * @param p the input packet for which the 'packet too big' should be sent,
+ *          p->payload pointing to the IPv6 header
+ * @param mtu the maximum mtu that we can accept
+ */
+void
+icmp6_packet_too_big(struct pbuf *p, u32_t mtu)
+{
+  icmp6_send_response(p, 0, mtu, ICMP6_TYPE_PTB);
+}
+
+/**
+ * Send an icmpv6 'time exceeded' packet.
+ *
+ * @param p the input packet for which the 'unreachable' should be sent,
+ *          p->payload pointing to the IPv6 header
+ * @param c ICMPv6 code for the time exceeded type
+ */
+void
+icmp6_time_exceeded(struct pbuf *p, enum icmp6_te_code c)
+{
+  icmp6_send_response(p, c, 0, ICMP6_TYPE_TE);
+}
+
+/**
+ * Send an icmpv6 'parameter problem' packet.
+ *
+ * @param p the input packet for which the 'param problem' should be sent,
+ *          p->payload pointing to the IP header
+ * @param c ICMPv6 code for the param problem type
+ * @param pointer the pointer to the byte where the parameter is found
+ */
+void
+icmp6_param_problem(struct pbuf *p, enum icmp6_pp_code c, u32_t pointer)
+{
+  icmp6_send_response(p, c, pointer, ICMP6_TYPE_PP);
+}
+
+/**
+ * Send an ICMPv6 packet in response to an incoming packet.
+ *
+ * @param p the input packet for which the response should be sent,
+ *          p->payload pointing to the IPv6 header
+ * @param code Code of the ICMPv6 header
+ * @param data Additional 32-bit parameter in the ICMPv6 header
+ * @param type Type of the ICMPv6 header
+ */
+static void
+icmp6_send_response(struct pbuf *p, u8_t code, u32_t data, u8_t type)
+{
+  struct pbuf *q;
+  struct icmp6_hdr *icmp6hdr;
+  const ip6_addr_t *reply_src;
+  ip6_addr_t *reply_dest;
+  ip6_addr_t reply_src_local, reply_dest_local;
+  struct ip6_hdr *ip6hdr;
+  struct netif *netif;
+
+  /* ICMPv6 header + IPv6 header + data */
+  q = pbuf_alloc(PBUF_IP, sizeof(struct icmp6_hdr) + IP6_HLEN + LWIP_ICMP6_DATASIZE,
+                 PBUF_RAM);
+  if (q == NULL) {
+    LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded: failed to allocate pbuf for ICMPv6 packet.\n"));
+    ICMP6_STATS_INC(icmp6.memerr);
+    return;
+  }
+  LWIP_ASSERT("check that first pbuf can hold icmp 6message",
+             (q->len >= (sizeof(struct icmp6_hdr) + IP6_HLEN + LWIP_ICMP6_DATASIZE)));
+
+  icmp6hdr = (struct icmp6_hdr *)q->payload;
+  icmp6hdr->type = type;
+  icmp6hdr->code = code;
+  icmp6hdr->data = data;
+
+  /* copy fields from original packet */
+  SMEMCPY((u8_t *)q->payload + sizeof(struct icmp6_hdr), (u8_t *)p->payload,
+          IP6_HLEN + LWIP_ICMP6_DATASIZE);
+
+  /* Get the destination address and netif for this ICMP message. */
+  if ((ip_current_netif() == NULL) ||
+      ((code == ICMP6_TE_FRAG) && (type == ICMP6_TYPE_TE))) {
+    /* Special case, as ip6_current_xxx is either NULL, or points
+     * to a different packet than the one that expired.
+     * We must use the addresses that are stored in the expired packet. */
+    ip6hdr = (struct ip6_hdr *)p->payload;
+    /* copy from packed address to aligned address */
+    ip6_addr_copy(reply_dest_local, ip6hdr->src);
+    ip6_addr_copy(reply_src_local, ip6hdr->dest);
+    reply_dest = &reply_dest_local;
+    reply_src = &reply_src_local;
+    netif = ip6_route(reply_src, reply_dest);
+    if (netif == NULL) {
+      /* drop */
+      pbuf_free(q);
+      ICMP6_STATS_INC(icmp6.rterr);
+      return;
+    }
+  }
+  else {
+    netif = ip_current_netif();
+    reply_dest = ip6_current_src_addr();
+
+    /* Select an address to use as source. */
+    reply_src = ip_2_ip6(ip6_select_source_address(netif, reply_dest));
+    if (reply_src == NULL) {
+      /* drop */
+      pbuf_free(q);
+      ICMP6_STATS_INC(icmp6.rterr);
+      return;
+    }
+  }
+
+  /* calculate checksum */
+  icmp6hdr->chksum = 0;
+#if CHECKSUM_GEN_ICMP6
+  IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_ICMP6) {
+    icmp6hdr->chksum = ip6_chksum_pseudo(q, IP6_NEXTH_ICMP6, q->tot_len,
+      reply_src, reply_dest);
+  }
+#endif /* CHECKSUM_GEN_ICMP6 */
+
+  ICMP6_STATS_INC(icmp6.xmit);
+  ip6_output_if(q, reply_src, reply_dest, LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, netif);
+  pbuf_free(q);
+}
+
+#endif /* LWIP_ICMP6 && LWIP_IPV6 */

+ 53 - 163
thirdparty/lwip/src/core/ipv6/inet6.c

@@ -1,163 +1,53 @@
-/**
- * @file
- * Functions common to all TCP/IPv6 modules, such as the Internet checksum and the
- * byte order functions.
- *
- */
-
-/*
- * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
- * All rights reserved. 
- * 
- * Redistribution and use in source and binary forms, with or without modification, 
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission. 
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
- * OF SUCH DAMAGE.
- *
- * This file is part of the lwIP TCP/IP stack.
- * 
- * Author: Adam Dunkels <adam@sics.se>
- *
- */
-
-#include "lwip/opt.h"
-
-#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);
-}
+/**
+ * @file
+ *
+ * INET v6 addresses.
+ */
+
+/*
+ * Copyright (c) 2010 Inico Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Ivan Delamer <delamer@inicotech.com>
+ *
+ *
+ * Please coordinate changes and requests with Ivan Delamer
+ * <delamer@inicotech.com>
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV6 && LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/def.h"
+#include "lwip/inet.h"
+
+/** This variable is initialized by the system to contain the wildcard IPv6 address.
+ */
+const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT;
+
+#endif /* LWIP_IPV6 */

+ 1122 - 397
thirdparty/lwip/src/core/ipv6/ip6.c

@@ -1,397 +1,1122 @@
-/*
- * 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_impl.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, ((struct ip_addr *)&(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) {
-#if LWIP_ICMP
-    /* Don't send ICMP messages in response to ICMP messages */
-    if (iphdr->nexthdr != IP_PROTO_ICMP) {
-      icmp_time_exceeded(p, ICMP_TE_TTL);
-    }
-#endif /* LWIP_ICMP */
-    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, ((struct ip_addr *)&(iphdr->dest)));
-#endif /* IP_DEBUG */
-  LWIP_DEBUGF(IP_DEBUG, ("\n"));
-
-  IP_STATS_INC(ip.fw);
-  IP_STATS_INC(ip.xmit);
-
-  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 */
-
-
-  IP_STATS_INC(ip.recv);
-
-  /* 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);
-    IP_STATS_INC(ip.err);
-    IP_STATS_INC(ip.drop);
-    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, ((struct ip_addr *)&(iphdr->dest)));
-    LWIP_DEBUGF(IP_DEBUG, ("netif->ip_addr "));
-    ip_addr_debug_print(IP_DEBUG, ((struct ip_addr *)&(iphdr->dest)));
-    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 */
-#if 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 */
-
-  if(pbuf_header(p, -IP_HLEN)) {
-    LWIP_ASSERT("Can't move over header in packet", 0);
-    return;
-  }
-
-  switch (iphdr->nexthdr) {
-  case IP_PROTO_UDP:
-    udp_input(p, inp);
-    break;
-  case IP_PROTO_TCP:
-    tcp_input(p, inp);
-    break;
-#if LWIP_ICMP
-  case IP_PROTO_ICMP:
-    icmp_input(p, inp);
-    break;
-#endif /* LWIP_ICMP */
-  default:
-#if LWIP_ICMP
-    /* send ICMP destination protocol unreachable */
-    icmp_dest_unreach(p, ICMP_DUR_PROTO);
-#endif /* LWIP_ICMP */
-    pbuf_free(p);
-    LWIP_DEBUGF(IP_DEBUG, ("Unsupported transport protocol %"U16_F"\n",
-          iphdr->nexthdr));
-
-    IP_STATS_INC(ip.proterr);
-    IP_STATS_INC(ip.drop);
-  }
-  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;
-
-  LWIP_DEBUGF(IP_DEBUG, ("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"));
-    IP_STATS_INC(ip.err);
-
-    return ERR_BUF;
-  }
-  LWIP_DEBUGF(IP_DEBUG, ("len %"U16_F" tot_len %"U16_F"\n", p->len, p->tot_len));
-
-  iphdr = p->payload;
-
-
-  if (dest != IP_HDRINCL) {
-    LWIP_DEBUGF(IP_DEBUG, ("!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);
-  }
-
-  IP_STATS_INC(ip.xmit);
-
-  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));
-    IP_STATS_INC(ip.rterr);
-    return ERR_RTE;
-  }
-
-  return ip_output_if (p, src, dest, ttl, proto, netif);
-}
-
-#if LWIP_NETIF_HWADDRHINT
-err_t
-ip_output_hinted(struct pbuf *p, struct ip_addr *src, struct ip_addr *dest,
-          u8_t ttl, u8_t tos, u8_t proto, u8_t *addr_hint)
-{
-  struct netif *netif;
-  err_t err;
-
-  if ((netif = ip_route(dest)) == NULL) {
-    LWIP_DEBUGF(IP_DEBUG, ("ip_output: No route to 0x%"X32_F"\n", dest->addr));
-    IP_STATS_INC(ip.rterr);
-    return ERR_RTE;
-  }
-
-  LWIP_NETIF_HWADDRHINT(netif, addr_hint);
-  err = ip_output_if(p, src, dest, ttl, tos, proto, netif);
-  LWIP_NETIF_HWADDRHINT(netif, NULL);
-
-  return err;
-}
-#endif /* LWIP_NETIF_HWADDRHINT*/
-
-#if IP_DEBUG
-void
-ip_debug_print(struct pbuf *p)
-{
-  struct ip_hdr *iphdr = p->payload;
-
-  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 */
+/**
+ * @file
+ *
+ * IPv6 layer.
+ */
+
+/*
+ * Copyright (c) 2010 Inico Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Ivan Delamer <delamer@inicotech.com>
+ *
+ *
+ * Please coordinate changes and requests with Ivan Delamer
+ * <delamer@inicotech.com>
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV6  /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/def.h"
+#include "lwip/mem.h"
+#include "lwip/netif.h"
+#include "lwip/ip.h"
+#include "lwip/ip6.h"
+#include "lwip/ip6_addr.h"
+#include "lwip/ip6_frag.h"
+#include "lwip/icmp6.h"
+#include "lwip/raw.h"
+#include "lwip/udp.h"
+#include "lwip/priv/tcp_priv.h"
+#include "lwip/dhcp6.h"
+#include "lwip/nd6.h"
+#include "lwip/mld6.h"
+#include "lwip/debug.h"
+#include "lwip/stats.h"
+
+#ifdef LWIP_HOOK_FILENAME
+#include LWIP_HOOK_FILENAME
+#endif
+
+/**
+ * Finds the appropriate network interface for a given IPv6 address. It tries to select
+ * a netif following a sequence of heuristics:
+ * 1) if there is only 1 netif, return it
+ * 2) if the destination is a link-local address, try to match the src address to a netif.
+ *    this is a tricky case because with multiple netifs, link-local addresses only have
+ *    meaning within a particular subnet/link.
+ * 3) tries to match the destination subnet to a configured address
+ * 4) tries to find a router
+ * 5) tries to match the source address to the netif
+ * 6) returns the default netif, if configured
+ *
+ * @param src the source IPv6 address, if known
+ * @param dest the destination IPv6 address for which to find the route
+ * @return the netif on which to send to reach dest
+ */
+struct netif *
+ip6_route(const ip6_addr_t *src, const ip6_addr_t *dest)
+{
+  struct netif *netif;
+  s8_t i;
+
+  /* If single netif configuration, fast return. */
+  if ((netif_list != NULL) && (netif_list->next == NULL)) {
+    if (!netif_is_up(netif_list) || !netif_is_link_up(netif_list)) {
+      return NULL;
+    }
+    return netif_list;
+  }
+
+  /* Special processing for link-local addresses. */
+  if (ip6_addr_islinklocal(dest)) {
+    if (ip6_addr_isany(src)) {
+      /* Use default netif, if Up. */
+      if (netif_default == NULL || !netif_is_up(netif_default) ||
+          !netif_is_link_up(netif_default)) {
+        return NULL;
+      }
+      return netif_default;
+    }
+
+    /* Try to find the netif for the source address, checking that link is up. */
+    for (netif = netif_list; netif != NULL; netif = netif->next) {
+      if (!netif_is_up(netif) || !netif_is_link_up(netif)) {
+        continue;
+      }
+      for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+        if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) &&
+            ip6_addr_cmp(src, netif_ip6_addr(netif, i))) {
+          return netif;
+        }
+      }
+    }
+
+    /* netif not found, use default netif, if up */
+    if (netif_default == NULL || !netif_is_up(netif_default) ||
+        !netif_is_link_up(netif_default)) {
+      return NULL;
+    }
+    return netif_default;
+  }
+
+  /* we come here for non-link-local addresses */
+#ifdef LWIP_HOOK_IP6_ROUTE
+  netif = LWIP_HOOK_IP6_ROUTE(src, dest);
+  if (netif != NULL) {
+    return netif;
+  }
+#endif
+
+  /* See if the destination subnet matches a configured address. */
+  for (netif = netif_list; netif != NULL; netif = netif->next) {
+    if (!netif_is_up(netif) || !netif_is_link_up(netif)) {
+      continue;
+    }
+    for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+      if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) &&
+          ip6_addr_netcmp(dest, netif_ip6_addr(netif, i))) {
+        return netif;
+      }
+    }
+  }
+
+  /* Get the netif for a suitable router. */
+  netif = nd6_find_route(dest);
+  if ((netif != NULL) && netif_is_up(netif) && netif_is_link_up(netif)) {
+    return netif;
+  }
+
+  /* try with the netif that matches the source address. */
+  if (!ip6_addr_isany(src)) {
+    for (netif = netif_list; netif != NULL; netif = netif->next) {
+      if (!netif_is_up(netif) || !netif_is_link_up(netif)) {
+        continue;
+      }
+      for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+        if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) &&
+            ip6_addr_cmp(src, netif_ip6_addr(netif, i))) {
+          return netif;
+        }
+      }
+    }
+  }
+
+#if LWIP_NETIF_LOOPBACK && !LWIP_HAVE_LOOPIF
+  /* loopif is disabled, loopback traffic is passed through any netif */
+  if (ip6_addr_isloopback(dest)) {
+    /* don't check for link on loopback traffic */
+    if (netif_default != NULL && netif_is_up(netif_default)) {
+      return netif_default;
+    }
+    /* default netif is not up, just use any netif for loopback traffic */
+    for (netif = netif_list; netif != NULL; netif = netif->next) {
+      if (netif_is_up(netif)) {
+        return netif;
+      }
+    }
+    return NULL;
+  }
+#endif /* LWIP_NETIF_LOOPBACK && !LWIP_HAVE_LOOPIF */
+
+  /* no matching netif found, use default netif, if up */
+  if ((netif_default == NULL) || !netif_is_up(netif_default) || !netif_is_link_up(netif_default)) {
+    return NULL;
+  }
+  return netif_default;
+}
+
+/**
+ * @ingroup ip6
+ * Select the best IPv6 source address for a given destination
+ * IPv6 address. Loosely follows RFC 3484. "Strong host" behavior
+ * is assumed.
+ *
+ * @param netif the netif on which to send a packet
+ * @param dest the destination we are trying to reach
+ * @return the most suitable source address to use, or NULL if no suitable
+ *         source address is found
+ */
+const ip_addr_t *
+ip6_select_source_address(struct netif *netif, const ip6_addr_t *dest)
+{
+  const ip_addr_t *src = NULL;
+  u8_t i;
+
+  /* If dest is link-local, choose a link-local source. */
+  if (ip6_addr_islinklocal(dest) || ip6_addr_ismulticast_linklocal(dest) || ip6_addr_ismulticast_iflocal(dest)) {
+    for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+      if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) &&
+          ip6_addr_islinklocal(netif_ip6_addr(netif, i))) {
+        return netif_ip_addr6(netif, i);
+      }
+    }
+  }
+
+  /* Choose a site-local with matching prefix. */
+  if (ip6_addr_issitelocal(dest) || ip6_addr_ismulticast_sitelocal(dest)) {
+    for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+      if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) &&
+          ip6_addr_issitelocal(netif_ip6_addr(netif, i)) &&
+          ip6_addr_netcmp(dest, netif_ip6_addr(netif, i))) {
+        return netif_ip_addr6(netif, i);
+      }
+    }
+  }
+
+  /* Choose a unique-local with matching prefix. */
+  if (ip6_addr_isuniquelocal(dest) || ip6_addr_ismulticast_orglocal(dest)) {
+    for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+      if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) &&
+          ip6_addr_isuniquelocal(netif_ip6_addr(netif, i)) &&
+          ip6_addr_netcmp(dest, netif_ip6_addr(netif, i))) {
+        return netif_ip_addr6(netif, i);
+      }
+    }
+  }
+
+  /* Choose a global with best matching prefix. */
+  if (ip6_addr_isglobal(dest) || ip6_addr_ismulticast_global(dest)) {
+    for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+      if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) &&
+          ip6_addr_isglobal(netif_ip6_addr(netif, i))) {
+        if (src == NULL) {
+          src = netif_ip_addr6(netif, i);
+        }
+        else {
+          /* Replace src only if we find a prefix match. */
+          /* @todo find longest matching prefix. */
+          if ((!(ip6_addr_netcmp(ip_2_ip6(src), dest))) &&
+              ip6_addr_netcmp(netif_ip6_addr(netif, i), dest)) {
+            src = netif_ip_addr6(netif, i);
+          }
+        }
+      }
+    }
+    if (src != NULL) {
+      return src;
+    }
+  }
+
+  /* Last resort: see if arbitrary prefix matches. */
+  for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+    if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) &&
+        ip6_addr_netcmp(dest, netif_ip6_addr(netif, i))) {
+      return netif_ip_addr6(netif, i);
+    }
+  }
+
+  return NULL;
+}
+
+#if LWIP_IPV6_FORWARD
+/**
+ * Forwards an IPv6 packet. It finds an appropriate route for the
+ * packet, decrements the HL value of the packet, and outputs
+ * the packet on the appropriate interface.
+ *
+ * @param p the packet to forward (p->payload points to IP header)
+ * @param iphdr the IPv6 header of the input packet
+ * @param inp the netif on which this packet was received
+ */
+static void
+ip6_forward(struct pbuf *p, struct ip6_hdr *iphdr, struct netif *inp)
+{
+  struct netif *netif;
+
+  /* do not forward link-local or loopback addresses */
+  if (ip6_addr_islinklocal(ip6_current_dest_addr()) ||
+      ip6_addr_isloopback(ip6_current_dest_addr())) {
+    LWIP_DEBUGF(IP6_DEBUG, ("ip6_forward: not forwarding link-local address.\n"));
+    IP6_STATS_INC(ip6.rterr);
+    IP6_STATS_INC(ip6.drop);
+    return;
+  }
+
+  /* Find network interface where to forward this IP packet to. */
+  netif = ip6_route(IP6_ADDR_ANY6, ip6_current_dest_addr());
+  if (netif == NULL) {
+    LWIP_DEBUGF(IP6_DEBUG, ("ip6_forward: no route for %"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F"\n",
+        IP6_ADDR_BLOCK1(ip6_current_dest_addr()),
+        IP6_ADDR_BLOCK2(ip6_current_dest_addr()),
+        IP6_ADDR_BLOCK3(ip6_current_dest_addr()),
+        IP6_ADDR_BLOCK4(ip6_current_dest_addr()),
+        IP6_ADDR_BLOCK5(ip6_current_dest_addr()),
+        IP6_ADDR_BLOCK6(ip6_current_dest_addr()),
+        IP6_ADDR_BLOCK7(ip6_current_dest_addr()),
+        IP6_ADDR_BLOCK8(ip6_current_dest_addr())));
+#if LWIP_ICMP6
+    /* Don't send ICMP messages in response to ICMP messages */
+    if (IP6H_NEXTH(iphdr) != IP6_NEXTH_ICMP6) {
+      icmp6_dest_unreach(p, ICMP6_DUR_NO_ROUTE);
+    }
+#endif /* LWIP_ICMP6 */
+    IP6_STATS_INC(ip6.rterr);
+    IP6_STATS_INC(ip6.drop);
+    return;
+  }
+  /* Do not forward packets onto the same network interface on which
+   * they arrived. */
+  if (netif == inp) {
+    LWIP_DEBUGF(IP6_DEBUG, ("ip6_forward: not bouncing packets back on incoming interface.\n"));
+    IP6_STATS_INC(ip6.rterr);
+    IP6_STATS_INC(ip6.drop);
+    return;
+  }
+
+  /* decrement HL */
+  IP6H_HOPLIM_SET(iphdr, IP6H_HOPLIM(iphdr) - 1);
+  /* send ICMP6 if HL == 0 */
+  if (IP6H_HOPLIM(iphdr) == 0) {
+#if LWIP_ICMP6
+    /* Don't send ICMP messages in response to ICMP messages */
+    if (IP6H_NEXTH(iphdr) != IP6_NEXTH_ICMP6) {
+      icmp6_time_exceeded(p, ICMP6_TE_HL);
+    }
+#endif /* LWIP_ICMP6 */
+    IP6_STATS_INC(ip6.drop);
+    return;
+  }
+
+  if (netif->mtu && (p->tot_len > netif->mtu)) {
+#if LWIP_ICMP6
+    /* Don't send ICMP messages in response to ICMP messages */
+    if (IP6H_NEXTH(iphdr) != IP6_NEXTH_ICMP6) {
+      icmp6_packet_too_big(p, netif->mtu);
+    }
+#endif /* LWIP_ICMP6 */
+    IP6_STATS_INC(ip6.drop);
+    return;
+  }
+
+  LWIP_DEBUGF(IP6_DEBUG, ("ip6_forward: forwarding packet to %"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F"\n",
+      IP6_ADDR_BLOCK1(ip6_current_dest_addr()),
+      IP6_ADDR_BLOCK2(ip6_current_dest_addr()),
+      IP6_ADDR_BLOCK3(ip6_current_dest_addr()),
+      IP6_ADDR_BLOCK4(ip6_current_dest_addr()),
+      IP6_ADDR_BLOCK5(ip6_current_dest_addr()),
+      IP6_ADDR_BLOCK6(ip6_current_dest_addr()),
+      IP6_ADDR_BLOCK7(ip6_current_dest_addr()),
+      IP6_ADDR_BLOCK8(ip6_current_dest_addr())));
+
+  /* transmit pbuf on chosen interface */
+  netif->output_ip6(netif, p, ip6_current_dest_addr());
+  IP6_STATS_INC(ip6.fw);
+  IP6_STATS_INC(ip6.xmit);
+  return;
+}
+#endif /* LWIP_IPV6_FORWARD */
+
+/**
+ * This function is called by the network interface device driver when
+ * an IPv6 packet is received. The function does the basic checks of the
+ * IP header such as packet size being at least larger than the header
+ * size etc. If the packet was not destined for us, the packet is
+ * forwarded (using ip6_forward).
+ *
+ * Finally, the packet is sent to the upper layer protocol input function.
+ *
+ * @param p the received IPv6 packet (p->payload points to IPv6 header)
+ * @param inp the netif on which this packet was received
+ * @return ERR_OK if the packet was processed (could return ERR_* if it wasn't
+ *         processed, but currently always returns ERR_OK)
+ */
+err_t
+ip6_input(struct pbuf *p, struct netif *inp)
+{
+  struct ip6_hdr *ip6hdr;
+  struct netif *netif;
+  u8_t nexth;
+  u16_t hlen; /* the current header length */
+  u8_t i;
+#if 0 /*IP_ACCEPT_LINK_LAYER_ADDRESSING*/
+  @todo
+  int check_ip_src=1;
+#endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING */
+
+  IP6_STATS_INC(ip6.recv);
+
+  /* identify the IP header */
+  ip6hdr = (struct ip6_hdr *)p->payload;
+  if (IP6H_V(ip6hdr) != 6) {
+    LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_WARNING, ("IPv6 packet dropped due to bad version number %"U32_F"\n",
+        IP6H_V(ip6hdr)));
+    pbuf_free(p);
+    IP6_STATS_INC(ip6.err);
+    IP6_STATS_INC(ip6.drop);
+    return ERR_OK;
+  }
+
+#ifdef LWIP_HOOK_IP6_INPUT
+  if (LWIP_HOOK_IP6_INPUT(p, inp)) {
+    /* the packet has been eaten */
+    return ERR_OK;
+  }
+#endif
+
+  /* header length exceeds first pbuf length, or ip length exceeds total pbuf length? */
+  if ((IP6_HLEN > p->len) || ((IP6H_PLEN(ip6hdr) + IP6_HLEN) > p->tot_len)) {
+    if (IP6_HLEN > p->len) {
+      LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+        ("IPv6 header (len %"U16_F") does not fit in first pbuf (len %"U16_F"), IP packet dropped.\n",
+            (u16_t)IP6_HLEN, p->len));
+    }
+    if ((IP6H_PLEN(ip6hdr) + IP6_HLEN) > p->tot_len) {
+      LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+        ("IPv6 (plen %"U16_F") is longer than pbuf (len %"U16_F"), IP packet dropped.\n",
+            (u16_t)(IP6H_PLEN(ip6hdr) + IP6_HLEN), p->tot_len));
+    }
+    /* free (drop) packet pbufs */
+    pbuf_free(p);
+    IP6_STATS_INC(ip6.lenerr);
+    IP6_STATS_INC(ip6.drop);
+    return ERR_OK;
+  }
+
+  /* Trim pbuf. This should have been done at the netif layer,
+   * but we'll do it anyway just to be sure that its done. */
+  pbuf_realloc(p, IP6_HLEN + IP6H_PLEN(ip6hdr));
+
+  /* copy IP addresses to aligned ip6_addr_t */
+  ip_addr_copy_from_ip6(ip_data.current_iphdr_dest, ip6hdr->dest);
+  ip_addr_copy_from_ip6(ip_data.current_iphdr_src, ip6hdr->src);
+
+  /* Don't accept virtual IPv4 mapped IPv6 addresses.
+   * Don't accept multicast source addresses. */
+  if (ip6_addr_isipv4mappedipv6(ip_2_ip6(&ip_data.current_iphdr_dest)) ||
+     ip6_addr_isipv4mappedipv6(ip_2_ip6(&ip_data.current_iphdr_src)) ||
+     ip6_addr_ismulticast(ip_2_ip6(&ip_data.current_iphdr_src))) {
+    IP6_STATS_INC(ip6.err);
+    IP6_STATS_INC(ip6.drop);
+    return ERR_OK;
+  }
+
+  /* current header pointer. */
+  ip_data.current_ip6_header = ip6hdr;
+
+  /* In netif, used in case we need to send ICMPv6 packets back. */
+  ip_data.current_netif = inp;
+  ip_data.current_input_netif = inp;
+
+  /* match packet against an interface, i.e. is this packet for us? */
+  if (ip6_addr_ismulticast(ip6_current_dest_addr())) {
+    /* Always joined to multicast if-local and link-local all-nodes group. */
+    if (ip6_addr_isallnodes_iflocal(ip6_current_dest_addr()) ||
+        ip6_addr_isallnodes_linklocal(ip6_current_dest_addr())) {
+      netif = inp;
+    }
+#if LWIP_IPV6_MLD
+    else if (mld6_lookfor_group(inp, ip6_current_dest_addr())) {
+      netif = inp;
+    }
+#else /* LWIP_IPV6_MLD */
+    else if (ip6_addr_issolicitednode(ip6_current_dest_addr())) {
+      /* Filter solicited node packets when MLD is not enabled
+       * (for Neighbor discovery). */
+      netif = NULL;
+      for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+        if (ip6_addr_isvalid(netif_ip6_addr_state(inp, i)) &&
+            ip6_addr_cmp_solicitednode(ip6_current_dest_addr(), netif_ip6_addr(inp, i))) {
+          netif = inp;
+          LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: solicited node packet accepted on interface %c%c\n",
+              netif->name[0], netif->name[1]));
+          break;
+        }
+      }
+    }
+#endif /* LWIP_IPV6_MLD */
+    else {
+      netif = NULL;
+    }
+  } else {
+    /* start trying with inp. if that's not acceptable, start walking the
+       list of configured netifs.
+       'first' is used as a boolean to mark whether we started walking the list */
+    int first = 1;
+    netif = inp;
+    do {
+      /* interface is up? */
+      if (netif_is_up(netif)) {
+        /* unicast to this interface address? address configured? */
+        for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+          if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) &&
+              ip6_addr_cmp(ip6_current_dest_addr(), netif_ip6_addr(netif, i))) {
+            /* exit outer loop */
+            goto netif_found;
+          }
+        }
+      }
+      if (first) {
+        if (ip6_addr_islinklocal(ip6_current_dest_addr())
+#if !LWIP_NETIF_LOOPBACK || LWIP_HAVE_LOOPIF
+            || ip6_addr_isloopback(ip6_current_dest_addr())
+#endif /* !LWIP_NETIF_LOOPBACK || LWIP_HAVE_LOOPIF */
+        ) {
+          /* Do not match link-local addresses to other netifs. The loopback
+           * address is to be considered link-local and packets to it should be
+           * dropped on other interfaces, as per RFC 4291 Sec. 2.5.3. This
+           * requirement cannot be implemented in the case that loopback
+           * traffic is sent across a non-loopback interface, however.
+           */
+          netif = NULL;
+          break;
+        }
+        first = 0;
+        netif = netif_list;
+      } else {
+        netif = netif->next;
+      }
+      if (netif == inp) {
+        netif = netif->next;
+      }
+    } while (netif != NULL);
+netif_found:
+    LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet accepted on interface %c%c\n",
+        netif ? netif->name[0] : 'X', netif? netif->name[1] : 'X'));
+  }
+
+  /* "::" packet source address? (used in duplicate address detection) */
+  if (ip6_addr_isany(ip6_current_src_addr()) &&
+      (!ip6_addr_issolicitednode(ip6_current_dest_addr()))) {
+    /* packet source is not valid */
+    /* free (drop) packet pbufs */
+    LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with src ANY_ADDRESS dropped\n"));
+    pbuf_free(p);
+    IP6_STATS_INC(ip6.drop);
+    goto ip6_input_cleanup;
+  }
+
+  /* packet not for us? */
+  if (netif == NULL) {
+    /* packet not for us, route or discard */
+    LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_TRACE, ("ip6_input: packet not for us.\n"));
+#if LWIP_IPV6_FORWARD
+    /* non-multicast packet? */
+    if (!ip6_addr_ismulticast(ip6_current_dest_addr())) {
+      /* try to forward IP packet on (other) interfaces */
+      ip6_forward(p, ip6hdr, inp);
+    }
+#endif /* LWIP_IPV6_FORWARD */
+    pbuf_free(p);
+    goto ip6_input_cleanup;
+  }
+
+  /* current netif pointer. */
+  ip_data.current_netif = netif;
+
+  /* Save next header type. */
+  nexth = IP6H_NEXTH(ip6hdr);
+
+  /* Init header length. */
+  hlen = ip_data.current_ip_header_tot_len = IP6_HLEN;
+
+  /* Move to payload. */
+  pbuf_header(p, -IP6_HLEN);
+
+  /* Process known option extension headers, if present. */
+  while (nexth != IP6_NEXTH_NONE)
+  {
+    switch (nexth) {
+    case IP6_NEXTH_HOPBYHOP:
+      LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Hop-by-Hop options header\n"));
+      /* Get next header type. */
+      nexth = *((u8_t *)p->payload);
+
+      /* Get the header length. */
+      hlen = 8 * (1 + *((u8_t *)p->payload + 1));
+      ip_data.current_ip_header_tot_len += hlen;
+
+      /* Skip over this header. */
+      if (hlen > p->len) {
+        LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+          ("IPv6 options header (hlen %"U16_F") does not fit in first pbuf (len %"U16_F"), IPv6 packet dropped.\n",
+              hlen, p->len));
+        /* free (drop) packet pbufs */
+        pbuf_free(p);
+        IP6_STATS_INC(ip6.lenerr);
+        IP6_STATS_INC(ip6.drop);
+        goto ip6_input_cleanup;
+      }
+
+      pbuf_header(p, -(s16_t)hlen);
+      break;
+    case IP6_NEXTH_DESTOPTS:
+      LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Destination options header\n"));
+      /* Get next header type. */
+      nexth = *((u8_t *)p->payload);
+
+      /* Get the header length. */
+      hlen = 8 * (1 + *((u8_t *)p->payload + 1));
+      ip_data.current_ip_header_tot_len += hlen;
+
+      /* Skip over this header. */
+      if (hlen > p->len) {
+        LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+          ("IPv6 options header (hlen %"U16_F") does not fit in first pbuf (len %"U16_F"), IPv6 packet dropped.\n",
+              hlen, p->len));
+        /* free (drop) packet pbufs */
+        pbuf_free(p);
+        IP6_STATS_INC(ip6.lenerr);
+        IP6_STATS_INC(ip6.drop);
+        goto ip6_input_cleanup;
+      }
+
+      pbuf_header(p, -(s16_t)hlen);
+      break;
+    case IP6_NEXTH_ROUTING:
+      LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Routing header\n"));
+      /* Get next header type. */
+      nexth = *((u8_t *)p->payload);
+
+      /* Get the header length. */
+      hlen = 8 * (1 + *((u8_t *)p->payload + 1));
+      ip_data.current_ip_header_tot_len += hlen;
+
+      /* Skip over this header. */
+      if (hlen > p->len) {
+        LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+          ("IPv6 options header (hlen %"U16_F") does not fit in first pbuf (len %"U16_F"), IPv6 packet dropped.\n",
+              hlen, p->len));
+        /* free (drop) packet pbufs */
+        pbuf_free(p);
+        IP6_STATS_INC(ip6.lenerr);
+        IP6_STATS_INC(ip6.drop);
+        goto ip6_input_cleanup;
+      }
+
+      pbuf_header(p, -(s16_t)hlen);
+      break;
+
+    case IP6_NEXTH_FRAGMENT:
+    {
+      struct ip6_frag_hdr *frag_hdr;
+      LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Fragment header\n"));
+
+      frag_hdr = (struct ip6_frag_hdr *)p->payload;
+
+      /* Get next header type. */
+      nexth = frag_hdr->_nexth;
+
+      /* Fragment Header length. */
+      hlen = 8;
+      ip_data.current_ip_header_tot_len += hlen;
+
+      /* Make sure this header fits in current pbuf. */
+      if (hlen > p->len) {
+        LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+          ("IPv6 options header (hlen %"U16_F") does not fit in first pbuf (len %"U16_F"), IPv6 packet dropped.\n",
+              hlen, p->len));
+        /* free (drop) packet pbufs */
+        pbuf_free(p);
+        IP6_FRAG_STATS_INC(ip6_frag.lenerr);
+        IP6_FRAG_STATS_INC(ip6_frag.drop);
+        goto ip6_input_cleanup;
+      }
+
+      /* Offset == 0 and more_fragments == 0? */
+      if ((frag_hdr->_fragment_offset &
+           PP_HTONS(IP6_FRAG_OFFSET_MASK | IP6_FRAG_MORE_FLAG)) == 0) {
+        /* This is a 1-fragment packet, usually a packet that we have
+         * already reassembled. Skip this header anc continue. */
+        pbuf_header(p, -(s16_t)hlen);
+      } else {
+#if LWIP_IPV6_REASS
+
+        /* reassemble the packet */
+        p = ip6_reass(p);
+        /* packet not fully reassembled yet? */
+        if (p == NULL) {
+          goto ip6_input_cleanup;
+        }
+
+        /* Returned p point to IPv6 header.
+         * Update all our variables and pointers and continue. */
+        ip6hdr = (struct ip6_hdr *)p->payload;
+        nexth = IP6H_NEXTH(ip6hdr);
+        hlen = ip_data.current_ip_header_tot_len = IP6_HLEN;
+        pbuf_header(p, -IP6_HLEN);
+
+#else /* LWIP_IPV6_REASS */
+        /* free (drop) packet pbufs */
+        LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Fragment header dropped (with LWIP_IPV6_REASS==0)\n"));
+        pbuf_free(p);
+        IP6_STATS_INC(ip6.opterr);
+        IP6_STATS_INC(ip6.drop);
+        goto ip6_input_cleanup;
+#endif /* LWIP_IPV6_REASS */
+      }
+      break;
+    }
+    default:
+      goto options_done;
+      break;
+    }
+  }
+options_done:
+
+  /* p points to IPv6 header again. */
+  pbuf_header_force(p, (s16_t)ip_data.current_ip_header_tot_len);
+
+  /* send to upper layers */
+  LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: \n"));
+  ip6_debug_print(p);
+  LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: p->len %"U16_F" p->tot_len %"U16_F"\n", p->len, p->tot_len));
+
+#if LWIP_RAW
+  /* raw input did not eat the packet? */
+  if (raw_input(p, inp) == 0)
+#endif /* LWIP_RAW */
+  {
+    switch (nexth) {
+    case IP6_NEXTH_NONE:
+      pbuf_free(p);
+      break;
+#if LWIP_UDP
+    case IP6_NEXTH_UDP:
+#if LWIP_UDPLITE
+    case IP6_NEXTH_UDPLITE:
+#endif /* LWIP_UDPLITE */
+      /* Point to payload. */
+      pbuf_header(p, -(s16_t)ip_data.current_ip_header_tot_len);
+      udp_input(p, inp);
+      break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+    case IP6_NEXTH_TCP:
+      /* Point to payload. */
+      pbuf_header(p, -(s16_t)ip_data.current_ip_header_tot_len);
+      tcp_input(p, inp);
+      break;
+#endif /* LWIP_TCP */
+#if LWIP_ICMP6
+    case IP6_NEXTH_ICMP6:
+      /* Point to payload. */
+      pbuf_header(p, -(s16_t)ip_data.current_ip_header_tot_len);
+      icmp6_input(p, inp);
+      break;
+#endif /* LWIP_ICMP */
+    default:
+#if LWIP_ICMP6
+      /* send ICMP parameter problem unless it was a multicast or ICMPv6 */
+      if ((!ip6_addr_ismulticast(ip6_current_dest_addr())) &&
+          (IP6H_NEXTH(ip6hdr) != IP6_NEXTH_ICMP6)) {
+        icmp6_param_problem(p, ICMP6_PP_HEADER, ip_data.current_ip_header_tot_len - hlen);
+      }
+#endif /* LWIP_ICMP */
+      LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip6_input: Unsupported transport protocol %"U16_F"\n", (u16_t)IP6H_NEXTH(ip6hdr)));
+      pbuf_free(p);
+      IP6_STATS_INC(ip6.proterr);
+      IP6_STATS_INC(ip6.drop);
+      break;
+    }
+  }
+
+ip6_input_cleanup:
+  ip_data.current_netif = NULL;
+  ip_data.current_input_netif = NULL;
+  ip_data.current_ip6_header = NULL;
+  ip_data.current_ip_header_tot_len = 0;
+  ip6_addr_set_zero(ip6_current_src_addr());
+  ip6_addr_set_zero(ip6_current_dest_addr());
+
+  return ERR_OK;
+}
+
+
+/**
+ * Sends an IPv6 packet on a network interface. This function constructs
+ * the IPv6 header. If the source IPv6 address is NULL, the IPv6 "ANY" address is
+ * used as source (usually during network startup). If the source IPv6 address it
+ * IP6_ADDR_ANY, the most appropriate IPv6 address of the outgoing network
+ * interface is filled in as source address. If the destination IPv6 address is
+ * LWIP_IP_HDRINCL, p is assumed to already include an IPv6 header and
+ * p->payload points to it instead of the data.
+ *
+ * @param p the packet to send (p->payload points to the data, e.g. next
+            protocol header; if dest == LWIP_IP_HDRINCL, p already includes an
+            IPv6 header and p->payload points to that IPv6 header)
+ * @param src the source IPv6 address to send from (if src == IP6_ADDR_ANY, an
+ *         IP address of the netif is selected and used as source address.
+ *         if src == NULL, IP6_ADDR_ANY is used as source)
+ * @param dest the destination IPv6 address to send the packet to
+ * @param hl the Hop Limit value to be set in the IPv6 header
+ * @param tc the Traffic Class value to be set in the IPv6 header
+ * @param nexth the Next Header to be set in the IPv6 header
+ * @param netif the netif on which to send this packet
+ * @return ERR_OK if the packet was sent OK
+ *         ERR_BUF if p doesn't have enough space for IPv6/LINK headers
+ *         returns errors returned by netif->output
+ */
+err_t
+ip6_output_if(struct pbuf *p, const ip6_addr_t *src, const ip6_addr_t *dest,
+             u8_t hl, u8_t tc,
+             u8_t nexth, struct netif *netif)
+{
+  const ip6_addr_t *src_used = src;
+  if (dest != LWIP_IP_HDRINCL) {
+    if (src != NULL && ip6_addr_isany(src)) {
+      src_used = ip_2_ip6(ip6_select_source_address(netif, dest));
+      if ((src_used == NULL) || ip6_addr_isany(src_used)) {
+        /* No appropriate source address was found for this packet. */
+        LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip6_output: No suitable source address for packet.\n"));
+        IP6_STATS_INC(ip6.rterr);
+        return ERR_RTE;
+      }
+    }
+  }
+  return ip6_output_if_src(p, src_used, dest, hl, tc, nexth, netif);
+}
+
+/**
+ * Same as ip6_output_if() but 'src' address is not replaced by netif address
+ * when it is 'any'.
+ */
+err_t
+ip6_output_if_src(struct pbuf *p, const ip6_addr_t *src, const ip6_addr_t *dest,
+             u8_t hl, u8_t tc,
+             u8_t nexth, struct netif *netif)
+{
+  struct ip6_hdr *ip6hdr;
+  ip6_addr_t dest_addr;
+
+  LWIP_IP_CHECK_PBUF_REF_COUNT_FOR_TX(p);
+
+  /* Should the IPv6 header be generated or is it already included in p? */
+  if (dest != LWIP_IP_HDRINCL) {
+    /* generate IPv6 header */
+    if (pbuf_header(p, IP6_HLEN)) {
+      LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip6_output: not enough room for IPv6 header in pbuf\n"));
+      IP6_STATS_INC(ip6.err);
+      return ERR_BUF;
+    }
+
+    ip6hdr = (struct ip6_hdr *)p->payload;
+    LWIP_ASSERT("check that first pbuf can hold struct ip6_hdr",
+               (p->len >= sizeof(struct ip6_hdr)));
+
+    IP6H_HOPLIM_SET(ip6hdr, hl);
+    IP6H_NEXTH_SET(ip6hdr, nexth);
+
+    /* dest cannot be NULL here */
+    ip6_addr_copy(ip6hdr->dest, *dest);
+
+    IP6H_VTCFL_SET(ip6hdr, 6, tc, 0);
+    IP6H_PLEN_SET(ip6hdr, p->tot_len - IP6_HLEN);
+
+    if (src == NULL) {
+      src = IP6_ADDR_ANY6;
+    }
+    /* src cannot be NULL here */
+    ip6_addr_copy(ip6hdr->src, *src);
+
+  } else {
+    /* IP header already included in p */
+    ip6hdr = (struct ip6_hdr *)p->payload;
+    ip6_addr_copy(dest_addr, ip6hdr->dest);
+    dest = &dest_addr;
+  }
+
+  IP6_STATS_INC(ip6.xmit);
+
+  LWIP_DEBUGF(IP6_DEBUG, ("ip6_output_if: %c%c%"U16_F"\n", netif->name[0], netif->name[1], (u16_t)netif->num));
+  ip6_debug_print(p);
+
+#if ENABLE_LOOPBACK
+  {
+    int i;
+#if !LWIP_HAVE_LOOPIF
+    if (ip6_addr_isloopback(dest)) {
+      return netif_loop_output(netif, p);
+    }
+#endif /* !LWIP_HAVE_LOOPIF */
+    for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+      if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) &&
+          ip6_addr_cmp(dest, netif_ip6_addr(netif, i))) {
+        /* Packet to self, enqueue it for loopback */
+        LWIP_DEBUGF(IP6_DEBUG, ("netif_loop_output()\n"));
+        return netif_loop_output(netif, p);
+      }
+    }
+  }
+#endif /* ENABLE_LOOPBACK */
+#if LWIP_IPV6_FRAG
+  /* don't fragment if interface has mtu set to 0 [loopif] */
+  if (netif->mtu && (p->tot_len > nd6_get_destination_mtu(dest, netif))) {
+    return ip6_frag(p, netif, dest);
+  }
+#endif /* LWIP_IPV6_FRAG */
+
+  LWIP_DEBUGF(IP6_DEBUG, ("netif->output_ip6()\n"));
+  return netif->output_ip6(netif, p, dest);
+}
+
+/**
+ * Simple interface to ip6_output_if. It finds the outgoing network
+ * interface and calls upon ip6_output_if to do the actual work.
+ *
+ * @param p the packet to send (p->payload points to the data, e.g. next
+            protocol header; if dest == LWIP_IP_HDRINCL, p already includes an
+            IPv6 header and p->payload points to that IPv6 header)
+ * @param src the source IPv6 address to send from (if src == IP6_ADDR_ANY, an
+ *         IP address of the netif is selected and used as source address.
+ *         if src == NULL, IP6_ADDR_ANY is used as source)
+ * @param dest the destination IPv6 address to send the packet to
+ * @param hl the Hop Limit value to be set in the IPv6 header
+ * @param tc the Traffic Class value to be set in the IPv6 header
+ * @param nexth the Next Header to be set in the IPv6 header
+ *
+ * @return ERR_RTE if no route is found
+ *         see ip_output_if() for more return values
+ */
+err_t
+ip6_output(struct pbuf *p, const ip6_addr_t *src, const ip6_addr_t *dest,
+          u8_t hl, u8_t tc, u8_t nexth)
+{
+  struct netif *netif;
+  struct ip6_hdr *ip6hdr;
+  ip6_addr_t src_addr, dest_addr;
+
+  LWIP_IP_CHECK_PBUF_REF_COUNT_FOR_TX(p);
+
+  if (dest != LWIP_IP_HDRINCL) {
+    netif = ip6_route(src, dest);
+  } else {
+    /* IP header included in p, read addresses. */
+    ip6hdr = (struct ip6_hdr *)p->payload;
+    ip6_addr_copy(src_addr, ip6hdr->src);
+    ip6_addr_copy(dest_addr, ip6hdr->dest);
+    netif = ip6_route(&src_addr, &dest_addr);
+  }
+
+  if (netif == NULL) {
+    LWIP_DEBUGF(IP6_DEBUG, ("ip6_output: no route for %"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F"\n",
+        IP6_ADDR_BLOCK1(dest),
+        IP6_ADDR_BLOCK2(dest),
+        IP6_ADDR_BLOCK3(dest),
+        IP6_ADDR_BLOCK4(dest),
+        IP6_ADDR_BLOCK5(dest),
+        IP6_ADDR_BLOCK6(dest),
+        IP6_ADDR_BLOCK7(dest),
+        IP6_ADDR_BLOCK8(dest)));
+    IP6_STATS_INC(ip6.rterr);
+    return ERR_RTE;
+  }
+
+  return ip6_output_if(p, src, dest, hl, tc, nexth, netif);
+}
+
+
+#if LWIP_NETIF_HWADDRHINT
+/** Like ip6_output, but takes and addr_hint pointer that is passed on to netif->addr_hint
+ *  before calling ip6_output_if.
+ *
+ * @param p the packet to send (p->payload points to the data, e.g. next
+            protocol header; if dest == LWIP_IP_HDRINCL, p already includes an
+            IPv6 header and p->payload points to that IPv6 header)
+ * @param src the source IPv6 address to send from (if src == IP6_ADDR_ANY, an
+ *         IP address of the netif is selected and used as source address.
+ *         if src == NULL, IP6_ADDR_ANY is used as source)
+ * @param dest the destination IPv6 address to send the packet to
+ * @param hl the Hop Limit value to be set in the IPv6 header
+ * @param tc the Traffic Class value to be set in the IPv6 header
+ * @param nexth the Next Header to be set in the IPv6 header
+ * @param addr_hint address hint pointer set to netif->addr_hint before
+ *        calling ip_output_if()
+ *
+ * @return ERR_RTE if no route is found
+ *         see ip_output_if() for more return values
+ */
+err_t
+ip6_output_hinted(struct pbuf *p, const ip6_addr_t *src, const ip6_addr_t *dest,
+          u8_t hl, u8_t tc, u8_t nexth, u8_t *addr_hint)
+{
+  struct netif *netif;
+  struct ip6_hdr *ip6hdr;
+  ip6_addr_t src_addr, dest_addr;
+  err_t err;
+
+  LWIP_IP_CHECK_PBUF_REF_COUNT_FOR_TX(p);
+
+  if (dest != LWIP_IP_HDRINCL) {
+    netif = ip6_route(src, dest);
+  } else {
+    /* IP header included in p, read addresses. */
+    ip6hdr = (struct ip6_hdr *)p->payload;
+    ip6_addr_copy(src_addr, ip6hdr->src);
+    ip6_addr_copy(dest_addr, ip6hdr->dest);
+    netif = ip6_route(&src_addr, &dest_addr);
+  }
+
+  if (netif == NULL) {
+    LWIP_DEBUGF(IP6_DEBUG, ("ip6_output: no route for %"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F"\n",
+        IP6_ADDR_BLOCK1(dest),
+        IP6_ADDR_BLOCK2(dest),
+        IP6_ADDR_BLOCK3(dest),
+        IP6_ADDR_BLOCK4(dest),
+        IP6_ADDR_BLOCK5(dest),
+        IP6_ADDR_BLOCK6(dest),
+        IP6_ADDR_BLOCK7(dest),
+        IP6_ADDR_BLOCK8(dest)));
+    IP6_STATS_INC(ip6.rterr);
+    return ERR_RTE;
+  }
+
+  NETIF_SET_HWADDRHINT(netif, addr_hint);
+  err = ip6_output_if(p, src, dest, hl, tc, nexth, netif);
+  NETIF_SET_HWADDRHINT(netif, NULL);
+
+  return err;
+}
+#endif /* LWIP_NETIF_HWADDRHINT*/
+
+#if LWIP_IPV6_MLD
+/**
+ * Add a hop-by-hop options header with a router alert option and padding.
+ *
+ * Used by MLD when sending a Multicast listener report/done message.
+ *
+ * @param p the packet to which we will prepend the options header
+ * @param nexth the next header protocol number (e.g. IP6_NEXTH_ICMP6)
+ * @param value the value of the router alert option data (e.g. IP6_ROUTER_ALERT_VALUE_MLD)
+ * @return ERR_OK if hop-by-hop header was added, ERR_* otherwise
+ */
+err_t
+ip6_options_add_hbh_ra(struct pbuf *p, u8_t nexth, u8_t value)
+{
+  struct ip6_hbh_hdr *hbh_hdr;
+
+  /* Move pointer to make room for hop-by-hop options header. */
+  if (pbuf_header(p, sizeof(struct ip6_hbh_hdr))) {
+    LWIP_DEBUGF(IP6_DEBUG, ("ip6_options: no space for options header\n"));
+    IP6_STATS_INC(ip6.err);
+    return ERR_BUF;
+  }
+
+  hbh_hdr = (struct ip6_hbh_hdr *)p->payload;
+
+  /* Set fields. */
+  hbh_hdr->_nexth = nexth;
+  hbh_hdr->_hlen = 0;
+  hbh_hdr->_ra_opt_type = IP6_ROUTER_ALERT_OPTION;
+  hbh_hdr->_ra_opt_dlen = 2;
+  hbh_hdr->_ra_opt_data = value;
+  hbh_hdr->_padn_opt_type = IP6_PADN_ALERT_OPTION;
+  hbh_hdr->_padn_opt_dlen = 0;
+
+  return ERR_OK;
+}
+#endif /* LWIP_IPV6_MLD */
+
+#if IP6_DEBUG
+/* Print an IPv6 header by using LWIP_DEBUGF
+ * @param p an IPv6 packet, p->payload pointing to the IPv6 header
+ */
+void
+ip6_debug_print(struct pbuf *p)
+{
+  struct ip6_hdr *ip6hdr = (struct ip6_hdr *)p->payload;
+
+  LWIP_DEBUGF(IP6_DEBUG, ("IPv6 header:\n"));
+  LWIP_DEBUGF(IP6_DEBUG, ("+-------------------------------+\n"));
+  LWIP_DEBUGF(IP6_DEBUG, ("| %2"U16_F" |  %3"U16_F"  |      %7"U32_F"     | (ver, class, flow)\n",
+                    IP6H_V(ip6hdr),
+                    IP6H_TC(ip6hdr),
+                    IP6H_FL(ip6hdr)));
+  LWIP_DEBUGF(IP6_DEBUG, ("+-------------------------------+\n"));
+  LWIP_DEBUGF(IP6_DEBUG, ("|     %5"U16_F"     |  %3"U16_F"  |  %3"U16_F"  | (plen, nexth, hopl)\n",
+                    IP6H_PLEN(ip6hdr),
+                    IP6H_NEXTH(ip6hdr),
+                    IP6H_HOPLIM(ip6hdr)));
+  LWIP_DEBUGF(IP6_DEBUG, ("+-------------------------------+\n"));
+  LWIP_DEBUGF(IP6_DEBUG, ("|  %4"X32_F" |  %4"X32_F" |  %4"X32_F" |  %4"X32_F" | (src)\n",
+                    IP6_ADDR_BLOCK1(&(ip6hdr->src)),
+                    IP6_ADDR_BLOCK2(&(ip6hdr->src)),
+                    IP6_ADDR_BLOCK3(&(ip6hdr->src)),
+                    IP6_ADDR_BLOCK4(&(ip6hdr->src))));
+  LWIP_DEBUGF(IP6_DEBUG, ("|  %4"X32_F" |  %4"X32_F" |  %4"X32_F" |  %4"X32_F" |\n",
+                    IP6_ADDR_BLOCK5(&(ip6hdr->src)),
+                    IP6_ADDR_BLOCK6(&(ip6hdr->src)),
+                    IP6_ADDR_BLOCK7(&(ip6hdr->src)),
+                    IP6_ADDR_BLOCK8(&(ip6hdr->src))));
+  LWIP_DEBUGF(IP6_DEBUG, ("+-------------------------------+\n"));
+  LWIP_DEBUGF(IP6_DEBUG, ("|  %4"X32_F" |  %4"X32_F" |  %4"X32_F" |  %4"X32_F" | (dest)\n",
+                    IP6_ADDR_BLOCK1(&(ip6hdr->dest)),
+                    IP6_ADDR_BLOCK2(&(ip6hdr->dest)),
+                    IP6_ADDR_BLOCK3(&(ip6hdr->dest)),
+                    IP6_ADDR_BLOCK4(&(ip6hdr->dest))));
+  LWIP_DEBUGF(IP6_DEBUG, ("|  %4"X32_F" |  %4"X32_F" |  %4"X32_F" |  %4"X32_F" |\n",
+                    IP6_ADDR_BLOCK5(&(ip6hdr->dest)),
+                    IP6_ADDR_BLOCK6(&(ip6hdr->dest)),
+                    IP6_ADDR_BLOCK7(&(ip6hdr->dest)),
+                    IP6_ADDR_BLOCK8(&(ip6hdr->dest))));
+  LWIP_DEBUGF(IP6_DEBUG, ("+-------------------------------+\n"));
+}
+#endif /* IP6_DEBUG */
+
+#endif /* LWIP_IPV6 */

+ 292 - 72
thirdparty/lwip/src/core/ipv6/ip6_addr.c

@@ -1,72 +1,292 @@
-/*
- * 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/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)
-{
-  SMEMCPY(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);
-}
+/**
+ * @file
+ *
+ * IPv6 addresses.
+ */
+
+/*
+ * Copyright (c) 2010 Inico Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Ivan Delamer <delamer@inicotech.com>
+ *
+ * Functions for handling IPv6 addresses.
+ *
+ * Please coordinate changes and requests with Ivan Delamer
+ * <delamer@inicotech.com>
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV6  /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/ip_addr.h"
+#include "lwip/def.h"
+
+/* used by IP6_ADDR_ANY(6) in ip6_addr.h */
+const ip_addr_t ip6_addr_any = IPADDR6_INIT(0ul, 0ul, 0ul, 0ul);
+
+#ifndef isprint
+#define in_range(c, lo, up)  ((u8_t)c >= lo && (u8_t)c <= up)
+#define isprint(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')
+#define xchar(i)             ((i) < 10 ? '0' + (i) : 'A' + (i) - 10)
+#endif
+
+/**
+ * Check whether "cp" is a valid ascii representation
+ * of an IPv6 address and convert to a binary address.
+ * Returns 1 if the address is valid, 0 if not.
+ *
+ * @param cp IPv6 address in ascii representation (e.g. "FF01::1")
+ * @param addr pointer to which to save the ip address in network order
+ * @return 1 if cp could be converted to addr, 0 on failure
+ */
+int
+ip6addr_aton(const char *cp, ip6_addr_t *addr)
+{
+  u32_t addr_index, zero_blocks, current_block_index, current_block_value;
+  const char *s;
+
+  /* Count the number of colons, to count the number of blocks in a "::" sequence
+     zero_blocks may be 1 even if there are no :: sequences */
+  zero_blocks = 8;
+  for (s = cp; *s != 0; s++) {
+    if (*s == ':') {
+      zero_blocks--;
+    } else if (!isxdigit(*s)) {
+      break;
+    }
+  }
+
+  /* parse each block */
+  addr_index = 0;
+  current_block_index = 0;
+  current_block_value = 0;
+  for (s = cp; *s != 0; s++) {
+    if (*s == ':') {
+      if (addr) {
+        if (current_block_index & 0x1) {
+          addr->addr[addr_index++] |= current_block_value;
+        }
+        else {
+          addr->addr[addr_index] = current_block_value << 16;
+        }
+      }
+      current_block_index++;
+      current_block_value = 0;
+      if (current_block_index > 7) {
+        /* address too long! */
+        return 0;
+      }
+      if (s[1] == ':') {
+        if (s[2] == ':') {
+          /* invalid format: three successive colons */
+          return 0;
+        }
+        s++;
+        /* "::" found, set zeros */
+        while (zero_blocks > 0) {
+          zero_blocks--;
+          if (current_block_index & 0x1) {
+            addr_index++;
+          } else {
+            if (addr) {
+              addr->addr[addr_index] = 0;
+            }
+          }
+          current_block_index++;
+          if (current_block_index > 7) {
+            /* address too long! */
+            return 0;
+          }
+        }
+      }
+    } else if (isxdigit(*s)) {
+      /* add current digit */
+      current_block_value = (current_block_value << 4) +
+          (isdigit(*s) ? (u32_t)(*s - '0') :
+          (u32_t)(10 + (islower(*s) ? *s - 'a' : *s - 'A')));
+    } else {
+      /* unexpected digit, space? CRLF? */
+      break;
+    }
+  }
+
+  if (addr) {
+    if (current_block_index & 0x1) {
+      addr->addr[addr_index++] |= current_block_value;
+    }
+    else {
+      addr->addr[addr_index] = current_block_value << 16;
+    }
+  }
+
+  /* convert to network byte order. */
+  if (addr) {
+    for (addr_index = 0; addr_index < 4; addr_index++) {
+      addr->addr[addr_index] = lwip_htonl(addr->addr[addr_index]);
+    }
+  }
+
+  if (current_block_index != 7) {
+    return 0;
+  }
+
+  return 1;
+}
+
+/**
+ * Convert numeric IPv6 address into ASCII representation.
+ * returns ptr to static buffer; not reentrant!
+ *
+ * @param addr ip6 address in network order to convert
+ * @return pointer to a global static (!) buffer that holds the ASCII
+ *         representation of addr
+ */
+char *
+ip6addr_ntoa(const ip6_addr_t *addr)
+{
+  static char str[40];
+  return ip6addr_ntoa_r(addr, str, 40);
+}
+
+/**
+ * Same as ipaddr_ntoa, but reentrant since a user-supplied buffer is used.
+ *
+ * @param addr ip6 address in network order to convert
+ * @param buf target buffer where the string is stored
+ * @param buflen length of buf
+ * @return either pointer to buf which now holds the ASCII
+ *         representation of addr or NULL if buf was too small
+ */
+char *
+ip6addr_ntoa_r(const ip6_addr_t *addr, char *buf, int buflen)
+{
+  u32_t current_block_index, current_block_value, next_block_value;
+  s32_t i;
+  u8_t zero_flag, empty_block_flag;
+
+  i = 0;
+  empty_block_flag = 0; /* used to indicate a zero chain for "::' */
+
+  for (current_block_index = 0; current_block_index < 8; current_block_index++) {
+    /* get the current 16-bit block */
+    current_block_value = lwip_htonl(addr->addr[current_block_index >> 1]);
+    if ((current_block_index & 0x1) == 0) {
+      current_block_value = current_block_value >> 16;
+    }
+    current_block_value &= 0xffff;
+
+    /* Check for empty block. */
+    if (current_block_value == 0) {
+      if (current_block_index == 7 && empty_block_flag == 1) {
+        /* special case, we must render a ':' for the last block. */
+        buf[i++] = ':';
+        if (i >= buflen) {
+          return NULL;
+        }
+        break;
+      }
+      if (empty_block_flag == 0) {
+        /* generate empty block "::", but only if more than one contiguous zero block,
+         * according to current formatting suggestions RFC 5952. */
+        next_block_value = lwip_htonl(addr->addr[(current_block_index + 1) >> 1]);
+        if ((current_block_index & 0x1) == 0x01) {
+            next_block_value = next_block_value >> 16;
+        }
+        next_block_value &= 0xffff;
+        if (next_block_value == 0) {
+          empty_block_flag = 1;
+          buf[i++] = ':';
+          if (i >= buflen) {
+            return NULL;
+          }
+          continue; /* move on to next block. */
+        }
+      } else if (empty_block_flag == 1) {
+        /* move on to next block. */
+        continue;
+      }
+    } else if (empty_block_flag == 1) {
+      /* Set this flag value so we don't produce multiple empty blocks. */
+      empty_block_flag = 2;
+    }
+
+    if (current_block_index > 0) {
+      buf[i++] = ':';
+      if (i >= buflen) {
+        return NULL;
+      }
+    }
+
+    if ((current_block_value & 0xf000) == 0) {
+      zero_flag = 1;
+    } else {
+      buf[i++] = xchar(((current_block_value & 0xf000) >> 12));
+      zero_flag = 0;
+      if (i >= buflen) {
+        return NULL;
+      }
+    }
+
+    if (((current_block_value & 0xf00) == 0) && (zero_flag)) {
+      /* do nothing */
+    } else {
+      buf[i++] = xchar(((current_block_value & 0xf00) >> 8));
+      zero_flag = 0;
+      if (i >= buflen) {
+        return NULL;
+      }
+    }
+
+    if (((current_block_value & 0xf0) == 0) && (zero_flag)) {
+      /* do nothing */
+    }
+    else {
+      buf[i++] = xchar(((current_block_value & 0xf0) >> 4));
+      zero_flag = 0;
+      if (i >= buflen) {
+        return NULL;
+      }
+    }
+
+    buf[i++] = xchar((current_block_value & 0xf));
+    if (i >= buflen) {
+      return NULL;
+    }
+  }
+
+  buf[i] = 0;
+
+  return buf;
+}
+
+#endif /* LWIP_IPV6 */

+ 805 - 0
thirdparty/lwip/src/core/ipv6/ip6_frag.c

@@ -0,0 +1,805 @@
+/**
+ * @file
+ *
+ * IPv6 fragmentation and reassembly.
+ */
+
+/*
+ * Copyright (c) 2010 Inico Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Ivan Delamer <delamer@inicotech.com>
+ *
+ *
+ * Please coordinate changes and requests with Ivan Delamer
+ * <delamer@inicotech.com>
+ */
+
+#include "lwip/opt.h"
+#include "lwip/ip6_frag.h"
+#include "lwip/ip6.h"
+#include "lwip/icmp6.h"
+#include "lwip/nd6.h"
+#include "lwip/ip.h"
+
+#include "lwip/pbuf.h"
+#include "lwip/memp.h"
+#include "lwip/stats.h"
+
+#include <string.h>
+
+#if LWIP_IPV6 && LWIP_IPV6_REASS  /* don't build if not configured for use in lwipopts.h */
+
+
+/** Setting this to 0, you can turn off checking the fragments for overlapping
+ * regions. The code gets a little smaller. Only use this if you know that
+ * overlapping won't occur on your network! */
+#ifndef IP_REASS_CHECK_OVERLAP
+#define IP_REASS_CHECK_OVERLAP 1
+#endif /* IP_REASS_CHECK_OVERLAP */
+
+/** Set to 0 to prevent freeing the oldest datagram when the reassembly buffer is
+ * full (IP_REASS_MAX_PBUFS pbufs are enqueued). The code gets a little smaller.
+ * Datagrams will be freed by timeout only. Especially useful when MEMP_NUM_REASSDATA
+ * is set to 1, so one datagram can be reassembled at a time, only. */
+#ifndef IP_REASS_FREE_OLDEST
+#define IP_REASS_FREE_OLDEST 1
+#endif /* IP_REASS_FREE_OLDEST */
+
+#if IPV6_FRAG_COPYHEADER
+#define IPV6_FRAG_REQROOM ((s16_t)(sizeof(struct ip6_reass_helper) - IP6_FRAG_HLEN))
+#endif
+
+#define IP_REASS_FLAG_LASTFRAG 0x01
+
+/** This is a helper struct which holds the starting
+ * offset and the ending offset of this fragment to
+ * easily chain the fragments.
+ * It has the same packing requirements as the IPv6 header, since it replaces
+ * the Fragment Header in memory in incoming fragments to keep
+ * track of the various fragments.
+ */
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct ip6_reass_helper {
+  PACK_STRUCT_FIELD(struct pbuf *next_pbuf);
+  PACK_STRUCT_FIELD(u16_t start);
+  PACK_STRUCT_FIELD(u16_t end);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
+
+/* static variables */
+static struct ip6_reassdata *reassdatagrams;
+static u16_t ip6_reass_pbufcount;
+
+/* Forward declarations. */
+static void ip6_reass_free_complete_datagram(struct ip6_reassdata *ipr);
+#if IP_REASS_FREE_OLDEST
+static void ip6_reass_remove_oldest_datagram(struct ip6_reassdata *ipr, int pbufs_needed);
+#endif /* IP_REASS_FREE_OLDEST */
+
+void
+ip6_reass_tmr(void)
+{
+  struct ip6_reassdata *r, *tmp;
+
+#if !IPV6_FRAG_COPYHEADER
+  LWIP_ASSERT("sizeof(struct ip6_reass_helper) <= IP6_FRAG_HLEN, set IPV6_FRAG_COPYHEADER to 1",
+    sizeof(struct ip6_reass_helper) <= IP6_FRAG_HLEN);
+#endif /* !IPV6_FRAG_COPYHEADER */
+
+  r = reassdatagrams;
+  while (r != NULL) {
+    /* Decrement the timer. Once it reaches 0,
+     * clean up the incomplete fragment assembly */
+    if (r->timer > 0) {
+      r->timer--;
+      r = r->next;
+    } else {
+      /* reassembly timed out */
+      tmp = r;
+      /* get the next pointer before freeing */
+      r = r->next;
+      /* free the helper struct and all enqueued pbufs */
+      ip6_reass_free_complete_datagram(tmp);
+     }
+   }
+}
+
+/**
+ * Free a datagram (struct ip6_reassdata) and all its pbufs.
+ * Updates the total count of enqueued pbufs (ip6_reass_pbufcount),
+ * sends an ICMP time exceeded packet.
+ *
+ * @param ipr datagram to free
+ */
+static void
+ip6_reass_free_complete_datagram(struct ip6_reassdata *ipr)
+{
+  struct ip6_reassdata *prev;
+  u16_t pbufs_freed = 0;
+  u16_t clen;
+  struct pbuf *p;
+  struct ip6_reass_helper *iprh;
+
+#if LWIP_ICMP6
+  iprh = (struct ip6_reass_helper *)ipr->p->payload;
+  if (iprh->start == 0) {
+    /* The first fragment was received, send ICMP time exceeded. */
+    /* First, de-queue the first pbuf from r->p. */
+    p = ipr->p;
+    ipr->p = iprh->next_pbuf;
+    /* Then, move back to the original ipv6 header (we are now pointing to Fragment header).
+       This cannot fail since we already checked when receiving this fragment. */
+    if (pbuf_header_force(p, (s16_t)((u8_t*)p->payload - (u8_t*)IPV6_FRAG_HDRREF(ipr->iphdr)))) {
+      LWIP_ASSERT("ip6_reass_free: moving p->payload to ip6 header failed\n", 0);
+    }
+    else {
+      icmp6_time_exceeded(p, ICMP6_TE_FRAG);
+    }
+    clen = pbuf_clen(p);
+    LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff);
+    pbufs_freed += clen;
+    pbuf_free(p);
+  }
+#endif /* LWIP_ICMP6 */
+
+  /* First, free all received pbufs.  The individual pbufs need to be released
+     separately as they have not yet been chained */
+  p = ipr->p;
+  while (p != NULL) {
+    struct pbuf *pcur;
+    iprh = (struct ip6_reass_helper *)p->payload;
+    pcur = p;
+    /* get the next pointer before freeing */
+    p = iprh->next_pbuf;
+    clen = pbuf_clen(pcur);
+    LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff);
+    pbufs_freed += clen;
+    pbuf_free(pcur);
+  }
+
+  /* Then, unchain the struct ip6_reassdata from the list and free it. */
+  if (ipr == reassdatagrams) {
+    reassdatagrams = ipr->next;
+  } else {
+    prev = reassdatagrams;
+    while (prev != NULL) {
+      if (prev->next == ipr) {
+        break;
+      }
+      prev = prev->next;
+    }
+    if (prev != NULL) {
+      prev->next = ipr->next;
+    }
+  }
+  memp_free(MEMP_IP6_REASSDATA, ipr);
+
+  /* Finally, update number of pbufs in reassembly queue */
+  LWIP_ASSERT("ip_reass_pbufcount >= clen", ip6_reass_pbufcount >= pbufs_freed);
+  ip6_reass_pbufcount -= pbufs_freed;
+}
+
+#if IP_REASS_FREE_OLDEST
+/**
+ * Free the oldest datagram to make room for enqueueing new fragments.
+ * The datagram ipr is not freed!
+ *
+ * @param ipr ip6_reassdata for the current fragment
+ * @param pbufs_needed number of pbufs needed to enqueue
+ *        (used for freeing other datagrams if not enough space)
+ */
+static void
+ip6_reass_remove_oldest_datagram(struct ip6_reassdata *ipr, int pbufs_needed)
+{
+  struct ip6_reassdata *r, *oldest;
+
+  /* Free datagrams until being allowed to enqueue 'pbufs_needed' pbufs,
+   * but don't free the current datagram! */
+  do {
+    r = oldest = reassdatagrams;
+    while (r != NULL) {
+      if (r != ipr) {
+        if (r->timer <= oldest->timer) {
+          /* older than the previous oldest */
+          oldest = r;
+        }
+      }
+      r = r->next;
+    }
+    if (oldest == ipr) {
+      /* nothing to free, ipr is the only element on the list */
+      return;
+    }
+    if (oldest != NULL) {
+      ip6_reass_free_complete_datagram(oldest);
+    }
+  } while (((ip6_reass_pbufcount + pbufs_needed) > IP_REASS_MAX_PBUFS) && (reassdatagrams != NULL));
+}
+#endif /* IP_REASS_FREE_OLDEST */
+
+/**
+ * Reassembles incoming IPv6 fragments into an IPv6 datagram.
+ *
+ * @param p points to the IPv6 Fragment Header
+ * @return NULL if reassembly is incomplete, pbuf pointing to
+ *         IPv6 Header if reassembly is complete
+ */
+struct pbuf *
+ip6_reass(struct pbuf *p)
+{
+  struct ip6_reassdata *ipr, *ipr_prev;
+  struct ip6_reass_helper *iprh, *iprh_tmp, *iprh_prev=NULL;
+  struct ip6_frag_hdr *frag_hdr;
+  u16_t offset, len;
+  u16_t clen;
+  u8_t valid = 1;
+  struct pbuf *q;
+
+  IP6_FRAG_STATS_INC(ip6_frag.recv);
+
+  if ((const void*)ip6_current_header() != ((u8_t*)p->payload) - IP6_HLEN) {
+    /* ip6_frag_hdr must be in the first pbuf, not chained */
+    IP6_FRAG_STATS_INC(ip6_frag.proterr);
+    IP6_FRAG_STATS_INC(ip6_frag.drop);
+    goto nullreturn;
+  }
+
+  frag_hdr = (struct ip6_frag_hdr *) p->payload;
+
+  clen = pbuf_clen(p);
+
+  offset = lwip_ntohs(frag_hdr->_fragment_offset);
+
+  /* Calculate fragment length from IPv6 payload length.
+   * Adjust for headers before Fragment Header.
+   * And finally adjust by Fragment Header length. */
+  len = lwip_ntohs(ip6_current_header()->_plen);
+  len -= (u16_t)(((u8_t*)p->payload - (const u8_t*)ip6_current_header()) - IP6_HLEN);
+  len -= IP6_FRAG_HLEN;
+
+  /* Look for the datagram the fragment belongs to in the current datagram queue,
+   * remembering the previous in the queue for later dequeueing. */
+  for (ipr = reassdatagrams, ipr_prev = NULL; ipr != NULL; ipr = ipr->next) {
+    /* Check if the incoming fragment matches the one currently present
+       in the reassembly buffer. If so, we proceed with copying the
+       fragment into the buffer. */
+    if ((frag_hdr->_identification == ipr->identification) &&
+        ip6_addr_cmp(ip6_current_src_addr(), &(IPV6_FRAG_HDRREF(ipr->iphdr)->src)) &&
+        ip6_addr_cmp(ip6_current_dest_addr(), &(IPV6_FRAG_HDRREF(ipr->iphdr)->dest))) {
+      IP6_FRAG_STATS_INC(ip6_frag.cachehit);
+      break;
+    }
+    ipr_prev = ipr;
+  }
+
+  if (ipr == NULL) {
+  /* Enqueue a new datagram into the datagram queue */
+    ipr = (struct ip6_reassdata *)memp_malloc(MEMP_IP6_REASSDATA);
+    if (ipr == NULL) {
+#if IP_REASS_FREE_OLDEST
+      /* Make room and try again. */
+      ip6_reass_remove_oldest_datagram(ipr, clen);
+      ipr = (struct ip6_reassdata *)memp_malloc(MEMP_IP6_REASSDATA);
+      if (ipr != NULL) {
+        /* re-search ipr_prev since it might have been removed */
+        for (ipr_prev = reassdatagrams; ipr_prev != NULL; ipr_prev = ipr_prev->next) {
+          if (ipr_prev->next == ipr) {
+            break;
+          }
+        }
+      } else
+#endif /* IP_REASS_FREE_OLDEST */
+      {
+        IP6_FRAG_STATS_INC(ip6_frag.memerr);
+        IP6_FRAG_STATS_INC(ip6_frag.drop);
+        goto nullreturn;
+      }
+    }
+
+    memset(ipr, 0, sizeof(struct ip6_reassdata));
+    ipr->timer = IP_REASS_MAXAGE;
+
+    /* enqueue the new structure to the front of the list */
+    ipr->next = reassdatagrams;
+    reassdatagrams = ipr;
+
+    /* Use the current IPv6 header for src/dest address reference.
+     * Eventually, we will replace it when we get the first fragment
+     * (it might be this one, in any case, it is done later). */
+#if IPV6_FRAG_COPYHEADER
+    MEMCPY(&ipr->iphdr, ip6_current_header(), IP6_HLEN);
+#else /* IPV6_FRAG_COPYHEADER */
+    /* need to use the none-const pointer here: */
+    ipr->iphdr = ip_data.current_ip6_header;
+#endif /* IPV6_FRAG_COPYHEADER */
+
+    /* copy the fragmented packet id. */
+    ipr->identification = frag_hdr->_identification;
+
+    /* copy the nexth field */
+    ipr->nexth = frag_hdr->_nexth;
+  }
+
+  /* Check if we are allowed to enqueue more datagrams. */
+  if ((ip6_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS) {
+#if IP_REASS_FREE_OLDEST
+    ip6_reass_remove_oldest_datagram(ipr, clen);
+    if ((ip6_reass_pbufcount + clen) <= IP_REASS_MAX_PBUFS) {
+      /* re-search ipr_prev since it might have been removed */
+      for (ipr_prev = reassdatagrams; ipr_prev != NULL; ipr_prev = ipr_prev->next) {
+        if (ipr_prev->next == ipr) {
+          break;
+        }
+      }
+    } else
+#endif /* IP_REASS_FREE_OLDEST */
+    {
+      /* @todo: send ICMPv6 time exceeded here? */
+      /* drop this pbuf */
+      IP6_FRAG_STATS_INC(ip6_frag.memerr);
+      IP6_FRAG_STATS_INC(ip6_frag.drop);
+      goto nullreturn;
+    }
+  }
+
+  /* Overwrite Fragment Header with our own helper struct. */
+#if IPV6_FRAG_COPYHEADER
+  if (IPV6_FRAG_REQROOM > 0) {
+    /* Make room for struct ip6_reass_helper (only required if sizeof(void*) > 4).
+       This cannot fail since we already checked when receiving this fragment. */
+    u8_t hdrerr = pbuf_header_force(p, IPV6_FRAG_REQROOM);
+    LWIP_UNUSED_ARG(hdrerr); /* in case of LWIP_NOASSERT */
+    LWIP_ASSERT("no room for struct ip6_reass_helper", hdrerr == 0);
+  }
+#else /* IPV6_FRAG_COPYHEADER */
+  LWIP_ASSERT("sizeof(struct ip6_reass_helper) <= IP6_FRAG_HLEN, set IPV6_FRAG_COPYHEADER to 1",
+    sizeof(struct ip6_reass_helper) <= IP6_FRAG_HLEN);
+#endif /* IPV6_FRAG_COPYHEADER */
+  iprh = (struct ip6_reass_helper *)p->payload;
+  iprh->next_pbuf = NULL;
+  iprh->start = (offset & IP6_FRAG_OFFSET_MASK);
+  iprh->end = (offset & IP6_FRAG_OFFSET_MASK) + len;
+
+  /* find the right place to insert this pbuf */
+  /* Iterate through until we either get to the end of the list (append),
+   * or we find on with a larger offset (insert). */
+  for (q = ipr->p; q != NULL;) {
+    iprh_tmp = (struct ip6_reass_helper*)q->payload;
+    if (iprh->start < iprh_tmp->start) {
+#if IP_REASS_CHECK_OVERLAP
+      if (iprh->end > iprh_tmp->start) {
+        /* fragment overlaps with following, throw away */
+        IP6_FRAG_STATS_INC(ip6_frag.proterr);
+        IP6_FRAG_STATS_INC(ip6_frag.drop);
+        goto nullreturn;
+      }
+      if (iprh_prev != NULL) {
+        if (iprh->start < iprh_prev->end) {
+          /* fragment overlaps with previous, throw away */
+          IP6_FRAG_STATS_INC(ip6_frag.proterr);
+          IP6_FRAG_STATS_INC(ip6_frag.drop);
+          goto nullreturn;
+        }
+      }
+#endif /* IP_REASS_CHECK_OVERLAP */
+      /* the new pbuf should be inserted before this */
+      iprh->next_pbuf = q;
+      if (iprh_prev != NULL) {
+        /* not the fragment with the lowest offset */
+        iprh_prev->next_pbuf = p;
+      } else {
+        /* fragment with the lowest offset */
+        ipr->p = p;
+      }
+      break;
+    } else if (iprh->start == iprh_tmp->start) {
+      /* received the same datagram twice: no need to keep the datagram */
+      IP6_FRAG_STATS_INC(ip6_frag.drop);
+      goto nullreturn;
+#if IP_REASS_CHECK_OVERLAP
+    } else if (iprh->start < iprh_tmp->end) {
+      /* overlap: no need to keep the new datagram */
+      IP6_FRAG_STATS_INC(ip6_frag.proterr);
+      IP6_FRAG_STATS_INC(ip6_frag.drop);
+      goto nullreturn;
+#endif /* IP_REASS_CHECK_OVERLAP */
+    } else {
+      /* Check if the fragments received so far have no gaps. */
+      if (iprh_prev != NULL) {
+        if (iprh_prev->end != iprh_tmp->start) {
+          /* There is a fragment missing between the current
+           * and the previous fragment */
+          valid = 0;
+        }
+      }
+    }
+    q = iprh_tmp->next_pbuf;
+    iprh_prev = iprh_tmp;
+  }
+
+  /* If q is NULL, then we made it to the end of the list. Determine what to do now */
+  if (q == NULL) {
+    if (iprh_prev != NULL) {
+      /* this is (for now), the fragment with the highest offset:
+       * chain it to the last fragment */
+#if IP_REASS_CHECK_OVERLAP
+      LWIP_ASSERT("check fragments don't overlap", iprh_prev->end <= iprh->start);
+#endif /* IP_REASS_CHECK_OVERLAP */
+      iprh_prev->next_pbuf = p;
+      if (iprh_prev->end != iprh->start) {
+        valid = 0;
+      }
+    } else {
+#if IP_REASS_CHECK_OVERLAP
+      LWIP_ASSERT("no previous fragment, this must be the first fragment!",
+        ipr->p == NULL);
+#endif /* IP_REASS_CHECK_OVERLAP */
+      /* this is the first fragment we ever received for this ip datagram */
+      ipr->p = p;
+    }
+  }
+
+  /* Track the current number of pbufs current 'in-flight', in order to limit
+  the number of fragments that may be enqueued at any one time */
+  ip6_reass_pbufcount += clen;
+
+  /* Remember IPv6 header if this is the first fragment. */
+  if (iprh->start == 0) {
+#if IPV6_FRAG_COPYHEADER
+    if (iprh->next_pbuf != NULL) {
+      MEMCPY(&ipr->iphdr, ip6_current_header(), IP6_HLEN);
+    }
+#else /* IPV6_FRAG_COPYHEADER */
+    /* need to use the none-const pointer here: */
+    ipr->iphdr = ip_data.current_ip6_header;
+#endif /* IPV6_FRAG_COPYHEADER */
+  }
+
+  /* If this is the last fragment, calculate total packet length. */
+  if ((offset & IP6_FRAG_MORE_FLAG) == 0) {
+    ipr->datagram_len = iprh->end;
+  }
+
+  /* Additional validity tests: we have received first and last fragment. */
+  iprh_tmp = (struct ip6_reass_helper*)ipr->p->payload;
+  if (iprh_tmp->start != 0) {
+    valid = 0;
+  }
+  if (ipr->datagram_len == 0) {
+    valid = 0;
+  }
+
+  /* Final validity test: no gaps between current and last fragment. */
+  iprh_prev = iprh;
+  q = iprh->next_pbuf;
+  while ((q != NULL) && valid) {
+    iprh = (struct ip6_reass_helper*)q->payload;
+    if (iprh_prev->end != iprh->start) {
+      valid = 0;
+      break;
+    }
+    iprh_prev = iprh;
+    q = iprh->next_pbuf;
+  }
+
+  if (valid) {
+    /* All fragments have been received */
+    struct ip6_hdr* iphdr_ptr;
+
+    /* chain together the pbufs contained within the ip6_reassdata list. */
+    iprh = (struct ip6_reass_helper*) ipr->p->payload;
+    while (iprh != NULL) {
+      struct pbuf* next_pbuf = iprh->next_pbuf;
+      if (next_pbuf != NULL) {
+        /* Save next helper struct (will be hidden in next step). */
+        iprh_tmp = (struct ip6_reass_helper*)next_pbuf->payload;
+
+        /* hide the fragment header for every succeeding fragment */
+        pbuf_header(next_pbuf, -IP6_FRAG_HLEN);
+#if IPV6_FRAG_COPYHEADER
+        if (IPV6_FRAG_REQROOM > 0) {
+          /* hide the extra bytes borrowed from ip6_hdr for struct ip6_reass_helper */
+          u8_t hdrerr = pbuf_header(next_pbuf, -(s16_t)(IPV6_FRAG_REQROOM));
+          LWIP_UNUSED_ARG(hdrerr); /* in case of LWIP_NOASSERT */
+          LWIP_ASSERT("no room for struct ip6_reass_helper", hdrerr == 0);
+        }
+#endif
+        pbuf_cat(ipr->p, next_pbuf);
+      }
+      else {
+        iprh_tmp = NULL;
+      }
+
+      iprh = iprh_tmp;
+    }
+
+#if IPV6_FRAG_COPYHEADER
+    if (IPV6_FRAG_REQROOM > 0) {
+      /* get back room for struct ip6_reass_helper (only required if sizeof(void*) > 4) */
+      u8_t hdrerr = pbuf_header(ipr->p, -(s16_t)(IPV6_FRAG_REQROOM));
+      LWIP_UNUSED_ARG(hdrerr); /* in case of LWIP_NOASSERT */
+      LWIP_ASSERT("no room for struct ip6_reass_helper", hdrerr == 0);
+    }
+    iphdr_ptr = (struct ip6_hdr*)((u8_t*)ipr->p->payload - IP6_HLEN);
+    MEMCPY(iphdr_ptr, &ipr->iphdr, IP6_HLEN);
+#else
+    iphdr_ptr = ipr->iphdr;
+#endif
+
+    /* Adjust datagram length by adding header lengths. */
+    ipr->datagram_len += (u16_t)(((u8_t*)ipr->p->payload - (u8_t*)iphdr_ptr)
+                         + IP6_FRAG_HLEN
+                         - IP6_HLEN);
+
+    /* Set payload length in ip header. */
+    iphdr_ptr->_plen = lwip_htons(ipr->datagram_len);
+
+    /* Get the first pbuf. */
+    p = ipr->p;
+
+    /* Restore Fragment Header in first pbuf. Mark as "single fragment"
+     * packet. Restore nexth. */
+    frag_hdr = (struct ip6_frag_hdr *) p->payload;
+    frag_hdr->_nexth = ipr->nexth;
+    frag_hdr->reserved = 0;
+    frag_hdr->_fragment_offset = 0;
+    frag_hdr->_identification = 0;
+
+    /* release the sources allocate for the fragment queue entry */
+    if (reassdatagrams == ipr) {
+      /* it was the first in the list */
+      reassdatagrams = ipr->next;
+    } else {
+      /* it wasn't the first, so it must have a valid 'prev' */
+      LWIP_ASSERT("sanity check linked list", ipr_prev != NULL);
+      ipr_prev->next = ipr->next;
+    }
+    memp_free(MEMP_IP6_REASSDATA, ipr);
+
+    /* adjust the number of pbufs currently queued for reassembly. */
+    ip6_reass_pbufcount -= pbuf_clen(p);
+
+    /* Move pbuf back to IPv6 header.
+       This cannot fail since we already checked when receiving this fragment. */
+    if (pbuf_header_force(p, (s16_t)((u8_t*)p->payload - (u8_t*)iphdr_ptr))) {
+      LWIP_ASSERT("ip6_reass: moving p->payload to ip6 header failed\n", 0);
+      pbuf_free(p);
+      return NULL;
+    }
+
+    /* Return the pbuf chain */
+    return p;
+  }
+  /* the datagram is not (yet?) reassembled completely */
+  return NULL;
+
+nullreturn:
+  pbuf_free(p);
+  return NULL;
+}
+
+#endif /* LWIP_IPV6 && LWIP_IPV6_REASS */
+
+#if LWIP_IPV6 && LWIP_IPV6_FRAG
+
+#if !LWIP_NETIF_TX_SINGLE_PBUF
+/** Allocate a new struct pbuf_custom_ref */
+static struct pbuf_custom_ref*
+ip6_frag_alloc_pbuf_custom_ref(void)
+{
+  return (struct pbuf_custom_ref*)memp_malloc(MEMP_FRAG_PBUF);
+}
+
+/** Free a struct pbuf_custom_ref */
+static void
+ip6_frag_free_pbuf_custom_ref(struct pbuf_custom_ref* p)
+{
+  LWIP_ASSERT("p != NULL", p != NULL);
+  memp_free(MEMP_FRAG_PBUF, p);
+}
+
+/** Free-callback function to free a 'struct pbuf_custom_ref', called by
+ * pbuf_free. */
+static void
+ip6_frag_free_pbuf_custom(struct pbuf *p)
+{
+  struct pbuf_custom_ref *pcr = (struct pbuf_custom_ref*)p;
+  LWIP_ASSERT("pcr != NULL", pcr != NULL);
+  LWIP_ASSERT("pcr == p", (void*)pcr == (void*)p);
+  if (pcr->original != NULL) {
+    pbuf_free(pcr->original);
+  }
+  ip6_frag_free_pbuf_custom_ref(pcr);
+}
+#endif /* !LWIP_NETIF_TX_SINGLE_PBUF */
+
+/**
+ * Fragment an IPv6 datagram if too large for the netif or path MTU.
+ *
+ * Chop the datagram in MTU sized chunks and send them in order
+ * by pointing PBUF_REFs into p
+ *
+ * @param p ipv6 packet to send
+ * @param netif the netif on which to send
+ * @param dest destination ipv6 address to which to send
+ *
+ * @return ERR_OK if sent successfully, err_t otherwise
+ */
+err_t
+ip6_frag(struct pbuf *p, struct netif *netif, const ip6_addr_t *dest)
+{
+  struct ip6_hdr *original_ip6hdr;
+  struct ip6_hdr *ip6hdr;
+  struct ip6_frag_hdr *frag_hdr;
+  struct pbuf *rambuf;
+#if !LWIP_NETIF_TX_SINGLE_PBUF
+  struct pbuf *newpbuf;
+  u16_t newpbuflen = 0;
+  u16_t left_to_copy;
+#endif
+  static u32_t identification;
+  u16_t nfb;
+  u16_t left, cop;
+  u16_t mtu;
+  u16_t fragment_offset = 0;
+  u16_t last;
+  u16_t poff = IP6_HLEN;
+
+  identification++;
+
+  original_ip6hdr = (struct ip6_hdr *)p->payload;
+
+  mtu = nd6_get_destination_mtu(dest, netif);
+
+  /* @todo we assume there are no options in the unfragmentable part (IPv6 header). */
+  left = p->tot_len - IP6_HLEN;
+
+  nfb = (mtu - (IP6_HLEN + IP6_FRAG_HLEN)) & IP6_FRAG_OFFSET_MASK;
+
+  while (left) {
+    last = (left <= nfb);
+
+    /* Fill this fragment */
+    cop = last ? left : nfb;
+
+#if LWIP_NETIF_TX_SINGLE_PBUF
+    rambuf = pbuf_alloc(PBUF_IP, cop + IP6_FRAG_HLEN, PBUF_RAM);
+    if (rambuf == NULL) {
+      IP6_FRAG_STATS_INC(ip6_frag.memerr);
+      return ERR_MEM;
+    }
+    LWIP_ASSERT("this needs a pbuf in one piece!",
+      (rambuf->len == rambuf->tot_len) && (rambuf->next == NULL));
+    poff += pbuf_copy_partial(p, (u8_t*)rambuf->payload + IP6_FRAG_HLEN, cop, poff);
+    /* make room for the IP header */
+    if (pbuf_header(rambuf, IP6_HLEN)) {
+      pbuf_free(rambuf);
+      IP6_FRAG_STATS_INC(ip6_frag.memerr);
+      return ERR_MEM;
+    }
+    /* fill in the IP header */
+    SMEMCPY(rambuf->payload, original_ip6hdr, IP6_HLEN);
+    ip6hdr = (struct ip6_hdr *)rambuf->payload;
+    frag_hdr = (struct ip6_frag_hdr *)((u8_t*)rambuf->payload + IP6_HLEN);
+#else
+    /* When not using a static buffer, create a chain of pbufs.
+     * The first will be a PBUF_RAM holding the link, IPv6, and Fragment header.
+     * The rest will be PBUF_REFs mirroring the pbuf chain to be fragged,
+     * but limited to the size of an mtu.
+     */
+    rambuf = pbuf_alloc(PBUF_LINK, IP6_HLEN + IP6_FRAG_HLEN, PBUF_RAM);
+    if (rambuf == NULL) {
+      IP6_FRAG_STATS_INC(ip6_frag.memerr);
+      return ERR_MEM;
+    }
+    LWIP_ASSERT("this needs a pbuf in one piece!",
+                (p->len >= (IP6_HLEN)));
+    SMEMCPY(rambuf->payload, original_ip6hdr, IP6_HLEN);
+    ip6hdr = (struct ip6_hdr *)rambuf->payload;
+    frag_hdr = (struct ip6_frag_hdr *)((u8_t*)rambuf->payload + IP6_HLEN);
+
+    /* Can just adjust p directly for needed offset. */
+    p->payload = (u8_t *)p->payload + poff;
+    p->len -= poff;
+    p->tot_len -= poff;
+
+    left_to_copy = cop;
+    while (left_to_copy) {
+      struct pbuf_custom_ref *pcr;
+      newpbuflen = (left_to_copy < p->len) ? left_to_copy : p->len;
+      /* Is this pbuf already empty? */
+      if (!newpbuflen) {
+        p = p->next;
+        continue;
+      }
+      pcr = ip6_frag_alloc_pbuf_custom_ref();
+      if (pcr == NULL) {
+        pbuf_free(rambuf);
+        IP6_FRAG_STATS_INC(ip6_frag.memerr);
+        return ERR_MEM;
+      }
+      /* Mirror this pbuf, although we might not need all of it. */
+      newpbuf = pbuf_alloced_custom(PBUF_RAW, newpbuflen, PBUF_REF, &pcr->pc, p->payload, newpbuflen);
+      if (newpbuf == NULL) {
+        ip6_frag_free_pbuf_custom_ref(pcr);
+        pbuf_free(rambuf);
+        IP6_FRAG_STATS_INC(ip6_frag.memerr);
+        return ERR_MEM;
+      }
+      pbuf_ref(p);
+      pcr->original = p;
+      pcr->pc.custom_free_function = ip6_frag_free_pbuf_custom;
+
+      /* Add it to end of rambuf's chain, but using pbuf_cat, not pbuf_chain
+       * so that it is removed when pbuf_dechain is later called on rambuf.
+       */
+      pbuf_cat(rambuf, newpbuf);
+      left_to_copy -= newpbuflen;
+      if (left_to_copy) {
+        p = p->next;
+      }
+    }
+    poff = newpbuflen;
+#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
+
+    /* Set headers */
+    frag_hdr->_nexth = original_ip6hdr->_nexth;
+    frag_hdr->reserved = 0;
+    frag_hdr->_fragment_offset = lwip_htons((fragment_offset & IP6_FRAG_OFFSET_MASK) | (last ? 0 : IP6_FRAG_MORE_FLAG));
+    frag_hdr->_identification = lwip_htonl(identification);
+
+    IP6H_NEXTH_SET(ip6hdr, IP6_NEXTH_FRAGMENT);
+    IP6H_PLEN_SET(ip6hdr, cop + IP6_FRAG_HLEN);
+
+    /* No need for separate header pbuf - we allowed room for it in rambuf
+     * when allocated.
+     */
+    IP6_FRAG_STATS_INC(ip6_frag.xmit);
+    netif->output_ip6(netif, rambuf, dest);
+
+    /* Unfortunately we can't reuse rambuf - the hardware may still be
+     * using the buffer. Instead we free it (and the ensuing chain) and
+     * recreate it next time round the loop. If we're lucky the hardware
+     * will have already sent the packet, the free will really free, and
+     * there will be zero memory penalty.
+     */
+
+    pbuf_free(rambuf);
+    left -= cop;
+    fragment_offset += cop;
+  }
+  return ERR_OK;
+}
+
+#endif /* LWIP_IPV6 && LWIP_IPV6_FRAG */

+ 588 - 0
thirdparty/lwip/src/core/ipv6/mld6.c

@@ -0,0 +1,588 @@
+/**
+ * @file
+ * Multicast listener discovery
+ *
+ * @defgroup mld6 MLD6
+ * @ingroup ip6
+ * Multicast listener discovery for IPv6. Aims to be compliant with RFC 2710.
+ * No support for MLDv2.\n
+ * To be called from TCPIP thread
+ */
+
+/*
+ * Copyright (c) 2010 Inico Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Ivan Delamer <delamer@inicotech.com>
+ *
+ *
+ * Please coordinate changes and requests with Ivan Delamer
+ * <delamer@inicotech.com>
+ */
+
+/* Based on igmp.c implementation of igmp v2 protocol */
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV6 && LWIP_IPV6_MLD  /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/mld6.h"
+#include "lwip/prot/mld6.h"
+#include "lwip/icmp6.h"
+#include "lwip/ip6.h"
+#include "lwip/ip6_addr.h"
+#include "lwip/ip.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/pbuf.h"
+#include "lwip/netif.h"
+#include "lwip/memp.h"
+#include "lwip/stats.h"
+
+#include <string.h>
+
+
+/*
+ * MLD constants
+ */
+#define MLD6_HL                           1
+#define MLD6_JOIN_DELAYING_MEMBER_TMR_MS  (500)
+
+#define MLD6_GROUP_NON_MEMBER             0
+#define MLD6_GROUP_DELAYING_MEMBER        1
+#define MLD6_GROUP_IDLE_MEMBER            2
+
+/* Forward declarations. */
+static struct mld_group *mld6_new_group(struct netif *ifp, const ip6_addr_t *addr);
+static err_t mld6_remove_group(struct netif *netif, struct mld_group *group);
+static void mld6_delayed_report(struct mld_group *group, u16_t maxresp);
+static void mld6_send(struct netif *netif, struct mld_group *group, u8_t type);
+
+
+/**
+ * Stop MLD processing on interface
+ *
+ * @param netif network interface on which stop MLD processing
+ */
+err_t
+mld6_stop(struct netif *netif)
+{
+  struct mld_group *group = netif_mld6_data(netif);
+
+  netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_MLD6, NULL);
+
+  while (group != NULL) {
+    struct mld_group *next = group->next; /* avoid use-after-free below */
+
+    /* disable the group at the MAC level */
+    if (netif->mld_mac_filter != NULL) {
+      netif->mld_mac_filter(netif, &(group->group_address), NETIF_DEL_MAC_FILTER);
+    }
+
+    /* free group */
+    memp_free(MEMP_MLD6_GROUP, group);
+
+    /* move to "next" */
+    group = next;
+  }
+  return ERR_OK;
+}
+
+/**
+ * Report MLD memberships for this interface
+ *
+ * @param netif network interface on which report MLD memberships
+ */
+void
+mld6_report_groups(struct netif *netif)
+{
+  struct mld_group *group = netif_mld6_data(netif);
+
+  while (group != NULL) {
+    mld6_delayed_report(group, MLD6_JOIN_DELAYING_MEMBER_TMR_MS);
+    group = group->next;
+  }
+}
+
+/**
+ * Search for a group that is joined on a netif
+ *
+ * @param ifp the network interface for which to look
+ * @param addr the group ipv6 address to search for
+ * @return a struct mld_group* if the group has been found,
+ *         NULL if the group wasn't found.
+ */
+struct mld_group *
+mld6_lookfor_group(struct netif *ifp, const ip6_addr_t *addr)
+{
+  struct mld_group *group = netif_mld6_data(ifp);
+
+  while (group != NULL) {
+    if (ip6_addr_cmp(&(group->group_address), addr)) {
+      return group;
+    }
+    group = group->next;
+  }
+
+  return NULL;
+}
+
+
+/**
+ * create a new group
+ *
+ * @param ifp the network interface for which to create
+ * @param addr the new group ipv6
+ * @return a struct mld_group*,
+ *         NULL on memory error.
+ */
+static struct mld_group *
+mld6_new_group(struct netif *ifp, const ip6_addr_t *addr)
+{
+  struct mld_group *group;
+
+  group = (struct mld_group *)memp_malloc(MEMP_MLD6_GROUP);
+  if (group != NULL) {
+    ip6_addr_set(&(group->group_address), addr);
+    group->timer              = 0; /* Not running */
+    group->group_state        = MLD6_GROUP_IDLE_MEMBER;
+    group->last_reporter_flag = 0;
+    group->use                = 0;
+    group->next               = netif_mld6_data(ifp);
+
+    netif_set_client_data(ifp, LWIP_NETIF_CLIENT_DATA_INDEX_MLD6, group);
+  }
+
+  return group;
+}
+
+/**
+ * Remove a group from the mld_group_list, but do not free it yet
+ *
+ * @param group the group to remove
+ * @return ERR_OK if group was removed from the list, an err_t otherwise
+ */
+static err_t
+mld6_remove_group(struct netif *netif, struct mld_group *group)
+{
+  err_t err = ERR_OK;
+
+  /* Is it the first group? */
+  if (netif_mld6_data(netif) == group) {
+    netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_MLD6, group->next);
+  } else {
+    /* look for group further down the list */
+    struct mld_group *tmpGroup;
+    for (tmpGroup = netif_mld6_data(netif); tmpGroup != NULL; tmpGroup = tmpGroup->next) {
+      if (tmpGroup->next == group) {
+        tmpGroup->next = group->next;
+        break;
+      }
+    }
+    /* Group not find group */
+    if (tmpGroup == NULL) {
+      err = ERR_ARG;
+    }
+  }
+
+  return err;
+}
+
+
+/**
+ * Process an input MLD message. Called by icmp6_input.
+ *
+ * @param p the mld packet, p->payload pointing to the icmpv6 header
+ * @param inp the netif on which this packet was received
+ */
+void
+mld6_input(struct pbuf *p, struct netif *inp)
+{
+  struct mld_header *mld_hdr;
+  struct mld_group *group;
+
+  MLD6_STATS_INC(mld6.recv);
+
+  /* Check that mld header fits in packet. */
+  if (p->len < sizeof(struct mld_header)) {
+    /* @todo debug message */
+    pbuf_free(p);
+    MLD6_STATS_INC(mld6.lenerr);
+    MLD6_STATS_INC(mld6.drop);
+    return;
+  }
+
+  mld_hdr = (struct mld_header *)p->payload;
+
+  switch (mld_hdr->type) {
+  case ICMP6_TYPE_MLQ: /* Multicast listener query. */
+    /* Is it a general query? */
+    if (ip6_addr_isallnodes_linklocal(ip6_current_dest_addr()) &&
+        ip6_addr_isany(&(mld_hdr->multicast_address))) {
+      MLD6_STATS_INC(mld6.rx_general);
+      /* Report all groups, except all nodes group, and if-local groups. */
+      group = netif_mld6_data(inp);
+      while (group != NULL) {
+        if ((!(ip6_addr_ismulticast_iflocal(&(group->group_address)))) &&
+            (!(ip6_addr_isallnodes_linklocal(&(group->group_address))))) {
+          mld6_delayed_report(group, mld_hdr->max_resp_delay);
+        }
+        group = group->next;
+      }
+    } else {
+      /* Have we joined this group?
+       * We use IP6 destination address to have a memory aligned copy.
+       * mld_hdr->multicast_address should be the same. */
+      MLD6_STATS_INC(mld6.rx_group);
+      group = mld6_lookfor_group(inp, ip6_current_dest_addr());
+      if (group != NULL) {
+        /* Schedule a report. */
+        mld6_delayed_report(group, mld_hdr->max_resp_delay);
+      }
+    }
+    break; /* ICMP6_TYPE_MLQ */
+  case ICMP6_TYPE_MLR: /* Multicast listener report. */
+    /* Have we joined this group?
+     * We use IP6 destination address to have a memory aligned copy.
+     * mld_hdr->multicast_address should be the same. */
+    MLD6_STATS_INC(mld6.rx_report);
+    group = mld6_lookfor_group(inp, ip6_current_dest_addr());
+    if (group != NULL) {
+      /* If we are waiting to report, cancel it. */
+      if (group->group_state == MLD6_GROUP_DELAYING_MEMBER) {
+        group->timer = 0; /* stopped */
+        group->group_state = MLD6_GROUP_IDLE_MEMBER;
+        group->last_reporter_flag = 0;
+      }
+    }
+    break; /* ICMP6_TYPE_MLR */
+  case ICMP6_TYPE_MLD: /* Multicast listener done. */
+    /* Do nothing, router will query us. */
+    break; /* ICMP6_TYPE_MLD */
+  default:
+    MLD6_STATS_INC(mld6.proterr);
+    MLD6_STATS_INC(mld6.drop);
+    break;
+  }
+
+  pbuf_free(p);
+}
+
+/**
+ * @ingroup mld6
+ * Join a group on a network interface.
+ *
+ * @param srcaddr ipv6 address of the network interface which should
+ *                join a new group. If IP6_ADDR_ANY, join on all netifs
+ * @param groupaddr the ipv6 address of the group to join
+ * @return ERR_OK if group was joined on the netif(s), an err_t otherwise
+ */
+err_t
+mld6_joingroup(const ip6_addr_t *srcaddr, const ip6_addr_t *groupaddr)
+{
+  err_t         err = ERR_VAL; /* no matching interface */
+  struct netif *netif;
+
+  /* loop through netif's */
+  netif = netif_list;
+  while (netif != NULL) {
+    /* Should we join this interface ? */
+    if (ip6_addr_isany(srcaddr) ||
+        netif_get_ip6_addr_match(netif, srcaddr) >= 0) {
+      err = mld6_joingroup_netif(netif, groupaddr);
+      if (err != ERR_OK) {
+        return err;
+      }
+    }
+
+    /* proceed to next network interface */
+    netif = netif->next;
+  }
+
+  return err;
+}
+
+/**
+ * @ingroup mld6
+ * Join a group on a network interface.
+ *
+ * @param netif the network interface which should join a new group.
+ * @param groupaddr the ipv6 address of the group to join
+ * @return ERR_OK if group was joined on the netif, an err_t otherwise
+ */
+err_t
+mld6_joingroup_netif(struct netif *netif, const ip6_addr_t *groupaddr)
+{
+  struct mld_group *group;
+
+  /* find group or create a new one if not found */
+  group = mld6_lookfor_group(netif, groupaddr);
+
+  if (group == NULL) {
+    /* Joining a new group. Create a new group entry. */
+    group = mld6_new_group(netif, groupaddr);
+    if (group == NULL) {
+      return ERR_MEM;
+    }
+
+    /* Activate this address on the MAC layer. */
+    if (netif->mld_mac_filter != NULL) {
+      netif->mld_mac_filter(netif, groupaddr, NETIF_ADD_MAC_FILTER);
+    }
+
+    /* Report our membership. */
+    MLD6_STATS_INC(mld6.tx_report);
+    mld6_send(netif, group, ICMP6_TYPE_MLR);
+    mld6_delayed_report(group, MLD6_JOIN_DELAYING_MEMBER_TMR_MS);
+  }
+
+  /* Increment group use */
+  group->use++;
+  return ERR_OK;
+}
+
+/**
+ * @ingroup mld6
+ * Leave a group on a network interface.
+ *
+ * @param srcaddr ipv6 address of the network interface which should
+ *                leave the group. If IP6_ISANY, leave on all netifs
+ * @param groupaddr the ipv6 address of the group to leave
+ * @return ERR_OK if group was left on the netif(s), an err_t otherwise
+ */
+err_t
+mld6_leavegroup(const ip6_addr_t *srcaddr, const ip6_addr_t *groupaddr)
+{
+  err_t         err = ERR_VAL; /* no matching interface */
+  struct netif *netif;
+
+  /* loop through netif's */
+  netif = netif_list;
+  while (netif != NULL) {
+    /* Should we leave this interface ? */
+    if (ip6_addr_isany(srcaddr) ||
+        netif_get_ip6_addr_match(netif, srcaddr) >= 0) {
+      err_t res = mld6_leavegroup_netif(netif, groupaddr);
+      if (err != ERR_OK) {
+        /* Store this result if we have not yet gotten a success */
+        err = res;
+      }
+    }
+    /* proceed to next network interface */
+    netif = netif->next;
+  }
+
+  return err;
+}
+
+/**
+ * @ingroup mld6
+ * Leave a group on a network interface.
+ *
+ * @param netif the network interface which should leave the group.
+ * @param groupaddr the ipv6 address of the group to leave
+ * @return ERR_OK if group was left on the netif, an err_t otherwise
+ */
+err_t
+mld6_leavegroup_netif(struct netif *netif, const ip6_addr_t *groupaddr)
+{
+  struct mld_group *group;
+
+  /* find group */
+  group = mld6_lookfor_group(netif, groupaddr);
+
+  if (group != NULL) {
+    /* Leave if there is no other use of the group */
+    if (group->use <= 1) {
+      /* Remove the group from the list */
+      mld6_remove_group(netif, group);
+
+      /* If we are the last reporter for this group */
+      if (group->last_reporter_flag) {
+        MLD6_STATS_INC(mld6.tx_leave);
+        mld6_send(netif, group, ICMP6_TYPE_MLD);
+      }
+
+      /* Disable the group at the MAC level */
+      if (netif->mld_mac_filter != NULL) {
+        netif->mld_mac_filter(netif, groupaddr, NETIF_DEL_MAC_FILTER);
+      }
+
+      /* free group struct */
+      memp_free(MEMP_MLD6_GROUP, group);
+    } else {
+      /* Decrement group use */
+      group->use--;
+    }
+
+    /* Left group */
+    return ERR_OK;
+  }
+
+  /* Group not found */
+  return ERR_VAL;
+}
+
+
+/**
+ * Periodic timer for mld processing. Must be called every
+ * MLD6_TMR_INTERVAL milliseconds (100).
+ *
+ * When a delaying member expires, a membership report is sent.
+ */
+void
+mld6_tmr(void)
+{
+  struct netif *netif = netif_list;
+
+  while (netif != NULL) {
+    struct mld_group *group = netif_mld6_data(netif);
+
+    while (group != NULL) {
+      if (group->timer > 0) {
+        group->timer--;
+        if (group->timer == 0) {
+          /* If the state is MLD6_GROUP_DELAYING_MEMBER then we send a report for this group */
+          if (group->group_state == MLD6_GROUP_DELAYING_MEMBER) {
+            MLD6_STATS_INC(mld6.tx_report);
+            mld6_send(netif, group, ICMP6_TYPE_MLR);
+            group->group_state = MLD6_GROUP_IDLE_MEMBER;
+          }
+        }
+      }
+      group = group->next;
+    }
+    netif = netif->next;
+  }
+}
+
+/**
+ * Schedule a delayed membership report for a group
+ *
+ * @param group the mld_group for which "delaying" membership report
+ *              should be sent
+ * @param maxresp the max resp delay provided in the query
+ */
+static void
+mld6_delayed_report(struct mld_group *group, u16_t maxresp)
+{
+  /* Convert maxresp from milliseconds to tmr ticks */
+  maxresp = maxresp / MLD6_TMR_INTERVAL;
+  if (maxresp == 0) {
+    maxresp = 1;
+  }
+
+#ifdef LWIP_RAND
+  /* Randomize maxresp. (if LWIP_RAND is supported) */
+  maxresp = LWIP_RAND() % maxresp;
+  if (maxresp == 0) {
+    maxresp = 1;
+  }
+#endif /* LWIP_RAND */
+
+  /* Apply timer value if no report has been scheduled already. */
+  if ((group->group_state == MLD6_GROUP_IDLE_MEMBER) ||
+     ((group->group_state == MLD6_GROUP_DELAYING_MEMBER) &&
+      ((group->timer == 0) || (maxresp < group->timer)))) {
+    group->timer = maxresp;
+    group->group_state = MLD6_GROUP_DELAYING_MEMBER;
+  }
+}
+
+/**
+ * Send a MLD message (report or done).
+ *
+ * An IPv6 hop-by-hop options header with a router alert option
+ * is prepended.
+ *
+ * @param group the group to report or quit
+ * @param type ICMP6_TYPE_MLR (report) or ICMP6_TYPE_MLD (done)
+ */
+static void
+mld6_send(struct netif *netif, struct mld_group *group, u8_t type)
+{
+  struct mld_header *mld_hdr;
+  struct pbuf *p;
+  const ip6_addr_t *src_addr;
+
+  /* Allocate a packet. Size is MLD header + IPv6 Hop-by-hop options header. */
+  p = pbuf_alloc(PBUF_IP, sizeof(struct mld_header) + sizeof(struct ip6_hbh_hdr), PBUF_RAM);
+  if (p == NULL) {
+    MLD6_STATS_INC(mld6.memerr);
+    return;
+  }
+
+  /* Move to make room for Hop-by-hop options header. */
+  if (pbuf_header(p, -IP6_HBH_HLEN)) {
+    pbuf_free(p);
+    MLD6_STATS_INC(mld6.lenerr);
+    return;
+  }
+
+  /* Select our source address. */
+  if (!ip6_addr_isvalid(netif_ip6_addr_state(netif, 0))) {
+    /* This is a special case, when we are performing duplicate address detection.
+     * We must join the multicast group, but we don't have a valid address yet. */
+    src_addr = IP6_ADDR_ANY6;
+  } else {
+    /* Use link-local address as source address. */
+    src_addr = netif_ip6_addr(netif, 0);
+  }
+
+  /* MLD message header pointer. */
+  mld_hdr = (struct mld_header *)p->payload;
+
+  /* Set fields. */
+  mld_hdr->type = type;
+  mld_hdr->code = 0;
+  mld_hdr->chksum = 0;
+  mld_hdr->max_resp_delay = 0;
+  mld_hdr->reserved = 0;
+  ip6_addr_set(&(mld_hdr->multicast_address), &(group->group_address));
+
+#if CHECKSUM_GEN_ICMP6
+  IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_ICMP6) {
+    mld_hdr->chksum = ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->len,
+      src_addr, &(group->group_address));
+  }
+#endif /* CHECKSUM_GEN_ICMP6 */
+
+  /* Add hop-by-hop headers options: router alert with MLD value. */
+  ip6_options_add_hbh_ra(p, IP6_NEXTH_ICMP6, IP6_ROUTER_ALERT_VALUE_MLD);
+
+  if (type == ICMP6_TYPE_MLR) {
+    /* Remember we were the last to report */
+    group->last_reporter_flag = 1;
+  }
+
+  /* Send the packet out. */
+  MLD6_STATS_INC(mld6.xmit);
+  ip6_output_if(p, (ip6_addr_isany(src_addr)) ? NULL : src_addr, &(group->group_address),
+      MLD6_HL, 0, IP6_NEXTH_HOPBYHOP, netif);
+  pbuf_free(p);
+}
+
+#endif /* LWIP_IPV6 */

+ 2102 - 0
thirdparty/lwip/src/core/ipv6/nd6.c

@@ -0,0 +1,2102 @@
+/**
+ * @file
+ *
+ * Neighbor discovery and stateless address autoconfiguration for IPv6.
+ * Aims to be compliant with RFC 4861 (Neighbor discovery) and RFC 4862
+ * (Address autoconfiguration).
+ */
+
+/*
+ * Copyright (c) 2010 Inico Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Ivan Delamer <delamer@inicotech.com>
+ *
+ *
+ * Please coordinate changes and requests with Ivan Delamer
+ * <delamer@inicotech.com>
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV6  /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/nd6.h"
+#include "lwip/priv/nd6_priv.h"
+#include "lwip/prot/nd6.h"
+#include "lwip/prot/icmp6.h"
+#include "lwip/pbuf.h"
+#include "lwip/mem.h"
+#include "lwip/memp.h"
+#include "lwip/ip6.h"
+#include "lwip/ip6_addr.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/netif.h"
+#include "lwip/icmp6.h"
+#include "lwip/mld6.h"
+#include "lwip/ip.h"
+#include "lwip/stats.h"
+#include "lwip/dns.h"
+
+#include <string.h>
+
+#ifdef LWIP_HOOK_FILENAME
+#include LWIP_HOOK_FILENAME
+#endif
+
+#if LWIP_IPV6_DUP_DETECT_ATTEMPTS > IP6_ADDR_TENTATIVE_COUNT_MASK
+#error LWIP_IPV6_DUP_DETECT_ATTEMPTS > IP6_ADDR_TENTATIVE_COUNT_MASK
+#endif
+
+/* Router tables. */
+struct nd6_neighbor_cache_entry neighbor_cache[LWIP_ND6_NUM_NEIGHBORS];
+struct nd6_destination_cache_entry destination_cache[LWIP_ND6_NUM_DESTINATIONS];
+struct nd6_prefix_list_entry prefix_list[LWIP_ND6_NUM_PREFIXES];
+struct nd6_router_list_entry default_router_list[LWIP_ND6_NUM_ROUTERS];
+
+/* Default values, can be updated by a RA message. */
+u32_t reachable_time = LWIP_ND6_REACHABLE_TIME;
+u32_t retrans_timer = LWIP_ND6_RETRANS_TIMER; /* @todo implement this value in timer */
+
+/* Index for cache entries. */
+static u8_t nd6_cached_neighbor_index;
+static u8_t nd6_cached_destination_index;
+
+/* Multicast address holder. */
+static ip6_addr_t multicast_address;
+
+/* Static buffer to parse RA packet options (size of a prefix option, biggest option) */
+static u8_t nd6_ra_buffer[sizeof(struct prefix_option)];
+
+/* Forward declarations. */
+static s8_t nd6_find_neighbor_cache_entry(const ip6_addr_t *ip6addr);
+static s8_t nd6_new_neighbor_cache_entry(void);
+static void nd6_free_neighbor_cache_entry(s8_t i);
+static s8_t nd6_find_destination_cache_entry(const ip6_addr_t *ip6addr);
+static s8_t nd6_new_destination_cache_entry(void);
+static s8_t nd6_is_prefix_in_netif(const ip6_addr_t *ip6addr, struct netif *netif);
+static s8_t nd6_select_router(const ip6_addr_t *ip6addr, struct netif *netif);
+static s8_t nd6_get_router(const ip6_addr_t *router_addr, struct netif *netif);
+static s8_t nd6_new_router(const ip6_addr_t *router_addr, struct netif *netif);
+static s8_t nd6_get_onlink_prefix(ip6_addr_t *prefix, struct netif *netif);
+static s8_t nd6_new_onlink_prefix(ip6_addr_t *prefix, struct netif *netif);
+static s8_t nd6_get_next_hop_entry(const ip6_addr_t *ip6addr, struct netif *netif);
+static err_t nd6_queue_packet(s8_t neighbor_index, struct pbuf *q);
+
+#define ND6_SEND_FLAG_MULTICAST_DEST 0x01
+#define ND6_SEND_FLAG_ALLNODES_DEST 0x02
+static void nd6_send_ns(struct netif *netif, const ip6_addr_t *target_addr, u8_t flags);
+static void nd6_send_na(struct netif *netif, const ip6_addr_t *target_addr, u8_t flags);
+static void nd6_send_neighbor_cache_probe(struct nd6_neighbor_cache_entry *entry, u8_t flags);
+#if LWIP_IPV6_SEND_ROUTER_SOLICIT
+static err_t nd6_send_rs(struct netif *netif);
+#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */
+
+#if LWIP_ND6_QUEUEING
+static void nd6_free_q(struct nd6_q_entry *q);
+#else /* LWIP_ND6_QUEUEING */
+#define nd6_free_q(q) pbuf_free(q)
+#endif /* LWIP_ND6_QUEUEING */
+static void nd6_send_q(s8_t i);
+
+
+/**
+ * Process an incoming neighbor discovery message
+ *
+ * @param p the nd packet, p->payload pointing to the icmpv6 header
+ * @param inp the netif on which this packet was received
+ */
+void
+nd6_input(struct pbuf *p, struct netif *inp)
+{
+  u8_t msg_type;
+  s8_t i;
+
+  ND6_STATS_INC(nd6.recv);
+
+  msg_type = *((u8_t *)p->payload);
+  switch (msg_type) {
+  case ICMP6_TYPE_NA: /* Neighbor Advertisement. */
+  {
+    struct na_header *na_hdr;
+    struct lladdr_option *lladdr_opt;
+
+    /* Check that na header fits in packet. */
+    if (p->len < (sizeof(struct na_header))) {
+      /* @todo debug message */
+      pbuf_free(p);
+      ND6_STATS_INC(nd6.lenerr);
+      ND6_STATS_INC(nd6.drop);
+      return;
+    }
+
+    na_hdr = (struct na_header *)p->payload;
+
+    /* Unsolicited NA?*/
+    if (ip6_addr_ismulticast(ip6_current_dest_addr())) {
+      ip6_addr_t target_address;
+      
+      /* This is an unsolicited NA.
+       * link-layer changed?
+       * part of DAD mechanism? */
+
+      /* Create an aligned copy. */
+      ip6_addr_set(&target_address, &(na_hdr->target_address));
+
+#if LWIP_IPV6_DUP_DETECT_ATTEMPTS
+      /* If the target address matches this netif, it is a DAD response. */
+      for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+        if (!ip6_addr_isinvalid(netif_ip6_addr_state(inp, i)) &&
+            ip6_addr_cmp(&target_address, netif_ip6_addr(inp, i))) {
+          /* We are using a duplicate address. */
+          netif_ip6_addr_set_state(inp, i, IP6_ADDR_INVALID);
+
+#if LWIP_IPV6_AUTOCONFIG
+          /* Check to see if this address was autoconfigured. */
+          if (!ip6_addr_islinklocal(&target_address)) {
+            i = nd6_get_onlink_prefix(&target_address, inp);
+            if (i >= 0) {
+              /* Mark this prefix as duplicate, so that we don't use it
+               * to generate this address again. */
+              prefix_list[i].flags |= ND6_PREFIX_AUTOCONFIG_ADDRESS_DUPLICATE;
+            }
+          }
+#endif /* LWIP_IPV6_AUTOCONFIG */
+
+          pbuf_free(p);
+          return;
+        }
+      }
+#endif /* LWIP_IPV6_DUP_DETECT_ATTEMPTS */
+
+      /* Check that link-layer address option also fits in packet. */
+      if (p->len < (sizeof(struct na_header) + 2)) {
+        /* @todo debug message */
+        pbuf_free(p);
+        ND6_STATS_INC(nd6.lenerr);
+        ND6_STATS_INC(nd6.drop);
+        return;
+      }
+
+      lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct na_header));
+
+      if (p->len < (sizeof(struct na_header) + (lladdr_opt->length << 3))) {
+        /* @todo debug message */
+        pbuf_free(p);
+        ND6_STATS_INC(nd6.lenerr);
+        ND6_STATS_INC(nd6.drop);
+        return;
+      }
+
+      /* This is an unsolicited NA, most likely there was a LLADDR change. */
+      i = nd6_find_neighbor_cache_entry(&target_address);
+      if (i >= 0) {
+        if (na_hdr->flags & ND6_FLAG_OVERRIDE) {
+          MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len);
+        }
+      }
+    } else {
+      ip6_addr_t target_address;
+
+      /* This is a solicited NA.
+       * neighbor address resolution response?
+       * neighbor unreachability detection response? */
+
+      /* Create an aligned copy. */
+      ip6_addr_set(&target_address, &(na_hdr->target_address));
+
+      /* Find the cache entry corresponding to this na. */
+      i = nd6_find_neighbor_cache_entry(&target_address);
+      if (i < 0) {
+        /* We no longer care about this target address. drop it. */
+        pbuf_free(p);
+        return;
+      }
+
+      /* Update cache entry. */
+      if ((na_hdr->flags & ND6_FLAG_OVERRIDE) ||
+          (neighbor_cache[i].state == ND6_INCOMPLETE)) {
+        /* Check that link-layer address option also fits in packet. */
+        if (p->len < (sizeof(struct na_header) + 2)) {
+          /* @todo debug message */
+          pbuf_free(p);
+          ND6_STATS_INC(nd6.lenerr);
+          ND6_STATS_INC(nd6.drop);
+          return;
+        }
+
+        lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct na_header));
+
+        if (p->len < (sizeof(struct na_header) + (lladdr_opt->length << 3))) {
+          /* @todo debug message */
+          pbuf_free(p);
+          ND6_STATS_INC(nd6.lenerr);
+          ND6_STATS_INC(nd6.drop);
+          return;
+        }
+
+        MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len);
+      }
+
+      neighbor_cache[i].netif = inp;
+      neighbor_cache[i].state = ND6_REACHABLE;
+      neighbor_cache[i].counter.reachable_time = reachable_time;
+
+      /* Send queued packets, if any. */
+      if (neighbor_cache[i].q != NULL) {
+        nd6_send_q(i);
+      }
+    }
+
+    break; /* ICMP6_TYPE_NA */
+  }
+  case ICMP6_TYPE_NS: /* Neighbor solicitation. */
+  {
+    struct ns_header *ns_hdr;
+    struct lladdr_option *lladdr_opt;
+    u8_t accepted;
+
+    /* Check that ns header fits in packet. */
+    if (p->len < sizeof(struct ns_header)) {
+      /* @todo debug message */
+      pbuf_free(p);
+      ND6_STATS_INC(nd6.lenerr);
+      ND6_STATS_INC(nd6.drop);
+      return;
+    }
+
+    ns_hdr = (struct ns_header *)p->payload;
+
+    /* Check if there is a link-layer address provided. Only point to it if in this buffer. */
+    if (p->len >= (sizeof(struct ns_header) + 2)) {
+      lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct ns_header));
+      if (p->len < (sizeof(struct ns_header) + (lladdr_opt->length << 3))) {
+        lladdr_opt = NULL;
+      }
+    } else {
+      lladdr_opt = NULL;
+    }
+
+    /* Check if the target address is configured on the receiving netif. */
+    accepted = 0;
+    for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; ++i) {
+      if ((ip6_addr_isvalid(netif_ip6_addr_state(inp, i)) ||
+           (ip6_addr_istentative(netif_ip6_addr_state(inp, i)) &&
+            ip6_addr_isany(ip6_current_src_addr()))) &&
+          ip6_addr_cmp(&(ns_hdr->target_address), netif_ip6_addr(inp, i))) {
+        accepted = 1;
+        break;
+      }
+    }
+
+    /* NS not for us? */
+    if (!accepted) {
+      pbuf_free(p);
+      return;
+    }
+
+    /* Check for ANY address in src (DAD algorithm). */
+    if (ip6_addr_isany(ip6_current_src_addr())) {
+      /* Sender is validating this address. */
+      for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; ++i) {
+        if (!ip6_addr_isinvalid(netif_ip6_addr_state(inp, i)) &&
+            ip6_addr_cmp(&(ns_hdr->target_address), netif_ip6_addr(inp, i))) {
+          /* Send a NA back so that the sender does not use this address. */
+          nd6_send_na(inp, netif_ip6_addr(inp, i), ND6_FLAG_OVERRIDE | ND6_SEND_FLAG_ALLNODES_DEST);
+          if (ip6_addr_istentative(netif_ip6_addr_state(inp, i))) {
+            /* We shouldn't use this address either. */
+            netif_ip6_addr_set_state(inp, i, IP6_ADDR_INVALID);
+          }
+        }
+      }
+    } else {
+      ip6_addr_t target_address;
+      
+      /* Sender is trying to resolve our address. */
+      /* Verify that they included their own link-layer address. */
+      if (lladdr_opt == NULL) {
+        /* Not a valid message. */
+        pbuf_free(p);
+        ND6_STATS_INC(nd6.proterr);
+        ND6_STATS_INC(nd6.drop);
+        return;
+      }
+
+      i = nd6_find_neighbor_cache_entry(ip6_current_src_addr());
+      if (i>= 0) {
+        /* We already have a record for the solicitor. */
+        if (neighbor_cache[i].state == ND6_INCOMPLETE) {
+          neighbor_cache[i].netif = inp;
+          MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len);
+
+          /* Delay probe in case we get confirmation of reachability from upper layer (TCP). */
+          neighbor_cache[i].state = ND6_DELAY;
+          neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME / ND6_TMR_INTERVAL;
+        }
+      } else {
+        /* Add their IPv6 address and link-layer address to neighbor cache.
+         * We will need it at least to send a unicast NA message, but most
+         * likely we will also be communicating with this node soon. */
+        i = nd6_new_neighbor_cache_entry();
+        if (i < 0) {
+          /* We couldn't assign a cache entry for this neighbor.
+           * we won't be able to reply. drop it. */
+          pbuf_free(p);
+          ND6_STATS_INC(nd6.memerr);
+          return;
+        }
+        neighbor_cache[i].netif = inp;
+        MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len);
+        ip6_addr_set(&(neighbor_cache[i].next_hop_address), ip6_current_src_addr());
+
+        /* Receiving a message does not prove reachability: only in one direction.
+         * Delay probe in case we get confirmation of reachability from upper layer (TCP). */
+        neighbor_cache[i].state = ND6_DELAY;
+        neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME / ND6_TMR_INTERVAL;
+      }
+
+      /* Create an aligned copy. */
+      ip6_addr_set(&target_address, &(ns_hdr->target_address));
+
+      /* Send back a NA for us. Allocate the reply pbuf. */
+      nd6_send_na(inp, &target_address, ND6_FLAG_SOLICITED | ND6_FLAG_OVERRIDE);
+    }
+
+    break; /* ICMP6_TYPE_NS */
+  }
+  case ICMP6_TYPE_RA: /* Router Advertisement. */
+  {
+    struct ra_header *ra_hdr;
+    u8_t *buffer; /* Used to copy options. */
+    u16_t offset;
+#if LWIP_ND6_RDNSS_MAX_DNS_SERVERS
+    /* There can by multiple RDNSS options per RA */
+    u8_t rdnss_server_idx = 0;
+#endif /* LWIP_ND6_RDNSS_MAX_DNS_SERVERS */
+
+    /* Check that RA header fits in packet. */
+    if (p->len < sizeof(struct ra_header)) {
+      /* @todo debug message */
+      pbuf_free(p);
+      ND6_STATS_INC(nd6.lenerr);
+      ND6_STATS_INC(nd6.drop);
+      return;
+    }
+
+    ra_hdr = (struct ra_header *)p->payload;
+
+    /* If we are sending RS messages, stop. */
+#if LWIP_IPV6_SEND_ROUTER_SOLICIT
+    /* ensure at least one solicitation is sent */
+    if ((inp->rs_count < LWIP_ND6_MAX_MULTICAST_SOLICIT) ||
+        (nd6_send_rs(inp) == ERR_OK)) {
+      inp->rs_count = 0;
+    }
+#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */
+
+    /* Get the matching default router entry. */
+    i = nd6_get_router(ip6_current_src_addr(), inp);
+    if (i < 0) {
+      /* Create a new router entry. */
+      i = nd6_new_router(ip6_current_src_addr(), inp);
+    }
+
+    if (i < 0) {
+      /* Could not create a new router entry. */
+      pbuf_free(p);
+      ND6_STATS_INC(nd6.memerr);
+      return;
+    }
+
+    /* Re-set invalidation timer. */
+    default_router_list[i].invalidation_timer = lwip_htons(ra_hdr->router_lifetime);
+
+    /* Re-set default timer values. */
+#if LWIP_ND6_ALLOW_RA_UPDATES
+    if (ra_hdr->retrans_timer > 0) {
+      retrans_timer = lwip_htonl(ra_hdr->retrans_timer);
+    }
+    if (ra_hdr->reachable_time > 0) {
+      reachable_time = lwip_htonl(ra_hdr->reachable_time);
+    }
+#endif /* LWIP_ND6_ALLOW_RA_UPDATES */
+
+    /* @todo set default hop limit... */
+    /* ra_hdr->current_hop_limit;*/
+
+    /* Update flags in local entry (incl. preference). */
+    default_router_list[i].flags = ra_hdr->flags;
+
+    /* Offset to options. */
+    offset = sizeof(struct ra_header);
+
+    /* Process each option. */
+    while ((p->tot_len - offset) > 0) {
+      if (p->len == p->tot_len) {
+        /* no need to copy from contiguous pbuf */
+        buffer = &((u8_t*)p->payload)[offset];
+      } else {
+        buffer = nd6_ra_buffer;
+        if (pbuf_copy_partial(p, buffer, sizeof(struct prefix_option), offset) != sizeof(struct prefix_option)) {
+          pbuf_free(p);
+          ND6_STATS_INC(nd6.lenerr);
+          ND6_STATS_INC(nd6.drop);
+          return;
+        }
+      }
+      if (buffer[1] == 0) {
+        /* zero-length extension. drop packet */
+        pbuf_free(p);
+        ND6_STATS_INC(nd6.lenerr);
+        ND6_STATS_INC(nd6.drop);
+        return;
+      }
+      switch (buffer[0]) {
+      case ND6_OPTION_TYPE_SOURCE_LLADDR:
+      {
+        struct lladdr_option *lladdr_opt;
+        lladdr_opt = (struct lladdr_option *)buffer;
+        if ((default_router_list[i].neighbor_entry != NULL) &&
+            (default_router_list[i].neighbor_entry->state == ND6_INCOMPLETE)) {
+          SMEMCPY(default_router_list[i].neighbor_entry->lladdr, lladdr_opt->addr, inp->hwaddr_len);
+          default_router_list[i].neighbor_entry->state = ND6_REACHABLE;
+          default_router_list[i].neighbor_entry->counter.reachable_time = reachable_time;
+        }
+        break;
+      }
+      case ND6_OPTION_TYPE_MTU:
+      {
+        struct mtu_option *mtu_opt;
+        mtu_opt = (struct mtu_option *)buffer;
+        if (lwip_htonl(mtu_opt->mtu) >= 1280) {
+#if LWIP_ND6_ALLOW_RA_UPDATES
+          inp->mtu = (u16_t)lwip_htonl(mtu_opt->mtu);
+#endif /* LWIP_ND6_ALLOW_RA_UPDATES */
+        }
+        break;
+      }
+      case ND6_OPTION_TYPE_PREFIX_INFO:
+      {
+        struct prefix_option *prefix_opt;
+        prefix_opt = (struct prefix_option *)buffer;
+
+        if ((prefix_opt->flags & ND6_PREFIX_FLAG_ON_LINK) &&
+            (prefix_opt->prefix_length == 64)  &&
+            !ip6_addr_islinklocal(&(prefix_opt->prefix))) {
+          /* Add to on-link prefix list. */
+          s8_t prefix;
+          ip6_addr_t prefix_addr;
+
+          /* Get a memory-aligned copy of the prefix. */
+          ip6_addr_set(&prefix_addr, &(prefix_opt->prefix));
+
+          /* find cache entry for this prefix. */
+          prefix = nd6_get_onlink_prefix(&prefix_addr, inp);
+          if (prefix < 0) {
+            /* Create a new cache entry. */
+            prefix = nd6_new_onlink_prefix(&prefix_addr, inp);
+          }
+          if (prefix >= 0) {
+            prefix_list[prefix].invalidation_timer = lwip_htonl(prefix_opt->valid_lifetime);
+
+#if LWIP_IPV6_AUTOCONFIG
+            if (prefix_opt->flags & ND6_PREFIX_FLAG_AUTONOMOUS) {
+              /* Mark prefix as autonomous, so that address autoconfiguration can take place.
+               * Only OR flag, so that we don't over-write other flags (such as ADDRESS_DUPLICATE)*/
+              prefix_list[prefix].flags |= ND6_PREFIX_AUTOCONFIG_AUTONOMOUS;
+            }
+#endif /* LWIP_IPV6_AUTOCONFIG */
+          }
+        }
+
+        break;
+      }
+      case ND6_OPTION_TYPE_ROUTE_INFO:
+        /* @todo implement preferred routes.
+        struct route_option * route_opt;
+        route_opt = (struct route_option *)buffer;*/
+
+        break;
+#if LWIP_ND6_RDNSS_MAX_DNS_SERVERS
+      case ND6_OPTION_TYPE_RDNSS:
+      {
+        u8_t num, n;
+        struct rdnss_option * rdnss_opt;
+
+        rdnss_opt = (struct rdnss_option *)buffer;
+        num = (rdnss_opt->length - 1) / 2;
+        for (n = 0; (rdnss_server_idx < DNS_MAX_SERVERS) && (n < num); n++) {
+          ip_addr_t rdnss_address;
+
+          /* Get a memory-aligned copy of the prefix. */
+          ip_addr_copy_from_ip6(rdnss_address, rdnss_opt->rdnss_address[n]);
+
+          if (htonl(rdnss_opt->lifetime) > 0) {
+            /* TODO implement Lifetime > 0 */
+            dns_setserver(rdnss_server_idx++, &rdnss_address);
+          } else {
+            /* TODO implement DNS removal in dns.c */
+            u8_t s;
+            for (s = 0; s < DNS_MAX_SERVERS; s++) {
+              const ip_addr_t *addr = dns_getserver(s);
+              if(ip_addr_cmp(addr, &rdnss_address)) {
+                dns_setserver(s, NULL);
+              }
+            }
+          }
+        }
+        break;
+      }
+#endif /* LWIP_ND6_RDNSS_MAX_DNS_SERVERS */
+      default:
+        /* Unrecognized option, abort. */
+        ND6_STATS_INC(nd6.proterr);
+        break;
+      }
+      /* option length is checked earlier to be non-zero to make sure loop ends */
+      offset += 8 * ((u16_t)buffer[1]);
+    }
+
+    break; /* ICMP6_TYPE_RA */
+  }
+  case ICMP6_TYPE_RD: /* Redirect */
+  {
+    struct redirect_header *redir_hdr;
+    struct lladdr_option *lladdr_opt;
+    ip6_addr_t tmp;
+
+    /* Check that Redir header fits in packet. */
+    if (p->len < sizeof(struct redirect_header)) {
+      /* @todo debug message */
+      pbuf_free(p);
+      ND6_STATS_INC(nd6.lenerr);
+      ND6_STATS_INC(nd6.drop);
+      return;
+    }
+
+    redir_hdr = (struct redirect_header *)p->payload;
+
+    if (p->len >= (sizeof(struct redirect_header) + 2)) {
+      lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct redirect_header));
+      if (p->len < (sizeof(struct redirect_header) + (lladdr_opt->length << 3))) {
+        lladdr_opt = NULL;
+      }
+    } else {
+      lladdr_opt = NULL;
+    }
+
+    /* Copy original destination address to current source address, to have an aligned copy. */
+    ip6_addr_set(&tmp, &(redir_hdr->destination_address));
+
+    /* Find dest address in cache */
+    i = nd6_find_destination_cache_entry(&tmp);
+    if (i < 0) {
+      /* Destination not in cache, drop packet. */
+      pbuf_free(p);
+      return;
+    }
+
+    /* Set the new target address. */
+    ip6_addr_set(&(destination_cache[i].next_hop_addr), &(redir_hdr->target_address));
+
+    /* If Link-layer address of other router is given, try to add to neighbor cache. */
+    if (lladdr_opt != NULL) {
+      if (lladdr_opt->type == ND6_OPTION_TYPE_TARGET_LLADDR) {
+        /* Copy target address to current source address, to have an aligned copy. */
+        ip6_addr_set(&tmp, &(redir_hdr->target_address));
+
+        i = nd6_find_neighbor_cache_entry(&tmp);
+        if (i < 0) {
+          i = nd6_new_neighbor_cache_entry();
+          if (i >= 0) {
+            neighbor_cache[i].netif = inp;
+            MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len);
+            ip6_addr_set(&(neighbor_cache[i].next_hop_address), &tmp);
+
+            /* Receiving a message does not prove reachability: only in one direction.
+             * Delay probe in case we get confirmation of reachability from upper layer (TCP). */
+            neighbor_cache[i].state = ND6_DELAY;
+            neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME / ND6_TMR_INTERVAL;
+          }
+        }
+        if (i >= 0) {
+          if (neighbor_cache[i].state == ND6_INCOMPLETE) {
+            MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len);
+            /* Receiving a message does not prove reachability: only in one direction.
+             * Delay probe in case we get confirmation of reachability from upper layer (TCP). */
+            neighbor_cache[i].state = ND6_DELAY;
+            neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME / ND6_TMR_INTERVAL;
+          }
+        }
+      }
+    }
+    break; /* ICMP6_TYPE_RD */
+  }
+  case ICMP6_TYPE_PTB: /* Packet too big */
+  {
+    struct icmp6_hdr *icmp6hdr; /* Packet too big message */
+    struct ip6_hdr *ip6hdr; /* IPv6 header of the packet which caused the error */
+    u32_t pmtu;
+    ip6_addr_t tmp;
+
+    /* Check that ICMPv6 header + IPv6 header fit in payload */
+    if (p->len < (sizeof(struct icmp6_hdr) + IP6_HLEN)) {
+      /* drop short packets */
+      pbuf_free(p);
+      ND6_STATS_INC(nd6.lenerr);
+      ND6_STATS_INC(nd6.drop);
+      return;
+    }
+
+    icmp6hdr = (struct icmp6_hdr *)p->payload;
+    ip6hdr = (struct ip6_hdr *)((u8_t*)p->payload + sizeof(struct icmp6_hdr));
+
+    /* Copy original destination address to current source address, to have an aligned copy. */
+    ip6_addr_set(&tmp, &(ip6hdr->dest));
+
+    /* Look for entry in destination cache. */
+    i = nd6_find_destination_cache_entry(&tmp);
+    if (i < 0) {
+      /* Destination not in cache, drop packet. */
+      pbuf_free(p);
+      return;
+    }
+
+    /* Change the Path MTU. */
+    pmtu = lwip_htonl(icmp6hdr->data);
+    destination_cache[i].pmtu = (u16_t)LWIP_MIN(pmtu, 0xFFFF);
+
+    break; /* ICMP6_TYPE_PTB */
+  }
+
+  default:
+    ND6_STATS_INC(nd6.proterr);
+    ND6_STATS_INC(nd6.drop);
+    break; /* default */
+  }
+
+  pbuf_free(p);
+}
+
+
+/**
+ * Periodic timer for Neighbor discovery functions:
+ *
+ * - Update neighbor reachability states
+ * - Update destination cache entries age
+ * - Update invalidation timers of default routers and on-link prefixes
+ * - Perform duplicate address detection (DAD) for our addresses
+ * - Send router solicitations
+ */
+void
+nd6_tmr(void)
+{
+  s8_t i;
+  struct netif *netif;
+
+  /* Process neighbor entries. */
+  for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) {
+    switch (neighbor_cache[i].state) {
+    case ND6_INCOMPLETE:
+      if ((neighbor_cache[i].counter.probes_sent >= LWIP_ND6_MAX_MULTICAST_SOLICIT) &&
+          (!neighbor_cache[i].isrouter)) {
+        /* Retries exceeded. */
+        nd6_free_neighbor_cache_entry(i);
+      } else {
+        /* Send a NS for this entry. */
+        neighbor_cache[i].counter.probes_sent++;
+        nd6_send_neighbor_cache_probe(&neighbor_cache[i], ND6_SEND_FLAG_MULTICAST_DEST);
+      }
+      break;
+    case ND6_REACHABLE:
+      /* Send queued packets, if any are left. Should have been sent already. */
+      if (neighbor_cache[i].q != NULL) {
+        nd6_send_q(i);
+      }
+      if (neighbor_cache[i].counter.reachable_time <= ND6_TMR_INTERVAL) {
+        /* Change to stale state. */
+        neighbor_cache[i].state = ND6_STALE;
+        neighbor_cache[i].counter.stale_time = 0;
+      } else {
+        neighbor_cache[i].counter.reachable_time -= ND6_TMR_INTERVAL;
+      }
+      break;
+    case ND6_STALE:
+      neighbor_cache[i].counter.stale_time++;
+      break;
+    case ND6_DELAY:
+      if (neighbor_cache[i].counter.delay_time <= 1) {
+        /* Change to PROBE state. */
+        neighbor_cache[i].state = ND6_PROBE;
+        neighbor_cache[i].counter.probes_sent = 0;
+      } else {
+        neighbor_cache[i].counter.delay_time--;
+      }
+      break;
+    case ND6_PROBE:
+      if ((neighbor_cache[i].counter.probes_sent >= LWIP_ND6_MAX_MULTICAST_SOLICIT) &&
+          (!neighbor_cache[i].isrouter)) {
+        /* Retries exceeded. */
+        nd6_free_neighbor_cache_entry(i);
+      } else {
+        /* Send a NS for this entry. */
+        neighbor_cache[i].counter.probes_sent++;
+        nd6_send_neighbor_cache_probe(&neighbor_cache[i], 0);
+      }
+      break;
+    case ND6_NO_ENTRY:
+    default:
+      /* Do nothing. */
+      break;
+    }
+  }
+
+  /* Process destination entries. */
+  for (i = 0; i < LWIP_ND6_NUM_DESTINATIONS; i++) {
+    destination_cache[i].age++;
+  }
+
+  /* Process router entries. */
+  for (i = 0; i < LWIP_ND6_NUM_ROUTERS; i++) {
+    if (default_router_list[i].neighbor_entry != NULL) {
+      /* Active entry. */
+      if (default_router_list[i].invalidation_timer > 0) {
+        default_router_list[i].invalidation_timer -= ND6_TMR_INTERVAL / 1000;
+      }
+      if (default_router_list[i].invalidation_timer < ND6_TMR_INTERVAL / 1000) {
+        /* Less than 1 second remaining. Clear this entry. */
+        default_router_list[i].neighbor_entry->isrouter = 0;
+        default_router_list[i].neighbor_entry = NULL;
+        default_router_list[i].invalidation_timer = 0;
+        default_router_list[i].flags = 0;
+      }
+    }
+  }
+
+  /* Process prefix entries. */
+  for (i = 0; i < LWIP_ND6_NUM_PREFIXES; i++) {
+    if (prefix_list[i].netif != NULL) {
+      if (prefix_list[i].invalidation_timer < ND6_TMR_INTERVAL / 1000) {
+        /* Entry timed out, remove it */
+        prefix_list[i].invalidation_timer = 0;
+
+#if LWIP_IPV6_AUTOCONFIG
+        /* If any addresses were configured with this prefix, remove them */
+        if (prefix_list[i].flags & ND6_PREFIX_AUTOCONFIG_ADDRESS_GENERATED) {
+          s8_t j;
+
+          for (j = 1; j < LWIP_IPV6_NUM_ADDRESSES; j++) {
+            if ((netif_ip6_addr_state(prefix_list[i].netif, j) != IP6_ADDR_INVALID) &&
+                ip6_addr_netcmp(&prefix_list[i].prefix, netif_ip6_addr(prefix_list[i].netif, j))) {
+              netif_ip6_addr_set_state(prefix_list[i].netif, j, IP6_ADDR_INVALID);
+              prefix_list[i].flags = 0;
+
+              /* Exit loop. */
+              break;
+            }
+          }
+        }
+#endif /* LWIP_IPV6_AUTOCONFIG */
+
+        prefix_list[i].netif = NULL;
+        prefix_list[i].flags = 0;
+      } else {
+        prefix_list[i].invalidation_timer -= ND6_TMR_INTERVAL / 1000;
+
+#if LWIP_IPV6_AUTOCONFIG
+        /* Initiate address autoconfiguration for this prefix, if conditions are met. */
+        if (prefix_list[i].netif->ip6_autoconfig_enabled &&
+            (prefix_list[i].flags & ND6_PREFIX_AUTOCONFIG_AUTONOMOUS) &&
+            !(prefix_list[i].flags & ND6_PREFIX_AUTOCONFIG_ADDRESS_GENERATED)) {
+          s8_t j;
+          /* Try to get an address on this netif that is invalid.
+           * Skip 0 index (link-local address) */
+          for (j = 1; j < LWIP_IPV6_NUM_ADDRESSES; j++) {
+            if (netif_ip6_addr_state(prefix_list[i].netif, j) == IP6_ADDR_INVALID) {
+              /* Generate an address using this prefix and interface ID from link-local address. */
+              netif_ip6_addr_set_parts(prefix_list[i].netif, j,
+                prefix_list[i].prefix.addr[0], prefix_list[i].prefix.addr[1],
+                netif_ip6_addr(prefix_list[i].netif, 0)->addr[2], netif_ip6_addr(prefix_list[i].netif, 0)->addr[3]);
+
+              /* Mark it as tentative (DAD will be performed if configured). */
+              netif_ip6_addr_set_state(prefix_list[i].netif, j, IP6_ADDR_TENTATIVE);
+
+              /* Mark this prefix with ADDRESS_GENERATED, so that we don't try again. */
+              prefix_list[i].flags |= ND6_PREFIX_AUTOCONFIG_ADDRESS_GENERATED;
+
+              /* Exit loop. */
+              break;
+            }
+          }
+        }
+#endif /* LWIP_IPV6_AUTOCONFIG */
+      }
+    }
+  }
+
+
+  /* Process our own addresses, if DAD configured. */
+  for (netif = netif_list; netif != NULL; netif = netif->next) {
+    for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; ++i) {
+      u8_t addr_state = netif_ip6_addr_state(netif, i);
+      if (ip6_addr_istentative(addr_state)) {
+        if ((addr_state & IP6_ADDR_TENTATIVE_COUNT_MASK) >= LWIP_IPV6_DUP_DETECT_ATTEMPTS) {
+          /* No NA received in response. Mark address as valid. */
+          netif_ip6_addr_set_state(netif, i, IP6_ADDR_PREFERRED);
+          /* @todo implement preferred and valid lifetimes. */
+        } else if (netif->flags & NETIF_FLAG_UP) {
+          /* Send a NS for this address. */
+          nd6_send_ns(netif, netif_ip6_addr(netif, i), ND6_SEND_FLAG_MULTICAST_DEST);
+          /* tentative: set next state by increasing by one */
+          netif_ip6_addr_set_state(netif, i, addr_state + 1);
+          /* @todo send max 1 NS per tmr call? enable return*/
+          /*return;*/
+        }
+      }
+    }
+  }
+
+#if LWIP_IPV6_SEND_ROUTER_SOLICIT
+  /* Send router solicitation messages, if necessary. */
+  for (netif = netif_list; netif != NULL; netif = netif->next) {
+    if ((netif->rs_count > 0) && (netif->flags & NETIF_FLAG_UP) &&
+        (!ip6_addr_isinvalid(netif_ip6_addr_state(netif, 0)))) {
+      if (nd6_send_rs(netif) == ERR_OK) {
+        netif->rs_count--;
+      }
+    }
+  }
+#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */
+
+}
+
+/** Send a neighbor solicitation message for a specific neighbor cache entry
+ *
+ * @param entry the neightbor cache entry for wich to send the message
+ * @param flags one of ND6_SEND_FLAG_*
+ */
+static void
+nd6_send_neighbor_cache_probe(struct nd6_neighbor_cache_entry *entry, u8_t flags)
+{
+  nd6_send_ns(entry->netif, &entry->next_hop_address, flags);
+}
+
+/**
+ * Send a neighbor solicitation message
+ *
+ * @param netif the netif on which to send the message
+ * @param target_addr the IPv6 target address for the ND message
+ * @param flags one of ND6_SEND_FLAG_*
+ */
+static void
+nd6_send_ns(struct netif *netif, const ip6_addr_t *target_addr, u8_t flags)
+{
+  struct ns_header *ns_hdr;
+  struct pbuf *p;
+  const ip6_addr_t *src_addr;
+  u16_t lladdr_opt_len;
+
+  if (ip6_addr_isvalid(netif_ip6_addr_state(netif,0))) {
+    /* Use link-local address as source address. */
+    src_addr = netif_ip6_addr(netif, 0);
+    /* calculate option length (in 8-byte-blocks) */
+    lladdr_opt_len = ((netif->hwaddr_len + 2) + 7) >> 3;
+  } else {
+    src_addr = IP6_ADDR_ANY6;
+    /* Option "MUST NOT be included when the source IP address is the unspecified address." */
+    lladdr_opt_len = 0;
+  }
+
+  /* Allocate a packet. */
+  p = pbuf_alloc(PBUF_IP, sizeof(struct ns_header) + (lladdr_opt_len << 3), PBUF_RAM);
+  if (p == NULL) {
+    ND6_STATS_INC(nd6.memerr);
+    return;
+  }
+
+  /* Set fields. */
+  ns_hdr = (struct ns_header *)p->payload;
+
+  ns_hdr->type = ICMP6_TYPE_NS;
+  ns_hdr->code = 0;
+  ns_hdr->chksum = 0;
+  ns_hdr->reserved = 0;
+  ip6_addr_set(&(ns_hdr->target_address), target_addr);
+
+  if (lladdr_opt_len != 0) {
+    struct lladdr_option *lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct ns_header));
+    lladdr_opt->type = ND6_OPTION_TYPE_SOURCE_LLADDR;
+    lladdr_opt->length = (u8_t)lladdr_opt_len;
+    SMEMCPY(lladdr_opt->addr, netif->hwaddr, netif->hwaddr_len);
+  }
+
+  /* Generate the solicited node address for the target address. */
+  if (flags & ND6_SEND_FLAG_MULTICAST_DEST) {
+    ip6_addr_set_solicitednode(&multicast_address, target_addr->addr[3]);
+    target_addr = &multicast_address;
+  }
+
+#if CHECKSUM_GEN_ICMP6
+  IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_ICMP6) {
+    ns_hdr->chksum = ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->len, src_addr,
+      target_addr);
+  }
+#endif /* CHECKSUM_GEN_ICMP6 */
+
+  /* Send the packet out. */
+  ND6_STATS_INC(nd6.xmit);
+  ip6_output_if(p, (src_addr == IP6_ADDR_ANY6) ? NULL : src_addr, target_addr,
+      LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, netif);
+  pbuf_free(p);
+}
+
+/**
+ * Send a neighbor advertisement message
+ *
+ * @param netif the netif on which to send the message
+ * @param target_addr the IPv6 target address for the ND message
+ * @param flags one of ND6_SEND_FLAG_*
+ */
+static void
+nd6_send_na(struct netif *netif, const ip6_addr_t *target_addr, u8_t flags)
+{
+  struct na_header *na_hdr;
+  struct lladdr_option *lladdr_opt;
+  struct pbuf *p;
+  const ip6_addr_t *src_addr;
+  const ip6_addr_t *dest_addr;
+  u16_t lladdr_opt_len;
+
+  /* Use link-local address as source address. */
+  /* src_addr = netif_ip6_addr(netif, 0); */
+  /* Use target address as source address. */
+  src_addr = target_addr;
+
+  /* Allocate a packet. */
+  lladdr_opt_len = ((netif->hwaddr_len + 2) >> 3) + (((netif->hwaddr_len + 2) & 0x07) ? 1 : 0);
+  p = pbuf_alloc(PBUF_IP, sizeof(struct na_header) + (lladdr_opt_len << 3), PBUF_RAM);
+  if (p == NULL) {
+    ND6_STATS_INC(nd6.memerr);
+    return;
+  }
+
+  /* Set fields. */
+  na_hdr = (struct na_header *)p->payload;
+  lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct na_header));
+
+  na_hdr->type = ICMP6_TYPE_NA;
+  na_hdr->code = 0;
+  na_hdr->chksum = 0;
+  na_hdr->flags = flags & 0xf0;
+  na_hdr->reserved[0] = 0;
+  na_hdr->reserved[1] = 0;
+  na_hdr->reserved[2] = 0;
+  ip6_addr_set(&(na_hdr->target_address), target_addr);
+
+  lladdr_opt->type = ND6_OPTION_TYPE_TARGET_LLADDR;
+  lladdr_opt->length = (u8_t)lladdr_opt_len;
+  SMEMCPY(lladdr_opt->addr, netif->hwaddr, netif->hwaddr_len);
+
+  /* Generate the solicited node address for the target address. */
+  if (flags & ND6_SEND_FLAG_MULTICAST_DEST) {
+    ip6_addr_set_solicitednode(&multicast_address, target_addr->addr[3]);
+    dest_addr = &multicast_address;
+  } else if (flags & ND6_SEND_FLAG_ALLNODES_DEST) {
+    ip6_addr_set_allnodes_linklocal(&multicast_address);
+    dest_addr = &multicast_address;
+  } else {
+    dest_addr = ip6_current_src_addr();
+  }
+
+#if CHECKSUM_GEN_ICMP6
+  IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_ICMP6) {
+    na_hdr->chksum = ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->len, src_addr,
+      dest_addr);
+  }
+#endif /* CHECKSUM_GEN_ICMP6 */
+
+  /* Send the packet out. */
+  ND6_STATS_INC(nd6.xmit);
+  ip6_output_if(p, src_addr, dest_addr,
+      LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, netif);
+  pbuf_free(p);
+}
+
+#if LWIP_IPV6_SEND_ROUTER_SOLICIT
+/**
+ * Send a router solicitation message
+ *
+ * @param netif the netif on which to send the message
+ */
+static err_t
+nd6_send_rs(struct netif *netif)
+{
+  struct rs_header *rs_hdr;
+  struct lladdr_option *lladdr_opt;
+  struct pbuf *p;
+  const ip6_addr_t *src_addr;
+  err_t err;
+  u16_t lladdr_opt_len = 0;
+
+  /* Link-local source address, or unspecified address? */
+  if (ip6_addr_isvalid(netif_ip6_addr_state(netif, 0))) {
+    src_addr = netif_ip6_addr(netif, 0);
+  } else {
+    src_addr = IP6_ADDR_ANY6;
+  }
+
+  /* Generate the all routers target address. */
+  ip6_addr_set_allrouters_linklocal(&multicast_address);
+
+  /* Allocate a packet. */
+  if (src_addr != IP6_ADDR_ANY6) {
+    lladdr_opt_len = ((netif->hwaddr_len + 2) >> 3) + (((netif->hwaddr_len + 2) & 0x07) ? 1 : 0);
+  }
+  p = pbuf_alloc(PBUF_IP, sizeof(struct rs_header) + (lladdr_opt_len << 3), PBUF_RAM);
+  if (p == NULL) {
+    ND6_STATS_INC(nd6.memerr);
+    return ERR_BUF;
+  }
+
+  /* Set fields. */
+  rs_hdr = (struct rs_header *)p->payload;
+
+  rs_hdr->type = ICMP6_TYPE_RS;
+  rs_hdr->code = 0;
+  rs_hdr->chksum = 0;
+  rs_hdr->reserved = 0;
+
+  if (src_addr != IP6_ADDR_ANY6) {
+    /* Include our hw address. */
+    lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct rs_header));
+    lladdr_opt->type = ND6_OPTION_TYPE_SOURCE_LLADDR;
+    lladdr_opt->length = (u8_t)lladdr_opt_len;
+    SMEMCPY(lladdr_opt->addr, netif->hwaddr, netif->hwaddr_len);
+  }
+
+#if CHECKSUM_GEN_ICMP6
+  IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_ICMP6) {
+    rs_hdr->chksum = ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->len, src_addr,
+      &multicast_address);
+  }
+#endif /* CHECKSUM_GEN_ICMP6 */
+
+  /* Send the packet out. */
+  ND6_STATS_INC(nd6.xmit);
+
+  err = ip6_output_if(p, (src_addr == IP6_ADDR_ANY6) ? NULL : src_addr, &multicast_address,
+      LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, netif);
+  pbuf_free(p);
+
+  return err;
+}
+#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */
+
+/**
+ * Search for a neighbor cache entry
+ *
+ * @param ip6addr the IPv6 address of the neighbor
+ * @return The neighbor cache entry index that matched, -1 if no
+ * entry is found
+ */
+static s8_t
+nd6_find_neighbor_cache_entry(const ip6_addr_t *ip6addr)
+{
+  s8_t i;
+  for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) {
+    if (ip6_addr_cmp(ip6addr, &(neighbor_cache[i].next_hop_address))) {
+      return i;
+    }
+  }
+  return -1;
+}
+
+/**
+ * Create a new neighbor cache entry.
+ *
+ * If no unused entry is found, will try to recycle an old entry
+ * according to ad-hoc "age" heuristic.
+ *
+ * @return The neighbor cache entry index that was created, -1 if no
+ * entry could be created
+ */
+static s8_t
+nd6_new_neighbor_cache_entry(void)
+{
+  s8_t i;
+  s8_t j;
+  u32_t time;
+
+
+  /* First, try to find an empty entry. */
+  for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) {
+    if (neighbor_cache[i].state == ND6_NO_ENTRY) {
+      return i;
+    }
+  }
+
+  /* We need to recycle an entry. in general, do not recycle if it is a router. */
+
+  /* Next, try to find a Stale entry. */
+  for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) {
+    if ((neighbor_cache[i].state == ND6_STALE) &&
+        (!neighbor_cache[i].isrouter)) {
+      nd6_free_neighbor_cache_entry(i);
+      return i;
+    }
+  }
+
+  /* Next, try to find a Probe entry. */
+  for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) {
+    if ((neighbor_cache[i].state == ND6_PROBE) &&
+        (!neighbor_cache[i].isrouter)) {
+      nd6_free_neighbor_cache_entry(i);
+      return i;
+    }
+  }
+
+  /* Next, try to find a Delayed entry. */
+  for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) {
+    if ((neighbor_cache[i].state == ND6_DELAY) &&
+        (!neighbor_cache[i].isrouter)) {
+      nd6_free_neighbor_cache_entry(i);
+      return i;
+    }
+  }
+
+  /* Next, try to find the oldest reachable entry. */
+  time = 0xfffffffful;
+  j = -1;
+  for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) {
+    if ((neighbor_cache[i].state == ND6_REACHABLE) &&
+        (!neighbor_cache[i].isrouter)) {
+      if (neighbor_cache[i].counter.reachable_time < time) {
+        j = i;
+        time = neighbor_cache[i].counter.reachable_time;
+      }
+    }
+  }
+  if (j >= 0) {
+    nd6_free_neighbor_cache_entry(j);
+    return j;
+  }
+
+  /* Next, find oldest incomplete entry without queued packets. */
+  time = 0;
+  j = -1;
+  for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) {
+    if (
+        (neighbor_cache[i].q == NULL) &&
+        (neighbor_cache[i].state == ND6_INCOMPLETE) &&
+        (!neighbor_cache[i].isrouter)) {
+      if (neighbor_cache[i].counter.probes_sent >= time) {
+        j = i;
+        time = neighbor_cache[i].counter.probes_sent;
+      }
+    }
+  }
+  if (j >= 0) {
+    nd6_free_neighbor_cache_entry(j);
+    return j;
+  }
+
+  /* Next, find oldest incomplete entry with queued packets. */
+  time = 0;
+  j = -1;
+  for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) {
+    if ((neighbor_cache[i].state == ND6_INCOMPLETE) &&
+        (!neighbor_cache[i].isrouter)) {
+      if (neighbor_cache[i].counter.probes_sent >= time) {
+        j = i;
+        time = neighbor_cache[i].counter.probes_sent;
+      }
+    }
+  }
+  if (j >= 0) {
+    nd6_free_neighbor_cache_entry(j);
+    return j;
+  }
+
+  /* No more entries to try. */
+  return -1;
+}
+
+/**
+ * Will free any resources associated with a neighbor cache
+ * entry, and will mark it as unused.
+ *
+ * @param i the neighbor cache entry index to free
+ */
+static void
+nd6_free_neighbor_cache_entry(s8_t i)
+{
+  if ((i < 0) || (i >= LWIP_ND6_NUM_NEIGHBORS)) {
+    return;
+  }
+  if (neighbor_cache[i].isrouter) {
+    /* isrouter needs to be cleared before deleting a neighbor cache entry */
+    return;
+  }
+
+  /* Free any queued packets. */
+  if (neighbor_cache[i].q != NULL) {
+    nd6_free_q(neighbor_cache[i].q);
+    neighbor_cache[i].q = NULL;
+  }
+
+  neighbor_cache[i].state = ND6_NO_ENTRY;
+  neighbor_cache[i].isrouter = 0;
+  neighbor_cache[i].netif = NULL;
+  neighbor_cache[i].counter.reachable_time = 0;
+  ip6_addr_set_zero(&(neighbor_cache[i].next_hop_address));
+}
+
+/**
+ * Search for a destination cache entry
+ *
+ * @param ip6addr the IPv6 address of the destination
+ * @return The destination cache entry index that matched, -1 if no
+ * entry is found
+ */
+static s8_t
+nd6_find_destination_cache_entry(const ip6_addr_t *ip6addr)
+{
+  s8_t i;
+  for (i = 0; i < LWIP_ND6_NUM_DESTINATIONS; i++) {
+    if (ip6_addr_cmp(ip6addr, &(destination_cache[i].destination_addr))) {
+      return i;
+    }
+  }
+  return -1;
+}
+
+/**
+ * Create a new destination cache entry. If no unused entry is found,
+ * will recycle oldest entry.
+ *
+ * @return The destination cache entry index that was created, -1 if no
+ * entry was created
+ */
+static s8_t
+nd6_new_destination_cache_entry(void)
+{
+  s8_t i, j;
+  u32_t age;
+
+  /* Find an empty entry. */
+  for (i = 0; i < LWIP_ND6_NUM_DESTINATIONS; i++) {
+    if (ip6_addr_isany(&(destination_cache[i].destination_addr))) {
+      return i;
+    }
+  }
+
+  /* Find oldest entry. */
+  age = 0;
+  j = LWIP_ND6_NUM_DESTINATIONS - 1;
+  for (i = 0; i < LWIP_ND6_NUM_DESTINATIONS; i++) {
+    if (destination_cache[i].age > age) {
+      j = i;
+    }
+  }
+
+  return j;
+}
+
+/**
+ * Clear the destination cache.
+ *
+ * This operation may be necessary for consistency in the light of changing
+ * local addresses and/or use of the gateway hook.
+ */
+void
+nd6_clear_destination_cache(void)
+{
+  int i;
+
+  for (i = 0; i < LWIP_ND6_NUM_DESTINATIONS; i++) {
+    ip6_addr_set_any(&destination_cache[i].destination_addr);
+  }
+}
+
+/**
+ * Determine whether an address matches an on-link prefix.
+ *
+ * @param ip6addr the IPv6 address to match
+ * @return 1 if the address is on-link, 0 otherwise
+ */
+static s8_t
+nd6_is_prefix_in_netif(const ip6_addr_t *ip6addr, struct netif *netif)
+{
+  s8_t i;
+  for (i = 0; i < LWIP_ND6_NUM_PREFIXES; i++) {
+    if ((prefix_list[i].netif == netif) &&
+        (prefix_list[i].invalidation_timer > 0) &&
+        ip6_addr_netcmp(ip6addr, &(prefix_list[i].prefix))) {
+      return 1;
+    }
+  }
+  /* Check to see if address prefix matches a (manually?) configured address. */
+  for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+    if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) &&
+        ip6_addr_netcmp(ip6addr, netif_ip6_addr(netif, i))) {
+      return 1;
+    }
+  }
+  return 0;
+}
+
+/**
+ * Select a default router for a destination.
+ *
+ * @param ip6addr the destination address
+ * @param netif the netif for the outgoing packet, if known
+ * @return the default router entry index, or -1 if no suitable
+ *         router is found
+ */
+static s8_t
+nd6_select_router(const ip6_addr_t *ip6addr, struct netif *netif)
+{
+  s8_t i;
+  /* last_router is used for round-robin router selection (as recommended
+   * in RFC). This is more robust in case one router is not reachable,
+   * we are not stuck trying to resolve it. */
+  static s8_t last_router;
+  (void)ip6addr; /* @todo match preferred routes!! (must implement ND6_OPTION_TYPE_ROUTE_INFO) */
+
+  /* @todo: implement default router preference */
+
+  /* Look for reachable routers. */
+  for (i = 0; i < LWIP_ND6_NUM_ROUTERS; i++) {
+    if (++last_router >= LWIP_ND6_NUM_ROUTERS) {
+      last_router = 0;
+    }
+    if ((default_router_list[i].neighbor_entry != NULL) &&
+        (netif != NULL ? netif == default_router_list[i].neighbor_entry->netif : 1) &&
+        (default_router_list[i].invalidation_timer > 0) &&
+        (default_router_list[i].neighbor_entry->state == ND6_REACHABLE)) {
+      return i;
+    }
+  }
+
+  /* Look for router in other reachability states, but still valid according to timer. */
+  for (i = 0; i < LWIP_ND6_NUM_ROUTERS; i++) {
+    if (++last_router >= LWIP_ND6_NUM_ROUTERS) {
+      last_router = 0;
+    }
+    if ((default_router_list[i].neighbor_entry != NULL) &&
+        (netif != NULL ? netif == default_router_list[i].neighbor_entry->netif : 1) &&
+        (default_router_list[i].invalidation_timer > 0)) {
+      return i;
+    }
+  }
+
+  /* Look for any router for which we have any information at all. */
+  for (i = 0; i < LWIP_ND6_NUM_ROUTERS; i++) {
+    if (++last_router >= LWIP_ND6_NUM_ROUTERS) {
+      last_router = 0;
+    }
+    if (default_router_list[i].neighbor_entry != NULL &&
+        (netif != NULL ? netif == default_router_list[i].neighbor_entry->netif : 1)) {
+      return i;
+    }
+  }
+
+  /* no suitable router found. */
+  return -1;
+}
+
+/**
+ * Find a router-announced route to the given destination.
+ *
+ * The caller is responsible for checking whether the returned netif, if any,
+ * is in a suitable state (up, link up) to be used for packet transmission.
+ *
+ * @param ip6addr the destination IPv6 address
+ * @return the netif to use for the destination, or NULL if none found
+ */
+struct netif *
+nd6_find_route(const ip6_addr_t *ip6addr)
+{
+  s8_t i;
+
+  i = nd6_select_router(ip6addr, NULL);
+  if (i >= 0) {
+    if (default_router_list[i].neighbor_entry != NULL) {
+      return default_router_list[i].neighbor_entry->netif; /* may be NULL */
+    }
+  }
+
+  return NULL;
+}
+
+/**
+ * Find an entry for a default router.
+ *
+ * @param router_addr the IPv6 address of the router
+ * @param netif the netif on which the router is found, if known
+ * @return the index of the router entry, or -1 if not found
+ */
+static s8_t
+nd6_get_router(const ip6_addr_t *router_addr, struct netif *netif)
+{
+  s8_t i;
+
+  /* Look for router. */
+  for (i = 0; i < LWIP_ND6_NUM_ROUTERS; i++) {
+    if ((default_router_list[i].neighbor_entry != NULL) &&
+        ((netif != NULL) ? netif == default_router_list[i].neighbor_entry->netif : 1) &&
+        ip6_addr_cmp(router_addr, &(default_router_list[i].neighbor_entry->next_hop_address))) {
+      return i;
+    }
+  }
+
+  /* router not found. */
+  return -1;
+}
+
+/**
+ * Create a new entry for a default router.
+ *
+ * @param router_addr the IPv6 address of the router
+ * @param netif the netif on which the router is connected, if known
+ * @return the index on the router table, or -1 if could not be created
+ */
+static s8_t
+nd6_new_router(const ip6_addr_t *router_addr, struct netif *netif)
+{
+  s8_t router_index;
+  s8_t free_router_index;
+  s8_t neighbor_index;
+
+  /* Do we have a neighbor entry for this router? */
+  neighbor_index = nd6_find_neighbor_cache_entry(router_addr);
+  if (neighbor_index < 0) {
+    /* Create a neighbor entry for this router. */
+    neighbor_index = nd6_new_neighbor_cache_entry();
+    if (neighbor_index < 0) {
+      /* Could not create neighbor entry for this router. */
+      return -1;
+    }
+    ip6_addr_set(&(neighbor_cache[neighbor_index].next_hop_address), router_addr);
+    neighbor_cache[neighbor_index].netif = netif;
+    neighbor_cache[neighbor_index].q = NULL;
+    neighbor_cache[neighbor_index].state = ND6_INCOMPLETE;
+    neighbor_cache[neighbor_index].counter.probes_sent = 1;
+    nd6_send_neighbor_cache_probe(&neighbor_cache[neighbor_index], ND6_SEND_FLAG_MULTICAST_DEST);
+  }
+
+  /* Mark neighbor as router. */
+  neighbor_cache[neighbor_index].isrouter = 1;
+
+  /* Look for empty entry. */
+  free_router_index = LWIP_ND6_NUM_ROUTERS;
+  for (router_index = LWIP_ND6_NUM_ROUTERS - 1; router_index >= 0; router_index--) {
+    /* check if router already exists (this is a special case for 2 netifs on the same subnet
+       - e.g. wifi and cable) */
+    if(default_router_list[router_index].neighbor_entry == &(neighbor_cache[neighbor_index])){ 
+      return router_index; 
+    } 
+    if (default_router_list[router_index].neighbor_entry == NULL) {
+      /* remember lowest free index to create a new entry */
+      free_router_index = router_index;
+    }
+  }
+  if (free_router_index < LWIP_ND6_NUM_ROUTERS) {
+    default_router_list[free_router_index].neighbor_entry = &(neighbor_cache[neighbor_index]);
+    return free_router_index;
+  }
+
+  /* Could not create a router entry. */
+
+  /* Mark neighbor entry as not-router. Entry might be useful as neighbor still. */
+  neighbor_cache[neighbor_index].isrouter = 0;
+
+  /* router not found. */
+  return -1;
+}
+
+/**
+ * Find the cached entry for an on-link prefix.
+ *
+ * @param prefix the IPv6 prefix that is on-link
+ * @param netif the netif on which the prefix is on-link
+ * @return the index on the prefix table, or -1 if not found
+ */
+static s8_t
+nd6_get_onlink_prefix(ip6_addr_t *prefix, struct netif *netif)
+{
+  s8_t i;
+
+  /* Look for prefix in list. */
+  for (i = 0; i < LWIP_ND6_NUM_PREFIXES; ++i) {
+    if ((ip6_addr_netcmp(&(prefix_list[i].prefix), prefix)) &&
+        (prefix_list[i].netif == netif)) {
+      return i;
+    }
+  }
+
+  /* Entry not available. */
+  return -1;
+}
+
+/**
+ * Creates a new entry for an on-link prefix.
+ *
+ * @param prefix the IPv6 prefix that is on-link
+ * @param netif the netif on which the prefix is on-link
+ * @return the index on the prefix table, or -1 if not created
+ */
+static s8_t
+nd6_new_onlink_prefix(ip6_addr_t *prefix, struct netif *netif)
+{
+  s8_t i;
+
+  /* Create new entry. */
+  for (i = 0; i < LWIP_ND6_NUM_PREFIXES; ++i) {
+    if ((prefix_list[i].netif == NULL) ||
+        (prefix_list[i].invalidation_timer == 0)) {
+      /* Found empty prefix entry. */
+      prefix_list[i].netif = netif;
+      ip6_addr_set(&(prefix_list[i].prefix), prefix);
+#if LWIP_IPV6_AUTOCONFIG
+      prefix_list[i].flags = 0;
+#endif /* LWIP_IPV6_AUTOCONFIG */
+      return i;
+    }
+  }
+
+  /* Entry not available. */
+  return -1;
+}
+
+/**
+ * Determine the next hop for a destination. Will determine if the
+ * destination is on-link, else a suitable on-link router is selected.
+ *
+ * The last entry index is cached for fast entry search.
+ *
+ * @param ip6addr the destination address
+ * @param netif the netif on which the packet will be sent
+ * @return the neighbor cache entry for the next hop, ERR_RTE if no
+ *         suitable next hop was found, ERR_MEM if no cache entry
+ *         could be created
+ */
+static s8_t
+nd6_get_next_hop_entry(const ip6_addr_t *ip6addr, struct netif *netif)
+{
+#ifdef LWIP_HOOK_ND6_GET_GW
+  const ip6_addr_t *next_hop_addr;
+#endif /* LWIP_HOOK_ND6_GET_GW */
+  s8_t i;
+
+#if LWIP_NETIF_HWADDRHINT
+  if (netif->addr_hint != NULL) {
+    /* per-pcb cached entry was given */
+    u8_t addr_hint = *(netif->addr_hint);
+    if (addr_hint < LWIP_ND6_NUM_DESTINATIONS) {
+      nd6_cached_destination_index = addr_hint;
+    }
+  }
+#endif /* LWIP_NETIF_HWADDRHINT */
+
+  /* Look for ip6addr in destination cache. */
+  if (ip6_addr_cmp(ip6addr, &(destination_cache[nd6_cached_destination_index].destination_addr))) {
+    /* the cached entry index is the right one! */
+    /* do nothing. */
+    ND6_STATS_INC(nd6.cachehit);
+  } else {
+    /* Search destination cache. */
+    i = nd6_find_destination_cache_entry(ip6addr);
+    if (i >= 0) {
+      /* found destination entry. make it our new cached index. */
+      nd6_cached_destination_index = i;
+    } else {
+      /* Not found. Create a new destination entry. */
+      i = nd6_new_destination_cache_entry();
+      if (i >= 0) {
+        /* got new destination entry. make it our new cached index. */
+        nd6_cached_destination_index = i;
+      } else {
+        /* Could not create a destination cache entry. */
+        return ERR_MEM;
+      }
+
+      /* Copy dest address to destination cache. */
+      ip6_addr_set(&(destination_cache[nd6_cached_destination_index].destination_addr), ip6addr);
+
+      /* Now find the next hop. is it a neighbor? */
+      if (ip6_addr_islinklocal(ip6addr) ||
+          nd6_is_prefix_in_netif(ip6addr, netif)) {
+        /* Destination in local link. */
+        destination_cache[nd6_cached_destination_index].pmtu = netif->mtu;
+        ip6_addr_copy(destination_cache[nd6_cached_destination_index].next_hop_addr, destination_cache[nd6_cached_destination_index].destination_addr);
+#ifdef LWIP_HOOK_ND6_GET_GW
+      } else if ((next_hop_addr = LWIP_HOOK_ND6_GET_GW(netif, ip6addr)) != NULL) {
+        /* Next hop for destination provided by hook function. */
+        destination_cache[nd6_cached_destination_index].pmtu = netif->mtu;
+        ip6_addr_set(&destination_cache[nd6_cached_destination_index].next_hop_addr, next_hop_addr);
+#endif /* LWIP_HOOK_ND6_GET_GW */
+      } else {
+        /* We need to select a router. */
+        i = nd6_select_router(ip6addr, netif);
+        if (i < 0) {
+          /* No router found. */
+          ip6_addr_set_any(&(destination_cache[nd6_cached_destination_index].destination_addr));
+          return ERR_RTE;
+        }
+        destination_cache[nd6_cached_destination_index].pmtu = netif->mtu; /* Start with netif mtu, correct through ICMPv6 if necessary */
+        ip6_addr_copy(destination_cache[nd6_cached_destination_index].next_hop_addr, default_router_list[i].neighbor_entry->next_hop_address);
+      }
+    }
+  }
+
+#if LWIP_NETIF_HWADDRHINT
+  if (netif->addr_hint != NULL) {
+    /* per-pcb cached entry was given */
+    *(netif->addr_hint) = nd6_cached_destination_index;
+  }
+#endif /* LWIP_NETIF_HWADDRHINT */
+
+  /* Look in neighbor cache for the next-hop address. */
+  if (ip6_addr_cmp(&(destination_cache[nd6_cached_destination_index].next_hop_addr),
+                   &(neighbor_cache[nd6_cached_neighbor_index].next_hop_address))) {
+    /* Cache hit. */
+    /* Do nothing. */
+    ND6_STATS_INC(nd6.cachehit);
+  } else {
+    i = nd6_find_neighbor_cache_entry(&(destination_cache[nd6_cached_destination_index].next_hop_addr));
+    if (i >= 0) {
+      /* Found a matching record, make it new cached entry. */
+      nd6_cached_neighbor_index = i;
+    } else {
+      /* Neighbor not in cache. Make a new entry. */
+      i = nd6_new_neighbor_cache_entry();
+      if (i >= 0) {
+        /* got new neighbor entry. make it our new cached index. */
+        nd6_cached_neighbor_index = i;
+      } else {
+        /* Could not create a neighbor cache entry. */
+        return ERR_MEM;
+      }
+
+      /* Initialize fields. */
+      ip6_addr_copy(neighbor_cache[i].next_hop_address,
+                   destination_cache[nd6_cached_destination_index].next_hop_addr);
+      neighbor_cache[i].isrouter = 0;
+      neighbor_cache[i].netif = netif;
+      neighbor_cache[i].state = ND6_INCOMPLETE;
+      neighbor_cache[i].counter.probes_sent = 1;
+      nd6_send_neighbor_cache_probe(&neighbor_cache[i], ND6_SEND_FLAG_MULTICAST_DEST);
+    }
+  }
+
+  /* Reset this destination's age. */
+  destination_cache[nd6_cached_destination_index].age = 0;
+
+  return nd6_cached_neighbor_index;
+}
+
+/**
+ * Queue a packet for a neighbor.
+ *
+ * @param neighbor_index the index in the neighbor cache table
+ * @param q packet to be queued
+ * @return ERR_OK if succeeded, ERR_MEM if out of memory
+ */
+static err_t
+nd6_queue_packet(s8_t neighbor_index, struct pbuf *q)
+{
+  err_t result = ERR_MEM;
+  struct pbuf *p;
+  int copy_needed = 0;
+#if LWIP_ND6_QUEUEING
+  struct nd6_q_entry *new_entry, *r;
+#endif /* LWIP_ND6_QUEUEING */
+
+  if ((neighbor_index < 0) || (neighbor_index >= LWIP_ND6_NUM_NEIGHBORS)) {
+    return ERR_ARG;
+  }
+
+  /* IF q includes a PBUF_REF, PBUF_POOL or PBUF_RAM, we have no choice but
+   * to copy the whole queue into a new PBUF_RAM (see bug #11400)
+   * PBUF_ROMs can be left as they are, since ROM must not get changed. */
+  p = q;
+  while (p) {
+    if (p->type != PBUF_ROM) {
+      copy_needed = 1;
+      break;
+    }
+    p = p->next;
+  }
+  if (copy_needed) {
+    /* copy the whole packet into new pbufs */
+    p = pbuf_alloc(PBUF_LINK, q->tot_len, PBUF_RAM);
+    while ((p == NULL) && (neighbor_cache[neighbor_index].q != NULL)) {
+      /* Free oldest packet (as per RFC recommendation) */
+#if LWIP_ND6_QUEUEING
+      r = neighbor_cache[neighbor_index].q;
+      neighbor_cache[neighbor_index].q = r->next;
+      r->next = NULL;
+      nd6_free_q(r);
+#else /* LWIP_ND6_QUEUEING */
+      pbuf_free(neighbor_cache[neighbor_index].q);
+      neighbor_cache[neighbor_index].q = NULL;
+#endif /* LWIP_ND6_QUEUEING */
+      p = pbuf_alloc(PBUF_LINK, q->tot_len, PBUF_RAM);
+    }
+    if (p != NULL) {
+      if (pbuf_copy(p, q) != ERR_OK) {
+        pbuf_free(p);
+        p = NULL;
+      }
+    }
+  } else {
+    /* referencing the old pbuf is enough */
+    p = q;
+    pbuf_ref(p);
+  }
+  /* packet was copied/ref'd? */
+  if (p != NULL) {
+    /* queue packet ... */
+#if LWIP_ND6_QUEUEING
+    /* allocate a new nd6 queue entry */
+    new_entry = (struct nd6_q_entry *)memp_malloc(MEMP_ND6_QUEUE);
+    if ((new_entry == NULL) && (neighbor_cache[neighbor_index].q != NULL)) {
+      /* Free oldest packet (as per RFC recommendation) */
+      r = neighbor_cache[neighbor_index].q;
+      neighbor_cache[neighbor_index].q = r->next;
+      r->next = NULL;
+      nd6_free_q(r);
+      new_entry = (struct nd6_q_entry *)memp_malloc(MEMP_ND6_QUEUE);
+    }
+    if (new_entry != NULL) {
+      new_entry->next = NULL;
+      new_entry->p = p;
+      if (neighbor_cache[neighbor_index].q != NULL) {
+        /* queue was already existent, append the new entry to the end */
+        r = neighbor_cache[neighbor_index].q;
+        while (r->next != NULL) {
+          r = r->next;
+        }
+        r->next = new_entry;
+      } else {
+        /* queue did not exist, first item in queue */
+        neighbor_cache[neighbor_index].q = new_entry;
+      }
+      LWIP_DEBUGF(LWIP_DBG_TRACE, ("ipv6: queued packet %p on neighbor entry %"S16_F"\n", (void *)p, (s16_t)neighbor_index));
+      result = ERR_OK;
+    } else {
+      /* the pool MEMP_ND6_QUEUE is empty */
+      pbuf_free(p);
+      LWIP_DEBUGF(LWIP_DBG_TRACE, ("ipv6: could not queue a copy of packet %p (out of memory)\n", (void *)p));
+      /* { result == ERR_MEM } through initialization */
+    }
+#else /* LWIP_ND6_QUEUEING */
+    /* Queue a single packet. If an older packet is already queued, free it as per RFC. */
+    if (neighbor_cache[neighbor_index].q != NULL) {
+      pbuf_free(neighbor_cache[neighbor_index].q);
+    }
+    neighbor_cache[neighbor_index].q = p;
+    LWIP_DEBUGF(LWIP_DBG_TRACE, ("ipv6: queued packet %p on neighbor entry %"S16_F"\n", (void *)p, (s16_t)neighbor_index));
+    result = ERR_OK;
+#endif /* LWIP_ND6_QUEUEING */
+  } else {
+    LWIP_DEBUGF(LWIP_DBG_TRACE, ("ipv6: could not queue a copy of packet %p (out of memory)\n", (void *)q));
+    /* { result == ERR_MEM } through initialization */
+  }
+
+  return result;
+}
+
+#if LWIP_ND6_QUEUEING
+/**
+ * Free a complete queue of nd6 q entries
+ *
+ * @param q a queue of nd6_q_entry to free
+ */
+static void
+nd6_free_q(struct nd6_q_entry *q)
+{
+  struct nd6_q_entry *r;
+  LWIP_ASSERT("q != NULL", q != NULL);
+  LWIP_ASSERT("q->p != NULL", q->p != NULL);
+  while (q) {
+    r = q;
+    q = q->next;
+    LWIP_ASSERT("r->p != NULL", (r->p != NULL));
+    pbuf_free(r->p);
+    memp_free(MEMP_ND6_QUEUE, r);
+  }
+}
+#endif /* LWIP_ND6_QUEUEING */
+
+/**
+ * Send queued packets for a neighbor
+ *
+ * @param i the neighbor to send packets to
+ */
+static void
+nd6_send_q(s8_t i)
+{
+  struct ip6_hdr *ip6hdr;
+  ip6_addr_t dest;
+#if LWIP_ND6_QUEUEING
+  struct nd6_q_entry *q;
+#endif /* LWIP_ND6_QUEUEING */
+
+  if ((i < 0) || (i >= LWIP_ND6_NUM_NEIGHBORS)) {
+    return;
+  }
+
+#if LWIP_ND6_QUEUEING
+  while (neighbor_cache[i].q != NULL) {
+    /* remember first in queue */
+    q = neighbor_cache[i].q;
+    /* pop first item off the queue */
+    neighbor_cache[i].q = q->next;
+    /* Get ipv6 header. */
+    ip6hdr = (struct ip6_hdr *)(q->p->payload);
+    /* Create an aligned copy. */
+    ip6_addr_set(&dest, &(ip6hdr->dest));
+    /* send the queued IPv6 packet */
+    (neighbor_cache[i].netif)->output_ip6(neighbor_cache[i].netif, q->p, &dest);
+    /* free the queued IP packet */
+    pbuf_free(q->p);
+    /* now queue entry can be freed */
+    memp_free(MEMP_ND6_QUEUE, q);
+  }
+#else /* LWIP_ND6_QUEUEING */
+  if (neighbor_cache[i].q != NULL) {
+    /* Get ipv6 header. */
+    ip6hdr = (struct ip6_hdr *)(neighbor_cache[i].q->payload);
+    /* Create an aligned copy. */
+    ip6_addr_set(&dest, &(ip6hdr->dest));
+    /* send the queued IPv6 packet */
+    (neighbor_cache[i].netif)->output_ip6(neighbor_cache[i].netif, neighbor_cache[i].q, &dest);
+    /* free the queued IP packet */
+    pbuf_free(neighbor_cache[i].q);
+    neighbor_cache[i].q = NULL;
+  }
+#endif /* LWIP_ND6_QUEUEING */
+}
+
+/**
+ * A packet is to be transmitted to a specific IPv6 destination on a specific
+ * interface. Check if we can find the hardware address of the next hop to use
+ * for the packet. If so, give the hardware address to the caller, which should
+ * use it to send the packet right away. Otherwise, enqueue the packet for
+ * later transmission while looking up the hardware address, if possible.
+ *
+ * As such, this function returns one of three different possible results:
+ *
+ * - ERR_OK with a non-NULL 'hwaddrp': the caller should send the packet now.
+ * - ERR_OK with a NULL 'hwaddrp': the packet has been enqueued for later.
+ * - not ERR_OK: something went wrong; forward the error upward in the stack.
+ *
+ * @param netif The lwIP network interface on which the IP packet will be sent.
+ * @param q The pbuf(s) containing the IP packet to be sent.
+ * @param ip6addr The destination IPv6 address of the packet.
+ * @param hwaddrp On success, filled with a pointer to a HW address or NULL (meaning
+ *        the packet has been queued).
+ * @return
+ * - ERR_OK on success, ERR_RTE if no route was found for the packet,
+ * or ERR_MEM if low memory conditions prohibit sending the packet at all.
+ */
+err_t
+nd6_get_next_hop_addr_or_queue(struct netif *netif, struct pbuf *q, const ip6_addr_t *ip6addr, const u8_t **hwaddrp)
+{
+  s8_t i;
+
+  /* Get next hop record. */
+  i = nd6_get_next_hop_entry(ip6addr, netif);
+  if (i < 0) {
+    /* failed to get a next hop neighbor record. */
+    return i;
+  }
+
+  /* Now that we have a destination record, send or queue the packet. */
+  if (neighbor_cache[i].state == ND6_STALE) {
+    /* Switch to delay state. */
+    neighbor_cache[i].state = ND6_DELAY;
+    neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME / ND6_TMR_INTERVAL;
+  }
+  /* @todo should we send or queue if PROBE? send for now, to let unicast NS pass. */
+  if ((neighbor_cache[i].state == ND6_REACHABLE) ||
+      (neighbor_cache[i].state == ND6_DELAY) ||
+      (neighbor_cache[i].state == ND6_PROBE)) {
+
+    /* Tell the caller to send out the packet now. */
+    *hwaddrp = neighbor_cache[i].lladdr;
+    return ERR_OK;
+  }
+
+  /* We should queue packet on this interface. */
+  *hwaddrp = NULL;
+  return nd6_queue_packet(i, q);
+}
+
+
+/**
+ * Get the Path MTU for a destination.
+ *
+ * @param ip6addr the destination address
+ * @param netif the netif on which the packet will be sent
+ * @return the Path MTU, if known, or the netif default MTU
+ */
+u16_t
+nd6_get_destination_mtu(const ip6_addr_t *ip6addr, struct netif *netif)
+{
+  s8_t i;
+
+  i = nd6_find_destination_cache_entry(ip6addr);
+  if (i >= 0) {
+    if (destination_cache[i].pmtu > 0) {
+      return destination_cache[i].pmtu;
+    }
+  }
+
+  if (netif != NULL) {
+    return netif->mtu;
+  }
+
+  return 1280; /* Minimum MTU */
+}
+
+
+#if LWIP_ND6_TCP_REACHABILITY_HINTS
+/**
+ * Provide the Neighbor discovery process with a hint that a
+ * destination is reachable. Called by tcp_receive when ACKs are
+ * received or sent (as per RFC). This is useful to avoid sending
+ * NS messages every 30 seconds.
+ *
+ * @param ip6addr the destination address which is know to be reachable
+ *                by an upper layer protocol (TCP)
+ */
+void
+nd6_reachability_hint(const ip6_addr_t *ip6addr)
+{
+  s8_t i;
+
+  /* Find destination in cache. */
+  if (ip6_addr_cmp(ip6addr, &(destination_cache[nd6_cached_destination_index].destination_addr))) {
+    i = nd6_cached_destination_index;
+    ND6_STATS_INC(nd6.cachehit);
+  } else {
+    i = nd6_find_destination_cache_entry(ip6addr);
+  }
+  if (i < 0) {
+    return;
+  }
+
+  /* Find next hop neighbor in cache. */
+  if (ip6_addr_cmp(&(destination_cache[i].next_hop_addr), &(neighbor_cache[nd6_cached_neighbor_index].next_hop_address))) {
+    i = nd6_cached_neighbor_index;
+    ND6_STATS_INC(nd6.cachehit);
+  } else {
+    i = nd6_find_neighbor_cache_entry(&(destination_cache[i].next_hop_addr));
+  }
+  if (i < 0) {
+    return;
+  }
+
+  /* For safety: don't set as reachable if we don't have a LL address yet. Misuse protection. */
+  if (neighbor_cache[i].state == ND6_INCOMPLETE || neighbor_cache[i].state == ND6_NO_ENTRY) {
+    return;
+  }
+
+  /* Set reachability state. */
+  neighbor_cache[i].state = ND6_REACHABLE;
+  neighbor_cache[i].counter.reachable_time = reachable_time;
+}
+#endif /* LWIP_ND6_TCP_REACHABILITY_HINTS */
+
+/**
+ * Remove all prefix, neighbor_cache and router entries of the specified netif.
+ *
+ * @param netif points to a network interface
+ */
+void
+nd6_cleanup_netif(struct netif *netif)
+{
+  u8_t i;
+  s8_t router_index;
+  for (i = 0; i < LWIP_ND6_NUM_PREFIXES; i++) {
+    if (prefix_list[i].netif == netif) {
+      prefix_list[i].netif = NULL;
+      prefix_list[i].flags = 0;
+    }
+  }
+  for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) {
+    if (neighbor_cache[i].netif == netif) {
+      for (router_index = 0; router_index < LWIP_ND6_NUM_ROUTERS; router_index++) {
+        if (default_router_list[router_index].neighbor_entry == &neighbor_cache[i]) {
+          default_router_list[router_index].neighbor_entry = NULL;
+          default_router_list[router_index].flags = 0;
+        }
+      }
+      neighbor_cache[i].isrouter = 0;
+      nd6_free_neighbor_cache_entry(i);
+    }
+  }
+}
+
+#if LWIP_IPV6_MLD
+/**
+ * The state of a local IPv6 address entry is about to change. If needed, join
+ * or leave the solicited-node multicast group for the address.
+ *
+ * @param netif The netif that owns the address.
+ * @param addr_idx The index of the address.
+ * @param new_state The new (IP6_ADDR_) state for the address.
+ */
+void
+nd6_adjust_mld_membership(struct netif *netif, s8_t addr_idx, u8_t new_state)
+{
+  u8_t old_state, old_member, new_member;
+
+  old_state = netif_ip6_addr_state(netif, addr_idx);
+
+  /* Determine whether we were, and should be, a member of the solicited-node
+   * multicast group for this address. For tentative addresses, the group is
+   * not joined until the address enters the TENTATIVE_1 (or VALID) state. */
+  old_member = (old_state != IP6_ADDR_INVALID && old_state != IP6_ADDR_TENTATIVE);
+  new_member = (new_state != IP6_ADDR_INVALID && new_state != IP6_ADDR_TENTATIVE);
+
+  if (old_member != new_member) {
+    ip6_addr_set_solicitednode(&multicast_address, netif_ip6_addr(netif, addr_idx)->addr[3]);
+
+    if (new_member) {
+      mld6_joingroup_netif(netif, &multicast_address);
+    } else {
+      mld6_leavegroup_netif(netif, &multicast_address);
+    }
+  }
+}
+#endif /* LWIP_IPV6_MLD */
+
+#endif /* LWIP_IPV6 */

+ 777 - 659
thirdparty/lwip/src/core/mem.c

@@ -1,659 +1,777 @@
-/**
- * @file
- * Dynamic memory manager
- *
- * This is a lightweight replacement for the standard C library malloc().
- *
- * If you want to use the standard C library malloc() instead, define
- * MEM_LIBC_MALLOC to 1 in your lwipopts.h
- *
- * To let mem_malloc() use pools (prevents fragmentation and is much faster than
- * a heap but might waste some memory), define MEM_USE_POOLS to 1, define
- * MEM_USE_CUSTOM_POOLS to 1 and create a file "lwippools.h" that includes a list
- * of pools like this (more pools can be added between _START and _END):
- *
- * Define three pools with sizes 256, 512, and 1512 bytes
- * LWIP_MALLOC_MEMPOOL_START
- * LWIP_MALLOC_MEMPOOL(20, 256)
- * LWIP_MALLOC_MEMPOOL(10, 512)
- * LWIP_MALLOC_MEMPOOL(5, 1512)
- * LWIP_MALLOC_MEMPOOL_END
- */
-
-/*
- * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * This file is part of the lwIP TCP/IP stack.
- *
- * Author: Adam Dunkels <adam@sics.se>
- *         Simon Goldschmidt
- *
- */
-
-#include "lwip/opt.h"
-
-#if !MEM_LIBC_MALLOC /* don't build if not configured for use in lwipopts.h */
-
-#include "lwip/def.h"
-#include "lwip/mem.h"
-#include "lwip/sys.h"
-#include "lwip/stats.h"
-#include "lwip/err.h"
-
-#include <string.h>
-
-#if MEM_USE_POOLS
-/* lwIP head implemented with different sized pools */
-
-/**
- * Allocate memory: determine the smallest pool that is big enough
- * to contain an element of 'size' and get an element from that pool.
- *
- * @param size the size in bytes of the memory needed
- * @return a pointer to the allocated memory or NULL if the pool is empty
- */
-void *
-mem_malloc(mem_size_t size)
-{
-  void *ret;
-  struct memp_malloc_helper *element;
-  memp_t poolnr;
-  mem_size_t required_size = size + LWIP_MEM_ALIGN_SIZE(sizeof(struct memp_malloc_helper));
-
-  for (poolnr = MEMP_POOL_FIRST; poolnr <= MEMP_POOL_LAST; poolnr = (memp_t)(poolnr + 1)) {
-#if MEM_USE_POOLS_TRY_BIGGER_POOL
-again:
-#endif /* MEM_USE_POOLS_TRY_BIGGER_POOL */
-    /* is this pool big enough to hold an element of the required size
-       plus a struct memp_malloc_helper that saves the pool this element came from? */
-    if (required_size <= memp_sizes[poolnr]) {
-      break;
-    }
-  }
-  if (poolnr > MEMP_POOL_LAST) {
-    LWIP_ASSERT("mem_malloc(): no pool is that big!", 0);
-    return NULL;
-  }
-  element = (struct memp_malloc_helper*)memp_malloc(poolnr);
-  if (element == NULL) {
-    /* No need to DEBUGF or ASSERT: This error is already
-       taken care of in memp.c */
-#if MEM_USE_POOLS_TRY_BIGGER_POOL
-    /** Try a bigger pool if this one is empty! */
-    if (poolnr < MEMP_POOL_LAST) {
-      poolnr++;
-      goto again;
-    }
-#endif /* MEM_USE_POOLS_TRY_BIGGER_POOL */
-    return NULL;
-  }
-
-  /* save the pool number this element came from */
-  element->poolnr = poolnr;
-  /* and return a pointer to the memory directly after the struct memp_malloc_helper */
-  ret = (u8_t*)element + LWIP_MEM_ALIGN_SIZE(sizeof(struct memp_malloc_helper));
-
-  return ret;
-}
-
-/**
- * Free memory previously allocated by mem_malloc. Loads the pool number
- * and calls memp_free with that pool number to put the element back into
- * its pool
- *
- * @param rmem the memory element to free
- */
-void
-mem_free(void *rmem)
-{
-  struct memp_malloc_helper *hmem;
-
-  LWIP_ASSERT("rmem != NULL", (rmem != NULL));
-  LWIP_ASSERT("rmem == MEM_ALIGN(rmem)", (rmem == LWIP_MEM_ALIGN(rmem)));
-
-  /* get the original struct memp_malloc_helper */
-  hmem = (struct memp_malloc_helper*)(void*)((u8_t*)rmem - LWIP_MEM_ALIGN_SIZE(sizeof(struct memp_malloc_helper)));
-
-  LWIP_ASSERT("hmem != NULL", (hmem != NULL));
-  LWIP_ASSERT("hmem == MEM_ALIGN(hmem)", (hmem == LWIP_MEM_ALIGN(hmem)));
-  LWIP_ASSERT("hmem->poolnr < MEMP_MAX", (hmem->poolnr < MEMP_MAX));
-
-  /* and put it in the pool we saved earlier */
-  memp_free(hmem->poolnr, hmem);
-}
-
-#else /* MEM_USE_POOLS */
-/* lwIP replacement for your libc malloc() */
-
-/**
- * The heap is made up as a list of structs of this type.
- * This does not have to be aligned since for getting its size,
- * we only use the macro SIZEOF_STRUCT_MEM, which automatically alignes.
- */
-struct mem {
-  /** index (-> ram[next]) of the next struct */
-  mem_size_t next;
-  /** index (-> ram[prev]) of the previous struct */
-  mem_size_t prev;
-  /** 1: this area is used; 0: this area is unused */
-  u8_t used;
-};
-
-/** All allocated blocks will be MIN_SIZE bytes big, at least!
- * MIN_SIZE can be overridden to suit your needs. Smaller values save space,
- * larger values could prevent too small blocks to fragment the RAM too much. */
-#ifndef MIN_SIZE
-#define MIN_SIZE             12
-#endif /* MIN_SIZE */
-/* some alignment macros: we define them here for better source code layout */
-#define MIN_SIZE_ALIGNED     LWIP_MEM_ALIGN_SIZE(MIN_SIZE)
-#define SIZEOF_STRUCT_MEM    LWIP_MEM_ALIGN_SIZE(sizeof(struct mem))
-#define MEM_SIZE_ALIGNED     LWIP_MEM_ALIGN_SIZE(MEM_SIZE)
-
-/** If you want to relocate the heap to external memory, simply define
- * LWIP_RAM_HEAP_POINTER as a void-pointer to that location.
- * If so, make sure the memory at that location is big enough (see below on
- * how that space is calculated). */
-#ifndef LWIP_RAM_HEAP_POINTER
-/** the heap. we need one struct mem at the end and some room for alignment */
-u8_t ram_heap[MEM_SIZE_ALIGNED + (2*SIZEOF_STRUCT_MEM) + MEM_ALIGNMENT];
-#define LWIP_RAM_HEAP_POINTER ram_heap
-#endif /* LWIP_RAM_HEAP_POINTER */
-
-/** pointer to the heap (ram_heap): for alignment, ram is now a pointer instead of an array */
-static u8_t *ram;
-/** the last entry, always unused! */
-static struct mem *ram_end;
-/** pointer to the lowest free block, this is used for faster search */
-static struct mem *lfree;
-
-/** concurrent access protection */
-#if !NO_SYS
-static sys_mutex_t mem_mutex;
-#endif
-
-#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
-
-static volatile u8_t mem_free_count;
-
-/* Allow mem_free from other (e.g. interrupt) context */
-#define LWIP_MEM_FREE_DECL_PROTECT()  SYS_ARCH_DECL_PROTECT(lev_free)
-#define LWIP_MEM_FREE_PROTECT()       SYS_ARCH_PROTECT(lev_free)
-#define LWIP_MEM_FREE_UNPROTECT()     SYS_ARCH_UNPROTECT(lev_free)
-#define LWIP_MEM_ALLOC_DECL_PROTECT() SYS_ARCH_DECL_PROTECT(lev_alloc)
-#define LWIP_MEM_ALLOC_PROTECT()      SYS_ARCH_PROTECT(lev_alloc)
-#define LWIP_MEM_ALLOC_UNPROTECT()    SYS_ARCH_UNPROTECT(lev_alloc)
-
-#else /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
-
-/* Protect the heap only by using a semaphore */
-#define LWIP_MEM_FREE_DECL_PROTECT()
-#define LWIP_MEM_FREE_PROTECT()    sys_mutex_lock(&mem_mutex)
-#define LWIP_MEM_FREE_UNPROTECT()  sys_mutex_unlock(&mem_mutex)
-/* mem_malloc is protected using semaphore AND LWIP_MEM_ALLOC_PROTECT */
-#define LWIP_MEM_ALLOC_DECL_PROTECT()
-#define LWIP_MEM_ALLOC_PROTECT()
-#define LWIP_MEM_ALLOC_UNPROTECT()
-
-#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
-
-
-/**
- * "Plug holes" by combining adjacent empty struct mems.
- * After this function is through, there should not exist
- * one empty struct mem pointing to another empty struct mem.
- *
- * @param mem this points to a struct mem which just has been freed
- * @internal this function is only called by mem_free() and mem_trim()
- *
- * This assumes access to the heap is protected by the calling function
- * already.
- */
-static void
-plug_holes(struct mem *mem)
-{
-  struct mem *nmem;
-  struct mem *pmem;
-
-  LWIP_ASSERT("plug_holes: mem >= ram", (u8_t *)mem >= ram);
-  LWIP_ASSERT("plug_holes: mem < ram_end", (u8_t *)mem < (u8_t *)ram_end);
-  LWIP_ASSERT("plug_holes: mem->used == 0", mem->used == 0);
-
-  /* plug hole forward */
-  LWIP_ASSERT("plug_holes: mem->next <= MEM_SIZE_ALIGNED", mem->next <= MEM_SIZE_ALIGNED);
-
-  nmem = (struct mem *)(void *)&ram[mem->next];
-  if (mem != nmem && nmem->used == 0 && (u8_t *)nmem != (u8_t *)ram_end) {
-    /* if mem->next is unused and not end of ram, combine mem and mem->next */
-    if (lfree == nmem) {
-      lfree = mem;
-    }
-    mem->next = nmem->next;
-    ((struct mem *)(void *)&ram[nmem->next])->prev = (mem_size_t)((u8_t *)mem - ram);
-  }
-
-  /* plug hole backward */
-  pmem = (struct mem *)(void *)&ram[mem->prev];
-  if (pmem != mem && pmem->used == 0) {
-    /* if mem->prev is unused, combine mem and mem->prev */
-    if (lfree == mem) {
-      lfree = pmem;
-    }
-    pmem->next = mem->next;
-    ((struct mem *)(void *)&ram[mem->next])->prev = (mem_size_t)((u8_t *)pmem - ram);
-  }
-}
-
-/**
- * Zero the heap and initialize start, end and lowest-free
- */
-void
-mem_init(void)
-{
-  struct mem *mem;
-
-  LWIP_ASSERT("Sanity check alignment",
-    (SIZEOF_STRUCT_MEM & (MEM_ALIGNMENT-1)) == 0);
-
-  /* align the heap */
-  ram = (u8_t *)LWIP_MEM_ALIGN(LWIP_RAM_HEAP_POINTER);
-  /* initialize the start of the heap */
-  mem = (struct mem *)(void *)ram;
-  mem->next = MEM_SIZE_ALIGNED;
-  mem->prev = 0;
-  mem->used = 0;
-  /* initialize the end of the heap */
-  ram_end = (struct mem *)(void *)&ram[MEM_SIZE_ALIGNED];
-  ram_end->used = 1;
-  ram_end->next = MEM_SIZE_ALIGNED;
-  ram_end->prev = MEM_SIZE_ALIGNED;
-
-  /* initialize the lowest-free pointer to the start of the heap */
-  lfree = (struct mem *)(void *)ram;
-
-  MEM_STATS_AVAIL(avail, MEM_SIZE_ALIGNED);
-
-  if(sys_mutex_new(&mem_mutex) != ERR_OK) {
-    LWIP_ASSERT("failed to create mem_mutex", 0);
-  }
-}
-
-/**
- * Put a struct mem back on the heap
- *
- * @param rmem is the data portion of a struct mem as returned by a previous
- *             call to mem_malloc()
- */
-void
-mem_free(void *rmem)
-{
-  struct mem *mem;
-  LWIP_MEM_FREE_DECL_PROTECT();
-
-  if (rmem == NULL) {
-    LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("mem_free(p == NULL) was called.\n"));
-    return;
-  }
-  LWIP_ASSERT("mem_free: sanity check alignment", (((mem_ptr_t)rmem) & (MEM_ALIGNMENT-1)) == 0);
-
-  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) {
-    SYS_ARCH_DECL_PROTECT(lev);
-    LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SEVERE, ("mem_free: illegal memory\n"));
-    /* protect mem stats from concurrent access */
-    SYS_ARCH_PROTECT(lev);
-    MEM_STATS_INC(illegal);
-    SYS_ARCH_UNPROTECT(lev);
-    return;
-  }
-  /* protect the heap from concurrent access */
-  LWIP_MEM_FREE_PROTECT();
-  /* Get the corresponding struct mem ... */
-  mem = (struct mem *)(void *)((u8_t *)rmem - SIZEOF_STRUCT_MEM);
-  /* ... which has to be in a used state ... */
-  LWIP_ASSERT("mem_free: mem->used", mem->used);
-  /* ... and is now unused. */
-  mem->used = 0;
-
-  if (mem < lfree) {
-    /* the newly freed struct is now the lowest */
-    lfree = mem;
-  }
-
-  MEM_STATS_DEC_USED(used, mem->next - (mem_size_t)(((u8_t *)mem - ram)));
-
-  /* finally, see if prev or next are free also */
-  plug_holes(mem);
-#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
-  mem_free_count = 1;
-#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
-  LWIP_MEM_FREE_UNPROTECT();
-}
-
-/**
- * Shrink memory returned by mem_malloc().
- *
- * @param rmem pointer to memory allocated by mem_malloc the is to be shrinked
- * @param newsize required size after shrinking (needs to be smaller than or
- *                equal to the previous size)
- * @return for compatibility reasons: is always == rmem, at the moment
- *         or NULL if newsize is > old size, in which case rmem is NOT touched
- *         or freed!
- */
-void *
-mem_trim(void *rmem, mem_size_t newsize)
-{
-  mem_size_t size;
-  mem_size_t ptr, ptr2;
-  struct mem *mem, *mem2;
-  /* use the FREE_PROTECT here: it protects with sem OR SYS_ARCH_PROTECT */
-  LWIP_MEM_FREE_DECL_PROTECT();
-
-  /* Expand the size of the allocated memory region so that we can
-     adjust for alignment. */
-  newsize = LWIP_MEM_ALIGN_SIZE(newsize);
-
-  if(newsize < MIN_SIZE_ALIGNED) {
-    /* every data block must be at least MIN_SIZE_ALIGNED long */
-    newsize = MIN_SIZE_ALIGNED;
-  }
-
-  if (newsize > MEM_SIZE_ALIGNED) {
-    return NULL;
-  }
-
-  LWIP_ASSERT("mem_trim: legal memory", (u8_t *)rmem >= (u8_t *)ram &&
-   (u8_t *)rmem < (u8_t *)ram_end);
-
-  if ((u8_t *)rmem < (u8_t *)ram || (u8_t *)rmem >= (u8_t *)ram_end) {
-    SYS_ARCH_DECL_PROTECT(lev);
-    LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SEVERE, ("mem_trim: illegal memory\n"));
-    /* protect mem stats from concurrent access */
-    SYS_ARCH_PROTECT(lev);
-    MEM_STATS_INC(illegal);
-    SYS_ARCH_UNPROTECT(lev);
-    return rmem;
-  }
-  /* Get the corresponding struct mem ... */
-  mem = (struct mem *)(void *)((u8_t *)rmem - SIZEOF_STRUCT_MEM);
-  /* ... and its offset pointer */
-  ptr = (mem_size_t)((u8_t *)mem - ram);
-
-  size = mem->next - ptr - SIZEOF_STRUCT_MEM;
-  LWIP_ASSERT("mem_trim can only shrink memory", newsize <= size);
-  if (newsize > size) {
-    /* not supported */
-    return NULL;
-  }
-  if (newsize == size) {
-    /* No change in size, simply return */
-    return rmem;
-  }
-
-  /* protect the heap from concurrent access */
-  LWIP_MEM_FREE_PROTECT();
-
-  mem2 = (struct mem *)(void *)&ram[mem->next];
-  if(mem2->used == 0) {
-    /* The next struct is unused, we can simply move it at little */
-    mem_size_t next;
-    /* remember the old next pointer */
-    next = mem2->next;
-    /* create new struct mem which is moved directly after the shrinked mem */
-    ptr2 = ptr + SIZEOF_STRUCT_MEM + newsize;
-    if (lfree == mem2) {
-      lfree = (struct mem *)(void *)&ram[ptr2];
-    }
-    mem2 = (struct mem *)(void *)&ram[ptr2];
-    mem2->used = 0;
-    /* restore the next pointer */
-    mem2->next = next;
-    /* link it back to mem */
-    mem2->prev = ptr;
-    /* link mem to it */
-    mem->next = ptr2;
-    /* last thing to restore linked list: as we have moved mem2,
-     * let 'mem2->next->prev' point to mem2 again. but only if mem2->next is not
-     * the end of the heap */
-    if (mem2->next != MEM_SIZE_ALIGNED) {
-      ((struct mem *)(void *)&ram[mem2->next])->prev = ptr2;
-    }
-    MEM_STATS_DEC_USED(used, (size - newsize));
-    /* no need to plug holes, we've already done that */
-  } else if (newsize + SIZEOF_STRUCT_MEM + MIN_SIZE_ALIGNED <= size) {
-    /* Next struct is used but there's room for another struct mem with
-     * at least MIN_SIZE_ALIGNED of data.
-     * Old size ('size') must be big enough to contain at least 'newsize' plus a struct mem
-     * ('SIZEOF_STRUCT_MEM') with some data ('MIN_SIZE_ALIGNED').
-     * @todo we could leave out MIN_SIZE_ALIGNED. We would create an empty
-     *       region that couldn't hold data, but when mem->next gets freed,
-     *       the 2 regions would be combined, resulting in more free memory */
-    ptr2 = ptr + SIZEOF_STRUCT_MEM + newsize;
-    mem2 = (struct mem *)(void *)&ram[ptr2];
-    if (mem2 < lfree) {
-      lfree = mem2;
-    }
-    mem2->used = 0;
-    mem2->next = mem->next;
-    mem2->prev = ptr;
-    mem->next = ptr2;
-    if (mem2->next != MEM_SIZE_ALIGNED) {
-      ((struct mem *)(void *)&ram[mem2->next])->prev = ptr2;
-    }
-    MEM_STATS_DEC_USED(used, (size - newsize));
-    /* the original mem->next is used, so no need to plug holes! */
-  }
-  /* else {
-    next struct mem is used but size between mem and mem2 is not big enough
-    to create another struct mem
-    -> don't do anyhting. 
-    -> the remaining space stays unused since it is too small
-  } */
-#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
-  mem_free_count = 1;
-#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
-  LWIP_MEM_FREE_UNPROTECT();
-  return rmem;
-}
-
-/**
- * Adam's mem_malloc() plus solution for bug #17922
- * Allocate a block of memory with a minimum of 'size' bytes.
- *
- * @param size is the minimum size of the requested block in bytes.
- * @return pointer to allocated memory or NULL if no free memory was found.
- *
- * Note that the returned value will always be aligned (as defined by MEM_ALIGNMENT).
- */
-void *
-mem_malloc(mem_size_t size)
-{
-  mem_size_t ptr, ptr2;
-  struct mem *mem, *mem2;
-#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
-  u8_t local_mem_free_count = 0;
-#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
-  LWIP_MEM_ALLOC_DECL_PROTECT();
-
-  if (size == 0) {
-    return NULL;
-  }
-
-  /* Expand the size of the allocated memory region so that we can
-     adjust for alignment. */
-  size = LWIP_MEM_ALIGN_SIZE(size);
-
-  if(size < MIN_SIZE_ALIGNED) {
-    /* every data block must be at least MIN_SIZE_ALIGNED long */
-    size = MIN_SIZE_ALIGNED;
-  }
-
-  if (size > MEM_SIZE_ALIGNED) {
-    return NULL;
-  }
-
-  /* protect the heap from concurrent access */
-  sys_mutex_lock(&mem_mutex);
-  LWIP_MEM_ALLOC_PROTECT();
-#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
-  /* run as long as a mem_free disturbed mem_malloc or mem_trim */
-  do {
-    local_mem_free_count = 0;
-#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
-
-    /* Scan through the heap searching for a free block that is big enough,
-     * beginning with the lowest free block.
-     */
-    for (ptr = (mem_size_t)((u8_t *)lfree - ram); ptr < MEM_SIZE_ALIGNED - size;
-         ptr = ((struct mem *)(void *)&ram[ptr])->next) {
-      mem = (struct mem *)(void *)&ram[ptr];
-#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
-      mem_free_count = 0;
-      LWIP_MEM_ALLOC_UNPROTECT();
-      /* allow mem_free or mem_trim to run */
-      LWIP_MEM_ALLOC_PROTECT();
-      if (mem_free_count != 0) {
-        /* If mem_free or mem_trim have run, we have to restart since they
-           could have altered our current struct mem. */
-        local_mem_free_count = 1;
-        break;
-      }
-#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
-
-      if ((!mem->used) &&
-          (mem->next - (ptr + SIZEOF_STRUCT_MEM)) >= size) {
-        /* mem is not used and at least perfect fit is possible:
-         * mem->next - (ptr + SIZEOF_STRUCT_MEM) gives us the 'user data size' of mem */
-
-        if (mem->next - (ptr + SIZEOF_STRUCT_MEM) >= (size + SIZEOF_STRUCT_MEM + MIN_SIZE_ALIGNED)) {
-          /* (in addition to the above, we test if another struct mem (SIZEOF_STRUCT_MEM) containing
-           * at least MIN_SIZE_ALIGNED of data also fits in the 'user data space' of 'mem')
-           * -> split large block, create empty remainder,
-           * remainder must be large enough to contain MIN_SIZE_ALIGNED data: if
-           * mem->next - (ptr + (2*SIZEOF_STRUCT_MEM)) == size,
-           * struct mem would fit in but no data between mem2 and mem2->next
-           * @todo we could leave out MIN_SIZE_ALIGNED. We would create an empty
-           *       region that couldn't hold data, but when mem->next gets freed,
-           *       the 2 regions would be combined, resulting in more free memory
-           */
-          ptr2 = ptr + SIZEOF_STRUCT_MEM + size;
-          /* create mem2 struct */
-          mem2 = (struct mem *)(void *)&ram[ptr2];
-          mem2->used = 0;
-          mem2->next = mem->next;
-          mem2->prev = ptr;
-          /* and insert it between mem and mem->next */
-          mem->next = ptr2;
-          mem->used = 1;
-
-          if (mem2->next != MEM_SIZE_ALIGNED) {
-            ((struct mem *)(void *)&ram[mem2->next])->prev = ptr2;
-          }
-          MEM_STATS_INC_USED(used, (size + SIZEOF_STRUCT_MEM));
-        } else {
-          /* (a mem2 struct does no fit into the user data space of mem and mem->next will always
-           * be used at this point: if not we have 2 unused structs in a row, plug_holes should have
-           * take care of this).
-           * -> near fit or excact fit: do not split, no mem2 creation
-           * also can't move mem->next directly behind mem, since mem->next
-           * will always be used at this point!
-           */
-          mem->used = 1;
-          MEM_STATS_INC_USED(used, mem->next - (mem_size_t)((u8_t *)mem - ram));
-        }
-#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
-mem_malloc_adjust_lfree:
-#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
-        if (mem == lfree) {
-          struct mem *cur = lfree;
-          /* Find next free block after mem and update lowest free pointer */
-          while (cur->used && cur != ram_end) {
-#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
-            mem_free_count = 0;
-            LWIP_MEM_ALLOC_UNPROTECT();
-            /* prevent high interrupt latency... */
-            LWIP_MEM_ALLOC_PROTECT();
-            if (mem_free_count != 0) {
-              /* If mem_free or mem_trim have run, we have to restart since they
-                 could have altered our current struct mem or lfree. */
-              goto mem_malloc_adjust_lfree;
-            }
-#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
-            cur = (struct mem *)(void *)&ram[cur->next];
-          }
-          lfree = cur;
-          LWIP_ASSERT("mem_malloc: !lfree->used", ((lfree == ram_end) || (!lfree->used)));
-        }
-        LWIP_MEM_ALLOC_UNPROTECT();
-        sys_mutex_unlock(&mem_mutex);
-        LWIP_ASSERT("mem_malloc: allocated memory not above ram_end.",
-         (mem_ptr_t)mem + SIZEOF_STRUCT_MEM + size <= (mem_ptr_t)ram_end);
-        LWIP_ASSERT("mem_malloc: allocated memory properly aligned.",
-         ((mem_ptr_t)mem + SIZEOF_STRUCT_MEM) % MEM_ALIGNMENT == 0);
-        LWIP_ASSERT("mem_malloc: sanity check alignment",
-          (((mem_ptr_t)mem) & (MEM_ALIGNMENT-1)) == 0);
-
-        return (u8_t *)mem + SIZEOF_STRUCT_MEM;
-      }
-    }
-#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
-    /* if we got interrupted by a mem_free, try again */
-  } while(local_mem_free_count != 0);
-#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
-  LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("mem_malloc: could not allocate %"S16_F" bytes\n", (s16_t)size));
-  MEM_STATS_INC(err);
-  LWIP_MEM_ALLOC_UNPROTECT();
-  sys_mutex_unlock(&mem_mutex);
-  return NULL;
-}
-
-#endif /* MEM_USE_POOLS */
-/**
- * Contiguously allocates enough space for count objects that are size bytes
- * of memory each and returns a pointer to the allocated memory.
- *
- * The allocated memory is filled with bytes of value zero.
- *
- * @param count number of objects to allocate
- * @param size size of the objects to allocate
- * @return pointer to allocated memory / NULL pointer if there is an error
- */
-void *mem_calloc(mem_size_t count, mem_size_t size)
-{
-  void *p;
-
-  /* allocate 'count' objects of size 'size' */
-  p = mem_malloc(count * size);
-  if (p) {
-    /* zero the memory */
-    memset(p, 0, count * size);
-  }
-  return p;
-}
-
-#endif /* !MEM_LIBC_MALLOC */
+/**
+ * @file
+ * Dynamic memory manager
+ *
+ * This is a lightweight replacement for the standard C library malloc().
+ *
+ * If you want to use the standard C library malloc() instead, define
+ * MEM_LIBC_MALLOC to 1 in your lwipopts.h
+ *
+ * To let mem_malloc() use pools (prevents fragmentation and is much faster than
+ * a heap but might waste some memory), define MEM_USE_POOLS to 1, define
+ * MEMP_USE_CUSTOM_POOLS to 1 and create a file "lwippools.h" that includes a list
+ * of pools like this (more pools can be added between _START and _END):
+ *
+ * Define three pools with sizes 256, 512, and 1512 bytes
+ * LWIP_MALLOC_MEMPOOL_START
+ * LWIP_MALLOC_MEMPOOL(20, 256)
+ * LWIP_MALLOC_MEMPOOL(10, 512)
+ * LWIP_MALLOC_MEMPOOL(5, 1512)
+ * LWIP_MALLOC_MEMPOOL_END
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *         Simon Goldschmidt
+ *
+ */
+
+#include "lwip/opt.h"
+#include "lwip/mem.h"
+#include "lwip/def.h"
+#include "lwip/sys.h"
+#include "lwip/stats.h"
+#include "lwip/err.h"
+
+#include <string.h>
+
+#if MEM_LIBC_MALLOC
+#include <stdlib.h> /* for malloc()/free() */
+#endif
+
+#if MEM_LIBC_MALLOC || MEM_USE_POOLS
+
+/** mem_init is not used when using pools instead of a heap or using
+ * C library malloc().
+ */
+void
+mem_init(void)
+{
+}
+
+/** mem_trim is not used when using pools instead of a heap or using
+ * C library malloc(): we can't free part of a pool element and the stack
+ * support mem_trim() to return a different pointer
+ */
+void*
+mem_trim(void *mem, mem_size_t size)
+{
+  LWIP_UNUSED_ARG(size);
+  return mem;
+}
+#endif /* MEM_LIBC_MALLOC || MEM_USE_POOLS */
+
+#if MEM_LIBC_MALLOC
+/* lwIP heap implemented using C library malloc() */
+
+/* in case C library malloc() needs extra protection,
+ * allow these defines to be overridden.
+ */
+#ifndef mem_clib_free
+#define mem_clib_free free
+#endif
+#ifndef mem_clib_malloc
+#define mem_clib_malloc malloc
+#endif
+#ifndef mem_clib_calloc
+#define mem_clib_calloc calloc
+#endif
+
+#if LWIP_STATS && MEM_STATS
+#define MEM_LIBC_STATSHELPER_SIZE LWIP_MEM_ALIGN_SIZE(sizeof(mem_size_t))
+#else
+#define MEM_LIBC_STATSHELPER_SIZE 0
+#endif
+
+/**
+ * Allocate a block of memory with a minimum of 'size' bytes.
+ *
+ * @param size is the minimum size of the requested block in bytes.
+ * @return pointer to allocated memory or NULL if no free memory was found.
+ *
+ * Note that the returned value must always be aligned (as defined by MEM_ALIGNMENT).
+ */
+void *
+mem_malloc(mem_size_t size)
+{
+  void* ret = mem_clib_malloc(size + MEM_LIBC_STATSHELPER_SIZE);
+  if (ret == NULL) {
+    MEM_STATS_INC(err);
+  } else {
+    LWIP_ASSERT("malloc() must return aligned memory", LWIP_MEM_ALIGN(ret) == ret);
+#if LWIP_STATS && MEM_STATS
+    *(mem_size_t*)ret = size;
+    ret = (u8_t*)ret + MEM_LIBC_STATSHELPER_SIZE;
+    MEM_STATS_INC_USED(used, size);
+#endif
+  }
+  return ret;
+}
+
+/** Put memory back on the heap
+ *
+ * @param rmem is the pointer as returned by a previous call to mem_malloc()
+ */
+void
+mem_free(void *rmem)
+{
+  LWIP_ASSERT("rmem != NULL", (rmem != NULL));
+  LWIP_ASSERT("rmem == MEM_ALIGN(rmem)", (rmem == LWIP_MEM_ALIGN(rmem)));
+#if LWIP_STATS && MEM_STATS
+  rmem = (u8_t*)rmem - MEM_LIBC_STATSHELPER_SIZE;
+  MEM_STATS_DEC_USED(used, *(mem_size_t*)rmem);
+#endif
+  mem_clib_free(rmem);
+}
+
+#elif MEM_USE_POOLS
+
+/* lwIP heap implemented with different sized pools */
+
+/**
+ * Allocate memory: determine the smallest pool that is big enough
+ * to contain an element of 'size' and get an element from that pool.
+ *
+ * @param size the size in bytes of the memory needed
+ * @return a pointer to the allocated memory or NULL if the pool is empty
+ */
+void *
+mem_malloc(mem_size_t size)
+{
+  void *ret;
+  struct memp_malloc_helper *element = NULL;
+  memp_t poolnr;
+  mem_size_t required_size = size + LWIP_MEM_ALIGN_SIZE(sizeof(struct memp_malloc_helper));
+
+  for (poolnr = MEMP_POOL_FIRST; poolnr <= MEMP_POOL_LAST; poolnr = (memp_t)(poolnr + 1)) {
+    /* is this pool big enough to hold an element of the required size
+       plus a struct memp_malloc_helper that saves the pool this element came from? */
+    if (required_size <= memp_pools[poolnr]->size) {
+      element = (struct memp_malloc_helper*)memp_malloc(poolnr);
+      if (element == NULL) {
+        /* No need to DEBUGF or ASSERT: This error is already taken care of in memp.c */
+#if MEM_USE_POOLS_TRY_BIGGER_POOL
+        /** Try a bigger pool if this one is empty! */
+        if (poolnr < MEMP_POOL_LAST) {
+          continue;
+        }
+#endif /* MEM_USE_POOLS_TRY_BIGGER_POOL */
+        MEM_STATS_INC(err);
+        return NULL;
+      }
+      break;
+    }
+  }
+  if (poolnr > MEMP_POOL_LAST) {
+    LWIP_ASSERT("mem_malloc(): no pool is that big!", 0);
+    MEM_STATS_INC(err);
+    return NULL;
+  }
+
+  /* save the pool number this element came from */
+  element->poolnr = poolnr;
+  /* and return a pointer to the memory directly after the struct memp_malloc_helper */
+  ret = (u8_t*)element + LWIP_MEM_ALIGN_SIZE(sizeof(struct memp_malloc_helper));
+
+#if MEMP_OVERFLOW_CHECK || (LWIP_STATS && MEM_STATS)
+  /* truncating to u16_t is safe because struct memp_desc::size is u16_t */
+  element->size = (u16_t)size;
+  MEM_STATS_INC_USED(used, element->size);
+#endif /* MEMP_OVERFLOW_CHECK || (LWIP_STATS && MEM_STATS) */
+#if MEMP_OVERFLOW_CHECK
+  /* initialize unused memory (diff between requested size and selected pool's size) */
+  memset((u8_t*)ret + size, 0xcd, memp_pools[poolnr]->size - size);
+#endif /* MEMP_OVERFLOW_CHECK */
+  return ret;
+}
+
+/**
+ * Free memory previously allocated by mem_malloc. Loads the pool number
+ * and calls memp_free with that pool number to put the element back into
+ * its pool
+ *
+ * @param rmem the memory element to free
+ */
+void
+mem_free(void *rmem)
+{
+  struct memp_malloc_helper *hmem;
+
+  LWIP_ASSERT("rmem != NULL", (rmem != NULL));
+  LWIP_ASSERT("rmem == MEM_ALIGN(rmem)", (rmem == LWIP_MEM_ALIGN(rmem)));
+
+  /* get the original struct memp_malloc_helper */
+  /* cast through void* to get rid of alignment warnings */
+  hmem = (struct memp_malloc_helper*)(void*)((u8_t*)rmem - LWIP_MEM_ALIGN_SIZE(sizeof(struct memp_malloc_helper)));
+
+  LWIP_ASSERT("hmem != NULL", (hmem != NULL));
+  LWIP_ASSERT("hmem == MEM_ALIGN(hmem)", (hmem == LWIP_MEM_ALIGN(hmem)));
+  LWIP_ASSERT("hmem->poolnr < MEMP_MAX", (hmem->poolnr < MEMP_MAX));
+
+  MEM_STATS_DEC_USED(used, hmem->size);
+#if MEMP_OVERFLOW_CHECK
+  {
+     u16_t i;
+     LWIP_ASSERT("MEM_USE_POOLS: invalid chunk size",
+        hmem->size <= memp_pools[hmem->poolnr]->size);
+     /* check that unused memory remained untouched (diff between requested size and selected pool's size) */
+     for (i = hmem->size; i < memp_pools[hmem->poolnr]->size; i++) {
+        u8_t data = *((u8_t*)rmem + i);
+        LWIP_ASSERT("MEM_USE_POOLS: mem overflow detected", data == 0xcd);
+     }
+  }
+#endif /* MEMP_OVERFLOW_CHECK */
+
+  /* and put it in the pool we saved earlier */
+  memp_free(hmem->poolnr, hmem);
+}
+
+#else /* MEM_USE_POOLS */
+/* lwIP replacement for your libc malloc() */
+
+/**
+ * The heap is made up as a list of structs of this type.
+ * This does not have to be aligned since for getting its size,
+ * we only use the macro SIZEOF_STRUCT_MEM, which automatically aligns.
+ */
+struct mem {
+  /** index (-> ram[next]) of the next struct */
+  mem_size_t next;
+  /** index (-> ram[prev]) of the previous struct */
+  mem_size_t prev;
+  /** 1: this area is used; 0: this area is unused */
+  u8_t used;
+};
+
+/** All allocated blocks will be MIN_SIZE bytes big, at least!
+ * MIN_SIZE can be overridden to suit your needs. Smaller values save space,
+ * larger values could prevent too small blocks to fragment the RAM too much. */
+#ifndef MIN_SIZE
+#define MIN_SIZE             12
+#endif /* MIN_SIZE */
+/* some alignment macros: we define them here for better source code layout */
+#define MIN_SIZE_ALIGNED     LWIP_MEM_ALIGN_SIZE(MIN_SIZE)
+#define SIZEOF_STRUCT_MEM    LWIP_MEM_ALIGN_SIZE(sizeof(struct mem))
+#define MEM_SIZE_ALIGNED     LWIP_MEM_ALIGN_SIZE(MEM_SIZE)
+
+/** If you want to relocate the heap to external memory, simply define
+ * LWIP_RAM_HEAP_POINTER as a void-pointer to that location.
+ * If so, make sure the memory at that location is big enough (see below on
+ * how that space is calculated). */
+#ifndef LWIP_RAM_HEAP_POINTER
+/** the heap. we need one struct mem at the end and some room for alignment */
+LWIP_DECLARE_MEMORY_ALIGNED(ram_heap, MEM_SIZE_ALIGNED + (2U*SIZEOF_STRUCT_MEM));
+#define LWIP_RAM_HEAP_POINTER ram_heap
+#endif /* LWIP_RAM_HEAP_POINTER */
+
+/** pointer to the heap (ram_heap): for alignment, ram is now a pointer instead of an array */
+static u8_t *ram;
+/** the last entry, always unused! */
+static struct mem *ram_end;
+/** pointer to the lowest free block, this is used for faster search */
+static struct mem *lfree;
+
+/** concurrent access protection */
+#if !NO_SYS
+static sys_mutex_t mem_mutex;
+#endif
+
+#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
+
+static volatile u8_t mem_free_count;
+
+/* Allow mem_free from other (e.g. interrupt) context */
+#define LWIP_MEM_FREE_DECL_PROTECT()  SYS_ARCH_DECL_PROTECT(lev_free)
+#define LWIP_MEM_FREE_PROTECT()       SYS_ARCH_PROTECT(lev_free)
+#define LWIP_MEM_FREE_UNPROTECT()     SYS_ARCH_UNPROTECT(lev_free)
+#define LWIP_MEM_ALLOC_DECL_PROTECT() SYS_ARCH_DECL_PROTECT(lev_alloc)
+#define LWIP_MEM_ALLOC_PROTECT()      SYS_ARCH_PROTECT(lev_alloc)
+#define LWIP_MEM_ALLOC_UNPROTECT()    SYS_ARCH_UNPROTECT(lev_alloc)
+
+#else /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
+
+/* Protect the heap only by using a semaphore */
+#define LWIP_MEM_FREE_DECL_PROTECT()
+#define LWIP_MEM_FREE_PROTECT()    sys_mutex_lock(&mem_mutex)
+#define LWIP_MEM_FREE_UNPROTECT()  sys_mutex_unlock(&mem_mutex)
+/* mem_malloc is protected using semaphore AND LWIP_MEM_ALLOC_PROTECT */
+#define LWIP_MEM_ALLOC_DECL_PROTECT()
+#define LWIP_MEM_ALLOC_PROTECT()
+#define LWIP_MEM_ALLOC_UNPROTECT()
+
+#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
+
+
+/**
+ * "Plug holes" by combining adjacent empty struct mems.
+ * After this function is through, there should not exist
+ * one empty struct mem pointing to another empty struct mem.
+ *
+ * @param mem this points to a struct mem which just has been freed
+ * @internal this function is only called by mem_free() and mem_trim()
+ *
+ * This assumes access to the heap is protected by the calling function
+ * already.
+ */
+static void
+plug_holes(struct mem *mem)
+{
+  struct mem *nmem;
+  struct mem *pmem;
+
+  LWIP_ASSERT("plug_holes: mem >= ram", (u8_t *)mem >= ram);
+  LWIP_ASSERT("plug_holes: mem < ram_end", (u8_t *)mem < (u8_t *)ram_end);
+  LWIP_ASSERT("plug_holes: mem->used == 0", mem->used == 0);
+
+  /* plug hole forward */
+  LWIP_ASSERT("plug_holes: mem->next <= MEM_SIZE_ALIGNED", mem->next <= MEM_SIZE_ALIGNED);
+
+  nmem = (struct mem *)(void *)&ram[mem->next];
+  if (mem != nmem && nmem->used == 0 && (u8_t *)nmem != (u8_t *)ram_end) {
+    /* if mem->next is unused and not end of ram, combine mem and mem->next */
+    if (lfree == nmem) {
+      lfree = mem;
+    }
+    mem->next = nmem->next;
+    ((struct mem *)(void *)&ram[nmem->next])->prev = (mem_size_t)((u8_t *)mem - ram);
+  }
+
+  /* plug hole backward */
+  pmem = (struct mem *)(void *)&ram[mem->prev];
+  if (pmem != mem && pmem->used == 0) {
+    /* if mem->prev is unused, combine mem and mem->prev */
+    if (lfree == mem) {
+      lfree = pmem;
+    }
+    pmem->next = mem->next;
+    ((struct mem *)(void *)&ram[mem->next])->prev = (mem_size_t)((u8_t *)pmem - ram);
+  }
+}
+
+/**
+ * Zero the heap and initialize start, end and lowest-free
+ */
+void
+mem_init(void)
+{
+  struct mem *mem;
+
+  LWIP_ASSERT("Sanity check alignment",
+    (SIZEOF_STRUCT_MEM & (MEM_ALIGNMENT-1)) == 0);
+
+  /* align the heap */
+  ram = (u8_t *)LWIP_MEM_ALIGN(LWIP_RAM_HEAP_POINTER);
+  /* initialize the start of the heap */
+  mem = (struct mem *)(void *)ram;
+  mem->next = MEM_SIZE_ALIGNED;
+  mem->prev = 0;
+  mem->used = 0;
+  /* initialize the end of the heap */
+  ram_end = (struct mem *)(void *)&ram[MEM_SIZE_ALIGNED];
+  ram_end->used = 1;
+  ram_end->next = MEM_SIZE_ALIGNED;
+  ram_end->prev = MEM_SIZE_ALIGNED;
+
+  /* initialize the lowest-free pointer to the start of the heap */
+  lfree = (struct mem *)(void *)ram;
+
+  MEM_STATS_AVAIL(avail, MEM_SIZE_ALIGNED);
+
+  if (sys_mutex_new(&mem_mutex) != ERR_OK) {
+    LWIP_ASSERT("failed to create mem_mutex", 0);
+  }
+}
+
+/**
+ * Put a struct mem back on the heap
+ *
+ * @param rmem is the data portion of a struct mem as returned by a previous
+ *             call to mem_malloc()
+ */
+void
+mem_free(void *rmem)
+{
+  struct mem *mem;
+  LWIP_MEM_FREE_DECL_PROTECT();
+
+  if (rmem == NULL) {
+    LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("mem_free(p == NULL) was called.\n"));
+    return;
+  }
+  LWIP_ASSERT("mem_free: sanity check alignment", (((mem_ptr_t)rmem) & (MEM_ALIGNMENT-1)) == 0);
+
+  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) {
+    SYS_ARCH_DECL_PROTECT(lev);
+    LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SEVERE, ("mem_free: illegal memory\n"));
+    /* protect mem stats from concurrent access */
+    SYS_ARCH_PROTECT(lev);
+    MEM_STATS_INC(illegal);
+    SYS_ARCH_UNPROTECT(lev);
+    return;
+  }
+  /* protect the heap from concurrent access */
+  LWIP_MEM_FREE_PROTECT();
+  /* Get the corresponding struct mem ... */
+  /* cast through void* to get rid of alignment warnings */
+  mem = (struct mem *)(void *)((u8_t *)rmem - SIZEOF_STRUCT_MEM);
+  /* ... which has to be in a used state ... */
+  LWIP_ASSERT("mem_free: mem->used", mem->used);
+  /* ... and is now unused. */
+  mem->used = 0;
+
+  if (mem < lfree) {
+    /* the newly freed struct is now the lowest */
+    lfree = mem;
+  }
+
+  MEM_STATS_DEC_USED(used, mem->next - (mem_size_t)(((u8_t *)mem - ram)));
+
+  /* finally, see if prev or next are free also */
+  plug_holes(mem);
+#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
+  mem_free_count = 1;
+#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
+  LWIP_MEM_FREE_UNPROTECT();
+}
+
+/**
+ * Shrink memory returned by mem_malloc().
+ *
+ * @param rmem pointer to memory allocated by mem_malloc the is to be shrinked
+ * @param newsize required size after shrinking (needs to be smaller than or
+ *                equal to the previous size)
+ * @return for compatibility reasons: is always == rmem, at the moment
+ *         or NULL if newsize is > old size, in which case rmem is NOT touched
+ *         or freed!
+ */
+void *
+mem_trim(void *rmem, mem_size_t newsize)
+{
+  mem_size_t size;
+  mem_size_t ptr, ptr2;
+  struct mem *mem, *mem2;
+  /* use the FREE_PROTECT here: it protects with sem OR SYS_ARCH_PROTECT */
+  LWIP_MEM_FREE_DECL_PROTECT();
+
+  /* Expand the size of the allocated memory region so that we can
+     adjust for alignment. */
+  newsize = LWIP_MEM_ALIGN_SIZE(newsize);
+
+  if (newsize < MIN_SIZE_ALIGNED) {
+    /* every data block must be at least MIN_SIZE_ALIGNED long */
+    newsize = MIN_SIZE_ALIGNED;
+  }
+
+  if (newsize > MEM_SIZE_ALIGNED) {
+    return NULL;
+  }
+
+  LWIP_ASSERT("mem_trim: legal memory", (u8_t *)rmem >= (u8_t *)ram &&
+   (u8_t *)rmem < (u8_t *)ram_end);
+
+  if ((u8_t *)rmem < (u8_t *)ram || (u8_t *)rmem >= (u8_t *)ram_end) {
+    SYS_ARCH_DECL_PROTECT(lev);
+    LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SEVERE, ("mem_trim: illegal memory\n"));
+    /* protect mem stats from concurrent access */
+    SYS_ARCH_PROTECT(lev);
+    MEM_STATS_INC(illegal);
+    SYS_ARCH_UNPROTECT(lev);
+    return rmem;
+  }
+  /* Get the corresponding struct mem ... */
+  /* cast through void* to get rid of alignment warnings */
+  mem = (struct mem *)(void *)((u8_t *)rmem - SIZEOF_STRUCT_MEM);
+  /* ... and its offset pointer */
+  ptr = (mem_size_t)((u8_t *)mem - ram);
+
+  size = mem->next - ptr - SIZEOF_STRUCT_MEM;
+  LWIP_ASSERT("mem_trim can only shrink memory", newsize <= size);
+  if (newsize > size) {
+    /* not supported */
+    return NULL;
+  }
+  if (newsize == size) {
+    /* No change in size, simply return */
+    return rmem;
+  }
+
+  /* protect the heap from concurrent access */
+  LWIP_MEM_FREE_PROTECT();
+
+  mem2 = (struct mem *)(void *)&ram[mem->next];
+  if (mem2->used == 0) {
+    /* The next struct is unused, we can simply move it at little */
+    mem_size_t next;
+    /* remember the old next pointer */
+    next = mem2->next;
+    /* create new struct mem which is moved directly after the shrinked mem */
+    ptr2 = ptr + SIZEOF_STRUCT_MEM + newsize;
+    if (lfree == mem2) {
+      lfree = (struct mem *)(void *)&ram[ptr2];
+    }
+    mem2 = (struct mem *)(void *)&ram[ptr2];
+    mem2->used = 0;
+    /* restore the next pointer */
+    mem2->next = next;
+    /* link it back to mem */
+    mem2->prev = ptr;
+    /* link mem to it */
+    mem->next = ptr2;
+    /* last thing to restore linked list: as we have moved mem2,
+     * let 'mem2->next->prev' point to mem2 again. but only if mem2->next is not
+     * the end of the heap */
+    if (mem2->next != MEM_SIZE_ALIGNED) {
+      ((struct mem *)(void *)&ram[mem2->next])->prev = ptr2;
+    }
+    MEM_STATS_DEC_USED(used, (size - newsize));
+    /* no need to plug holes, we've already done that */
+  } else if (newsize + SIZEOF_STRUCT_MEM + MIN_SIZE_ALIGNED <= size) {
+    /* Next struct is used but there's room for another struct mem with
+     * at least MIN_SIZE_ALIGNED of data.
+     * Old size ('size') must be big enough to contain at least 'newsize' plus a struct mem
+     * ('SIZEOF_STRUCT_MEM') with some data ('MIN_SIZE_ALIGNED').
+     * @todo we could leave out MIN_SIZE_ALIGNED. We would create an empty
+     *       region that couldn't hold data, but when mem->next gets freed,
+     *       the 2 regions would be combined, resulting in more free memory */
+    ptr2 = ptr + SIZEOF_STRUCT_MEM + newsize;
+    mem2 = (struct mem *)(void *)&ram[ptr2];
+    if (mem2 < lfree) {
+      lfree = mem2;
+    }
+    mem2->used = 0;
+    mem2->next = mem->next;
+    mem2->prev = ptr;
+    mem->next = ptr2;
+    if (mem2->next != MEM_SIZE_ALIGNED) {
+      ((struct mem *)(void *)&ram[mem2->next])->prev = ptr2;
+    }
+    MEM_STATS_DEC_USED(used, (size - newsize));
+    /* the original mem->next is used, so no need to plug holes! */
+  }
+  /* else {
+    next struct mem is used but size between mem and mem2 is not big enough
+    to create another struct mem
+    -> don't do anyhting.
+    -> the remaining space stays unused since it is too small
+  } */
+#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
+  mem_free_count = 1;
+#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
+  LWIP_MEM_FREE_UNPROTECT();
+  return rmem;
+}
+
+/**
+ * Allocate a block of memory with a minimum of 'size' bytes.
+ *
+ * @param size is the minimum size of the requested block in bytes.
+ * @return pointer to allocated memory or NULL if no free memory was found.
+ *
+ * Note that the returned value will always be aligned (as defined by MEM_ALIGNMENT).
+ */
+void *
+mem_malloc(mem_size_t size)
+{
+  mem_size_t ptr, ptr2;
+  struct mem *mem, *mem2;
+#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
+  u8_t local_mem_free_count = 0;
+#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
+  LWIP_MEM_ALLOC_DECL_PROTECT();
+
+  if (size == 0) {
+    return NULL;
+  }
+
+  /* Expand the size of the allocated memory region so that we can
+     adjust for alignment. */
+  size = LWIP_MEM_ALIGN_SIZE(size);
+
+  if (size < MIN_SIZE_ALIGNED) {
+    /* every data block must be at least MIN_SIZE_ALIGNED long */
+    size = MIN_SIZE_ALIGNED;
+  }
+
+  if (size > MEM_SIZE_ALIGNED) {
+    return NULL;
+  }
+
+  /* protect the heap from concurrent access */
+  sys_mutex_lock(&mem_mutex);
+  LWIP_MEM_ALLOC_PROTECT();
+#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
+  /* run as long as a mem_free disturbed mem_malloc or mem_trim */
+  do {
+    local_mem_free_count = 0;
+#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
+
+    /* Scan through the heap searching for a free block that is big enough,
+     * beginning with the lowest free block.
+     */
+    for (ptr = (mem_size_t)((u8_t *)lfree - ram); ptr < MEM_SIZE_ALIGNED - size;
+         ptr = ((struct mem *)(void *)&ram[ptr])->next) {
+      mem = (struct mem *)(void *)&ram[ptr];
+#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
+      mem_free_count = 0;
+      LWIP_MEM_ALLOC_UNPROTECT();
+      /* allow mem_free or mem_trim to run */
+      LWIP_MEM_ALLOC_PROTECT();
+      if (mem_free_count != 0) {
+        /* If mem_free or mem_trim have run, we have to restart since they
+           could have altered our current struct mem. */
+        local_mem_free_count = 1;
+        break;
+      }
+#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
+
+      if ((!mem->used) &&
+          (mem->next - (ptr + SIZEOF_STRUCT_MEM)) >= size) {
+        /* mem is not used and at least perfect fit is possible:
+         * mem->next - (ptr + SIZEOF_STRUCT_MEM) gives us the 'user data size' of mem */
+
+        if (mem->next - (ptr + SIZEOF_STRUCT_MEM) >= (size + SIZEOF_STRUCT_MEM + MIN_SIZE_ALIGNED)) {
+          /* (in addition to the above, we test if another struct mem (SIZEOF_STRUCT_MEM) containing
+           * at least MIN_SIZE_ALIGNED of data also fits in the 'user data space' of 'mem')
+           * -> split large block, create empty remainder,
+           * remainder must be large enough to contain MIN_SIZE_ALIGNED data: if
+           * mem->next - (ptr + (2*SIZEOF_STRUCT_MEM)) == size,
+           * struct mem would fit in but no data between mem2 and mem2->next
+           * @todo we could leave out MIN_SIZE_ALIGNED. We would create an empty
+           *       region that couldn't hold data, but when mem->next gets freed,
+           *       the 2 regions would be combined, resulting in more free memory
+           */
+          ptr2 = ptr + SIZEOF_STRUCT_MEM + size;
+          /* create mem2 struct */
+          mem2 = (struct mem *)(void *)&ram[ptr2];
+          mem2->used = 0;
+          mem2->next = mem->next;
+          mem2->prev = ptr;
+          /* and insert it between mem and mem->next */
+          mem->next = ptr2;
+          mem->used = 1;
+
+          if (mem2->next != MEM_SIZE_ALIGNED) {
+            ((struct mem *)(void *)&ram[mem2->next])->prev = ptr2;
+          }
+          MEM_STATS_INC_USED(used, (size + SIZEOF_STRUCT_MEM));
+        } else {
+          /* (a mem2 struct does no fit into the user data space of mem and mem->next will always
+           * be used at this point: if not we have 2 unused structs in a row, plug_holes should have
+           * take care of this).
+           * -> near fit or exact fit: do not split, no mem2 creation
+           * also can't move mem->next directly behind mem, since mem->next
+           * will always be used at this point!
+           */
+          mem->used = 1;
+          MEM_STATS_INC_USED(used, mem->next - (mem_size_t)((u8_t *)mem - ram));
+        }
+#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
+mem_malloc_adjust_lfree:
+#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
+        if (mem == lfree) {
+          struct mem *cur = lfree;
+          /* Find next free block after mem and update lowest free pointer */
+          while (cur->used && cur != ram_end) {
+#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
+            mem_free_count = 0;
+            LWIP_MEM_ALLOC_UNPROTECT();
+            /* prevent high interrupt latency... */
+            LWIP_MEM_ALLOC_PROTECT();
+            if (mem_free_count != 0) {
+              /* If mem_free or mem_trim have run, we have to restart since they
+                 could have altered our current struct mem or lfree. */
+              goto mem_malloc_adjust_lfree;
+            }
+#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
+            cur = (struct mem *)(void *)&ram[cur->next];
+          }
+          lfree = cur;
+          LWIP_ASSERT("mem_malloc: !lfree->used", ((lfree == ram_end) || (!lfree->used)));
+        }
+        LWIP_MEM_ALLOC_UNPROTECT();
+        sys_mutex_unlock(&mem_mutex);
+        LWIP_ASSERT("mem_malloc: allocated memory not above ram_end.",
+         (mem_ptr_t)mem + SIZEOF_STRUCT_MEM + size <= (mem_ptr_t)ram_end);
+        LWIP_ASSERT("mem_malloc: allocated memory properly aligned.",
+         ((mem_ptr_t)mem + SIZEOF_STRUCT_MEM) % MEM_ALIGNMENT == 0);
+        LWIP_ASSERT("mem_malloc: sanity check alignment",
+          (((mem_ptr_t)mem) & (MEM_ALIGNMENT-1)) == 0);
+
+        return (u8_t *)mem + SIZEOF_STRUCT_MEM;
+      }
+    }
+#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
+    /* if we got interrupted by a mem_free, try again */
+  } while (local_mem_free_count != 0);
+#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
+  LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("mem_malloc: could not allocate %"S16_F" bytes\n", (s16_t)size));
+  MEM_STATS_INC(err);
+  LWIP_MEM_ALLOC_UNPROTECT();
+  sys_mutex_unlock(&mem_mutex);
+  return NULL;
+}
+
+#endif /* MEM_USE_POOLS */
+
+#if MEM_LIBC_MALLOC && (!LWIP_STATS || !MEM_STATS)
+void *
+mem_calloc(mem_size_t count, mem_size_t size)
+{
+  return mem_clib_calloc(count, size);
+}
+
+#else /* MEM_LIBC_MALLOC && (!LWIP_STATS || !MEM_STATS) */
+/**
+ * Contiguously allocates enough space for count objects that are size bytes
+ * of memory each and returns a pointer to the allocated memory.
+ *
+ * The allocated memory is filled with bytes of value zero.
+ *
+ * @param count number of objects to allocate
+ * @param size size of the objects to allocate
+ * @return pointer to allocated memory / NULL pointer if there is an error
+ */
+void *
+mem_calloc(mem_size_t count, mem_size_t size)
+{
+  void *p;
+
+  /* allocate 'count' objects of size 'size' */
+  p = mem_malloc(count * size);
+  if (p) {
+    /* zero the memory */
+    memset(p, 0, (size_t)count * (size_t)size);
+  }
+  return p;
+}
+#endif /* MEM_LIBC_MALLOC && (!LWIP_STATS || !MEM_STATS) */

+ 496 - 470
thirdparty/lwip/src/core/memp.c

@@ -1,470 +1,496 @@
-/**
- * @file
- * Dynamic pool memory manager
- *
- * lwIP has dedicated pools for many structures (netconn, protocol control blocks,
- * packet buffers, ...). All these pools are managed here.
- */
-
-/*
- * 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_impl.h"
-#include "lwip/igmp.h"
-#include "lwip/api.h"
-#include "lwip/api_msg.h"
-#include "lwip/tcpip.h"
-#include "lwip/sys.h"
-#include "lwip/timers.h"
-#include "lwip/stats.h"
-#include "netif/etharp.h"
-#include "lwip/ip_frag.h"
-#include "lwip/snmp_structs.h"
-#include "lwip/snmp_msg.h"
-#include "lwip/dns.h"
-#include "netif/ppp_oe.h"
-
-#include <string.h>
-
-#if !MEMP_MEM_MALLOC /* don't build if not configured for use in lwipopts.h */
-
-struct memp {
-  struct memp *next;
-#if MEMP_OVERFLOW_CHECK
-  const char *file;
-  int line;
-#endif /* MEMP_OVERFLOW_CHECK */
-};
-
-#if MEMP_OVERFLOW_CHECK
-/* if MEMP_OVERFLOW_CHECK is turned on, we reserve some bytes at the beginning
- * and at the end of each element, initialize them as 0xcd and check
- * them later. */
-/* If MEMP_OVERFLOW_CHECK is >= 2, on every call to memp_malloc or memp_free,
- * every single element in each pool is checked!
- * This is VERY SLOW but also very helpful. */
-/* MEMP_SANITY_REGION_BEFORE and MEMP_SANITY_REGION_AFTER can be overridden in
- * lwipopts.h to change the amount reserved for checking. */
-#ifndef MEMP_SANITY_REGION_BEFORE
-#define MEMP_SANITY_REGION_BEFORE  16
-#endif /* MEMP_SANITY_REGION_BEFORE*/
-#if MEMP_SANITY_REGION_BEFORE > 0
-#define MEMP_SANITY_REGION_BEFORE_ALIGNED    LWIP_MEM_ALIGN_SIZE(MEMP_SANITY_REGION_BEFORE)
-#else
-#define MEMP_SANITY_REGION_BEFORE_ALIGNED    0
-#endif /* MEMP_SANITY_REGION_BEFORE*/
-#ifndef MEMP_SANITY_REGION_AFTER
-#define MEMP_SANITY_REGION_AFTER   16
-#endif /* MEMP_SANITY_REGION_AFTER*/
-#if MEMP_SANITY_REGION_AFTER > 0
-#define MEMP_SANITY_REGION_AFTER_ALIGNED     LWIP_MEM_ALIGN_SIZE(MEMP_SANITY_REGION_AFTER)
-#else
-#define MEMP_SANITY_REGION_AFTER_ALIGNED     0
-#endif /* MEMP_SANITY_REGION_AFTER*/
-
-/* MEMP_SIZE: save space for struct memp and for sanity check */
-#define MEMP_SIZE          (LWIP_MEM_ALIGN_SIZE(sizeof(struct memp)) + MEMP_SANITY_REGION_BEFORE_ALIGNED)
-#define MEMP_ALIGN_SIZE(x) (LWIP_MEM_ALIGN_SIZE(x) + MEMP_SANITY_REGION_AFTER_ALIGNED)
-
-#else /* MEMP_OVERFLOW_CHECK */
-
-/* No sanity checks
- * We don't need to preserve the struct memp while not allocated, so we
- * can save a little space and set MEMP_SIZE to 0.
- */
-#define MEMP_SIZE           0
-#define MEMP_ALIGN_SIZE(x) (LWIP_MEM_ALIGN_SIZE(x))
-
-#endif /* MEMP_OVERFLOW_CHECK */
-
-/** This array holds the first free element of each pool.
- *  Elements form a linked list. */
-static struct memp *memp_tab[MEMP_MAX];
-
-#else /* MEMP_MEM_MALLOC */
-
-#define MEMP_ALIGN_SIZE(x) (LWIP_MEM_ALIGN_SIZE(x))
-
-#endif /* MEMP_MEM_MALLOC */
-
-/** This array holds the element sizes of each pool. */
-#if !MEM_USE_POOLS && !MEMP_MEM_MALLOC
-static
-#endif
-const u16_t memp_sizes[MEMP_MAX] = {
-#define LWIP_MEMPOOL(name,num,size,desc)  LWIP_MEM_ALIGN_SIZE(size),
-#include "lwip/memp_std.h"
-};
-
-#if !MEMP_MEM_MALLOC /* don't build if not configured for use in lwipopts.h */
-
-/** This array holds the number of elements in each pool. */
-static const u16_t memp_num[MEMP_MAX] = {
-#define LWIP_MEMPOOL(name,num,size,desc)  (num),
-#include "lwip/memp_std.h"
-};
-
-/** This array holds a textual description of each pool. */
-#ifdef LWIP_DEBUG
-static const char *memp_desc[MEMP_MAX] = {
-#define LWIP_MEMPOOL(name,num,size,desc)  (desc),
-#include "lwip/memp_std.h"
-};
-#endif /* LWIP_DEBUG */
-
-#if MEMP_SEPARATE_POOLS
-
-/** This creates each memory pool. These are named memp_memory_XXX_base (where
- * XXX is the name of the pool defined in memp_std.h).
- * To relocate a pool, declare it as extern in cc.h. Example for GCC:
- *   extern u8_t __attribute__((section(".onchip_mem"))) memp_memory_UDP_PCB_base[];
- */
-#define LWIP_MEMPOOL(name,num,size,desc) u8_t memp_memory_ ## name ## _base \
-  [((num) * (MEMP_SIZE + MEMP_ALIGN_SIZE(size)))];   
-#include "lwip/memp_std.h"
-
-/** This array holds the base of each memory pool. */
-static u8_t *const memp_bases[] = { 
-#define LWIP_MEMPOOL(name,num,size,desc) memp_memory_ ## name ## _base,   
-#include "lwip/memp_std.h"
-};
-
-#else /* MEMP_SEPARATE_POOLS */
-
-/** This is the actual memory used by the pools (all pools in one big block). */
-static u8_t memp_memory[MEM_ALIGNMENT - 1 
-#define LWIP_MEMPOOL(name,num,size,desc) + ( (num) * (MEMP_SIZE + MEMP_ALIGN_SIZE(size) ) )
-#include "lwip/memp_std.h"
-];
-
-#endif /* MEMP_SEPARATE_POOLS */
-
-#if MEMP_SANITY_CHECK
-/**
- * Check that memp-lists don't form a circle, using "Floyd's cycle-finding algorithm".
- */
-static int
-memp_sanity(void)
-{
-  s16_t i;
-  struct memp *t, *h;
-
-  for (i = 0; i < MEMP_MAX; i++) {
-    t = memp_tab[i];
-    if(t != NULL) {
-      for (h = t->next; (t != NULL) && (h != NULL); t = t->next,
-        h = (((h->next != NULL) && (h->next->next != NULL)) ? h->next->next : NULL)) {
-        if (t == h) {
-          return 0;
-        }
-      }
-    }
-  }
-  return 1;
-}
-#endif /* MEMP_SANITY_CHECK*/
-#if MEMP_OVERFLOW_CHECK
-#if defined(LWIP_DEBUG) && MEMP_STATS
-static const char * memp_overflow_names[] = {
-#define LWIP_MEMPOOL(name,num,size,desc) "/"desc,
-#include "lwip/memp_std.h"
-  };
-#endif
-
-/**
- * Check if a memp element was victim of an overflow
- * (e.g. the restricted area after it has been altered)
- *
- * @param p the memp element to check
- * @param memp_type the pool p comes from
- */
-static void
-memp_overflow_check_element_overflow(struct memp *p, u16_t memp_type)
-{
-  u16_t k;
-  u8_t *m;
-#if MEMP_SANITY_REGION_AFTER_ALIGNED > 0
-  m = (u8_t*)p + MEMP_SIZE + memp_sizes[memp_type];
-  for (k = 0; k < MEMP_SANITY_REGION_AFTER_ALIGNED; k++) {
-    if (m[k] != 0xcd) {
-      char errstr[128] = "detected memp overflow in pool ";
-      char digit[] = "0";
-      if(memp_type >= 10) {
-        digit[0] = '0' + (memp_type/10);
-        strcat(errstr, digit);
-      }
-      digit[0] = '0' + (memp_type%10);
-      strcat(errstr, digit);
-#if defined(LWIP_DEBUG) && MEMP_STATS
-      strcat(errstr, memp_overflow_names[memp_type]);
-#endif
-      LWIP_ASSERT(errstr, 0);
-    }
-  }
-#endif
-}
-
-/**
- * Check if a memp element was victim of an underflow
- * (e.g. the restricted area before it has been altered)
- *
- * @param p the memp element to check
- * @param memp_type the pool p comes from
- */
-static void
-memp_overflow_check_element_underflow(struct memp *p, u16_t memp_type)
-{
-  u16_t k;
-  u8_t *m;
-#if MEMP_SANITY_REGION_BEFORE_ALIGNED > 0
-  m = (u8_t*)p + MEMP_SIZE - MEMP_SANITY_REGION_BEFORE_ALIGNED;
-  for (k = 0; k < MEMP_SANITY_REGION_BEFORE_ALIGNED; k++) {
-    if (m[k] != 0xcd) {
-      char errstr[128] = "detected memp underflow in pool ";
-      char digit[] = "0";
-      if(memp_type >= 10) {
-        digit[0] = '0' + (memp_type/10);
-        strcat(errstr, digit);
-      }
-      digit[0] = '0' + (memp_type%10);
-      strcat(errstr, digit);
-#if defined(LWIP_DEBUG) && MEMP_STATS
-      strcat(errstr, memp_overflow_names[memp_type]);
-#endif
-      LWIP_ASSERT(errstr, 0);
-    }
-  }
-#endif
-}
-
-/**
- * Do an overflow check for all elements in every pool.
- *
- * @see memp_overflow_check_element for a description of the check
- */
-static void
-memp_overflow_check_all(void)
-{
-  u16_t i, j;
-  struct memp *p;
-
-  p = (struct memp *)LWIP_MEM_ALIGN(memp_memory);
-  for (i = 0; i < MEMP_MAX; ++i) {
-    p = p;
-    for (j = 0; j < memp_num[i]; ++j) {
-      memp_overflow_check_element_overflow(p, i);
-      p = (struct memp*)((u8_t*)p + MEMP_SIZE + memp_sizes[i] + MEMP_SANITY_REGION_AFTER_ALIGNED);
-    }
-  }
-  p = (struct memp *)LWIP_MEM_ALIGN(memp_memory);
-  for (i = 0; i < MEMP_MAX; ++i) {
-    p = p;
-    for (j = 0; j < memp_num[i]; ++j) {
-      memp_overflow_check_element_underflow(p, i);
-      p = (struct memp*)((u8_t*)p + MEMP_SIZE + memp_sizes[i] + MEMP_SANITY_REGION_AFTER_ALIGNED);
-    }
-  }
-}
-
-/**
- * Initialize the restricted areas of all memp elements in every pool.
- */
-static void
-memp_overflow_init(void)
-{
-  u16_t i, j;
-  struct memp *p;
-  u8_t *m;
-
-  p = (struct memp *)LWIP_MEM_ALIGN(memp_memory);
-  for (i = 0; i < MEMP_MAX; ++i) {
-    p = p;
-    for (j = 0; j < memp_num[i]; ++j) {
-#if MEMP_SANITY_REGION_BEFORE_ALIGNED > 0
-      m = (u8_t*)p + MEMP_SIZE - MEMP_SANITY_REGION_BEFORE_ALIGNED;
-      memset(m, 0xcd, MEMP_SANITY_REGION_BEFORE_ALIGNED);
-#endif
-#if MEMP_SANITY_REGION_AFTER_ALIGNED > 0
-      m = (u8_t*)p + MEMP_SIZE + memp_sizes[i];
-      memset(m, 0xcd, MEMP_SANITY_REGION_AFTER_ALIGNED);
-#endif
-      p = (struct memp*)((u8_t*)p + MEMP_SIZE + memp_sizes[i] + MEMP_SANITY_REGION_AFTER_ALIGNED);
-    }
-  }
-}
-#endif /* MEMP_OVERFLOW_CHECK */
-
-/**
- * Initialize this module.
- * 
- * Carves out memp_memory into linked lists for each pool-type.
- */
-void
-memp_init(void)
-{
-  struct memp *memp;
-  u16_t i, j;
-
-  for (i = 0; i < MEMP_MAX; ++i) {
-    MEMP_STATS_AVAIL(used, i, 0);
-    MEMP_STATS_AVAIL(max, i, 0);
-    MEMP_STATS_AVAIL(err, i, 0);
-    MEMP_STATS_AVAIL(avail, i, memp_num[i]);
-  }
-
-#if !MEMP_SEPARATE_POOLS
-  memp = (struct memp *)LWIP_MEM_ALIGN(memp_memory);
-#endif /* !MEMP_SEPARATE_POOLS */
-  /* for every pool: */
-  for (i = 0; i < MEMP_MAX; ++i) {
-    memp_tab[i] = NULL;
-#if MEMP_SEPARATE_POOLS
-    memp = (struct memp*)memp_bases[i];
-#endif /* MEMP_SEPARATE_POOLS */
-    /* create a linked list of memp elements */
-    for (j = 0; j < memp_num[i]; ++j) {
-      memp->next = memp_tab[i];
-      memp_tab[i] = memp;
-      memp = (struct memp *)(void *)((u8_t *)memp + MEMP_SIZE + memp_sizes[i]
-#if MEMP_OVERFLOW_CHECK
-        + MEMP_SANITY_REGION_AFTER_ALIGNED
-#endif
-      );
-    }
-  }
-#if MEMP_OVERFLOW_CHECK
-  memp_overflow_init();
-  /* check everything a first time to see if it worked */
-  memp_overflow_check_all();
-#endif /* MEMP_OVERFLOW_CHECK */
-}
-
-/**
- * Get an element from a specific pool.
- *
- * @param type the pool to get an element from
- *
- * the debug version has two more parameters:
- * @param file file name calling this function
- * @param line number of line where this function is called
- *
- * @return a pointer to the allocated memory or a NULL pointer on error
- */
-void *
-#if !MEMP_OVERFLOW_CHECK
-memp_malloc(memp_t type)
-#else
-memp_malloc_fn(memp_t type, const char* file, const int line)
-#endif
-{
-  struct memp *memp;
-  SYS_ARCH_DECL_PROTECT(old_level);
- 
-  LWIP_ERROR("memp_malloc: type < MEMP_MAX", (type < MEMP_MAX), return NULL;);
-
-  SYS_ARCH_PROTECT(old_level);
-#if MEMP_OVERFLOW_CHECK >= 2
-  memp_overflow_check_all();
-#endif /* MEMP_OVERFLOW_CHECK >= 2 */
-
-  memp = memp_tab[type];
-  
-  if (memp != NULL) {
-    memp_tab[type] = memp->next;
-#if MEMP_OVERFLOW_CHECK
-    memp->next = NULL;
-    memp->file = file;
-    memp->line = line;
-#endif /* MEMP_OVERFLOW_CHECK */
-    MEMP_STATS_INC_USED(used, type);
-    LWIP_ASSERT("memp_malloc: memp properly aligned",
-                ((mem_ptr_t)memp % MEM_ALIGNMENT) == 0);
-    memp = (struct memp*)(void *)((u8_t*)memp + MEMP_SIZE);
-  } else {
-    LWIP_DEBUGF(MEMP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("memp_malloc: out of memory in pool %s\n", memp_desc[type]));
-    MEMP_STATS_INC(err, type);
-  }
-
-  SYS_ARCH_UNPROTECT(old_level);
-
-  return memp;
-}
-
-/**
- * Put an element back into its pool.
- *
- * @param type the pool where to put mem
- * @param mem the memp element to free
- */
-void
-memp_free(memp_t type, void *mem)
-{
-  struct memp *memp;
-  SYS_ARCH_DECL_PROTECT(old_level);
-
-  if (mem == NULL) {
-    return;
-  }
-  LWIP_ASSERT("memp_free: mem properly aligned",
-                ((mem_ptr_t)mem % MEM_ALIGNMENT) == 0);
-
-  memp = (struct memp *)(void *)((u8_t*)mem - MEMP_SIZE);
-
-  SYS_ARCH_PROTECT(old_level);
-#if MEMP_OVERFLOW_CHECK
-#if MEMP_OVERFLOW_CHECK >= 2
-  memp_overflow_check_all();
-#else
-  memp_overflow_check_element_overflow(memp, type);
-  memp_overflow_check_element_underflow(memp, type);
-#endif /* MEMP_OVERFLOW_CHECK >= 2 */
-#endif /* MEMP_OVERFLOW_CHECK */
-
-  MEMP_STATS_DEC(used, type); 
-  
-  memp->next = memp_tab[type]; 
-  memp_tab[type] = memp;
-
-#if MEMP_SANITY_CHECK
-  LWIP_ASSERT("memp sanity", memp_sanity());
-#endif /* MEMP_SANITY_CHECK */
-
-  SYS_ARCH_UNPROTECT(old_level);
-}
-
-#endif /* MEMP_MEM_MALLOC */
+/**
+ * @file
+ * Dynamic pool memory manager
+ *
+ * lwIP has dedicated pools for many structures (netconn, protocol control blocks,
+ * packet buffers, ...). All these pools are managed here.
+ *
+ * @defgroup mempool Memory pools
+ * @ingroup infrastructure
+ * Custom memory pools
+
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#include "lwip/memp.h"
+#include "lwip/sys.h"
+#include "lwip/stats.h"
+
+#include <string.h>
+
+/* Make sure we include everything we need for size calculation required by memp_std.h */
+#include "lwip/pbuf.h"
+#include "lwip/raw.h"
+#include "lwip/udp.h"
+#include "lwip/tcp.h"
+#include "lwip/priv/tcp_priv.h"
+#include "lwip/ip4_frag.h"
+#include "lwip/netbuf.h"
+#include "lwip/api.h"
+#include "lwip/priv/tcpip_priv.h"
+#include "lwip/priv/api_msg.h"
+#include "lwip/sockets.h"
+#include "lwip/netifapi.h"
+#include "lwip/etharp.h"
+#include "lwip/igmp.h"
+#include "lwip/timeouts.h"
+/* needed by default MEMP_NUM_SYS_TIMEOUT */
+#include "netif/ppp/ppp_opts.h"
+#include "lwip/netdb.h"
+#include "lwip/dns.h"
+#include "lwip/priv/nd6_priv.h"
+#include "lwip/ip6_frag.h"
+#include "lwip/mld6.h"
+
+#define LWIP_MEMPOOL(name,num,size,desc) LWIP_MEMPOOL_DECLARE(name,num,size,desc)
+#include "lwip/priv/memp_std.h"
+
+const struct memp_desc* const memp_pools[MEMP_MAX] = {
+#define LWIP_MEMPOOL(name,num,size,desc) &memp_ ## name,
+#include "lwip/priv/memp_std.h"
+};
+
+#ifdef LWIP_HOOK_FILENAME
+#include LWIP_HOOK_FILENAME
+#endif
+
+#if MEMP_MEM_MALLOC && MEMP_OVERFLOW_CHECK >= 2
+#undef MEMP_OVERFLOW_CHECK
+/* MEMP_OVERFLOW_CHECK >= 2 does not work with MEMP_MEM_MALLOC, use 1 instead */
+#define MEMP_OVERFLOW_CHECK 1
+#endif
+
+#if MEMP_SANITY_CHECK && !MEMP_MEM_MALLOC
+/**
+ * Check that memp-lists don't form a circle, using "Floyd's cycle-finding algorithm".
+ */
+static int
+memp_sanity(const struct memp_desc *desc)
+{
+  struct memp *t, *h;
+
+  t = *desc->tab;
+  if (t != NULL) {
+    for (h = t->next; (t != NULL) && (h != NULL); t = t->next,
+      h = ((h->next != NULL) ? h->next->next : NULL)) {
+      if (t == h) {
+        return 0;
+      }
+    }
+  }
+
+  return 1;
+}
+#endif /* MEMP_SANITY_CHECK && !MEMP_MEM_MALLOC */
+
+#if MEMP_OVERFLOW_CHECK
+/**
+ * Check if a memp element was victim of an overflow
+ * (e.g. the restricted area after it has been altered)
+ *
+ * @param p the memp element to check
+ * @param desc the pool p comes from
+ */
+static void
+memp_overflow_check_element_overflow(struct memp *p, const struct memp_desc *desc)
+{
+#if MEMP_SANITY_REGION_AFTER_ALIGNED > 0
+  u16_t k;
+  u8_t *m;
+  m = (u8_t*)p + MEMP_SIZE + desc->size;
+  for (k = 0; k < MEMP_SANITY_REGION_AFTER_ALIGNED; k++) {
+    if (m[k] != 0xcd) {
+      char errstr[128] = "detected memp overflow in pool ";
+      strcat(errstr, desc->desc);
+      LWIP_ASSERT(errstr, 0);
+    }
+  }
+#else /* MEMP_SANITY_REGION_AFTER_ALIGNED > 0 */
+  LWIP_UNUSED_ARG(p);
+  LWIP_UNUSED_ARG(desc);
+#endif /* MEMP_SANITY_REGION_AFTER_ALIGNED > 0 */
+}
+
+/**
+ * Check if a memp element was victim of an underflow
+ * (e.g. the restricted area before it has been altered)
+ *
+ * @param p the memp element to check
+ * @param desc the pool p comes from
+ */
+static void
+memp_overflow_check_element_underflow(struct memp *p, const struct memp_desc *desc)
+{
+#if MEMP_SANITY_REGION_BEFORE_ALIGNED > 0
+  u16_t k;
+  u8_t *m;
+  m = (u8_t*)p + MEMP_SIZE - MEMP_SANITY_REGION_BEFORE_ALIGNED;
+  for (k = 0; k < MEMP_SANITY_REGION_BEFORE_ALIGNED; k++) {
+    if (m[k] != 0xcd) {
+      char errstr[128] = "detected memp underflow in pool ";
+      strcat(errstr, desc->desc);
+      LWIP_ASSERT(errstr, 0);
+    }
+  }
+#else /* MEMP_SANITY_REGION_BEFORE_ALIGNED > 0 */
+  LWIP_UNUSED_ARG(p);
+  LWIP_UNUSED_ARG(desc);
+#endif /* MEMP_SANITY_REGION_BEFORE_ALIGNED > 0 */
+}
+
+/**
+ * Initialize the restricted area of on memp element.
+ */
+static void
+memp_overflow_init_element(struct memp *p, const struct memp_desc *desc)
+{
+#if MEMP_SANITY_REGION_BEFORE_ALIGNED > 0 || MEMP_SANITY_REGION_AFTER_ALIGNED > 0
+  u8_t *m;
+#if MEMP_SANITY_REGION_BEFORE_ALIGNED > 0
+  m = (u8_t*)p + MEMP_SIZE - MEMP_SANITY_REGION_BEFORE_ALIGNED;
+  memset(m, 0xcd, MEMP_SANITY_REGION_BEFORE_ALIGNED);
+#endif
+#if MEMP_SANITY_REGION_AFTER_ALIGNED > 0
+  m = (u8_t*)p + MEMP_SIZE + desc->size;
+  memset(m, 0xcd, MEMP_SANITY_REGION_AFTER_ALIGNED);
+#endif
+#else /* MEMP_SANITY_REGION_BEFORE_ALIGNED > 0 || MEMP_SANITY_REGION_AFTER_ALIGNED > 0 */
+  LWIP_UNUSED_ARG(p);
+  LWIP_UNUSED_ARG(desc);
+#endif /* MEMP_SANITY_REGION_BEFORE_ALIGNED > 0 || MEMP_SANITY_REGION_AFTER_ALIGNED > 0 */
+}
+
+#if MEMP_OVERFLOW_CHECK >= 2
+/**
+ * Do an overflow check for all elements in every pool.
+ *
+ * @see memp_overflow_check_element for a description of the check
+ */
+static void
+memp_overflow_check_all(void)
+{
+  u16_t i, j;
+  struct memp *p;
+  SYS_ARCH_DECL_PROTECT(old_level);
+  SYS_ARCH_PROTECT(old_level);
+
+  for (i = 0; i < MEMP_MAX; ++i) {
+    p = (struct memp*)LWIP_MEM_ALIGN(memp_pools[i]->base);
+    for (j = 0; j < memp_pools[i]->num; ++j) {
+      memp_overflow_check_element_overflow(p, memp_pools[i]);
+      memp_overflow_check_element_underflow(p, memp_pools[i]);
+      p = LWIP_ALIGNMENT_CAST(struct memp*, ((u8_t*)p + MEMP_SIZE + memp_pools[i]->size + MEMP_SANITY_REGION_AFTER_ALIGNED));
+    }
+  }
+  SYS_ARCH_UNPROTECT(old_level);
+}
+#endif /* MEMP_OVERFLOW_CHECK >= 2 */
+#endif /* MEMP_OVERFLOW_CHECK */
+
+/**
+ * Initialize custom memory pool.
+ * Related functions: memp_malloc_pool, memp_free_pool
+ *
+ * @param desc pool to initialize
+ */
+void
+memp_init_pool(const struct memp_desc *desc)
+{
+#if MEMP_MEM_MALLOC
+  LWIP_UNUSED_ARG(desc);
+#else
+  int i;
+  struct memp *memp;
+
+  *desc->tab = NULL;
+  memp = (struct memp*)LWIP_MEM_ALIGN(desc->base);
+  /* create a linked list of memp elements */
+  for (i = 0; i < desc->num; ++i) {
+    memp->next = *desc->tab;
+    *desc->tab = memp;
+#if MEMP_OVERFLOW_CHECK
+    memp_overflow_init_element(memp, desc);
+#endif /* MEMP_OVERFLOW_CHECK */
+   /* cast through void* to get rid of alignment warnings */
+   memp = (struct memp *)(void *)((u8_t *)memp + MEMP_SIZE + desc->size
+#if MEMP_OVERFLOW_CHECK
+      + MEMP_SANITY_REGION_AFTER_ALIGNED
+#endif
+    );
+  }
+#if MEMP_STATS
+  desc->stats->avail = desc->num;
+#endif /* MEMP_STATS */
+#endif /* !MEMP_MEM_MALLOC */
+
+#if MEMP_STATS && (defined(LWIP_DEBUG) || LWIP_STATS_DISPLAY)
+  desc->stats->name  = desc->desc;
+#endif /* MEMP_STATS && (defined(LWIP_DEBUG) || LWIP_STATS_DISPLAY) */
+}
+
+/**
+ * Initializes lwIP built-in pools.
+ * Related functions: memp_malloc, memp_free
+ *
+ * Carves out memp_memory into linked lists for each pool-type.
+ */
+void
+memp_init(void)
+{
+  u16_t i;
+
+  /* for every pool: */
+  for (i = 0; i < LWIP_ARRAYSIZE(memp_pools); i++) {
+    memp_init_pool(memp_pools[i]);
+
+#if LWIP_STATS && MEMP_STATS
+    lwip_stats.memp[i] = memp_pools[i]->stats;
+#endif
+  }
+
+#if MEMP_OVERFLOW_CHECK >= 2
+  /* check everything a first time to see if it worked */
+  memp_overflow_check_all();
+#endif /* MEMP_OVERFLOW_CHECK >= 2 */
+}
+
+static void*
+#if !MEMP_OVERFLOW_CHECK
+do_memp_malloc_pool(const struct memp_desc *desc)
+#else
+do_memp_malloc_pool_fn(const struct memp_desc *desc, const char* file, const int line)
+#endif
+{
+  struct memp *memp;
+  SYS_ARCH_DECL_PROTECT(old_level);
+
+#if MEMP_MEM_MALLOC
+  memp = (struct memp *)mem_malloc(MEMP_SIZE + MEMP_ALIGN_SIZE(desc->size));
+  SYS_ARCH_PROTECT(old_level);
+#else /* MEMP_MEM_MALLOC */
+  SYS_ARCH_PROTECT(old_level);
+
+  memp = *desc->tab;
+#endif /* MEMP_MEM_MALLOC */
+
+  if (memp != NULL) {
+#if !MEMP_MEM_MALLOC
+#if MEMP_OVERFLOW_CHECK == 1
+    memp_overflow_check_element_overflow(memp, desc);
+    memp_overflow_check_element_underflow(memp, desc);
+#endif /* MEMP_OVERFLOW_CHECK */
+
+    *desc->tab = memp->next;
+#if MEMP_OVERFLOW_CHECK
+    memp->next = NULL;
+#endif /* MEMP_OVERFLOW_CHECK */
+#endif /* !MEMP_MEM_MALLOC */
+#if MEMP_OVERFLOW_CHECK
+    memp->file = file;
+    memp->line = line;
+#if MEMP_MEM_MALLOC
+    memp_overflow_init_element(memp, desc);
+#endif /* MEMP_MEM_MALLOC */
+#endif /* MEMP_OVERFLOW_CHECK */
+    LWIP_ASSERT("memp_malloc: memp properly aligned",
+                ((mem_ptr_t)memp % MEM_ALIGNMENT) == 0);
+#if MEMP_STATS
+    desc->stats->used++;
+    if (desc->stats->used > desc->stats->max) {
+      desc->stats->max = desc->stats->used;
+    }
+#endif
+    SYS_ARCH_UNPROTECT(old_level);
+    /* cast through u8_t* to get rid of alignment warnings */
+    return ((u8_t*)memp + MEMP_SIZE);
+  } else {
+    LWIP_DEBUGF(MEMP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("memp_malloc: out of memory in pool %s\n", desc->desc));
+#if MEMP_STATS
+    desc->stats->err++;
+#endif
+  }
+
+  SYS_ARCH_UNPROTECT(old_level);
+  return NULL;
+}
+
+/**
+ * Get an element from a custom pool.
+ *
+ * @param desc the pool to get an element from
+ *
+ * @return a pointer to the allocated memory or a NULL pointer on error
+ */
+void *
+#if !MEMP_OVERFLOW_CHECK
+memp_malloc_pool(const struct memp_desc *desc)
+#else
+memp_malloc_pool_fn(const struct memp_desc *desc, const char* file, const int line)
+#endif
+{
+  LWIP_ASSERT("invalid pool desc", desc != NULL);
+  if (desc == NULL) {
+    return NULL;
+  }
+
+#if !MEMP_OVERFLOW_CHECK
+  return do_memp_malloc_pool(desc);
+#else
+  return do_memp_malloc_pool_fn(desc, file, line);
+#endif
+}
+
+/**
+ * Get an element from a specific pool.
+ *
+ * @param type the pool to get an element from
+ *
+ * @return a pointer to the allocated memory or a NULL pointer on error
+ */
+void *
+#if !MEMP_OVERFLOW_CHECK
+memp_malloc(memp_t type)
+#else
+memp_malloc_fn(memp_t type, const char* file, const int line)
+#endif
+{
+  void *memp;
+  LWIP_ERROR("memp_malloc: type < MEMP_MAX", (type < MEMP_MAX), return NULL;);
+
+#if MEMP_OVERFLOW_CHECK >= 2
+  memp_overflow_check_all();
+#endif /* MEMP_OVERFLOW_CHECK >= 2 */
+
+#if !MEMP_OVERFLOW_CHECK
+  memp = do_memp_malloc_pool(memp_pools[type]);
+#else
+  memp = do_memp_malloc_pool_fn(memp_pools[type], file, line);
+#endif
+
+  return memp;
+}
+
+static void
+do_memp_free_pool(const struct memp_desc* desc, void *mem)
+{
+  struct memp *memp;
+  SYS_ARCH_DECL_PROTECT(old_level);
+
+  LWIP_ASSERT("memp_free: mem properly aligned",
+                ((mem_ptr_t)mem % MEM_ALIGNMENT) == 0);
+
+  /* cast through void* to get rid of alignment warnings */
+  memp = (struct memp *)(void *)((u8_t*)mem - MEMP_SIZE);
+
+  SYS_ARCH_PROTECT(old_level);
+
+#if MEMP_OVERFLOW_CHECK == 1
+  memp_overflow_check_element_overflow(memp, desc);
+  memp_overflow_check_element_underflow(memp, desc);
+#endif /* MEMP_OVERFLOW_CHECK */
+
+#if MEMP_STATS
+  desc->stats->used--;
+#endif
+
+#if MEMP_MEM_MALLOC
+  LWIP_UNUSED_ARG(desc);
+  SYS_ARCH_UNPROTECT(old_level);
+  mem_free(memp);
+#else /* MEMP_MEM_MALLOC */
+  memp->next = *desc->tab;
+  *desc->tab = memp;
+
+#if MEMP_SANITY_CHECK
+  LWIP_ASSERT("memp sanity", memp_sanity(desc));
+#endif /* MEMP_SANITY_CHECK */
+
+  SYS_ARCH_UNPROTECT(old_level);
+#endif /* !MEMP_MEM_MALLOC */
+}
+
+/**
+ * Put a custom pool element back into its pool.
+ *
+ * @param desc the pool where to put mem
+ * @param mem the memp element to free
+ */
+void
+memp_free_pool(const struct memp_desc* desc, void *mem)
+{
+  LWIP_ASSERT("invalid pool desc", desc != NULL);
+  if ((desc == NULL) || (mem == NULL)) {
+    return;
+  }
+
+  do_memp_free_pool(desc, mem);
+}
+
+/**
+ * Put an element back into its pool.
+ *
+ * @param type the pool where to put mem
+ * @param mem the memp element to free
+ */
+void
+memp_free(memp_t type, void *mem)
+{
+#ifdef LWIP_HOOK_MEMP_AVAILABLE
+  struct memp *old_first;
+#endif
+
+  LWIP_ERROR("memp_free: type < MEMP_MAX", (type < MEMP_MAX), return;);
+
+  if (mem == NULL) {
+    return;
+  }
+
+#if MEMP_OVERFLOW_CHECK >= 2
+  memp_overflow_check_all();
+#endif /* MEMP_OVERFLOW_CHECK >= 2 */
+
+#ifdef LWIP_HOOK_MEMP_AVAILABLE
+  old_first = *memp_pools[type]->tab;
+#endif
+
+  do_memp_free_pool(memp_pools[type], mem);
+
+#ifdef LWIP_HOOK_MEMP_AVAILABLE
+  if (old_first == NULL) {
+    LWIP_HOOK_MEMP_AVAILABLE(type);
+  }
+#endif
+}

+ 1265 - 774
thirdparty/lwip/src/core/netif.c

@@ -1,774 +1,1265 @@
-/**
- * @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_impl.h"
-#include "lwip/snmp.h"
-#include "lwip/igmp.h"
-#include "netif/etharp.h"
-#include "lwip/stats.h"
-#if ENABLE_LOOPBACK
-#include "lwip/sys.h"
-#if LWIP_NETIF_LOOPBACK_MULTITHREADING
-#include "lwip/tcpip.h"
-#endif /* LWIP_NETIF_LOOPBACK_MULTITHREADING */
-#endif /* ENABLE_LOOPBACK */
-
-#if LWIP_AUTOIP
-#include "lwip/autoip.h"
-#endif /* LWIP_AUTOIP */
-#if LWIP_DHCP
-#include "lwip/dhcp.h"
-#endif /* LWIP_DHCP */
-
-#if LWIP_NETIF_STATUS_CALLBACK
-#define NETIF_STATUS_CALLBACK(n) do{ if (n->status_callback) { (n->status_callback)(n); }}while(0)
-#else
-#define NETIF_STATUS_CALLBACK(n)
-#endif /* LWIP_NETIF_STATUS_CALLBACK */ 
-
-#if LWIP_NETIF_LINK_CALLBACK
-#define NETIF_LINK_CALLBACK(n) do{ if (n->link_callback) { (n->link_callback)(n); }}while(0)
-#else
-#define NETIF_LINK_CALLBACK(n)
-#endif /* LWIP_NETIF_LINK_CALLBACK */ 
-
-struct netif *netif_list;
-struct netif *netif_default;
-
-static u8_t netif_num;
-
-#if LWIP_HAVE_LOOPIF
-static struct netif loop_netif;
-
-/**
- * Initialize a lwip network interface structure for a loopback interface
- *
- * @param netif the lwip network interface structure for this loopif
- * @return ERR_OK if the loopif is initialized
- *         ERR_MEM if private data couldn't be allocated
- */
-static err_t
-netif_loopif_init(struct netif *netif)
-{
-  /* initialize the snmp variables and counters inside the struct netif
-   * ifSpeed: no assumption can be made!
-   */
-  NETIF_INIT_SNMP(netif, snmp_ifType_softwareLoopback, 0);
-
-  netif->name[0] = 'l';
-  netif->name[1] = 'o';
-  netif->output = netif_loop_output;
-  return ERR_OK;
-}
-#endif /* LWIP_HAVE_LOOPIF */
-
-void
-netif_init(void)
-{
-#if LWIP_HAVE_LOOPIF
-  ip_addr_t loop_ipaddr, loop_netmask, loop_gw;
-  IP4_ADDR(&loop_gw, 127,0,0,1);
-  IP4_ADDR(&loop_ipaddr, 127,0,0,1);
-  IP4_ADDR(&loop_netmask, 255,0,0,0);
-
-#if NO_SYS
-  netif_add(&loop_netif, &loop_ipaddr, &loop_netmask, &loop_gw, NULL, netif_loopif_init, ip_input);
-#else  /* NO_SYS */
-  netif_add(&loop_netif, &loop_ipaddr, &loop_netmask, &loop_gw, NULL, netif_loopif_init, tcpip_input);
-#endif /* NO_SYS */
-  netif_set_up(&loop_netif);
-
-#endif /* LWIP_HAVE_LOOPIF */
-}
-
-/**
- * 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, ip_addr_t *ipaddr, ip_addr_t *netmask,
-  ip_addr_t *gw, void *state, netif_init_fn init, netif_input_fn input)
-{
-
-  LWIP_ASSERT("No init function given", init != NULL);
-
-  /* reset new interface configuration state */
-  ip_addr_set_zero(&netif->ip_addr);
-  ip_addr_set_zero(&netif->netmask);
-  ip_addr_set_zero(&netif->gw);
-  netif->flags = 0;
-#if LWIP_DHCP
-  /* netif not under DHCP control by default */
-  netif->dhcp = NULL;
-#endif /* LWIP_DHCP */
-#if LWIP_AUTOIP
-  /* netif not under AutoIP control by default */
-  netif->autoip = NULL;
-#endif /* LWIP_AUTOIP */
-#if LWIP_NETIF_STATUS_CALLBACK
-  netif->status_callback = NULL;
-#endif /* LWIP_NETIF_STATUS_CALLBACK */
-#if LWIP_NETIF_LINK_CALLBACK
-  netif->link_callback = NULL;
-#endif /* LWIP_NETIF_LINK_CALLBACK */
-#if LWIP_IGMP
-  netif->igmp_mac_filter = NULL;
-#endif /* LWIP_IGMP */
-#if ENABLE_LOOPBACK
-  netif->loop_first = NULL;
-  netif->loop_last = NULL;
-#endif /* ENABLE_LOOPBACK */
-
-  /* remember netif specific state information data */
-  netif->state = state;
-  netif->num = netif_num++;
-  netif->input = input;
-  NETIF_SET_HWADDRHINT(netif, NULL);
-#if ENABLE_LOOPBACK && LWIP_LOOPBACK_MAX_PBUFS
-  netif->loop_cnt_current = 0;
-#endif /* ENABLE_LOOPBACK && LWIP_LOOPBACK_MAX_PBUFS */
-
-  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;
-  snmp_inc_iflist();
-
-#if LWIP_IGMP
-  /* start IGMP processing */
-  if (netif->flags & NETIF_FLAG_IGMP) {
-    igmp_start(netif);
-  }
-#endif /* LWIP_IGMP */
-
-  LWIP_DEBUGF(NETIF_DEBUG, ("netif: added interface %c%c IP 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;
-}
-
-/**
- * Change IP address configuration for a network interface (including netmask
- * and default gateway).
- *
- * @param netif the network interface to change
- * @param ipaddr the new IP address
- * @param netmask the new netmask
- * @param gw the new default gateway
- */
-void
-netif_set_addr(struct netif *netif, ip_addr_t *ipaddr, ip_addr_t *netmask,
-    ip_addr_t *gw)
-{
-  netif_set_ipaddr(netif, ipaddr);
-  netif_set_netmask(netif, netmask);
-  netif_set_gw(netif, gw);
-}
-
-/**
- * Remove a network interface from the list of lwIP netifs.
- *
- * @param netif the network interface to remove
- */
-void
-netif_remove(struct netif *netif)
-{
-  if (netif == NULL) {
-    return;
-  }
-
-#if LWIP_IGMP
-  /* stop IGMP processing */
-  if (netif->flags & NETIF_FLAG_IGMP) {
-    igmp_stop(netif);
-  }
-#endif /* LWIP_IGMP */
-  if (netif_is_up(netif)) {
-    /* set netif down before removing (call callback function) */
-    netif_set_down(netif);
-  }
-
-  snmp_delete_ipaddridx_tree(netif);
-
-  /*  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 */
-  }
-  snmp_dec_iflist();
-  /* this netif is default? */
-  if (netif_default == netif) {
-    /* reset default netif */
-    netif_set_default(NULL);
-  }
-#if LWIP_NETIF_REMOVE_CALLBACK
-  if (netif->remove_callback) {
-    netif->remove_callback(netif);
-  }
-#endif /* LWIP_NETIF_REMOVE_CALLBACK */
-  LWIP_DEBUGF( NETIF_DEBUG, ("netif_remove: removed netif\n") );
-}
-
-/**
- * Find a network interface by searching for its name
- *
- * @param name the name of the netif (like netif->name) plus concatenated number
- * in ascii representation (e.g. 'en0')
- */
-struct netif *
-netif_find(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;
-}
-
-/**
- * Change the IP address of a network interface
- *
- * @param netif the network interface to change
- * @param ipaddr the new IP address
- *
- * @note call netif_set_addr() if you also want to change netmask and
- * default gateway
- */
-void
-netif_set_ipaddr(struct netif *netif, ip_addr_t *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 (ipaddr && (ip_addr_cmp(ipaddr, &(netif->ip_addr))) == 0) {
-    /* extern struct tcp_pcb *tcp_active_pcbs; defined by tcp.h */
-    LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_STATE, ("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))
-#if LWIP_AUTOIP
-        /* connections to link-local addresses must persist (RFC3927 ch. 1.9) */
-        && !ip_addr_islinklocal(&(pcb->local_ip))
-#endif /* LWIP_AUTOIP */
-        ) {
-        /* this connection must be aborted */
-        struct tcp_pcb *next = pcb->next;
-        LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_STATE, ("netif_set_ipaddr: aborting TCP pcb %p\n", (void *)pcb));
-        tcp_abort(pcb);
-        pcb = next;
-      } else {
-        pcb = pcb->next;
-      }
-    }
-    for (lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) {
-      /* PCB bound to current local interface address? */
-      if ((!(ip_addr_isany(&(lpcb->local_ip)))) &&
-          (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
-  snmp_delete_ipaddridx_tree(netif);
-  snmp_delete_iprteidx_tree(0,netif);
-  /* set new IP address to netif */
-  ip_addr_set(&(netif->ip_addr), ipaddr);
-  snmp_insert_ipaddridx_tree(netif);
-  snmp_insert_iprteidx_tree(0,netif);
-
-  LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("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_16(&netif->ip_addr),
-    ip4_addr2_16(&netif->ip_addr),
-    ip4_addr3_16(&netif->ip_addr),
-    ip4_addr4_16(&netif->ip_addr)));
-}
-
-/**
- * Change the default gateway for a network interface
- *
- * @param netif the network interface to change
- * @param gw the new default gateway
- *
- * @note call netif_set_addr() if you also want to change ip address and netmask
- */
-void
-netif_set_gw(struct netif *netif, ip_addr_t *gw)
-{
-  ip_addr_set(&(netif->gw), gw);
-  LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("netif: GW address of interface %c%c set to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
-    netif->name[0], netif->name[1],
-    ip4_addr1_16(&netif->gw),
-    ip4_addr2_16(&netif->gw),
-    ip4_addr3_16(&netif->gw),
-    ip4_addr4_16(&netif->gw)));
-}
-
-/**
- * Change the netmask of a network interface
- *
- * @param netif the network interface to change
- * @param netmask the new netmask
- *
- * @note call netif_set_addr() if you also want to change ip address and
- * default gateway
- */
-void
-netif_set_netmask(struct netif *netif, ip_addr_t *netmask)
-{
-  snmp_delete_iprteidx_tree(0, netif);
-  /* set new netmask to netif */
-  ip_addr_set(&(netif->netmask), netmask);
-  snmp_insert_iprteidx_tree(0, netif);
-  LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("netif: netmask of interface %c%c set to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
-    netif->name[0], netif->name[1],
-    ip4_addr1_16(&netif->netmask),
-    ip4_addr2_16(&netif->netmask),
-    ip4_addr3_16(&netif->netmask),
-    ip4_addr4_16(&netif->netmask)));
-}
-
-/**
- * Set a network interface as the default network interface
- * (used to output all packets for which no specific route is found)
- *
- * @param netif the default network interface
- */
-void
-netif_set_default(struct netif *netif)
-{
-  if (netif == NULL) {
-    /* remove default route */
-    snmp_delete_iprteidx_tree(1, netif);
-  } else {
-    /* install default route */
-    snmp_insert_iprteidx_tree(1, 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)
-{
-  if (!(netif->flags & NETIF_FLAG_UP)) {
-    netif->flags |= NETIF_FLAG_UP;
-    
-#if LWIP_SNMP
-    snmp_get_sysuptime(&netif->ts);
-#endif /* LWIP_SNMP */
-
-    NETIF_STATUS_CALLBACK(netif);
-
-    if (netif->flags & NETIF_FLAG_LINK_UP) {
-#if LWIP_ARP
-      /* For Ethernet network interfaces, we would like to send a "gratuitous ARP" */ 
-      if (netif->flags & (NETIF_FLAG_ETHARP)) {
-        etharp_gratuitous(netif);
-      }
-#endif /* LWIP_ARP */
-
-#if LWIP_IGMP
-      /* resend IGMP memberships */
-      if (netif->flags & NETIF_FLAG_IGMP) {
-        igmp_report_groups( netif);
-      }
-#endif /* LWIP_IGMP */
-    }
-  }
-}
-
-/**
- * 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)
-{
-  if (netif->flags & NETIF_FLAG_UP) {
-    netif->flags &= ~NETIF_FLAG_UP;
-#if LWIP_SNMP
-    snmp_get_sysuptime(&netif->ts);
-#endif
-
-#if LWIP_ARP
-    if (netif->flags & NETIF_FLAG_ETHARP) {
-      etharp_cleanup_netif(netif);
-    }
-#endif /* LWIP_ARP */
-    NETIF_STATUS_CALLBACK(netif);
-  }
-}
-
-#if LWIP_NETIF_STATUS_CALLBACK
-/**
- * Set callback to be called when interface is brought up/down
- */
-void netif_set_status_callback(struct netif *netif, netif_status_callback_fn status_callback)
-{
-  if (netif) {
-    netif->status_callback = status_callback;
-  }
-}
-#endif /* LWIP_NETIF_STATUS_CALLBACK */
-
-#if LWIP_NETIF_REMOVE_CALLBACK
-/**
- * Set callback to be called when the interface has been removed
- */
-void
-netif_set_remove_callback(struct netif *netif, netif_status_callback_fn remove_callback)
-{
-  if (netif) {
-    netif->remove_callback = remove_callback;
-  }
-}
-#endif /* LWIP_NETIF_REMOVE_CALLBACK */
-
-/**
- * Called by a driver when its link goes up
- */
-void netif_set_link_up(struct netif *netif )
-{
-  if (!(netif->flags & NETIF_FLAG_LINK_UP)) {
-    netif->flags |= NETIF_FLAG_LINK_UP;
-
-#if LWIP_DHCP
-    if (netif->dhcp) {
-      dhcp_network_changed(netif);
-    }
-#endif /* LWIP_DHCP */
-
-#if LWIP_AUTOIP
-    if (netif->autoip) {
-      autoip_network_changed(netif);
-    }
-#endif /* LWIP_AUTOIP */
-
-    if (netif->flags & NETIF_FLAG_UP) {
-#if LWIP_ARP
-      /* For Ethernet network interfaces, we would like to send a "gratuitous ARP" */ 
-      if (netif->flags & NETIF_FLAG_ETHARP) {
-        etharp_gratuitous(netif);
-      }
-#endif /* LWIP_ARP */
-
-#if LWIP_IGMP
-      /* resend IGMP memberships */
-      if (netif->flags & NETIF_FLAG_IGMP) {
-        igmp_report_groups( netif);
-      }
-#endif /* LWIP_IGMP */
-    }
-    NETIF_LINK_CALLBACK(netif);
-  }
-}
-
-/**
- * Called by a driver when its link goes down
- */
-void netif_set_link_down(struct netif *netif )
-{
-  if (netif->flags & NETIF_FLAG_LINK_UP) {
-    netif->flags &= ~NETIF_FLAG_LINK_UP;
-    NETIF_LINK_CALLBACK(netif);
-  }
-}
-
-#if LWIP_NETIF_LINK_CALLBACK
-/**
- * Set callback to be called when link is brought up/down
- */
-void netif_set_link_callback(struct netif *netif, netif_status_callback_fn link_callback)
-{
-  if (netif) {
-    netif->link_callback = link_callback;
-  }
-}
-#endif /* LWIP_NETIF_LINK_CALLBACK */
-
-#if ENABLE_LOOPBACK
-/**
- * Send an IP packet to be received on the same netif (loopif-like).
- * The pbuf is simply copied and handed back to netif->input.
- * In multithreaded mode, this is done directly since netif->input must put
- * the packet on a queue.
- * In callback mode, the packet is put on an internal queue and is fed to
- * netif->input by netif_poll().
- *
- * @param netif the lwip network interface structure
- * @param p the (IP) packet to 'send'
- * @param ipaddr the ip address to send the packet to (not used)
- * @return ERR_OK if the packet has been sent
- *         ERR_MEM if the pbuf used to copy the packet couldn't be allocated
- */
-err_t
-netif_loop_output(struct netif *netif, struct pbuf *p,
-       ip_addr_t *ipaddr)
-{
-  struct pbuf *r;
-  err_t err;
-  struct pbuf *last;
-#if LWIP_LOOPBACK_MAX_PBUFS
-  u8_t clen = 0;
-#endif /* LWIP_LOOPBACK_MAX_PBUFS */
-  /* If we have a loopif, SNMP counters are adjusted for it,
-   * if not they are adjusted for 'netif'. */
-#if LWIP_SNMP
-#if LWIP_HAVE_LOOPIF
-  struct netif *stats_if = &loop_netif;
-#else /* LWIP_HAVE_LOOPIF */
-  struct netif *stats_if = netif;
-#endif /* LWIP_HAVE_LOOPIF */
-#endif /* LWIP_SNMP */
-  SYS_ARCH_DECL_PROTECT(lev);
-  LWIP_UNUSED_ARG(ipaddr);
-
-  /* Allocate a new pbuf */
-  r = pbuf_alloc(PBUF_LINK, p->tot_len, PBUF_RAM);
-  if (r == NULL) {
-    LINK_STATS_INC(link.memerr);
-    LINK_STATS_INC(link.drop);
-    snmp_inc_ifoutdiscards(stats_if);
-    return ERR_MEM;
-  }
-#if LWIP_LOOPBACK_MAX_PBUFS
-  clen = pbuf_clen(r);
-  /* check for overflow or too many pbuf on queue */
-  if(((netif->loop_cnt_current + clen) < netif->loop_cnt_current) ||
-     ((netif->loop_cnt_current + clen) > LWIP_LOOPBACK_MAX_PBUFS)) {
-    pbuf_free(r);
-    LINK_STATS_INC(link.memerr);
-    LINK_STATS_INC(link.drop);
-    snmp_inc_ifoutdiscards(stats_if);
-    return ERR_MEM;
-  }
-  netif->loop_cnt_current += clen;
-#endif /* LWIP_LOOPBACK_MAX_PBUFS */
-
-  /* Copy the whole pbuf queue p into the single pbuf r */
-  if ((err = pbuf_copy(r, p)) != ERR_OK) {
-    pbuf_free(r);
-    LINK_STATS_INC(link.memerr);
-    LINK_STATS_INC(link.drop);
-    snmp_inc_ifoutdiscards(stats_if);
-    return err;
-  }
-
-  /* Put the packet on a linked list which gets emptied through calling
-     netif_poll(). */
-
-  /* let last point to the last pbuf in chain r */
-  for (last = r; last->next != NULL; last = last->next);
-
-  SYS_ARCH_PROTECT(lev);
-  if(netif->loop_first != NULL) {
-    LWIP_ASSERT("if first != NULL, last must also be != NULL", netif->loop_last != NULL);
-    netif->loop_last->next = r;
-    netif->loop_last = last;
-  } else {
-    netif->loop_first = r;
-    netif->loop_last = last;
-  }
-  SYS_ARCH_UNPROTECT(lev);
-
-  LINK_STATS_INC(link.xmit);
-  snmp_add_ifoutoctets(stats_if, p->tot_len);
-  snmp_inc_ifoutucastpkts(stats_if);
-
-#if LWIP_NETIF_LOOPBACK_MULTITHREADING
-  /* For multithreading environment, schedule a call to netif_poll */
-  tcpip_callback((tcpip_callback_fn)netif_poll, netif);
-#endif /* LWIP_NETIF_LOOPBACK_MULTITHREADING */
-
-  return ERR_OK;
-}
-
-/**
- * Call netif_poll() in the main loop of your application. This is to prevent
- * reentering non-reentrant functions like tcp_input(). Packets passed to
- * netif_loop_output() are put on a list that is passed to netif->input() by
- * netif_poll().
- */
-void
-netif_poll(struct netif *netif)
-{
-  struct pbuf *in;
-  /* If we have a loopif, SNMP counters are adjusted for it,
-   * if not they are adjusted for 'netif'. */
-#if LWIP_SNMP
-#if LWIP_HAVE_LOOPIF
-  struct netif *stats_if = &loop_netif;
-#else /* LWIP_HAVE_LOOPIF */
-  struct netif *stats_if = netif;
-#endif /* LWIP_HAVE_LOOPIF */
-#endif /* LWIP_SNMP */
-  SYS_ARCH_DECL_PROTECT(lev);
-
-  do {
-    /* Get a packet from the list. With SYS_LIGHTWEIGHT_PROT=1, this is protected */
-    SYS_ARCH_PROTECT(lev);
-    in = netif->loop_first;
-    if (in != NULL) {
-      struct pbuf *in_end = in;
-#if LWIP_LOOPBACK_MAX_PBUFS
-      u8_t clen = pbuf_clen(in);
-      /* adjust the number of pbufs on queue */
-      LWIP_ASSERT("netif->loop_cnt_current underflow",
-        ((netif->loop_cnt_current - clen) < netif->loop_cnt_current));
-      netif->loop_cnt_current -= clen;
-#endif /* LWIP_LOOPBACK_MAX_PBUFS */
-      while (in_end->len != in_end->tot_len) {
-        LWIP_ASSERT("bogus pbuf: len != tot_len but next == NULL!", in_end->next != NULL);
-        in_end = in_end->next;
-      }
-      /* 'in_end' now points to the last pbuf from 'in' */
-      if (in_end == netif->loop_last) {
-        /* this was the last pbuf in the list */
-        netif->loop_first = netif->loop_last = NULL;
-      } else {
-        /* pop the pbuf off the list */
-        netif->loop_first = in_end->next;
-        LWIP_ASSERT("should not be null since first != last!", netif->loop_first != NULL);
-      }
-      /* De-queue the pbuf from its successors on the 'loop_' list. */
-      in_end->next = NULL;
-    }
-    SYS_ARCH_UNPROTECT(lev);
-
-    if (in != NULL) {
-      LINK_STATS_INC(link.recv);
-      snmp_add_ifinoctets(stats_if, in->tot_len);
-      snmp_inc_ifinucastpkts(stats_if);
-      /* loopback packets are always IP packets! */
-      if (ip_input(in, netif) != ERR_OK) {
-        pbuf_free(in);
-      }
-      /* Don't reference the packet any more! */
-      in = NULL;
-    }
-  /* go on while there is a packet on the list */
-  } while (netif->loop_first != NULL);
-}
-
-#if !LWIP_NETIF_LOOPBACK_MULTITHREADING
-/**
- * Calls netif_poll() for every netif on the netif_list.
- */
-void
-netif_poll_all(void)
-{
-  struct netif *netif = netif_list;
-  /* loop through netifs */
-  while (netif != NULL) {
-    netif_poll(netif);
-    /* proceed to next network interface */
-    netif = netif->next;
-  }
-}
-#endif /* !LWIP_NETIF_LOOPBACK_MULTITHREADING */
-#endif /* ENABLE_LOOPBACK */
+/**
+ * @file
+ * lwIP network interface abstraction
+ * 
+ * @defgroup netif Network interface (NETIF)
+ * @ingroup callbackstyle_api
+ * 
+ * @defgroup netif_ip4 IPv4 address handling
+ * @ingroup netif
+ * 
+ * @defgroup netif_ip6 IPv6 address handling
+ * @ingroup netif
+ * 
+ * @defgroup netif_cd Client data handling
+ * Store data (void*) on a netif for application usage.
+ * @see @ref LWIP_NUM_NETIF_CLIENT_DATA
+ * @ingroup netif
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ */
+
+#include "lwip/opt.h"
+
+#include <string.h>
+
+#include "lwip/def.h"
+#include "lwip/ip_addr.h"
+#include "lwip/ip6_addr.h"
+#include "lwip/netif.h"
+#include "lwip/priv/tcp_priv.h"
+#include "lwip/udp.h"
+#include "lwip/raw.h"
+#include "lwip/snmp.h"
+#include "lwip/igmp.h"
+#include "lwip/etharp.h"
+#include "lwip/stats.h"
+#include "lwip/sys.h"
+#include "lwip/ip.h"
+#if ENABLE_LOOPBACK
+#if LWIP_NETIF_LOOPBACK_MULTITHREADING
+#include "lwip/tcpip.h"
+#endif /* LWIP_NETIF_LOOPBACK_MULTITHREADING */
+#endif /* ENABLE_LOOPBACK */
+
+#include "netif/ethernet.h"
+
+#if LWIP_AUTOIP
+#include "lwip/autoip.h"
+#endif /* LWIP_AUTOIP */
+#if LWIP_DHCP
+#include "lwip/dhcp.h"
+#endif /* LWIP_DHCP */
+#if LWIP_IPV6_DHCP6
+#include "lwip/dhcp6.h"
+#endif /* LWIP_IPV6_DHCP6 */
+#if LWIP_IPV6_MLD
+#include "lwip/mld6.h"
+#endif /* LWIP_IPV6_MLD */
+#if LWIP_IPV6
+#include "lwip/nd6.h"
+#endif
+
+#if LWIP_NETIF_STATUS_CALLBACK
+#define NETIF_STATUS_CALLBACK(n) do{ if (n->status_callback) { (n->status_callback)(n); }}while(0)
+#else
+#define NETIF_STATUS_CALLBACK(n)
+#endif /* LWIP_NETIF_STATUS_CALLBACK */
+
+#if LWIP_NETIF_LINK_CALLBACK
+#define NETIF_LINK_CALLBACK(n) do{ if (n->link_callback) { (n->link_callback)(n); }}while(0)
+#else
+#define NETIF_LINK_CALLBACK(n)
+#endif /* LWIP_NETIF_LINK_CALLBACK */
+
+struct netif *netif_list;
+struct netif *netif_default;
+
+static u8_t netif_num;
+
+#if LWIP_NUM_NETIF_CLIENT_DATA > 0
+static u8_t netif_client_id;
+#endif
+
+#define NETIF_REPORT_TYPE_IPV4  0x01
+#define NETIF_REPORT_TYPE_IPV6  0x02
+static void netif_issue_reports(struct netif* netif, u8_t report_type);
+
+#if LWIP_IPV6
+static err_t netif_null_output_ip6(struct netif *netif, struct pbuf *p, const ip6_addr_t *ipaddr);
+#endif /* LWIP_IPV6 */
+
+#if LWIP_HAVE_LOOPIF
+#if LWIP_IPV4
+static err_t netif_loop_output_ipv4(struct netif *netif, struct pbuf *p, const ip4_addr_t* addr);
+#endif
+#if LWIP_IPV6
+static err_t netif_loop_output_ipv6(struct netif *netif, struct pbuf *p, const ip6_addr_t* addr);
+#endif
+
+
+static struct netif loop_netif;
+
+/**
+ * Initialize a lwip network interface structure for a loopback interface
+ *
+ * @param netif the lwip network interface structure for this loopif
+ * @return ERR_OK if the loopif is initialized
+ *         ERR_MEM if private data couldn't be allocated
+ */
+static err_t
+netif_loopif_init(struct netif *netif)
+{
+  /* initialize the snmp variables and counters inside the struct netif
+   * ifSpeed: no assumption can be made!
+   */
+  MIB2_INIT_NETIF(netif, snmp_ifType_softwareLoopback, 0);
+
+  netif->name[0] = 'l';
+  netif->name[1] = 'o';
+#if LWIP_IPV4
+  netif->output = netif_loop_output_ipv4;
+#endif
+#if LWIP_IPV6
+  netif->output_ip6 = netif_loop_output_ipv6;
+#endif
+#if LWIP_LOOPIF_MULTICAST
+  netif->flags |= NETIF_FLAG_IGMP;
+#endif
+  return ERR_OK;
+}
+#endif /* LWIP_HAVE_LOOPIF */
+
+void
+netif_init(void)
+{
+#if LWIP_HAVE_LOOPIF
+#if LWIP_IPV4
+#define LOOPIF_ADDRINIT &loop_ipaddr, &loop_netmask, &loop_gw,
+  ip4_addr_t loop_ipaddr, loop_netmask, loop_gw;
+  IP4_ADDR(&loop_gw, 127,0,0,1);
+  IP4_ADDR(&loop_ipaddr, 127,0,0,1);
+  IP4_ADDR(&loop_netmask, 255,0,0,0);
+#else /* LWIP_IPV4 */
+#define LOOPIF_ADDRINIT
+#endif /* LWIP_IPV4 */
+
+#if NO_SYS
+  netif_add(&loop_netif, LOOPIF_ADDRINIT NULL, netif_loopif_init, ip_input);
+#else  /* NO_SYS */
+  netif_add(&loop_netif, LOOPIF_ADDRINIT NULL, netif_loopif_init, tcpip_input);
+#endif /* NO_SYS */
+
+#if LWIP_IPV6
+  IP_ADDR6_HOST(loop_netif.ip6_addr, 0, 0, 0, 0x00000001UL);
+  loop_netif.ip6_addr_state[0] = IP6_ADDR_VALID;
+#endif /* LWIP_IPV6 */
+
+  netif_set_link_up(&loop_netif);
+  netif_set_up(&loop_netif);
+
+#endif /* LWIP_HAVE_LOOPIF */
+}
+
+/**
+ * @ingroup lwip_nosys
+ * Forwards a received packet for input processing with
+ * ethernet_input() or ip_input() depending on netif flags.
+ * Don't call directly, pass to netif_add() and call
+ * netif->input().
+ * Only works if the netif driver correctly sets 
+ * NETIF_FLAG_ETHARP and/or NETIF_FLAG_ETHERNET flag!
+ */
+err_t
+netif_input(struct pbuf *p, struct netif *inp)
+{
+#if LWIP_ETHERNET
+  if (inp->flags & (NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET)) {
+    return ethernet_input(p, inp);
+  } else
+#endif /* LWIP_ETHERNET */
+  return ip_input(p, inp);
+}
+
+/**
+ * @ingroup netif
+ * Add a network interface to the list of lwIP netifs.
+ *
+ * @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.\n
+ * It is recommended to use a function that passes the input directly
+ * to the stack (netif_input(), NO_SYS=1 mode) or via sending a
+ * message to TCPIP thread (tcpip_input(), NO_SYS=0 mode).\n
+ * These functions use netif flags NETIF_FLAG_ETHARP and NETIF_FLAG_ETHERNET
+ * to decide whether to forward to ethernet_input() or ip_input().
+ * In other words, the functions only work when the netif
+ * driver is implemented correctly!\n
+ * Most members of struct netif should be be initialized by the 
+ * netif init function = netif driver (init parameter of this function).\n
+ * IPv6: Don't forget to call netif_create_ip6_linklocal_address() after
+ * setting the MAC address in struct netif.hwaddr
+ * (IPv6 requires a link-local address).
+ * 
+ * @return netif, or NULL if failed.
+ */
+struct netif *
+netif_add(struct netif *netif,
+#if LWIP_IPV4
+          const ip4_addr_t *ipaddr, const ip4_addr_t *netmask, const ip4_addr_t *gw,
+#endif /* LWIP_IPV4 */
+          void *state, netif_init_fn init, netif_input_fn input)
+{
+#if LWIP_IPV6
+  s8_t i;
+#endif
+
+  LWIP_ASSERT("No init function given", init != NULL);
+
+  /* reset new interface configuration state */
+#if LWIP_IPV4
+  ip_addr_set_zero_ip4(&netif->ip_addr);
+  ip_addr_set_zero_ip4(&netif->netmask);
+  ip_addr_set_zero_ip4(&netif->gw);
+#endif /* LWIP_IPV4 */
+#if LWIP_IPV6
+  for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+    ip_addr_set_zero_ip6(&netif->ip6_addr[i]);
+    netif->ip6_addr_state[i] = IP6_ADDR_INVALID;
+  }
+  netif->output_ip6 = netif_null_output_ip6;
+#endif /* LWIP_IPV6 */
+  NETIF_SET_CHECKSUM_CTRL(netif, NETIF_CHECKSUM_ENABLE_ALL);
+  netif->flags = 0;
+#ifdef netif_get_client_data
+  memset(netif->client_data, 0, sizeof(netif->client_data));
+#endif /* LWIP_NUM_NETIF_CLIENT_DATA */
+#if LWIP_IPV6_AUTOCONFIG
+  /* IPv6 address autoconfiguration not enabled by default */
+  netif->ip6_autoconfig_enabled = 0;
+#endif /* LWIP_IPV6_AUTOCONFIG */
+#if LWIP_IPV6_SEND_ROUTER_SOLICIT
+  netif->rs_count = LWIP_ND6_MAX_MULTICAST_SOLICIT;
+#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */
+#if LWIP_NETIF_STATUS_CALLBACK
+  netif->status_callback = NULL;
+#endif /* LWIP_NETIF_STATUS_CALLBACK */
+#if LWIP_NETIF_LINK_CALLBACK
+  netif->link_callback = NULL;
+#endif /* LWIP_NETIF_LINK_CALLBACK */
+#if LWIP_IGMP
+  netif->igmp_mac_filter = NULL;
+#endif /* LWIP_IGMP */
+#if LWIP_IPV6 && LWIP_IPV6_MLD
+  netif->mld_mac_filter = NULL;
+#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
+#if ENABLE_LOOPBACK
+  netif->loop_first = NULL;
+  netif->loop_last = NULL;
+#endif /* ENABLE_LOOPBACK */
+
+  /* remember netif specific state information data */
+  netif->state = state;
+  netif->num = netif_num++;
+  netif->input = input;
+
+  NETIF_SET_HWADDRHINT(netif, NULL);
+#if ENABLE_LOOPBACK && LWIP_LOOPBACK_MAX_PBUFS
+  netif->loop_cnt_current = 0;
+#endif /* ENABLE_LOOPBACK && LWIP_LOOPBACK_MAX_PBUFS */
+
+#if LWIP_IPV4
+  netif_set_addr(netif, ipaddr, netmask, gw);
+#endif /* LWIP_IPV4 */
+
+  /* call user specified initialization function for netif */
+  if (init(netif) != ERR_OK) {
+    return NULL;
+  }
+
+  /* add this netif to the list */
+  netif->next = netif_list;
+  netif_list = netif;
+  mib2_netif_added(netif);
+
+#if LWIP_IGMP
+  /* start IGMP processing */
+  if (netif->flags & NETIF_FLAG_IGMP) {
+    igmp_start(netif);
+  }
+#endif /* LWIP_IGMP */
+
+  LWIP_DEBUGF(NETIF_DEBUG, ("netif: added interface %c%c IP",
+    netif->name[0], netif->name[1]));
+#if LWIP_IPV4
+  LWIP_DEBUGF(NETIF_DEBUG, (" addr "));
+  ip4_addr_debug_print(NETIF_DEBUG, ipaddr);
+  LWIP_DEBUGF(NETIF_DEBUG, (" netmask "));
+  ip4_addr_debug_print(NETIF_DEBUG, netmask);
+  LWIP_DEBUGF(NETIF_DEBUG, (" gw "));
+  ip4_addr_debug_print(NETIF_DEBUG, gw);
+#endif /* LWIP_IPV4 */
+  LWIP_DEBUGF(NETIF_DEBUG, ("\n"));
+  return netif;
+}
+
+#if LWIP_IPV4
+/**
+ * @ingroup netif_ip4
+ * Change IP address configuration for a network interface (including netmask
+ * and default gateway).
+ *
+ * @param netif the network interface to change
+ * @param ipaddr the new IP address
+ * @param netmask the new netmask
+ * @param gw the new default gateway
+ */
+void
+netif_set_addr(struct netif *netif, const ip4_addr_t *ipaddr, const ip4_addr_t *netmask,
+    const ip4_addr_t *gw)
+{
+  if (ip4_addr_isany(ipaddr)) {
+    /* when removing an address, we have to remove it *before* changing netmask/gw
+       to ensure that tcp RST segment can be sent correctly */
+    netif_set_ipaddr(netif, ipaddr);
+    netif_set_netmask(netif, netmask);
+    netif_set_gw(netif, gw);
+  } else {
+    netif_set_netmask(netif, netmask);
+    netif_set_gw(netif, gw);
+    /* set ipaddr last to ensure netmask/gw have been set when status callback is called */
+    netif_set_ipaddr(netif, ipaddr);
+  }
+}
+#endif /* LWIP_IPV4*/
+
+/**
+ * @ingroup netif
+ * Remove a network interface from the list of lwIP netifs.
+ *
+ * @param netif the network interface to remove
+ */
+void
+netif_remove(struct netif *netif)
+{
+#if LWIP_IPV6
+  int i;
+#endif
+
+  if (netif == NULL) {
+    return;
+  }
+
+#if LWIP_IPV4
+  if (!ip4_addr_isany_val(*netif_ip4_addr(netif))) {
+#if LWIP_TCP
+    tcp_netif_ip_addr_changed(netif_ip_addr4(netif), NULL);
+#endif /* LWIP_TCP */
+#if LWIP_UDP
+    udp_netif_ip_addr_changed(netif_ip_addr4(netif), NULL);
+#endif /* LWIP_UDP */
+#if LWIP_RAW
+    raw_netif_ip_addr_changed(netif_ip_addr4(netif), NULL);
+#endif /* LWIP_RAW */
+  }
+
+#if LWIP_IGMP
+  /* stop IGMP processing */
+  if (netif->flags & NETIF_FLAG_IGMP) {
+    igmp_stop(netif);
+  }
+#endif /* LWIP_IGMP */
+#endif /* LWIP_IPV4*/
+
+#if LWIP_IPV6
+  for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+    if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i))) {
+#if LWIP_TCP
+      tcp_netif_ip_addr_changed(netif_ip_addr6(netif, i), NULL);
+#endif /* LWIP_TCP */
+#if LWIP_UDP
+      udp_netif_ip_addr_changed(netif_ip_addr6(netif, i), NULL);
+#endif /* LWIP_UDP */
+#if LWIP_RAW
+      raw_netif_ip_addr_changed(netif_ip_addr6(netif, i), NULL);
+#endif /* LWIP_RAW */
+    }
+  }
+#if LWIP_IPV6_MLD
+  /* stop MLD processing */
+  mld6_stop(netif);
+#endif /* LWIP_IPV6_MLD */
+#endif /* LWIP_IPV6 */
+  if (netif_is_up(netif)) {
+    /* set netif down before removing (call callback function) */
+    netif_set_down(netif);
+  }
+
+  mib2_remove_ip4(netif);
+
+  /* this netif is default? */
+  if (netif_default == netif) {
+    /* reset default netif */
+    netif_set_default(NULL);
+  }
+  /*  is it the first netif? */
+  if (netif_list == netif) {
+    netif_list = netif->next;
+  } else {
+    /*  look for netif further down the list */
+    struct netif * tmp_netif;
+    for (tmp_netif = netif_list; tmp_netif != NULL; tmp_netif = tmp_netif->next) {
+      if (tmp_netif->next == netif) {
+        tmp_netif->next = netif->next;
+        break;
+      }
+    }
+    if (tmp_netif == NULL) {
+      return; /* netif is not on the list */
+    }
+  }
+  mib2_netif_removed(netif);
+#if LWIP_NETIF_REMOVE_CALLBACK
+  if (netif->remove_callback) {
+    netif->remove_callback(netif);
+  }
+#endif /* LWIP_NETIF_REMOVE_CALLBACK */
+  LWIP_DEBUGF( NETIF_DEBUG, ("netif_remove: removed netif\n") );
+}
+
+/**
+ * @ingroup netif
+ * Find a network interface by searching for its name
+ *
+ * @param name the name of the netif (like netif->name) plus concatenated number
+ * in ascii representation (e.g. 'en0')
+ */
+struct netif *
+netif_find(const char *name)
+{
+  struct netif *netif;
+  u8_t num;
+
+  if (name == NULL) {
+    return NULL;
+  }
+
+  num = (u8_t)(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;
+}
+
+#if LWIP_IPV4
+/**
+ * @ingroup netif_ip4
+ * Change the IP address of a network interface
+ *
+ * @param netif the network interface to change
+ * @param ipaddr the new IP address
+ *
+ * @note call netif_set_addr() if you also want to change netmask and
+ * default gateway
+ */
+void
+netif_set_ipaddr(struct netif *netif, const ip4_addr_t *ipaddr)
+{
+  ip_addr_t new_addr;
+  *ip_2_ip4(&new_addr) = (ipaddr ? *ipaddr : *IP4_ADDR_ANY4);
+  IP_SET_TYPE_VAL(new_addr, IPADDR_TYPE_V4);
+
+  /* address is actually being changed? */
+  if (ip4_addr_cmp(ip_2_ip4(&new_addr), netif_ip4_addr(netif)) == 0) {
+    LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_STATE, ("netif_set_ipaddr: netif address being changed\n"));
+#if LWIP_TCP
+    tcp_netif_ip_addr_changed(netif_ip_addr4(netif), &new_addr);
+#endif /* LWIP_TCP */
+#if LWIP_UDP
+    udp_netif_ip_addr_changed(netif_ip_addr4(netif), &new_addr);
+#endif /* LWIP_UDP */
+#if LWIP_RAW
+    raw_netif_ip_addr_changed(netif_ip_addr4(netif), &new_addr);
+#endif /* LWIP_RAW */
+
+    mib2_remove_ip4(netif);
+    mib2_remove_route_ip4(0, netif);
+    /* set new IP address to netif */
+    ip4_addr_set(ip_2_ip4(&netif->ip_addr), ipaddr);
+    IP_SET_TYPE_VAL(netif->ip_addr, IPADDR_TYPE_V4);
+    mib2_add_ip4(netif);
+    mib2_add_route_ip4(0, netif);
+
+    netif_issue_reports(netif, NETIF_REPORT_TYPE_IPV4);
+
+    NETIF_STATUS_CALLBACK(netif);
+  }
+
+  LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("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_16(netif_ip4_addr(netif)),
+    ip4_addr2_16(netif_ip4_addr(netif)),
+    ip4_addr3_16(netif_ip4_addr(netif)),
+    ip4_addr4_16(netif_ip4_addr(netif))));
+}
+
+/**
+ * @ingroup netif_ip4
+ * Change the default gateway for a network interface
+ *
+ * @param netif the network interface to change
+ * @param gw the new default gateway
+ *
+ * @note call netif_set_addr() if you also want to change ip address and netmask
+ */
+void
+netif_set_gw(struct netif *netif, const ip4_addr_t *gw)
+{
+  ip4_addr_set(ip_2_ip4(&netif->gw), gw);
+  IP_SET_TYPE_VAL(netif->gw, IPADDR_TYPE_V4);
+  LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("netif: GW address of interface %c%c set to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+    netif->name[0], netif->name[1],
+    ip4_addr1_16(netif_ip4_gw(netif)),
+    ip4_addr2_16(netif_ip4_gw(netif)),
+    ip4_addr3_16(netif_ip4_gw(netif)),
+    ip4_addr4_16(netif_ip4_gw(netif))));
+}
+
+/**
+ * @ingroup netif_ip4
+ * Change the netmask of a network interface
+ *
+ * @param netif the network interface to change
+ * @param netmask the new netmask
+ *
+ * @note call netif_set_addr() if you also want to change ip address and
+ * default gateway
+ */
+void
+netif_set_netmask(struct netif *netif, const ip4_addr_t *netmask)
+{
+  mib2_remove_route_ip4(0, netif);
+  /* set new netmask to netif */
+  ip4_addr_set(ip_2_ip4(&netif->netmask), netmask);
+  IP_SET_TYPE_VAL(netif->netmask, IPADDR_TYPE_V4);
+  mib2_add_route_ip4(0, netif);
+  LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("netif: netmask of interface %c%c set to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+    netif->name[0], netif->name[1],
+    ip4_addr1_16(netif_ip4_netmask(netif)),
+    ip4_addr2_16(netif_ip4_netmask(netif)),
+    ip4_addr3_16(netif_ip4_netmask(netif)),
+    ip4_addr4_16(netif_ip4_netmask(netif))));
+}
+#endif /* LWIP_IPV4 */
+
+/**
+ * @ingroup netif
+ * Set a network interface as the default network interface
+ * (used to output all packets for which no specific route is found)
+ *
+ * @param netif the default network interface
+ */
+void
+netif_set_default(struct netif *netif)
+{
+  if (netif == NULL) {
+    /* remove default route */
+    mib2_remove_route_ip4(1, netif);
+  } else {
+    /* install default route */
+    mib2_add_route_ip4(1, netif);
+  }
+  netif_default = netif;
+  LWIP_DEBUGF(NETIF_DEBUG, ("netif: setting default interface %c%c\n",
+           netif ? netif->name[0] : '\'', netif ? netif->name[1] : '\''));
+}
+
+/**
+ * @ingroup netif
+ * Bring an interface up, available for processing
+ * traffic.
+ */
+void
+netif_set_up(struct netif *netif)
+{
+  if (!(netif->flags & NETIF_FLAG_UP)) {
+    netif->flags |= NETIF_FLAG_UP;
+
+    MIB2_COPY_SYSUPTIME_TO(&netif->ts);
+
+    NETIF_STATUS_CALLBACK(netif);
+
+    if (netif->flags & NETIF_FLAG_LINK_UP) {
+      netif_issue_reports(netif, NETIF_REPORT_TYPE_IPV4|NETIF_REPORT_TYPE_IPV6);
+    }
+  }
+}
+
+/** Send ARP/IGMP/MLD/RS events, e.g. on link-up/netif-up or addr-change
+ */
+static void
+netif_issue_reports(struct netif* netif, u8_t report_type)
+{
+#if LWIP_IPV4
+  if ((report_type & NETIF_REPORT_TYPE_IPV4) &&
+      !ip4_addr_isany_val(*netif_ip4_addr(netif))) {
+#if LWIP_ARP
+    /* For Ethernet network interfaces, we would like to send a "gratuitous ARP" */
+    if (netif->flags & (NETIF_FLAG_ETHARP)) {
+      etharp_gratuitous(netif);
+    }
+#endif /* LWIP_ARP */
+
+#if LWIP_IGMP
+    /* resend IGMP memberships */
+    if (netif->flags & NETIF_FLAG_IGMP) {
+      igmp_report_groups(netif);
+    }
+#endif /* LWIP_IGMP */
+  }
+#endif /* LWIP_IPV4 */
+
+#if LWIP_IPV6
+  if (report_type & NETIF_REPORT_TYPE_IPV6) {
+#if LWIP_IPV6_MLD
+    /* send mld memberships */
+    mld6_report_groups(netif);
+#endif /* LWIP_IPV6_MLD */
+#if LWIP_IPV6_SEND_ROUTER_SOLICIT
+    /* Send Router Solicitation messages. */
+    netif->rs_count = LWIP_ND6_MAX_MULTICAST_SOLICIT;
+#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */
+  }
+#endif /* LWIP_IPV6 */
+}
+
+/**
+ * @ingroup netif
+ * Bring an interface down, disabling any traffic processing.
+ */
+void
+netif_set_down(struct netif *netif)
+{
+  if (netif->flags & NETIF_FLAG_UP) {
+    netif->flags &= ~NETIF_FLAG_UP;
+    MIB2_COPY_SYSUPTIME_TO(&netif->ts);
+
+#if LWIP_IPV4 && LWIP_ARP
+    if (netif->flags & NETIF_FLAG_ETHARP) {
+      etharp_cleanup_netif(netif);
+    }
+#endif /* LWIP_IPV4 && LWIP_ARP */
+
+#if LWIP_IPV6
+    nd6_cleanup_netif(netif);
+#endif /* LWIP_IPV6 */
+
+    NETIF_STATUS_CALLBACK(netif);
+  }
+}
+
+#if LWIP_NETIF_STATUS_CALLBACK
+/**
+ * @ingroup netif
+ * Set callback to be called when interface is brought up/down or address is changed while up
+ */
+void
+netif_set_status_callback(struct netif *netif, netif_status_callback_fn status_callback)
+{
+  if (netif) {
+    netif->status_callback = status_callback;
+  }
+}
+#endif /* LWIP_NETIF_STATUS_CALLBACK */
+
+#if LWIP_NETIF_REMOVE_CALLBACK
+/**
+ * @ingroup netif
+ * Set callback to be called when the interface has been removed
+ */
+void
+netif_set_remove_callback(struct netif *netif, netif_status_callback_fn remove_callback)
+{
+  if (netif) {
+    netif->remove_callback = remove_callback;
+  }
+}
+#endif /* LWIP_NETIF_REMOVE_CALLBACK */
+
+/**
+ * @ingroup netif
+ * Called by a driver when its link goes up
+ */
+void
+netif_set_link_up(struct netif *netif)
+{
+  if (!(netif->flags & NETIF_FLAG_LINK_UP)) {
+    netif->flags |= NETIF_FLAG_LINK_UP;
+
+#if LWIP_DHCP
+    dhcp_network_changed(netif);
+#endif /* LWIP_DHCP */
+
+#if LWIP_AUTOIP
+    autoip_network_changed(netif);
+#endif /* LWIP_AUTOIP */
+
+    if (netif->flags & NETIF_FLAG_UP) {
+      netif_issue_reports(netif, NETIF_REPORT_TYPE_IPV4|NETIF_REPORT_TYPE_IPV6);
+    }
+    NETIF_LINK_CALLBACK(netif);
+  }
+}
+
+/**
+ * @ingroup netif
+ * Called by a driver when its link goes down
+ */
+void
+netif_set_link_down(struct netif *netif )
+{
+  if (netif->flags & NETIF_FLAG_LINK_UP) {
+    netif->flags &= ~NETIF_FLAG_LINK_UP;
+    NETIF_LINK_CALLBACK(netif);
+  }
+}
+
+#if LWIP_NETIF_LINK_CALLBACK
+/**
+ * @ingroup netif
+ * Set callback to be called when link is brought up/down
+ */
+void
+netif_set_link_callback(struct netif *netif, netif_status_callback_fn link_callback)
+{
+  if (netif) {
+    netif->link_callback = link_callback;
+  }
+}
+#endif /* LWIP_NETIF_LINK_CALLBACK */
+
+#if ENABLE_LOOPBACK
+/**
+ * @ingroup netif
+ * Send an IP packet to be received on the same netif (loopif-like).
+ * The pbuf is simply copied and handed back to netif->input.
+ * In multithreaded mode, this is done directly since netif->input must put
+ * the packet on a queue.
+ * In callback mode, the packet is put on an internal queue and is fed to
+ * netif->input by netif_poll().
+ *
+ * @param netif the lwip network interface structure
+ * @param p the (IP) packet to 'send'
+ * @return ERR_OK if the packet has been sent
+ *         ERR_MEM if the pbuf used to copy the packet couldn't be allocated
+ */
+err_t
+netif_loop_output(struct netif *netif, struct pbuf *p)
+{
+  struct pbuf *r;
+  err_t err;
+  struct pbuf *last;
+#if LWIP_LOOPBACK_MAX_PBUFS
+  u16_t clen = 0;
+#endif /* LWIP_LOOPBACK_MAX_PBUFS */
+  /* If we have a loopif, SNMP counters are adjusted for it,
+   * if not they are adjusted for 'netif'. */
+#if MIB2_STATS
+#if LWIP_HAVE_LOOPIF
+  struct netif *stats_if = &loop_netif;
+#else /* LWIP_HAVE_LOOPIF */
+  struct netif *stats_if = netif;
+#endif /* LWIP_HAVE_LOOPIF */
+#endif /* MIB2_STATS */
+  SYS_ARCH_DECL_PROTECT(lev);
+
+  /* Allocate a new pbuf */
+  r = pbuf_alloc(PBUF_LINK, p->tot_len, PBUF_RAM);
+  if (r == NULL) {
+    LINK_STATS_INC(link.memerr);
+    LINK_STATS_INC(link.drop);
+    MIB2_STATS_NETIF_INC(stats_if, ifoutdiscards);
+    return ERR_MEM;
+  }
+#if LWIP_LOOPBACK_MAX_PBUFS
+  clen = pbuf_clen(r);
+  /* check for overflow or too many pbuf on queue */
+  if (((netif->loop_cnt_current + clen) < netif->loop_cnt_current) ||
+     ((netif->loop_cnt_current + clen) > LWIP_LOOPBACK_MAX_PBUFS)) {
+    pbuf_free(r);
+    LINK_STATS_INC(link.memerr);
+    LINK_STATS_INC(link.drop);
+    MIB2_STATS_NETIF_INC(stats_if, ifoutdiscards);
+    return ERR_MEM;
+  }
+  netif->loop_cnt_current += clen;
+#endif /* LWIP_LOOPBACK_MAX_PBUFS */
+
+  /* Copy the whole pbuf queue p into the single pbuf r */
+  if ((err = pbuf_copy(r, p)) != ERR_OK) {
+    pbuf_free(r);
+    LINK_STATS_INC(link.memerr);
+    LINK_STATS_INC(link.drop);
+    MIB2_STATS_NETIF_INC(stats_if, ifoutdiscards);
+    return err;
+  }
+
+  /* Put the packet on a linked list which gets emptied through calling
+     netif_poll(). */
+
+  /* let last point to the last pbuf in chain r */
+  for (last = r; last->next != NULL; last = last->next);
+
+  SYS_ARCH_PROTECT(lev);
+  if (netif->loop_first != NULL) {
+    LWIP_ASSERT("if first != NULL, last must also be != NULL", netif->loop_last != NULL);
+    netif->loop_last->next = r;
+    netif->loop_last = last;
+  } else {
+    netif->loop_first = r;
+    netif->loop_last = last;
+  }
+  SYS_ARCH_UNPROTECT(lev);
+
+  LINK_STATS_INC(link.xmit);
+  MIB2_STATS_NETIF_ADD(stats_if, ifoutoctets, p->tot_len);
+  MIB2_STATS_NETIF_INC(stats_if, ifoutucastpkts);
+
+#if LWIP_NETIF_LOOPBACK_MULTITHREADING
+  /* For multithreading environment, schedule a call to netif_poll */
+  tcpip_callback_with_block((tcpip_callback_fn)netif_poll, netif, 0);
+#endif /* LWIP_NETIF_LOOPBACK_MULTITHREADING */
+
+  return ERR_OK;
+}
+
+#if LWIP_HAVE_LOOPIF
+#if LWIP_IPV4
+static err_t
+netif_loop_output_ipv4(struct netif *netif, struct pbuf *p, const ip4_addr_t* addr)
+{
+  LWIP_UNUSED_ARG(addr);
+  return netif_loop_output(netif, p);
+}
+#endif /* LWIP_IPV4 */
+
+#if LWIP_IPV6
+static err_t
+netif_loop_output_ipv6(struct netif *netif, struct pbuf *p, const ip6_addr_t* addr)
+{
+  LWIP_UNUSED_ARG(addr);
+  return netif_loop_output(netif, p);
+}
+#endif /* LWIP_IPV6 */
+#endif /* LWIP_HAVE_LOOPIF */
+
+
+/**
+ * Call netif_poll() in the main loop of your application. This is to prevent
+ * reentering non-reentrant functions like tcp_input(). Packets passed to
+ * netif_loop_output() are put on a list that is passed to netif->input() by
+ * netif_poll().
+ */
+void
+netif_poll(struct netif *netif)
+{
+  /* If we have a loopif, SNMP counters are adjusted for it,
+   * if not they are adjusted for 'netif'. */
+#if MIB2_STATS
+#if LWIP_HAVE_LOOPIF
+  struct netif *stats_if = &loop_netif;
+#else /* LWIP_HAVE_LOOPIF */
+  struct netif *stats_if = netif;
+#endif /* LWIP_HAVE_LOOPIF */
+#endif /* MIB2_STATS */
+  SYS_ARCH_DECL_PROTECT(lev);
+
+  /* Get a packet from the list. With SYS_LIGHTWEIGHT_PROT=1, this is protected */
+  SYS_ARCH_PROTECT(lev);
+  while (netif->loop_first != NULL) {
+    struct pbuf *in, *in_end;
+#if LWIP_LOOPBACK_MAX_PBUFS
+    u8_t clen = 1;
+#endif /* LWIP_LOOPBACK_MAX_PBUFS */
+
+    in = in_end = netif->loop_first;
+    while (in_end->len != in_end->tot_len) {
+      LWIP_ASSERT("bogus pbuf: len != tot_len but next == NULL!", in_end->next != NULL);
+      in_end = in_end->next;
+#if LWIP_LOOPBACK_MAX_PBUFS
+      clen++;
+#endif /* LWIP_LOOPBACK_MAX_PBUFS */
+    }
+#if LWIP_LOOPBACK_MAX_PBUFS
+    /* adjust the number of pbufs on queue */
+    LWIP_ASSERT("netif->loop_cnt_current underflow",
+      ((netif->loop_cnt_current - clen) < netif->loop_cnt_current));
+    netif->loop_cnt_current -= clen;
+#endif /* LWIP_LOOPBACK_MAX_PBUFS */
+
+    /* 'in_end' now points to the last pbuf from 'in' */
+    if (in_end == netif->loop_last) {
+      /* this was the last pbuf in the list */
+      netif->loop_first = netif->loop_last = NULL;
+    } else {
+      /* pop the pbuf off the list */
+      netif->loop_first = in_end->next;
+      LWIP_ASSERT("should not be null since first != last!", netif->loop_first != NULL);
+    }
+    /* De-queue the pbuf from its successors on the 'loop_' list. */
+    in_end->next = NULL;
+    SYS_ARCH_UNPROTECT(lev);
+
+    LINK_STATS_INC(link.recv);
+    MIB2_STATS_NETIF_ADD(stats_if, ifinoctets, in->tot_len);
+    MIB2_STATS_NETIF_INC(stats_if, ifinucastpkts);
+    /* loopback packets are always IP packets! */
+    if (ip_input(in, netif) != ERR_OK) {
+      pbuf_free(in);
+    }
+    SYS_ARCH_PROTECT(lev);
+  }
+  SYS_ARCH_UNPROTECT(lev);
+}
+
+#if !LWIP_NETIF_LOOPBACK_MULTITHREADING
+/**
+ * Calls netif_poll() for every netif on the netif_list.
+ */
+void
+netif_poll_all(void)
+{
+  struct netif *netif = netif_list;
+  /* loop through netifs */
+  while (netif != NULL) {
+    netif_poll(netif);
+    /* proceed to next network interface */
+    netif = netif->next;
+  }
+}
+#endif /* !LWIP_NETIF_LOOPBACK_MULTITHREADING */
+#endif /* ENABLE_LOOPBACK */
+
+#if LWIP_NUM_NETIF_CLIENT_DATA > 0
+/**
+ * @ingroup netif_cd
+ * Allocate an index to store data in client_data member of struct netif.
+ * Returned value is an index in mentioned array.
+ * @see LWIP_NUM_NETIF_CLIENT_DATA
+ */
+u8_t
+netif_alloc_client_data_id(void)
+{
+  u8_t result = netif_client_id;
+  netif_client_id++;
+
+  LWIP_ASSERT("Increase LWIP_NUM_NETIF_CLIENT_DATA in lwipopts.h", result < LWIP_NUM_NETIF_CLIENT_DATA);
+  return result + LWIP_NETIF_CLIENT_DATA_INDEX_MAX;
+}
+#endif
+
+#if LWIP_IPV6
+/**
+ * @ingroup netif_ip6
+ * Change an IPv6 address of a network interface
+ *
+ * @param netif the network interface to change
+ * @param addr_idx index of the IPv6 address
+ * @param addr6 the new IPv6 address
+ *
+ * @note call netif_ip6_addr_set_state() to set the address valid/temptative
+ */
+void
+netif_ip6_addr_set(struct netif *netif, s8_t addr_idx, const ip6_addr_t *addr6)
+{
+  LWIP_ASSERT("addr6 != NULL", addr6 != NULL);
+  netif_ip6_addr_set_parts(netif, addr_idx, addr6->addr[0], addr6->addr[1],
+    addr6->addr[2], addr6->addr[3]);
+}
+
+/*
+ * Change an IPv6 address of a network interface (internal version taking 4 * u32_t)
+ *
+ * @param netif the network interface to change
+ * @param addr_idx index of the IPv6 address
+ * @param i0 word0 of the new IPv6 address
+ * @param i1 word1 of the new IPv6 address
+ * @param i2 word2 of the new IPv6 address
+ * @param i3 word3 of the new IPv6 address
+ */
+void
+netif_ip6_addr_set_parts(struct netif *netif, s8_t addr_idx, u32_t i0, u32_t i1, u32_t i2, u32_t i3)
+{
+  const ip6_addr_t *old_addr;
+  LWIP_ASSERT("netif != NULL", netif != NULL);
+  LWIP_ASSERT("invalid index", addr_idx < LWIP_IPV6_NUM_ADDRESSES);
+
+  old_addr = netif_ip6_addr(netif, addr_idx);
+  /* address is actually being changed? */
+  if ((old_addr->addr[0] != i0) || (old_addr->addr[1] != i1) ||
+      (old_addr->addr[2] != i2) || (old_addr->addr[3] != i3)) {
+    LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_STATE, ("netif_ip6_addr_set: netif address being changed\n"));
+
+    if (netif_ip6_addr_state(netif, addr_idx) & IP6_ADDR_VALID) {
+#if LWIP_TCP || LWIP_UDP
+      ip_addr_t new_ipaddr;
+      IP_ADDR6(&new_ipaddr, i0, i1, i2, i3);
+#endif /* LWIP_TCP || LWIP_UDP */
+#if LWIP_TCP
+      tcp_netif_ip_addr_changed(netif_ip_addr6(netif, addr_idx), &new_ipaddr);
+#endif /* LWIP_TCP */
+#if LWIP_UDP
+      udp_netif_ip_addr_changed(netif_ip_addr6(netif, addr_idx), &new_ipaddr);
+#endif /* LWIP_UDP */
+#if LWIP_RAW
+      raw_netif_ip_addr_changed(netif_ip_addr6(netif, addr_idx), &new_ipaddr);
+#endif /* LWIP_RAW */
+    }
+    /* @todo: remove/readd mib2 ip6 entries? */
+
+    IP6_ADDR(ip_2_ip6(&(netif->ip6_addr[addr_idx])), i0, i1, i2, i3);
+    IP_SET_TYPE_VAL(netif->ip6_addr[addr_idx], IPADDR_TYPE_V6);
+
+    if (netif_ip6_addr_state(netif, addr_idx) & IP6_ADDR_VALID) {
+      netif_issue_reports(netif, NETIF_REPORT_TYPE_IPV6);
+      NETIF_STATUS_CALLBACK(netif);
+    }
+  }
+
+  LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("netif: IPv6 address %d of interface %c%c set to %s/0x%"X8_F"\n",
+    addr_idx, netif->name[0], netif->name[1], ip6addr_ntoa(netif_ip6_addr(netif, addr_idx)),
+    netif_ip6_addr_state(netif, addr_idx)));
+}
+
+/**
+ * @ingroup netif_ip6
+ * Change the state of an IPv6 address of a network interface
+ * (INVALID, TEMPTATIVE, PREFERRED, DEPRECATED, where TEMPTATIVE
+ * includes the number of checks done, see ip6_addr.h)
+ *
+ * @param netif the network interface to change
+ * @param addr_idx index of the IPv6 address
+ * @param state the new IPv6 address state
+ */
+void
+netif_ip6_addr_set_state(struct netif* netif, s8_t addr_idx, u8_t state)
+{
+  u8_t old_state;
+  LWIP_ASSERT("netif != NULL", netif != NULL);
+  LWIP_ASSERT("invalid index", addr_idx < LWIP_IPV6_NUM_ADDRESSES);
+
+  old_state = netif_ip6_addr_state(netif, addr_idx);
+  /* state is actually being changed? */
+  if (old_state != state) {
+    u8_t old_valid = old_state & IP6_ADDR_VALID;
+    u8_t new_valid = state & IP6_ADDR_VALID;
+    LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_STATE, ("netif_ip6_addr_set_state: netif address state being changed\n"));
+
+#if LWIP_IPV6_MLD
+    /* Reevaluate solicited-node multicast group membership. */
+    if (netif->flags & NETIF_FLAG_MLD6) {
+      nd6_adjust_mld_membership(netif, addr_idx, state);
+    }
+#endif /* LWIP_IPV6_MLD */
+
+    if (old_valid && !new_valid) {
+      /* address about to be removed by setting invalid */
+#if LWIP_TCP
+      tcp_netif_ip_addr_changed(netif_ip_addr6(netif, addr_idx), NULL);
+#endif /* LWIP_TCP */
+#if LWIP_UDP
+      udp_netif_ip_addr_changed(netif_ip_addr6(netif, addr_idx), NULL);
+#endif /* LWIP_UDP */
+#if LWIP_RAW
+      raw_netif_ip_addr_changed(netif_ip_addr6(netif, addr_idx), NULL);
+#endif /* LWIP_RAW */
+      /* @todo: remove mib2 ip6 entries? */
+    }
+    netif->ip6_addr_state[addr_idx] = state;
+
+    if (!old_valid && new_valid) {
+      /* address added by setting valid */
+      /* @todo: add mib2 ip6 entries? */
+      netif_issue_reports(netif, NETIF_REPORT_TYPE_IPV6);
+    }
+    if ((old_state & IP6_ADDR_PREFERRED) != (state & IP6_ADDR_PREFERRED)) {
+      /* address state has changed (valid flag changed or switched between
+         preferred and deprecated) -> call the callback function */
+      NETIF_STATUS_CALLBACK(netif);
+    }
+  }
+
+  LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("netif: IPv6 address %d of interface %c%c set to %s/0x%"X8_F"\n",
+    addr_idx, netif->name[0], netif->name[1], ip6addr_ntoa(netif_ip6_addr(netif, addr_idx)),
+    netif_ip6_addr_state(netif, addr_idx)));
+}
+
+/**
+ * Checks if a specific address is assigned to the netif and returns its
+ * index.
+ *
+ * @param netif the netif to check
+ * @param ip6addr the IPv6 address to find
+ * @return >= 0: address found, this is its index
+ *         -1: address not found on this netif
+ */
+s8_t
+netif_get_ip6_addr_match(struct netif *netif, const ip6_addr_t *ip6addr)
+{
+  s8_t i;
+  for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+    if (!ip6_addr_isinvalid(netif_ip6_addr_state(netif, i)) &&
+        ip6_addr_cmp(netif_ip6_addr(netif, i), ip6addr)) {
+      return i;
+    }
+  }
+  return -1;
+}
+
+/**
+ * @ingroup netif_ip6
+ * Create a link-local IPv6 address on a netif (stored in slot 0)
+ *
+ * @param netif the netif to create the address on
+ * @param from_mac_48bit if != 0, assume hwadr is a 48-bit MAC address (std conversion)
+ *                       if == 0, use hwaddr directly as interface ID
+ */
+void
+netif_create_ip6_linklocal_address(struct netif *netif, u8_t from_mac_48bit)
+{
+  u8_t i, addr_index;
+
+  /* Link-local prefix. */
+  ip_2_ip6(&netif->ip6_addr[0])->addr[0] = PP_HTONL(0xfe800000ul);
+  ip_2_ip6(&netif->ip6_addr[0])->addr[1] = 0;
+
+  /* Generate interface ID. */
+  if (from_mac_48bit) {
+    /* Assume hwaddr is a 48-bit IEEE 802 MAC. Convert to EUI-64 address. Complement Group bit. */
+    ip_2_ip6(&netif->ip6_addr[0])->addr[2] = lwip_htonl((((u32_t)(netif->hwaddr[0] ^ 0x02)) << 24) |
+        ((u32_t)(netif->hwaddr[1]) << 16) |
+        ((u32_t)(netif->hwaddr[2]) << 8) |
+        (0xff));
+    ip_2_ip6(&netif->ip6_addr[0])->addr[3] = lwip_htonl((0xfeul << 24) |
+        ((u32_t)(netif->hwaddr[3]) << 16) |
+        ((u32_t)(netif->hwaddr[4]) << 8) |
+        (netif->hwaddr[5]));
+  } else {
+    /* Use hwaddr directly as interface ID. */
+    ip_2_ip6(&netif->ip6_addr[0])->addr[2] = 0;
+    ip_2_ip6(&netif->ip6_addr[0])->addr[3] = 0;
+
+    addr_index = 3;
+    for (i = 0; (i < 8) && (i < netif->hwaddr_len); i++) {
+      if (i == 4) {
+        addr_index--;
+      }
+      ip_2_ip6(&netif->ip6_addr[0])->addr[addr_index] |= ((u32_t)(netif->hwaddr[netif->hwaddr_len - i - 1])) << (8 * (i & 0x03));
+    }
+  }
+
+  /* Set address state. */
+#if LWIP_IPV6_DUP_DETECT_ATTEMPTS
+  /* Will perform duplicate address detection (DAD). */
+  netif_ip6_addr_set_state(netif, 0, IP6_ADDR_TENTATIVE);
+#else
+  /* Consider address valid. */
+  netif_ip6_addr_set_state(netif, 0, IP6_ADDR_PREFERRED);
+#endif /* LWIP_IPV6_AUTOCONFIG */
+}
+
+/**
+ * @ingroup netif_ip6
+ * This function allows for the easy addition of a new IPv6 address to an interface.
+ * It takes care of finding an empty slot and then sets the address tentative
+ * (to make sure that all the subsequent processing happens).
+ *
+ * @param netif netif to add the address on
+ * @param ip6addr address to add
+ * @param chosen_idx if != NULL, the chosen IPv6 address index will be stored here
+ */
+err_t
+netif_add_ip6_address(struct netif *netif, const ip6_addr_t *ip6addr, s8_t *chosen_idx)
+{
+  s8_t i;
+
+  i = netif_get_ip6_addr_match(netif, ip6addr);
+  if (i >= 0) {
+    /* Address already added */
+    if (chosen_idx != NULL) {
+      *chosen_idx = i;
+    }
+    return ERR_OK;
+  }
+
+  /* Find a free slot -- musn't be the first one (reserved for link local) */
+  for (i = 1; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+    if (ip6_addr_isinvalid(netif_ip6_addr_state(netif, i))) {
+      ip_addr_copy_from_ip6(netif->ip6_addr[i], *ip6addr);
+      netif_ip6_addr_set_state(netif, i, IP6_ADDR_TENTATIVE);
+      if (chosen_idx != NULL) {
+        *chosen_idx = i;
+      }
+      return ERR_OK;
+    }
+  }
+
+  if (chosen_idx != NULL) {
+    *chosen_idx = -1;
+  }
+  return ERR_VAL;
+}
+
+/** Dummy IPv6 output function for netifs not supporting IPv6
+ */
+static err_t
+netif_null_output_ip6(struct netif *netif, struct pbuf *p, const ip6_addr_t *ipaddr)
+{
+  LWIP_UNUSED_ARG(netif);
+  LWIP_UNUSED_ARG(p);
+  LWIP_UNUSED_ARG(ipaddr);
+
+  return ERR_IF;
+}
+#endif /* LWIP_IPV6 */

+ 1442 - 1179
thirdparty/lwip/src/core/pbuf.c

@@ -1,1179 +1,1442 @@
-/**
- * @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, PACKET QUEUES ARE
- * NOT SUPPORTED!!! Use helper structs to queue multiple packets.
- * 
- * The differences between a pbuf chain and a packet queue are very
- * precise but subtle. 
- *
- * The last pbuf of a packet has a ->tot_len field that equals the
- * ->len field. It can be found by traversing the list. If the last
- * pbuf of a packet has a ->next field other than NULL, more packets
- * are on the queue.
- *
- * Therefore, looping through a pbuf of a single packet, has an
- * loop end condition (tot_len == p->len), NOT (next == NULL).
- */
-
-/*
- * 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/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"
-#if LWIP_TCP && TCP_QUEUE_OOSEQ
-#include "lwip/tcp_impl.h"
-#endif
-#if LWIP_CHECKSUM_ON_COPY
-#include "lwip/inet_chksum.h"
-#endif
-
-#include <string.h>
-
-#define SIZEOF_STRUCT_PBUF        LWIP_MEM_ALIGN_SIZE(sizeof(struct pbuf))
-/* Since the pool is created in memp, PBUF_POOL_BUFSIZE will be automatically
-   aligned there. Therefore, PBUF_POOL_BUFSIZE_ALIGNED can be used here. */
-#define PBUF_POOL_BUFSIZE_ALIGNED LWIP_MEM_ALIGN_SIZE(PBUF_POOL_BUFSIZE)
-
-#if !LWIP_TCP || !TCP_QUEUE_OOSEQ || !PBUF_POOL_FREE_OOSEQ
-#define PBUF_POOL_IS_EMPTY()
-#else /* !LWIP_TCP || !TCP_QUEUE_OOSEQ || !PBUF_POOL_FREE_OOSEQ */
-
-#if !NO_SYS
-#ifndef PBUF_POOL_FREE_OOSEQ_QUEUE_CALL
-#include "lwip/tcpip.h"
-#define PBUF_POOL_FREE_OOSEQ_QUEUE_CALL()  do { \
-  if(tcpip_callback_with_block(pbuf_free_ooseq_callback, NULL, 0) != ERR_OK) { \
-      SYS_ARCH_PROTECT(old_level); \
-      pbuf_free_ooseq_pending = 0; \
-      SYS_ARCH_UNPROTECT(old_level); \
-  } } while(0)
-#endif /* PBUF_POOL_FREE_OOSEQ_QUEUE_CALL */
-#endif /* !NO_SYS */
-
-volatile u8_t pbuf_free_ooseq_pending;
-#define PBUF_POOL_IS_EMPTY() pbuf_pool_is_empty()
-
-/**
- * Attempt to reclaim some memory from queued out-of-sequence TCP segments
- * if we run out of pool pbufs. It's better to give priority to new packets
- * if we're running out.
- *
- * This must be done in the correct thread context therefore this function
- * can only be used with NO_SYS=0 and through tcpip_callback.
- */
-#if !NO_SYS
-static
-#endif /* !NO_SYS */
-void
-pbuf_free_ooseq(void)
-{
-  struct tcp_pcb* pcb;
-  SYS_ARCH_DECL_PROTECT(old_level);
-
-  SYS_ARCH_PROTECT(old_level);
-  pbuf_free_ooseq_pending = 0;
-  SYS_ARCH_UNPROTECT(old_level);
-
-  for (pcb = tcp_active_pcbs; NULL != pcb; pcb = pcb->next) {
-    if (NULL != pcb->ooseq) {
-      /** Free the ooseq pbufs of one PCB only */
-      LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free_ooseq: freeing out-of-sequence pbufs\n"));
-      tcp_segs_free(pcb->ooseq);
-      pcb->ooseq = NULL;
-      return;
-    }
-  }
-}
-
-#if !NO_SYS
-/**
- * Just a callback function for tcpip_timeout() that calls pbuf_free_ooseq().
- */
-static void
-pbuf_free_ooseq_callback(void *arg)
-{
-  LWIP_UNUSED_ARG(arg);
-  pbuf_free_ooseq();
-}
-#endif /* !NO_SYS */
-
-/** Queue a call to pbuf_free_ooseq if not already queued. */
-static void
-pbuf_pool_is_empty(void)
-{
-#ifndef PBUF_POOL_FREE_OOSEQ_QUEUE_CALL
-  SYS_ARCH_DECL_PROTECT(old_level);
-  SYS_ARCH_PROTECT(old_level);
-  pbuf_free_ooseq_pending = 1;
-  SYS_ARCH_UNPROTECT(old_level);
-#else /* PBUF_POOL_FREE_OOSEQ_QUEUE_CALL */
-  u8_t queued;
-  SYS_ARCH_DECL_PROTECT(old_level);
-  SYS_ARCH_PROTECT(old_level);
-  queued = pbuf_free_ooseq_pending;
-  pbuf_free_ooseq_pending = 1;
-  SYS_ARCH_UNPROTECT(old_level);
-
-  if(!queued) {
-    /* queue a call to pbuf_free_ooseq if not already queued */
-    PBUF_POOL_FREE_OOSEQ_QUEUE_CALL();
-  }
-#endif /* PBUF_POOL_FREE_OOSEQ_QUEUE_CALL */
-}
-#endif /* !LWIP_TCP || !TCP_QUEUE_OOSEQ || !PBUF_POOL_FREE_OOSEQ */
-
-/**
- * Allocates a pbuf of the given type (possibly a chain for PBUF_POOL type).
- *
- * The actual memory allocated for the pbuf is determined by the
- * layer at which the pbuf is allocated and the requested size
- * (from the size parameter).
- *
- * @param layer flag to define header size
- * @param length size of the pbuf's payload
- * @param type this parameter decides how and where the pbuf
- * should be allocated as follows:
- *
- * - PBUF_RAM: buffer memory for pbuf is allocated as one large
- *             chunk. This includes protocol headers as well.
- * - PBUF_ROM: no buffer memory is allocated for the pbuf, even for
- *             protocol headers. Additional headers must be prepended
- *             by allocating another pbuf and chain in to the front of
- *             the ROM pbuf. It is assumed that the memory used is really
- *             similar to ROM in that it is immutable and will not be
- *             changed. Memory which is dynamic should generally not
- *             be attached to PBUF_ROM pbufs. Use PBUF_REF instead.
- * - PBUF_REF: no buffer memory is allocated for the pbuf, even for
- *             protocol headers. It is assumed that the pbuf is only
- *             being used in a single thread. If the pbuf gets queued,
- *             then pbuf_take should be called to copy the buffer.
- * - PBUF_POOL: the pbuf is allocated as a pbuf chain, with pbufs from
- *              the pbuf pool that is allocated during pbuf_init().
- *
- * @return the allocated pbuf. If multiple pbufs where allocated, this
- * is the first pbuf of a pbuf chain.
- */
-struct pbuf *
-pbuf_alloc(pbuf_layer layer, u16_t length, pbuf_type type)
-{
-  struct pbuf *p, *q, *r;
-  u16_t offset;
-  s32_t rem_len; /* remaining length */
-  LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc(length=%"U16_F")\n", length));
-
-  /* determine header offset */
-  switch (layer) {
-  case PBUF_TRANSPORT:
-    /* add room for transport (often TCP) layer header */
-    offset = PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN;
-    break;
-  case PBUF_IP:
-    /* add room for IP layer header */
-    offset = PBUF_LINK_HLEN + PBUF_IP_HLEN;
-    break;
-  case PBUF_LINK:
-    /* add room for link layer header */
-    offset = PBUF_LINK_HLEN;
-    break;
-  case PBUF_RAW:
-    offset = 0;
-    break;
-  default:
-    LWIP_ASSERT("pbuf_alloc: bad pbuf layer", 0);
-    return NULL;
-  }
-
-  switch (type) {
-  case PBUF_POOL:
-    /* allocate head of pbuf chain into p */
-    p = (struct pbuf *)memp_malloc(MEMP_PBUF_POOL);
-    LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc: allocated pbuf %p\n", (void *)p));
-    if (p == NULL) {
-      PBUF_POOL_IS_EMPTY();
-      return NULL;
-    }
-    p->type = type;
-    p->next = NULL;
-
-    /* make the payload pointer point 'offset' bytes into pbuf data memory */
-    p->payload = LWIP_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 = LWIP_MIN(length, PBUF_POOL_BUFSIZE_ALIGNED - LWIP_MEM_ALIGN_SIZE(offset));
-    LWIP_ASSERT("check p->payload + p->len does not overflow pbuf",
-                ((u8_t*)p->payload + p->len <=
-                 (u8_t*)p + SIZEOF_STRUCT_PBUF + PBUF_POOL_BUFSIZE_ALIGNED));
-    LWIP_ASSERT("PBUF_POOL_BUFSIZE must be bigger than MEM_ALIGNMENT",
-      (PBUF_POOL_BUFSIZE_ALIGNED - LWIP_MEM_ALIGN_SIZE(offset)) > 0 );
-    /* 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 = (struct pbuf *)memp_malloc(MEMP_PBUF_POOL);
-      if (q == NULL) {
-        PBUF_POOL_IS_EMPTY();
-        /* free chain so far allocated */
-        pbuf_free(p);
-        /* bail out unsuccesfully */
-        return NULL;
-      }
-      q->type = type;
-      q->flags = 0;
-      q->next = NULL;
-      /* make previous pbuf point to this pbuf */
-      r->next = q;
-      /* set total length of this pbuf and next in chain */
-      LWIP_ASSERT("rem_len < max_u16_t", rem_len < 0xffff);
-      q->tot_len = (u16_t)rem_len;
-      /* this pbuf length is pool size, unless smaller sized tail */
-      q->len = LWIP_MIN((u16_t)rem_len, PBUF_POOL_BUFSIZE_ALIGNED);
-      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);
-      LWIP_ASSERT("check p->payload + p->len does not overflow pbuf",
-                  ((u8_t*)p->payload + p->len <=
-                   (u8_t*)p + SIZEOF_STRUCT_PBUF + PBUF_POOL_BUFSIZE_ALIGNED));
-      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 = (struct pbuf*)mem_malloc(LWIP_MEM_ALIGN_SIZE(SIZEOF_STRUCT_PBUF + offset) + LWIP_MEM_ALIGN_SIZE(length));
-    if (p == NULL) {
-      return NULL;
-    }
-    /* Set up internal structure of the pbuf. */
-    p->payload = LWIP_MEM_ALIGN((void *)((u8_t *)p + SIZEOF_STRUCT_PBUF + offset));
-    p->len = p->tot_len = length;
-    p->next = NULL;
-    p->type = type;
-
-    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 = (struct pbuf *)memp_malloc(MEMP_PBUF);
-    if (p == NULL) {
-      LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
-                  ("pbuf_alloc: Could not allocate MEMP_PBUF for PBUF_%s.\n",
-                  (type == 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->type = type;
-    break;
-  default:
-    LWIP_ASSERT("pbuf_alloc: erroneous type", 0);
-    return NULL;
-  }
-  /* set reference count */
-  p->ref = 1;
-  /* set flags */
-  p->flags = 0;
-  LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc(length=%"U16_F") == %p\n", length, (void *)p));
-  return p;
-}
-
-#if LWIP_SUPPORT_CUSTOM_PBUF
-/** Initialize a custom pbuf (already allocated).
- *
- * @param layer flag to define header size
- * @param length size of the pbuf's payload
- * @param type type of the pbuf (only used to treat the pbuf accordingly, as
- *        this function allocates no memory)
- * @param p pointer to the custom pbuf to initialize (already allocated)
- * @param payload_mem pointer to the buffer that is used for payload and headers,
- *        must be at least big enough to hold 'length' plus the header size,
- *        may be NULL if set later.
- *        ATTENTION: The caller is responsible for correct alignment of this buffer!!
- * @param payload_mem_len the size of the 'payload_mem' buffer, must be at least
- *        big enough to hold 'length' plus the header size
- */
-struct pbuf*
-pbuf_alloced_custom(pbuf_layer l, u16_t length, pbuf_type type, struct pbuf_custom *p,
-                    void *payload_mem, u16_t payload_mem_len)
-{
-  u16_t offset;
-  LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloced_custom(length=%"U16_F")\n", length));
-
-  /* determine header offset */
-  switch (l) {
-  case PBUF_TRANSPORT:
-    /* add room for transport (often TCP) layer header */
-    offset = PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN;
-    break;
-  case PBUF_IP:
-    /* add room for IP layer header */
-    offset = PBUF_LINK_HLEN + PBUF_IP_HLEN;
-    break;
-  case PBUF_LINK:
-    /* add room for link layer header */
-    offset = PBUF_LINK_HLEN;
-    break;
-  case PBUF_RAW:
-    offset = 0;
-    break;
-  default:
-    LWIP_ASSERT("pbuf_alloced_custom: bad pbuf layer", 0);
-    return NULL;
-  }
-
-  if (LWIP_MEM_ALIGN_SIZE(offset) + length > payload_mem_len) {
-    LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_LEVEL_WARNING, ("pbuf_alloced_custom(length=%"U16_F") buffer too short\n", length));
-    return NULL;
-  }
-
-  p->pbuf.next = NULL;
-  if (payload_mem != NULL) {
-    p->pbuf.payload = (u8_t *)payload_mem + LWIP_MEM_ALIGN_SIZE(offset);
-  } else {
-    p->pbuf.payload = NULL;
-  }
-  p->pbuf.flags = PBUF_FLAG_IS_CUSTOM;
-  p->pbuf.len = p->pbuf.tot_len = length;
-  p->pbuf.type = type;
-  p->pbuf.ref = 1;
-  return &p->pbuf;
-}
-#endif /* LWIP_SUPPORT_CUSTOM_PBUF */
-
-/**
- * Shrink a pbuf chain to a desired length.
- *
- * @param p pbuf to shrink.
- * @param new_len desired new length of pbuf chain
- *
- * Depending on the desired length, the first few pbufs in a chain might
- * be skipped and left unchanged. The new last pbuf in the chain will be
- * resized, and any remaining pbufs will be freed.
- *
- * @note If the pbuf is ROM/REF, only the ->tot_len and ->len fields are adjusted.
- * @note May not be called on a packet queue.
- *
- * @note Despite its name, pbuf_realloc cannot grow the size of a pbuf (chain).
- */
-void
-pbuf_realloc(struct pbuf *p, u16_t new_len)
-{
-  struct pbuf *q;
-  u16_t rem_len; /* remaining length */
-  s32_t grow;
-
-  LWIP_ASSERT("pbuf_realloc: p != NULL", p != NULL);
-  LWIP_ASSERT("pbuf_realloc: sane p->type", p->type == PBUF_POOL ||
-              p->type == PBUF_ROM ||
-              p->type == PBUF_RAM ||
-              p->type == PBUF_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 */
-    LWIP_ASSERT("grow < max_u16_t", grow < 0xffff);
-    q->tot_len += (u16_t)grow;
-    /* proceed to next pbuf in chain */
-    q = q->next;
-    LWIP_ASSERT("pbuf_realloc: q != NULL", q != NULL);
-  }
-  /* we have now reached the new last pbuf (in q) */
-  /* rem_len == desired length for pbuf q */
-
-  /* shrink allocated memory for PBUF_RAM */
-  /* (other types merely adjust their length fields */
-  if ((q->type == PBUF_RAM) && (rem_len != q->len)) {
-    /* reallocate and adjust the length of the pbuf that will be split */
-    q = (struct pbuf *)mem_trim(q, (u16_t)((u8_t *)q->payload - (u8_t *)q) + rem_len);
-    LWIP_ASSERT("mem_trim returned q == NULL", q != NULL);
-  }
-  /* 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 p pbuf to change the header size.
- * @param header_size_increment Number of bytes to increment header size which
- * increases the size of the pbuf. New space is on the front.
- * (Using a negative value decreases the header size.)
- * If 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)
-{
-  u16_t type;
-  void *payload;
-  u16_t increment_magnitude;
-
-  LWIP_ASSERT("p != NULL", p != NULL);
-  if ((header_size_increment == 0) || (p == NULL)) {
-    return 0;
-  }
- 
-  if (header_size_increment < 0){
-    increment_magnitude = -header_size_increment;
-    /* Check that we aren't going to move off the end of the pbuf */
-    LWIP_ERROR("increment_magnitude <= p->len", (increment_magnitude <= p->len), return 1;);
-  } else {
-    increment_magnitude = header_size_increment;
-#if 0
-    /* Can't assert these as some callers speculatively call
-         pbuf_header() to see if it's OK.  Will return 1 below instead. */
-    /* Check that we've got the correct type of pbuf to work with */
-    LWIP_ASSERT("p->type == PBUF_RAM || p->type == PBUF_POOL", 
-                p->type == PBUF_RAM || p->type == PBUF_POOL);
-    /* Check that we aren't going to move off the beginning of the pbuf */
-    LWIP_ASSERT("p->payload - increment_magnitude >= p + SIZEOF_STRUCT_PBUF",
-                (u8_t *)p->payload - increment_magnitude >= (u8_t *)p + SIZEOF_STRUCT_PBUF);
-#endif
-  }
-
-  type = p->type;
-  /* remember current payload pointer */
-  payload = p->payload;
-
-  /* pbuf types containing payloads? */
-  if (type == PBUF_RAM || type == PBUF_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 | LWIP_DBG_LEVEL_SERIOUS,
-        ("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 (type == PBUF_REF || type == PBUF_ROM) {
-    /* hide a header in the payload? */
-    if ((header_size_increment < 0) && (increment_magnitude <= p->len)) {
-      /* increase payload pointer */
-      p->payload = (u8_t *)p->payload - header_size_increment;
-    } else {
-      /* cannot expand payload to front (yet!)
-       * bail out unsuccesfully */
-      return 1;
-    }
-  } else {
-    /* Unknown type */
-    LWIP_ASSERT("bad pbuf type", 0);
-    return 1;
-  }
-  /* modify pbuf length fields */
-  p->len += header_size_increment;
-  p->tot_len += header_size_increment;
-
-  LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("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 p 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)
-{
-  u16_t type;
-  struct pbuf *q;
-  u8_t count;
-
-  if (p == NULL) {
-    LWIP_ASSERT("p != NULL", p != NULL);
-    /* if assertions are disabled, proceed with debug output */
-    LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
-      ("pbuf_free(p == NULL) was called.\n"));
-    return 0;
-  }
-  LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free(%p)\n", (void *)p));
-
-  PERF_START;
-
-  LWIP_ASSERT("pbuf_free: sane type",
-    p->type == PBUF_RAM || p->type == PBUF_ROM ||
-    p->type == PBUF_REF || p->type == PBUF_POOL);
-
-  count = 0;
-  /* de-allocate all consecutive pbufs from the head of the chain that
-   * obtain a zero reference count after decrementing*/
-  while (p != NULL) {
-    u16_t ref;
-    SYS_ARCH_DECL_PROTECT(old_level);
-    /* Since decrementing ref cannot be guaranteed to be a single machine operation
-     * we must protect it. We put the new ref into a local variable to prevent
-     * further protection. */
-    SYS_ARCH_PROTECT(old_level);
-    /* all pbufs in a chain are referenced at least once */
-    LWIP_ASSERT("pbuf_free: p->ref > 0", p->ref > 0);
-    /* decrease reference count (number of pointers to pbuf) */
-    ref = --(p->ref);
-    SYS_ARCH_UNPROTECT(old_level);
-    /* this pbuf is no longer referenced to? */
-    if (ref == 0) {
-      /* remember next pbuf in chain for next iteration */
-      q = p->next;
-      LWIP_DEBUGF( PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free: deallocating %p\n", (void *)p));
-      type = p->type;
-#if LWIP_SUPPORT_CUSTOM_PBUF
-      /* is this a custom pbuf? */
-      if ((p->flags & PBUF_FLAG_IS_CUSTOM) != 0) {
-        struct pbuf_custom *pc = (struct pbuf_custom*)p;
-        LWIP_ASSERT("pc->custom_free_function != NULL", pc->custom_free_function != NULL);
-        pc->custom_free_function(p);
-      } else
-#endif /* LWIP_SUPPORT_CUSTOM_PBUF */
-      {
-        /* is this a pbuf from the pool? */
-        if (type == PBUF_POOL) {
-          memp_free(MEMP_PBUF_POOL, p);
-        /* is this a ROM or RAM referencing pbuf? */
-        } else if (type == PBUF_ROM || type == PBUF_REF) {
-          memp_free(MEMP_PBUF, p);
-        /* type == PBUF_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 | LWIP_DBG_TRACE, ("pbuf_free: %p has ref %"U16_F", ending here.\n", (void *)p, ref));
-      /* stop walking through the chain */
-      p = NULL;
-    }
-  }
-  PERF_STOP("pbuf_free");
-  /* return number of de-allocated pbufs */
-  return count;
-}
-
-/**
- * Count number of pbufs in a chain
- *
- * @param p first pbuf of chain
- * @return the number of pbufs in a chain
- */
-
-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_ERROR("(h != NULL) && (t != NULL) (programmer violates API)",
-             ((h != NULL) && (t != NULL)), return;);
-
-  /* proceed to last pbuf of chain */
-  for (p = h; p->next != NULL; p = p->next) {
-    /* add total length of second chain to all totals of first chain */
-    p->tot_len += 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 | LWIP_DBG_TRACE, ("pbuf_chain: %p references %p\n", (void *)h, (void *)t));
-}
-
-/**
- * Dechains the first pbuf from its succeeding pbufs in the chain.
- *
- * Makes p->tot_len field equal to p->len.
- * @param p pbuf to dechain
- * @return remainder of the pbuf chain, or NULL if it was de-allocated.
- * @note May not be called on a packet queue.
- */
-struct pbuf *
-pbuf_dechain(struct pbuf *p)
-{
-  struct pbuf *q;
-  u8_t tail_gone = 1;
-  /* tail */
-  q = p->next;
-  /* pbuf has successor in chain? */
-  if (q != NULL) {
-    /* assert tot_len invariant: (p->tot_len == p->len + (p->next? p->next->tot_len: 0) */
-    LWIP_ASSERT("p->tot_len == p->len + q->tot_len", q->tot_len == p->tot_len - p->len);
-    /* enforce invariant if assertion is disabled */
-    q->tot_len = p->tot_len - p->len;
-    /* decouple pbuf from remainder */
-    p->next = NULL;
-    /* total length of pbuf p is its own length only */
-    p->tot_len = p->len;
-    /* q is no longer referenced by p, free it */
-    LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_dechain: unreferencing %p\n", (void *)q));
-    tail_gone = pbuf_free(q);
-    if (tail_gone > 0) {
-      LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE,
-                  ("pbuf_dechain: deallocated %p (as it is no longer referenced)\n", (void *)q));
-    }
-    /* return remaining tail or NULL if deallocated */
-  }
-  /* assert tot_len invariant: (p->tot_len == p->len + (p->next? p->next->tot_len: 0) */
-  LWIP_ASSERT("p->tot_len == p->len", p->tot_len == p->len);
-  return ((tail_gone > 0) ? NULL : q);
-}
-
-/**
- *
- * Create PBUF_RAM copies of pbufs.
- *
- * Used to queue packets on behalf of the lwIP stack, such as
- * ARP based queueing.
- *
- * @note You MUST explicitly use p = pbuf_take(p);
- *
- * @note Only one packet is copied, no packet queue!
- *
- * @param p_to pbuf destination of the copy
- * @param p_from pbuf source of the copy
- *
- * @return ERR_OK if pbuf was copied
- *         ERR_ARG if one of the pbufs is NULL or p_to is not big
- *                 enough to hold p_from
- */
-err_t
-pbuf_copy(struct pbuf *p_to, struct pbuf *p_from)
-{
-  u16_t offset_to=0, offset_from=0, len;
-
-  LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_copy(%p, %p)\n",
-    (void*)p_to, (void*)p_from));
-
-  /* is the target big enough to hold the source? */
-  LWIP_ERROR("pbuf_copy: target not big enough to hold source", ((p_to != NULL) &&
-             (p_from != NULL) && (p_to->tot_len >= p_from->tot_len)), return ERR_ARG;);
-
-  /* iterate through pbuf chain */
-  do
-  {
-    /* copy one part of the original chain */
-    if ((p_to->len - offset_to) >= (p_from->len - offset_from)) {
-      /* complete current p_from fits into current p_to */
-      len = p_from->len - offset_from;
-    } else {
-      /* current p_from does not fit into current p_to */
-      len = p_to->len - offset_to;
-    }
-    MEMCPY((u8_t*)p_to->payload + offset_to, (u8_t*)p_from->payload + offset_from, len);
-    offset_to += len;
-    offset_from += len;
-    LWIP_ASSERT("offset_to <= p_to->len", offset_to <= p_to->len);
-    LWIP_ASSERT("offset_from <= p_from->len", offset_from <= p_from->len);
-    if (offset_from >= p_from->len) {
-      /* on to next p_from (if any) */
-      offset_from = 0;
-      p_from = p_from->next;
-    }
-    if (offset_to == p_to->len) {
-      /* on to next p_to (if any) */
-      offset_to = 0;
-      p_to = p_to->next;
-      LWIP_ERROR("p_to != NULL", (p_to != NULL) || (p_from == NULL) , return ERR_ARG;);
-    }
-
-    if((p_from != NULL) && (p_from->len == p_from->tot_len)) {
-      /* don't copy more than one packet! */
-      LWIP_ERROR("pbuf_copy() does not allow packet queues!\n",
-                 (p_from->next == NULL), return ERR_VAL;);
-    }
-    if((p_to != NULL) && (p_to->len == p_to->tot_len)) {
-      /* don't copy more than one packet! */
-      LWIP_ERROR("pbuf_copy() does not allow packet queues!\n",
-                  (p_to->next == NULL), return ERR_VAL;);
-    }
-  } while (p_from);
-  LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_copy: end of chain reached.\n"));
-  return ERR_OK;
-}
-
-/**
- * Copy (part of) the contents of a packet buffer
- * to an application supplied buffer.
- *
- * @param buf the pbuf from which to copy data
- * @param dataptr the application supplied buffer
- * @param len length of data to copy (dataptr must be big enough). No more 
- * than buf->tot_len will be copied, irrespective of len
- * @param offset offset into the packet buffer from where to begin copying len bytes
- * @return the number of bytes copied, or 0 on failure
- */
-u16_t
-pbuf_copy_partial(struct pbuf *buf, void *dataptr, u16_t len, u16_t offset)
-{
-  struct pbuf *p;
-  u16_t left;
-  u16_t buf_copy_len;
-  u16_t copied_total = 0;
-
-  LWIP_ERROR("pbuf_copy_partial: invalid buf", (buf != NULL), return 0;);
-  LWIP_ERROR("pbuf_copy_partial: invalid dataptr", (dataptr != NULL), return 0;);
-
-  left = 0;
-
-  if((buf == NULL) || (dataptr == NULL)) {
-    return 0;
-  }
-
-  /* Note some systems use byte copy if dataptr or one of the pbuf payload pointers are unaligned. */
-  for(p = buf; len != 0 && p != NULL; p = p->next) {
-    if ((offset != 0) && (offset >= p->len)) {
-      /* don't copy from this buffer -> on to the next */
-      offset -= p->len;
-    } else {
-      /* copy from this buffer. maybe only partially. */
-      buf_copy_len = p->len - offset;
-      if (buf_copy_len > len)
-          buf_copy_len = len;
-      /* copy the necessary parts of the buffer */
-      MEMCPY(&((char*)dataptr)[left], &((char*)p->payload)[offset], buf_copy_len);
-      copied_total += buf_copy_len;
-      left += buf_copy_len;
-      len -= buf_copy_len;
-      offset = 0;
-    }
-  }
-  return copied_total;
-}
-
-/**
- * Copy application supplied data into a pbuf.
- * This function can only be used to copy the equivalent of buf->tot_len data.
- *
- * @param buf pbuf to fill with data
- * @param dataptr application supplied data buffer
- * @param len length of the application supplied data buffer
- *
- * @return ERR_OK if successful, ERR_MEM if the pbuf is not big enough
- */
-err_t
-pbuf_take(struct pbuf *buf, const void *dataptr, u16_t len)
-{
-  struct pbuf *p;
-  u16_t buf_copy_len;
-  u16_t total_copy_len = len;
-  u16_t copied_total = 0;
-
-  LWIP_ERROR("pbuf_take: invalid buf", (buf != NULL), return 0;);
-  LWIP_ERROR("pbuf_take: invalid dataptr", (dataptr != NULL), return 0;);
-
-  if ((buf == NULL) || (dataptr == NULL) || (buf->tot_len < len)) {
-    return ERR_ARG;
-  }
-
-  /* Note some systems use byte copy if dataptr or one of the pbuf payload pointers are unaligned. */
-  for(p = buf; total_copy_len != 0; p = p->next) {
-    LWIP_ASSERT("pbuf_take: invalid pbuf", p != NULL);
-    buf_copy_len = total_copy_len;
-    if (buf_copy_len > p->len) {
-      /* this pbuf cannot hold all remaining data */
-      buf_copy_len = p->len;
-    }
-    /* copy the necessary parts of the buffer */
-    MEMCPY(p->payload, &((char*)dataptr)[copied_total], buf_copy_len);
-    total_copy_len -= buf_copy_len;
-    copied_total += buf_copy_len;
-  }
-  LWIP_ASSERT("did not copy all data", total_copy_len == 0 && copied_total == len);
-  return ERR_OK;
-}
-
-/**
- * Creates a single pbuf out of a queue of pbufs.
- *
- * @remark: Either the source pbuf 'p' is freed by this function or the original
- *          pbuf 'p' is returned, therefore the caller has to check the result!
- *
- * @param p the source pbuf
- * @param layer pbuf_layer of the new pbuf
- *
- * @return a new, single pbuf (p->next is NULL)
- *         or the old pbuf if allocation fails
- */
-struct pbuf*
-pbuf_coalesce(struct pbuf *p, pbuf_layer layer)
-{
-  struct pbuf *q;
-  err_t err;
-  if (p->next == NULL) {
-    return p;
-  }
-  q = pbuf_alloc(layer, p->tot_len, PBUF_RAM);
-  if (q == NULL) {
-    /* @todo: what do we do now? */
-    return p;
-  }
-  err = pbuf_copy(q, p);
-  LWIP_ASSERT("pbuf_copy failed", err == ERR_OK);
-  pbuf_free(p);
-  return q;
-}
-
-#if LWIP_CHECKSUM_ON_COPY
-/**
- * Copies data into a single pbuf (*not* into a pbuf queue!) and updates
- * the checksum while copying
- *
- * @param p the pbuf to copy data into
- * @param start_offset offset of p->payload where to copy the data to
- * @param dataptr data to copy into the pbuf
- * @param len length of data to copy into the pbuf
- * @param chksum pointer to the checksum which is updated
- * @return ERR_OK if successful, another error if the data does not fit
- *         within the (first) pbuf (no pbuf queues!)
- */
-err_t
-pbuf_fill_chksum(struct pbuf *p, u16_t start_offset, const void *dataptr,
-                 u16_t len, u16_t *chksum)
-{
-  u32_t acc;
-  u16_t copy_chksum;
-  char *dst_ptr;
-  LWIP_ASSERT("p != NULL", p != NULL);
-  LWIP_ASSERT("dataptr != NULL", dataptr != NULL);
-  LWIP_ASSERT("chksum != NULL", chksum != NULL);
-  LWIP_ASSERT("len != 0", len != 0);
-
-  if ((start_offset >= p->len) || (start_offset + len > p->len)) {
-    return ERR_ARG;
-  }
-
-  dst_ptr = ((char*)p->payload) + start_offset;
-  copy_chksum = LWIP_CHKSUM_COPY(dst_ptr, dataptr, len);
-  if ((start_offset & 1) != 0) {
-    copy_chksum = SWAP_BYTES_IN_WORD(copy_chksum);
-  }
-  acc = *chksum;
-  acc += copy_chksum;
-  *chksum = FOLD_U32T(acc);
-  return ERR_OK;
-}
-#endif /* LWIP_CHECKSUM_ON_COPY */
-
- /** Get one byte from the specified position in a pbuf
- * WARNING: returns zero for offset >= p->tot_len
- *
- * @param p pbuf to parse
- * @param offset offset into p of the byte to return
- * @return byte at an offset into p OR ZERO IF 'offset' >= p->tot_len
- */
-u8_t
-pbuf_get_at(struct pbuf* p, u16_t offset)
-{
-  u16_t copy_from = offset;
-  struct pbuf* q = p;
-
-  /* get the correct pbuf */
-  while ((q != NULL) && (q->len <= copy_from)) {
-    copy_from -= q->len;
-    q = q->next;
-  }
-  /* return requested data if pbuf is OK */
-  if ((q != NULL) && (q->len > copy_from)) {
-    return ((u8_t*)q->payload)[copy_from];
-  }
-  return 0;
-}
-
-/** Compare pbuf contents at specified offset with memory s2, both of length n
- *
- * @param p pbuf to compare
- * @param offset offset into p at wich to start comparing
- * @param s2 buffer to compare
- * @param n length of buffer to compare
- * @return zero if equal, nonzero otherwise
- *         (0xffff if p is too short, diffoffset+1 otherwise)
- */
-u16_t
-pbuf_memcmp(struct pbuf* p, u16_t offset, const void* s2, u16_t n)
-{
-  u16_t start = offset;
-  struct pbuf* q = p;
-
-  /* get the correct pbuf */
-  while ((q != NULL) && (q->len <= start)) {
-    start -= q->len;
-    q = q->next;
-  }
-  /* return requested data if pbuf is OK */
-  if ((q != NULL) && (q->len > start)) {
-    u16_t i;
-    for(i = 0; i < n; i++) {
-      u8_t a = pbuf_get_at(q, start + i);
-      u8_t b = ((u8_t*)s2)[i];
-      if (a != b) {
-        return i+1;
-      }
-    }
-    return 0;
-  }
-  return 0xffff;
-}
-
-/** Find occurrence of mem (with length mem_len) in pbuf p, starting at offset
- * start_offset.
- *
- * @param p pbuf to search, maximum length is 0xFFFE since 0xFFFF is used as
- *        return value 'not found'
- * @param mem search for the contents of this buffer
- * @param mem_len length of 'mem'
- * @param start_offset offset into p at which to start searching
- * @return 0xFFFF if substr was not found in p or the index where it was found
- */
-u16_t
-pbuf_memfind(struct pbuf* p, const void* mem, u16_t mem_len, u16_t start_offset)
-{
-  u16_t i;
-  u16_t max = p->tot_len - mem_len;
-  if (p->tot_len >= mem_len + start_offset) {
-    for(i = start_offset; i <= max; ) {
-      u16_t plus = pbuf_memcmp(p, i, mem, mem_len);
-      if (plus == 0) {
-        return i;
-      } else {
-        i += plus;
-      }
-    }
-  }
-  return 0xFFFF;
-}
-
-/** Find occurrence of substr with length substr_len in pbuf p, start at offset
- * start_offset
- * WARNING: in contrast to strstr(), this one does not stop at the first \0 in
- * the pbuf/source string!
- *
- * @param p pbuf to search, maximum length is 0xFFFE since 0xFFFF is used as
- *        return value 'not found'
- * @param substr string to search for in p, maximum length is 0xFFFE
- * @return 0xFFFF if substr was not found in p or the index where it was found
- */
-u16_t
-pbuf_strstr(struct pbuf* p, const char* substr)
-{
-  size_t substr_len;
-  if ((substr == NULL) || (substr[0] == 0) || (p->tot_len == 0xFFFF)) {
-    return 0xFFFF;
-  }
-  substr_len = strlen(substr);
-  if (substr_len >= 0xFFFF) {
-    return 0xFFFF;
-  }
-  return pbuf_memfind(p, substr, (u16_t)substr_len, 0);
-}
+/**
+ * @file
+ * Packet buffer management
+ */
+
+/**
+ * @defgroup pbuf Packet buffers (PBUF)
+ * @ingroup infrastructure
+ *
+ * Packets are built from the pbuf data structure. It supports dynamic
+ * memory allocation for packet contents or can reference externally
+ * managed packet contents both in RAM and ROM. Quick allocation for
+ * incoming packets is provided through pools with fixed sized pbufs.
+ *
+ * A packet may span over multiple pbufs, chained as a singly linked
+ * list. This is called a "pbuf chain".
+ *
+ * Multiple packets may be queued, also using this singly linked list.
+ * This is called a "packet queue".
+ *
+ * So, a packet queue consists of one or more pbuf chains, each of
+ * which consist of one or more pbufs. CURRENTLY, PACKET QUEUES ARE
+ * NOT SUPPORTED!!! Use helper structs to queue multiple packets.
+ *
+ * The differences between a pbuf chain and a packet queue are very
+ * precise but subtle.
+ *
+ * The last pbuf of a packet has a ->tot_len field that equals the
+ * ->len field. It can be found by traversing the list. If the last
+ * pbuf of a packet has a ->next field other than NULL, more packets
+ * are on the queue.
+ *
+ * Therefore, looping through a pbuf of a single packet, has an
+ * loop end condition (tot_len == p->len), NOT (next == NULL).
+ *
+ * Example of custom pbuf usage for zero-copy RX:
+  @code{.c}
+typedef struct my_custom_pbuf
+{
+   struct pbuf_custom p;
+   void* dma_descriptor;
+} my_custom_pbuf_t;
+
+LWIP_MEMPOOL_DECLARE(RX_POOL, 10, sizeof(my_custom_pbuf_t), "Zero-copy RX PBUF pool");
+
+void my_pbuf_free_custom(void* p)
+{
+  my_custom_pbuf_t* my_puf = (my_custom_pbuf_t*)p;
+
+  LOCK_INTERRUPTS();
+  free_rx_dma_descriptor(my_pbuf->dma_descriptor);
+  LWIP_MEMPOOL_FREE(RX_POOL, my_pbuf);
+  UNLOCK_INTERRUPTS();
+}
+
+void eth_rx_irq()
+{
+  dma_descriptor*   dma_desc = get_RX_DMA_descriptor_from_ethernet();
+  my_custom_pbuf_t* my_pbuf  = (my_custom_pbuf_t*)LWIP_MEMPOOL_ALLOC(RX_POOL);
+
+  my_pbuf->p.custom_free_function = my_pbuf_free_custom;
+  my_pbuf->dma_descriptor         = dma_desc;
+
+  invalidate_cpu_cache(dma_desc->rx_data, dma_desc->rx_length);
+  
+  struct pbuf* p = pbuf_alloced_custom(PBUF_RAW,
+     dma_desc->rx_length,
+     PBUF_REF,
+     &my_pbuf->p,
+     dma_desc->rx_data,
+     dma_desc->max_buffer_size);
+
+  if(netif->input(p, netif) != ERR_OK) {
+    pbuf_free(p);
+  }
+}
+  @endcode
+ */
+
+/*
+ * 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/stats.h"
+#include "lwip/def.h"
+#include "lwip/mem.h"
+#include "lwip/memp.h"
+#include "lwip/pbuf.h"
+#include "lwip/sys.h"
+#if LWIP_TCP && TCP_QUEUE_OOSEQ
+#include "lwip/priv/tcp_priv.h"
+#endif
+#if LWIP_CHECKSUM_ON_COPY
+#include "lwip/inet_chksum.h"
+#endif
+
+#include <string.h>
+
+#define SIZEOF_STRUCT_PBUF        LWIP_MEM_ALIGN_SIZE(sizeof(struct pbuf))
+/* Since the pool is created in memp, PBUF_POOL_BUFSIZE will be automatically
+   aligned there. Therefore, PBUF_POOL_BUFSIZE_ALIGNED can be used here. */
+#define PBUF_POOL_BUFSIZE_ALIGNED LWIP_MEM_ALIGN_SIZE(PBUF_POOL_BUFSIZE)
+
+#if !LWIP_TCP || !TCP_QUEUE_OOSEQ || !PBUF_POOL_FREE_OOSEQ
+#define PBUF_POOL_IS_EMPTY()
+#else /* !LWIP_TCP || !TCP_QUEUE_OOSEQ || !PBUF_POOL_FREE_OOSEQ */
+
+#if !NO_SYS
+#ifndef PBUF_POOL_FREE_OOSEQ_QUEUE_CALL
+#include "lwip/tcpip.h"
+#define PBUF_POOL_FREE_OOSEQ_QUEUE_CALL()  do { \
+  if (tcpip_callback_with_block(pbuf_free_ooseq_callback, NULL, 0) != ERR_OK) { \
+      SYS_ARCH_PROTECT(old_level); \
+      pbuf_free_ooseq_pending = 0; \
+      SYS_ARCH_UNPROTECT(old_level); \
+  } } while(0)
+#endif /* PBUF_POOL_FREE_OOSEQ_QUEUE_CALL */
+#endif /* !NO_SYS */
+
+volatile u8_t pbuf_free_ooseq_pending;
+#define PBUF_POOL_IS_EMPTY() pbuf_pool_is_empty()
+
+/**
+ * Attempt to reclaim some memory from queued out-of-sequence TCP segments
+ * if we run out of pool pbufs. It's better to give priority to new packets
+ * if we're running out.
+ *
+ * This must be done in the correct thread context therefore this function
+ * can only be used with NO_SYS=0 and through tcpip_callback.
+ */
+#if !NO_SYS
+static
+#endif /* !NO_SYS */
+void
+pbuf_free_ooseq(void)
+{
+  struct tcp_pcb* pcb;
+  SYS_ARCH_SET(pbuf_free_ooseq_pending, 0);
+
+  for (pcb = tcp_active_pcbs; NULL != pcb; pcb = pcb->next) {
+    if (NULL != pcb->ooseq) {
+      /** Free the ooseq pbufs of one PCB only */
+      LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free_ooseq: freeing out-of-sequence pbufs\n"));
+      tcp_segs_free(pcb->ooseq);
+      pcb->ooseq = NULL;
+      return;
+    }
+  }
+}
+
+#if !NO_SYS
+/**
+ * Just a callback function for tcpip_callback() that calls pbuf_free_ooseq().
+ */
+static void
+pbuf_free_ooseq_callback(void *arg)
+{
+  LWIP_UNUSED_ARG(arg);
+  pbuf_free_ooseq();
+}
+#endif /* !NO_SYS */
+
+/** Queue a call to pbuf_free_ooseq if not already queued. */
+static void
+pbuf_pool_is_empty(void)
+{
+#ifndef PBUF_POOL_FREE_OOSEQ_QUEUE_CALL
+  SYS_ARCH_SET(pbuf_free_ooseq_pending, 1);
+#else /* PBUF_POOL_FREE_OOSEQ_QUEUE_CALL */
+  u8_t queued;
+  SYS_ARCH_DECL_PROTECT(old_level);
+  SYS_ARCH_PROTECT(old_level);
+  queued = pbuf_free_ooseq_pending;
+  pbuf_free_ooseq_pending = 1;
+  SYS_ARCH_UNPROTECT(old_level);
+
+  if (!queued) {
+    /* queue a call to pbuf_free_ooseq if not already queued */
+    PBUF_POOL_FREE_OOSEQ_QUEUE_CALL();
+  }
+#endif /* PBUF_POOL_FREE_OOSEQ_QUEUE_CALL */
+}
+#endif /* !LWIP_TCP || !TCP_QUEUE_OOSEQ || !PBUF_POOL_FREE_OOSEQ */
+
+/**
+ * @ingroup pbuf
+ * Allocates a pbuf of the given type (possibly a chain for PBUF_POOL type).
+ *
+ * The actual memory allocated for the pbuf is determined by the
+ * layer at which the pbuf is allocated and the requested size
+ * (from the size parameter).
+ *
+ * @param layer flag to define header size
+ * @param length size of the pbuf's payload
+ * @param type this parameter decides how and where the pbuf
+ * should be allocated as follows:
+ *
+ * - PBUF_RAM: buffer memory for pbuf is allocated as one large
+ *             chunk. This includes protocol headers as well.
+ * - PBUF_ROM: no buffer memory is allocated for the pbuf, even for
+ *             protocol headers. Additional headers must be prepended
+ *             by allocating another pbuf and chain in to the front of
+ *             the ROM pbuf. It is assumed that the memory used is really
+ *             similar to ROM in that it is immutable and will not be
+ *             changed. Memory which is dynamic should generally not
+ *             be attached to PBUF_ROM pbufs. Use PBUF_REF instead.
+ * - PBUF_REF: no buffer memory is allocated for the pbuf, even for
+ *             protocol headers. It is assumed that the pbuf is only
+ *             being used in a single thread. If the pbuf gets queued,
+ *             then pbuf_take should be called to copy the buffer.
+ * - PBUF_POOL: the pbuf is allocated as a pbuf chain, with pbufs from
+ *              the pbuf pool that is allocated during pbuf_init().
+ *
+ * @return the allocated pbuf. If multiple pbufs where allocated, this
+ * is the first pbuf of a pbuf chain.
+ */
+struct pbuf *
+pbuf_alloc(pbuf_layer layer, u16_t length, pbuf_type type)
+{
+  struct pbuf *p, *q, *r;
+  u16_t offset;
+  s32_t rem_len; /* remaining length */
+  LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc(length=%"U16_F")\n", length));
+
+  /* determine header offset */
+  switch (layer) {
+  case PBUF_TRANSPORT:
+    /* add room for transport (often TCP) layer header */
+    offset = PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN;
+    break;
+  case PBUF_IP:
+    /* add room for IP layer header */
+    offset = PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN + PBUF_IP_HLEN;
+    break;
+  case PBUF_LINK:
+    /* add room for link layer header */
+    offset = PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN;
+    break;
+  case PBUF_RAW_TX:
+    /* add room for encapsulating link layer headers (e.g. 802.11) */
+    offset = PBUF_LINK_ENCAPSULATION_HLEN;
+    break;
+  case PBUF_RAW:
+    /* no offset (e.g. RX buffers or chain successors) */
+    offset = 0;
+    break;
+  default:
+    LWIP_ASSERT("pbuf_alloc: bad pbuf layer", 0);
+    return NULL;
+  }
+
+  switch (type) {
+  case PBUF_POOL:
+    /* allocate head of pbuf chain into p */
+    p = (struct pbuf *)memp_malloc(MEMP_PBUF_POOL);
+    LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc: allocated pbuf %p\n", (void *)p));
+    if (p == NULL) {
+      PBUF_POOL_IS_EMPTY();
+      return NULL;
+    }
+    p->type = type;
+    p->next = NULL;
+
+    /* make the payload pointer point 'offset' bytes into pbuf data memory */
+    p->payload = LWIP_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 = LWIP_MIN(length, PBUF_POOL_BUFSIZE_ALIGNED - LWIP_MEM_ALIGN_SIZE(offset));
+    LWIP_ASSERT("check p->payload + p->len does not overflow pbuf",
+                ((u8_t*)p->payload + p->len <=
+                 (u8_t*)p + SIZEOF_STRUCT_PBUF + PBUF_POOL_BUFSIZE_ALIGNED));
+    LWIP_ASSERT("PBUF_POOL_BUFSIZE must be bigger than MEM_ALIGNMENT",
+      (PBUF_POOL_BUFSIZE_ALIGNED - LWIP_MEM_ALIGN_SIZE(offset)) > 0 );
+    /* 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 = (struct pbuf *)memp_malloc(MEMP_PBUF_POOL);
+      if (q == NULL) {
+        PBUF_POOL_IS_EMPTY();
+        /* free chain so far allocated */
+        pbuf_free(p);
+        /* bail out unsuccessfully */
+        return NULL;
+      }
+      q->type = type;
+      q->flags = 0;
+      q->next = NULL;
+      /* make previous pbuf point to this pbuf */
+      r->next = q;
+      /* set total length of this pbuf and next in chain */
+      LWIP_ASSERT("rem_len < max_u16_t", rem_len < 0xffff);
+      q->tot_len = (u16_t)rem_len;
+      /* this pbuf length is pool size, unless smaller sized tail */
+      q->len = LWIP_MIN((u16_t)rem_len, PBUF_POOL_BUFSIZE_ALIGNED);
+      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);
+      LWIP_ASSERT("check p->payload + p->len does not overflow pbuf",
+                  ((u8_t*)p->payload + p->len <=
+                   (u8_t*)p + SIZEOF_STRUCT_PBUF + PBUF_POOL_BUFSIZE_ALIGNED));
+      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:
+    {
+      mem_size_t alloc_len = LWIP_MEM_ALIGN_SIZE(SIZEOF_STRUCT_PBUF + offset) + LWIP_MEM_ALIGN_SIZE(length);
+      
+      /* bug #50040: Check for integer overflow when calculating alloc_len */
+      if (alloc_len < LWIP_MEM_ALIGN_SIZE(length)) {
+        return NULL;
+      }
+    
+      /* If pbuf is to be allocated in RAM, allocate memory for it. */
+      p = (struct pbuf*)mem_malloc(alloc_len);
+    }
+
+    if (p == NULL) {
+      return NULL;
+    }
+    /* Set up internal structure of the pbuf. */
+    p->payload = LWIP_MEM_ALIGN((void *)((u8_t *)p + SIZEOF_STRUCT_PBUF + offset));
+    p->len = p->tot_len = length;
+    p->next = NULL;
+    p->type = type;
+
+    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 = (struct pbuf *)memp_malloc(MEMP_PBUF);
+    if (p == NULL) {
+      LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+                  ("pbuf_alloc: Could not allocate MEMP_PBUF for PBUF_%s.\n",
+                  (type == 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->type = type;
+    break;
+  default:
+    LWIP_ASSERT("pbuf_alloc: erroneous type", 0);
+    return NULL;
+  }
+  /* set reference count */
+  p->ref = 1;
+  /* set flags */
+  p->flags = 0;
+  LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc(length=%"U16_F") == %p\n", length, (void *)p));
+  return p;
+}
+
+#if LWIP_SUPPORT_CUSTOM_PBUF
+/**
+ * @ingroup pbuf
+ * Initialize a custom pbuf (already allocated).
+ *
+ * @param l flag to define header size
+ * @param length size of the pbuf's payload
+ * @param type type of the pbuf (only used to treat the pbuf accordingly, as
+ *        this function allocates no memory)
+ * @param p pointer to the custom pbuf to initialize (already allocated)
+ * @param payload_mem pointer to the buffer that is used for payload and headers,
+ *        must be at least big enough to hold 'length' plus the header size,
+ *        may be NULL if set later.
+ *        ATTENTION: The caller is responsible for correct alignment of this buffer!!
+ * @param payload_mem_len the size of the 'payload_mem' buffer, must be at least
+ *        big enough to hold 'length' plus the header size
+ */
+struct pbuf*
+pbuf_alloced_custom(pbuf_layer l, u16_t length, pbuf_type type, struct pbuf_custom *p,
+                    void *payload_mem, u16_t payload_mem_len)
+{
+  u16_t offset;
+  LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloced_custom(length=%"U16_F")\n", length));
+
+  /* determine header offset */
+  switch (l) {
+  case PBUF_TRANSPORT:
+    /* add room for transport (often TCP) layer header */
+    offset = PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN;
+    break;
+  case PBUF_IP:
+    /* add room for IP layer header */
+    offset = PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN + PBUF_IP_HLEN;
+    break;
+  case PBUF_LINK:
+    /* add room for link layer header */
+    offset = PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN;
+    break;
+  case PBUF_RAW_TX:
+    /* add room for encapsulating link layer headers (e.g. 802.11) */
+    offset = PBUF_LINK_ENCAPSULATION_HLEN;
+    break;
+  case PBUF_RAW:
+    offset = 0;
+    break;
+  default:
+    LWIP_ASSERT("pbuf_alloced_custom: bad pbuf layer", 0);
+    return NULL;
+  }
+
+  if (LWIP_MEM_ALIGN_SIZE(offset) + length > payload_mem_len) {
+    LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_LEVEL_WARNING, ("pbuf_alloced_custom(length=%"U16_F") buffer too short\n", length));
+    return NULL;
+  }
+
+  p->pbuf.next = NULL;
+  if (payload_mem != NULL) {
+    p->pbuf.payload = (u8_t *)payload_mem + LWIP_MEM_ALIGN_SIZE(offset);
+  } else {
+    p->pbuf.payload = NULL;
+  }
+  p->pbuf.flags = PBUF_FLAG_IS_CUSTOM;
+  p->pbuf.len = p->pbuf.tot_len = length;
+  p->pbuf.type = type;
+  p->pbuf.ref = 1;
+  return &p->pbuf;
+}
+#endif /* LWIP_SUPPORT_CUSTOM_PBUF */
+
+/**
+ * @ingroup pbuf
+ * Shrink a pbuf chain to a desired length.
+ *
+ * @param p pbuf to shrink.
+ * @param new_len desired new length of pbuf chain
+ *
+ * Depending on the desired length, the first few pbufs in a chain might
+ * be skipped and left unchanged. The new last pbuf in the chain will be
+ * resized, and any remaining pbufs will be freed.
+ *
+ * @note If the pbuf is ROM/REF, only the ->tot_len and ->len fields are adjusted.
+ * @note May not be called on a packet queue.
+ *
+ * @note Despite its name, pbuf_realloc cannot grow the size of a pbuf (chain).
+ */
+void
+pbuf_realloc(struct pbuf *p, u16_t new_len)
+{
+  struct pbuf *q;
+  u16_t rem_len; /* remaining length */
+  s32_t grow;
+
+  LWIP_ASSERT("pbuf_realloc: p != NULL", p != NULL);
+  LWIP_ASSERT("pbuf_realloc: sane p->type", p->type == PBUF_POOL ||
+              p->type == PBUF_ROM ||
+              p->type == PBUF_RAM ||
+              p->type == PBUF_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 */
+    LWIP_ASSERT("grow < max_u16_t", grow < 0xffff);
+    q->tot_len += (u16_t)grow;
+    /* proceed to next pbuf in chain */
+    q = q->next;
+    LWIP_ASSERT("pbuf_realloc: q != NULL", q != NULL);
+  }
+  /* we have now reached the new last pbuf (in q) */
+  /* rem_len == desired length for pbuf q */
+
+  /* shrink allocated memory for PBUF_RAM */
+  /* (other types merely adjust their length fields */
+  if ((q->type == PBUF_RAM) && (rem_len != q->len)
+#if LWIP_SUPPORT_CUSTOM_PBUF
+      && ((q->flags & PBUF_FLAG_IS_CUSTOM) == 0)
+#endif /* LWIP_SUPPORT_CUSTOM_PBUF */
+     ) {
+    /* reallocate and adjust the length of the pbuf that will be split */
+    q = (struct pbuf *)mem_trim(q, (u16_t)((u8_t *)q->payload - (u8_t *)q) + rem_len);
+    LWIP_ASSERT("mem_trim returned q == NULL", q != NULL);
+  }
+  /* 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.
+ * @see pbuf_header.
+ *
+ * @param p pbuf to change the header size.
+ * @param header_size_increment Number of bytes to increment header size.
+ * @param force Allow 'header_size_increment > 0' for PBUF_REF/PBUF_ROM types
+ *
+ * @return non-zero on failure, zero on success.
+ *
+ */
+static u8_t
+pbuf_header_impl(struct pbuf *p, s16_t header_size_increment, u8_t force)
+{
+  u16_t type;
+  void *payload;
+  u16_t increment_magnitude;
+
+  LWIP_ASSERT("p != NULL", p != NULL);
+  if ((header_size_increment == 0) || (p == NULL)) {
+    return 0;
+  }
+
+  if (header_size_increment < 0) {
+    increment_magnitude = (u16_t)-header_size_increment;
+    /* Check that we aren't going to move off the end of the pbuf */
+    LWIP_ERROR("increment_magnitude <= p->len", (increment_magnitude <= p->len), return 1;);
+  } else {
+    increment_magnitude = (u16_t)header_size_increment;
+#if 0
+    /* Can't assert these as some callers speculatively call
+         pbuf_header() to see if it's OK.  Will return 1 below instead. */
+    /* Check that we've got the correct type of pbuf to work with */
+    LWIP_ASSERT("p->type == PBUF_RAM || p->type == PBUF_POOL",
+                p->type == PBUF_RAM || p->type == PBUF_POOL);
+    /* Check that we aren't going to move off the beginning of the pbuf */
+    LWIP_ASSERT("p->payload - increment_magnitude >= p + SIZEOF_STRUCT_PBUF",
+                (u8_t *)p->payload - increment_magnitude >= (u8_t *)p + SIZEOF_STRUCT_PBUF);
+#endif
+  }
+
+  type = p->type;
+  /* remember current payload pointer */
+  payload = p->payload;
+
+  /* pbuf types containing payloads? */
+  if (type == PBUF_RAM || type == PBUF_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 | LWIP_DBG_TRACE,
+        ("pbuf_header: failed as %p < %p (not enough space for new header size)\n",
+        (void *)p->payload, (void *)((u8_t *)p + SIZEOF_STRUCT_PBUF)));
+      /* restore old payload pointer */
+      p->payload = payload;
+      /* bail out unsuccessfully */
+      return 1;
+    }
+  /* pbuf types referring to external payloads? */
+  } else if (type == PBUF_REF || type == PBUF_ROM) {
+    /* hide a header in the payload? */
+    if ((header_size_increment < 0) && (increment_magnitude <= p->len)) {
+      /* increase payload pointer */
+      p->payload = (u8_t *)p->payload - header_size_increment;
+    } else if ((header_size_increment > 0) && force) {
+      p->payload = (u8_t *)p->payload - header_size_increment;
+    } else {
+      /* cannot expand payload to front (yet!)
+       * bail out unsuccessfully */
+      return 1;
+    }
+  } else {
+    /* Unknown type */
+    LWIP_ASSERT("bad pbuf type", 0);
+    return 1;
+  }
+  /* modify pbuf length fields */
+  p->len += header_size_increment;
+  p->tot_len += header_size_increment;
+
+  LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_header: old %p new %p (%"S16_F")\n",
+    (void *)payload, (void *)p->payload, header_size_increment));
+
+  return 0;
+}
+
+/**
+ * Adjusts the payload pointer to hide or reveal headers in the payload.
+ *
+ * Adjusts the ->payload pointer so that space for a header
+ * (dis)appears in the pbuf payload.
+ *
+ * The ->payload, ->tot_len and ->len fields are adjusted.
+ *
+ * @param p pbuf to change the header size.
+ * @param header_size_increment Number of bytes to increment header size which
+ * increases the size of the pbuf. New space is on the front.
+ * (Using a negative value decreases the header size.)
+ * If hdr_size_inc is 0, this function does nothing and returns successful.
+ *
+ * PBUF_ROM and PBUF_REF type buffers cannot have their sizes increased, so
+ * the call will fail. A check is made that the increase in header size does
+ * not move the payload pointer in front of the start of the buffer.
+ * @return non-zero on failure, zero on success.
+ *
+ */
+u8_t
+pbuf_header(struct pbuf *p, s16_t header_size_increment)
+{
+   return pbuf_header_impl(p, header_size_increment, 0);
+}
+
+/**
+ * Same as pbuf_header but does not check if 'header_size > 0' is allowed.
+ * This is used internally only, to allow PBUF_REF for RX.
+ */
+u8_t
+pbuf_header_force(struct pbuf *p, s16_t header_size_increment)
+{
+   return pbuf_header_impl(p, header_size_increment, 1);
+}
+
+/**
+ * @ingroup pbuf
+ * Dereference a pbuf chain or queue and deallocate any no-longer-used
+ * pbufs at the head of this chain or queue.
+ *
+ * Decrements the pbuf reference count. If it reaches zero, the pbuf is
+ * deallocated.
+ *
+ * For a pbuf chain, this is repeated for each pbuf in the chain,
+ * up to the first pbuf which has a non-zero reference count after
+ * decrementing. So, when all reference counts are one, the whole
+ * chain is free'd.
+ *
+ * @param p The pbuf (chain) to be dereferenced.
+ *
+ * @return the number of pbufs that were de-allocated
+ * from the head of the chain.
+ *
+ * @note 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)
+{
+  u16_t type;
+  struct pbuf *q;
+  u8_t count;
+
+  if (p == NULL) {
+    LWIP_ASSERT("p != NULL", p != NULL);
+    /* if assertions are disabled, proceed with debug output */
+    LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+      ("pbuf_free(p == NULL) was called.\n"));
+    return 0;
+  }
+  LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free(%p)\n", (void *)p));
+
+  PERF_START;
+
+  LWIP_ASSERT("pbuf_free: sane type",
+    p->type == PBUF_RAM || p->type == PBUF_ROM ||
+    p->type == PBUF_REF || p->type == PBUF_POOL);
+
+  count = 0;
+  /* de-allocate all consecutive pbufs from the head of the chain that
+   * obtain a zero reference count after decrementing*/
+  while (p != NULL) {
+    u16_t ref;
+    SYS_ARCH_DECL_PROTECT(old_level);
+    /* Since decrementing ref cannot be guaranteed to be a single machine operation
+     * we must protect it. We put the new ref into a local variable to prevent
+     * further protection. */
+    SYS_ARCH_PROTECT(old_level);
+    /* all pbufs in a chain are referenced at least once */
+    LWIP_ASSERT("pbuf_free: p->ref > 0", p->ref > 0);
+    /* decrease reference count (number of pointers to pbuf) */
+    ref = --(p->ref);
+    SYS_ARCH_UNPROTECT(old_level);
+    /* this pbuf is no longer referenced to? */
+    if (ref == 0) {
+      /* remember next pbuf in chain for next iteration */
+      q = p->next;
+      LWIP_DEBUGF( PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free: deallocating %p\n", (void *)p));
+      type = p->type;
+#if LWIP_SUPPORT_CUSTOM_PBUF
+      /* is this a custom pbuf? */
+      if ((p->flags & PBUF_FLAG_IS_CUSTOM) != 0) {
+        struct pbuf_custom *pc = (struct pbuf_custom*)p;
+        LWIP_ASSERT("pc->custom_free_function != NULL", pc->custom_free_function != NULL);
+        pc->custom_free_function(p);
+      } else
+#endif /* LWIP_SUPPORT_CUSTOM_PBUF */
+      {
+        /* is this a pbuf from the pool? */
+        if (type == PBUF_POOL) {
+          memp_free(MEMP_PBUF_POOL, p);
+        /* is this a ROM or RAM referencing pbuf? */
+        } else if (type == PBUF_ROM || type == PBUF_REF) {
+          memp_free(MEMP_PBUF, p);
+        /* type == PBUF_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 | LWIP_DBG_TRACE, ("pbuf_free: %p has ref %"U16_F", ending here.\n", (void *)p, ref));
+      /* stop walking through the chain */
+      p = NULL;
+    }
+  }
+  PERF_STOP("pbuf_free");
+  /* return number of de-allocated pbufs */
+  return count;
+}
+
+/**
+ * Count number of pbufs in a chain
+ *
+ * @param p first pbuf of chain
+ * @return the number of pbufs in a chain
+ */
+u16_t
+pbuf_clen(const struct pbuf *p)
+{
+  u16_t len;
+
+  len = 0;
+  while (p != NULL) {
+    ++len;
+    p = p->next;
+  }
+  return len;
+}
+
+/**
+ * @ingroup pbuf
+ * Increment the reference count of the pbuf.
+ *
+ * @param p pbuf to increase reference counter of
+ *
+ */
+void
+pbuf_ref(struct pbuf *p)
+{
+  /* pbuf given? */
+  if (p != NULL) {
+    SYS_ARCH_INC(p->ref, 1);
+    LWIP_ASSERT("pbuf ref overflow", p->ref > 0);
+  }
+}
+
+/**
+ * @ingroup pbuf
+ * Concatenate two pbufs (each may be a pbuf chain) and take over
+ * the caller's reference of the tail pbuf.
+ *
+ * @note The caller MAY NOT reference the tail pbuf afterwards.
+ * Use pbuf_chain() for that purpose.
+ *
+ * @see pbuf_chain()
+ */
+void
+pbuf_cat(struct pbuf *h, struct pbuf *t)
+{
+  struct pbuf *p;
+
+  LWIP_ERROR("(h != NULL) && (t != NULL) (programmer violates API)",
+             ((h != NULL) && (t != NULL)), return;);
+
+  /* proceed to last pbuf of chain */
+  for (p = h; p->next != NULL; p = p->next) {
+    /* add total length of second chain to all totals of first chain */
+    p->tot_len += 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.
+   */
+}
+
+/**
+ * @ingroup pbuf
+ * Chain two pbufs (or pbuf chains) together.
+ *
+ * The caller MUST call pbuf_free(t) once it has stopped
+ * using it. Use pbuf_cat() instead if you no longer use t.
+ *
+ * @param h head pbuf (chain)
+ * @param t tail pbuf (chain)
+ * @note The pbufs MUST belong to the same packet.
+ * @note MAY NOT be called on a packet queue.
+ *
+ * The ->tot_len fields of all pbufs of the head chain are adjusted.
+ * The ->next field of the last pbuf of the head chain is adjusted.
+ * The ->ref field of the first pbuf of the tail chain is adjusted.
+ *
+ */
+void
+pbuf_chain(struct pbuf *h, struct pbuf *t)
+{
+  pbuf_cat(h, t);
+  /* t is now referenced by h */
+  pbuf_ref(t);
+  LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_chain: %p references %p\n", (void *)h, (void *)t));
+}
+
+/**
+ * Dechains the first pbuf from its succeeding pbufs in the chain.
+ *
+ * Makes p->tot_len field equal to p->len.
+ * @param p pbuf to dechain
+ * @return remainder of the pbuf chain, or NULL if it was de-allocated.
+ * @note May not be called on a packet queue.
+ */
+struct pbuf *
+pbuf_dechain(struct pbuf *p)
+{
+  struct pbuf *q;
+  u8_t tail_gone = 1;
+  /* tail */
+  q = p->next;
+  /* pbuf has successor in chain? */
+  if (q != NULL) {
+    /* assert tot_len invariant: (p->tot_len == p->len + (p->next? p->next->tot_len: 0) */
+    LWIP_ASSERT("p->tot_len == p->len + q->tot_len", q->tot_len == p->tot_len - p->len);
+    /* enforce invariant if assertion is disabled */
+    q->tot_len = p->tot_len - p->len;
+    /* decouple pbuf from remainder */
+    p->next = NULL;
+    /* total length of pbuf p is its own length only */
+    p->tot_len = p->len;
+    /* q is no longer referenced by p, free it */
+    LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_dechain: unreferencing %p\n", (void *)q));
+    tail_gone = pbuf_free(q);
+    if (tail_gone > 0) {
+      LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE,
+                  ("pbuf_dechain: deallocated %p (as it is no longer referenced)\n", (void *)q));
+    }
+    /* return remaining tail or NULL if deallocated */
+  }
+  /* assert tot_len invariant: (p->tot_len == p->len + (p->next? p->next->tot_len: 0) */
+  LWIP_ASSERT("p->tot_len == p->len", p->tot_len == p->len);
+  return ((tail_gone > 0) ? NULL : q);
+}
+
+/**
+ * @ingroup pbuf
+ * Create PBUF_RAM copies of pbufs.
+ *
+ * Used to queue packets on behalf of the lwIP stack, such as
+ * ARP based queueing.
+ *
+ * @note You MUST explicitly use p = pbuf_take(p);
+ *
+ * @note Only one packet is copied, no packet queue!
+ *
+ * @param p_to pbuf destination of the copy
+ * @param p_from pbuf source of the copy
+ *
+ * @return ERR_OK if pbuf was copied
+ *         ERR_ARG if one of the pbufs is NULL or p_to is not big
+ *                 enough to hold p_from
+ */
+err_t
+pbuf_copy(struct pbuf *p_to, const struct pbuf *p_from)
+{
+  u16_t offset_to=0, offset_from=0, len;
+
+  LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_copy(%p, %p)\n",
+    (const void*)p_to, (const void*)p_from));
+
+  /* is the target big enough to hold the source? */
+  LWIP_ERROR("pbuf_copy: target not big enough to hold source", ((p_to != NULL) &&
+             (p_from != NULL) && (p_to->tot_len >= p_from->tot_len)), return ERR_ARG;);
+
+  /* iterate through pbuf chain */
+  do
+  {
+    /* copy one part of the original chain */
+    if ((p_to->len - offset_to) >= (p_from->len - offset_from)) {
+      /* complete current p_from fits into current p_to */
+      len = p_from->len - offset_from;
+    } else {
+      /* current p_from does not fit into current p_to */
+      len = p_to->len - offset_to;
+    }
+    MEMCPY((u8_t*)p_to->payload + offset_to, (u8_t*)p_from->payload + offset_from, len);
+    offset_to += len;
+    offset_from += len;
+    LWIP_ASSERT("offset_to <= p_to->len", offset_to <= p_to->len);
+    LWIP_ASSERT("offset_from <= p_from->len", offset_from <= p_from->len);
+    if (offset_from >= p_from->len) {
+      /* on to next p_from (if any) */
+      offset_from = 0;
+      p_from = p_from->next;
+    }
+    if (offset_to == p_to->len) {
+      /* on to next p_to (if any) */
+      offset_to = 0;
+      p_to = p_to->next;
+      LWIP_ERROR("p_to != NULL", (p_to != NULL) || (p_from == NULL) , return ERR_ARG;);
+    }
+
+    if ((p_from != NULL) && (p_from->len == p_from->tot_len)) {
+      /* don't copy more than one packet! */
+      LWIP_ERROR("pbuf_copy() does not allow packet queues!",
+                 (p_from->next == NULL), return ERR_VAL;);
+    }
+    if ((p_to != NULL) && (p_to->len == p_to->tot_len)) {
+      /* don't copy more than one packet! */
+      LWIP_ERROR("pbuf_copy() does not allow packet queues!",
+                  (p_to->next == NULL), return ERR_VAL;);
+    }
+  } while (p_from);
+  LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_copy: end of chain reached.\n"));
+  return ERR_OK;
+}
+
+/**
+ * @ingroup pbuf
+ * Copy (part of) the contents of a packet buffer
+ * to an application supplied buffer.
+ *
+ * @param buf the pbuf from which to copy data
+ * @param dataptr the application supplied buffer
+ * @param len length of data to copy (dataptr must be big enough). No more
+ * than buf->tot_len will be copied, irrespective of len
+ * @param offset offset into the packet buffer from where to begin copying len bytes
+ * @return the number of bytes copied, or 0 on failure
+ */
+u16_t
+pbuf_copy_partial(const struct pbuf *buf, void *dataptr, u16_t len, u16_t offset)
+{
+  const struct pbuf *p;
+  u16_t left;
+  u16_t buf_copy_len;
+  u16_t copied_total = 0;
+
+  LWIP_ERROR("pbuf_copy_partial: invalid buf", (buf != NULL), return 0;);
+  LWIP_ERROR("pbuf_copy_partial: invalid dataptr", (dataptr != NULL), return 0;);
+
+  left = 0;
+
+  if ((buf == NULL) || (dataptr == NULL)) {
+    return 0;
+  }
+
+  /* Note some systems use byte copy if dataptr or one of the pbuf payload pointers are unaligned. */
+  for (p = buf; len != 0 && p != NULL; p = p->next) {
+    if ((offset != 0) && (offset >= p->len)) {
+      /* don't copy from this buffer -> on to the next */
+      offset -= p->len;
+    } else {
+      /* copy from this buffer. maybe only partially. */
+      buf_copy_len = p->len - offset;
+      if (buf_copy_len > len) {
+        buf_copy_len = len;
+      }
+      /* copy the necessary parts of the buffer */
+      MEMCPY(&((char*)dataptr)[left], &((char*)p->payload)[offset], buf_copy_len);
+      copied_total += buf_copy_len;
+      left += buf_copy_len;
+      len -= buf_copy_len;
+      offset = 0;
+    }
+  }
+  return copied_total;
+}
+
+#if LWIP_TCP && TCP_QUEUE_OOSEQ && LWIP_WND_SCALE
+/**
+ * This method modifies a 'pbuf chain', so that its total length is
+ * smaller than 64K. The remainder of the original pbuf chain is stored
+ * in *rest.
+ * This function never creates new pbufs, but splits an existing chain
+ * in two parts. The tot_len of the modified packet queue will likely be
+ * smaller than 64K.
+ * 'packet queues' are not supported by this function.
+ *
+ * @param p the pbuf queue to be split
+ * @param rest pointer to store the remainder (after the first 64K)
+ */
+void pbuf_split_64k(struct pbuf *p, struct pbuf **rest)
+{
+  *rest = NULL;
+  if ((p != NULL) && (p->next != NULL)) {
+    u16_t tot_len_front = p->len;
+    struct pbuf *i = p;
+    struct pbuf *r = p->next;
+
+    /* continue until the total length (summed up as u16_t) overflows */
+    while ((r != NULL) && ((u16_t)(tot_len_front + r->len) > tot_len_front)) {
+      tot_len_front += r->len;
+      i = r;
+      r = r->next;
+    }
+    /* i now points to last packet of the first segment. Set next
+       pointer to NULL */
+    i->next = NULL;
+
+    if (r != NULL) {
+      /* Update the tot_len field in the first part */
+      for (i = p; i != NULL; i = i->next) {
+        i->tot_len -= r->tot_len;
+        LWIP_ASSERT("tot_len/len mismatch in last pbuf",
+                    (i->next != NULL) || (i->tot_len == i->len));
+      }
+      if (p->flags & PBUF_FLAG_TCP_FIN) {
+        r->flags |= PBUF_FLAG_TCP_FIN;
+      }
+
+      /* tot_len field in rest does not need modifications */
+      /* reference counters do not need modifications */
+      *rest = r;
+    }
+  }
+}
+#endif /* LWIP_TCP && TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */
+
+/* Actual implementation of pbuf_skip() but returning const pointer... */
+static const struct pbuf*
+pbuf_skip_const(const struct pbuf* in, u16_t in_offset, u16_t* out_offset)
+{
+  u16_t offset_left = in_offset;
+  const struct pbuf* q = in;
+
+  /* get the correct pbuf */
+  while ((q != NULL) && (q->len <= offset_left)) {
+    offset_left -= q->len;
+    q = q->next;
+  }
+  if (out_offset != NULL) {
+    *out_offset = offset_left;
+  }
+  return q;
+}
+
+/**
+ * @ingroup pbuf
+ * Skip a number of bytes at the start of a pbuf
+ *
+ * @param in input pbuf
+ * @param in_offset offset to skip
+ * @param out_offset resulting offset in the returned pbuf
+ * @return the pbuf in the queue where the offset is
+ */
+struct pbuf*
+pbuf_skip(struct pbuf* in, u16_t in_offset, u16_t* out_offset)
+{
+  const struct pbuf* out = pbuf_skip_const(in, in_offset, out_offset);
+  return LWIP_CONST_CAST(struct pbuf*, out);
+}
+
+/**
+ * @ingroup pbuf
+ * Copy application supplied data into a pbuf.
+ * This function can only be used to copy the equivalent of buf->tot_len data.
+ *
+ * @param buf pbuf to fill with data
+ * @param dataptr application supplied data buffer
+ * @param len length of the application supplied data buffer
+ *
+ * @return ERR_OK if successful, ERR_MEM if the pbuf is not big enough
+ */
+err_t
+pbuf_take(struct pbuf *buf, const void *dataptr, u16_t len)
+{
+  struct pbuf *p;
+  u16_t buf_copy_len;
+  u16_t total_copy_len = len;
+  u16_t copied_total = 0;
+
+  LWIP_ERROR("pbuf_take: invalid buf", (buf != NULL), return ERR_ARG;);
+  LWIP_ERROR("pbuf_take: invalid dataptr", (dataptr != NULL), return ERR_ARG;);
+  LWIP_ERROR("pbuf_take: buf not large enough", (buf->tot_len >= len), return ERR_MEM;);
+
+  if ((buf == NULL) || (dataptr == NULL) || (buf->tot_len < len)) {
+    return ERR_ARG;
+  }
+
+  /* Note some systems use byte copy if dataptr or one of the pbuf payload pointers are unaligned. */
+  for (p = buf; total_copy_len != 0; p = p->next) {
+    LWIP_ASSERT("pbuf_take: invalid pbuf", p != NULL);
+    buf_copy_len = total_copy_len;
+    if (buf_copy_len > p->len) {
+      /* this pbuf cannot hold all remaining data */
+      buf_copy_len = p->len;
+    }
+    /* copy the necessary parts of the buffer */
+    MEMCPY(p->payload, &((const char*)dataptr)[copied_total], buf_copy_len);
+    total_copy_len -= buf_copy_len;
+    copied_total += buf_copy_len;
+  }
+  LWIP_ASSERT("did not copy all data", total_copy_len == 0 && copied_total == len);
+  return ERR_OK;
+}
+
+/**
+ * @ingroup pbuf
+ * Same as pbuf_take() but puts data at an offset
+ *
+ * @param buf pbuf to fill with data
+ * @param dataptr application supplied data buffer
+ * @param len length of the application supplied data buffer
+ * @param offset offset in pbuf where to copy dataptr to
+ *
+ * @return ERR_OK if successful, ERR_MEM if the pbuf is not big enough
+ */
+err_t
+pbuf_take_at(struct pbuf *buf, const void *dataptr, u16_t len, u16_t offset)
+{
+  u16_t target_offset;
+  struct pbuf* q = pbuf_skip(buf, offset, &target_offset);
+
+  /* return requested data if pbuf is OK */
+  if ((q != NULL) && (q->tot_len >= target_offset + len)) {
+    u16_t remaining_len = len;
+    const u8_t* src_ptr = (const u8_t*)dataptr;
+    /* copy the part that goes into the first pbuf */
+    u16_t first_copy_len = LWIP_MIN(q->len - target_offset, len);
+    MEMCPY(((u8_t*)q->payload) + target_offset, dataptr, first_copy_len);
+    remaining_len -= first_copy_len;
+    src_ptr += first_copy_len;
+    if (remaining_len > 0) {
+      return pbuf_take(q->next, src_ptr, remaining_len);
+    }
+    return ERR_OK;
+  }
+  return ERR_MEM;
+}
+
+/**
+ * @ingroup pbuf
+ * Creates a single pbuf out of a queue of pbufs.
+ *
+ * @remark: Either the source pbuf 'p' is freed by this function or the original
+ *          pbuf 'p' is returned, therefore the caller has to check the result!
+ *
+ * @param p the source pbuf
+ * @param layer pbuf_layer of the new pbuf
+ *
+ * @return a new, single pbuf (p->next is NULL)
+ *         or the old pbuf if allocation fails
+ */
+struct pbuf*
+pbuf_coalesce(struct pbuf *p, pbuf_layer layer)
+{
+  struct pbuf *q;
+  err_t err;
+  if (p->next == NULL) {
+    return p;
+  }
+  q = pbuf_alloc(layer, p->tot_len, PBUF_RAM);
+  if (q == NULL) {
+    /* @todo: what do we do now? */
+    return p;
+  }
+  err = pbuf_copy(q, p);
+  LWIP_UNUSED_ARG(err); /* in case of LWIP_NOASSERT */
+  LWIP_ASSERT("pbuf_copy failed", err == ERR_OK);
+  pbuf_free(p);
+  return q;
+}
+
+#if LWIP_CHECKSUM_ON_COPY
+/**
+ * Copies data into a single pbuf (*not* into a pbuf queue!) and updates
+ * the checksum while copying
+ *
+ * @param p the pbuf to copy data into
+ * @param start_offset offset of p->payload where to copy the data to
+ * @param dataptr data to copy into the pbuf
+ * @param len length of data to copy into the pbuf
+ * @param chksum pointer to the checksum which is updated
+ * @return ERR_OK if successful, another error if the data does not fit
+ *         within the (first) pbuf (no pbuf queues!)
+ */
+err_t
+pbuf_fill_chksum(struct pbuf *p, u16_t start_offset, const void *dataptr,
+                 u16_t len, u16_t *chksum)
+{
+  u32_t acc;
+  u16_t copy_chksum;
+  char *dst_ptr;
+  LWIP_ASSERT("p != NULL", p != NULL);
+  LWIP_ASSERT("dataptr != NULL", dataptr != NULL);
+  LWIP_ASSERT("chksum != NULL", chksum != NULL);
+  LWIP_ASSERT("len != 0", len != 0);
+
+  if ((start_offset >= p->len) || (start_offset + len > p->len)) {
+    return ERR_ARG;
+  }
+
+  dst_ptr = ((char*)p->payload) + start_offset;
+  copy_chksum = LWIP_CHKSUM_COPY(dst_ptr, dataptr, len);
+  if ((start_offset & 1) != 0) {
+    copy_chksum = SWAP_BYTES_IN_WORD(copy_chksum);
+  }
+  acc = *chksum;
+  acc += copy_chksum;
+  *chksum = FOLD_U32T(acc);
+  return ERR_OK;
+}
+#endif /* LWIP_CHECKSUM_ON_COPY */
+
+/**
+ * @ingroup pbuf
+ * Get one byte from the specified position in a pbuf
+ * WARNING: returns zero for offset >= p->tot_len
+ *
+ * @param p pbuf to parse
+ * @param offset offset into p of the byte to return
+ * @return byte at an offset into p OR ZERO IF 'offset' >= p->tot_len
+ */
+u8_t
+pbuf_get_at(const struct pbuf* p, u16_t offset)
+{
+  int ret = pbuf_try_get_at(p, offset);
+  if (ret >= 0) {
+    return (u8_t)ret;
+  }
+  return 0;
+}
+
+/**
+ * @ingroup pbuf
+ * Get one byte from the specified position in a pbuf
+ *
+ * @param p pbuf to parse
+ * @param offset offset into p of the byte to return
+ * @return byte at an offset into p [0..0xFF] OR negative if 'offset' >= p->tot_len
+ */
+int
+pbuf_try_get_at(const struct pbuf* p, u16_t offset)
+{
+  u16_t q_idx;
+  const struct pbuf* q = pbuf_skip_const(p, offset, &q_idx);
+
+  /* return requested data if pbuf is OK */
+  if ((q != NULL) && (q->len > q_idx)) {
+    return ((u8_t*)q->payload)[q_idx];
+  }
+  return -1;
+}
+
+/**
+ * @ingroup pbuf
+ * Put one byte to the specified position in a pbuf
+ * WARNING: silently ignores offset >= p->tot_len
+ *
+ * @param p pbuf to fill
+ * @param offset offset into p of the byte to write
+ * @param data byte to write at an offset into p
+ */
+void
+pbuf_put_at(struct pbuf* p, u16_t offset, u8_t data)
+{
+  u16_t q_idx;
+  struct pbuf* q = pbuf_skip(p, offset, &q_idx);
+
+  /* write requested data if pbuf is OK */
+  if ((q != NULL) && (q->len > q_idx)) {
+    ((u8_t*)q->payload)[q_idx] = data;
+  }
+}
+
+/**
+ * @ingroup pbuf
+ * Compare pbuf contents at specified offset with memory s2, both of length n
+ *
+ * @param p pbuf to compare
+ * @param offset offset into p at which to start comparing
+ * @param s2 buffer to compare
+ * @param n length of buffer to compare
+ * @return zero if equal, nonzero otherwise
+ *         (0xffff if p is too short, diffoffset+1 otherwise)
+ */
+u16_t
+pbuf_memcmp(const struct pbuf* p, u16_t offset, const void* s2, u16_t n)
+{
+  u16_t start = offset;
+  const struct pbuf* q = p;
+  u16_t i;
+ 
+  /* pbuf long enough to perform check? */
+  if(p->tot_len < (offset + n)) {
+    return 0xffff;
+  }
+ 
+  /* get the correct pbuf from chain. We know it succeeds because of p->tot_len check above. */
+  while ((q != NULL) && (q->len <= start)) {
+    start -= q->len;
+    q = q->next;
+  }
+ 
+  /* return requested data if pbuf is OK */
+  for (i = 0; i < n; i++) {
+    /* We know pbuf_get_at() succeeds because of p->tot_len check above. */
+    u8_t a = pbuf_get_at(q, start + i);
+    u8_t b = ((const u8_t*)s2)[i];
+    if (a != b) {
+      return i+1;
+    }
+  }
+  return 0;
+}
+
+/**
+ * @ingroup pbuf
+ * Find occurrence of mem (with length mem_len) in pbuf p, starting at offset
+ * start_offset.
+ *
+ * @param p pbuf to search, maximum length is 0xFFFE since 0xFFFF is used as
+ *        return value 'not found'
+ * @param mem search for the contents of this buffer
+ * @param mem_len length of 'mem'
+ * @param start_offset offset into p at which to start searching
+ * @return 0xFFFF if substr was not found in p or the index where it was found
+ */
+u16_t
+pbuf_memfind(const struct pbuf* p, const void* mem, u16_t mem_len, u16_t start_offset)
+{
+  u16_t i;
+  u16_t max = p->tot_len - mem_len;
+  if (p->tot_len >= mem_len + start_offset) {
+    for (i = start_offset; i <= max; i++) {
+      u16_t plus = pbuf_memcmp(p, i, mem, mem_len);
+      if (plus == 0) {
+        return i;
+      }
+    }
+  }
+  return 0xFFFF;
+}
+
+/**
+ * Find occurrence of substr with length substr_len in pbuf p, start at offset
+ * start_offset
+ * WARNING: in contrast to strstr(), this one does not stop at the first \0 in
+ * the pbuf/source string!
+ *
+ * @param p pbuf to search, maximum length is 0xFFFE since 0xFFFF is used as
+ *        return value 'not found'
+ * @param substr string to search for in p, maximum length is 0xFFFE
+ * @return 0xFFFF if substr was not found in p or the index where it was found
+ */
+u16_t
+pbuf_strstr(const struct pbuf* p, const char* substr)
+{
+  size_t substr_len;
+  if ((substr == NULL) || (substr[0] == 0) || (p->tot_len == 0xFFFF)) {
+    return 0xFFFF;
+  }
+  substr_len = strlen(substr);
+  if (substr_len >= 0xFFFF) {
+    return 0xFFFF;
+  }
+  return pbuf_memfind(p, substr, (u16_t)substr_len, 0);
+}

+ 521 - 350
thirdparty/lwip/src/core/raw.c

@@ -1,350 +1,521 @@
-/**
- * @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 "lwip/opt.h"
-
-#if LWIP_RAW /* don't build if not configured for use in lwipopts.h */
-
-#include "lwip/def.h"
-#include "lwip/memp.h"
-#include "lwip/ip_addr.h"
-#include "lwip/netif.h"
-#include "lwip/raw.h"
-#include "lwip/stats.h"
-#include "arch/perf.h"
-
-#include <string.h>
-
-/** The list of RAW PCBs */
-static struct raw_pcb *raw_pcbs;
-
-/**
- * Determine if in incoming IP packet is covered by a RAW PCB
- * and if so, pass it to a user-provided receive callback function.
- *
- * Given an incoming IP datagram (as a chain of pbufs) this function
- * finds a corresponding RAW PCB and calls the corresponding receive
- * callback function.
- *
- * @param p pbuf to be demultiplexed to a RAW PCB.
- * @param inp network interface on which the datagram was received.
- * @return - 1 if the packet has been eaten by a RAW PCB receive
- *           callback function. The caller MAY NOT not reference the
- *           packet any longer, and MAY NOT call pbuf_free().
- * @return - 0 if packet is not eaten (pbuf is still referenced by the
- *           caller).
- *
- */
-u8_t
-raw_input(struct pbuf *p, struct netif *inp)
-{
-  struct raw_pcb *pcb, *prev;
-  struct ip_hdr *iphdr;
-  s16_t proto;
-  u8_t eaten = 0;
-
-  LWIP_UNUSED_ARG(inp);
-
-  iphdr = (struct ip_hdr *)p->payload;
-  proto = IPH_PROTO(iphdr);
-
-  prev = NULL;
-  pcb = raw_pcbs;
-  /* loop through all raw pcbs until the packet is eaten by one */
-  /* this allows multiple pcbs to match against the packet by design */
-  while ((eaten == 0) && (pcb != NULL)) {
-    if ((pcb->protocol == proto) &&
-        (ip_addr_isany(&pcb->local_ip) ||
-         ip_addr_cmp(&(pcb->local_ip), &current_iphdr_dest))) {
-#if IP_SOF_BROADCAST_RECV
-      /* broadcast filter? */
-      if (ip_get_option(pcb, SOF_BROADCAST) || !ip_addr_isbroadcast(&current_iphdr_dest, inp))
-#endif /* IP_SOF_BROADCAST_RECV */
-      {
-        /* 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, ip_current_src_addr()) != 0) {
-            /* receive function ate the packet */
-            p = NULL;
-            eaten = 1;
-            if (prev != NULL) {
-            /* move the pcb to the front of raw_pcbs so that is
-               found faster next time */
-              prev->next = pcb->next;
-              pcb->next = raw_pcbs;
-              raw_pcbs = pcb;
-            }
-          }
-        }
-        /* no receive callback function was set for this raw PCB */
-      }
-      /* drop the packet */
-    }
-    prev = pcb;
-    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, ip_addr_t *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, ip_addr_t *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, raw_recv_fn recv, 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, ip_addr_t *ipaddr)
-{
-  err_t err;
-  struct netif *netif;
-  ip_addr_t *src_ip;
-  struct pbuf *q; /* q will be sent down the stack */
-  
-  LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_TRACE, ("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 | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("raw_sendto: could not allocate header\n"));
-      return ERR_MEM;
-    }
-    if (p->tot_len != 0) {
-      /* chain header q in front of given pbuf p */
-      pbuf_chain(q, p);
-    }
-    /* { first pbuf q points to header pbuf } */
-    LWIP_DEBUGF(RAW_DEBUG, ("raw_sendto: added header pbuf %p before given pbuf %p\n", (void *)q, (void *)p));
-  }  else {
-    /* first pbuf q equals given pbuf */
-    q = p;
-    if(pbuf_header(q, -IP_HLEN)) {
-      LWIP_ASSERT("Can't restore header we just removed!", 0);
-      return ERR_MEM;
-    }
-  }
-
-  if ((netif = ip_route(ipaddr)) == NULL) {
-    LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, ("raw_sendto: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
-      ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr)));
-    /* free any temporary header pbuf allocated by pbuf_header() */
-    if (q != p) {
-      pbuf_free(q);
-    }
-    return ERR_RTE;
-  }
-
-#if IP_SOF_BROADCAST
-  /* broadcast filter? */
-  if (!ip_get_option(pcb, SOF_BROADCAST) && ip_addr_isbroadcast(ipaddr, netif)) {
-    LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, ("raw_sendto: SOF_BROADCAST not enabled on pcb %p\n", (void *)pcb));
-    /* free any temporary header pbuf allocated by pbuf_header() */
-    if (q != p) {
-      pbuf_free(q);
-    }
-    return ERR_VAL;
-  }
-#endif /* IP_SOF_BROADCAST */
-
-  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);
-  }
-
-  NETIF_SET_HWADDRHINT(netif, &pcb->addr_hint);
-  err = ip_output_if (q, src_ip, ipaddr, pcb->ttl, pcb->tos, pcb->protocol, netif);
-  NETIF_SET_HWADDRHINT(netif, NULL);
-
-  /* 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
- *
- */
-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(u8_t proto)
-{
-  struct raw_pcb *pcb;
-
-  LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_TRACE, ("raw_new\n"));
-
-  pcb = (struct raw_pcb *)memp_malloc(MEMP_RAW_PCB);
-  /* could allocate RAW PCB? */
-  if (pcb != NULL) {
-    /* initialize PCB to all zeroes */
-    memset(pcb, 0, sizeof(struct raw_pcb));
-    pcb->protocol = proto;
-    pcb->ttl = RAW_TTL;
-    pcb->next = raw_pcbs;
-    raw_pcbs = pcb;
-  }
-  return pcb;
-}
-
-#endif /* LWIP_RAW */
+/**
+ * @file
+ * Implementation of raw protocol PCBs for low-level handling of
+ * different types of protocols besides (or overriding) those
+ * already available in lwIP.\n
+ * See also @ref raw_raw
+ * 
+ * @defgroup raw_raw RAW
+ * @ingroup callbackstyle_api
+ * Implementation of raw protocol PCBs for low-level handling of
+ * different types of protocols besides (or overriding) those
+ * already available in lwIP.\n
+ * @see @ref raw_api
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_RAW /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/def.h"
+#include "lwip/memp.h"
+#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+#include "lwip/raw.h"
+#include "lwip/stats.h"
+#include "lwip/ip6.h"
+#include "lwip/ip6_addr.h"
+#include "lwip/inet_chksum.h"
+
+#include <string.h>
+
+/** The list of RAW PCBs */
+static struct raw_pcb *raw_pcbs;
+
+static u8_t
+raw_input_match(struct raw_pcb *pcb, u8_t broadcast)
+{
+  LWIP_UNUSED_ARG(broadcast); /* in IPv6 only case */
+
+#if LWIP_IPV4 && LWIP_IPV6
+  /* Dual-stack: PCBs listening to any IP type also listen to any IP address */
+  if (IP_IS_ANY_TYPE_VAL(pcb->local_ip)) {
+#if IP_SOF_BROADCAST_RECV
+    if ((broadcast != 0) && !ip_get_option(pcb, SOF_BROADCAST)) {
+      return 0;
+    }
+#endif /* IP_SOF_BROADCAST_RECV */
+    return 1;
+  }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
+  /* Only need to check PCB if incoming IP version matches PCB IP version */
+  if (IP_ADDR_PCB_VERSION_MATCH_EXACT(pcb, ip_current_dest_addr())) {
+#if LWIP_IPV4
+    /* Special case: IPv4 broadcast: receive all broadcasts
+     * Note: broadcast variable can only be 1 if it is an IPv4 broadcast */
+    if (broadcast != 0) {
+#if IP_SOF_BROADCAST_RECV
+      if (ip_get_option(pcb, SOF_BROADCAST))
+#endif /* IP_SOF_BROADCAST_RECV */
+      {
+        if (ip4_addr_isany(ip_2_ip4(&pcb->local_ip))) {
+          return 1;
+        }
+      }
+    } else
+#endif /* LWIP_IPV4 */
+    /* Handle IPv4 and IPv6: catch all or exact match */
+    if (ip_addr_isany(&pcb->local_ip) ||
+       ip_addr_cmp(&pcb->local_ip, ip_current_dest_addr())) {
+      return 1;
+    }
+  }
+
+  return 0;
+}
+
+/**
+ * Determine if in incoming IP packet is covered by a RAW PCB
+ * and if so, pass it to a user-provided receive callback function.
+ *
+ * Given an incoming IP datagram (as a chain of pbufs) this function
+ * finds a corresponding RAW PCB and calls the corresponding receive
+ * callback function.
+ *
+ * @param p pbuf to be demultiplexed to a RAW PCB.
+ * @param inp network interface on which the datagram was received.
+ * @return - 1 if the packet has been eaten by a RAW PCB receive
+ *           callback function. The caller MAY NOT not reference the
+ *           packet any longer, and MAY NOT call pbuf_free().
+ * @return - 0 if packet is not eaten (pbuf is still referenced by the
+ *           caller).
+ *
+ */
+u8_t
+raw_input(struct pbuf *p, struct netif *inp)
+{
+  struct raw_pcb *pcb, *prev;
+  s16_t proto;
+  u8_t eaten = 0;
+  u8_t broadcast = ip_addr_isbroadcast(ip_current_dest_addr(), ip_current_netif());
+
+  LWIP_UNUSED_ARG(inp);
+
+#if LWIP_IPV6
+#if LWIP_IPV4
+  if (IP_HDR_GET_VERSION(p->payload) == 6)
+#endif /* LWIP_IPV4 */
+  {
+    struct ip6_hdr *ip6hdr = (struct ip6_hdr *)p->payload;
+    proto = IP6H_NEXTH(ip6hdr);
+  }
+#if LWIP_IPV4
+  else
+#endif /* LWIP_IPV4 */
+#endif /* LWIP_IPV6 */
+#if LWIP_IPV4
+  {
+    proto = IPH_PROTO((struct ip_hdr *)p->payload);
+  }
+#endif /* LWIP_IPV4 */
+
+  prev = NULL;
+  pcb = raw_pcbs;
+  /* loop through all raw pcbs until the packet is eaten by one */
+  /* this allows multiple pcbs to match against the packet by design */
+  while ((eaten == 0) && (pcb != NULL)) {
+    if ((pcb->protocol == proto) && raw_input_match(pcb, broadcast)) {
+      /* receive callback function available? */
+      if (pcb->recv != NULL) {
+#ifndef LWIP_NOASSERT
+        void* old_payload = p->payload;
+#endif
+        /* the receive callback function did not eat the packet? */
+        eaten = pcb->recv(pcb->recv_arg, pcb, p, ip_current_src_addr());
+        if (eaten != 0) {
+          /* receive function ate the packet */
+          p = NULL;
+          eaten = 1;
+          if (prev != NULL) {
+          /* move the pcb to the front of raw_pcbs so that is
+             found faster next time */
+            prev->next = pcb->next;
+            pcb->next = raw_pcbs;
+            raw_pcbs = pcb;
+          }
+        } else {
+          /* sanity-check that the receive callback did not alter the pbuf */
+          LWIP_ASSERT("raw pcb recv callback altered pbuf payload pointer without eating packet",
+            p->payload == old_payload);
+        }
+      }
+      /* no receive callback function was set for this raw PCB */
+    }
+    /* drop the packet */
+    prev = pcb;
+    pcb = pcb->next;
+  }
+  return eaten;
+}
+
+/**
+ * @ingroup raw_raw
+ * Bind a RAW PCB.
+ *
+ * @param pcb RAW PCB to be bound with a local address ipaddr.
+ * @param ipaddr local IP address to bind with. Use IP4_ADDR_ANY to
+ * bind to all local interfaces.
+ *
+ * @return lwIP error code.
+ * - ERR_OK. Successful. No error occurred.
+ * - ERR_USE. The specified IP address is already bound to by
+ * another RAW PCB.
+ *
+ * @see raw_disconnect()
+ */
+err_t
+raw_bind(struct raw_pcb *pcb, const ip_addr_t *ipaddr)
+{
+  if ((pcb == NULL) || (ipaddr == NULL)) {
+    return ERR_VAL;
+  }
+  ip_addr_set_ipaddr(&pcb->local_ip, ipaddr);
+  return ERR_OK;
+}
+
+/**
+ * @ingroup raw_raw
+ * Connect an RAW PCB. This function is required by upper layers
+ * of lwip. Using the raw api you could use raw_sendto() instead
+ *
+ * This will associate the RAW PCB with the remote address.
+ *
+ * @param pcb RAW PCB to be connected with remote address ipaddr and port.
+ * @param ipaddr remote IP address to connect with.
+ *
+ * @return lwIP error code
+ *
+ * @see raw_disconnect() and raw_sendto()
+ */
+err_t
+raw_connect(struct raw_pcb *pcb, const ip_addr_t *ipaddr)
+{
+  if ((pcb == NULL) || (ipaddr == NULL)) {
+    return ERR_VAL;
+  }
+  ip_addr_set_ipaddr(&pcb->remote_ip, ipaddr);
+  return ERR_OK;
+}
+
+/**
+ * @ingroup raw_raw
+ * Set the callback function for received packets that match the
+ * raw PCB's protocol and binding.
+ *
+ * The callback function MUST either
+ * - eat the packet by calling pbuf_free() and returning non-zero. The
+ *   packet will not be passed to other raw PCBs or other protocol layers.
+ * - not free the packet, and return zero. The packet will be matched
+ *   against further PCBs and/or forwarded to another protocol layers.
+ */
+void
+raw_recv(struct raw_pcb *pcb, raw_recv_fn recv, void *recv_arg)
+{
+  /* remember recv() callback and user data */
+  pcb->recv = recv;
+  pcb->recv_arg = recv_arg;
+}
+
+/**
+ * @ingroup raw_raw
+ * Send the raw IP packet to the given address. 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, const ip_addr_t *ipaddr)
+{
+  err_t err;
+  struct netif *netif;
+  const ip_addr_t *src_ip;
+  struct pbuf *q; /* q will be sent down the stack */
+  s16_t header_size;
+
+  if ((pcb == NULL) || (ipaddr == NULL) || !IP_ADDR_PCB_VERSION_MATCH(pcb, ipaddr)) {
+    return ERR_VAL;
+  }
+
+  LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_TRACE, ("raw_sendto\n"));
+
+  header_size = (
+#if LWIP_IPV4 && LWIP_IPV6
+    IP_IS_V6(ipaddr) ? IP6_HLEN : IP_HLEN);
+#elif LWIP_IPV4
+    IP_HLEN);
+#else
+    IP6_HLEN);
+#endif
+
+  /* not enough space to add an IP header to first pbuf in given p chain? */
+  if (pbuf_header(p, header_size)) {
+    /* allocate header in new pbuf */
+    q = pbuf_alloc(PBUF_IP, 0, PBUF_RAM);
+    /* new header pbuf could not be allocated? */
+    if (q == NULL) {
+      LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("raw_sendto: could not allocate header\n"));
+      return ERR_MEM;
+    }
+    if (p->tot_len != 0) {
+      /* chain header q in front of given pbuf p */
+      pbuf_chain(q, p);
+    }
+    /* { first pbuf q points to header pbuf } */
+    LWIP_DEBUGF(RAW_DEBUG, ("raw_sendto: added header pbuf %p before given pbuf %p\n", (void *)q, (void *)p));
+  } else {
+    /* first pbuf q equals given pbuf */
+    q = p;
+    if (pbuf_header(q, -header_size)) {
+      LWIP_ASSERT("Can't restore header we just removed!", 0);
+      return ERR_MEM;
+    }
+  }
+
+  if(IP_IS_ANY_TYPE_VAL(pcb->local_ip)) {
+    /* Don't call ip_route() with IP_ANY_TYPE */
+    netif = ip_route(IP46_ADDR_ANY(IP_GET_TYPE(ipaddr)), ipaddr);
+  } else {
+    netif = ip_route(&pcb->local_ip, ipaddr);
+  }
+
+  if (netif == NULL) {
+    LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, ("raw_sendto: No route to "));
+    ip_addr_debug_print(RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, ipaddr);
+    /* free any temporary header pbuf allocated by pbuf_header() */
+    if (q != p) {
+      pbuf_free(q);
+    }
+    return ERR_RTE;
+  }
+
+#if IP_SOF_BROADCAST
+  if (IP_IS_V4(ipaddr))
+  {
+    /* broadcast filter? */
+    if (!ip_get_option(pcb, SOF_BROADCAST) && ip_addr_isbroadcast(ipaddr, netif)) {
+      LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, ("raw_sendto: SOF_BROADCAST not enabled on pcb %p\n", (void *)pcb));
+      /* free any temporary header pbuf allocated by pbuf_header() */
+      if (q != p) {
+        pbuf_free(q);
+      }
+      return ERR_VAL;
+    }
+  }
+#endif /* IP_SOF_BROADCAST */
+
+  if (ip_addr_isany(&pcb->local_ip)) {
+    /* use outgoing network interface IP address as source address */
+    src_ip = ip_netif_get_local_ip(netif, ipaddr);
+#if LWIP_IPV6
+    if (src_ip == NULL) {
+      if (q != p) {
+        pbuf_free(q);
+      }
+      return ERR_RTE;
+    }
+#endif /* LWIP_IPV6 */
+  } else {
+    /* use RAW PCB local IP address as source address */
+    src_ip = &pcb->local_ip;
+  }
+
+#if LWIP_IPV6
+  /* If requested, based on the IPV6_CHECKSUM socket option per RFC3542,
+     compute the checksum and update the checksum in the payload. */
+  if (IP_IS_V6(ipaddr) && pcb->chksum_reqd) {
+    u16_t chksum = ip6_chksum_pseudo(p, pcb->protocol, p->tot_len, ip_2_ip6(src_ip), ip_2_ip6(ipaddr));
+    LWIP_ASSERT("Checksum must fit into first pbuf", p->len >= (pcb->chksum_offset + 2));
+    SMEMCPY(((u8_t *)p->payload) + pcb->chksum_offset, &chksum, sizeof(u16_t));
+  }
+#endif
+
+  NETIF_SET_HWADDRHINT(netif, &pcb->addr_hint);
+  err = ip_output_if(q, src_ip, ipaddr, pcb->ttl, pcb->tos, pcb->protocol, netif);
+  NETIF_SET_HWADDRHINT(netif, NULL);
+
+  /* did we chain a header earlier? */
+  if (q != p) {
+    /* free the header */
+    pbuf_free(q);
+  }
+  return err;
+}
+
+/**
+ * @ingroup raw_raw
+ * Send the raw IP packet to the address given by raw_connect()
+ *
+ * @param pcb the raw pcb which to send
+ * @param p the IP payload to send
+ *
+ */
+err_t
+raw_send(struct raw_pcb *pcb, struct pbuf *p)
+{
+  return raw_sendto(pcb, p, &pcb->remote_ip);
+}
+
+/**
+ * @ingroup raw_raw
+ * Remove an RAW PCB.
+ *
+ * @param pcb RAW PCB to be removed. The PCB is removed from the list of
+ * RAW PCB's and the data structure is freed from memory.
+ *
+ * @see raw_new()
+ */
+void
+raw_remove(struct raw_pcb *pcb)
+{
+  struct raw_pcb *pcb2;
+  /* pcb to be removed is first in list? */
+  if (raw_pcbs == pcb) {
+    /* make list start at 2nd pcb */
+    raw_pcbs = raw_pcbs->next;
+    /* pcb not 1st in list */
+  } else {
+    for (pcb2 = raw_pcbs; pcb2 != NULL; pcb2 = pcb2->next) {
+      /* find pcb in raw_pcbs list */
+      if (pcb2->next != NULL && pcb2->next == pcb) {
+        /* remove pcb from list */
+        pcb2->next = pcb->next;
+        break;
+      }
+    }
+  }
+  memp_free(MEMP_RAW_PCB, pcb);
+}
+
+/**
+ * @ingroup raw_raw
+ * Create a RAW PCB.
+ *
+ * @return The RAW PCB which was created. NULL if the PCB data structure
+ * could not be allocated.
+ *
+ * @param proto the protocol number of the IPs payload (e.g. IP_PROTO_ICMP)
+ *
+ * @see raw_remove()
+ */
+struct raw_pcb *
+raw_new(u8_t proto)
+{
+  struct raw_pcb *pcb;
+
+  LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_TRACE, ("raw_new\n"));
+
+  pcb = (struct raw_pcb *)memp_malloc(MEMP_RAW_PCB);
+  /* could allocate RAW PCB? */
+  if (pcb != NULL) {
+    /* initialize PCB to all zeroes */
+    memset(pcb, 0, sizeof(struct raw_pcb));
+    pcb->protocol = proto;
+    pcb->ttl = RAW_TTL;
+    pcb->next = raw_pcbs;
+    raw_pcbs = pcb;
+  }
+  return pcb;
+}
+
+/**
+ * @ingroup raw_raw
+ * Create a RAW PCB for specific IP type.
+ *
+ * @return The RAW PCB which was created. NULL if the PCB data structure
+ * could not be allocated.
+ *
+ * @param type IP address type, see @ref lwip_ip_addr_type definitions.
+ * If you want to listen to IPv4 and IPv6 (dual-stack) packets,
+ * supply @ref IPADDR_TYPE_ANY as argument and bind to @ref IP_ANY_TYPE.
+ * @param proto the protocol number (next header) of the IPv6 packet payload
+ *              (e.g. IP6_NEXTH_ICMP6)
+ *
+ * @see raw_remove()
+ */
+struct raw_pcb *
+raw_new_ip_type(u8_t type, u8_t proto)
+{
+  struct raw_pcb *pcb;
+  pcb = raw_new(proto);
+#if LWIP_IPV4 && LWIP_IPV6
+  if (pcb != NULL) {
+    IP_SET_TYPE_VAL(pcb->local_ip,  type);
+    IP_SET_TYPE_VAL(pcb->remote_ip, type);
+  }
+#else /* LWIP_IPV4 && LWIP_IPV6 */
+  LWIP_UNUSED_ARG(type);
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+  return pcb;
+}
+
+/** This function is called from netif.c when address is changed
+ *
+ * @param old_addr IP address of the netif before change
+ * @param new_addr IP address of the netif after change
+ */
+void raw_netif_ip_addr_changed(const ip_addr_t* old_addr, const ip_addr_t* new_addr)
+{
+  struct raw_pcb* rpcb;
+
+  if (!ip_addr_isany(old_addr) && !ip_addr_isany(new_addr)) {
+    for (rpcb = raw_pcbs; rpcb != NULL; rpcb = rpcb->next) {
+      /* PCB bound to current local interface address? */
+      if (ip_addr_cmp(&rpcb->local_ip, old_addr)) {
+        /* The PCB is bound to the old ipaddr and
+         * is set to bound to the new one instead */
+        ip_addr_copy(rpcb->local_ip, *new_addr);
+      }
+    }
+  }
+}
+
+#endif /* LWIP_RAW */

+ 0 - 657
thirdparty/lwip/src/core/snmp/asn1_dec.c

@@ -1,657 +0,0 @@
-/**
- * @file
- * Abstract Syntax Notation One (ISO 8824, 8825) decoding
- *
- * @todo not optimised (yet), favor correctness over speed, favor speed over size
- */
-
-/*
- * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * Author: Christiaan Simons <christiaan.simons@axon.tv>
- */
-
-#include "lwip/opt.h"
-
-#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
-
-#include "lwip/snmp_asn1.h"
-
-/**
- * Retrieves type field from incoming pbuf chain.
- *
- * @param p points to a pbuf holding an ASN1 coded type field
- * @param ofs points to the offset within the pbuf chain of the ASN1 coded type field
- * @param type return ASN1 type
- * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode
- */
-err_t
-snmp_asn1_dec_type(struct pbuf *p, u16_t ofs, u8_t *type)
-{
-  u16_t plen, base;
-  u8_t *msg_ptr;
-
-  plen = 0;
-  while (p != NULL)
-  {
-    base = plen;
-    plen += p->len;
-    if (ofs < plen)
-    {
-      msg_ptr = (u8_t*)p->payload;
-      msg_ptr += ofs - base;
-      *type = *msg_ptr;
-      return ERR_OK;
-    }
-    p = p->next;
-  }
-  /* p == NULL, ofs >= plen */
-  return ERR_ARG;
-}
-
-/**
- * Decodes length field from incoming pbuf chain into host length.
- *
- * @param p points to a pbuf holding an ASN1 coded length
- * @param ofs points to the offset within the pbuf chain of the ASN1 coded length
- * @param octets_used returns number of octets used by the length code
- * @param length return host order length, upto 64k
- * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode
- */
-err_t
-snmp_asn1_dec_length(struct pbuf *p, u16_t ofs, u8_t *octets_used, u16_t *length)
-{
-  u16_t plen, base;
-  u8_t *msg_ptr;
-
-  plen = 0;
-  while (p != NULL)
-  {
-    base = plen;
-    plen += p->len;
-    if (ofs < plen)
-    {
-      msg_ptr = (u8_t*)p->payload;
-      msg_ptr += ofs - base;
-
-      if (*msg_ptr < 0x80)
-      {
-        /* primitive definite length format */
-        *octets_used = 1;
-        *length = *msg_ptr;
-        return ERR_OK;
-      }
-      else if (*msg_ptr == 0x80)
-      {
-        /* constructed indefinite length format, termination with two zero octets */
-        u8_t zeros;
-        u8_t i;
-
-        *length = 0;
-        zeros = 0;
-        while (zeros != 2)
-        {
-          i = 2;
-          while (i > 0)
-          {
-            i--;
-            (*length) += 1;
-            ofs += 1;
-            if (ofs >= plen)
-            {
-              /* next octet in next pbuf */
-              p = p->next;
-              if (p == NULL) { return ERR_ARG; }
-              msg_ptr = (u8_t*)p->payload;
-              plen += p->len;
-            }
-            else
-            {
-              /* next octet in same pbuf */
-              msg_ptr++;
-            }
-            if (*msg_ptr == 0)
-            {
-              zeros++;
-              if (zeros == 2)
-              {
-                /* stop while (i > 0) */
-                i = 0;
-              }
-            }
-            else
-            {
-              zeros = 0;
-            }
-          }
-        }
-        *octets_used = 1;
-        return ERR_OK;
-      }
-      else if (*msg_ptr == 0x81)
-      {
-        /* constructed definite length format, one octet */
-        ofs += 1;
-        if (ofs >= plen)
-        {
-          /* next octet in next pbuf */
-          p = p->next;
-          if (p == NULL) { return ERR_ARG; }
-          msg_ptr = (u8_t*)p->payload;
-        }
-        else
-        {
-          /* next octet in same pbuf */
-          msg_ptr++;
-        }
-        *length = *msg_ptr;
-        *octets_used = 2;
-        return ERR_OK;
-      }
-      else if (*msg_ptr == 0x82)
-      {
-        u8_t i;
-
-        /* constructed definite length format, two octets */
-        i = 2;
-        while (i > 0)
-        {
-          i--;
-          ofs += 1;
-          if (ofs >= plen)
-          {
-            /* next octet in next pbuf */
-            p = p->next;
-            if (p == NULL) { return ERR_ARG; }
-            msg_ptr = (u8_t*)p->payload;
-            plen += p->len;
-          }
-          else
-          {
-            /* next octet in same pbuf */
-            msg_ptr++;
-          }
-          if (i == 0)
-          {
-            /* least significant length octet */
-            *length |= *msg_ptr;
-          }
-          else
-          {
-            /* most significant length octet */
-            *length = (*msg_ptr) << 8;
-          }
-        }
-        *octets_used = 3;
-        return ERR_OK;
-      }
-      else
-      {
-        /* constructed definite length format 3..127 octets, this is too big (>64k) */
-        /**  @todo: do we need to accept inefficient codings with many leading zero's? */
-        *octets_used = 1 + ((*msg_ptr) & 0x7f);
-        return ERR_ARG;
-      }
-    }
-    p = p->next;
-  }
-
-  /* p == NULL, ofs >= plen */
-  return ERR_ARG;
-}
-
-/**
- * Decodes positive integer (counter, gauge, timeticks) into u32_t.
- *
- * @param p points to a pbuf holding an ASN1 coded integer
- * @param ofs points to the offset within the pbuf chain of the ASN1 coded integer
- * @param len length of the coded integer field
- * @param value return host order integer
- * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode
- *
- * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded
- * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value
- * of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!!
- */
-err_t
-snmp_asn1_dec_u32t(struct pbuf *p, u16_t ofs, u16_t len, u32_t *value)
-{
-  u16_t plen, base;
-  u8_t *msg_ptr;
-
-  plen = 0;
-  while (p != NULL)
-  {
-    base = plen;
-    plen += p->len;
-    if (ofs < plen)
-    {
-      msg_ptr = (u8_t*)p->payload;
-      msg_ptr += ofs - base;
-      if ((len > 0) && (len < 6))
-      {
-        /* start from zero */
-        *value = 0;
-        if (*msg_ptr & 0x80)
-        {
-          /* negative, expecting zero sign bit! */
-          return ERR_ARG;
-        }
-        else
-        {
-          /* positive */
-          if ((len > 1) && (*msg_ptr == 0))
-          {
-            /* skip leading "sign byte" octet 0x00 */
-            len--;
-            ofs += 1;
-            if (ofs >= plen)
-            {
-              /* next octet in next pbuf */
-              p = p->next;
-              if (p == NULL) { return ERR_ARG; }
-              msg_ptr = (u8_t*)p->payload;
-              plen += p->len;
-            }
-            else
-            {
-              /* next octet in same pbuf */
-              msg_ptr++;
-            }
-          }
-        }
-        /* OR octets with value */
-        while (len > 1)
-        {
-          len--;
-          *value |= *msg_ptr;
-          *value <<= 8;
-          ofs += 1;
-          if (ofs >= plen)
-          {
-            /* next octet in next pbuf */
-            p = p->next;
-            if (p == NULL) { return ERR_ARG; }
-            msg_ptr = (u8_t*)p->payload;
-            plen += p->len;
-          }
-          else
-          {
-            /* next octet in same pbuf */
-            msg_ptr++;
-          }
-        }
-        *value |= *msg_ptr;
-        return ERR_OK;
-      }
-      else
-      {
-        return ERR_ARG;
-      }
-    }
-    p = p->next;
-  }
-  /* p == NULL, ofs >= plen */
-  return ERR_ARG;
-}
-
-/**
- * Decodes integer into s32_t.
- *
- * @param p points to a pbuf holding an ASN1 coded integer
- * @param ofs points to the offset within the pbuf chain of the ASN1 coded integer
- * @param len length of the coded integer field
- * @param value return host order integer
- * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode
- *
- * @note ASN coded integers are _always_ signed!
- */
-err_t
-snmp_asn1_dec_s32t(struct pbuf *p, u16_t ofs, u16_t len, s32_t *value)
-{
-  u16_t plen, base;
-  u8_t *msg_ptr;
-#if BYTE_ORDER == LITTLE_ENDIAN
-  u8_t *lsb_ptr = (u8_t*)value;
-#endif
-#if BYTE_ORDER == BIG_ENDIAN
-  u8_t *lsb_ptr = (u8_t*)value + sizeof(s32_t) - 1;
-#endif
-  u8_t sign;
-
-  plen = 0;
-  while (p != NULL)
-  {
-    base = plen;
-    plen += p->len;
-    if (ofs < plen)
-    {
-      msg_ptr = (u8_t*)p->payload;
-      msg_ptr += ofs - base;
-      if ((len > 0) && (len < 5))
-      {
-        if (*msg_ptr & 0x80)
-        {
-          /* negative, start from -1 */
-          *value = -1;
-          sign = 1;
-        }
-        else
-        {
-          /* positive, start from 0 */
-          *value = 0;
-          sign = 0;
-        }
-        /* OR/AND octets with value */
-        while (len > 1)
-        {
-          len--;
-          if (sign)
-          {
-            *lsb_ptr &= *msg_ptr;
-            *value <<= 8;
-            *lsb_ptr |= 255;
-          }
-          else
-          {
-            *lsb_ptr |= *msg_ptr;
-            *value <<= 8;
-          }
-          ofs += 1;
-          if (ofs >= plen)
-          {
-            /* next octet in next pbuf */
-            p = p->next;
-            if (p == NULL) { return ERR_ARG; }
-            msg_ptr = (u8_t*)p->payload;
-            plen += p->len;
-          }
-          else
-          {
-            /* next octet in same pbuf */
-            msg_ptr++;
-          }
-        }
-        if (sign)
-        {
-          *lsb_ptr &= *msg_ptr;
-        }
-        else
-        {
-          *lsb_ptr |= *msg_ptr;
-        }
-        return ERR_OK;
-      }
-      else
-      {
-        return ERR_ARG;
-      }
-    }
-    p = p->next;
-  }
-  /* p == NULL, ofs >= plen */
-  return ERR_ARG;
-}
-
-/**
- * Decodes object identifier from incoming message into array of s32_t.
- *
- * @param p points to a pbuf holding an ASN1 coded object identifier
- * @param ofs points to the offset within the pbuf chain of the ASN1 coded object identifier
- * @param len length of the coded object identifier
- * @param oid return object identifier struct
- * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode
- */
-err_t
-snmp_asn1_dec_oid(struct pbuf *p, u16_t ofs, u16_t len, struct snmp_obj_id *oid)
-{
-  u16_t plen, base;
-  u8_t *msg_ptr;
-  s32_t *oid_ptr;
-
-  plen = 0;
-  while (p != NULL)
-  {
-    base = plen;
-    plen += p->len;
-    if (ofs < plen)
-    {
-      msg_ptr = (u8_t*)p->payload;
-      msg_ptr += ofs - base;
-
-      oid->len = 0;
-      oid_ptr = &oid->id[0];
-      if (len > 0)
-      {
-        /* first compressed octet */
-        if (*msg_ptr == 0x2B)
-        {
-          /* (most) common case 1.3 (iso.org) */
-          *oid_ptr = 1;
-          oid_ptr++;
-          *oid_ptr = 3;
-          oid_ptr++;
-        }
-        else if (*msg_ptr < 40)
-        {
-          *oid_ptr = 0;
-          oid_ptr++;
-          *oid_ptr = *msg_ptr;
-          oid_ptr++;
-        }
-        else if (*msg_ptr < 80)
-        {
-          *oid_ptr = 1;
-          oid_ptr++;
-          *oid_ptr = (*msg_ptr) - 40;
-          oid_ptr++;
-        }
-        else
-        {
-          *oid_ptr = 2;
-          oid_ptr++;
-          *oid_ptr = (*msg_ptr) - 80;
-          oid_ptr++;
-        }
-        oid->len = 2;
-      }
-      else
-      {
-        /* accepting zero length identifiers e.g. for
-           getnext operation. uncommon but valid */
-        return ERR_OK;
-      }
-      len--;
-      if (len > 0)
-      {
-        ofs += 1;
-        if (ofs >= plen)
-        {
-          /* next octet in next pbuf */
-          p = p->next;
-          if (p == NULL) { return ERR_ARG; }
-          msg_ptr = (u8_t*)p->payload;
-          plen += p->len;
-        }
-        else
-        {
-          /* next octet in same pbuf */
-          msg_ptr++;
-        }
-      }
-      while ((len > 0) && (oid->len < LWIP_SNMP_OBJ_ID_LEN))
-      {
-        /* sub-identifier uses multiple octets */
-        if (*msg_ptr & 0x80)
-        {
-          s32_t sub_id = 0;
-
-          while ((*msg_ptr & 0x80) && (len > 1))
-          {
-            len--;
-            sub_id = (sub_id << 7) + (*msg_ptr & ~0x80);
-            ofs += 1;
-            if (ofs >= plen)
-            {
-              /* next octet in next pbuf */
-              p = p->next;
-              if (p == NULL) { return ERR_ARG; }
-              msg_ptr = (u8_t*)p->payload;
-              plen += p->len;
-            }
-            else
-            {
-              /* next octet in same pbuf */
-              msg_ptr++;
-            }
-          }
-          if (!(*msg_ptr & 0x80) && (len > 0))
-          {
-            /* last octet sub-identifier */
-            len--;
-            sub_id = (sub_id << 7) + *msg_ptr;
-            *oid_ptr = sub_id;
-          }
-        }
-        else
-        {
-          /* !(*msg_ptr & 0x80) sub-identifier uses single octet */
-          len--;
-          *oid_ptr = *msg_ptr;
-        }
-        if (len > 0)
-        {
-          /* remaining oid bytes available ... */
-          ofs += 1;
-          if (ofs >= plen)
-          {
-            /* next octet in next pbuf */
-            p = p->next;
-            if (p == NULL) { return ERR_ARG; }
-            msg_ptr = (u8_t*)p->payload;
-            plen += p->len;
-          }
-          else
-          {
-            /* next octet in same pbuf */
-            msg_ptr++;
-          }
-        }
-        oid_ptr++;
-        oid->len++;
-      }
-      if (len == 0)
-      {
-        /* len == 0, end of oid */
-        return ERR_OK;
-      }
-      else
-      {
-        /* len > 0, oid->len == LWIP_SNMP_OBJ_ID_LEN or malformed encoding */
-        return ERR_ARG;
-      }
-
-    }
-    p = p->next;
-  }
-  /* p == NULL, ofs >= plen */
-  return ERR_ARG;
-}
-
-/**
- * Decodes (copies) raw data (ip-addresses, octet strings, opaque encoding)
- * from incoming message into array.
- *
- * @param p points to a pbuf holding an ASN1 coded raw data
- * @param ofs points to the offset within the pbuf chain of the ASN1 coded raw data
- * @param len length of the coded raw data (zero is valid, e.g. empty string!)
- * @param raw_len length of the raw return value
- * @param raw return raw bytes
- * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode
- */
-err_t
-snmp_asn1_dec_raw(struct pbuf *p, u16_t ofs, u16_t len, u16_t raw_len, u8_t *raw)
-{
-  u16_t plen, base;
-  u8_t *msg_ptr;
-
-  if (len > 0)
-  {
-    plen = 0;
-    while (p != NULL)
-    {
-      base = plen;
-      plen += p->len;
-      if (ofs < plen)
-      {
-        msg_ptr = (u8_t*)p->payload;
-        msg_ptr += ofs - base;
-        if (raw_len >= len)
-        {
-          while (len > 1)
-          {
-            /* copy len - 1 octets */
-            len--;
-            *raw = *msg_ptr;
-            raw++;
-            ofs += 1;
-            if (ofs >= plen)
-            {
-              /* next octet in next pbuf */
-              p = p->next;
-              if (p == NULL) { return ERR_ARG; }
-              msg_ptr = (u8_t*)p->payload;
-              plen += p->len;
-            }
-            else
-            {
-              /* next octet in same pbuf */
-              msg_ptr++;
-            }
-          }
-          /* copy last octet */
-          *raw = *msg_ptr;
-          return ERR_OK;
-        }
-        else
-        {
-          /* raw_len < len, not enough dst space */
-          return ERR_ARG;
-        }
-      }
-      p = p->next;
-    }
-    /* p == NULL, ofs >= plen */
-    return ERR_ARG;
-  }
-  else
-  {
-    /* len == 0, empty string */
-    return ERR_OK;
-  }
-}
-
-#endif /* LWIP_SNMP */

+ 0 - 611
thirdparty/lwip/src/core/snmp/asn1_enc.c

@@ -1,611 +0,0 @@
-/**
- * @file
- * Abstract Syntax Notation One (ISO 8824, 8825) encoding
- *
- * @todo not optimised (yet), favor correctness over speed, favor speed over size
- */
-
-/*
- * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * Author: Christiaan Simons <christiaan.simons@axon.tv>
- */
-
-#include "lwip/opt.h"
-
-#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
-
-#include "lwip/snmp_asn1.h"
-
-/**
- * Returns octet count for length.
- *
- * @param length
- * @param octets_needed points to the return value
- */
-void
-snmp_asn1_enc_length_cnt(u16_t length, u8_t *octets_needed)
-{
-  if (length < 0x80U)
-  {
-    *octets_needed = 1;
-  }
-  else if (length < 0x100U)
-  {
-    *octets_needed = 2;
-  }
-  else
-  {
-    *octets_needed = 3;
-  }
-}
-
-/**
- * Returns octet count for an u32_t.
- *
- * @param value
- * @param octets_needed points to the return value
- *
- * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded
- * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value
- * of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!!
- */
-void
-snmp_asn1_enc_u32t_cnt(u32_t value, u16_t *octets_needed)
-{
-  if (value < 0x80UL)
-  {
-    *octets_needed = 1;
-  }
-  else if (value < 0x8000UL)
-  {
-    *octets_needed = 2;
-  }
-  else if (value < 0x800000UL)
-  {
-    *octets_needed = 3;
-  }
-  else if (value < 0x80000000UL)
-  {
-    *octets_needed = 4;
-  }
-  else
-  {
-    *octets_needed = 5;
-  }
-}
-
-/**
- * Returns octet count for an s32_t.
- *
- * @param value
- * @param octets_needed points to the return value
- *
- * @note ASN coded integers are _always_ signed.
- */
-void
-snmp_asn1_enc_s32t_cnt(s32_t value, u16_t *octets_needed)
-{
-  if (value < 0)
-  {
-    value = ~value;
-  }
-  if (value < 0x80L)
-  {
-    *octets_needed = 1;
-  }
-  else if (value < 0x8000L)
-  {
-    *octets_needed = 2;
-  }
-  else if (value < 0x800000L)
-  {
-    *octets_needed = 3;
-  }
-  else
-  {
-    *octets_needed = 4;
-  }
-}
-
-/**
- * Returns octet count for an object identifier.
- *
- * @param ident_len object identifier array length
- * @param ident points to object identifier array
- * @param octets_needed points to the return value
- */
-void
-snmp_asn1_enc_oid_cnt(u8_t ident_len, s32_t *ident, u16_t *octets_needed)
-{
-  s32_t sub_id;
-  u8_t cnt;
-
-  cnt = 0;
-  if (ident_len > 1)
-  {
-    /* compressed prefix in one octet */
-    cnt++;
-    ident_len -= 2;
-    ident += 2;
-  }
-  while(ident_len > 0)
-  {
-    ident_len--;
-    sub_id = *ident;
-
-    sub_id >>= 7;
-    cnt++;
-    while(sub_id > 0)
-    {
-      sub_id >>= 7;
-      cnt++;
-    }
-    ident++;
-  }
-  *octets_needed = cnt;
-}
-
-/**
- * Encodes ASN type field into a pbuf chained ASN1 msg.
- *
- * @param p points to output pbuf to encode value into
- * @param ofs points to the offset within the pbuf chain
- * @param type input ASN1 type
- * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode
- */
-err_t
-snmp_asn1_enc_type(struct pbuf *p, u16_t ofs, u8_t type)
-{
-  u16_t plen, base;
-  u8_t *msg_ptr;
-
-  plen = 0;
-  while (p != NULL)
-  {
-    base = plen;
-    plen += p->len;
-    if (ofs < plen)
-    {
-      msg_ptr = (u8_t*)p->payload;
-      msg_ptr += ofs - base;
-      *msg_ptr = type;
-      return ERR_OK;
-    }
-    p = p->next;
-  }
-  /* p == NULL, ofs >= plen */
-  return ERR_ARG;
-}
-
-/**
- * Encodes host order length field into a pbuf chained ASN1 msg.
- *
- * @param p points to output pbuf to encode length into
- * @param ofs points to the offset within the pbuf chain
- * @param length is the host order length to be encoded
- * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode
- */
-err_t
-snmp_asn1_enc_length(struct pbuf *p, u16_t ofs, u16_t length)
-{
-  u16_t plen, base;
-  u8_t *msg_ptr;
-
-  plen = 0;
-  while (p != NULL)
-  {
-    base = plen;
-    plen += p->len;
-    if (ofs < plen)
-    {
-      msg_ptr = (u8_t*)p->payload;
-      msg_ptr += ofs - base;
-
-      if (length < 0x80)
-      {
-        *msg_ptr = (u8_t)length;
-        return ERR_OK;
-      }
-      else if (length < 0x100)
-      {
-        *msg_ptr = 0x81;
-        ofs += 1;
-        if (ofs >= plen)
-        {
-          /* next octet in next pbuf */
-          p = p->next;
-          if (p == NULL) { return ERR_ARG; }
-          msg_ptr = (u8_t*)p->payload;
-        }
-        else
-        {
-          /* next octet in same pbuf */
-          msg_ptr++;
-        }
-        *msg_ptr = (u8_t)length;
-        return ERR_OK;
-      }
-      else
-      {
-        u8_t i;
-
-        /* length >= 0x100 && length <= 0xFFFF */
-        *msg_ptr = 0x82;
-        i = 2;
-        while (i > 0)
-        {
-          i--;
-          ofs += 1;
-          if (ofs >= plen)
-          {
-            /* next octet in next pbuf */
-            p = p->next;
-            if (p == NULL) { return ERR_ARG; }
-            msg_ptr = (u8_t*)p->payload;
-            plen += p->len;
-          }
-          else
-          {
-            /* next octet in same pbuf */
-            msg_ptr++;
-          }
-          if (i == 0)
-          {
-            /* least significant length octet */
-            *msg_ptr = (u8_t)length;
-          }
-          else
-          {
-            /* most significant length octet */
-            *msg_ptr = (u8_t)(length >> 8);
-          }
-        }
-        return ERR_OK;
-      }
-    }
-    p = p->next;
-  }
-  /* p == NULL, ofs >= plen */
-  return ERR_ARG;
-}
-
-/**
- * Encodes u32_t (counter, gauge, timeticks) into a pbuf chained ASN1 msg.
- *
- * @param p points to output pbuf to encode value into
- * @param ofs points to the offset within the pbuf chain
- * @param octets_needed encoding length (from snmp_asn1_enc_u32t_cnt())
- * @param value is the host order u32_t value to be encoded
- * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode
- *
- * @see snmp_asn1_enc_u32t_cnt()
- */
-err_t
-snmp_asn1_enc_u32t(struct pbuf *p, u16_t ofs, u16_t octets_needed, u32_t value)
-{
-  u16_t plen, base;
-  u8_t *msg_ptr;
-
-  plen = 0;
-  while (p != NULL)
-  {
-    base = plen;
-    plen += p->len;
-    if (ofs < plen)
-    {
-      msg_ptr = (u8_t*)p->payload;
-      msg_ptr += ofs - base;
-
-      if (octets_needed == 5)
-      {
-        /* not enough bits in 'value' add leading 0x00 */
-        octets_needed--;
-        *msg_ptr = 0x00;
-        ofs += 1;
-        if (ofs >= plen)
-        {
-          /* next octet in next pbuf */
-          p = p->next;
-          if (p == NULL) { return ERR_ARG; }
-          msg_ptr = (u8_t*)p->payload;
-          plen += p->len;
-        }
-        else
-        {
-          /* next octet in same pbuf */
-          msg_ptr++;
-        }
-      }
-      while (octets_needed > 1)
-      {
-        octets_needed--;
-        *msg_ptr = (u8_t)(value >> (octets_needed << 3));
-        ofs += 1;
-        if (ofs >= plen)
-        {
-          /* next octet in next pbuf */
-          p = p->next;
-          if (p == NULL) { return ERR_ARG; }
-          msg_ptr = (u8_t*)p->payload;
-          plen += p->len;
-        }
-        else
-        {
-          /* next octet in same pbuf */
-          msg_ptr++;
-        }
-      }
-      /* (only) one least significant octet */
-      *msg_ptr = (u8_t)value;
-      return ERR_OK;
-    }
-    p = p->next;
-  }
-  /* p == NULL, ofs >= plen */
-  return ERR_ARG;
-}
-
-/**
- * Encodes s32_t integer into a pbuf chained ASN1 msg.
- *
- * @param p points to output pbuf to encode value into
- * @param ofs points to the offset within the pbuf chain
- * @param octets_needed encoding length (from snmp_asn1_enc_s32t_cnt())
- * @param value is the host order s32_t value to be encoded
- * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode
- *
- * @see snmp_asn1_enc_s32t_cnt()
- */
-err_t
-snmp_asn1_enc_s32t(struct pbuf *p, u16_t ofs, u16_t octets_needed, s32_t value)
-{
-  u16_t plen, base;
-  u8_t *msg_ptr;
-
-  plen = 0;
-  while (p != NULL)
-  {
-    base = plen;
-    plen += p->len;
-    if (ofs < plen)
-    {
-      msg_ptr = (u8_t*)p->payload;
-      msg_ptr += ofs - base;
-
-      while (octets_needed > 1)
-      {
-        octets_needed--;
-        *msg_ptr = (u8_t)(value >> (octets_needed << 3));
-        ofs += 1;
-        if (ofs >= plen)
-        {
-          /* next octet in next pbuf */
-          p = p->next;
-          if (p == NULL) { return ERR_ARG; }
-          msg_ptr = (u8_t*)p->payload;
-          plen += p->len;
-        }
-        else
-        {
-          /* next octet in same pbuf */
-          msg_ptr++;
-        }
-      }
-      /* (only) one least significant octet */
-      *msg_ptr = (u8_t)value;
-      return ERR_OK;
-    }
-    p = p->next;
-  }
-  /* p == NULL, ofs >= plen */
-  return ERR_ARG;
-}
-
-/**
- * Encodes object identifier into a pbuf chained ASN1 msg.
- *
- * @param p points to output pbuf to encode oid into
- * @param ofs points to the offset within the pbuf chain
- * @param ident_len object identifier array length
- * @param ident points to object identifier array
- * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode
- */
-err_t
-snmp_asn1_enc_oid(struct pbuf *p, u16_t ofs, u8_t ident_len, s32_t *ident)
-{
-  u16_t plen, base;
-  u8_t *msg_ptr;
-
-  plen = 0;
-  while (p != NULL)
-  {
-    base = plen;
-    plen += p->len;
-    if (ofs < plen)
-    {
-      msg_ptr = (u8_t*)p->payload;
-      msg_ptr += ofs - base;
-
-      if (ident_len > 1)
-      {
-        if ((ident[0] == 1) && (ident[1] == 3))
-        {
-          /* compressed (most common) prefix .iso.org */
-          *msg_ptr = 0x2b;
-        }
-        else
-        {
-          /* calculate prefix */
-          *msg_ptr = (u8_t)((ident[0] * 40) + ident[1]);
-        }
-        ofs += 1;
-        if (ofs >= plen)
-        {
-          /* next octet in next pbuf */
-          p = p->next;
-          if (p == NULL) { return ERR_ARG; }
-          msg_ptr = (u8_t*)p->payload;
-          plen += p->len;
-        }
-        else
-        {
-          /* next octet in same pbuf */
-          msg_ptr++;
-        }
-        ident_len -= 2;
-        ident += 2;
-      }
-      else
-      {
-/* @bug:  allow empty varbinds for symmetry (we must decode them for getnext), allow partial compression??  */
-        /* ident_len <= 1, at least we need zeroDotZero (0.0) (ident_len == 2) */
-        return ERR_ARG;
-      }
-      while (ident_len > 0)
-      {
-        s32_t sub_id;
-        u8_t shift, tail;
-
-        ident_len--;
-        sub_id = *ident;
-        tail = 0;
-        shift = 28;
-        while(shift > 0)
-        {
-          u8_t code;
-
-          code = (u8_t)(sub_id >> shift);
-          if ((code != 0) || (tail != 0))
-          {
-            tail = 1;
-            *msg_ptr = code | 0x80;
-            ofs += 1;
-            if (ofs >= plen)
-            {
-              /* next octet in next pbuf */
-              p = p->next;
-              if (p == NULL) { return ERR_ARG; }
-              msg_ptr = (u8_t*)p->payload;
-              plen += p->len;
-            }
-            else
-            {
-              /* next octet in same pbuf */
-              msg_ptr++;
-            }
-          }
-          shift -= 7;
-        }
-        *msg_ptr = (u8_t)sub_id & 0x7F;
-        if (ident_len > 0)
-        {
-          ofs += 1;
-          if (ofs >= plen)
-          {
-            /* next octet in next pbuf */
-            p = p->next;
-            if (p == NULL) { return ERR_ARG; }
-            msg_ptr = (u8_t*)p->payload;
-            plen += p->len;
-          }
-          else
-          {
-            /* next octet in same pbuf */
-            msg_ptr++;
-          }
-        }
-        /* proceed to next sub-identifier */
-        ident++;
-      }
-      return ERR_OK;
-    }
-    p = p->next;
-  }
-  /* p == NULL, ofs >= plen */
-  return ERR_ARG;
-}
-
-/**
- * Encodes raw data (octet string, opaque) into a pbuf chained ASN1 msg.
- *
- * @param p points to output pbuf to encode raw data into
- * @param ofs points to the offset within the pbuf chain
- * @param raw_len raw data length
- * @param raw points raw data
- * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode
- */
-err_t
-snmp_asn1_enc_raw(struct pbuf *p, u16_t ofs, u16_t raw_len, u8_t *raw)
-{
-  u16_t plen, base;
-  u8_t *msg_ptr;
-
-  plen = 0;
-  while (p != NULL)
-  {
-    base = plen;
-    plen += p->len;
-    if (ofs < plen)
-    {
-      msg_ptr = (u8_t*)p->payload;
-      msg_ptr += ofs - base;
-
-      while (raw_len > 1)
-      {
-        /* copy raw_len - 1 octets */
-        raw_len--;
-        *msg_ptr = *raw;
-        raw++;
-        ofs += 1;
-        if (ofs >= plen)
-        {
-          /* next octet in next pbuf */
-          p = p->next;
-          if (p == NULL) { return ERR_ARG; }
-          msg_ptr = (u8_t*)p->payload;
-          plen += p->len;
-        }
-        else
-        {
-          /* next octet in same pbuf */
-          msg_ptr++;
-        }
-      }
-      if (raw_len > 0)
-      {
-        /* copy last or single octet */
-        *msg_ptr = *raw;
-      }
-      return ERR_OK;
-    }
-    p = p->next;
-  }
-  /* p == NULL, ofs >= plen */
-  return ERR_ARG;
-}
-
-#endif /* LWIP_SNMP */

+ 0 - 4146
thirdparty/lwip/src/core/snmp/mib2.c

@@ -1,4146 +0,0 @@
-/**
- * @file
- * Management Information Base II (RFC1213) objects and functions.
- *
- * @note the object identifiers for this MIB-2 and private MIB tree
- * must be kept in sorted ascending order. This to ensure correct getnext operation.
- */
-
-/*
- * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * Author: Christiaan Simons <christiaan.simons@axon.tv>
- */
-
-#include "lwip/opt.h"
-
-#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
-
-#include "lwip/snmp.h"
-#include "lwip/netif.h"
-#include "lwip/ip.h"
-#include "lwip/ip_frag.h"
-#include "lwip/mem.h"
-#include "lwip/tcp_impl.h"
-#include "lwip/udp.h"
-#include "lwip/snmp_asn1.h"
-#include "lwip/snmp_structs.h"
-#include "lwip/sys.h"
-#include "netif/etharp.h"
-
-/**
- * IANA assigned enterprise ID for lwIP is 26381
- * @see http://www.iana.org/assignments/enterprise-numbers
- *
- * @note this enterprise ID is assigned to the lwIP project,
- * all object identifiers living under this ID are assigned
- * by the lwIP maintainers (contact Christiaan Simons)!
- * @note don't change this define, use snmp_set_sysobjid()
- *
- * If you need to create your own private MIB you'll need
- * to apply for your own enterprise ID with IANA:
- * http://www.iana.org/numbers.html
- */
-#define SNMP_ENTERPRISE_ID 26381
-#define SNMP_SYSOBJID_LEN 7
-#define SNMP_SYSOBJID {1, 3, 6, 1, 4, 1, SNMP_ENTERPRISE_ID}
-
-#ifndef SNMP_SYSSERVICES
-#define SNMP_SYSSERVICES ((1 << 6) | (1 << 3) | ((IP_FORWARD) << 2))
-#endif
-
-#ifndef SNMP_GET_SYSUPTIME
-#define SNMP_GET_SYSUPTIME(sysuptime)  (sysuptime = (sys_now() / 10))
-#endif
-
-static void system_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
-static void system_get_value(struct obj_def *od, u16_t len, void *value);
-static u8_t system_set_test(struct obj_def *od, u16_t len, void *value);
-static void system_set_value(struct obj_def *od, u16_t len, void *value);
-static void interfaces_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
-static void interfaces_get_value(struct obj_def *od, u16_t len, void *value);
-static void ifentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
-static void ifentry_get_value(struct obj_def *od, u16_t len, void *value);
-#if !SNMP_SAFE_REQUESTS
-static u8_t ifentry_set_test (struct obj_def *od, u16_t len, void *value);
-static void ifentry_set_value (struct obj_def *od, u16_t len, void *value);
-#endif /* SNMP_SAFE_REQUESTS */
-static void atentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
-static void atentry_get_value(struct obj_def *od, u16_t len, void *value);
-static void ip_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
-static void ip_get_value(struct obj_def *od, u16_t len, void *value);
-static u8_t ip_set_test(struct obj_def *od, u16_t len, void *value);
-static void ip_addrentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
-static void ip_addrentry_get_value(struct obj_def *od, u16_t len, void *value);
-static void ip_rteentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
-static void ip_rteentry_get_value(struct obj_def *od, u16_t len, void *value);
-static void ip_ntomentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
-static void ip_ntomentry_get_value(struct obj_def *od, u16_t len, void *value);
-static void icmp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
-static void icmp_get_value(struct obj_def *od, u16_t len, void *value);
-#if LWIP_TCP
-static void tcp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
-static void tcp_get_value(struct obj_def *od, u16_t len, void *value);
-#ifdef THIS_SEEMS_UNUSED
-static void tcpconnentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
-static void tcpconnentry_get_value(struct obj_def *od, u16_t len, void *value);
-#endif
-#endif
-static void udp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
-static void udp_get_value(struct obj_def *od, u16_t len, void *value);
-static void udpentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
-static void udpentry_get_value(struct obj_def *od, u16_t len, void *value);
-static void snmp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
-static void snmp_get_value(struct obj_def *od, u16_t len, void *value);
-static u8_t snmp_set_test(struct obj_def *od, u16_t len, void *value);
-static void snmp_set_value(struct obj_def *od, u16_t len, void *value);
-
-
-/* snmp .1.3.6.1.2.1.11 */
-const mib_scalar_node snmp_scalar = {
-  &snmp_get_object_def,
-  &snmp_get_value,
-  &snmp_set_test,
-  &snmp_set_value,
-  MIB_NODE_SC,
-  0
-};
-const s32_t snmp_ids[28] = {
-  1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15, 16,
-  17, 18, 19, 20, 21, 22, 24, 25, 26, 27, 28, 29, 30
-};
-struct mib_node* const snmp_nodes[28] = {
-  (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar,
-  (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar,
-  (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar,
-  (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar,
-  (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar,
-  (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar,
-  (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar,
-  (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar,
-  (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar,
-  (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar,
-  (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar,
-  (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar,
-  (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar,
-  (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar
-};
-const struct mib_array_node snmp = {
-  &noleafs_get_object_def,
-  &noleafs_get_value,
-  &noleafs_set_test,
-  &noleafs_set_value,
-  MIB_NODE_AR,
-  28,
-  snmp_ids,
-  snmp_nodes
-};
-
-/* dot3 and EtherLike MIB not planned. (transmission .1.3.6.1.2.1.10) */
-/* historical (some say hysterical). (cmot .1.3.6.1.2.1.9) */
-/* lwIP has no EGP, thus may not implement it. (egp .1.3.6.1.2.1.8) */
-
-/* udp .1.3.6.1.2.1.7 */
-/** index root node for udpTable */
-struct mib_list_rootnode udp_root = {
-  &noleafs_get_object_def,
-  &noleafs_get_value,
-  &noleafs_set_test,
-  &noleafs_set_value,
-  MIB_NODE_LR,
-  0,
-  NULL,
-  NULL,
-  0
-};
-const s32_t udpentry_ids[2] = { 1, 2 };
-struct mib_node* const udpentry_nodes[2] = {
-  (struct mib_node*)&udp_root, (struct mib_node*)&udp_root,
-};
-const struct mib_array_node udpentry = {
-  &noleafs_get_object_def,
-  &noleafs_get_value,
-  &noleafs_set_test,
-  &noleafs_set_value,
-  MIB_NODE_AR,
-  2,
-  udpentry_ids,
-  udpentry_nodes
-};
-
-s32_t udptable_id = 1;
-struct mib_node* udptable_node = (struct mib_node*)&udpentry;
-struct mib_ram_array_node udptable = {
-  &noleafs_get_object_def,
-  &noleafs_get_value,
-  &noleafs_set_test,
-  &noleafs_set_value,
-  MIB_NODE_RA,
-  0,
-  &udptable_id,
-  &udptable_node
-};
-
-const mib_scalar_node udp_scalar = {
-  &udp_get_object_def,
-  &udp_get_value,
-  &noleafs_set_test,
-  &noleafs_set_value,
-  MIB_NODE_SC,
-  0
-};
-const s32_t udp_ids[5] = { 1, 2, 3, 4, 5 };
-struct mib_node* const udp_nodes[5] = {
-  (struct mib_node*)&udp_scalar, (struct mib_node*)&udp_scalar,
-  (struct mib_node*)&udp_scalar, (struct mib_node*)&udp_scalar,
-  (struct mib_node*)&udptable
-};
-const struct mib_array_node udp = {
-  &noleafs_get_object_def,
-  &noleafs_get_value,
-  &noleafs_set_test,
-  &noleafs_set_value,
-  MIB_NODE_AR,
-  5,
-  udp_ids,
-  udp_nodes
-};
-
-/* tcp .1.3.6.1.2.1.6 */
-#if LWIP_TCP
-/* only if the TCP protocol is available may implement this group */
-/** index root node for tcpConnTable */
-struct mib_list_rootnode tcpconntree_root = {
-  &noleafs_get_object_def,
-  &noleafs_get_value,
-  &noleafs_set_test,
-  &noleafs_set_value,
-  MIB_NODE_LR,
-  0,
-  NULL,
-  NULL,
-  0
-};
-const s32_t tcpconnentry_ids[5] = { 1, 2, 3, 4, 5 };
-struct mib_node* const tcpconnentry_nodes[5] = {
-  (struct mib_node*)&tcpconntree_root, (struct mib_node*)&tcpconntree_root,
-  (struct mib_node*)&tcpconntree_root, (struct mib_node*)&tcpconntree_root,
-  (struct mib_node*)&tcpconntree_root
-};
-const struct mib_array_node tcpconnentry = {
-  &noleafs_get_object_def,
-  &noleafs_get_value,
-  &noleafs_set_test,
-  &noleafs_set_value,
-  MIB_NODE_AR,
-  5,
-  tcpconnentry_ids,
-  tcpconnentry_nodes
-};
-
-s32_t tcpconntable_id = 1;
-struct mib_node* tcpconntable_node = (struct mib_node*)&tcpconnentry;
-struct mib_ram_array_node tcpconntable = {
-  &noleafs_get_object_def,
-  &noleafs_get_value,
-  &noleafs_set_test,
-  &noleafs_set_value,
-  MIB_NODE_RA,
-/** @todo update maxlength when inserting / deleting from table
-   0 when table is empty, 1 when more than one entry */
-  0,
-  &tcpconntable_id,
-  &tcpconntable_node
-};
-
-const mib_scalar_node tcp_scalar = {
-  &tcp_get_object_def,
-  &tcp_get_value,
-  &noleafs_set_test,
-  &noleafs_set_value,
-  MIB_NODE_SC,
-  0
-};
-const s32_t tcp_ids[15] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
-struct mib_node* const tcp_nodes[15] = {
-  (struct mib_node*)&tcp_scalar, (struct mib_node*)&tcp_scalar,
-  (struct mib_node*)&tcp_scalar, (struct mib_node*)&tcp_scalar,
-  (struct mib_node*)&tcp_scalar, (struct mib_node*)&tcp_scalar,
-  (struct mib_node*)&tcp_scalar, (struct mib_node*)&tcp_scalar,
-  (struct mib_node*)&tcp_scalar, (struct mib_node*)&tcp_scalar,
-  (struct mib_node*)&tcp_scalar, (struct mib_node*)&tcp_scalar,
-  (struct mib_node*)&tcpconntable, (struct mib_node*)&tcp_scalar,
-  (struct mib_node*)&tcp_scalar
-};
-const struct mib_array_node tcp = {
-  &noleafs_get_object_def,
-  &noleafs_get_value,
-  &noleafs_set_test,
-  &noleafs_set_value,
-  MIB_NODE_AR,
-  15,
-  tcp_ids,
-  tcp_nodes
-};
-#endif
-
-/* icmp .1.3.6.1.2.1.5 */
-const mib_scalar_node icmp_scalar = {
-  &icmp_get_object_def,
-  &icmp_get_value,
-  &noleafs_set_test,
-  &noleafs_set_value,
-  MIB_NODE_SC,
-  0
-};
-const s32_t icmp_ids[26] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26 };
-struct mib_node* const icmp_nodes[26] = {
-  (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar,
-  (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar,
-  (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar,
-  (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar,
-  (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar,
-  (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar,
-  (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar,
-  (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar,
-  (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar,
-  (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar,
-  (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar,
-  (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar,
-  (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar
-};
-const struct mib_array_node icmp = {
-  &noleafs_get_object_def,
-  &noleafs_get_value,
-  &noleafs_set_test,
-  &noleafs_set_value,
-  MIB_NODE_AR,
-  26,
-  icmp_ids,
-  icmp_nodes
-};
-
-/** index root node for ipNetToMediaTable */
-struct mib_list_rootnode ipntomtree_root = {
-  &noleafs_get_object_def,
-  &noleafs_get_value,
-  &noleafs_set_test,
-  &noleafs_set_value,
-  MIB_NODE_LR,
-  0,
-  NULL,
-  NULL,
-  0
-};
-const s32_t ipntomentry_ids[4] = { 1, 2, 3, 4 };
-struct mib_node* const ipntomentry_nodes[4] = {
-  (struct mib_node*)&ipntomtree_root, (struct mib_node*)&ipntomtree_root,
-  (struct mib_node*)&ipntomtree_root, (struct mib_node*)&ipntomtree_root
-};
-const struct mib_array_node ipntomentry = {
-  &noleafs_get_object_def,
-  &noleafs_get_value,
-  &noleafs_set_test,
-  &noleafs_set_value,
-  MIB_NODE_AR,
-  4,
-  ipntomentry_ids,
-  ipntomentry_nodes
-};
-
-s32_t ipntomtable_id = 1;
-struct mib_node* ipntomtable_node = (struct mib_node*)&ipntomentry;
-struct mib_ram_array_node ipntomtable = {
-  &noleafs_get_object_def,
-  &noleafs_get_value,
-  &noleafs_set_test,
-  &noleafs_set_value,
-  MIB_NODE_RA,
-  0,
-  &ipntomtable_id,
-  &ipntomtable_node
-};
-
-/** index root node for ipRouteTable */
-struct mib_list_rootnode iprtetree_root = {
-  &noleafs_get_object_def,
-  &noleafs_get_value,
-  &noleafs_set_test,
-  &noleafs_set_value,
-  MIB_NODE_LR,
-  0,
-  NULL,
-  NULL,
-  0
-};
-const s32_t iprteentry_ids[13] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 };
-struct mib_node* const iprteentry_nodes[13] = {
-  (struct mib_node*)&iprtetree_root, (struct mib_node*)&iprtetree_root,
-  (struct mib_node*)&iprtetree_root, (struct mib_node*)&iprtetree_root,
-  (struct mib_node*)&iprtetree_root, (struct mib_node*)&iprtetree_root,
-  (struct mib_node*)&iprtetree_root, (struct mib_node*)&iprtetree_root,
-  (struct mib_node*)&iprtetree_root, (struct mib_node*)&iprtetree_root,
-  (struct mib_node*)&iprtetree_root, (struct mib_node*)&iprtetree_root,
-  (struct mib_node*)&iprtetree_root
-};
-const struct mib_array_node iprteentry = {
-  &noleafs_get_object_def,
-  &noleafs_get_value,
-  &noleafs_set_test,
-  &noleafs_set_value,
-  MIB_NODE_AR,
-  13,
-  iprteentry_ids,
-  iprteentry_nodes
-};
-
-s32_t iprtetable_id = 1;
-struct mib_node* iprtetable_node = (struct mib_node*)&iprteentry;
-struct mib_ram_array_node iprtetable = {
-  &noleafs_get_object_def,
-  &noleafs_get_value,
-  &noleafs_set_test,
-  &noleafs_set_value,
-  MIB_NODE_RA,
-  0,
-  &iprtetable_id,
-  &iprtetable_node
-};
-
-/** index root node for ipAddrTable */
-struct mib_list_rootnode ipaddrtree_root = {
-  &noleafs_get_object_def,
-  &noleafs_get_value,
-  &noleafs_set_test,
-  &noleafs_set_value,
-  MIB_NODE_LR,
-  0,
-  NULL,
-  NULL,
-  0
-};
-const s32_t ipaddrentry_ids[5] = { 1, 2, 3, 4, 5 };
-struct mib_node* const ipaddrentry_nodes[5] = {
-  (struct mib_node*)&ipaddrtree_root,
-  (struct mib_node*)&ipaddrtree_root,
-  (struct mib_node*)&ipaddrtree_root,
-  (struct mib_node*)&ipaddrtree_root,
-  (struct mib_node*)&ipaddrtree_root
-};
-const struct mib_array_node ipaddrentry = {
-  &noleafs_get_object_def,
-  &noleafs_get_value,
-  &noleafs_set_test,
-  &noleafs_set_value,
-  MIB_NODE_AR,
-  5,
-  ipaddrentry_ids,
-  ipaddrentry_nodes
-};
-
-s32_t ipaddrtable_id = 1;
-struct mib_node* ipaddrtable_node = (struct mib_node*)&ipaddrentry;
-struct mib_ram_array_node ipaddrtable = {
-  &noleafs_get_object_def,
-  &noleafs_get_value,
-  &noleafs_set_test,
-  &noleafs_set_value,
-  MIB_NODE_RA,
-  0,
-  &ipaddrtable_id,
-  &ipaddrtable_node
-};
-
-/* ip .1.3.6.1.2.1.4 */
-const mib_scalar_node ip_scalar = {
-  &ip_get_object_def,
-  &ip_get_value,
-  &ip_set_test,
-  &noleafs_set_value,
-  MIB_NODE_SC,
-  0
-};
-const s32_t ip_ids[23] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23 };
-struct mib_node* const ip_nodes[23] = {
-  (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar,
-  (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar,
-  (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar,
-  (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar,
-  (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar,
-  (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar,
-  (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar,
-  (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar,
-  (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar,
-  (struct mib_node*)&ip_scalar, (struct mib_node*)&ipaddrtable,
-  (struct mib_node*)&iprtetable, (struct mib_node*)&ipntomtable,
-  (struct mib_node*)&ip_scalar
-};
-const struct mib_array_node mib2_ip = {
-  &noleafs_get_object_def,
-  &noleafs_get_value,
-  &noleafs_set_test,
-  &noleafs_set_value,
-  MIB_NODE_AR,
-  23,
-  ip_ids,
-  ip_nodes
-};
-
-/** index root node for atTable */
-struct mib_list_rootnode arptree_root = {
-  &noleafs_get_object_def,
-  &noleafs_get_value,
-  &noleafs_set_test,
-  &noleafs_set_value,
-  MIB_NODE_LR,
-  0,
-  NULL,
-  NULL,
-  0
-};
-const s32_t atentry_ids[3] = { 1, 2, 3 };
-struct mib_node* const atentry_nodes[3] = {
-  (struct mib_node*)&arptree_root,
-  (struct mib_node*)&arptree_root,
-  (struct mib_node*)&arptree_root
-};
-const struct mib_array_node atentry = {
-  &noleafs_get_object_def,
-  &noleafs_get_value,
-  &noleafs_set_test,
-  &noleafs_set_value,
-  MIB_NODE_AR,
-  3,
-  atentry_ids,
-  atentry_nodes
-};
-
-const s32_t attable_id = 1;
-struct mib_node* const attable_node = (struct mib_node*)&atentry;
-const struct mib_array_node attable = {
-  &noleafs_get_object_def,
-  &noleafs_get_value,
-  &noleafs_set_test,
-  &noleafs_set_value,
-  MIB_NODE_AR,
-  1,
-  &attable_id,
-  &attable_node
-};
-
-/* at .1.3.6.1.2.1.3 */
-s32_t at_id = 1;
-struct mib_node* mib2_at_node = (struct mib_node*)&attable;
-struct mib_ram_array_node at = {
-  &noleafs_get_object_def,
-  &noleafs_get_value,
-  &noleafs_set_test,
-  &noleafs_set_value,
-  MIB_NODE_RA,
-  0,
-  &at_id,
-  &mib2_at_node
-};
-
-/** index root node for ifTable */
-struct mib_list_rootnode iflist_root = {
-  &ifentry_get_object_def,
-  &ifentry_get_value,
-#if SNMP_SAFE_REQUESTS
-  &noleafs_set_test,
-  &noleafs_set_value,
-#else /* SNMP_SAFE_REQUESTS */
-  &ifentry_set_test,
-  &ifentry_set_value,
-#endif /* SNMP_SAFE_REQUESTS */
-  MIB_NODE_LR,
-  0,
-  NULL,
-  NULL,
-  0
-};
-const s32_t ifentry_ids[22] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22 };
-struct mib_node* const ifentry_nodes[22] = {
-  (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root,
-  (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root,
-  (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root,
-  (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root,
-  (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root,
-  (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root,
-  (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root,
-  (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root,
-  (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root,
-  (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root,
-  (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root
-};
-const struct mib_array_node ifentry = {
-  &noleafs_get_object_def,
-  &noleafs_get_value,
-  &noleafs_set_test,
-  &noleafs_set_value,
-  MIB_NODE_AR,
-  22,
-  ifentry_ids,
-  ifentry_nodes
-};
-
-s32_t iftable_id = 1;
-struct mib_node* iftable_node = (struct mib_node*)&ifentry;
-struct mib_ram_array_node iftable = {
-  &noleafs_get_object_def,
-  &noleafs_get_value,
-  &noleafs_set_test,
-  &noleafs_set_value,
-  MIB_NODE_RA,
-  0,
-  &iftable_id,
-  &iftable_node
-};
-
-/* interfaces .1.3.6.1.2.1.2 */
-const mib_scalar_node interfaces_scalar = {
-  &interfaces_get_object_def,
-  &interfaces_get_value,
-  &noleafs_set_test,
-  &noleafs_set_value,
-  MIB_NODE_SC,
-  0
-};
-const s32_t interfaces_ids[2] = { 1, 2 };
-struct mib_node* const interfaces_nodes[2] = {
-  (struct mib_node*)&interfaces_scalar, (struct mib_node*)&iftable
-};
-const struct mib_array_node interfaces = {
-  &noleafs_get_object_def,
-  &noleafs_get_value,
-  &noleafs_set_test,
-  &noleafs_set_value,
-  MIB_NODE_AR,
-  2,
-  interfaces_ids,
-  interfaces_nodes
-};
-
-
-/*             0 1 2 3 4 5 6 */
-/* system .1.3.6.1.2.1.1 */
-const mib_scalar_node sys_tem_scalar = {
-  &system_get_object_def,
-  &system_get_value,
-  &system_set_test,
-  &system_set_value,
-  MIB_NODE_SC,
-  0
-};
-const s32_t sys_tem_ids[7] = { 1, 2, 3, 4, 5, 6, 7 };
-struct mib_node* const sys_tem_nodes[7] = {
-  (struct mib_node*)&sys_tem_scalar, (struct mib_node*)&sys_tem_scalar,
-  (struct mib_node*)&sys_tem_scalar, (struct mib_node*)&sys_tem_scalar,
-  (struct mib_node*)&sys_tem_scalar, (struct mib_node*)&sys_tem_scalar,
-  (struct mib_node*)&sys_tem_scalar
-};
-/* work around name issue with 'sys_tem', some compiler(s?) seem to reserve 'system' */
-const struct mib_array_node sys_tem = {
-  &noleafs_get_object_def,
-  &noleafs_get_value,
-  &noleafs_set_test,
-  &noleafs_set_value,
-  MIB_NODE_AR,
-  7,
-  sys_tem_ids,
-  sys_tem_nodes
-};
-
-/* mib-2 .1.3.6.1.2.1 */
-#if LWIP_TCP
-#define MIB2_GROUPS 8
-#else
-#define MIB2_GROUPS 7
-#endif
-const s32_t mib2_ids[MIB2_GROUPS] =
-{
-  1,
-  2,
-  3,
-  4,
-  5,
-#if LWIP_TCP
-  6,
-#endif
-  7,
-  11
-};
-struct mib_node* const mib2_nodes[MIB2_GROUPS] = {
-  (struct mib_node*)&sys_tem,
-  (struct mib_node*)&interfaces,
-  (struct mib_node*)&at,
-  (struct mib_node*)&mib2_ip,
-  (struct mib_node*)&icmp,
-#if LWIP_TCP
-  (struct mib_node*)&tcp,
-#endif
-  (struct mib_node*)&udp,
-  (struct mib_node*)&snmp
-};
-
-const struct mib_array_node mib2 = {
-  &noleafs_get_object_def,
-  &noleafs_get_value,
-  &noleafs_set_test,
-  &noleafs_set_value,
-  MIB_NODE_AR,
-  MIB2_GROUPS,
-  mib2_ids,
-  mib2_nodes
-};
-
-/* mgmt .1.3.6.1.2 */
-const s32_t mgmt_ids[1] = { 1 };
-struct mib_node* const mgmt_nodes[1] = { (struct mib_node*)&mib2 };
-const struct mib_array_node mgmt = {
-  &noleafs_get_object_def,
-  &noleafs_get_value,
-  &noleafs_set_test,
-  &noleafs_set_value,
-  MIB_NODE_AR,
-  1,
-  mgmt_ids,
-  mgmt_nodes
-};
-
-/* internet .1.3.6.1 */
-#if SNMP_PRIVATE_MIB
-/* When using a private MIB, you have to create a file 'private_mib.h' that contains
- * a 'struct mib_array_node mib_private' which contains your MIB. */
-s32_t internet_ids[2] = { 2, 4 };
-struct mib_node* const internet_nodes[2] = { (struct mib_node*)&mgmt, (struct mib_node*)&mib_private };
-const struct mib_array_node internet = {
-  &noleafs_get_object_def,
-  &noleafs_get_value,
-  &noleafs_set_test,
-  &noleafs_set_value,
-  MIB_NODE_AR,
-  2,
-  internet_ids,
-  internet_nodes
-};
-#else
-const s32_t internet_ids[1] = { 2 };
-struct mib_node* const internet_nodes[1] = { (struct mib_node*)&mgmt };
-const struct mib_array_node internet = {
-  &noleafs_get_object_def,
-  &noleafs_get_value,
-  &noleafs_set_test,
-  &noleafs_set_value,
-  MIB_NODE_AR,
-  1,
-  internet_ids,
-  internet_nodes
-};
-#endif
-
-/** mib-2.system.sysObjectID  */
-static struct snmp_obj_id sysobjid = {SNMP_SYSOBJID_LEN, SNMP_SYSOBJID};
-/** enterprise ID for generic TRAPs, .iso.org.dod.internet.mgmt.mib-2.snmp */
-static struct snmp_obj_id snmpgrp_id = {7,{1,3,6,1,2,1,11}};
-/** mib-2.system.sysServices */
-static const s32_t sysservices = SNMP_SYSSERVICES;
-
-/** mib-2.system.sysDescr */
-static const u8_t sysdescr_len_default = 4;
-static const u8_t sysdescr_default[] = "lwIP";
-static u8_t* sysdescr_len_ptr = (u8_t*)&sysdescr_len_default;
-static u8_t* sysdescr_ptr = (u8_t*)&sysdescr_default[0];
-/** mib-2.system.sysContact */
-static const u8_t syscontact_len_default = 0;
-static const u8_t syscontact_default[] = "";
-static u8_t* syscontact_len_ptr = (u8_t*)&syscontact_len_default;
-static u8_t* syscontact_ptr = (u8_t*)&syscontact_default[0];
-/** mib-2.system.sysName */
-static const u8_t sysname_len_default = 8;
-static const u8_t sysname_default[] = "FQDN-unk";
-static u8_t* sysname_len_ptr = (u8_t*)&sysname_len_default;
-static u8_t* sysname_ptr = (u8_t*)&sysname_default[0];
-/** mib-2.system.sysLocation */
-static const u8_t syslocation_len_default = 0;
-static const u8_t syslocation_default[] = "";
-static u8_t* syslocation_len_ptr = (u8_t*)&syslocation_len_default;
-static u8_t* syslocation_ptr = (u8_t*)&syslocation_default[0];
-/** mib-2.snmp.snmpEnableAuthenTraps */
-static const u8_t snmpenableauthentraps_default = 2; /* disabled */
-static u8_t* snmpenableauthentraps_ptr = (u8_t*)&snmpenableauthentraps_default;
-
-/** mib-2.interfaces.ifTable.ifEntry.ifSpecific (zeroDotZero) */
-static const struct snmp_obj_id ifspecific = {2, {0, 0}};
-/** mib-2.ip.ipRouteTable.ipRouteEntry.ipRouteInfo (zeroDotZero) */
-static const struct snmp_obj_id iprouteinfo = {2, {0, 0}};
-
-
-
-/* mib-2.system counter(s) */
-static u32_t sysuptime = 0;
-
-/* mib-2.ip counter(s) */
-static u32_t ipinreceives = 0,
-             ipinhdrerrors = 0,
-             ipinaddrerrors = 0,
-             ipforwdatagrams = 0,
-             ipinunknownprotos = 0,
-             ipindiscards = 0,
-             ipindelivers = 0,
-             ipoutrequests = 0,
-             ipoutdiscards = 0,
-             ipoutnoroutes = 0,
-             ipreasmreqds = 0,
-             ipreasmoks = 0,
-             ipreasmfails = 0,
-             ipfragoks = 0,
-             ipfragfails = 0,
-             ipfragcreates = 0,
-             iproutingdiscards = 0;
-/* mib-2.icmp counter(s) */
-static u32_t icmpinmsgs = 0,
-             icmpinerrors = 0,
-             icmpindestunreachs = 0,
-             icmpintimeexcds = 0,
-             icmpinparmprobs = 0,
-             icmpinsrcquenchs = 0,
-             icmpinredirects = 0,
-             icmpinechos = 0,
-             icmpinechoreps = 0,
-             icmpintimestamps = 0,
-             icmpintimestampreps = 0,
-             icmpinaddrmasks = 0,
-             icmpinaddrmaskreps = 0,
-             icmpoutmsgs = 0,
-             icmpouterrors = 0,
-             icmpoutdestunreachs = 0,
-             icmpouttimeexcds = 0,
-             icmpoutparmprobs = 0,
-             icmpoutsrcquenchs = 0,
-             icmpoutredirects = 0,
-             icmpoutechos = 0,
-             icmpoutechoreps = 0,
-             icmpouttimestamps = 0,
-             icmpouttimestampreps = 0,
-             icmpoutaddrmasks = 0,
-             icmpoutaddrmaskreps = 0;
-/* mib-2.tcp counter(s) */
-static u32_t tcpactiveopens = 0,
-             tcppassiveopens = 0,
-             tcpattemptfails = 0,
-             tcpestabresets = 0,
-             tcpinsegs = 0,
-             tcpoutsegs = 0,
-             tcpretranssegs = 0,
-             tcpinerrs = 0,
-             tcpoutrsts = 0;
-/* mib-2.udp counter(s) */
-static u32_t udpindatagrams = 0,
-             udpnoports = 0,
-             udpinerrors = 0,
-             udpoutdatagrams = 0;
-/* mib-2.snmp counter(s) */
-static u32_t snmpinpkts = 0,
-             snmpoutpkts = 0,
-             snmpinbadversions = 0,
-             snmpinbadcommunitynames = 0,
-             snmpinbadcommunityuses = 0,
-             snmpinasnparseerrs = 0,
-             snmpintoobigs = 0,
-             snmpinnosuchnames = 0,
-             snmpinbadvalues = 0,
-             snmpinreadonlys = 0,
-             snmpingenerrs = 0,
-             snmpintotalreqvars = 0,
-             snmpintotalsetvars = 0,
-             snmpingetrequests = 0,
-             snmpingetnexts = 0,
-             snmpinsetrequests = 0,
-             snmpingetresponses = 0,
-             snmpintraps = 0,
-             snmpouttoobigs = 0,
-             snmpoutnosuchnames = 0,
-             snmpoutbadvalues = 0,
-             snmpoutgenerrs = 0,
-             snmpoutgetrequests = 0,
-             snmpoutgetnexts = 0,
-             snmpoutsetrequests = 0,
-             snmpoutgetresponses = 0,
-             snmpouttraps = 0;
-
-
-
-/* prototypes of the following functions are in lwip/src/include/lwip/snmp.h */
-/**
- * Copy octet string.
- *
- * @param dst points to destination
- * @param src points to source
- * @param n number of octets to copy.
- */
-void ocstrncpy(u8_t *dst, u8_t *src, u16_t n)
-{
-  u16_t i = n;
-  while (i > 0) {
-    i--;
-    *dst++ = *src++;
-  }
-}
-
-/**
- * Copy object identifier (s32_t) array.
- *
- * @param dst points to destination
- * @param src points to source
- * @param n number of sub identifiers to copy.
- */
-void objectidncpy(s32_t *dst, s32_t *src, u8_t n)
-{
-  u8_t i = n;
-  while(i > 0) {
-    i--;
-    *dst++ = *src++;
-  }
-}
-
-/**
- * Initializes sysDescr pointers.
- *
- * @param str if non-NULL then copy str pointer
- * @param len points to string length, excluding zero terminator
- */
-void snmp_set_sysdesr(u8_t *str, u8_t *len)
-{
-  if (str != NULL)
-  {
-    sysdescr_ptr = str;
-    sysdescr_len_ptr = len;
-  }
-}
-
-void snmp_get_sysobjid_ptr(struct snmp_obj_id **oid)
-{
-  *oid = &sysobjid;
-}
-
-/**
- * Initializes sysObjectID value.
- *
- * @param oid points to stuct snmp_obj_id to copy
- */
-void snmp_set_sysobjid(struct snmp_obj_id *oid)
-{
-  sysobjid = *oid;
-}
-
-/**
- * Must be called at regular 10 msec interval from a timer interrupt
- * or signal handler depending on your runtime environment.
- */
-void snmp_inc_sysuptime(void)
-{
-  sysuptime++;
-}
-
-void snmp_add_sysuptime(u32_t value)
-{
-  sysuptime+=value;
-}
-
-void snmp_get_sysuptime(u32_t *value)
-{
-  SNMP_GET_SYSUPTIME(sysuptime);
-  *value = sysuptime;
-}
-
-/**
- * Initializes sysContact pointers,
- * e.g. ptrs to non-volatile memory external to lwIP.
- *
- * @param ocstr if non-NULL then copy str pointer
- * @param ocstrlen points to string length, excluding zero terminator
- */
-void snmp_set_syscontact(u8_t *ocstr, u8_t *ocstrlen)
-{
-  if (ocstr != NULL)
-  {
-    syscontact_ptr = ocstr;
-    syscontact_len_ptr = ocstrlen;
-  }
-}
-
-/**
- * Initializes sysName pointers,
- * e.g. ptrs to non-volatile memory external to lwIP.
- *
- * @param ocstr if non-NULL then copy str pointer
- * @param ocstrlen points to string length, excluding zero terminator
- */
-void snmp_set_sysname(u8_t *ocstr, u8_t *ocstrlen)
-{
-  if (ocstr != NULL)
-  {
-    sysname_ptr = ocstr;
-    sysname_len_ptr = ocstrlen;
-  }
-}
-
-/**
- * Initializes sysLocation pointers,
- * e.g. ptrs to non-volatile memory external to lwIP.
- *
- * @param ocstr if non-NULL then copy str pointer
- * @param ocstrlen points to string length, excluding zero terminator
- */
-void snmp_set_syslocation(u8_t *ocstr, u8_t *ocstrlen)
-{
-  if (ocstr != NULL)
-  {
-    syslocation_ptr = ocstr;
-    syslocation_len_ptr = ocstrlen;
-  }
-}
-
-
-void snmp_add_ifinoctets(struct netif *ni, u32_t value)
-{
-  ni->ifinoctets += value;
-}
-
-void snmp_inc_ifinucastpkts(struct netif *ni)
-{
-  (ni->ifinucastpkts)++;
-}
-
-void snmp_inc_ifinnucastpkts(struct netif *ni)
-{
-  (ni->ifinnucastpkts)++;
-}
-
-void snmp_inc_ifindiscards(struct netif *ni)
-{
-  (ni->ifindiscards)++;
-}
-
-void snmp_add_ifoutoctets(struct netif *ni, u32_t value)
-{
-  ni->ifoutoctets += value;
-}
-
-void snmp_inc_ifoutucastpkts(struct netif *ni)
-{
-  (ni->ifoutucastpkts)++;
-}
-
-void snmp_inc_ifoutnucastpkts(struct netif *ni)
-{
-  (ni->ifoutnucastpkts)++;
-}
-
-void snmp_inc_ifoutdiscards(struct netif *ni)
-{
-  (ni->ifoutdiscards)++;
-}
-
-void snmp_inc_iflist(void)
-{
-  struct mib_list_node *if_node = NULL;
-
-  snmp_mib_node_insert(&iflist_root, iflist_root.count + 1, &if_node);
-  /* enable getnext traversal on filled table */
-  iftable.maxlength = 1;
-}
-
-void snmp_dec_iflist(void)
-{
-  snmp_mib_node_delete(&iflist_root, iflist_root.tail);
-  /* disable getnext traversal on empty table */
-  if(iflist_root.count == 0) iftable.maxlength = 0;
-}
-
-/**
- * Inserts ARP table indexes (.xIfIndex.xNetAddress)
- * into arp table index trees (both atTable and ipNetToMediaTable).
- */
-void snmp_insert_arpidx_tree(struct netif *ni, ip_addr_t *ip)
-{
-  struct mib_list_rootnode *at_rn;
-  struct mib_list_node *at_node;
-  s32_t arpidx[5];
-  u8_t level, tree;
-
-  LWIP_ASSERT("ni != NULL", ni != NULL);
-  snmp_netiftoifindex(ni, &arpidx[0]);
-  snmp_iptooid(ip, &arpidx[1]);
-
-  for (tree = 0; tree < 2; tree++)
-  {
-    if (tree == 0)
-    {
-      at_rn = &arptree_root;
-    }
-    else
-    {
-      at_rn = &ipntomtree_root;
-    }
-    for (level = 0; level < 5; level++)
-    {
-      at_node = NULL;
-      snmp_mib_node_insert(at_rn, arpidx[level], &at_node);
-      if ((level != 4) && (at_node != NULL))
-      {
-        if (at_node->nptr == NULL)
-        {
-          at_rn = snmp_mib_lrn_alloc();
-          at_node->nptr = (struct mib_node*)at_rn;
-          if (at_rn != NULL)
-          {
-            if (level == 3)
-            {
-              if (tree == 0)
-              {
-                at_rn->get_object_def = atentry_get_object_def;
-                at_rn->get_value = atentry_get_value;
-              }
-              else
-              {
-                at_rn->get_object_def = ip_ntomentry_get_object_def;
-                at_rn->get_value = ip_ntomentry_get_value;
-              }
-              at_rn->set_test = noleafs_set_test;
-              at_rn->set_value = noleafs_set_value;
-            }
-          }
-          else
-          {
-            /* at_rn == NULL, malloc failure */
-            LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_insert_arpidx_tree() insert failed, mem full"));
-            break;
-          }
-        }
-        else
-        {
-          at_rn = (struct mib_list_rootnode*)at_node->nptr;
-        }
-      }
-    }
-  }
-  /* enable getnext traversal on filled tables */
-  at.maxlength = 1;
-  ipntomtable.maxlength = 1;
-}
-
-/**
- * Removes ARP table indexes (.xIfIndex.xNetAddress)
- * from arp table index trees.
- */
-void snmp_delete_arpidx_tree(struct netif *ni, ip_addr_t *ip)
-{
-  struct mib_list_rootnode *at_rn, *next, *del_rn[5];
-  struct mib_list_node *at_n, *del_n[5];
-  s32_t arpidx[5];
-  u8_t fc, tree, level, del_cnt;
-
-  snmp_netiftoifindex(ni, &arpidx[0]);
-  snmp_iptooid(ip, &arpidx[1]);
-
-  for (tree = 0; tree < 2; tree++)
-  {
-    /* mark nodes for deletion */
-    if (tree == 0)
-    {
-      at_rn = &arptree_root;
-    }
-    else
-    {
-      at_rn = &ipntomtree_root;
-    }
-    level = 0;
-    del_cnt = 0;
-    while ((level < 5) && (at_rn != NULL))
-    {
-      fc = snmp_mib_node_find(at_rn, arpidx[level], &at_n);
-      if (fc == 0)
-      {
-        /* arpidx[level] does not exist */
-        del_cnt = 0;
-        at_rn = NULL;
-      }
-      else if (fc == 1)
-      {
-        del_rn[del_cnt] = at_rn;
-        del_n[del_cnt] = at_n;
-        del_cnt++;
-        at_rn = (struct mib_list_rootnode*)(at_n->nptr);
-      }
-      else if (fc == 2)
-      {
-        /* reset delete (2 or more childs) */
-        del_cnt = 0;
-        at_rn = (struct mib_list_rootnode*)(at_n->nptr);
-      }
-      level++;
-    }
-    /* delete marked index nodes */
-    while (del_cnt > 0)
-    {
-      del_cnt--;
-
-      at_rn = del_rn[del_cnt];
-      at_n = del_n[del_cnt];
-
-      next = snmp_mib_node_delete(at_rn, at_n);
-      if (next != NULL)
-      {
-        LWIP_ASSERT("next_count == 0",next->count == 0);
-        snmp_mib_lrn_free(next);
-      }
-    }
-  }
-  /* disable getnext traversal on empty tables */
-  if(arptree_root.count == 0) at.maxlength = 0;
-  if(ipntomtree_root.count == 0) ipntomtable.maxlength = 0;
-}
-
-void snmp_inc_ipinreceives(void)
-{
-  ipinreceives++;
-}
-
-void snmp_inc_ipinhdrerrors(void)
-{
-  ipinhdrerrors++;
-}
-
-void snmp_inc_ipinaddrerrors(void)
-{
-  ipinaddrerrors++;
-}
-
-void snmp_inc_ipforwdatagrams(void)
-{
-  ipforwdatagrams++;
-}
-
-void snmp_inc_ipinunknownprotos(void)
-{
-  ipinunknownprotos++;
-}
-
-void snmp_inc_ipindiscards(void)
-{
-  ipindiscards++;
-}
-
-void snmp_inc_ipindelivers(void)
-{
-  ipindelivers++;
-}
-
-void snmp_inc_ipoutrequests(void)
-{
-  ipoutrequests++;
-}
-
-void snmp_inc_ipoutdiscards(void)
-{
-  ipoutdiscards++;
-}
-
-void snmp_inc_ipoutnoroutes(void)
-{
-  ipoutnoroutes++;
-}
-
-void snmp_inc_ipreasmreqds(void)
-{
-  ipreasmreqds++;
-}
-
-void snmp_inc_ipreasmoks(void)
-{
-  ipreasmoks++;
-}
-
-void snmp_inc_ipreasmfails(void)
-{
-  ipreasmfails++;
-}
-
-void snmp_inc_ipfragoks(void)
-{
-  ipfragoks++;
-}
-
-void snmp_inc_ipfragfails(void)
-{
-  ipfragfails++;
-}
-
-void snmp_inc_ipfragcreates(void)
-{
-  ipfragcreates++;
-}
-
-void snmp_inc_iproutingdiscards(void)
-{
-  iproutingdiscards++;
-}
-
-/**
- * Inserts ipAddrTable indexes (.ipAdEntAddr)
- * into index tree.
- */
-void snmp_insert_ipaddridx_tree(struct netif *ni)
-{
-  struct mib_list_rootnode *ipa_rn;
-  struct mib_list_node *ipa_node;
-  s32_t ipaddridx[4];
-  u8_t level;
-
-  LWIP_ASSERT("ni != NULL", ni != NULL);
-  snmp_iptooid(&ni->ip_addr, &ipaddridx[0]);
-
-  level = 0;
-  ipa_rn = &ipaddrtree_root;
-  while (level < 4)
-  {
-    ipa_node = NULL;
-    snmp_mib_node_insert(ipa_rn, ipaddridx[level], &ipa_node);
-    if ((level != 3) && (ipa_node != NULL))
-    {
-      if (ipa_node->nptr == NULL)
-      {
-        ipa_rn = snmp_mib_lrn_alloc();
-        ipa_node->nptr = (struct mib_node*)ipa_rn;
-        if (ipa_rn != NULL)
-        {
-          if (level == 2)
-          {
-            ipa_rn->get_object_def = ip_addrentry_get_object_def;
-            ipa_rn->get_value = ip_addrentry_get_value;
-            ipa_rn->set_test = noleafs_set_test;
-            ipa_rn->set_value = noleafs_set_value;
-          }
-        }
-        else
-        {
-          /* ipa_rn == NULL, malloc failure */
-          LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_insert_ipaddridx_tree() insert failed, mem full"));
-          break;
-        }
-      }
-      else
-      {
-        ipa_rn = (struct mib_list_rootnode*)ipa_node->nptr;
-      }
-    }
-    level++;
-  }
-  /* enable getnext traversal on filled table */
-  ipaddrtable.maxlength = 1;
-}
-
-/**
- * Removes ipAddrTable indexes (.ipAdEntAddr)
- * from index tree.
- */
-void snmp_delete_ipaddridx_tree(struct netif *ni)
-{
-  struct mib_list_rootnode *ipa_rn, *next, *del_rn[4];
-  struct mib_list_node *ipa_n, *del_n[4];
-  s32_t ipaddridx[4];
-  u8_t fc, level, del_cnt;
-
-  LWIP_ASSERT("ni != NULL", ni != NULL);
-  snmp_iptooid(&ni->ip_addr, &ipaddridx[0]);
-
-  /* mark nodes for deletion */
-  level = 0;
-  del_cnt = 0;
-  ipa_rn = &ipaddrtree_root;
-  while ((level < 4) && (ipa_rn != NULL))
-  {
-    fc = snmp_mib_node_find(ipa_rn, ipaddridx[level], &ipa_n);
-    if (fc == 0)
-    {
-      /* ipaddridx[level] does not exist */
-      del_cnt = 0;
-      ipa_rn = NULL;
-    }
-    else if (fc == 1)
-    {
-      del_rn[del_cnt] = ipa_rn;
-      del_n[del_cnt] = ipa_n;
-      del_cnt++;
-      ipa_rn = (struct mib_list_rootnode*)(ipa_n->nptr);
-    }
-    else if (fc == 2)
-    {
-      /* reset delete (2 or more childs) */
-      del_cnt = 0;
-      ipa_rn = (struct mib_list_rootnode*)(ipa_n->nptr);
-    }
-    level++;
-  }
-  /* delete marked index nodes */
-  while (del_cnt > 0)
-  {
-    del_cnt--;
-
-    ipa_rn = del_rn[del_cnt];
-    ipa_n = del_n[del_cnt];
-
-    next = snmp_mib_node_delete(ipa_rn, ipa_n);
-    if (next != NULL)
-    {
-      LWIP_ASSERT("next_count == 0",next->count == 0);
-      snmp_mib_lrn_free(next);
-    }
-  }
-  /* disable getnext traversal on empty table */
-  if (ipaddrtree_root.count == 0) ipaddrtable.maxlength = 0;
-}
-
-/**
- * Inserts ipRouteTable indexes (.ipRouteDest)
- * into index tree.
- *
- * @param dflt non-zero for the default rte, zero for network rte
- * @param ni points to network interface for this rte
- *
- * @todo record sysuptime for _this_ route when it is installed
- *   (needed for ipRouteAge) in the netif.
- */
-void snmp_insert_iprteidx_tree(u8_t dflt, struct netif *ni)
-{
-  u8_t insert = 0;
-  ip_addr_t dst;
-
-  if (dflt != 0)
-  {
-    /* the default route 0.0.0.0 */
-    ip_addr_set_any(&dst);
-    insert = 1;
-  }
-  else
-  {
-    /* route to the network address */
-    ip_addr_get_network(&dst, &ni->ip_addr, &ni->netmask);
-    /* exclude 0.0.0.0 network (reserved for default rte) */
-    if (!ip_addr_isany(&dst)) {
-      insert = 1;
-    }
-  }
-  if (insert)
-  {
-    struct mib_list_rootnode *iprte_rn;
-    struct mib_list_node *iprte_node;
-    s32_t iprteidx[4];
-    u8_t level;
-
-    snmp_iptooid(&dst, &iprteidx[0]);
-    level = 0;
-    iprte_rn = &iprtetree_root;
-    while (level < 4)
-    {
-      iprte_node = NULL;
-      snmp_mib_node_insert(iprte_rn, iprteidx[level], &iprte_node);
-      if ((level != 3) && (iprte_node != NULL))
-      {
-        if (iprte_node->nptr == NULL)
-        {
-          iprte_rn = snmp_mib_lrn_alloc();
-          iprte_node->nptr = (struct mib_node*)iprte_rn;
-          if (iprte_rn != NULL)
-          {
-            if (level == 2)
-            {
-              iprte_rn->get_object_def = ip_rteentry_get_object_def;
-              iprte_rn->get_value = ip_rteentry_get_value;
-              iprte_rn->set_test = noleafs_set_test;
-              iprte_rn->set_value = noleafs_set_value;
-            }
-          }
-          else
-          {
-            /* iprte_rn == NULL, malloc failure */
-            LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_insert_iprteidx_tree() insert failed, mem full"));
-            break;
-          }
-        }
-        else
-        {
-          iprte_rn = (struct mib_list_rootnode*)iprte_node->nptr;
-        }
-      }
-      level++;
-    }
-  }
-  /* enable getnext traversal on filled table */
-  iprtetable.maxlength = 1;
-}
-
-/**
- * Removes ipRouteTable indexes (.ipRouteDest)
- * from index tree.
- *
- * @param dflt non-zero for the default rte, zero for network rte
- * @param ni points to network interface for this rte or NULL
- *   for default route to be removed.
- */
-void snmp_delete_iprteidx_tree(u8_t dflt, struct netif *ni)
-{
-  u8_t del = 0;
-  ip_addr_t dst;
-
-  if (dflt != 0)
-  {
-    /* the default route 0.0.0.0 */
-    ip_addr_set_any(&dst);
-    del = 1;
-  }
-  else
-  {
-    /* route to the network address */
-    ip_addr_get_network(&dst, &ni->ip_addr, &ni->netmask);
-    /* exclude 0.0.0.0 network (reserved for default rte) */
-    if (!ip_addr_isany(&dst)) {
-      del = 1;
-    }
-  }
-  if (del)
-  {
-    struct mib_list_rootnode *iprte_rn, *next, *del_rn[4];
-    struct mib_list_node *iprte_n, *del_n[4];
-    s32_t iprteidx[4];
-    u8_t fc, level, del_cnt;
-
-    snmp_iptooid(&dst, &iprteidx[0]);
-    /* mark nodes for deletion */
-    level = 0;
-    del_cnt = 0;
-    iprte_rn = &iprtetree_root;
-    while ((level < 4) && (iprte_rn != NULL))
-    {
-      fc = snmp_mib_node_find(iprte_rn, iprteidx[level], &iprte_n);
-      if (fc == 0)
-      {
-        /* iprteidx[level] does not exist */
-        del_cnt = 0;
-        iprte_rn = NULL;
-      }
-      else if (fc == 1)
-      {
-        del_rn[del_cnt] = iprte_rn;
-        del_n[del_cnt] = iprte_n;
-        del_cnt++;
-        iprte_rn = (struct mib_list_rootnode*)(iprte_n->nptr);
-      }
-      else if (fc == 2)
-      {
-        /* reset delete (2 or more childs) */
-        del_cnt = 0;
-        iprte_rn = (struct mib_list_rootnode*)(iprte_n->nptr);
-      }
-      level++;
-    }
-    /* delete marked index nodes */
-    while (del_cnt > 0)
-    {
-      del_cnt--;
-
-      iprte_rn = del_rn[del_cnt];
-      iprte_n = del_n[del_cnt];
-
-      next = snmp_mib_node_delete(iprte_rn, iprte_n);
-      if (next != NULL)
-      {
-        LWIP_ASSERT("next_count == 0",next->count == 0);
-        snmp_mib_lrn_free(next);
-      }
-    }
-  }
-  /* disable getnext traversal on empty table */
-  if (iprtetree_root.count == 0) iprtetable.maxlength = 0;
-}
-
-
-void snmp_inc_icmpinmsgs(void)
-{
-  icmpinmsgs++;
-}
-
-void snmp_inc_icmpinerrors(void)
-{
-  icmpinerrors++;
-}
-
-void snmp_inc_icmpindestunreachs(void)
-{
-  icmpindestunreachs++;
-}
-
-void snmp_inc_icmpintimeexcds(void)
-{
-  icmpintimeexcds++;
-}
-
-void snmp_inc_icmpinparmprobs(void)
-{
-  icmpinparmprobs++;
-}
-
-void snmp_inc_icmpinsrcquenchs(void)
-{
-  icmpinsrcquenchs++;
-}
-
-void snmp_inc_icmpinredirects(void)
-{
-  icmpinredirects++;
-}
-
-void snmp_inc_icmpinechos(void)
-{
-  icmpinechos++;
-}
-
-void snmp_inc_icmpinechoreps(void)
-{
-  icmpinechoreps++;
-}
-
-void snmp_inc_icmpintimestamps(void)
-{
-  icmpintimestamps++;
-}
-
-void snmp_inc_icmpintimestampreps(void)
-{
-  icmpintimestampreps++;
-}
-
-void snmp_inc_icmpinaddrmasks(void)
-{
-  icmpinaddrmasks++;
-}
-
-void snmp_inc_icmpinaddrmaskreps(void)
-{
-  icmpinaddrmaskreps++;
-}
-
-void snmp_inc_icmpoutmsgs(void)
-{
-  icmpoutmsgs++;
-}
-
-void snmp_inc_icmpouterrors(void)
-{
-  icmpouterrors++;
-}
-
-void snmp_inc_icmpoutdestunreachs(void)
-{
-  icmpoutdestunreachs++;
-}
-
-void snmp_inc_icmpouttimeexcds(void)
-{
-  icmpouttimeexcds++;
-}
-
-void snmp_inc_icmpoutparmprobs(void)
-{
-  icmpoutparmprobs++;
-}
-
-void snmp_inc_icmpoutsrcquenchs(void)
-{
-  icmpoutsrcquenchs++;
-}
-
-void snmp_inc_icmpoutredirects(void)
-{
-  icmpoutredirects++;
-}
-
-void snmp_inc_icmpoutechos(void)
-{
-  icmpoutechos++;
-}
-
-void snmp_inc_icmpoutechoreps(void)
-{
-  icmpoutechoreps++;
-}
-
-void snmp_inc_icmpouttimestamps(void)
-{
-  icmpouttimestamps++;
-}
-
-void snmp_inc_icmpouttimestampreps(void)
-{
-  icmpouttimestampreps++;
-}
-
-void snmp_inc_icmpoutaddrmasks(void)
-{
-  icmpoutaddrmasks++;
-}
-
-void snmp_inc_icmpoutaddrmaskreps(void)
-{
-  icmpoutaddrmaskreps++;
-}
-
-void snmp_inc_tcpactiveopens(void)
-{
-  tcpactiveopens++;
-}
-
-void snmp_inc_tcppassiveopens(void)
-{
-  tcppassiveopens++;
-}
-
-void snmp_inc_tcpattemptfails(void)
-{
-  tcpattemptfails++;
-}
-
-void snmp_inc_tcpestabresets(void)
-{
-  tcpestabresets++;
-}
-
-void snmp_inc_tcpinsegs(void)
-{
-  tcpinsegs++;
-}
-
-void snmp_inc_tcpoutsegs(void)
-{
-  tcpoutsegs++;
-}
-
-void snmp_inc_tcpretranssegs(void)
-{
-  tcpretranssegs++;
-}
-
-void snmp_inc_tcpinerrs(void)
-{
-  tcpinerrs++;
-}
-
-void snmp_inc_tcpoutrsts(void)
-{
-  tcpoutrsts++;
-}
-
-void snmp_inc_udpindatagrams(void)
-{
-  udpindatagrams++;
-}
-
-void snmp_inc_udpnoports(void)
-{
-  udpnoports++;
-}
-
-void snmp_inc_udpinerrors(void)
-{
-  udpinerrors++;
-}
-
-void snmp_inc_udpoutdatagrams(void)
-{
-  udpoutdatagrams++;
-}
-
-/**
- * Inserts udpTable indexes (.udpLocalAddress.udpLocalPort)
- * into index tree.
- */
-void snmp_insert_udpidx_tree(struct udp_pcb *pcb)
-{
-  struct mib_list_rootnode *udp_rn;
-  struct mib_list_node *udp_node;
-  s32_t udpidx[5];
-  u8_t level;
-
-  LWIP_ASSERT("pcb != NULL", pcb != NULL);
-  snmp_iptooid(&pcb->local_ip, &udpidx[0]);
-  udpidx[4] = pcb->local_port;
-
-  udp_rn = &udp_root;
-  for (level = 0; level < 5; level++)
-  {
-    udp_node = NULL;
-    snmp_mib_node_insert(udp_rn, udpidx[level], &udp_node);
-    if ((level != 4) && (udp_node != NULL))
-    {
-      if (udp_node->nptr == NULL)
-      {
-        udp_rn = snmp_mib_lrn_alloc();
-        udp_node->nptr = (struct mib_node*)udp_rn;
-        if (udp_rn != NULL)
-        {
-          if (level == 3)
-          {
-            udp_rn->get_object_def = udpentry_get_object_def;
-            udp_rn->get_value = udpentry_get_value;
-            udp_rn->set_test = noleafs_set_test;
-            udp_rn->set_value = noleafs_set_value;
-          }
-        }
-        else
-        {
-          /* udp_rn == NULL, malloc failure */
-          LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_insert_udpidx_tree() insert failed, mem full"));
-          break;
-        }
-      }
-      else
-      {
-        udp_rn = (struct mib_list_rootnode*)udp_node->nptr;
-      }
-    }
-  }
-  udptable.maxlength = 1;
-}
-
-/**
- * Removes udpTable indexes (.udpLocalAddress.udpLocalPort)
- * from index tree.
- */
-void snmp_delete_udpidx_tree(struct udp_pcb *pcb)
-{
-  struct udp_pcb *npcb;
-  struct mib_list_rootnode *udp_rn, *next, *del_rn[5];
-  struct mib_list_node *udp_n, *del_n[5];
-  s32_t udpidx[5];
-  u8_t bindings, fc, level, del_cnt;
-
-  LWIP_ASSERT("pcb != NULL", pcb != NULL);
-  snmp_iptooid(&pcb->local_ip, &udpidx[0]);
-  udpidx[4] = pcb->local_port;
-
-  /* count PCBs for a given binding
-     (e.g. when reusing ports or for temp output PCBs) */
-  bindings = 0;
-  npcb = udp_pcbs;
-  while ((npcb != NULL))
-  {
-    if (ip_addr_cmp(&npcb->local_ip, &pcb->local_ip) &&
-        (npcb->local_port == udpidx[4]))
-    {
-      bindings++;
-    }
-    npcb = npcb->next;
-  }
-  if (bindings == 1)
-  {
-    /* selectively remove */
-    /* mark nodes for deletion */
-    level = 0;
-    del_cnt = 0;
-    udp_rn = &udp_root;
-    while ((level < 5) && (udp_rn != NULL))
-    {
-      fc = snmp_mib_node_find(udp_rn, udpidx[level], &udp_n);
-      if (fc == 0)
-      {
-        /* udpidx[level] does not exist */
-        del_cnt = 0;
-        udp_rn = NULL;
-      }
-      else if (fc == 1)
-      {
-        del_rn[del_cnt] = udp_rn;
-        del_n[del_cnt] = udp_n;
-        del_cnt++;
-        udp_rn = (struct mib_list_rootnode*)(udp_n->nptr);
-      }
-      else if (fc == 2)
-      {
-        /* reset delete (2 or more childs) */
-        del_cnt = 0;
-        udp_rn = (struct mib_list_rootnode*)(udp_n->nptr);
-      }
-      level++;
-    }
-    /* delete marked index nodes */
-    while (del_cnt > 0)
-    {
-      del_cnt--;
-
-      udp_rn = del_rn[del_cnt];
-      udp_n = del_n[del_cnt];
-
-      next = snmp_mib_node_delete(udp_rn, udp_n);
-      if (next != NULL)
-      {
-        LWIP_ASSERT("next_count == 0",next->count == 0);
-        snmp_mib_lrn_free(next);
-      }
-    }
-  }
-  /* disable getnext traversal on empty table */
-  if (udp_root.count == 0) udptable.maxlength = 0;
-}
-
-
-void snmp_inc_snmpinpkts(void)
-{
-  snmpinpkts++;
-}
-
-void snmp_inc_snmpoutpkts(void)
-{
-  snmpoutpkts++;
-}
-
-void snmp_inc_snmpinbadversions(void)
-{
-  snmpinbadversions++;
-}
-
-void snmp_inc_snmpinbadcommunitynames(void)
-{
-  snmpinbadcommunitynames++;
-}
-
-void snmp_inc_snmpinbadcommunityuses(void)
-{
-  snmpinbadcommunityuses++;
-}
-
-void snmp_inc_snmpinasnparseerrs(void)
-{
-  snmpinasnparseerrs++;
-}
-
-void snmp_inc_snmpintoobigs(void)
-{
-  snmpintoobigs++;
-}
-
-void snmp_inc_snmpinnosuchnames(void)
-{
-  snmpinnosuchnames++;
-}
-
-void snmp_inc_snmpinbadvalues(void)
-{
-  snmpinbadvalues++;
-}
-
-void snmp_inc_snmpinreadonlys(void)
-{
-  snmpinreadonlys++;
-}
-
-void snmp_inc_snmpingenerrs(void)
-{
-  snmpingenerrs++;
-}
-
-void snmp_add_snmpintotalreqvars(u8_t value)
-{
-  snmpintotalreqvars += value;
-}
-
-void snmp_add_snmpintotalsetvars(u8_t value)
-{
-  snmpintotalsetvars += value;
-}
-
-void snmp_inc_snmpingetrequests(void)
-{
-  snmpingetrequests++;
-}
-
-void snmp_inc_snmpingetnexts(void)
-{
-  snmpingetnexts++;
-}
-
-void snmp_inc_snmpinsetrequests(void)
-{
-  snmpinsetrequests++;
-}
-
-void snmp_inc_snmpingetresponses(void)
-{
-  snmpingetresponses++;
-}
-
-void snmp_inc_snmpintraps(void)
-{
-  snmpintraps++;
-}
-
-void snmp_inc_snmpouttoobigs(void)
-{
-  snmpouttoobigs++;
-}
-
-void snmp_inc_snmpoutnosuchnames(void)
-{
-  snmpoutnosuchnames++;
-}
-
-void snmp_inc_snmpoutbadvalues(void)
-{
-  snmpoutbadvalues++;
-}
-
-void snmp_inc_snmpoutgenerrs(void)
-{
-  snmpoutgenerrs++;
-}
-
-void snmp_inc_snmpoutgetrequests(void)
-{
-  snmpoutgetrequests++;
-}
-
-void snmp_inc_snmpoutgetnexts(void)
-{
-  snmpoutgetnexts++;
-}
-
-void snmp_inc_snmpoutsetrequests(void)
-{
-  snmpoutsetrequests++;
-}
-
-void snmp_inc_snmpoutgetresponses(void)
-{
-  snmpoutgetresponses++;
-}
-
-void snmp_inc_snmpouttraps(void)
-{
-  snmpouttraps++;
-}
-
-void snmp_get_snmpgrpid_ptr(struct snmp_obj_id **oid)
-{
-  *oid = &snmpgrp_id;
-}
-
-void snmp_set_snmpenableauthentraps(u8_t *value)
-{
-  if (value != NULL)
-  {
-    snmpenableauthentraps_ptr = value;
-  }
-}
-
-void snmp_get_snmpenableauthentraps(u8_t *value)
-{
-  *value = *snmpenableauthentraps_ptr;
-}
-
-void
-noleafs_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
-{
-  LWIP_UNUSED_ARG(ident_len);
-  LWIP_UNUSED_ARG(ident);
-  od->instance = MIB_OBJECT_NONE;
-}
-
-void
-noleafs_get_value(struct obj_def *od, u16_t len, void *value)
-{
-  LWIP_UNUSED_ARG(od);
-  LWIP_UNUSED_ARG(len);
-  LWIP_UNUSED_ARG(value);
-}
-
-u8_t
-noleafs_set_test(struct obj_def *od, u16_t len, void *value)
-{
-  LWIP_UNUSED_ARG(od);
-  LWIP_UNUSED_ARG(len);
-  LWIP_UNUSED_ARG(value);
-  /* can't set */
-  return 0;
-}
-
-void
-noleafs_set_value(struct obj_def *od, u16_t len, void *value)
-{
-  LWIP_UNUSED_ARG(od);
-  LWIP_UNUSED_ARG(len);
-  LWIP_UNUSED_ARG(value);
-}
-
-
-/**
- * Returns systems object definitions.
- *
- * @param ident_len the address length (2)
- * @param ident points to objectname.0 (object id trailer)
- * @param od points to object definition.
- */
-static void
-system_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
-{
-  u8_t id;
-
-  /* return to object name, adding index depth (1) */
-  ident_len += 1;
-  ident -= 1;
-  if (ident_len == 2)
-  {
-    od->id_inst_len = ident_len;
-    od->id_inst_ptr = ident;
-
-    LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff));
-    id = (u8_t)ident[0];
-    LWIP_DEBUGF(SNMP_MIB_DEBUG,("get_object_def system.%"U16_F".0\n",(u16_t)id));
-    switch (id)
-    {
-      case 1: /* sysDescr */
-        od->instance = MIB_OBJECT_SCALAR;
-        od->access = MIB_OBJECT_READ_ONLY;
-        od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR);
-        od->v_len = *sysdescr_len_ptr;
-        break;
-      case 2: /* sysObjectID */
-        od->instance = MIB_OBJECT_SCALAR;
-        od->access = MIB_OBJECT_READ_ONLY;
-        od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID);
-        od->v_len = sysobjid.len * sizeof(s32_t);
-        break;
-      case 3: /* sysUpTime */
-        od->instance = MIB_OBJECT_SCALAR;
-        od->access = MIB_OBJECT_READ_ONLY;
-        od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS);
-        od->v_len = sizeof(u32_t);
-        break;
-      case 4: /* sysContact */
-        od->instance = MIB_OBJECT_SCALAR;
-        od->access = MIB_OBJECT_READ_WRITE;
-        od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR);
-        od->v_len = *syscontact_len_ptr;
-        break;
-      case 5: /* sysName */
-        od->instance = MIB_OBJECT_SCALAR;
-        od->access = MIB_OBJECT_READ_WRITE;
-        od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR);
-        od->v_len = *sysname_len_ptr;
-        break;
-      case 6: /* sysLocation */
-        od->instance = MIB_OBJECT_SCALAR;
-        od->access = MIB_OBJECT_READ_WRITE;
-        od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR);
-        od->v_len = *syslocation_len_ptr;
-        break;
-      case 7: /* sysServices */
-        od->instance = MIB_OBJECT_SCALAR;
-        od->access = MIB_OBJECT_READ_ONLY;
-        od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
-        od->v_len = sizeof(s32_t);
-        break;
-      default:
-        LWIP_DEBUGF(SNMP_MIB_DEBUG,("system_get_object_def: no such object\n"));
-        od->instance = MIB_OBJECT_NONE;
-        break;
-    };
-  }
-  else
-  {
-    LWIP_DEBUGF(SNMP_MIB_DEBUG,("system_get_object_def: no scalar\n"));
-    od->instance = MIB_OBJECT_NONE;
-  }
-}
-
-/**
- * Returns system object value.
- *
- * @param ident_len the address length (2)
- * @param ident points to objectname.0 (object id trailer)
- * @param len return value space (in bytes)
- * @param value points to (varbind) space to copy value into.
- */
-static void
-system_get_value(struct obj_def *od, u16_t len, void *value)
-{
-  u8_t id;
-
-  LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
-  id = (u8_t)od->id_inst_ptr[0];
-  switch (id)
-  {
-    case 1: /* sysDescr */
-      ocstrncpy((u8_t*)value, sysdescr_ptr, len);
-      break;
-    case 2: /* sysObjectID */
-      objectidncpy((s32_t*)value, (s32_t*)sysobjid.id, (u8_t)(len / sizeof(s32_t)));
-      break;
-    case 3: /* sysUpTime */
-      {
-        snmp_get_sysuptime((u32_t*)value);
-      }
-      break;
-    case 4: /* sysContact */
-      ocstrncpy((u8_t*)value, syscontact_ptr, len);
-      break;
-    case 5: /* sysName */
-      ocstrncpy((u8_t*)value, sysname_ptr, len);
-      break;
-    case 6: /* sysLocation */
-      ocstrncpy((u8_t*)value, syslocation_ptr, len);
-      break;
-    case 7: /* sysServices */
-      {
-        s32_t *sint_ptr = (s32_t*)value;
-        *sint_ptr = sysservices;
-      }
-      break;
-  };
-}
-
-static u8_t
-system_set_test(struct obj_def *od, u16_t len, void *value)
-{
-  u8_t id, set_ok;
-
-  LWIP_UNUSED_ARG(value);
-  set_ok = 0;
-  LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
-  id = (u8_t)od->id_inst_ptr[0];
-  switch (id)
-  {
-    case 4: /* sysContact */
-      if ((syscontact_ptr != syscontact_default) &&
-          (len <= 255))
-      {
-        set_ok = 1;
-      }
-      break;
-    case 5: /* sysName */
-      if ((sysname_ptr != sysname_default) &&
-          (len <= 255))
-      {
-        set_ok = 1;
-      }
-      break;
-    case 6: /* sysLocation */
-      if ((syslocation_ptr != syslocation_default) &&
-          (len <= 255))
-      {
-        set_ok = 1;
-      }
-      break;
-  };
-  return set_ok;
-}
-
-static void
-system_set_value(struct obj_def *od, u16_t len, void *value)
-{
-  u8_t id;
-
-  LWIP_ASSERT("invalid len", len <= 0xff);
-  LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
-  id = (u8_t)od->id_inst_ptr[0];
-  switch (id)
-  {
-    case 4: /* sysContact */
-      ocstrncpy(syscontact_ptr, (u8_t*)value, len);
-      *syscontact_len_ptr = (u8_t)len;
-      break;
-    case 5: /* sysName */
-      ocstrncpy(sysname_ptr, (u8_t*)value, len);
-      *sysname_len_ptr = (u8_t)len;
-      break;
-    case 6: /* sysLocation */
-      ocstrncpy(syslocation_ptr, (u8_t*)value, len);
-      *syslocation_len_ptr = (u8_t)len;
-      break;
-  };
-}
-
-/**
- * Returns interfaces.ifnumber object definition.
- *
- * @param ident_len the address length (2)
- * @param ident points to objectname.index
- * @param od points to object definition.
- */
-static void
-interfaces_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
-{
-  /* return to object name, adding index depth (1) */
-  ident_len += 1;
-  ident -= 1;
-  if (ident_len == 2)
-  {
-    od->id_inst_len = ident_len;
-    od->id_inst_ptr = ident;
-
-    od->instance = MIB_OBJECT_SCALAR;
-    od->access = MIB_OBJECT_READ_ONLY;
-    od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
-    od->v_len = sizeof(s32_t);
-  }
-  else
-  {
-    LWIP_DEBUGF(SNMP_MIB_DEBUG,("interfaces_get_object_def: no scalar\n"));
-    od->instance = MIB_OBJECT_NONE;
-  }
-}
-
-/**
- * Returns interfaces.ifnumber object value.
- *
- * @param ident_len the address length (2)
- * @param ident points to objectname.0 (object id trailer)
- * @param len return value space (in bytes)
- * @param value points to (varbind) space to copy value into.
- */
-static void
-interfaces_get_value(struct obj_def *od, u16_t len, void *value)
-{
-  LWIP_UNUSED_ARG(len);
-  if (od->id_inst_ptr[0] == 1)
-  {
-    s32_t *sint_ptr = (s32_t*)value;
-    *sint_ptr = iflist_root.count;
-  }
-}
-
-/**
- * Returns ifentry object definitions.
- *
- * @param ident_len the address length (2)
- * @param ident points to objectname.index
- * @param od points to object definition.
- */
-static void
-ifentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
-{
-  u8_t id;
-
-  /* return to object name, adding index depth (1) */
-  ident_len += 1;
-  ident -= 1;
-  if (ident_len == 2)
-  {
-    od->id_inst_len = ident_len;
-    od->id_inst_ptr = ident;
-
-    LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff));
-    id = (u8_t)ident[0];
-    LWIP_DEBUGF(SNMP_MIB_DEBUG,("get_object_def ifentry.%"U16_F"\n",(u16_t)id));
-    switch (id)
-    {
-      case 1: /* ifIndex */
-      case 3: /* ifType */
-      case 4: /* ifMtu */
-      case 8: /* ifOperStatus */
-        od->instance = MIB_OBJECT_TAB;
-        od->access = MIB_OBJECT_READ_ONLY;
-        od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
-        od->v_len = sizeof(s32_t);
-        break;
-      case 2: /* ifDescr */
-        od->instance = MIB_OBJECT_TAB;
-        od->access = MIB_OBJECT_READ_ONLY;
-        od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR);
-        /** @todo this should be some sort of sizeof(struct netif.name) */
-        od->v_len = 2;
-        break;
-      case 5: /* ifSpeed */
-      case 21: /* ifOutQLen */
-        od->instance = MIB_OBJECT_TAB;
-        od->access = MIB_OBJECT_READ_ONLY;
-        od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_GAUGE);
-        od->v_len = sizeof(u32_t);
-        break;
-      case 6: /* ifPhysAddress */
-        {
-          struct netif *netif;
-
-          snmp_ifindextonetif(ident[1], &netif);
-          od->instance = MIB_OBJECT_TAB;
-          od->access = MIB_OBJECT_READ_ONLY;
-          od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR);
-          od->v_len = netif->hwaddr_len;
-        }
-        break;
-      case 7: /* ifAdminStatus */
-        od->instance = MIB_OBJECT_TAB;
-        od->access = MIB_OBJECT_READ_WRITE;
-        od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
-        od->v_len = sizeof(s32_t);
-        break;
-      case 9: /* ifLastChange */
-        od->instance = MIB_OBJECT_TAB;
-        od->access = MIB_OBJECT_READ_ONLY;
-        od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS);
-        od->v_len = sizeof(u32_t);
-        break;
-      case 10: /* ifInOctets */
-      case 11: /* ifInUcastPkts */
-      case 12: /* ifInNUcastPkts */
-      case 13: /* ifInDiscarts */
-      case 14: /* ifInErrors */
-      case 15: /* ifInUnkownProtos */
-      case 16: /* ifOutOctets */
-      case 17: /* ifOutUcastPkts */
-      case 18: /* ifOutNUcastPkts */
-      case 19: /* ifOutDiscarts */
-      case 20: /* ifOutErrors */
-        od->instance = MIB_OBJECT_TAB;
-        od->access = MIB_OBJECT_READ_ONLY;
-        od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER);
-        od->v_len = sizeof(u32_t);
-        break;
-      case 22: /* ifSpecific */
-        /** @note returning zeroDotZero (0.0) no media specific MIB support */
-        od->instance = MIB_OBJECT_TAB;
-        od->access = MIB_OBJECT_READ_ONLY;
-        od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID);
-        od->v_len = ifspecific.len * sizeof(s32_t);
-        break;
-      default:
-        LWIP_DEBUGF(SNMP_MIB_DEBUG,("ifentry_get_object_def: no such object\n"));
-        od->instance = MIB_OBJECT_NONE;
-        break;
-    };
-  }
-  else
-  {
-    LWIP_DEBUGF(SNMP_MIB_DEBUG,("ifentry_get_object_def: no scalar\n"));
-    od->instance = MIB_OBJECT_NONE;
-  }
-}
-
-/**
- * Returns ifentry object value.
- *
- * @param ident_len the address length (2)
- * @param ident points to objectname.0 (object id trailer)
- * @param len return value space (in bytes)
- * @param value points to (varbind) space to copy value into.
- */
-static void
-ifentry_get_value(struct obj_def *od, u16_t len, void *value)
-{
-  struct netif *netif;
-  u8_t id;
-
-  snmp_ifindextonetif(od->id_inst_ptr[1], &netif);
-  LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
-  id = (u8_t)od->id_inst_ptr[0];
-  switch (id)
-  {
-    case 1: /* ifIndex */
-      {
-        s32_t *sint_ptr = (s32_t*)value;
-        *sint_ptr = od->id_inst_ptr[1];
-      }
-      break;
-    case 2: /* ifDescr */
-      ocstrncpy((u8_t*)value, (u8_t*)netif->name, len);
-      break;
-    case 3: /* ifType */
-      {
-        s32_t *sint_ptr = (s32_t*)value;
-        *sint_ptr = netif->link_type;
-      }
-      break;
-    case 4: /* ifMtu */
-      {
-        s32_t *sint_ptr = (s32_t*)value;
-        *sint_ptr = netif->mtu;
-      }
-      break;
-    case 5: /* ifSpeed */
-      {
-        u32_t *uint_ptr = (u32_t*)value;
-        *uint_ptr = netif->link_speed;
-      }
-      break;
-    case 6: /* ifPhysAddress */
-      ocstrncpy((u8_t*)value, netif->hwaddr, len);
-      break;
-    case 7: /* ifAdminStatus */
-      {
-        s32_t *sint_ptr = (s32_t*)value;
-        if (netif_is_up(netif))
-        {
-          if (netif_is_link_up(netif))
-          {
-            *sint_ptr = 1; /* up */
-          }
-          else
-          {
-            *sint_ptr = 7; /* lowerLayerDown */
-          }
-        }
-        else
-        {
-          *sint_ptr = 2; /* down */
-        }
-      }
-      break;
-    case 8: /* ifOperStatus */
-      {
-        s32_t *sint_ptr = (s32_t*)value;
-        if (netif_is_up(netif))
-        {
-          *sint_ptr = 1;
-        }
-        else
-        {
-          *sint_ptr = 2;
-        }
-      }
-      break;
-    case 9: /* ifLastChange */
-      {
-        u32_t *uint_ptr = (u32_t*)value;
-        *uint_ptr = netif->ts;
-      }
-      break;
-    case 10: /* ifInOctets */
-      {
-        u32_t *uint_ptr = (u32_t*)value;
-        *uint_ptr = netif->ifinoctets;
-      }
-      break;
-    case 11: /* ifInUcastPkts */
-      {
-        u32_t *uint_ptr = (u32_t*)value;
-        *uint_ptr = netif->ifinucastpkts;
-      }
-      break;
-    case 12: /* ifInNUcastPkts */
-      {
-        u32_t *uint_ptr = (u32_t*)value;
-        *uint_ptr = netif->ifinnucastpkts;
-      }
-      break;
-    case 13: /* ifInDiscarts */
-      {
-        u32_t *uint_ptr = (u32_t*)value;
-        *uint_ptr = netif->ifindiscards;
-      }
-      break;
-    case 14: /* ifInErrors */
-    case 15: /* ifInUnkownProtos */
-      /** @todo add these counters! */
-      {
-        u32_t *uint_ptr = (u32_t*)value;
-        *uint_ptr = 0;
-      }
-      break;
-    case 16: /* ifOutOctets */
-      {
-        u32_t *uint_ptr = (u32_t*)value;
-        *uint_ptr = netif->ifoutoctets;
-      }
-      break;
-    case 17: /* ifOutUcastPkts */
-      {
-        u32_t *uint_ptr = (u32_t*)value;
-        *uint_ptr = netif->ifoutucastpkts;
-      }
-      break;
-    case 18: /* ifOutNUcastPkts */
-      {
-        u32_t *uint_ptr = (u32_t*)value;
-        *uint_ptr = netif->ifoutnucastpkts;
-      }
-      break;
-    case 19: /* ifOutDiscarts */
-      {
-        u32_t *uint_ptr = (u32_t*)value;
-        *uint_ptr = netif->ifoutdiscards;
-      }
-      break;
-    case 20: /* ifOutErrors */
-       /** @todo add this counter! */
-      {
-        u32_t *uint_ptr = (u32_t*)value;
-        *uint_ptr = 0;
-      }
-      break;
-    case 21: /* ifOutQLen */
-      /** @todo figure out if this must be 0 (no queue) or 1? */
-      {
-        u32_t *uint_ptr = (u32_t*)value;
-        *uint_ptr = 0;
-      }
-      break;
-    case 22: /* ifSpecific */
-      objectidncpy((s32_t*)value, (s32_t*)ifspecific.id, (u8_t)(len / sizeof(s32_t)));
-      break;
-  };
-}
-
-#if !SNMP_SAFE_REQUESTS
-static u8_t
-ifentry_set_test(struct obj_def *od, u16_t len, void *value)
-{
-  struct netif *netif;
-  u8_t id, set_ok;
-  LWIP_UNUSED_ARG(len);
-
-  set_ok = 0;
-  snmp_ifindextonetif(od->id_inst_ptr[1], &netif);
-  id = (u8_t)od->id_inst_ptr[0];
-  switch (id)
-  {
-    case 7: /* ifAdminStatus */
-      {
-        s32_t *sint_ptr = (s32_t*)value;
-        if (*sint_ptr == 1 || *sint_ptr == 2)
-          set_ok = 1;
-      }
-      break;
-  }
-  return set_ok;
-}
-
-static void
-ifentry_set_value(struct obj_def *od, u16_t len, void *value)
-{
-  struct netif *netif;
-  u8_t id;
-  LWIP_UNUSED_ARG(len);
-
-  snmp_ifindextonetif(od->id_inst_ptr[1], &netif);
-  id = (u8_t)od->id_inst_ptr[0];
-  switch (id)
-  {
-    case 7: /* ifAdminStatus */
-      {
-        s32_t *sint_ptr = (s32_t*)value;
-        if (*sint_ptr == 1)
-        {
-          netif_set_up(netif);
-        }
-        else if (*sint_ptr == 2)
-        {
-          netif_set_down(netif);
-         }
-      }
-      break;
-  }
-}
-#endif /* SNMP_SAFE_REQUESTS */
-
-/**
- * Returns atentry object definitions.
- *
- * @param ident_len the address length (6)
- * @param ident points to objectname.atifindex.atnetaddress
- * @param od points to object definition.
- */
-static void
-atentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
-{
-  /* return to object name, adding index depth (5) */
-  ident_len += 5;
-  ident -= 5;
-
-  if (ident_len == 6)
-  {
-    od->id_inst_len = ident_len;
-    od->id_inst_ptr = ident;
-
-    switch (ident[0])
-    {
-      case 1: /* atIfIndex */
-        od->instance = MIB_OBJECT_TAB;
-        od->access = MIB_OBJECT_READ_WRITE;
-        od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
-        od->v_len = sizeof(s32_t);
-        break;
-      case 2: /* atPhysAddress */
-        od->instance = MIB_OBJECT_TAB;
-        od->access = MIB_OBJECT_READ_WRITE;
-        od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR);
-        od->v_len = 6; /** @todo try to use netif::hwaddr_len */
-        break;
-      case 3: /* atNetAddress */
-        od->instance = MIB_OBJECT_TAB;
-        od->access = MIB_OBJECT_READ_WRITE;
-        od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR);
-        od->v_len = 4;
-        break;
-      default:
-        LWIP_DEBUGF(SNMP_MIB_DEBUG,("atentry_get_object_def: no such object\n"));
-        od->instance = MIB_OBJECT_NONE;
-        break;
-    }
-  }
-  else
-  {
-    LWIP_DEBUGF(SNMP_MIB_DEBUG,("atentry_get_object_def: no scalar\n"));
-    od->instance = MIB_OBJECT_NONE;
-  }
-}
-
-static void
-atentry_get_value(struct obj_def *od, u16_t len, void *value)
-{
-#if LWIP_ARP
-  u8_t id;
-  struct eth_addr* ethaddr_ret;
-  ip_addr_t* ipaddr_ret;
-#endif /* LWIP_ARP */
-  ip_addr_t ip;
-  struct netif *netif;
-
-  LWIP_UNUSED_ARG(len);
-  LWIP_UNUSED_ARG(value);/* if !LWIP_ARP */
-
-  snmp_ifindextonetif(od->id_inst_ptr[1], &netif);
-  snmp_oidtoip(&od->id_inst_ptr[2], &ip);
-
-#if LWIP_ARP /** @todo implement a netif_find_addr */
-  if (etharp_find_addr(netif, &ip, &ethaddr_ret, &ipaddr_ret) > -1)
-  {
-    LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
-    id = (u8_t)od->id_inst_ptr[0];
-    switch (id)
-    {
-      case 1: /* atIfIndex */
-        {
-          s32_t *sint_ptr = (s32_t*)value;
-          *sint_ptr = od->id_inst_ptr[1];
-        }
-        break;
-      case 2: /* atPhysAddress */
-        {
-          struct eth_addr *dst = (struct eth_addr*)value;
-
-          *dst = *ethaddr_ret;
-        }
-        break;
-      case 3: /* atNetAddress */
-        {
-          ip_addr_t *dst = (ip_addr_t*)value;
-
-          *dst = *ipaddr_ret;
-        }
-        break;
-    }
-  }
-#endif /* LWIP_ARP */
-}
-
-static void
-ip_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
-{
-  u8_t id;
-
-  /* return to object name, adding index depth (1) */
-  ident_len += 1;
-  ident -= 1;
-  if (ident_len == 2)
-  {
-    od->id_inst_len = ident_len;
-    od->id_inst_ptr = ident;
-
-    LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff));
-    id = (u8_t)ident[0];
-    LWIP_DEBUGF(SNMP_MIB_DEBUG,("get_object_def ip.%"U16_F".0\n",(u16_t)id));
-    switch (id)
-    {
-      case 1: /* ipForwarding */
-      case 2: /* ipDefaultTTL */
-        od->instance = MIB_OBJECT_SCALAR;
-        od->access = MIB_OBJECT_READ_WRITE;
-        od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
-        od->v_len = sizeof(s32_t);
-        break;
-      case 3: /* ipInReceives */
-      case 4: /* ipInHdrErrors */
-      case 5: /* ipInAddrErrors */
-      case 6: /* ipForwDatagrams */
-      case 7: /* ipInUnknownProtos */
-      case 8: /* ipInDiscards */
-      case 9: /* ipInDelivers */
-      case 10: /* ipOutRequests */
-      case 11: /* ipOutDiscards */
-      case 12: /* ipOutNoRoutes */
-      case 14: /* ipReasmReqds */
-      case 15: /* ipReasmOKs */
-      case 16: /* ipReasmFails */
-      case 17: /* ipFragOKs */
-      case 18: /* ipFragFails */
-      case 19: /* ipFragCreates */
-      case 23: /* ipRoutingDiscards */
-        od->instance = MIB_OBJECT_SCALAR;
-        od->access = MIB_OBJECT_READ_ONLY;
-        od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER);
-        od->v_len = sizeof(u32_t);
-        break;
-      case 13: /* ipReasmTimeout */
-        od->instance = MIB_OBJECT_SCALAR;
-        od->access = MIB_OBJECT_READ_ONLY;
-        od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
-        od->v_len = sizeof(s32_t);
-        break;
-      default:
-        LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_get_object_def: no such object\n"));
-        od->instance = MIB_OBJECT_NONE;
-        break;
-    };
-  }
-  else
-  {
-    LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_get_object_def: no scalar\n"));
-    od->instance = MIB_OBJECT_NONE;
-  }
-}
-
-static void
-ip_get_value(struct obj_def *od, u16_t len, void *value)
-{
-  u8_t id;
-
-  LWIP_UNUSED_ARG(len);
-  LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
-  id = (u8_t)od->id_inst_ptr[0];
-  switch (id)
-  {
-    case 1: /* ipForwarding */
-      {
-        s32_t *sint_ptr = (s32_t*)value;
-#if IP_FORWARD
-        /* forwarding */
-        *sint_ptr = 1;
-#else
-        /* not-forwarding */
-        *sint_ptr = 2;
-#endif
-      }
-      break;
-    case 2: /* ipDefaultTTL */
-      {
-        s32_t *sint_ptr = (s32_t*)value;
-        *sint_ptr = IP_DEFAULT_TTL;
-      }
-      break;
-    case 3: /* ipInReceives */
-      {
-        u32_t *uint_ptr = (u32_t*)value;
-        *uint_ptr = ipinreceives;
-      }
-      break;
-    case 4: /* ipInHdrErrors */
-      {
-        u32_t *uint_ptr = (u32_t*)value;
-        *uint_ptr = ipinhdrerrors;
-      }
-      break;
-    case 5: /* ipInAddrErrors */
-      {
-        u32_t *uint_ptr = (u32_t*)value;
-        *uint_ptr = ipinaddrerrors;
-      }
-      break;
-    case 6: /* ipForwDatagrams */
-      {
-        u32_t *uint_ptr = (u32_t*)value;
-        *uint_ptr = ipforwdatagrams;
-      }
-      break;
-    case 7: /* ipInUnknownProtos */
-      {
-        u32_t *uint_ptr = (u32_t*)value;
-        *uint_ptr = ipinunknownprotos;
-      }
-      break;
-    case 8: /* ipInDiscards */
-      {
-        u32_t *uint_ptr = (u32_t*)value;
-        *uint_ptr = ipindiscards;
-      }
-      break;
-    case 9: /* ipInDelivers */
-      {
-        u32_t *uint_ptr = (u32_t*)value;
-        *uint_ptr = ipindelivers;
-      }
-      break;
-    case 10: /* ipOutRequests */
-      {
-        u32_t *uint_ptr = (u32_t*)value;
-        *uint_ptr = ipoutrequests;
-      }
-      break;
-    case 11: /* ipOutDiscards */
-      {
-        u32_t *uint_ptr = (u32_t*)value;
-        *uint_ptr = ipoutdiscards;
-      }
-      break;
-    case 12: /* ipOutNoRoutes */
-      {
-        u32_t *uint_ptr = (u32_t*)value;
-        *uint_ptr = ipoutnoroutes;
-      }
-      break;
-    case 13: /* ipReasmTimeout */
-      {
-        s32_t *sint_ptr = (s32_t*)value;
-#if IP_REASSEMBLY
-        *sint_ptr = IP_REASS_MAXAGE;
-#else
-        *sint_ptr = 0;
-#endif
-      }
-      break;
-    case 14: /* ipReasmReqds */
-      {
-        u32_t *uint_ptr = (u32_t*)value;
-        *uint_ptr = ipreasmreqds;
-      }
-      break;
-    case 15: /* ipReasmOKs */
-      {
-        u32_t *uint_ptr = (u32_t*)value;
-        *uint_ptr = ipreasmoks;
-      }
-      break;
-    case 16: /* ipReasmFails */
-      {
-        u32_t *uint_ptr = (u32_t*)value;
-        *uint_ptr = ipreasmfails;
-      }
-      break;
-    case 17: /* ipFragOKs */
-      {
-        u32_t *uint_ptr = (u32_t*)value;
-        *uint_ptr = ipfragoks;
-      }
-      break;
-    case 18: /* ipFragFails */
-      {
-        u32_t *uint_ptr = (u32_t*)value;
-        *uint_ptr = ipfragfails;
-      }
-      break;
-    case 19: /* ipFragCreates */
-      {
-        u32_t *uint_ptr = (u32_t*)value;
-        *uint_ptr = ipfragcreates;
-      }
-      break;
-    case 23: /* ipRoutingDiscards */
-      /** @todo can lwIP discard routes at all?? hardwire this to 0?? */
-      {
-        u32_t *uint_ptr = (u32_t*)value;
-        *uint_ptr = iproutingdiscards;
-      }
-      break;
-  };
-}
-
-/**
- * Test ip object value before setting.
- *
- * @param od is the object definition
- * @param len return value space (in bytes)
- * @param value points to (varbind) space to copy value from.
- *
- * @note we allow set if the value matches the hardwired value,
- *   otherwise return badvalue.
- */
-static u8_t
-ip_set_test(struct obj_def *od, u16_t len, void *value)
-{
-  u8_t id, set_ok;
-  s32_t *sint_ptr = (s32_t*)value;
-
-  LWIP_UNUSED_ARG(len);
-  set_ok = 0;
-  LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
-  id = (u8_t)od->id_inst_ptr[0];
-  switch (id)
-  {
-    case 1: /* ipForwarding */
-#if IP_FORWARD
-      /* forwarding */
-      if (*sint_ptr == 1)
-#else
-      /* not-forwarding */
-      if (*sint_ptr == 2)
-#endif
-      {
-        set_ok = 1;
-      }
-      break;
-    case 2: /* ipDefaultTTL */
-      if (*sint_ptr == IP_DEFAULT_TTL)
-      {
-        set_ok = 1;
-      }
-      break;
-  };
-  return set_ok;
-}
-
-static void
-ip_addrentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
-{
-  /* return to object name, adding index depth (4) */
-  ident_len += 4;
-  ident -= 4;
-
-  if (ident_len == 5)
-  {
-    u8_t id;
-
-    od->id_inst_len = ident_len;
-    od->id_inst_ptr = ident;
-
-    LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff));
-    id = (u8_t)ident[0];
-    switch (id)
-    {
-      case 1: /* ipAdEntAddr */
-      case 3: /* ipAdEntNetMask */
-        od->instance = MIB_OBJECT_TAB;
-        od->access = MIB_OBJECT_READ_ONLY;
-        od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR);
-        od->v_len = 4;
-        break;
-      case 2: /* ipAdEntIfIndex */
-      case 4: /* ipAdEntBcastAddr */
-      case 5: /* ipAdEntReasmMaxSize */
-        od->instance = MIB_OBJECT_TAB;
-        od->access = MIB_OBJECT_READ_ONLY;
-        od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
-        od->v_len = sizeof(s32_t);
-        break;
-      default:
-        LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_addrentry_get_object_def: no such object\n"));
-        od->instance = MIB_OBJECT_NONE;
-        break;
-    }
-  }
-  else
-  {
-    LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_addrentry_get_object_def: no scalar\n"));
-    od->instance = MIB_OBJECT_NONE;
-  }
-}
-
-static void
-ip_addrentry_get_value(struct obj_def *od, u16_t len, void *value)
-{
-  u8_t id;
-  u16_t ifidx;
-  ip_addr_t ip;
-  struct netif *netif = netif_list;
-
-  LWIP_UNUSED_ARG(len);
-  snmp_oidtoip(&od->id_inst_ptr[1], &ip);
-  ifidx = 0;
-  while ((netif != NULL) && !ip_addr_cmp(&ip, &netif->ip_addr))
-  {
-    netif = netif->next;
-    ifidx++;
-  }
-
-  if (netif != NULL)
-  {
-    LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
-    id = (u8_t)od->id_inst_ptr[0];
-    switch (id)
-    {
-      case 1: /* ipAdEntAddr */
-        {
-          ip_addr_t *dst = (ip_addr_t*)value;
-          *dst = netif->ip_addr;
-        }
-        break;
-      case 2: /* ipAdEntIfIndex */
-        {
-          s32_t *sint_ptr = (s32_t*)value;
-          *sint_ptr = ifidx + 1;
-        }
-        break;
-      case 3: /* ipAdEntNetMask */
-        {
-          ip_addr_t *dst = (ip_addr_t*)value;
-          *dst = netif->netmask;
-        }
-        break;
-      case 4: /* ipAdEntBcastAddr */
-        {
-          s32_t *sint_ptr = (s32_t*)value;
-
-          /* lwIP oddity, there's no broadcast
-            address in the netif we can rely on */
-          *sint_ptr = IPADDR_BROADCAST & 1;
-        }
-        break;
-      case 5: /* ipAdEntReasmMaxSize */
-        {
-          s32_t *sint_ptr = (s32_t*)value;
-#if IP_REASSEMBLY
-          /* @todo The theoretical maximum is IP_REASS_MAX_PBUFS * size of the pbufs,
-           * but only if receiving one fragmented packet at a time.
-           * The current solution is to calculate for 2 simultaneous packets...
-           */
-          *sint_ptr = (IP_HLEN + ((IP_REASS_MAX_PBUFS/2) *
-            (PBUF_POOL_BUFSIZE - PBUF_LINK_HLEN - IP_HLEN)));
-#else
-          /** @todo returning MTU would be a bad thing and
-             returning a wild guess like '576' isn't good either */
-          *sint_ptr = 0;
-#endif
-        }
-        break;
-    }
-  }
-}
-
-/**
- * @note
- * lwIP IP routing is currently using the network addresses in netif_list.
- * if no suitable network IP is found in netif_list, the default_netif is used.
- */
-static void
-ip_rteentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
-{
-  u8_t id;
-
-  /* return to object name, adding index depth (4) */
-  ident_len += 4;
-  ident -= 4;
-
-  if (ident_len == 5)
-  {
-    od->id_inst_len = ident_len;
-    od->id_inst_ptr = ident;
-
-    LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff));
-    id = (u8_t)ident[0];
-    switch (id)
-    {
-      case 1: /* ipRouteDest */
-      case 7: /* ipRouteNextHop */
-      case 11: /* ipRouteMask */
-        od->instance = MIB_OBJECT_TAB;
-        od->access = MIB_OBJECT_READ_WRITE;
-        od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR);
-        od->v_len = 4;
-        break;
-      case 2: /* ipRouteIfIndex */
-      case 3: /* ipRouteMetric1 */
-      case 4: /* ipRouteMetric2 */
-      case 5: /* ipRouteMetric3 */
-      case 6: /* ipRouteMetric4 */
-      case 8: /* ipRouteType */
-      case 10: /* ipRouteAge */
-      case 12: /* ipRouteMetric5 */
-        od->instance = MIB_OBJECT_TAB;
-        od->access = MIB_OBJECT_READ_WRITE;
-        od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
-        od->v_len = sizeof(s32_t);
-        break;
-      case 9: /* ipRouteProto */
-        od->instance = MIB_OBJECT_TAB;
-        od->access = MIB_OBJECT_READ_ONLY;
-        od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
-        od->v_len = sizeof(s32_t);
-        break;
-      case 13: /* ipRouteInfo */
-        /** @note returning zeroDotZero (0.0) no routing protocol specific MIB */
-        od->instance = MIB_OBJECT_TAB;
-        od->access = MIB_OBJECT_READ_ONLY;
-        od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID);
-        od->v_len = iprouteinfo.len * sizeof(s32_t);
-        break;
-      default:
-        LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_rteentry_get_object_def: no such object\n"));
-        od->instance = MIB_OBJECT_NONE;
-        break;
-    }
-  }
-  else
-  {
-    LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_rteentry_get_object_def: no scalar\n"));
-    od->instance = MIB_OBJECT_NONE;
-  }
-}
-
-static void
-ip_rteentry_get_value(struct obj_def *od, u16_t len, void *value)
-{
-  struct netif *netif;
-  ip_addr_t dest;
-  s32_t *ident;
-  u8_t id;
-
-  ident = od->id_inst_ptr;
-  snmp_oidtoip(&ident[1], &dest);
-
-  if (ip_addr_isany(&dest))
-  {
-    /* ip_route() uses default netif for default route */
-    netif = netif_default;
-  }
-  else
-  {
-    /* not using ip_route(), need exact match! */
-    netif = netif_list;
-    while ((netif != NULL) &&
-            !ip_addr_netcmp(&dest, &(netif->ip_addr), &(netif->netmask)) )
-    {
-      netif = netif->next;
-    }
-  }
-  if (netif != NULL)
-  {
-    LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff));
-    id = (u8_t)ident[0];
-    switch (id)
-    {
-      case 1: /* ipRouteDest */
-        {
-          ip_addr_t *dst = (ip_addr_t*)value;
-
-          if (ip_addr_isany(&dest))
-          {
-            /* default rte has 0.0.0.0 dest */
-            ip_addr_set_zero(dst);
-          }
-          else
-          {
-            /* netifs have netaddress dest */
-            ip_addr_get_network(dst, &netif->ip_addr, &netif->netmask);
-          }
-        }
-        break;
-      case 2: /* ipRouteIfIndex */
-        {
-          s32_t *sint_ptr = (s32_t*)value;
-
-          snmp_netiftoifindex(netif, sint_ptr);
-        }
-        break;
-      case 3: /* ipRouteMetric1 */
-        {
-          s32_t *sint_ptr = (s32_t*)value;
-
-          if (ip_addr_isany(&dest))
-          {
-            /* default rte has metric 1 */
-            *sint_ptr = 1;
-          }
-          else
-          {
-            /* other rtes have metric 0 */
-            *sint_ptr = 0;
-          }
-        }
-        break;
-      case 4: /* ipRouteMetric2 */
-      case 5: /* ipRouteMetric3 */
-      case 6: /* ipRouteMetric4 */
-      case 12: /* ipRouteMetric5 */
-        {
-          s32_t *sint_ptr = (s32_t*)value;
-          /* not used */
-          *sint_ptr = -1;
-        }
-        break;
-      case 7: /* ipRouteNextHop */
-        {
-          ip_addr_t *dst = (ip_addr_t*)value;
-
-          if (ip_addr_isany(&dest))
-          {
-            /* default rte: gateway */
-            *dst = netif->gw;
-          }
-          else
-          {
-            /* other rtes: netif ip_addr  */
-            *dst = netif->ip_addr;
-          }
-        }
-        break;
-      case 8: /* ipRouteType */
-        {
-          s32_t *sint_ptr = (s32_t*)value;
-
-          if (ip_addr_isany(&dest))
-          {
-            /* default rte is indirect */
-            *sint_ptr = 4;
-          }
-          else
-          {
-            /* other rtes are direct */
-            *sint_ptr = 3;
-          }
-        }
-        break;
-      case 9: /* ipRouteProto */
-        {
-          s32_t *sint_ptr = (s32_t*)value;
-          /* locally defined routes */
-          *sint_ptr = 2;
-        }
-        break;
-      case 10: /* ipRouteAge */
-        {
-          s32_t *sint_ptr = (s32_t*)value;
-          /** @todo (sysuptime - timestamp last change) / 100
-              @see snmp_insert_iprteidx_tree() */
-          *sint_ptr = 0;
-        }
-        break;
-      case 11: /* ipRouteMask */
-        {
-          ip_addr_t *dst = (ip_addr_t*)value;
-
-          if (ip_addr_isany(&dest))
-          {
-            /* default rte use 0.0.0.0 mask */
-            ip_addr_set_zero(dst);
-          }
-          else
-          {
-            /* other rtes use netmask */
-            *dst = netif->netmask;
-          }
-        }
-        break;
-      case 13: /* ipRouteInfo */
-        objectidncpy((s32_t*)value, (s32_t*)iprouteinfo.id, (u8_t)(len / sizeof(s32_t)));
-        break;
-    }
-  }
-}
-
-static void
-ip_ntomentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
-{
-  /* return to object name, adding index depth (5) */
-  ident_len += 5;
-  ident -= 5;
-
-  if (ident_len == 6)
-  {
-    u8_t id;
-
-    od->id_inst_len = ident_len;
-    od->id_inst_ptr = ident;
-
-    LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff));
-    id = (u8_t)ident[0];
-    switch (id)
-    {
-      case 1: /* ipNetToMediaIfIndex */
-      case 4: /* ipNetToMediaType */
-        od->instance = MIB_OBJECT_TAB;
-        od->access = MIB_OBJECT_READ_WRITE;
-        od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
-        od->v_len = sizeof(s32_t);
-        break;
-      case 2: /* ipNetToMediaPhysAddress */
-        od->instance = MIB_OBJECT_TAB;
-        od->access = MIB_OBJECT_READ_WRITE;
-        od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR);
-        od->v_len = 6; /** @todo try to use netif::hwaddr_len */
-        break;
-      case 3: /* ipNetToMediaNetAddress */
-        od->instance = MIB_OBJECT_TAB;
-        od->access = MIB_OBJECT_READ_WRITE;
-        od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR);
-        od->v_len = 4;
-        break;
-      default:
-        LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_ntomentry_get_object_def: no such object\n"));
-        od->instance = MIB_OBJECT_NONE;
-        break;
-    }
-  }
-  else
-  {
-    LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_ntomentry_get_object_def: no scalar\n"));
-    od->instance = MIB_OBJECT_NONE;
-  }
-}
-
-static void
-ip_ntomentry_get_value(struct obj_def *od, u16_t len, void *value)
-{
-#if LWIP_ARP
-  u8_t id;
-  struct eth_addr* ethaddr_ret;
-  ip_addr_t* ipaddr_ret;
-#endif /* LWIP_ARP */
-  ip_addr_t ip;
-  struct netif *netif;
-
-  LWIP_UNUSED_ARG(len);
-  LWIP_UNUSED_ARG(value);/* if !LWIP_ARP */
-
-  snmp_ifindextonetif(od->id_inst_ptr[1], &netif);
-  snmp_oidtoip(&od->id_inst_ptr[2], &ip);
-
-#if LWIP_ARP /** @todo implement a netif_find_addr */
-  if (etharp_find_addr(netif, &ip, &ethaddr_ret, &ipaddr_ret) > -1)
-  {
-    LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
-    id = (u8_t)od->id_inst_ptr[0];
-    switch (id)
-    {
-      case 1: /* ipNetToMediaIfIndex */
-        {
-          s32_t *sint_ptr = (s32_t*)value;
-          *sint_ptr = od->id_inst_ptr[1];
-        }
-        break;
-      case 2: /* ipNetToMediaPhysAddress */
-        {
-          struct eth_addr *dst = (struct eth_addr*)value;
-
-          *dst = *ethaddr_ret;
-        }
-        break;
-      case 3: /* ipNetToMediaNetAddress */
-        {
-          ip_addr_t *dst = (ip_addr_t*)value;
-
-          *dst = *ipaddr_ret;
-        }
-        break;
-      case 4: /* ipNetToMediaType */
-        {
-          s32_t *sint_ptr = (s32_t*)value;
-          /* dynamic (?) */
-          *sint_ptr = 3;
-        }
-        break;
-    }
-  }
-#endif /* LWIP_ARP */
-}
-
-static void
-icmp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
-{
-  /* return to object name, adding index depth (1) */
-  ident_len += 1;
-  ident -= 1;
-  if ((ident_len == 2) &&
-      (ident[0] > 0) && (ident[0] < 27))
-  {
-    od->id_inst_len = ident_len;
-    od->id_inst_ptr = ident;
-
-    od->instance = MIB_OBJECT_SCALAR;
-    od->access = MIB_OBJECT_READ_ONLY;
-    od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER);
-    od->v_len = sizeof(u32_t);
-  }
-  else
-  {
-    LWIP_DEBUGF(SNMP_MIB_DEBUG,("icmp_get_object_def: no scalar\n"));
-    od->instance = MIB_OBJECT_NONE;
-  }
-}
-
-static void
-icmp_get_value(struct obj_def *od, u16_t len, void *value)
-{
-  u32_t *uint_ptr = (u32_t*)value;
-  u8_t id;
-
-  LWIP_UNUSED_ARG(len);
-  LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
-  id = (u8_t)od->id_inst_ptr[0];
-  switch (id)
-  {
-    case 1: /* icmpInMsgs */
-      *uint_ptr = icmpinmsgs;
-      break;
-    case 2: /* icmpInErrors */
-      *uint_ptr = icmpinerrors;
-      break;
-    case 3: /* icmpInDestUnreachs */
-      *uint_ptr = icmpindestunreachs;
-      break;
-    case 4: /* icmpInTimeExcds */
-      *uint_ptr = icmpintimeexcds;
-      break;
-    case 5: /* icmpInParmProbs */
-      *uint_ptr = icmpinparmprobs;
-      break;
-    case 6: /* icmpInSrcQuenchs */
-      *uint_ptr = icmpinsrcquenchs;
-      break;
-    case 7: /* icmpInRedirects */
-      *uint_ptr = icmpinredirects;
-      break;
-    case 8: /* icmpInEchos */
-      *uint_ptr = icmpinechos;
-      break;
-    case 9: /* icmpInEchoReps */
-      *uint_ptr = icmpinechoreps;
-      break;
-    case 10: /* icmpInTimestamps */
-      *uint_ptr = icmpintimestamps;
-      break;
-    case 11: /* icmpInTimestampReps */
-      *uint_ptr = icmpintimestampreps;
-      break;
-    case 12: /* icmpInAddrMasks */
-      *uint_ptr = icmpinaddrmasks;
-      break;
-    case 13: /* icmpInAddrMaskReps */
-      *uint_ptr = icmpinaddrmaskreps;
-      break;
-    case 14: /* icmpOutMsgs */
-      *uint_ptr = icmpoutmsgs;
-      break;
-    case 15: /* icmpOutErrors */
-      *uint_ptr = icmpouterrors;
-      break;
-    case 16: /* icmpOutDestUnreachs */
-      *uint_ptr = icmpoutdestunreachs;
-      break;
-    case 17: /* icmpOutTimeExcds */
-      *uint_ptr = icmpouttimeexcds;
-      break;
-    case 18: /* icmpOutParmProbs */
-      *uint_ptr = icmpoutparmprobs;
-      break;
-    case 19: /* icmpOutSrcQuenchs */
-      *uint_ptr = icmpoutsrcquenchs;
-      break;
-    case 20: /* icmpOutRedirects */
-      *uint_ptr = icmpoutredirects;
-      break;
-    case 21: /* icmpOutEchos */
-      *uint_ptr = icmpoutechos;
-      break;
-    case 22: /* icmpOutEchoReps */
-      *uint_ptr = icmpoutechoreps;
-      break;
-    case 23: /* icmpOutTimestamps */
-      *uint_ptr = icmpouttimestamps;
-      break;
-    case 24: /* icmpOutTimestampReps */
-      *uint_ptr = icmpouttimestampreps;
-      break;
-    case 25: /* icmpOutAddrMasks */
-      *uint_ptr = icmpoutaddrmasks;
-      break;
-    case 26: /* icmpOutAddrMaskReps */
-      *uint_ptr = icmpoutaddrmaskreps;
-      break;
-  }
-}
-
-#if LWIP_TCP
-/** @todo tcp grp */
-static void
-tcp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
-{
-  u8_t id;
-
-  /* return to object name, adding index depth (1) */
-  ident_len += 1;
-  ident -= 1;
-  if (ident_len == 2)
-  {
-    od->id_inst_len = ident_len;
-    od->id_inst_ptr = ident;
-
-    LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff));
-    id = (u8_t)ident[0];
-    LWIP_DEBUGF(SNMP_MIB_DEBUG,("get_object_def tcp.%"U16_F".0\n",(u16_t)id));
-
-    switch (id)
-    {
-      case 1: /* tcpRtoAlgorithm */
-      case 2: /* tcpRtoMin */
-      case 3: /* tcpRtoMax */
-      case 4: /* tcpMaxConn */
-        od->instance = MIB_OBJECT_SCALAR;
-        od->access = MIB_OBJECT_READ_ONLY;
-        od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
-        od->v_len = sizeof(s32_t);
-        break;
-      case 5: /* tcpActiveOpens */
-      case 6: /* tcpPassiveOpens */
-      case 7: /* tcpAttemptFails */
-      case 8: /* tcpEstabResets */
-      case 10: /* tcpInSegs */
-      case 11: /* tcpOutSegs */
-      case 12: /* tcpRetransSegs */
-      case 14: /* tcpInErrs */
-      case 15: /* tcpOutRsts */
-        od->instance = MIB_OBJECT_SCALAR;
-        od->access = MIB_OBJECT_READ_ONLY;
-        od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER);
-        od->v_len = sizeof(u32_t);
-        break;
-      case 9: /* tcpCurrEstab */
-        od->instance = MIB_OBJECT_TAB;
-        od->access = MIB_OBJECT_READ_ONLY;
-        od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_GAUGE);
-        od->v_len = sizeof(u32_t);
-        break;
-      default:
-        LWIP_DEBUGF(SNMP_MIB_DEBUG,("tcp_get_object_def: no such object\n"));
-        od->instance = MIB_OBJECT_NONE;
-        break;
-    };
-  }
-  else
-  {
-    LWIP_DEBUGF(SNMP_MIB_DEBUG,("tcp_get_object_def: no scalar\n"));
-    od->instance = MIB_OBJECT_NONE;
-  }
-}
-
-static void
-tcp_get_value(struct obj_def *od, u16_t len, void *value)
-{
-  u32_t *uint_ptr = (u32_t*)value;
-  s32_t *sint_ptr = (s32_t*)value;
-  u8_t id;
-
-  LWIP_UNUSED_ARG(len);
-  LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
-  id = (u8_t)od->id_inst_ptr[0];
-  switch (id)
-  {
-    case 1: /* tcpRtoAlgorithm, vanj(4) */
-      *sint_ptr = 4;
-      break;
-    case 2: /* tcpRtoMin */
-      /* @todo not the actual value, a guess,
-          needs to be calculated */
-      *sint_ptr = 1000;
-      break;
-    case 3: /* tcpRtoMax */
-      /* @todo not the actual value, a guess,
-         needs to be calculated */
-      *sint_ptr = 60000;
-      break;
-    case 4: /* tcpMaxConn */
-      *sint_ptr = MEMP_NUM_TCP_PCB;
-      break;
-    case 5: /* tcpActiveOpens */
-      *uint_ptr = tcpactiveopens;
-      break;
-    case 6: /* tcpPassiveOpens */
-      *uint_ptr = tcppassiveopens;
-      break;
-    case 7: /* tcpAttemptFails */
-      *uint_ptr = tcpattemptfails;
-      break;
-    case 8: /* tcpEstabResets */
-      *uint_ptr = tcpestabresets;
-      break;
-    case 9: /* tcpCurrEstab */
-      {
-        u16_t tcpcurrestab = 0;
-        struct tcp_pcb *pcb = tcp_active_pcbs;
-        while (pcb != NULL)
-        {
-          if ((pcb->state == ESTABLISHED) ||
-              (pcb->state == CLOSE_WAIT))
-          {
-            tcpcurrestab++;
-          }
-          pcb = pcb->next;
-        }
-        *uint_ptr = tcpcurrestab;
-      }
-      break;
-    case 10: /* tcpInSegs */
-      *uint_ptr = tcpinsegs;
-      break;
-    case 11: /* tcpOutSegs */
-      *uint_ptr = tcpoutsegs;
-      break;
-    case 12: /* tcpRetransSegs */
-      *uint_ptr = tcpretranssegs;
-      break;
-    case 14: /* tcpInErrs */
-      *uint_ptr = tcpinerrs;
-      break;
-    case 15: /* tcpOutRsts */
-      *uint_ptr = tcpoutrsts;
-      break;
-  }
-}
-#ifdef THIS_SEEMS_UNUSED
-static void
-tcpconnentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
-{
-  /* return to object name, adding index depth (10) */
-  ident_len += 10;
-  ident -= 10;
-
-  if (ident_len == 11)
-  {
-    u8_t id;
-
-    od->id_inst_len = ident_len;
-    od->id_inst_ptr = ident;
-
-    id = ident[0];
-    LWIP_DEBUGF(SNMP_MIB_DEBUG,("get_object_def tcp.%"U16_F".0\n",(u16_t)id));
-
-    switch (id)
-    {
-      case 1: /* tcpConnState */
-        od->instance = MIB_OBJECT_TAB;
-        od->access = MIB_OBJECT_READ_WRITE;
-        od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
-        od->v_len = sizeof(s32_t);
-        break;
-      case 2: /* tcpConnLocalAddress */
-      case 4: /* tcpConnRemAddress */
-        od->instance = MIB_OBJECT_TAB;
-        od->access = MIB_OBJECT_READ_ONLY;
-        od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR);
-        od->v_len = 4;
-        break;
-      case 3: /* tcpConnLocalPort */
-      case 5: /* tcpConnRemPort */
-        od->instance = MIB_OBJECT_TAB;
-        od->access = MIB_OBJECT_READ_ONLY;
-        od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
-        od->v_len = sizeof(s32_t);
-        break;
-      default:
-        LWIP_DEBUGF(SNMP_MIB_DEBUG,("tcpconnentry_get_object_def: no such object\n"));
-        od->instance = MIB_OBJECT_NONE;
-        break;
-    };
-  }
-  else
-  {
-    LWIP_DEBUGF(SNMP_MIB_DEBUG,("tcpconnentry_get_object_def: no such object\n"));
-    od->instance = MIB_OBJECT_NONE;
-  }
-}
-
-static void
-tcpconnentry_get_value(struct obj_def *od, u16_t len, void *value)
-{
-  ip_addr_t lip, rip;
-  u16_t lport, rport;
-  s32_t *ident;
-
-  ident = od->id_inst_ptr;
-  snmp_oidtoip(&ident[1], &lip);
-  lport = ident[5];
-  snmp_oidtoip(&ident[6], &rip);
-  rport = ident[10];
-
-  /** @todo find matching PCB */
-}
-#endif /* if 0 */
-#endif
-
-static void
-udp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
-{
-  /* return to object name, adding index depth (1) */
-  ident_len += 1;
-  ident -= 1;
-  if ((ident_len == 2) &&
-      (ident[0] > 0) && (ident[0] < 6))
-  {
-    od->id_inst_len = ident_len;
-    od->id_inst_ptr = ident;
-
-    od->instance = MIB_OBJECT_SCALAR;
-    od->access = MIB_OBJECT_READ_ONLY;
-    od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER);
-    od->v_len = sizeof(u32_t);
-  }
-  else
-  {
-    LWIP_DEBUGF(SNMP_MIB_DEBUG,("udp_get_object_def: no scalar\n"));
-    od->instance = MIB_OBJECT_NONE;
-  }
-}
-
-static void
-udp_get_value(struct obj_def *od, u16_t len, void *value)
-{
-  u32_t *uint_ptr = (u32_t*)value;
-  u8_t id;
-
-  LWIP_UNUSED_ARG(len);
-  LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
-  id = (u8_t)od->id_inst_ptr[0];
-  switch (id)
-  {
-    case 1: /* udpInDatagrams */
-      *uint_ptr = udpindatagrams;
-      break;
-    case 2: /* udpNoPorts */
-      *uint_ptr = udpnoports;
-      break;
-    case 3: /* udpInErrors */
-      *uint_ptr = udpinerrors;
-      break;
-    case 4: /* udpOutDatagrams */
-      *uint_ptr = udpoutdatagrams;
-      break;
-  }
-}
-
-static void
-udpentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
-{
-  /* return to object name, adding index depth (5) */
-  ident_len += 5;
-  ident -= 5;
-
-  if (ident_len == 6)
-  {
-    od->id_inst_len = ident_len;
-    od->id_inst_ptr = ident;
-
-    switch (ident[0])
-    {
-      case 1: /* udpLocalAddress */
-        od->instance = MIB_OBJECT_TAB;
-        od->access = MIB_OBJECT_READ_ONLY;
-        od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR);
-        od->v_len = 4;
-        break;
-      case 2: /* udpLocalPort */
-        od->instance = MIB_OBJECT_TAB;
-        od->access = MIB_OBJECT_READ_ONLY;
-        od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
-        od->v_len = sizeof(s32_t);
-        break;
-      default:
-        LWIP_DEBUGF(SNMP_MIB_DEBUG,("udpentry_get_object_def: no such object\n"));
-        od->instance = MIB_OBJECT_NONE;
-        break;
-    }
-  }
-  else
-  {
-    LWIP_DEBUGF(SNMP_MIB_DEBUG,("udpentry_get_object_def: no scalar\n"));
-    od->instance = MIB_OBJECT_NONE;
-  }
-}
-
-static void
-udpentry_get_value(struct obj_def *od, u16_t len, void *value)
-{
-  u8_t id;
-  struct udp_pcb *pcb;
-  ip_addr_t ip;
-  u16_t port;
-
-  LWIP_UNUSED_ARG(len);
-  snmp_oidtoip(&od->id_inst_ptr[1], &ip);
-  LWIP_ASSERT("invalid port", (od->id_inst_ptr[5] >= 0) && (od->id_inst_ptr[5] <= 0xffff));
-  port = (u16_t)od->id_inst_ptr[5];
-
-  pcb = udp_pcbs;
-  while ((pcb != NULL) &&
-         !(ip_addr_cmp(&pcb->local_ip, &ip) &&
-           (pcb->local_port == port)))
-  {
-    pcb = pcb->next;
-  }
-
-  if (pcb != NULL)
-  {
-    LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
-    id = (u8_t)od->id_inst_ptr[0];
-    switch (id)
-    {
-      case 1: /* udpLocalAddress */
-        {
-          ip_addr_t *dst = (ip_addr_t*)value;
-          *dst = pcb->local_ip;
-        }
-        break;
-      case 2: /* udpLocalPort */
-        {
-          s32_t *sint_ptr = (s32_t*)value;
-          *sint_ptr = pcb->local_port;
-        }
-        break;
-    }
-  }
-}
-
-static void
-snmp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
-{
-  /* return to object name, adding index depth (1) */
-  ident_len += 1;
-  ident -= 1;
-  if (ident_len == 2)
-  {
-    u8_t id;
-
-    od->id_inst_len = ident_len;
-    od->id_inst_ptr = ident;
-
-    LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff));
-    id = (u8_t)ident[0];
-    switch (id)
-    {
-      case 1: /* snmpInPkts */
-      case 2: /* snmpOutPkts */
-      case 3: /* snmpInBadVersions */
-      case 4: /* snmpInBadCommunityNames */
-      case 5: /* snmpInBadCommunityUses */
-      case 6: /* snmpInASNParseErrs */
-      case 8: /* snmpInTooBigs */
-      case 9: /* snmpInNoSuchNames */
-      case 10: /* snmpInBadValues */
-      case 11: /* snmpInReadOnlys */
-      case 12: /* snmpInGenErrs */
-      case 13: /* snmpInTotalReqVars */
-      case 14: /* snmpInTotalSetVars */
-      case 15: /* snmpInGetRequests */
-      case 16: /* snmpInGetNexts */
-      case 17: /* snmpInSetRequests */
-      case 18: /* snmpInGetResponses */
-      case 19: /* snmpInTraps */
-      case 20: /* snmpOutTooBigs */
-      case 21: /* snmpOutNoSuchNames */
-      case 22: /* snmpOutBadValues */
-      case 24: /* snmpOutGenErrs */
-      case 25: /* snmpOutGetRequests */
-      case 26: /* snmpOutGetNexts */
-      case 27: /* snmpOutSetRequests */
-      case 28: /* snmpOutGetResponses */
-      case 29: /* snmpOutTraps */
-        od->instance = MIB_OBJECT_SCALAR;
-        od->access = MIB_OBJECT_READ_ONLY;
-        od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER);
-        od->v_len = sizeof(u32_t);
-        break;
-      case 30: /* snmpEnableAuthenTraps */
-        od->instance = MIB_OBJECT_SCALAR;
-        od->access = MIB_OBJECT_READ_WRITE;
-        od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
-        od->v_len = sizeof(s32_t);
-        break;
-      default:
-        LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_get_object_def: no such object\n"));
-        od->instance = MIB_OBJECT_NONE;
-        break;
-    };
-  }
-  else
-  {
-    LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_get_object_def: no scalar\n"));
-    od->instance = MIB_OBJECT_NONE;
-  }
-}
-
-static void
-snmp_get_value(struct obj_def *od, u16_t len, void *value)
-{
-  u32_t *uint_ptr = (u32_t*)value;
-  u8_t id;
-
-  LWIP_UNUSED_ARG(len);
-  LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
-  id = (u8_t)od->id_inst_ptr[0];
-  switch (id)
-  {
-      case 1: /* snmpInPkts */
-        *uint_ptr = snmpinpkts;
-        break;
-      case 2: /* snmpOutPkts */
-        *uint_ptr = snmpoutpkts;
-        break;
-      case 3: /* snmpInBadVersions */
-        *uint_ptr = snmpinbadversions;
-        break;
-      case 4: /* snmpInBadCommunityNames */
-        *uint_ptr = snmpinbadcommunitynames;
-        break;
-      case 5: /* snmpInBadCommunityUses */
-        *uint_ptr = snmpinbadcommunityuses;
-        break;
-      case 6: /* snmpInASNParseErrs */
-        *uint_ptr = snmpinasnparseerrs;
-        break;
-      case 8: /* snmpInTooBigs */
-        *uint_ptr = snmpintoobigs;
-        break;
-      case 9: /* snmpInNoSuchNames */
-        *uint_ptr = snmpinnosuchnames;
-        break;
-      case 10: /* snmpInBadValues */
-        *uint_ptr = snmpinbadvalues;
-        break;
-      case 11: /* snmpInReadOnlys */
-        *uint_ptr = snmpinreadonlys;
-        break;
-      case 12: /* snmpInGenErrs */
-        *uint_ptr = snmpingenerrs;
-        break;
-      case 13: /* snmpInTotalReqVars */
-        *uint_ptr = snmpintotalreqvars;
-        break;
-      case 14: /* snmpInTotalSetVars */
-        *uint_ptr = snmpintotalsetvars;
-        break;
-      case 15: /* snmpInGetRequests */
-        *uint_ptr = snmpingetrequests;
-        break;
-      case 16: /* snmpInGetNexts */
-        *uint_ptr = snmpingetnexts;
-        break;
-      case 17: /* snmpInSetRequests */
-        *uint_ptr = snmpinsetrequests;
-        break;
-      case 18: /* snmpInGetResponses */
-        *uint_ptr = snmpingetresponses;
-        break;
-      case 19: /* snmpInTraps */
-        *uint_ptr = snmpintraps;
-        break;
-      case 20: /* snmpOutTooBigs */
-        *uint_ptr = snmpouttoobigs;
-        break;
-      case 21: /* snmpOutNoSuchNames */
-        *uint_ptr = snmpoutnosuchnames;
-        break;
-      case 22: /* snmpOutBadValues */
-        *uint_ptr = snmpoutbadvalues;
-        break;
-      case 24: /* snmpOutGenErrs */
-        *uint_ptr = snmpoutgenerrs;
-        break;
-      case 25: /* snmpOutGetRequests */
-        *uint_ptr = snmpoutgetrequests;
-        break;
-      case 26: /* snmpOutGetNexts */
-        *uint_ptr = snmpoutgetnexts;
-        break;
-      case 27: /* snmpOutSetRequests */
-        *uint_ptr = snmpoutsetrequests;
-        break;
-      case 28: /* snmpOutGetResponses */
-        *uint_ptr = snmpoutgetresponses;
-        break;
-      case 29: /* snmpOutTraps */
-        *uint_ptr = snmpouttraps;
-        break;
-      case 30: /* snmpEnableAuthenTraps */
-        *uint_ptr = *snmpenableauthentraps_ptr;
-        break;
-  };
-}
-
-/**
- * Test snmp object value before setting.
- *
- * @param od is the object definition
- * @param len return value space (in bytes)
- * @param value points to (varbind) space to copy value from.
- */
-static u8_t
-snmp_set_test(struct obj_def *od, u16_t len, void *value)
-{
-  u8_t id, set_ok;
-
-  LWIP_UNUSED_ARG(len);
-  set_ok = 0;
-  LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
-  id = (u8_t)od->id_inst_ptr[0];
-  if (id == 30)
-  {
-    /* snmpEnableAuthenTraps */
-    s32_t *sint_ptr = (s32_t*)value;
-
-    if (snmpenableauthentraps_ptr != &snmpenableauthentraps_default)
-    {
-      /* we should have writable non-volatile mem here */
-      if ((*sint_ptr == 1) || (*sint_ptr == 2))
-      {
-        set_ok = 1;
-      }
-    }
-    else
-    {
-      /* const or hardwired value */
-      if (*sint_ptr == snmpenableauthentraps_default)
-      {
-        set_ok = 1;
-      }
-    }
-  }
-  return set_ok;
-}
-
-static void
-snmp_set_value(struct obj_def *od, u16_t len, void *value)
-{
-  u8_t id;
-
-  LWIP_UNUSED_ARG(len);
-  LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
-  id = (u8_t)od->id_inst_ptr[0];
-  if (id == 30)
-  {
-    /* snmpEnableAuthenTraps */
-    /* @todo @fixme: which kind of pointer is 'value'? s32_t or u8_t??? */
-    u8_t *ptr = (u8_t*)value;
-    *snmpenableauthentraps_ptr = *ptr;
-  }
-}
-
-#endif /* LWIP_SNMP */

+ 0 - 1174
thirdparty/lwip/src/core/snmp/mib_structs.c

@@ -1,1174 +0,0 @@
-/**
- * @file
- * MIB tree access/construction functions.
- */
-
-/*
- * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * Author: Christiaan Simons <christiaan.simons@axon.tv>
- */
-
-#include "lwip/opt.h"
-
-#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
-
-#include "lwip/snmp_structs.h"
-#include "lwip/memp.h"
-#include "lwip/netif.h"
-
-/** .iso.org.dod.internet address prefix, @see snmp_iso_*() */
-const s32_t prefix[4] = {1, 3, 6, 1};
-
-#define NODE_STACK_SIZE (LWIP_SNMP_OBJ_ID_LEN)
-/** node stack entry (old news?) */
-struct nse
-{
-  /** right child */
-  struct mib_node* r_ptr;
-  /** right child identifier */
-  s32_t r_id;
-  /** right child next level */
-  u8_t r_nl;
-};
-static u8_t node_stack_cnt;
-static struct nse node_stack[NODE_STACK_SIZE];
-
-/**
- * Pushes nse struct onto stack.
- */
-static void
-push_node(struct nse* node)
-{
-  LWIP_ASSERT("node_stack_cnt < NODE_STACK_SIZE",node_stack_cnt < NODE_STACK_SIZE);
-  LWIP_DEBUGF(SNMP_MIB_DEBUG,("push_node() node=%p id=%"S32_F"\n",(void*)(node->r_ptr),node->r_id));
-  if (node_stack_cnt < NODE_STACK_SIZE)
-  {
-    node_stack[node_stack_cnt] = *node;
-    node_stack_cnt++;
-  }
-}
-
-/**
- * Pops nse struct from stack.
- */
-static void
-pop_node(struct nse* node)
-{
-  if (node_stack_cnt > 0)
-  {
-    node_stack_cnt--;
-    *node = node_stack[node_stack_cnt];
-  }
-  LWIP_DEBUGF(SNMP_MIB_DEBUG,("pop_node() node=%p id=%"S32_F"\n",(void *)(node->r_ptr),node->r_id));
-}
-
-/**
- * Conversion from ifIndex to lwIP netif
- * @param ifindex is a s32_t object sub-identifier
- * @param netif points to returned netif struct pointer
- */
-void
-snmp_ifindextonetif(s32_t ifindex, struct netif **netif)
-{
-  struct netif *nif = netif_list;
-  s32_t i, ifidx;
-
-  ifidx = ifindex - 1;
-  i = 0;
-  while ((nif != NULL) && (i < ifidx))
-  {
-    nif = nif->next;
-    i++;
-  }
-  *netif = nif;
-}
-
-/**
- * Conversion from lwIP netif to ifIndex
- * @param netif points to a netif struct
- * @param ifidx points to s32_t object sub-identifier
- */
-void
-snmp_netiftoifindex(struct netif *netif, s32_t *ifidx)
-{
-  struct netif *nif = netif_list;
-  u16_t i;
-
-  i = 0;
-  while ((nif != NULL) && (nif != netif))
-  {
-    nif = nif->next;
-    i++;
-  }
-  *ifidx = i+1;
-}
-
-/**
- * Conversion from oid to lwIP ip_addr
- * @param ident points to s32_t ident[4] input
- * @param ip points to output struct
- */
-void
-snmp_oidtoip(s32_t *ident, ip_addr_t *ip)
-{
-  IP4_ADDR(ip, ident[0], ident[1], ident[2], ident[3]);
-}
-
-/**
- * Conversion from lwIP ip_addr to oid
- * @param ip points to input struct
- * @param ident points to s32_t ident[4] output
- */
-void
-snmp_iptooid(ip_addr_t *ip, s32_t *ident)
-{
-  ident[0] = ip4_addr1(ip);
-  ident[1] = ip4_addr2(ip);
-  ident[2] = ip4_addr3(ip);
-  ident[3] = ip4_addr4(ip);
-}
-
-struct mib_list_node *
-snmp_mib_ln_alloc(s32_t id)
-{
-  struct mib_list_node *ln;
-
-  ln = (struct mib_list_node *)memp_malloc(MEMP_SNMP_NODE);
-  if (ln != NULL)
-  {
-    ln->prev = NULL;
-    ln->next = NULL;
-    ln->objid = id;
-    ln->nptr = NULL;
-  }
-  return ln;
-}
-
-void
-snmp_mib_ln_free(struct mib_list_node *ln)
-{
-  memp_free(MEMP_SNMP_NODE, ln);
-}
-
-struct mib_list_rootnode *
-snmp_mib_lrn_alloc(void)
-{
-  struct mib_list_rootnode *lrn;
-
-  lrn = (struct mib_list_rootnode*)memp_malloc(MEMP_SNMP_ROOTNODE);
-  if (lrn != NULL)
-  {
-    lrn->get_object_def = noleafs_get_object_def;
-    lrn->get_value = noleafs_get_value;
-    lrn->set_test = noleafs_set_test;
-    lrn->set_value = noleafs_set_value;
-    lrn->node_type = MIB_NODE_LR;
-    lrn->maxlength = 0;
-    lrn->head = NULL;
-    lrn->tail = NULL;
-    lrn->count = 0;
-  }
-  return lrn;
-}
-
-void
-snmp_mib_lrn_free(struct mib_list_rootnode *lrn)
-{
-  memp_free(MEMP_SNMP_ROOTNODE, lrn);
-}
-
-/**
- * Inserts node in idx list in a sorted
- * (ascending order) fashion and
- * allocates the node if needed.
- *
- * @param rn points to the root node
- * @param objid is the object sub identifier
- * @param insn points to a pointer to the inserted node
- *   used for constructing the tree.
- * @return -1 if failed, 1 if inserted, 2 if present.
- */
-s8_t
-snmp_mib_node_insert(struct mib_list_rootnode *rn, s32_t objid, struct mib_list_node **insn)
-{
-  struct mib_list_node *nn;
-  s8_t insert;
-
-  LWIP_ASSERT("rn != NULL",rn != NULL);
-
-  /* -1 = malloc failure, 0 = not inserted, 1 = inserted, 2 = was present */
-  insert = 0;
-  if (rn->head == NULL)
-  {
-    /* empty list, add first node */
-    LWIP_DEBUGF(SNMP_MIB_DEBUG,("alloc empty list objid==%"S32_F"\n",objid));
-    nn = snmp_mib_ln_alloc(objid);
-    if (nn != NULL)
-    {
-      rn->head = nn;
-      rn->tail = nn;
-      *insn = nn;
-      insert = 1;
-    }
-    else
-    {
-      insert = -1;
-    }
-  }
-  else
-  {
-    struct mib_list_node *n;
-    /* at least one node is present */
-    n = rn->head;
-    while ((n != NULL) && (insert == 0))
-    {
-      if (n->objid == objid)
-      {
-        /* node is already there */
-        LWIP_DEBUGF(SNMP_MIB_DEBUG,("node already there objid==%"S32_F"\n",objid));
-        *insn = n;
-        insert = 2;
-      }
-      else if (n->objid < objid)
-      {
-        if (n->next == NULL)
-        {
-          /* alloc and insert at the tail */
-          LWIP_DEBUGF(SNMP_MIB_DEBUG,("alloc ins tail objid==%"S32_F"\n",objid));
-          nn = snmp_mib_ln_alloc(objid);
-          if (nn != NULL)
-          {
-            nn->next = NULL;
-            nn->prev = n;
-            n->next = nn;
-            rn->tail = nn;
-            *insn = nn;
-            insert = 1;
-          }
-          else
-          {
-            /* insertion failure */
-            insert = -1;
-          }
-        }
-        else
-        {
-          /* there's more to explore: traverse list */
-          LWIP_DEBUGF(SNMP_MIB_DEBUG,("traverse list\n"));
-          n = n->next;
-        }
-      }
-      else
-      {
-        /* n->objid > objid */
-        /* alloc and insert between n->prev and n */
-        LWIP_DEBUGF(SNMP_MIB_DEBUG,("alloc ins n->prev, objid==%"S32_F", n\n",objid));
-        nn = snmp_mib_ln_alloc(objid);
-        if (nn != NULL)
-        {
-          if (n->prev == NULL)
-          {
-            /* insert at the head */
-            nn->next = n;
-            nn->prev = NULL;
-            rn->head = nn;
-            n->prev = nn;
-          }
-          else
-          {
-            /* insert in the middle */
-            nn->next = n;
-            nn->prev = n->prev;
-            n->prev->next = nn;
-            n->prev = nn;
-          }
-          *insn = nn;
-          insert = 1;
-        }
-        else
-        {
-          /* insertion failure */
-          insert = -1;
-        }
-      }
-    }
-  }
-  if (insert == 1)
-  {
-    rn->count += 1;
-  }
-  LWIP_ASSERT("insert != 0",insert != 0);
-  return insert;
-}
-
-/**
- * Finds node in idx list and returns deletion mark.
- *
- * @param rn points to the root node
- * @param objid  is the object sub identifier
- * @param fn returns pointer to found node
- * @return 0 if not found, 1 if deletable,
- *   2 can't delete (2 or more children), 3 not a list_node
- */
-s8_t
-snmp_mib_node_find(struct mib_list_rootnode *rn, s32_t objid, struct mib_list_node **fn)
-{
-  s8_t fc;
-  struct mib_list_node *n;
-
-  LWIP_ASSERT("rn != NULL",rn != NULL);
-  n = rn->head;
-  while ((n != NULL) && (n->objid != objid))
-  {
-    n = n->next;
-  }
-  if (n == NULL)
-  {
-    fc = 0;
-  }
-  else if (n->nptr == NULL)
-  {
-    /* leaf, can delete node */
-    fc = 1;
-  }
-  else
-  {
-    struct mib_list_rootnode *r;
-
-    if (n->nptr->node_type == MIB_NODE_LR)
-    {
-      r = (struct mib_list_rootnode *)n->nptr;
-      if (r->count > 1)
-      {
-        /* can't delete node */
-        fc = 2;
-      }
-      else
-      {
-        /* count <= 1, can delete node */
-        fc = 1;
-      }
-    }
-    else
-    {
-      /* other node type */
-      fc = 3;
-    }
-  }
-  *fn = n;
-  return fc;
-}
-
-/**
- * Removes node from idx list
- * if it has a single child left.
- *
- * @param rn points to the root node
- * @param n points to the node to delete
- * @return the nptr to be freed by caller
- */
-struct mib_list_rootnode *
-snmp_mib_node_delete(struct mib_list_rootnode *rn, struct mib_list_node *n)
-{
-  struct mib_list_rootnode *next;
-
-  LWIP_ASSERT("rn != NULL",rn != NULL);
-  LWIP_ASSERT("n != NULL",n != NULL);
-
-  /* caller must remove this sub-tree */
-  next = (struct mib_list_rootnode*)(n->nptr);
-  rn->count -= 1;
-
-  if (n == rn->head)
-  {
-    rn->head = n->next;
-    if (n->next != NULL)
-    {
-      /* not last node, new list begin */
-      n->next->prev = NULL;
-    }
-  }
-  else if (n == rn->tail)
-  {
-    rn->tail = n->prev;
-    if (n->prev != NULL)
-    {
-      /* not last node, new list end */
-      n->prev->next = NULL;
-    }
-  }
-  else
-  {
-    /* node must be in the middle */
-    n->prev->next = n->next;
-    n->next->prev = n->prev;
-  }
-  LWIP_DEBUGF(SNMP_MIB_DEBUG,("free list objid==%"S32_F"\n",n->objid));
-  snmp_mib_ln_free(n);
-  if (rn->count == 0)
-  {
-    rn->head = NULL;
-    rn->tail = NULL;
-  }
-  return next;
-}
-
-
-
-/**
- * Searches tree for the supplied (scalar?) object identifier.
- *
- * @param node points to the root of the tree ('.internet')
- * @param ident_len the length of the supplied object identifier
- * @param ident points to the array of sub identifiers
- * @param np points to the found object instance (return)
- * @return pointer to the requested parent (!) node if success, NULL otherwise
- */
-struct mib_node *
-snmp_search_tree(struct mib_node *node, u8_t ident_len, s32_t *ident, struct snmp_name_ptr *np)
-{
-  u8_t node_type, ext_level;
-
-  ext_level = 0;
-  LWIP_DEBUGF(SNMP_MIB_DEBUG,("node==%p *ident==%"S32_F"\n",(void*)node,*ident));
-  while (node != NULL)
-  {
-    node_type = node->node_type;
-    if ((node_type == MIB_NODE_AR) || (node_type == MIB_NODE_RA))
-    {
-      struct mib_array_node *an;
-      u16_t i;
-
-      if (ident_len > 0)
-      {
-        /* array node (internal ROM or RAM, fixed length) */
-        an = (struct mib_array_node *)node;
-        i = 0;
-        while ((i < an->maxlength) && (an->objid[i] != *ident))
-        {
-          i++;
-        }
-        if (i < an->maxlength)
-        {
-          /* found it, if available proceed to child, otherwise inspect leaf */
-          LWIP_DEBUGF(SNMP_MIB_DEBUG,("an->objid[%"U16_F"]==%"S32_F" *ident==%"S32_F"\n",i,an->objid[i],*ident));
-          if (an->nptr[i] == NULL)
-          {
-            /* a scalar leaf OR table,
-               inspect remaining instance number / table index */
-            np->ident_len = ident_len;
-            np->ident = ident;
-            return (struct mib_node*)an;
-          }
-          else
-          {
-            /* follow next child pointer */
-            ident++;
-            ident_len--;
-            node = an->nptr[i];
-          }
-        }
-        else
-        {
-          /* search failed, identifier mismatch (nosuchname) */
-          LWIP_DEBUGF(SNMP_MIB_DEBUG,("an search failed *ident==%"S32_F"\n",*ident));
-          return NULL;
-        }
-      }
-      else
-      {
-        /* search failed, short object identifier (nosuchname) */
-        LWIP_DEBUGF(SNMP_MIB_DEBUG,("an search failed, short object identifier\n"));
-        return NULL;
-      }
-    }
-    else if(node_type == MIB_NODE_LR)
-    {
-      struct mib_list_rootnode *lrn;
-      struct mib_list_node *ln;
-
-      if (ident_len > 0)
-      {
-        /* list root node (internal 'RAM', variable length) */
-        lrn = (struct mib_list_rootnode *)node;
-        ln = lrn->head;
-        /* iterate over list, head to tail */
-        while ((ln != NULL) && (ln->objid != *ident))
-        {
-          ln = ln->next;
-        }
-        if (ln != NULL)
-        {
-          /* found it, proceed to child */;
-          LWIP_DEBUGF(SNMP_MIB_DEBUG,("ln->objid==%"S32_F" *ident==%"S32_F"\n",ln->objid,*ident));
-          if (ln->nptr == NULL)
-          {
-            np->ident_len = ident_len;
-            np->ident = ident;
-            return (struct mib_node*)lrn;
-          }
-          else
-          {
-            /* follow next child pointer */
-            ident_len--;
-            ident++;
-            node = ln->nptr;
-          }
-        }
-        else
-        {
-          /* search failed */
-          LWIP_DEBUGF(SNMP_MIB_DEBUG,("ln search failed *ident==%"S32_F"\n",*ident));
-          return NULL;
-        }
-      }
-      else
-      {
-        /* search failed, short object identifier (nosuchname) */
-        LWIP_DEBUGF(SNMP_MIB_DEBUG,("ln search failed, short object identifier\n"));
-        return NULL;
-      }
-    }
-    else if(node_type == MIB_NODE_EX)
-    {
-      struct mib_external_node *en;
-      u16_t i, len;
-
-      if (ident_len > 0)
-      {
-        /* external node (addressing and access via functions) */
-        en = (struct mib_external_node *)node;
-
-        i = 0;
-        len = en->level_length(en->addr_inf,ext_level);
-        while ((i < len) && (en->ident_cmp(en->addr_inf,ext_level,i,*ident) != 0))
-        {
-          i++;
-        }
-        if (i < len)
-        {
-          s32_t debug_id;
-
-          en->get_objid(en->addr_inf,ext_level,i,&debug_id);
-          LWIP_DEBUGF(SNMP_MIB_DEBUG,("en->objid==%"S32_F" *ident==%"S32_F"\n",debug_id,*ident));
-          if ((ext_level + 1) == en->tree_levels)
-          {
-            np->ident_len = ident_len;
-            np->ident = ident;
-            return (struct mib_node*)en;
-          }
-          else
-          {
-            /* found it, proceed to child */
-            ident_len--;
-            ident++;
-            ext_level++;
-          }
-        }
-        else
-        {
-          /* search failed */
-          LWIP_DEBUGF(SNMP_MIB_DEBUG,("en search failed *ident==%"S32_F"\n",*ident));
-          return NULL;
-        }
-      }
-      else
-      {
-        /* search failed, short object identifier (nosuchname) */
-        LWIP_DEBUGF(SNMP_MIB_DEBUG,("en search failed, short object identifier\n"));
-        return NULL;
-      }
-    }
-    else if (node_type == MIB_NODE_SC)
-    {
-      mib_scalar_node *sn;
-
-      sn = (mib_scalar_node *)node;
-      if ((ident_len == 1) && (*ident == 0))
-      {
-        np->ident_len = ident_len;
-        np->ident = ident;
-        return (struct mib_node*)sn;
-      }
-      else
-      {
-        /* search failed, short object identifier (nosuchname) */
-        LWIP_DEBUGF(SNMP_MIB_DEBUG,("search failed, invalid object identifier length\n"));
-        return NULL;
-      }
-    }
-    else
-    {
-      /* unknown node_type */
-      LWIP_DEBUGF(SNMP_MIB_DEBUG,("search failed node_type %"U16_F" unkown\n",(u16_t)node_type));
-      return NULL;
-    }
-  }
-  /* done, found nothing */
-  LWIP_DEBUGF(SNMP_MIB_DEBUG,("search failed node==%p\n",(void*)node));
-  return NULL;
-}
-
-/**
- * Test table for presence of at least one table entry.
- */
-static u8_t
-empty_table(struct mib_node *node)
-{
-  u8_t node_type;
-  u8_t empty = 0;
-
-  if (node != NULL)
-  {
-    node_type = node->node_type;
-    if (node_type == MIB_NODE_LR)
-    {
-      struct mib_list_rootnode *lrn;
-      lrn = (struct mib_list_rootnode *)node;
-      if ((lrn->count == 0) || (lrn->head == NULL))
-      {
-        empty = 1;
-      }
-    }
-    else if ((node_type == MIB_NODE_AR) || (node_type == MIB_NODE_RA))
-    {
-      struct mib_array_node *an;
-      an = (struct mib_array_node *)node;
-      if ((an->maxlength == 0) || (an->nptr == NULL))
-      {
-        empty = 1;
-      }
-    }
-    else if (node_type == MIB_NODE_EX)
-    {
-      struct mib_external_node *en;
-      en = (struct mib_external_node *)node;
-      if (en->tree_levels == 0)
-      {
-        empty = 1;
-      }
-    }
-  }
-  return empty;
-}
-
-/**
- * Tree expansion.
- */
-struct mib_node *
-snmp_expand_tree(struct mib_node *node, u8_t ident_len, s32_t *ident, struct snmp_obj_id *oidret)
-{
-  u8_t node_type, ext_level, climb_tree;
-
-  ext_level = 0;
-  /* reset node stack */
-  node_stack_cnt = 0;
-  while (node != NULL)
-  {
-    climb_tree = 0;
-    node_type = node->node_type;
-    if ((node_type == MIB_NODE_AR) || (node_type == MIB_NODE_RA))
-    {
-      struct mib_array_node *an;
-      u16_t i;
-
-      /* array node (internal ROM or RAM, fixed length) */
-      an = (struct mib_array_node *)node;
-      if (ident_len > 0)
-      {
-        i = 0;
-        while ((i < an->maxlength) && (an->objid[i] < *ident))
-        {
-          i++;
-        }
-        if (i < an->maxlength)
-        {
-          LWIP_DEBUGF(SNMP_MIB_DEBUG,("an->objid[%"U16_F"]==%"S32_F" *ident==%"S32_F"\n",i,an->objid[i],*ident));
-          /* add identifier to oidret */
-          oidret->id[oidret->len] = an->objid[i];
-          (oidret->len)++;
-
-          if (an->nptr[i] == NULL)
-          {
-            LWIP_DEBUGF(SNMP_MIB_DEBUG,("leaf node\n"));
-            /* leaf node (e.g. in a fixed size table) */
-            if (an->objid[i] > *ident)
-            {
-              return (struct mib_node*)an;
-            }
-            else if ((i + 1) < an->maxlength)
-            {
-              /* an->objid[i] == *ident */
-              (oidret->len)--;
-              oidret->id[oidret->len] = an->objid[i + 1];
-              (oidret->len)++;
-              return (struct mib_node*)an;
-            }
-            else
-            {
-              /* (i + 1) == an->maxlength */
-              (oidret->len)--;
-              climb_tree = 1;
-            }
-          }
-          else
-          {
-            u8_t j;
-            struct nse cur_node;
-
-            LWIP_DEBUGF(SNMP_MIB_DEBUG,("non-leaf node\n"));
-            /* non-leaf, store right child ptr and id */
-            LWIP_ASSERT("i < 0xff", i < 0xff);
-            j = (u8_t)i + 1;
-            while ((j < an->maxlength) && (empty_table(an->nptr[j])))
-            {
-              j++;
-            }
-            if (j < an->maxlength)
-            {
-              cur_node.r_ptr = an->nptr[j];
-              cur_node.r_id = an->objid[j];
-              cur_node.r_nl = 0;
-            }
-            else
-            {
-              cur_node.r_ptr = NULL;
-            }
-            push_node(&cur_node);
-            if (an->objid[i] == *ident)
-            {
-              ident_len--;
-              ident++;
-            }
-            else
-            {
-              /* an->objid[i] < *ident */
-              ident_len = 0;
-            }
-            /* follow next child pointer */
-            node = an->nptr[i];
-          }
-        }
-        else
-        {
-          /* i == an->maxlength */
-          climb_tree = 1;
-        }
-      }
-      else
-      {
-        u8_t j;
-        /* ident_len == 0, complete with leftmost '.thing' */
-        j = 0;
-        while ((j < an->maxlength) && empty_table(an->nptr[j]))
-        {
-          j++;
-        }
-        if (j < an->maxlength)
-        {
-          LWIP_DEBUGF(SNMP_MIB_DEBUG,("left an->objid[j]==%"S32_F"\n",an->objid[j]));
-          oidret->id[oidret->len] = an->objid[j];
-          (oidret->len)++;
-          if (an->nptr[j] == NULL)
-          {
-            /* leaf node */
-            return (struct mib_node*)an;
-          }
-          else
-          {
-            /* no leaf, continue */
-            node = an->nptr[j];
-          }
-        }
-        else
-        {
-          /* j == an->maxlength */
-          climb_tree = 1;
-        }
-      }
-    }
-    else if(node_type == MIB_NODE_LR)
-    {
-      struct mib_list_rootnode *lrn;
-      struct mib_list_node *ln;
-
-      /* list root node (internal 'RAM', variable length) */
-      lrn = (struct mib_list_rootnode *)node;
-      if (ident_len > 0)
-      {
-        ln = lrn->head;
-        /* iterate over list, head to tail */
-        while ((ln != NULL) && (ln->objid < *ident))
-        {
-          ln = ln->next;
-        }
-        if (ln != NULL)
-        {
-          LWIP_DEBUGF(SNMP_MIB_DEBUG,("ln->objid==%"S32_F" *ident==%"S32_F"\n",ln->objid,*ident));
-          oidret->id[oidret->len] = ln->objid;
-          (oidret->len)++;
-          if (ln->nptr == NULL)
-          {
-            /* leaf node */
-            if (ln->objid > *ident)
-            {
-              return (struct mib_node*)lrn;
-            }
-            else if (ln->next != NULL)
-            {
-              /* ln->objid == *ident */
-              (oidret->len)--;
-              oidret->id[oidret->len] = ln->next->objid;
-              (oidret->len)++;
-              return (struct mib_node*)lrn;
-            }
-            else
-            {
-              /* ln->next == NULL */
-              (oidret->len)--;
-              climb_tree = 1;
-            }
-          }
-          else
-          {
-            struct mib_list_node *jn;
-            struct nse cur_node;
-
-            /* non-leaf, store right child ptr and id */
-            jn = ln->next;
-            while ((jn != NULL) && empty_table(jn->nptr))
-            {
-              jn = jn->next;
-            }
-            if (jn != NULL)
-            {
-              cur_node.r_ptr = jn->nptr;
-              cur_node.r_id = jn->objid;
-              cur_node.r_nl = 0;
-            }
-            else
-            {
-              cur_node.r_ptr = NULL;
-            }
-            push_node(&cur_node);
-            if (ln->objid == *ident)
-            {
-              ident_len--;
-              ident++;
-            }
-            else
-            {
-              /* ln->objid < *ident */
-              ident_len = 0;
-            }
-            /* follow next child pointer */
-            node = ln->nptr;
-          }
-
-        }
-        else
-        {
-          /* ln == NULL */
-          climb_tree = 1;
-        }
-      }
-      else
-      {
-        struct mib_list_node *jn;
-        /* ident_len == 0, complete with leftmost '.thing' */
-        jn = lrn->head;
-        while ((jn != NULL) && empty_table(jn->nptr))
-        {
-          jn = jn->next;
-        }
-        if (jn != NULL)
-        {
-          LWIP_DEBUGF(SNMP_MIB_DEBUG,("left jn->objid==%"S32_F"\n",jn->objid));
-          oidret->id[oidret->len] = jn->objid;
-          (oidret->len)++;
-          if (jn->nptr == NULL)
-          {
-            /* leaf node */
-            LWIP_DEBUGF(SNMP_MIB_DEBUG,("jn->nptr == NULL\n"));
-            return (struct mib_node*)lrn;
-          }
-          else
-          {
-            /* no leaf, continue */
-            node = jn->nptr;
-          }
-        }
-        else
-        {
-          /* jn == NULL */
-          climb_tree = 1;
-        }
-      }
-    }
-    else if(node_type == MIB_NODE_EX)
-    {
-      struct mib_external_node *en;
-      s32_t ex_id;
-
-      /* external node (addressing and access via functions) */
-      en = (struct mib_external_node *)node;
-      if (ident_len > 0)
-      {
-        u16_t i, len;
-
-        i = 0;
-        len = en->level_length(en->addr_inf,ext_level);
-        while ((i < len) && (en->ident_cmp(en->addr_inf,ext_level,i,*ident) < 0))
-        {
-          i++;
-        }
-        if (i < len)
-        {
-          /* add identifier to oidret */
-          en->get_objid(en->addr_inf,ext_level,i,&ex_id);
-          LWIP_DEBUGF(SNMP_MIB_DEBUG,("en->objid[%"U16_F"]==%"S32_F" *ident==%"S32_F"\n",i,ex_id,*ident));
-          oidret->id[oidret->len] = ex_id;
-          (oidret->len)++;
-
-          if ((ext_level + 1) == en->tree_levels)
-          {
-            LWIP_DEBUGF(SNMP_MIB_DEBUG,("leaf node\n"));
-            /* leaf node */
-            if (ex_id > *ident)
-            {
-              return (struct mib_node*)en;
-            }
-            else if ((i + 1) < len)
-            {
-              /* ex_id == *ident */
-              en->get_objid(en->addr_inf,ext_level,i + 1,&ex_id);
-              (oidret->len)--;
-              oidret->id[oidret->len] = ex_id;
-              (oidret->len)++;
-              return (struct mib_node*)en;
-            }
-            else
-            {
-              /* (i + 1) == len */
-              (oidret->len)--;
-              climb_tree = 1;
-            }
-          }
-          else
-          {
-            u8_t j;
-            struct nse cur_node;
-
-            LWIP_DEBUGF(SNMP_MIB_DEBUG,("non-leaf node\n"));
-            /* non-leaf, store right child ptr and id */
-            LWIP_ASSERT("i < 0xff", i < 0xff);
-            j = (u8_t)i + 1;
-            if (j < len)
-            {
-              /* right node is the current external node */
-              cur_node.r_ptr = node;
-              en->get_objid(en->addr_inf,ext_level,j,&cur_node.r_id);
-              cur_node.r_nl = ext_level + 1;
-            }
-            else
-            {
-              cur_node.r_ptr = NULL;
-            }
-            push_node(&cur_node);
-            if (en->ident_cmp(en->addr_inf,ext_level,i,*ident) == 0)
-            {
-              ident_len--;
-              ident++;
-            }
-            else
-            {
-              /* external id < *ident */
-              ident_len = 0;
-            }
-            /* proceed to child */
-            ext_level++;
-          }
-        }
-        else
-        {
-          /* i == len (en->level_len()) */
-          climb_tree = 1;
-        }
-      }
-      else
-      {
-        /* ident_len == 0, complete with leftmost '.thing' */
-        en->get_objid(en->addr_inf,ext_level,0,&ex_id);
-        LWIP_DEBUGF(SNMP_MIB_DEBUG,("left en->objid==%"S32_F"\n",ex_id));
-        oidret->id[oidret->len] = ex_id;
-        (oidret->len)++;
-        if ((ext_level + 1) == en->tree_levels)
-        {
-          /* leaf node */
-          LWIP_DEBUGF(SNMP_MIB_DEBUG,("(ext_level + 1) == en->tree_levels\n"));
-          return (struct mib_node*)en;
-        }
-        else
-        {
-          /* no leaf, proceed to child */
-          ext_level++;
-        }
-      }
-    }
-    else if(node_type == MIB_NODE_SC)
-    {
-      mib_scalar_node *sn;
-
-      /* scalar node  */
-      sn = (mib_scalar_node *)node;
-      if (ident_len > 0)
-      {
-        /* at .0 */
-        climb_tree = 1;
-      }
-      else
-      {
-        /* ident_len == 0, complete object identifier */
-        oidret->id[oidret->len] = 0;
-        (oidret->len)++;
-        /* leaf node */
-        LWIP_DEBUGF(SNMP_MIB_DEBUG,("completed scalar leaf\n"));
-        return (struct mib_node*)sn;
-      }
-    }
-    else
-    {
-      /* unknown/unhandled node_type */
-      LWIP_DEBUGF(SNMP_MIB_DEBUG,("expand failed node_type %"U16_F" unkown\n",(u16_t)node_type));
-      return NULL;
-    }
-
-    if (climb_tree)
-    {
-      struct nse child;
-
-      /* find right child ptr */
-      child.r_ptr = NULL;
-      child.r_id = 0;
-      child.r_nl = 0;
-      while ((node_stack_cnt > 0) && (child.r_ptr == NULL))
-      {
-        pop_node(&child);
-        /* trim returned oid */
-        (oidret->len)--;
-      }
-      if (child.r_ptr != NULL)
-      {
-        /* incoming ident is useless beyond this point */
-        ident_len = 0;
-        oidret->id[oidret->len] = child.r_id;
-        oidret->len++;
-        node = child.r_ptr;
-        ext_level = child.r_nl;
-      }
-      else
-      {
-        /* tree ends here ... */
-        LWIP_DEBUGF(SNMP_MIB_DEBUG,("expand failed, tree ends here\n"));
-        return NULL;
-      }
-    }
-  }
-  /* done, found nothing */
-  LWIP_DEBUGF(SNMP_MIB_DEBUG,("expand failed node==%p\n",(void*)node));
-  return NULL;
-}
-
-/**
- * Test object identifier for the iso.org.dod.internet prefix.
- *
- * @param ident_len the length of the supplied object identifier
- * @param ident points to the array of sub identifiers
- * @return 1 if it matches, 0 otherwise
- */
-u8_t
-snmp_iso_prefix_tst(u8_t ident_len, s32_t *ident)
-{
-  if ((ident_len > 3) &&
-      (ident[0] == 1) && (ident[1] == 3) &&
-      (ident[2] == 6) && (ident[3] == 1))
-  {
-    return 1;
-  }
-  else
-  {
-    return 0;
-  }
-}
-
-/**
- * Expands object identifier to the iso.org.dod.internet
- * prefix for use in getnext operation.
- *
- * @param ident_len the length of the supplied object identifier
- * @param ident points to the array of sub identifiers
- * @param oidret points to returned expanded object identifier
- * @return 1 if it matches, 0 otherwise
- *
- * @note ident_len 0 is allowed, expanding to the first known object id!!
- */
-u8_t
-snmp_iso_prefix_expand(u8_t ident_len, s32_t *ident, struct snmp_obj_id *oidret)
-{
-  const s32_t *prefix_ptr;
-  s32_t *ret_ptr;
-  u8_t i;
-
-  i = 0;
-  prefix_ptr = &prefix[0];
-  ret_ptr = &oidret->id[0];
-  ident_len = ((ident_len < 4)?ident_len:4);
-  while ((i < ident_len) && ((*ident) <= (*prefix_ptr)))
-  {
-    *ret_ptr++ = *prefix_ptr++;
-    ident++;
-    i++;
-  }
-  if (i == ident_len)
-  {
-    /* match, complete missing bits */
-    while (i < 4)
-    {
-      *ret_ptr++ = *prefix_ptr++;
-      i++;
-    }
-    oidret->len = i;
-    return 1;
-  }
-  else
-  {
-    /* i != ident_len */
-    return 0;
-  }
-}
-
-#endif /* LWIP_SNMP */

+ 0 - 1491
thirdparty/lwip/src/core/snmp/msg_in.c

@@ -1,1491 +0,0 @@
-/**
- * @file
- * SNMP input message processing (RFC1157).
- */
-
-/*
- * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * Author: Christiaan Simons <christiaan.simons@axon.tv>
- */
-
-#include "lwip/opt.h"
-#include "settings_api.h"
-
-#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
-
-#include "lwip/snmp.h"
-#include "lwip/snmp_asn1.h"
-#include "lwip/snmp_msg.h"
-#include "lwip/snmp_structs.h"
-#include "lwip/ip_addr.h"
-#include "lwip/memp.h"
-#include "lwip/udp.h"
-#include "lwip/stats.h"
-
-#include <string.h>
-
-/* public (non-static) constants */
-/** SNMP v1 == 0 */
-const s32_t snmp_version = 0;
-/** default SNMP community string */
-const char snmp_publiccommunity[7] = "public";
-
-/**
-  * @brief  Общая структура настроек
-  */
-extern SETTINGS_t sSettings;
-
-/* statically allocated buffers for SNMP_CONCURRENT_REQUESTS */
-struct snmp_msg_pstat msg_input_list[SNMP_CONCURRENT_REQUESTS];
-/* UDP Protocol Control Block */
-struct udp_pcb *snmp1_pcb;
-
-static void snmp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port);
-static err_t snmp_pdu_header_check(struct pbuf *p, u16_t ofs, u16_t pdu_len, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat);
-static err_t snmp_pdu_dec_varbindlist(struct pbuf *p, u16_t ofs, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat);
-
-
-/**
- * Starts SNMP Agent.
- * Allocates UDP pcb and binds it to IP_ADDR_ANY port 161.
- */
-void
-snmp_init(void)
-{
-  struct snmp_msg_pstat *msg_ps;
-  u8_t i;
-
-  snmp1_pcb = udp_new();
-  if (snmp1_pcb != NULL)
-  {
-    udp_recv(snmp1_pcb, snmp_recv, (void *)SNMP_IN_PORT);
-    udp_bind(snmp1_pcb, IP_ADDR_ANY, SNMP_IN_PORT);
-  }
-  msg_ps = &msg_input_list[0];
-  for (i=0; i<SNMP_CONCURRENT_REQUESTS; i++)
-  {
-    msg_ps->state = SNMP_MSG_EMPTY;
-    msg_ps->error_index = 0;
-    msg_ps->error_status = SNMP_ES_NOERROR;
-    msg_ps++;
-  }
-  trap_msg.pcb = snmp1_pcb;
-
-#ifdef SNMP_PRIVATE_MIB_INIT
-  /* If defined, this must be a function-like define to initialize the
-   * private MIB after the stack has been initialized.
-   * The private MIB can also be initialized in tcpip_callback (or after
-   * the stack is initialized), this define is only for convenience. */
-  SNMP_PRIVATE_MIB_INIT();
-#endif /* SNMP_PRIVATE_MIB_INIT */
-
-  /* The coldstart trap will only be output
-     if our outgoing interface is up & configured  */
-  //snmp_coldstart_trap();
-}
-
-static void
-snmp_error_response(struct snmp_msg_pstat *msg_ps, u8_t error)
-{
-  /* move names back from outvb to invb */
-  int v;
-  struct snmp_varbind *vbi = msg_ps->invb.head;
-  struct snmp_varbind *vbo = msg_ps->outvb.head;
-  for (v=0; v<msg_ps->vb_idx; v++) {
-    vbi->ident_len = vbo->ident_len;
-    vbo->ident_len = 0;
-    vbi->ident = vbo->ident;
-    vbo->ident = NULL;
-    vbi = vbi->next;
-    vbo = vbo->next;
-  }
-  /* free outvb */
-  snmp_varbind_list_free(&msg_ps->outvb);
-  /* we send invb back */
-  msg_ps->outvb = msg_ps->invb;
-  msg_ps->invb.head = NULL;
-  msg_ps->invb.tail = NULL;
-  msg_ps->invb.count = 0;
-  msg_ps->error_status = error;
-  /* error index must be 0 for error too big */
-  msg_ps->error_index = (error != SNMP_ES_TOOBIG) ? (1 + msg_ps->vb_idx) : 0;
-  snmp_send_response(msg_ps);
-  snmp_varbind_list_free(&msg_ps->outvb);
-  msg_ps->state = SNMP_MSG_EMPTY;
-}
-
-static void
-snmp_ok_response(struct snmp_msg_pstat *msg_ps)
-{
-  err_t err_ret;
-
-  err_ret = snmp_send_response(msg_ps);
-  if (err_ret == ERR_MEM)
-  {
-    /* serious memory problem, can't return tooBig */
-  }
-  else
-  {
-    LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event = %"S32_F"\n",msg_ps->error_status));
-  }
-  /* free varbinds (if available) */
-  snmp_varbind_list_free(&msg_ps->invb);
-  snmp_varbind_list_free(&msg_ps->outvb);
-  msg_ps->state = SNMP_MSG_EMPTY;
-}
-
-/**
- * Service an internal or external event for SNMP GET.
- *
- * @param request_id identifies requests from 0 to (SNMP_CONCURRENT_REQUESTS-1)
- * @param msg_ps points to the assosicated message process state
- */
-static void
-snmp_msg_get_event(u8_t request_id, struct snmp_msg_pstat *msg_ps)
-{
-  LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_get_event: msg_ps->state==%"U16_F"\n",(u16_t)msg_ps->state));
-
-  if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_OBJDEF)
-  {
-    struct mib_external_node *en;
-    struct snmp_name_ptr np;
-
-    /* get_object_def() answer*/
-    en = msg_ps->ext_mib_node;
-    np = msg_ps->ext_name_ptr;
-
-    /* translate answer into a known lifeform */
-    en->get_object_def_a(request_id, np.ident_len, np.ident, &msg_ps->ext_object_def);
-    if ((msg_ps->ext_object_def.instance != MIB_OBJECT_NONE) &&
-        (msg_ps->ext_object_def.access & MIB_ACCESS_READ))
-    {
-      msg_ps->state = SNMP_MSG_EXTERNAL_GET_VALUE;
-      en->get_value_q(request_id, &msg_ps->ext_object_def);
-    }
-    else
-    {
-      en->get_object_def_pc(request_id, np.ident_len, np.ident);
-      /* search failed, object id points to unknown object (nosuchname) */
-      snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
-    }
-  }
-  else if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_VALUE)
-  {
-    struct mib_external_node *en;
-    struct snmp_varbind *vb;
-
-    /* get_value() answer */
-    en = msg_ps->ext_mib_node;
-
-    /* allocate output varbind */
-    vb = (struct snmp_varbind *)memp_malloc(MEMP_SNMP_VARBIND);
-    if (vb != NULL)
-    {
-      vb->next = NULL;
-      vb->prev = NULL;
-
-      /* move name from invb to outvb */
-      vb->ident = msg_ps->vb_ptr->ident;
-      vb->ident_len = msg_ps->vb_ptr->ident_len;
-      /* ensure this memory is refereced once only */
-      msg_ps->vb_ptr->ident = NULL;
-      msg_ps->vb_ptr->ident_len = 0;
-
-      vb->value_type = msg_ps->ext_object_def.asn_type;
-      LWIP_ASSERT("invalid length", msg_ps->ext_object_def.v_len <= 0xff);
-      vb->value_len = (u8_t)msg_ps->ext_object_def.v_len;
-      if (vb->value_len > 0)
-      {
-        LWIP_ASSERT("SNMP_MAX_OCTET_STRING_LEN is configured too low", vb->value_len <= SNMP_MAX_VALUE_SIZE);
-        vb->value = memp_malloc(MEMP_SNMP_VALUE);
-        if (vb->value != NULL)
-        {
-          en->get_value_a(request_id, &msg_ps->ext_object_def, vb->value_len, vb->value);
-          snmp_varbind_tail_add(&msg_ps->outvb, vb);
-          /* search again (if vb_idx < msg_ps->invb.count) */
-          msg_ps->state = SNMP_MSG_SEARCH_OBJ;
-          msg_ps->vb_idx += 1;
-        }
-        else
-        {
-          en->get_value_pc(request_id, &msg_ps->ext_object_def);
-          LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event: no variable space\n"));
-          msg_ps->vb_ptr->ident = vb->ident;
-          msg_ps->vb_ptr->ident_len = vb->ident_len;
-          memp_free(MEMP_SNMP_VARBIND, vb);
-          snmp_error_response(msg_ps,SNMP_ES_TOOBIG);
-        }
-      }
-      else
-      {
-        /* vb->value_len == 0, empty value (e.g. empty string) */
-        en->get_value_a(request_id, &msg_ps->ext_object_def, 0, NULL);
-        vb->value = NULL;
-        snmp_varbind_tail_add(&msg_ps->outvb, vb);
-        /* search again (if vb_idx < msg_ps->invb.count) */
-        msg_ps->state = SNMP_MSG_SEARCH_OBJ;
-        msg_ps->vb_idx += 1;
-      }
-    }
-    else
-    {
-      en->get_value_pc(request_id, &msg_ps->ext_object_def);
-      LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event: no outvb space\n"));
-      snmp_error_response(msg_ps,SNMP_ES_TOOBIG);
-    }
-  }
-
-  while ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) &&
-         (msg_ps->vb_idx < msg_ps->invb.count))
-  {
-    struct mib_node *mn;
-    struct snmp_name_ptr np;
-
-    if (msg_ps->vb_idx == 0)
-    {
-      msg_ps->vb_ptr = msg_ps->invb.head;
-    }
-    else
-    {
-      msg_ps->vb_ptr = msg_ps->vb_ptr->next;
-    }
-    /** test object identifier for .iso.org.dod.internet prefix */
-    if (snmp_iso_prefix_tst(msg_ps->vb_ptr->ident_len,  msg_ps->vb_ptr->ident))
-    {
-      mn = snmp_search_tree((struct mib_node*)&internet, msg_ps->vb_ptr->ident_len - 4,
-                             msg_ps->vb_ptr->ident + 4, &np);
-      if (mn != NULL)
-      {
-        if (mn->node_type == MIB_NODE_EX)
-        {
-          /* external object */
-          struct mib_external_node *en = (struct mib_external_node*)mn;
-
-          msg_ps->state = SNMP_MSG_EXTERNAL_GET_OBJDEF;
-          /* save en && args in msg_ps!! */
-          msg_ps->ext_mib_node = en;
-          msg_ps->ext_name_ptr = np;
-
-          en->get_object_def_q(en->addr_inf, request_id, np.ident_len, np.ident);
-        }
-        else
-        {
-          /* internal object */
-          struct obj_def object_def;
-
-          msg_ps->state = SNMP_MSG_INTERNAL_GET_OBJDEF;
-          mn->get_object_def(np.ident_len, np.ident, &object_def);
-          if ((object_def.instance != MIB_OBJECT_NONE) &&
-            (object_def.access & MIB_ACCESS_READ))
-          {
-            mn = mn;
-          }
-          else
-          {
-            /* search failed, object id points to unknown object (nosuchname) */
-            mn =  NULL;
-          }
-          if (mn != NULL)
-          {
-            struct snmp_varbind *vb;
-
-            msg_ps->state = SNMP_MSG_INTERNAL_GET_VALUE;
-            /* allocate output varbind */
-            vb = (struct snmp_varbind *)memp_malloc(MEMP_SNMP_VARBIND);
-            if (vb != NULL)
-            {
-              vb->next = NULL;
-              vb->prev = NULL;
-
-              /* move name from invb to outvb */
-              vb->ident = msg_ps->vb_ptr->ident;
-              vb->ident_len = msg_ps->vb_ptr->ident_len;
-              /* ensure this memory is refereced once only */
-              msg_ps->vb_ptr->ident = NULL;
-              msg_ps->vb_ptr->ident_len = 0;
-
-              vb->value_type = object_def.asn_type;
-              LWIP_ASSERT("invalid length", object_def.v_len <= 0xff);
-              vb->value_len = (u8_t)object_def.v_len;
-              if (vb->value_len > 0)
-              {
-                LWIP_ASSERT("SNMP_MAX_OCTET_STRING_LEN is configured too low",
-                  vb->value_len <= SNMP_MAX_VALUE_SIZE);
-                vb->value = memp_malloc(MEMP_SNMP_VALUE);
-                if (vb->value != NULL)
-                {
-                  mn->get_value(&object_def, vb->value_len, vb->value);
-                  snmp_varbind_tail_add(&msg_ps->outvb, vb);
-                  msg_ps->state = SNMP_MSG_SEARCH_OBJ;
-                  msg_ps->vb_idx += 1;
-                }
-                else
-                {
-                  LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event: couldn't allocate variable space\n"));
-                  msg_ps->vb_ptr->ident = vb->ident;
-                  msg_ps->vb_ptr->ident_len = vb->ident_len;
-                  vb->ident = NULL;
-                  vb->ident_len = 0;
-                  memp_free(MEMP_SNMP_VARBIND, vb);
-                  snmp_error_response(msg_ps,SNMP_ES_TOOBIG);
-                }
-              }
-              else
-              {
-                /* vb->value_len == 0, empty value (e.g. empty string) */
-                vb->value = NULL;
-                snmp_varbind_tail_add(&msg_ps->outvb, vb);
-                msg_ps->state = SNMP_MSG_SEARCH_OBJ;
-                msg_ps->vb_idx += 1;
-              }
-            }
-            else
-            {
-              LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event: couldn't allocate outvb space\n"));
-              snmp_error_response(msg_ps,SNMP_ES_TOOBIG);
-            }
-          }
-        }
-      }
-    }
-    else
-    {
-      mn = NULL;
-    }
-    if (mn == NULL)
-    {
-      /* mn == NULL, noSuchName */
-      snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
-    }
-  }
-  if ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) &&
-      (msg_ps->vb_idx == msg_ps->invb.count))
-  {
-    snmp_ok_response(msg_ps);
-  }
-}
-
-/**
- * Service an internal or external event for SNMP GETNEXT.
- *
- * @param request_id identifies requests from 0 to (SNMP_CONCURRENT_REQUESTS-1)
- * @param msg_ps points to the assosicated message process state
- */
-static void
-snmp_msg_getnext_event(u8_t request_id, struct snmp_msg_pstat *msg_ps)
-{
-  LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_getnext_event: msg_ps->state==%"U16_F"\n",(u16_t)msg_ps->state));
-
-  if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_OBJDEF)
-  {
-    struct mib_external_node *en;
-
-    /* get_object_def() answer*/
-    en = msg_ps->ext_mib_node;
-
-    /* translate answer into a known lifeform */
-    en->get_object_def_a(request_id, 1, &msg_ps->ext_oid.id[msg_ps->ext_oid.len - 1], &msg_ps->ext_object_def);
-    if (msg_ps->ext_object_def.instance != MIB_OBJECT_NONE)
-    {
-      msg_ps->state = SNMP_MSG_EXTERNAL_GET_VALUE;
-      en->get_value_q(request_id, &msg_ps->ext_object_def);
-    }
-    else
-    {
-      en->get_object_def_pc(request_id, 1, &msg_ps->ext_oid.id[msg_ps->ext_oid.len - 1]);
-      /* search failed, object id points to unknown object (nosuchname) */
-      snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
-    }
-  }
-  else if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_VALUE)
-  {
-    struct mib_external_node *en;
-    struct snmp_varbind *vb;
-
-    /* get_value() answer */
-    en = msg_ps->ext_mib_node;
-
-    LWIP_ASSERT("invalid length", msg_ps->ext_object_def.v_len <= 0xff);
-    vb = snmp_varbind_alloc(&msg_ps->ext_oid,
-                            msg_ps->ext_object_def.asn_type,
-                            (u8_t)msg_ps->ext_object_def.v_len);
-    if (vb != NULL)
-    {
-      en->get_value_a(request_id, &msg_ps->ext_object_def, vb->value_len, vb->value);
-      snmp_varbind_tail_add(&msg_ps->outvb, vb);
-      msg_ps->state = SNMP_MSG_SEARCH_OBJ;
-      msg_ps->vb_idx += 1;
-    }
-    else
-    {
-      en->get_value_pc(request_id, &msg_ps->ext_object_def);
-      LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_getnext_event: couldn't allocate outvb space\n"));
-      snmp_error_response(msg_ps,SNMP_ES_TOOBIG);
-    }
-  }
-
-  while ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) &&
-         (msg_ps->vb_idx < msg_ps->invb.count))
-  {
-    struct mib_node *mn;
-    struct snmp_obj_id oid;
-
-    if (msg_ps->vb_idx == 0)
-    {
-      msg_ps->vb_ptr = msg_ps->invb.head;
-    }
-    else
-    {
-      msg_ps->vb_ptr = msg_ps->vb_ptr->next;
-    }
-    if (snmp_iso_prefix_expand(msg_ps->vb_ptr->ident_len, msg_ps->vb_ptr->ident, &oid))
-    {
-      if (msg_ps->vb_ptr->ident_len > 3)
-      {
-        /* can offset ident_len and ident */
-        mn = snmp_expand_tree((struct mib_node*)&internet,
-                              msg_ps->vb_ptr->ident_len - 4,
-                              msg_ps->vb_ptr->ident + 4, &oid);
-      }
-      else
-      {
-        /* can't offset ident_len -4, ident + 4 */
-        mn = snmp_expand_tree((struct mib_node*)&internet, 0, NULL, &oid);
-      }
-    }
-    else
-    {
-      mn = NULL;
-    }
-    if (mn != NULL)
-    {
-      if (mn->node_type == MIB_NODE_EX)
-      {
-        /* external object */
-        struct mib_external_node *en = (struct mib_external_node*)mn;
-
-        msg_ps->state = SNMP_MSG_EXTERNAL_GET_OBJDEF;
-        /* save en && args in msg_ps!! */
-        msg_ps->ext_mib_node = en;
-        msg_ps->ext_oid = oid;
-
-        en->get_object_def_q(en->addr_inf, request_id, 1, &oid.id[oid.len - 1]);
-      }
-      else
-      {
-        /* internal object */
-        struct obj_def object_def;
-        struct snmp_varbind *vb;
-
-        msg_ps->state = SNMP_MSG_INTERNAL_GET_OBJDEF;
-        mn->get_object_def(1, &oid.id[oid.len - 1], &object_def);
-
-        LWIP_ASSERT("invalid length", object_def.v_len <= 0xff);
-        vb = snmp_varbind_alloc(&oid, object_def.asn_type, (u8_t)object_def.v_len);
-        if (vb != NULL)
-        {
-          msg_ps->state = SNMP_MSG_INTERNAL_GET_VALUE;
-          mn->get_value(&object_def, object_def.v_len, vb->value);
-          snmp_varbind_tail_add(&msg_ps->outvb, vb);
-          msg_ps->state = SNMP_MSG_SEARCH_OBJ;
-          msg_ps->vb_idx += 1;
-        }
-        else
-        {
-          LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_recv couldn't allocate outvb space\n"));
-          snmp_error_response(msg_ps,SNMP_ES_TOOBIG);
-        }
-      }
-    }
-    if (mn == NULL)
-    {
-      /* mn == NULL, noSuchName */
-      snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
-    }
-  }
-  if ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) &&
-      (msg_ps->vb_idx == msg_ps->invb.count))
-  {
-    snmp_ok_response(msg_ps);
-  }
-}
-
-/**
- * Service an internal or external event for SNMP SET.
- *
- * @param request_id identifies requests from 0 to (SNMP_CONCURRENT_REQUESTS-1)
- * @param msg_ps points to the assosicated message process state
- */
-static void
-snmp_msg_set_event(u8_t request_id, struct snmp_msg_pstat *msg_ps)
-{
-  LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_set_event: msg_ps->state==%"U16_F"\n",(u16_t)msg_ps->state));
-
-  if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_OBJDEF)
-  {
-    struct mib_external_node *en;
-    struct snmp_name_ptr np;
-
-    /* get_object_def() answer*/
-    en = msg_ps->ext_mib_node;
-    np = msg_ps->ext_name_ptr;
-
-    /* translate answer into a known lifeform */
-    en->get_object_def_a(request_id, np.ident_len, np.ident, &msg_ps->ext_object_def);
-    if (msg_ps->ext_object_def.instance != MIB_OBJECT_NONE)
-    {
-      msg_ps->state = SNMP_MSG_EXTERNAL_SET_TEST;
-      en->set_test_q(request_id, &msg_ps->ext_object_def);
-    }
-    else
-    {
-      en->get_object_def_pc(request_id, np.ident_len, np.ident);
-      /* search failed, object id points to unknown object (nosuchname) */
-      snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
-    }
-  }
-  else if (msg_ps->state == SNMP_MSG_EXTERNAL_SET_TEST)
-  {
-    struct mib_external_node *en;
-
-    /* set_test() answer*/
-    en = msg_ps->ext_mib_node;
-
-    if (msg_ps->ext_object_def.access & MIB_ACCESS_WRITE)
-    {
-       if ((msg_ps->ext_object_def.asn_type == msg_ps->vb_ptr->value_type) &&
-           (en->set_test_a(request_id,&msg_ps->ext_object_def,
-                           msg_ps->vb_ptr->value_len,msg_ps->vb_ptr->value) != 0))
-      {
-        msg_ps->state = SNMP_MSG_SEARCH_OBJ;
-        msg_ps->vb_idx += 1;
-      }
-      else
-      {
-        en->set_test_pc(request_id,&msg_ps->ext_object_def);
-        /* bad value */
-        snmp_error_response(msg_ps,SNMP_ES_BADVALUE);
-      }
-    }
-    else
-    {
-      en->set_test_pc(request_id,&msg_ps->ext_object_def);
-      /* object not available for set */
-      snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
-    }
-  }
-  else if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_OBJDEF_S)
-  {
-    struct mib_external_node *en;
-    struct snmp_name_ptr np;
-
-    /* get_object_def() answer*/
-    en = msg_ps->ext_mib_node;
-    np = msg_ps->ext_name_ptr;
-
-    /* translate answer into a known lifeform */
-    en->get_object_def_a(request_id, np.ident_len, np.ident, &msg_ps->ext_object_def);
-    if (msg_ps->ext_object_def.instance != MIB_OBJECT_NONE)
-    {
-      msg_ps->state = SNMP_MSG_EXTERNAL_SET_VALUE;
-      en->set_value_q(request_id, &msg_ps->ext_object_def,
-                      msg_ps->vb_ptr->value_len,msg_ps->vb_ptr->value);
-    }
-    else
-    {
-      en->get_object_def_pc(request_id, np.ident_len, np.ident);
-      /* set_value failed, object has disappeared for some odd reason?? */
-      snmp_error_response(msg_ps,SNMP_ES_GENERROR);
-    }
-  }
-  else if (msg_ps->state == SNMP_MSG_EXTERNAL_SET_VALUE)
-  {
-    struct mib_external_node *en;
-
-    /** set_value_a() */
-    en = msg_ps->ext_mib_node;
-    en->set_value_a(request_id, &msg_ps->ext_object_def,
-      msg_ps->vb_ptr->value_len, msg_ps->vb_ptr->value);
-
-    /** @todo use set_value_pc() if toobig */
-    msg_ps->state = SNMP_MSG_INTERNAL_SET_VALUE;
-    msg_ps->vb_idx += 1;
-  }
-
-  /* test all values before setting */
-  while ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) &&
-         (msg_ps->vb_idx < msg_ps->invb.count))
-  {
-    struct mib_node *mn;
-    struct snmp_name_ptr np;
-
-    if (msg_ps->vb_idx == 0)
-    {
-      msg_ps->vb_ptr = msg_ps->invb.head;
-    }
-    else
-    {
-      msg_ps->vb_ptr = msg_ps->vb_ptr->next;
-    }
-    /** test object identifier for .iso.org.dod.internet prefix */
-    if (snmp_iso_prefix_tst(msg_ps->vb_ptr->ident_len,  msg_ps->vb_ptr->ident))
-    {
-      mn = snmp_search_tree((struct mib_node*)&internet, msg_ps->vb_ptr->ident_len - 4,
-                             msg_ps->vb_ptr->ident + 4, &np);
-      if (mn != NULL)
-      {
-        if (mn->node_type == MIB_NODE_EX)
-        {
-          /* external object */
-          struct mib_external_node *en = (struct mib_external_node*)mn;
-
-          msg_ps->state = SNMP_MSG_EXTERNAL_GET_OBJDEF;
-          /* save en && args in msg_ps!! */
-          msg_ps->ext_mib_node = en;
-          msg_ps->ext_name_ptr = np;
-
-          en->get_object_def_q(en->addr_inf, request_id, np.ident_len, np.ident);
-        }
-        else
-        {
-          /* internal object */
-          struct obj_def object_def;
-
-          msg_ps->state = SNMP_MSG_INTERNAL_GET_OBJDEF;
-          mn->get_object_def(np.ident_len, np.ident, &object_def);
-          if (object_def.instance != MIB_OBJECT_NONE)
-          {
-            mn = mn;
-          }
-          else
-          {
-            /* search failed, object id points to unknown object (nosuchname) */
-            mn = NULL;
-          }
-          if (mn != NULL)
-          {
-            msg_ps->state = SNMP_MSG_INTERNAL_SET_TEST;
-
-            if (object_def.access & MIB_ACCESS_WRITE)
-            {
-              if ((object_def.asn_type == msg_ps->vb_ptr->value_type) &&
-                  (mn->set_test(&object_def,msg_ps->vb_ptr->value_len,msg_ps->vb_ptr->value) != 0))
-              {
-                msg_ps->state = SNMP_MSG_SEARCH_OBJ;
-                msg_ps->vb_idx += 1;
-              }
-              else
-              {
-                /* bad value */
-                snmp_error_response(msg_ps,SNMP_ES_BADVALUE);
-              }
-            }
-            else
-            {
-              /* object not available for set */
-              snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
-            }
-          }
-        }
-      }
-    }
-    else
-    {
-      mn = NULL;
-    }
-    if (mn == NULL)
-    {
-      /* mn == NULL, noSuchName */
-      snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
-    }
-  }
-
-  if ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) &&
-      (msg_ps->vb_idx == msg_ps->invb.count))
-  {
-    msg_ps->vb_idx = 0;
-    msg_ps->state = SNMP_MSG_INTERNAL_SET_VALUE;
-  }
-
-  /* set all values "atomically" (be as "atomic" as possible) */
-  while ((msg_ps->state == SNMP_MSG_INTERNAL_SET_VALUE) &&
-         (msg_ps->vb_idx < msg_ps->invb.count))
-  {
-    struct mib_node *mn;
-    struct snmp_name_ptr np;
-
-    if (msg_ps->vb_idx == 0)
-    {
-      msg_ps->vb_ptr = msg_ps->invb.head;
-    }
-    else
-    {
-      msg_ps->vb_ptr = msg_ps->vb_ptr->next;
-    }
-    /* skip iso prefix test, was done previously while settesting() */
-    mn = snmp_search_tree((struct mib_node*)&internet, msg_ps->vb_ptr->ident_len - 4,
-                           msg_ps->vb_ptr->ident + 4, &np);
-    /* check if object is still available
-       (e.g. external hot-plug thingy present?) */
-    if (mn != NULL)
-    {
-      if (mn->node_type == MIB_NODE_EX)
-      {
-        /* external object */
-        struct mib_external_node *en = (struct mib_external_node*)mn;
-
-        msg_ps->state = SNMP_MSG_EXTERNAL_GET_OBJDEF_S;
-        /* save en && args in msg_ps!! */
-        msg_ps->ext_mib_node = en;
-        msg_ps->ext_name_ptr = np;
-
-        en->get_object_def_q(en->addr_inf, request_id, np.ident_len, np.ident);
-      }
-      else
-      {
-        /* internal object */
-        struct obj_def object_def;
-
-        msg_ps->state = SNMP_MSG_INTERNAL_GET_OBJDEF_S;
-        mn->get_object_def(np.ident_len, np.ident, &object_def);
-        msg_ps->state = SNMP_MSG_INTERNAL_SET_VALUE;
-        mn->set_value(&object_def,msg_ps->vb_ptr->value_len,msg_ps->vb_ptr->value);
-        msg_ps->vb_idx += 1;
-      }
-    }
-  }
-  if ((msg_ps->state == SNMP_MSG_INTERNAL_SET_VALUE) &&
-      (msg_ps->vb_idx == msg_ps->invb.count))
-  {
-    /* simply echo the input if we can set it
-       @todo do we need to return the actual value?
-       e.g. if value is silently modified or behaves sticky? */
-    msg_ps->outvb = msg_ps->invb;
-    msg_ps->invb.head = NULL;
-    msg_ps->invb.tail = NULL;
-    msg_ps->invb.count = 0;
-    snmp_ok_response(msg_ps);
-  }
-}
-
-
-/**
- * Handle one internal or external event.
- * Called for one async event. (recv external/private answer)
- *
- * @param request_id identifies requests from 0 to (SNMP_CONCURRENT_REQUESTS-1)
- */
-void
-snmp_msg_event(u8_t request_id)
-{
-  struct snmp_msg_pstat *msg_ps;
-
-  if (request_id < SNMP_CONCURRENT_REQUESTS)
-  {
-    msg_ps = &msg_input_list[request_id];
-    if (msg_ps->rt == SNMP_ASN1_PDU_GET_NEXT_REQ)
-    {
-      snmp_msg_getnext_event(request_id, msg_ps);
-    }
-    else if (msg_ps->rt == SNMP_ASN1_PDU_GET_REQ)
-    {
-      snmp_msg_get_event(request_id, msg_ps);
-    }
-    else if(msg_ps->rt == SNMP_ASN1_PDU_SET_REQ)
-    {
-      snmp_msg_set_event(request_id, msg_ps);
-    }
-  }
-}
-
-
-/* lwIP UDP receive callback function */
-static void
-snmp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port)
-{
-  //volatile struct snmp_msg_pstat *msg_ps;
-  struct snmp_msg_pstat *msg_ps;
-  u8_t req_idx;
-  err_t err_ret;
-  u16_t payload_len = p->tot_len;
-  u16_t payload_ofs = 0;
-  u16_t varbind_ofs = 0;
-
-  /* suppress unused argument warning */
-  LWIP_UNUSED_ARG(arg);
-
-  /* traverse input message process list, look for SNMP_MSG_EMPTY */
-  msg_ps = &msg_input_list[0];
-  req_idx = 0;
-  while ((req_idx < SNMP_CONCURRENT_REQUESTS) && (msg_ps->state != SNMP_MSG_EMPTY))
-  {
-    req_idx++;
-    msg_ps++;
-  }
-  if (req_idx == SNMP_CONCURRENT_REQUESTS)
-  {
-    /* exceeding number of concurrent requests */
-    pbuf_free(p);
-    return;
-  }
-
-  /* accepting request */
-  snmp_inc_snmpinpkts();
-  /* record used 'protocol control block' */
-  msg_ps->pcb = pcb;
-  /* source address (network order) */
-  msg_ps->sip = *addr;
-  /* source port (host order (lwIP oddity)) */
-  msg_ps->sp = port;
-
-  /* check total length, version, community, pdu type */
-  err_ret = snmp_pdu_header_check(p, payload_ofs, payload_len, &varbind_ofs, msg_ps);
-  /* Only accept requests and requests without error (be robust) */
-  /* Reject response and trap headers or error requests as input! */
-  if ((err_ret != ERR_OK) ||
-      ((msg_ps->rt != SNMP_ASN1_PDU_GET_REQ) &&
-       (msg_ps->rt != SNMP_ASN1_PDU_GET_NEXT_REQ) &&
-       (msg_ps->rt != SNMP_ASN1_PDU_SET_REQ)) ||
-      ((msg_ps->error_status != SNMP_ES_NOERROR) ||
-       (msg_ps->error_index != 0)) )
-  {
-    /* header check failed drop request silently, do not return error! */
-    pbuf_free(p);
-    LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_pdu_header_check() failed\n"));
-    return;
-  }
-  LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_recv ok, community %s\n", msg_ps->community));
-
-  /* Builds a list of variable bindings. Copy the varbinds from the pbuf
-    chain to glue them when these are divided over two or more pbuf's. */
-  err_ret = snmp_pdu_dec_varbindlist(p, varbind_ofs, &varbind_ofs, msg_ps);
-  /* we've decoded the incoming message, release input msg now */
-  pbuf_free(p);
-  if ((err_ret != ERR_OK) || (msg_ps->invb.count == 0))
-  {
-    /* varbind-list decode failed, or varbind list empty.
-       drop request silently, do not return error!
-       (errors are only returned for a specific varbind failure) */
-    LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_pdu_dec_varbindlist() failed\n"));
-    return;
-  }
-
-  msg_ps->error_status = SNMP_ES_NOERROR;
-  msg_ps->error_index = 0;
-  /* find object for each variable binding */
-  msg_ps->state = SNMP_MSG_SEARCH_OBJ;
-  /* first variable binding from list to inspect */
-  msg_ps->vb_idx = 0;
-
-  LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_recv varbind cnt=%"U16_F"\n",(u16_t)msg_ps->invb.count));
-
-  /* handle input event and as much objects as possible in one go */
-  snmp_msg_event(req_idx);
-}
-
-/**
- * Checks and decodes incoming SNMP message header, logs header errors.
- *
- * @param p points to pbuf chain of SNMP message (UDP payload)
- * @param ofs points to first octet of SNMP message
- * @param pdu_len the length of the UDP payload
- * @param ofs_ret returns the ofset of the variable bindings
- * @param m_stat points to the current message request state return
- * @return
- * - ERR_OK SNMP header is sane and accepted
- * - ERR_ARG SNMP header is either malformed or rejected
- */
-static err_t
-snmp_pdu_header_check(struct pbuf *p, u16_t ofs, u16_t pdu_len, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat)
-{
-  err_t derr;
-  u16_t len, ofs_base;
-  u8_t  len_octets;
-  u8_t  type;
-  s32_t version;
-
-  ofs_base = ofs;
-  snmp_asn1_dec_type(p, ofs, &type);
-  derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
-  if ((derr != ERR_OK) ||
-      (pdu_len != (1 + len_octets + len)) ||
-      (type != (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)))
-  {
-    snmp_inc_snmpinasnparseerrs();
-    return ERR_ARG;
-  }
-  ofs += (1 + len_octets);
-  snmp_asn1_dec_type(p, ofs, &type);
-  derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
-  if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)))
-  {
-    /* can't decode or no integer (version) */
-    snmp_inc_snmpinasnparseerrs();
-    return ERR_ARG;
-  }
-  derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &version);
-  if (derr != ERR_OK)
-  {
-    /* can't decode */
-    snmp_inc_snmpinasnparseerrs();
-    return ERR_ARG;
-  }
-  if (version != 0)
-  {
-    /* not version 1 */
-    snmp_inc_snmpinbadversions();
-    return ERR_ARG;
-  }
-  ofs += (1 + len_octets + len);
-  snmp_asn1_dec_type(p, ofs, &type);
-  derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
-  if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR)))
-  {
-    /* can't decode or no octet string (community) */
-    snmp_inc_snmpinasnparseerrs();
-    return ERR_ARG;
-  }
-  derr = snmp_asn1_dec_raw(p, ofs + 1 + len_octets, len, SNMP_COMMUNITY_STR_LEN, m_stat->community);
-  if (derr != ERR_OK)
-  {
-    snmp_inc_snmpinasnparseerrs();
-    return ERR_ARG;
-  }
-  /* add zero terminator */
-  len = ((len < (SNMP_COMMUNITY_STR_LEN))?(len):(SNMP_COMMUNITY_STR_LEN));
-  m_stat->community[len] = 0;
-  m_stat->com_strlen = (u8_t)len;
-  
-  /* Проверка public communuty */
-  /*
-  if (strncmp(snmp_publiccommunity, (const char*)m_stat->community, SNMP_COMMUNITY_STR_LEN) != 0)
-  {
-    // @todo: move this if we need to check more names 
-    snmp_inc_snmpinbadcommunitynames();
-    snmp_authfail_trap();
-    return ERR_ARG;
-  }*/
-  
-  ofs += (1 + len_octets + len);
-  snmp_asn1_dec_type(p, ofs, &type);
-  derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
-  if (derr != ERR_OK)
-  {
-    snmp_inc_snmpinasnparseerrs();
-    return ERR_ARG;
-  }
-  
-  /* FIX for write/read communuty */
-  /* GetRequest PDU */
-  if (type == (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_REQ)) 
-  {	
-  	if (strncmp(sSettings.sSnmp.readCommunity, (const char*)m_stat->community, strlen(sSettings.sSnmp.readCommunity)) != 0
-		|| (strlen(sSettings.sSnmp.readCommunity) != strlen((const char*)m_stat->community)) )
-    {
-      snmp_inc_snmpinbadcommunitynames();
-      snmp_authfail_trap();
-      return ERR_ARG;
-    }
-  }	
-  /* SetRequest PDU */
-  else if (type == (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_SET_REQ)) 	
-  {
-    if (strncmp(sSettings.sSnmp.writeCommunity, (const char*)m_stat->community, strlen(sSettings.sSnmp.writeCommunity)) != 0
-		|| (strlen(sSettings.sSnmp.writeCommunity) != strlen((const char*)m_stat->community)) )
-    {
-      snmp_inc_snmpinbadcommunitynames();
-      snmp_authfail_trap();
-      return ERR_ARG;
-    }
-  }	
-  
-  switch(type)
-  {
-    case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_REQ):
-      // GetRequest PDU 
-      snmp_inc_snmpingetrequests();
-      derr = ERR_OK;
-      break;
-
-    case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_NEXT_REQ):
-      // GetNextRequest PDU 
-      snmp_inc_snmpingetnexts();
-      derr = ERR_OK;
-      break;
-
-    case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_RESP):
-      // GetResponse PDU 
-      snmp_inc_snmpingetresponses();
-      derr = ERR_ARG;
-      break;
-    case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_SET_REQ):
-      // SetRequest PDU 
-      snmp_inc_snmpinsetrequests();
-      derr = ERR_OK;
-      break;
-    case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_TRAP):
-      // Trap PDU 
-      snmp_inc_snmpintraps();
-      derr = ERR_ARG;
-      break;
-    default:
-      snmp_inc_snmpinasnparseerrs();
-      derr = ERR_ARG;
-      break;
-  }
-  if (derr != ERR_OK)
-  {
-    /* unsupported input PDU for this agent (no parse error) */
-    return ERR_ARG;
-  }
-  m_stat->rt = type & 0x1F;
-  ofs += (1 + len_octets);
-  if (len != (pdu_len - (ofs - ofs_base)))
-  {
-    /* decoded PDU length does not equal actual payload length */
-    snmp_inc_snmpinasnparseerrs();
-    return ERR_ARG;
-  }
-  snmp_asn1_dec_type(p, ofs, &type);
-  derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
-  if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)))
-  {
-    /* can't decode or no integer (request ID) */
-    snmp_inc_snmpinasnparseerrs();
-    return ERR_ARG;
-  }
-  derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &m_stat->rid);
-  if (derr != ERR_OK)
-  {
-    /* can't decode */
-    snmp_inc_snmpinasnparseerrs();
-    return ERR_ARG;
-  }
-  ofs += (1 + len_octets + len);
-  snmp_asn1_dec_type(p, ofs, &type);
-  derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
-  if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)))
-  {
-    /* can't decode or no integer (error-status) */
-    snmp_inc_snmpinasnparseerrs();
-    return ERR_ARG;
-  }
-  /* must be noError (0) for incoming requests.
-     log errors for mib-2 completeness and for debug purposes */
-  derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &m_stat->error_status);
-  if (derr != ERR_OK)
-  {
-    /* can't decode */
-    snmp_inc_snmpinasnparseerrs();
-    return ERR_ARG;
-  }
-  switch (m_stat->error_status)
-  {
-    case SNMP_ES_TOOBIG:
-      snmp_inc_snmpintoobigs();
-      break;
-    case SNMP_ES_NOSUCHNAME:
-      snmp_inc_snmpinnosuchnames();
-      break;
-    case SNMP_ES_BADVALUE:
-      snmp_inc_snmpinbadvalues();
-      break;
-    case SNMP_ES_READONLY:
-      snmp_inc_snmpinreadonlys();
-      break;
-    case SNMP_ES_GENERROR:
-      snmp_inc_snmpingenerrs();
-      break;
-  }
-  ofs += (1 + len_octets + len);
-  snmp_asn1_dec_type(p, ofs, &type);
-  derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
-  if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)))
-  {
-    /* can't decode or no integer (error-index) */
-    snmp_inc_snmpinasnparseerrs();
-    return ERR_ARG;
-  }
-  /* must be 0 for incoming requests.
-     decode anyway to catch bad integers (and dirty tricks) */
-  derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &m_stat->error_index);
-  if (derr != ERR_OK)
-  {
-    /* can't decode */
-    snmp_inc_snmpinasnparseerrs();
-    return ERR_ARG;
-  }
-  ofs += (1 + len_octets + len);
-  *ofs_ret = ofs;
-  return ERR_OK;
-}
-
-static err_t
-snmp_pdu_dec_varbindlist(struct pbuf *p, u16_t ofs, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat)
-{
-  err_t derr;
-  u16_t len, vb_len;
-  u8_t  len_octets;
-  u8_t type;
-
-  /* variable binding list */
-  snmp_asn1_dec_type(p, ofs, &type);
-  derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &vb_len);
-  if ((derr != ERR_OK) ||
-      (type != (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)))
-  {
-    snmp_inc_snmpinasnparseerrs();
-    return ERR_ARG;
-  }
-  ofs += (1 + len_octets);
-
-  /* start with empty list */
-  m_stat->invb.count = 0;
-  m_stat->invb.head = NULL;
-  m_stat->invb.tail = NULL;
-
-  while (vb_len > 0)
-  {
-    struct snmp_obj_id oid, oid_value;
-    struct snmp_varbind *vb;
-
-    snmp_asn1_dec_type(p, ofs, &type);
-    derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
-    if ((derr != ERR_OK) ||
-        (type != (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)) ||
-        (len == 0) || (len > vb_len))
-    {
-      snmp_inc_snmpinasnparseerrs();
-      /* free varbinds (if available) */
-      snmp_varbind_list_free(&m_stat->invb);
-      return ERR_ARG;
-    }
-    ofs += (1 + len_octets);
-    vb_len -= (1 + len_octets);
-
-    snmp_asn1_dec_type(p, ofs, &type);
-    derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
-    if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID)))
-    {
-      /* can't decode object name length */
-      snmp_inc_snmpinasnparseerrs();
-      /* free varbinds (if available) */
-      snmp_varbind_list_free(&m_stat->invb);
-      return ERR_ARG;
-    }
-    derr = snmp_asn1_dec_oid(p, ofs + 1 + len_octets, len, &oid);
-    if (derr != ERR_OK)
-    {
-      /* can't decode object name */
-      snmp_inc_snmpinasnparseerrs();
-      /* free varbinds (if available) */
-      snmp_varbind_list_free(&m_stat->invb);
-      return ERR_ARG;
-    }
-    ofs += (1 + len_octets + len);
-    vb_len -= (1 + len_octets + len);
-
-    snmp_asn1_dec_type(p, ofs, &type);
-    derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
-    if (derr != ERR_OK)
-    {
-      /* can't decode object value length */
-      snmp_inc_snmpinasnparseerrs();
-      /* free varbinds (if available) */
-      snmp_varbind_list_free(&m_stat->invb);
-      return ERR_ARG;
-    }
-
-    switch (type)
-    {
-      case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG):
-        vb = snmp_varbind_alloc(&oid, type, sizeof(s32_t));
-        if (vb != NULL)
-        {
-          s32_t *vptr = (s32_t*)vb->value;
-
-          derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, vptr);
-          snmp_varbind_tail_add(&m_stat->invb, vb);
-        }
-        else
-        {
-          derr = ERR_ARG;
-        }
-        break;
-      case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER):
-      case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_GAUGE):
-      case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS):
-        vb = snmp_varbind_alloc(&oid, type, sizeof(u32_t));
-        if (vb != NULL)
-        {
-          u32_t *vptr = (u32_t*)vb->value;
-
-          derr = snmp_asn1_dec_u32t(p, ofs + 1 + len_octets, len, vptr);
-          snmp_varbind_tail_add(&m_stat->invb, vb);
-        }
-        else
-        {
-          derr = ERR_ARG;
-        }
-        break;
-      case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR):
-      case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_OPAQUE):
-        LWIP_ASSERT("invalid length", len <= 0xff);
-        vb = snmp_varbind_alloc(&oid, type, (u8_t)len);
-        if (vb != NULL)
-        {
-          derr = snmp_asn1_dec_raw(p, ofs + 1 + len_octets, len, vb->value_len, (u8_t*)vb->value);
-          snmp_varbind_tail_add(&m_stat->invb, vb);
-        }
-        else
-        {
-          derr = ERR_ARG;
-        }
-        break;
-      case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_NUL):
-        vb = snmp_varbind_alloc(&oid, type, 0);
-        if (vb != NULL)
-        {
-          snmp_varbind_tail_add(&m_stat->invb, vb);
-          derr = ERR_OK;
-        }
-        else
-        {
-          derr = ERR_ARG;
-        }
-        break;
-      case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID):
-        derr = snmp_asn1_dec_oid(p, ofs + 1 + len_octets, len, &oid_value);
-        if (derr == ERR_OK)
-        {
-          vb = snmp_varbind_alloc(&oid, type, oid_value.len * sizeof(s32_t));
-          if (vb != NULL)
-          {
-            u8_t i = oid_value.len;
-            s32_t *vptr = (s32_t*)vb->value;
-
-            while(i > 0)
-            {
-              i--;
-              vptr[i] = oid_value.id[i];
-            }
-            snmp_varbind_tail_add(&m_stat->invb, vb);
-            derr = ERR_OK;
-          }
-          else
-          {
-            derr = ERR_ARG;
-          }
-        }
-        break;
-      case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR):
-        if (len == 4)
-        {
-          /* must be exactly 4 octets! */
-          vb = snmp_varbind_alloc(&oid, type, 4);
-          if (vb != NULL)
-          {
-            derr = snmp_asn1_dec_raw(p, ofs + 1 + len_octets, len, vb->value_len, (u8_t*)vb->value);
-            snmp_varbind_tail_add(&m_stat->invb, vb);
-          }
-          else
-          {
-            derr = ERR_ARG;
-          }
-        }
-        else
-        {
-          derr = ERR_ARG;
-        }
-        break;
-      default:
-        derr = ERR_ARG;
-        break;
-    }
-    if (derr != ERR_OK)
-    {
-      snmp_inc_snmpinasnparseerrs();
-      /* free varbinds (if available) */
-      snmp_varbind_list_free(&m_stat->invb);
-      return ERR_ARG;
-    }
-    ofs += (1 + len_octets + len);
-    vb_len -= (1 + len_octets + len);
-  }
-
-  if (m_stat->rt == SNMP_ASN1_PDU_SET_REQ)
-  {
-    snmp_add_snmpintotalsetvars(m_stat->invb.count);
-  }
-  else
-  {
-    snmp_add_snmpintotalreqvars(m_stat->invb.count);
-  }
-
-  *ofs_ret = ofs;
-  return ERR_OK;
-}
-
-struct snmp_varbind*
-snmp_varbind_alloc(struct snmp_obj_id *oid, u8_t type, u8_t len)
-{
-  struct snmp_varbind *vb;
-
-  vb = (struct snmp_varbind *)memp_malloc(MEMP_SNMP_VARBIND);
-  if (vb != NULL)
-  {
-    u8_t i;
-
-    vb->next = NULL;
-    vb->prev = NULL;
-    i = oid->len;
-    vb->ident_len = i;
-    if (i > 0)
-    {
-      LWIP_ASSERT("SNMP_MAX_TREE_DEPTH is configured too low", i <= SNMP_MAX_TREE_DEPTH);
-      /* allocate array of s32_t for our object identifier */
-      vb->ident = (s32_t*)memp_malloc(MEMP_SNMP_VALUE);
-      if (vb->ident == NULL)
-      {
-        LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_varbind_alloc: couldn't allocate ident value space\n"));
-        memp_free(MEMP_SNMP_VARBIND, vb);
-        return NULL;
-      }
-      while(i > 0)
-      {
-        i--;
-        vb->ident[i] = oid->id[i];
-      }
-    }
-    else
-    {
-      /* i == 0, pass zero length object identifier */
-      vb->ident = NULL;
-    }
-    vb->value_type = type;
-    vb->value_len = len;
-    if (len > 0)
-    {
-      LWIP_ASSERT("SNMP_MAX_OCTET_STRING_LEN is configured too low", vb->value_len <= SNMP_MAX_VALUE_SIZE);
-      /* allocate raw bytes for our object value */
-      vb->value = memp_malloc(MEMP_SNMP_VALUE);
-      if (vb->value == NULL)
-      {
-        LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_varbind_alloc: couldn't allocate value space\n"));
-        if (vb->ident != NULL)
-        {
-          memp_free(MEMP_SNMP_VALUE, vb->ident);
-        }
-        memp_free(MEMP_SNMP_VARBIND, vb);
-        return NULL;
-      }
-    }
-    else
-    {
-      /* ASN1_NUL type, or zero length ASN1_OC_STR */
-      vb->value = NULL;
-    }
-  }
-  else
-  {
-    LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_varbind_alloc: couldn't allocate varbind space\n"));
-  }
-  return vb;
-}
-
-void
-snmp_varbind_free(struct snmp_varbind *vb)
-{
-  if (vb->value != NULL )
-  {
-    memp_free(MEMP_SNMP_VALUE, vb->value);
-  }
-  if (vb->ident != NULL )
-  {
-    memp_free(MEMP_SNMP_VALUE, vb->ident);
-  }
-  memp_free(MEMP_SNMP_VARBIND, vb);
-}
-
-void
-snmp_varbind_list_free(struct snmp_varbind_root *root)
-{
-  struct snmp_varbind *vb, *prev;
-
-  vb = root->tail;
-  while ( vb != NULL )
-  {
-    prev = vb->prev;
-    snmp_varbind_free(vb);
-    vb = prev;
-  }
-  root->count = 0;
-  root->head = NULL;
-  root->tail = NULL;
-}
-
-void
-snmp_varbind_tail_add(struct snmp_varbind_root *root, struct snmp_varbind *vb)
-{
-  if (root->count == 0)
-  {
-    /* add first varbind to list */
-    root->head = vb;
-    root->tail = vb;
-  }
-  else
-  {
-    /* add nth varbind to list tail */
-    root->tail->next = vb;
-    vb->prev = root->tail;
-    root->tail = vb;
-  }
-  root->count += 1;
-}
-
-struct snmp_varbind*
-snmp_varbind_tail_remove(struct snmp_varbind_root *root)
-{
-  struct snmp_varbind* vb;
-
-  if (root->count > 0)
-  {
-    /* remove tail varbind */
-    vb = root->tail;
-    root->tail = vb->prev;
-    vb->prev->next = NULL;
-    root->count -= 1;
-  }
-  else
-  {
-    /* nothing to remove */
-    vb = NULL;
-  }
-  return vb;
-}
-
-#endif /* LWIP_SNMP */

+ 0 - 674
thirdparty/lwip/src/core/snmp/msg_out.c

@@ -1,674 +0,0 @@
-/**
- * @file
- * SNMP output message processing (RFC1157).
- *
- * Output responses and traps are build in two passes:
- *
- * Pass 0: iterate over the output message backwards to determine encoding lengths
- * Pass 1: the actual forward encoding of internal form into ASN1
- *
- * The single-pass encoding method described by Comer & Stevens
- * requires extra buffer space and copying for reversal of the packet.
- * The buffer requirement can be prohibitively large for big payloads
- * (>= 484) therefore we use the two encoding passes.
- */
-
-/*
- * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * Author: Christiaan Simons <christiaan.simons@axon.tv>
- */
-
-#include "lwip/opt.h"
-
-#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
-
-#include "lwip/udp.h"
-#include "lwip/netif.h"
-#include "lwip/snmp.h"
-#include "lwip/snmp_asn1.h"
-#include "lwip/snmp_msg.h"
-
-struct snmp_trap_dst
-{
-  /* destination IP address in network order */
-  ip_addr_t dip;
-  /* set to 0 when disabled, >0 when enabled */
-  u8_t enable;
-};
-struct snmp_trap_dst trap_dst[SNMP_TRAP_DESTINATIONS];
-
-/** TRAP message structure */
-struct snmp_msg_trap trap_msg;
-
-static u16_t snmp_resp_header_sum(struct snmp_msg_pstat *m_stat, u16_t vb_len);
-static u16_t snmp_trap_header_sum(struct snmp_msg_trap *m_trap, u16_t vb_len);
-static u16_t snmp_varbind_list_sum(struct snmp_varbind_root *root);
-
-static u16_t snmp_resp_header_enc(struct snmp_msg_pstat *m_stat, struct pbuf *p);
-static u16_t snmp_trap_header_enc(struct snmp_msg_trap *m_trap, struct pbuf *p);
-static u16_t snmp_varbind_list_enc(struct snmp_varbind_root *root, struct pbuf *p, u16_t ofs);
-
-/**
- * Sets enable switch for this trap destination.
- * @param dst_idx index in 0 .. SNMP_TRAP_DESTINATIONS-1
- * @param enable switch if 0 destination is disabled >0 enabled.
- */
-void
-snmp_trap_dst_enable(u8_t dst_idx, u8_t enable)
-{
-  if (dst_idx < SNMP_TRAP_DESTINATIONS)
-  {
-    trap_dst[dst_idx].enable = enable;
-  }
-}
-
-/**
- * Sets IPv4 address for this trap destination.
- * @param dst_idx index in 0 .. SNMP_TRAP_DESTINATIONS-1
- * @param dst IPv4 address in host order.
- */
-void
-snmp_trap_dst_ip_set(u8_t dst_idx, ip_addr_t *dst)
-{
-  if (dst_idx < SNMP_TRAP_DESTINATIONS)
-  {
-    ip_addr_set(&trap_dst[dst_idx].dip, dst);
-  }
-}
-
-/**
- * Sends a 'getresponse' message to the request originator.
- *
- * @param m_stat points to the current message request state source
- * @return ERR_OK when success, ERR_MEM if we're out of memory
- *
- * @note the caller is responsible for filling in outvb in the m_stat
- * and provide error-status and index (except for tooBig errors) ...
- */
-err_t
-snmp_send_response(struct snmp_msg_pstat *m_stat)
-{
-  struct snmp_varbind_root emptyvb = {NULL, NULL, 0, 0, 0};
-  struct pbuf *p;
-  u16_t tot_len;
-  err_t err;
-
-  /* pass 0, calculate length fields */
-  tot_len = snmp_varbind_list_sum(&m_stat->outvb);
-  tot_len = snmp_resp_header_sum(m_stat, tot_len);
-
-  /* try allocating pbuf(s) for complete response */
-  p = pbuf_alloc(PBUF_TRANSPORT, tot_len, PBUF_POOL);
-  if (p == NULL)
-  {
-    LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_snd_response() tooBig\n"));
-
-    /* can't construct reply, return error-status tooBig */
-    m_stat->error_status = SNMP_ES_TOOBIG;
-    m_stat->error_index = 0;
-    /* pass 0, recalculate lengths, for empty varbind-list */
-    tot_len = snmp_varbind_list_sum(&emptyvb);
-    tot_len = snmp_resp_header_sum(m_stat, tot_len);
-    /* retry allocation once for header and empty varbind-list */
-    p = pbuf_alloc(PBUF_TRANSPORT, tot_len, PBUF_POOL);
-  }
-  if (p != NULL)
-  {
-    /* first pbuf alloc try or retry alloc success */
-    u16_t ofs;
-
-    LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_snd_response() p != NULL\n"));
-
-    /* pass 1, size error, encode packet ino the pbuf(s) */
-    ofs = snmp_resp_header_enc(m_stat, p);
-    snmp_varbind_list_enc(&m_stat->outvb, p, ofs);
-
-    switch (m_stat->error_status)
-    {
-      case SNMP_ES_TOOBIG:
-        snmp_inc_snmpouttoobigs();
-        break;
-      case SNMP_ES_NOSUCHNAME:
-        snmp_inc_snmpoutnosuchnames();
-        break;
-      case SNMP_ES_BADVALUE:
-        snmp_inc_snmpoutbadvalues();
-        break;
-      case SNMP_ES_GENERROR:
-        snmp_inc_snmpoutgenerrs();
-        break;
-    }
-    snmp_inc_snmpoutgetresponses();
-    snmp_inc_snmpoutpkts();
-
-    /** @todo do we need separate rx and tx pcbs for threaded case? */
-    /** connect to the originating source */
-    udp_connect(m_stat->pcb, &m_stat->sip, m_stat->sp);
-    err = udp_send(m_stat->pcb, p);
-    if (err == ERR_MEM)
-    {
-      /** @todo release some memory, retry and return tooBig? tooMuchHassle? */
-      err = ERR_MEM;
-    }
-    else
-    {
-      err = ERR_OK;
-    }
-    /** disassociate remote address and port with this pcb */
-    udp_disconnect(m_stat->pcb);
-
-    pbuf_free(p);
-    LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_snd_response() done\n"));
-    return err;
-  }
-  else
-  {
-    /* first pbuf alloc try or retry alloc failed
-       very low on memory, couldn't return tooBig */
-    return ERR_MEM;
-  }
-}
-
-
-/**
- * Sends an generic or enterprise specific trap message.
- *
- * @param generic_trap is the trap code
- * @param eoid points to enterprise object identifier
- * @param specific_trap used for enterprise traps when generic_trap == 6
- * @return ERR_OK when success, ERR_MEM if we're out of memory
- *
- * @note the caller is responsible for filling in outvb in the trap_msg
- * @note the use of the enterpise identifier field
- * is per RFC1215.
- * Use .iso.org.dod.internet.mgmt.mib-2.snmp for generic traps
- * and .iso.org.dod.internet.private.enterprises.yourenterprise
- * (sysObjectID) for specific traps.
- */
-err_t
-snmp_send_trap(s8_t generic_trap, struct snmp_obj_id *eoid, s32_t specific_trap)
-{
-  struct snmp_trap_dst *td;
-  struct netif *dst_if;
-  ip_addr_t dst_ip;
-  struct pbuf *p;
-  u16_t i,tot_len;
-
-  for (i=0, td = &trap_dst[0]; i<SNMP_TRAP_DESTINATIONS; i++, td++)
-  {
-    if ((td->enable != 0) && !ip_addr_isany(&td->dip))
-    {
-      /* network order trap destination */
-      ip_addr_copy(trap_msg.dip, td->dip);
-      /* lookup current source address for this dst */
-      dst_if = ip_route(&td->dip);
-      ip_addr_copy(dst_ip, dst_if->ip_addr);
-      /* @todo: what about IPv6? */
-      trap_msg.sip_raw[0] = ip4_addr1(&dst_ip);
-      trap_msg.sip_raw[1] = ip4_addr2(&dst_ip);
-      trap_msg.sip_raw[2] = ip4_addr3(&dst_ip);
-      trap_msg.sip_raw[3] = ip4_addr4(&dst_ip);
-      trap_msg.gen_trap = generic_trap;
-      trap_msg.spc_trap = specific_trap;
-      if (generic_trap == SNMP_GENTRAP_ENTERPRISESPC)
-      {
-        /* enterprise-Specific trap */
-        trap_msg.enterprise = eoid;
-      }
-      else
-      {
-        /* generic (MIB-II) trap */
-        snmp_get_snmpgrpid_ptr(&trap_msg.enterprise);
-      }
-      snmp_get_sysuptime(&trap_msg.ts);
-
-      /* pass 0, calculate length fields */
-      tot_len = snmp_varbind_list_sum(&trap_msg.outvb);
-      tot_len = snmp_trap_header_sum(&trap_msg, tot_len);
-
-      /* allocate pbuf(s) */
-      p = pbuf_alloc(PBUF_TRANSPORT, tot_len, PBUF_POOL);
-      if (p != NULL)
-      {
-        u16_t ofs;
-
-        /* pass 1, encode packet ino the pbuf(s) */
-        ofs = snmp_trap_header_enc(&trap_msg, p);
-        snmp_varbind_list_enc(&trap_msg.outvb, p, ofs);
-
-        snmp_inc_snmpouttraps();
-        snmp_inc_snmpoutpkts();
-
-        /** send to the TRAP destination */
-        udp_sendto(trap_msg.pcb, p, &trap_msg.dip, SNMP_TRAP_PORT);
-
-        pbuf_free(p);
-      }
-      else
-      {
-        return ERR_MEM;
-      }
-    }
-  }
-  return ERR_OK;
-}
-
-void
-snmp_coldstart_trap(void)
-{
-  trap_msg.outvb.head = NULL;
-  trap_msg.outvb.tail = NULL;
-  trap_msg.outvb.count = 0;
-  snmp_send_trap(SNMP_GENTRAP_COLDSTART, NULL, 0);
-}
-
-void
-snmp_authfail_trap(void)
-{
-  u8_t enable;
-  snmp_get_snmpenableauthentraps(&enable);
-  if (enable == 1)
-  {
-    trap_msg.outvb.head = NULL;
-    trap_msg.outvb.tail = NULL;
-    trap_msg.outvb.count = 0;
-    snmp_send_trap(SNMP_GENTRAP_AUTHFAIL, NULL, 0);
-  }
-}
-
-/**
- * Sums response header field lengths from tail to head and
- * returns resp_header_lengths for second encoding pass.
- *
- * @param vb_len varbind-list length
- * @param rhl points to returned header lengths
- * @return the required lenght for encoding the response header
- */
-static u16_t
-snmp_resp_header_sum(struct snmp_msg_pstat *m_stat, u16_t vb_len)
-{
-  u16_t tot_len;
-  struct snmp_resp_header_lengths *rhl;
-
-  rhl = &m_stat->rhl;
-  tot_len = vb_len;
-  snmp_asn1_enc_s32t_cnt(m_stat->error_index, &rhl->erridxlen);
-  snmp_asn1_enc_length_cnt(rhl->erridxlen, &rhl->erridxlenlen);
-  tot_len += 1 + rhl->erridxlenlen + rhl->erridxlen;
-
-  snmp_asn1_enc_s32t_cnt(m_stat->error_status, &rhl->errstatlen);
-  snmp_asn1_enc_length_cnt(rhl->errstatlen, &rhl->errstatlenlen);
-  tot_len += 1 + rhl->errstatlenlen + rhl->errstatlen;
-
-  snmp_asn1_enc_s32t_cnt(m_stat->rid, &rhl->ridlen);
-  snmp_asn1_enc_length_cnt(rhl->ridlen, &rhl->ridlenlen);
-  tot_len += 1 + rhl->ridlenlen + rhl->ridlen;
-
-  rhl->pdulen = tot_len;
-  snmp_asn1_enc_length_cnt(rhl->pdulen, &rhl->pdulenlen);
-  tot_len += 1 + rhl->pdulenlen;
-
-  rhl->comlen = m_stat->com_strlen;
-  snmp_asn1_enc_length_cnt(rhl->comlen, &rhl->comlenlen);
-  tot_len += 1 + rhl->comlenlen + rhl->comlen;
-
-  snmp_asn1_enc_s32t_cnt(snmp_version, &rhl->verlen);
-  snmp_asn1_enc_length_cnt(rhl->verlen, &rhl->verlenlen);
-  tot_len += 1 + rhl->verlen + rhl->verlenlen;
-
-  rhl->seqlen = tot_len;
-  snmp_asn1_enc_length_cnt(rhl->seqlen, &rhl->seqlenlen);
-  tot_len += 1 + rhl->seqlenlen;
-
-  return tot_len;
-}
-
-/**
- * Sums trap header field lengths from tail to head and
- * returns trap_header_lengths for second encoding pass.
- *
- * @param vb_len varbind-list length
- * @param thl points to returned header lengths
- * @return the required lenght for encoding the trap header
- */
-static u16_t
-snmp_trap_header_sum(struct snmp_msg_trap *m_trap, u16_t vb_len)
-{
-  u16_t tot_len;
-  struct snmp_trap_header_lengths *thl;
-
-  thl = &m_trap->thl;
-  tot_len = vb_len;
-
-  snmp_asn1_enc_u32t_cnt(m_trap->ts, &thl->tslen);
-  snmp_asn1_enc_length_cnt(thl->tslen, &thl->tslenlen);
-  tot_len += 1 + thl->tslen + thl->tslenlen;
-
-  snmp_asn1_enc_s32t_cnt(m_trap->spc_trap, &thl->strplen);
-  snmp_asn1_enc_length_cnt(thl->strplen, &thl->strplenlen);
-  tot_len += 1 + thl->strplen + thl->strplenlen;
-
-  snmp_asn1_enc_s32t_cnt(m_trap->gen_trap, &thl->gtrplen);
-  snmp_asn1_enc_length_cnt(thl->gtrplen, &thl->gtrplenlen);
-  tot_len += 1 + thl->gtrplen + thl->gtrplenlen;
-
-  thl->aaddrlen = 4;
-  snmp_asn1_enc_length_cnt(thl->aaddrlen, &thl->aaddrlenlen);
-  tot_len += 1 + thl->aaddrlen + thl->aaddrlenlen;
-
-  snmp_asn1_enc_oid_cnt(m_trap->enterprise->len, &m_trap->enterprise->id[0], &thl->eidlen);
-  snmp_asn1_enc_length_cnt(thl->eidlen, &thl->eidlenlen);
-  tot_len += 1 + thl->eidlen + thl->eidlenlen;
-
-  thl->pdulen = tot_len;
-  snmp_asn1_enc_length_cnt(thl->pdulen, &thl->pdulenlen);
-  tot_len += 1 + thl->pdulenlen;
-
-  thl->comlen = sizeof(snmp_publiccommunity) - 1;
-  snmp_asn1_enc_length_cnt(thl->comlen, &thl->comlenlen);
-  tot_len += 1 + thl->comlenlen + thl->comlen;
-
-  snmp_asn1_enc_s32t_cnt(snmp_version, &thl->verlen);
-  snmp_asn1_enc_length_cnt(thl->verlen, &thl->verlenlen);
-  tot_len += 1 + thl->verlen + thl->verlenlen;
-
-  thl->seqlen = tot_len;
-  snmp_asn1_enc_length_cnt(thl->seqlen, &thl->seqlenlen);
-  tot_len += 1 + thl->seqlenlen;
-
-  return tot_len;
-}
-
-/**
- * Sums varbind lengths from tail to head and
- * annotates lengths in varbind for second encoding pass.
- *
- * @param root points to the root of the variable binding list
- * @return the required lenght for encoding the variable bindings
- */
-static u16_t
-snmp_varbind_list_sum(struct snmp_varbind_root *root)
-{
-  struct snmp_varbind *vb;
-  u32_t *uint_ptr;
-  s32_t *sint_ptr;
-  u16_t tot_len;
-
-  tot_len = 0;
-  vb = root->tail;
-  while ( vb != NULL )
-  {
-    /* encoded value lenght depends on type */
-    switch (vb->value_type)
-    {
-      case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG):
-        sint_ptr = (s32_t*)vb->value;
-        snmp_asn1_enc_s32t_cnt(*sint_ptr, &vb->vlen);
-        break;
-      case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER):
-      case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_GAUGE):
-      case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS):
-        uint_ptr = (u32_t*)vb->value;
-        snmp_asn1_enc_u32t_cnt(*uint_ptr, &vb->vlen);
-        break;
-      case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR):
-      case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_NUL):
-      case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR):
-      case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_OPAQUE):
-        vb->vlen = vb->value_len;
-        break;
-      case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID):
-        sint_ptr = (s32_t*)vb->value;
-        snmp_asn1_enc_oid_cnt(vb->value_len / sizeof(s32_t), sint_ptr, &vb->vlen);
-        break;
-      default:
-        /* unsupported type */
-        vb->vlen = 0;
-        break;
-    };
-    /* encoding length of value length field */
-    snmp_asn1_enc_length_cnt(vb->vlen, &vb->vlenlen);
-    snmp_asn1_enc_oid_cnt(vb->ident_len, vb->ident, &vb->olen);
-    snmp_asn1_enc_length_cnt(vb->olen, &vb->olenlen);
-
-    vb->seqlen = 1 + vb->vlenlen + vb->vlen;
-    vb->seqlen += 1 + vb->olenlen + vb->olen;
-    snmp_asn1_enc_length_cnt(vb->seqlen, &vb->seqlenlen);
-
-    /* varbind seq */
-    tot_len += 1 + vb->seqlenlen + vb->seqlen;
-
-    vb = vb->prev;
-  }
-
-  /* varbind-list seq */
-  root->seqlen = tot_len;
-  snmp_asn1_enc_length_cnt(root->seqlen, &root->seqlenlen);
-  tot_len += 1 + root->seqlenlen;
-
-  return tot_len;
-}
-
-/**
- * Encodes response header from head to tail.
- */
-static u16_t
-snmp_resp_header_enc(struct snmp_msg_pstat *m_stat, struct pbuf *p)
-{
-  u16_t ofs;
-
-  ofs = 0;
-  snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ));
-  ofs += 1;
-  snmp_asn1_enc_length(p, ofs, m_stat->rhl.seqlen);
-  ofs += m_stat->rhl.seqlenlen;
-
-  snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG));
-  ofs += 1;
-  snmp_asn1_enc_length(p, ofs, m_stat->rhl.verlen);
-  ofs += m_stat->rhl.verlenlen;
-  snmp_asn1_enc_s32t(p, ofs, m_stat->rhl.verlen, snmp_version);
-  ofs += m_stat->rhl.verlen;
-
-  snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR));
-  ofs += 1;
-  snmp_asn1_enc_length(p, ofs, m_stat->rhl.comlen);
-  ofs += m_stat->rhl.comlenlen;
-  snmp_asn1_enc_raw(p, ofs, m_stat->rhl.comlen, m_stat->community);
-  ofs += m_stat->rhl.comlen;
-
-  snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_RESP));
-  ofs += 1;
-  snmp_asn1_enc_length(p, ofs, m_stat->rhl.pdulen);
-  ofs += m_stat->rhl.pdulenlen;
-
-  snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG));
-  ofs += 1;
-  snmp_asn1_enc_length(p, ofs, m_stat->rhl.ridlen);
-  ofs += m_stat->rhl.ridlenlen;
-  snmp_asn1_enc_s32t(p, ofs, m_stat->rhl.ridlen, m_stat->rid);
-  ofs += m_stat->rhl.ridlen;
-
-  snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG));
-  ofs += 1;
-  snmp_asn1_enc_length(p, ofs, m_stat->rhl.errstatlen);
-  ofs += m_stat->rhl.errstatlenlen;
-  snmp_asn1_enc_s32t(p, ofs, m_stat->rhl.errstatlen, m_stat->error_status);
-  ofs += m_stat->rhl.errstatlen;
-
-  snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG));
-  ofs += 1;
-  snmp_asn1_enc_length(p, ofs, m_stat->rhl.erridxlen);
-  ofs += m_stat->rhl.erridxlenlen;
-  snmp_asn1_enc_s32t(p, ofs, m_stat->rhl.erridxlen, m_stat->error_index);
-  ofs += m_stat->rhl.erridxlen;
-
-  return ofs;
-}
-
-/**
- * Encodes trap header from head to tail.
- */
-static u16_t
-snmp_trap_header_enc(struct snmp_msg_trap *m_trap, struct pbuf *p)
-{
-  u16_t ofs;
-
-  ofs = 0;
-  snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ));
-  ofs += 1;
-  snmp_asn1_enc_length(p, ofs, m_trap->thl.seqlen);
-  ofs += m_trap->thl.seqlenlen;
-
-  snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG));
-  ofs += 1;
-  snmp_asn1_enc_length(p, ofs, m_trap->thl.verlen);
-  ofs += m_trap->thl.verlenlen;
-  snmp_asn1_enc_s32t(p, ofs, m_trap->thl.verlen, snmp_version);
-  ofs += m_trap->thl.verlen;
-
-  snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR));
-  ofs += 1;
-  snmp_asn1_enc_length(p, ofs, m_trap->thl.comlen);
-  ofs += m_trap->thl.comlenlen;
-  snmp_asn1_enc_raw(p, ofs, m_trap->thl.comlen, (u8_t *)&snmp_publiccommunity[0]);
-  ofs += m_trap->thl.comlen;
-
-  snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_TRAP));
-  ofs += 1;
-  snmp_asn1_enc_length(p, ofs, m_trap->thl.pdulen);
-  ofs += m_trap->thl.pdulenlen;
-
-  snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID));
-  ofs += 1;
-  snmp_asn1_enc_length(p, ofs, m_trap->thl.eidlen);
-  ofs += m_trap->thl.eidlenlen;
-  snmp_asn1_enc_oid(p, ofs, m_trap->enterprise->len, &m_trap->enterprise->id[0]);
-  ofs += m_trap->thl.eidlen;
-
-  snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR));
-  ofs += 1;
-  snmp_asn1_enc_length(p, ofs, m_trap->thl.aaddrlen);
-  ofs += m_trap->thl.aaddrlenlen;
-  snmp_asn1_enc_raw(p, ofs, m_trap->thl.aaddrlen, &m_trap->sip_raw[0]);
-  ofs += m_trap->thl.aaddrlen;
-
-  snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG));
-  ofs += 1;
-  snmp_asn1_enc_length(p, ofs, m_trap->thl.gtrplen);
-  ofs += m_trap->thl.gtrplenlen;
-  snmp_asn1_enc_u32t(p, ofs, m_trap->thl.gtrplen, m_trap->gen_trap);
-  ofs += m_trap->thl.gtrplen;
-
-  snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG));
-  ofs += 1;
-  snmp_asn1_enc_length(p, ofs, m_trap->thl.strplen);
-  ofs += m_trap->thl.strplenlen;
-  snmp_asn1_enc_u32t(p, ofs, m_trap->thl.strplen, m_trap->spc_trap);
-  ofs += m_trap->thl.strplen;
-
-  snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS));
-  ofs += 1;
-  snmp_asn1_enc_length(p, ofs, m_trap->thl.tslen);
-  ofs += m_trap->thl.tslenlen;
-  snmp_asn1_enc_u32t(p, ofs, m_trap->thl.tslen, m_trap->ts);
-  ofs += m_trap->thl.tslen;
-
-  return ofs;
-}
-
-/**
- * Encodes varbind list from head to tail.
- */
-static u16_t
-snmp_varbind_list_enc(struct snmp_varbind_root *root, struct pbuf *p, u16_t ofs)
-{
-  struct snmp_varbind *vb;
-  s32_t *sint_ptr;
-  u32_t *uint_ptr;
-  u8_t *raw_ptr;
-
-  snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ));
-  ofs += 1;
-  snmp_asn1_enc_length(p, ofs, root->seqlen);
-  ofs += root->seqlenlen;
-
-  vb = root->head;
-  while ( vb != NULL )
-  {
-    snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ));
-    ofs += 1;
-    snmp_asn1_enc_length(p, ofs, vb->seqlen);
-    ofs += vb->seqlenlen;
-
-    snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID));
-    ofs += 1;
-    snmp_asn1_enc_length(p, ofs, vb->olen);
-    ofs += vb->olenlen;
-    snmp_asn1_enc_oid(p, ofs, vb->ident_len, &vb->ident[0]);
-    ofs += vb->olen;
-
-    snmp_asn1_enc_type(p, ofs, vb->value_type);
-    ofs += 1;
-    snmp_asn1_enc_length(p, ofs, vb->vlen);
-    ofs += vb->vlenlen;
-
-    switch (vb->value_type)
-    {
-      case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG):
-        sint_ptr = (s32_t*)vb->value;
-        snmp_asn1_enc_s32t(p, ofs, vb->vlen, *sint_ptr);
-        break;
-      case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER):
-      case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_GAUGE):
-      case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS):
-        uint_ptr = (u32_t*)vb->value;
-        snmp_asn1_enc_u32t(p, ofs, vb->vlen, *uint_ptr);
-        break;
-      case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR):
-      case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR):
-      case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_OPAQUE):
-        raw_ptr = (u8_t*)vb->value;
-        snmp_asn1_enc_raw(p, ofs, vb->vlen, raw_ptr);
-        break;
-      case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_NUL):
-        break;
-      case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID):
-        sint_ptr = (s32_t*)vb->value;
-        snmp_asn1_enc_oid(p, ofs, vb->value_len / sizeof(s32_t), sint_ptr);
-        break;
-      default:
-        /* unsupported type */
-        break;
-    };
-    ofs += vb->vlen;
-    vb = vb->next;
-  }
-  return ofs;
-}
-
-#endif /* LWIP_SNMP */

+ 169 - 176
thirdparty/lwip/src/core/stats.c

@@ -1,176 +1,169 @@
-/**
- * @file
- * Statistics module
- *
- */
-
-/*
- * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
- * All rights reserved. 
- * 
- * Redistribution and use in source and binary forms, with or without modification, 
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission. 
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
- * OF SUCH DAMAGE.
- *
- * This file is part of the lwIP TCP/IP stack.
- * 
- * Author: Adam Dunkels <adam@sics.se>
- *
- */
-
-#include "lwip/opt.h"
-
-#if LWIP_STATS /* don't build if not configured for use in lwipopts.h */
-
-#include "lwip/def.h"
-#include "lwip/stats.h"
-#include "lwip/mem.h"
-
-#include <string.h>
-
-struct stats_ lwip_stats;
-
-void stats_init(void)
-{
-#ifdef LWIP_DEBUG
-#if MEMP_STATS
-  const char * memp_names[] = {
-#define LWIP_MEMPOOL(name,num,size,desc) desc,
-#include "lwip/memp_std.h"
-  };
-  int i;
-  for (i = 0; i < MEMP_MAX; i++) {
-    lwip_stats.memp[i].name = memp_names[i];
-  }
-#endif /* MEMP_STATS */
-#if MEM_STATS
-  lwip_stats.mem.name = "MEM";
-#endif /* MEM_STATS */
-#endif /* LWIP_DEBUG */
-}
-
-#if LWIP_STATS_DISPLAY
-void
-stats_display_proto(struct stats_proto *proto, const char *name)
-{
-  LWIP_PLATFORM_DIAG(("\n%s\n\t", name));
-  LWIP_PLATFORM_DIAG(("xmit: %"STAT_COUNTER_F"\n\t", proto->xmit)); 
-  LWIP_PLATFORM_DIAG(("recv: %"STAT_COUNTER_F"\n\t", proto->recv)); 
-  LWIP_PLATFORM_DIAG(("fw: %"STAT_COUNTER_F"\n\t", proto->fw)); 
-  LWIP_PLATFORM_DIAG(("drop: %"STAT_COUNTER_F"\n\t", proto->drop)); 
-  LWIP_PLATFORM_DIAG(("chkerr: %"STAT_COUNTER_F"\n\t", proto->chkerr)); 
-  LWIP_PLATFORM_DIAG(("lenerr: %"STAT_COUNTER_F"\n\t", proto->lenerr)); 
-  LWIP_PLATFORM_DIAG(("memerr: %"STAT_COUNTER_F"\n\t", proto->memerr)); 
-  LWIP_PLATFORM_DIAG(("rterr: %"STAT_COUNTER_F"\n\t", proto->rterr)); 
-  LWIP_PLATFORM_DIAG(("proterr: %"STAT_COUNTER_F"\n\t", proto->proterr)); 
-  LWIP_PLATFORM_DIAG(("opterr: %"STAT_COUNTER_F"\n\t", proto->opterr)); 
-  LWIP_PLATFORM_DIAG(("err: %"STAT_COUNTER_F"\n\t", proto->err)); 
-  LWIP_PLATFORM_DIAG(("cachehit: %"STAT_COUNTER_F"\n", proto->cachehit)); 
-}
-
-#if IGMP_STATS
-void
-stats_display_igmp(struct stats_igmp *igmp)
-{
-  LWIP_PLATFORM_DIAG(("\nIGMP\n\t"));
-  LWIP_PLATFORM_DIAG(("xmit: %"STAT_COUNTER_F"\n\t", igmp->xmit)); 
-  LWIP_PLATFORM_DIAG(("recv: %"STAT_COUNTER_F"\n\t", igmp->recv)); 
-  LWIP_PLATFORM_DIAG(("drop: %"STAT_COUNTER_F"\n\t", igmp->drop)); 
-  LWIP_PLATFORM_DIAG(("chkerr: %"STAT_COUNTER_F"\n\t", igmp->chkerr)); 
-  LWIP_PLATFORM_DIAG(("lenerr: %"STAT_COUNTER_F"\n\t", igmp->lenerr)); 
-  LWIP_PLATFORM_DIAG(("memerr: %"STAT_COUNTER_F"\n\t", igmp->memerr)); 
-  LWIP_PLATFORM_DIAG(("proterr: %"STAT_COUNTER_F"\n\t", igmp->proterr)); 
-  LWIP_PLATFORM_DIAG(("rx_v1: %"STAT_COUNTER_F"\n\t", igmp->rx_v1)); 
-  LWIP_PLATFORM_DIAG(("rx_group: %"STAT_COUNTER_F"\n", igmp->rx_group));
-  LWIP_PLATFORM_DIAG(("rx_general: %"STAT_COUNTER_F"\n", igmp->rx_general));
-  LWIP_PLATFORM_DIAG(("rx_report: %"STAT_COUNTER_F"\n\t", igmp->rx_report)); 
-  LWIP_PLATFORM_DIAG(("tx_join: %"STAT_COUNTER_F"\n\t", igmp->tx_join)); 
-  LWIP_PLATFORM_DIAG(("tx_leave: %"STAT_COUNTER_F"\n\t", igmp->tx_leave)); 
-  LWIP_PLATFORM_DIAG(("tx_report: %"STAT_COUNTER_F"\n\t", igmp->tx_report)); 
-}
-#endif /* IGMP_STATS */
-
-#if MEM_STATS || MEMP_STATS
-void
-stats_display_mem(struct stats_mem *mem, const char *name)
-{
-  LWIP_PLATFORM_DIAG(("\nMEM %s\n\t", name));
-  LWIP_PLATFORM_DIAG(("avail: %"U32_F"\n\t", (u32_t)mem->avail)); 
-  LWIP_PLATFORM_DIAG(("used: %"U32_F"\n\t", (u32_t)mem->used)); 
-  LWIP_PLATFORM_DIAG(("max: %"U32_F"\n\t", (u32_t)mem->max)); 
-  LWIP_PLATFORM_DIAG(("err: %"U32_F"\n", (u32_t)mem->err));
-}
-
-#if MEMP_STATS
-void
-stats_display_memp(struct stats_mem *mem, int index)
-{
-  char * memp_names[] = {
-#define LWIP_MEMPOOL(name,num,size,desc) desc,
-#include "lwip/memp_std.h"
-  };
-  if(index < MEMP_MAX) {
-    stats_display_mem(mem, memp_names[index]);
-  }
-}
-#endif /* MEMP_STATS */
-#endif /* MEM_STATS || MEMP_STATS */
-
-#if SYS_STATS
-void
-stats_display_sys(struct stats_sys *sys)
-{
-  LWIP_PLATFORM_DIAG(("\nSYS\n\t"));
-  LWIP_PLATFORM_DIAG(("sem.used:  %"U32_F"\n\t", (u32_t)sys->sem.used)); 
-  LWIP_PLATFORM_DIAG(("sem.max:   %"U32_F"\n\t", (u32_t)sys->sem.max)); 
-  LWIP_PLATFORM_DIAG(("sem.err:   %"U32_F"\n\t", (u32_t)sys->sem.err)); 
-  LWIP_PLATFORM_DIAG(("mutex.used: %"U32_F"\n\t", (u32_t)sys->mutex.used)); 
-  LWIP_PLATFORM_DIAG(("mutex.max:  %"U32_F"\n\t", (u32_t)sys->mutex.max)); 
-  LWIP_PLATFORM_DIAG(("mutex.err:  %"U32_F"\n\t", (u32_t)sys->mutex.err)); 
-  LWIP_PLATFORM_DIAG(("mbox.used:  %"U32_F"\n\t", (u32_t)sys->mbox.used)); 
-  LWIP_PLATFORM_DIAG(("mbox.max:   %"U32_F"\n\t", (u32_t)sys->mbox.max)); 
-  LWIP_PLATFORM_DIAG(("mbox.err:   %"U32_F"\n\t", (u32_t)sys->mbox.err)); 
-}
-#endif /* SYS_STATS */
-
-void
-stats_display(void)
-{
-  s16_t i;
-
-  LINK_STATS_DISPLAY();
-  ETHARP_STATS_DISPLAY();
-  IPFRAG_STATS_DISPLAY();
-  IP_STATS_DISPLAY();
-  IGMP_STATS_DISPLAY();
-  ICMP_STATS_DISPLAY();
-  UDP_STATS_DISPLAY();
-  TCP_STATS_DISPLAY();
-  MEM_STATS_DISPLAY();
-  for (i = 0; i < MEMP_MAX; i++) {
-    MEMP_STATS_DISPLAY(i);
-  }
-  SYS_STATS_DISPLAY();
-}
-#endif /* LWIP_STATS_DISPLAY */
-
-#endif /* LWIP_STATS */
-
+/**
+ * @file
+ * Statistics module
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_STATS /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/def.h"
+#include "lwip/stats.h"
+#include "lwip/mem.h"
+#include "lwip/debug.h"
+
+#include <string.h>
+
+struct stats_ lwip_stats;
+
+void
+stats_init(void)
+{
+#ifdef LWIP_DEBUG
+#if MEM_STATS
+  lwip_stats.mem.name = "MEM";
+#endif /* MEM_STATS */
+#endif /* LWIP_DEBUG */
+}
+
+#if LWIP_STATS_DISPLAY
+void
+stats_display_proto(struct stats_proto *proto, const char *name)
+{
+  LWIP_PLATFORM_DIAG(("\n%s\n\t", name));
+  LWIP_PLATFORM_DIAG(("xmit: %"STAT_COUNTER_F"\n\t", proto->xmit));
+  LWIP_PLATFORM_DIAG(("recv: %"STAT_COUNTER_F"\n\t", proto->recv));
+  LWIP_PLATFORM_DIAG(("fw: %"STAT_COUNTER_F"\n\t", proto->fw));
+  LWIP_PLATFORM_DIAG(("drop: %"STAT_COUNTER_F"\n\t", proto->drop));
+  LWIP_PLATFORM_DIAG(("chkerr: %"STAT_COUNTER_F"\n\t", proto->chkerr));
+  LWIP_PLATFORM_DIAG(("lenerr: %"STAT_COUNTER_F"\n\t", proto->lenerr));
+  LWIP_PLATFORM_DIAG(("memerr: %"STAT_COUNTER_F"\n\t", proto->memerr));
+  LWIP_PLATFORM_DIAG(("rterr: %"STAT_COUNTER_F"\n\t", proto->rterr));
+  LWIP_PLATFORM_DIAG(("proterr: %"STAT_COUNTER_F"\n\t", proto->proterr));
+  LWIP_PLATFORM_DIAG(("opterr: %"STAT_COUNTER_F"\n\t", proto->opterr));
+  LWIP_PLATFORM_DIAG(("err: %"STAT_COUNTER_F"\n\t", proto->err));
+  LWIP_PLATFORM_DIAG(("cachehit: %"STAT_COUNTER_F"\n", proto->cachehit));
+}
+
+#if IGMP_STATS || MLD6_STATS
+void
+stats_display_igmp(struct stats_igmp *igmp, const char *name)
+{
+  LWIP_PLATFORM_DIAG(("\n%s\n\t", name));
+  LWIP_PLATFORM_DIAG(("xmit: %"STAT_COUNTER_F"\n\t", igmp->xmit));
+  LWIP_PLATFORM_DIAG(("recv: %"STAT_COUNTER_F"\n\t", igmp->recv));
+  LWIP_PLATFORM_DIAG(("drop: %"STAT_COUNTER_F"\n\t", igmp->drop));
+  LWIP_PLATFORM_DIAG(("chkerr: %"STAT_COUNTER_F"\n\t", igmp->chkerr));
+  LWIP_PLATFORM_DIAG(("lenerr: %"STAT_COUNTER_F"\n\t", igmp->lenerr));
+  LWIP_PLATFORM_DIAG(("memerr: %"STAT_COUNTER_F"\n\t", igmp->memerr));
+  LWIP_PLATFORM_DIAG(("proterr: %"STAT_COUNTER_F"\n\t", igmp->proterr));
+  LWIP_PLATFORM_DIAG(("rx_v1: %"STAT_COUNTER_F"\n\t", igmp->rx_v1));
+  LWIP_PLATFORM_DIAG(("rx_group: %"STAT_COUNTER_F"\n\t", igmp->rx_group));
+  LWIP_PLATFORM_DIAG(("rx_general: %"STAT_COUNTER_F"\n\t", igmp->rx_general));
+  LWIP_PLATFORM_DIAG(("rx_report: %"STAT_COUNTER_F"\n\t", igmp->rx_report));
+  LWIP_PLATFORM_DIAG(("tx_join: %"STAT_COUNTER_F"\n\t", igmp->tx_join));
+  LWIP_PLATFORM_DIAG(("tx_leave: %"STAT_COUNTER_F"\n\t", igmp->tx_leave));
+  LWIP_PLATFORM_DIAG(("tx_report: %"STAT_COUNTER_F"\n", igmp->tx_report));
+}
+#endif /* IGMP_STATS || MLD6_STATS */
+
+#if MEM_STATS || MEMP_STATS
+void
+stats_display_mem(struct stats_mem *mem, const char *name)
+{
+  LWIP_PLATFORM_DIAG(("\nMEM %s\n\t", name));
+  LWIP_PLATFORM_DIAG(("avail: %"U32_F"\n\t", (u32_t)mem->avail));
+  LWIP_PLATFORM_DIAG(("used: %"U32_F"\n\t", (u32_t)mem->used));
+  LWIP_PLATFORM_DIAG(("max: %"U32_F"\n\t", (u32_t)mem->max));
+  LWIP_PLATFORM_DIAG(("err: %"U32_F"\n", (u32_t)mem->err));
+}
+
+#if MEMP_STATS
+void
+stats_display_memp(struct stats_mem *mem, int index)
+{
+  if (index < MEMP_MAX) {
+    stats_display_mem(mem, mem->name);
+  }
+}
+#endif /* MEMP_STATS */
+#endif /* MEM_STATS || MEMP_STATS */
+
+#if SYS_STATS
+void
+stats_display_sys(struct stats_sys *sys)
+{
+  LWIP_PLATFORM_DIAG(("\nSYS\n\t"));
+  LWIP_PLATFORM_DIAG(("sem.used:  %"U32_F"\n\t", (u32_t)sys->sem.used));
+  LWIP_PLATFORM_DIAG(("sem.max:   %"U32_F"\n\t", (u32_t)sys->sem.max));
+  LWIP_PLATFORM_DIAG(("sem.err:   %"U32_F"\n\t", (u32_t)sys->sem.err));
+  LWIP_PLATFORM_DIAG(("mutex.used: %"U32_F"\n\t", (u32_t)sys->mutex.used));
+  LWIP_PLATFORM_DIAG(("mutex.max:  %"U32_F"\n\t", (u32_t)sys->mutex.max));
+  LWIP_PLATFORM_DIAG(("mutex.err:  %"U32_F"\n\t", (u32_t)sys->mutex.err));
+  LWIP_PLATFORM_DIAG(("mbox.used:  %"U32_F"\n\t", (u32_t)sys->mbox.used));
+  LWIP_PLATFORM_DIAG(("mbox.max:   %"U32_F"\n\t", (u32_t)sys->mbox.max));
+  LWIP_PLATFORM_DIAG(("mbox.err:   %"U32_F"\n", (u32_t)sys->mbox.err));
+}
+#endif /* SYS_STATS */
+
+void
+stats_display(void)
+{
+  s16_t i;
+
+  LINK_STATS_DISPLAY();
+  ETHARP_STATS_DISPLAY();
+  IPFRAG_STATS_DISPLAY();
+  IP6_FRAG_STATS_DISPLAY();
+  IP_STATS_DISPLAY();
+  ND6_STATS_DISPLAY();
+  IP6_STATS_DISPLAY();
+  IGMP_STATS_DISPLAY();
+  MLD6_STATS_DISPLAY();
+  ICMP_STATS_DISPLAY();
+  ICMP6_STATS_DISPLAY();
+  UDP_STATS_DISPLAY();
+  TCP_STATS_DISPLAY();
+  MEM_STATS_DISPLAY();
+  for (i = 0; i < MEMP_MAX; i++) {
+    MEMP_STATS_DISPLAY(i);
+  }
+  SYS_STATS_DISPLAY();
+}
+#endif /* LWIP_STATS_DISPLAY */
+
+#endif /* LWIP_STATS */
+

+ 106 - 68
thirdparty/lwip/src/core/sys.c

@@ -1,68 +1,106 @@
-/**
- * @file
- * lwIP Operating System abstraction
- *
- */
-
-/*
- * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * This file is part of the lwIP TCP/IP stack.
- *
- * Author: Adam Dunkels <adam@sics.se>
- *
- */
-
-#include "lwip/opt.h"
-
-#include "lwip/sys.h"
-
-/* Most of the functions defined in sys.h must be implemented in the
- * architecture-dependent file sys_arch.c */
-
-#if !NO_SYS
-
-#ifndef sys_msleep
-/**
- * Sleep for some ms. Timeouts are NOT processed while sleeping.
- *
- * @param ms number of milliseconds to sleep
- */
-void
-sys_msleep(u32_t ms)
-{
-  if (ms > 0) {
-    sys_sem_t delaysem;
-    err_t err = sys_sem_new(&delaysem, 0);
-    if (err == ERR_OK) {
-      sys_arch_sem_wait(&delaysem, ms);
-      sys_sem_free(&delaysem);
-    }
-  }
-}
-#endif /* sys_msleep */
-
-#endif /* !NO_SYS */
+/**
+ * @file
+ * lwIP Operating System abstraction
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+/**
+ * @defgroup sys_layer Porting (system abstraction layer)
+ * @ingroup lwip
+ * @verbinclude "sys_arch.txt"
+ *
+ * @defgroup sys_os OS abstraction layer
+ * @ingroup sys_layer
+ * No need to implement functions in this section in NO_SYS mode.
+ *
+ * @defgroup sys_sem Semaphores
+ * @ingroup sys_os
+ *
+ * @defgroup sys_mutex Mutexes
+ * @ingroup sys_os
+ * Mutexes are recommended to correctly handle priority inversion,
+ * especially if you use LWIP_CORE_LOCKING .
+ *
+ * @defgroup sys_mbox Mailboxes
+ * @ingroup sys_os
+ *
+ * @defgroup sys_time Time
+ * @ingroup sys_layer
+ *
+ * @defgroup sys_prot Critical sections
+ * @ingroup sys_layer
+ * Used to protect short regions of code against concurrent access.
+ * - Your system is a bare-metal system (probably with an RTOS)
+ *   and interrupts are under your control:
+ *   Implement this as LockInterrupts() / UnlockInterrupts()
+ * - Your system uses an RTOS with deferred interrupt handling from a
+ *   worker thread: Implement as a global mutex or lock/unlock scheduler
+ * - Your system uses a high-level OS with e.g. POSIX signals:
+ *   Implement as a global mutex
+ *
+ * @defgroup sys_misc Misc
+ * @ingroup sys_os
+ */
+
+#include "lwip/opt.h"
+
+#include "lwip/sys.h"
+
+/* Most of the functions defined in sys.h must be implemented in the
+ * architecture-dependent file sys_arch.c */
+
+#if !NO_SYS
+
+#ifndef sys_msleep
+/**
+ * Sleep for some ms. Timeouts are NOT processed while sleeping.
+ *
+ * @param ms number of milliseconds to sleep
+ */
+void
+sys_msleep(u32_t ms)
+{
+  if (ms > 0) {
+    sys_sem_t delaysem;
+    err_t err = sys_sem_new(&delaysem, 0);
+    if (err == ERR_OK) {
+      sys_arch_sem_wait(&delaysem, ms);
+      sys_sem_free(&delaysem);
+    }
+  }
+}
+#endif /* sys_msleep */
+
+#endif /* !NO_SYS */

+ 2164 - 1742
thirdparty/lwip/src/core/tcp.c

@@ -1,1742 +1,2164 @@
-/**
- * @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 "lwip/opt.h"
-
-#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */
-
-#include "lwip/def.h"
-#include "lwip/mem.h"
-#include "lwip/memp.h"
-#include "lwip/snmp.h"
-#include "lwip/tcp.h"
-#include "lwip/tcp_impl.h"
-#include "lwip/debug.h"
-#include "lwip/stats.h"
-
-#include <string.h>
-
-#ifndef TCP_LOCAL_PORT_RANGE_START
-/* From http://www.iana.org/assignments/port-numbers:
-   "The Dynamic and/or Private Ports are those from 49152 through 65535" */
-#define TCP_LOCAL_PORT_RANGE_START        0xc000
-#define TCP_LOCAL_PORT_RANGE_END          0xffff
-#define TCP_ENSURE_LOCAL_PORT_RANGE(port) (((port) & ~TCP_LOCAL_PORT_RANGE_START) + TCP_LOCAL_PORT_RANGE_START)
-#endif
-
-#if LWIP_TCP_KEEPALIVE
-#define TCP_KEEP_DUR(pcb)   ((pcb)->keep_cnt * (pcb)->keep_intvl)
-#define TCP_KEEP_INTVL(pcb) ((pcb)->keep_intvl)
-#else /* LWIP_TCP_KEEPALIVE */
-#define TCP_KEEP_DUR(pcb)   TCP_MAXIDLE
-#define TCP_KEEP_INTVL(pcb) TCP_KEEPINTVL_DEFAULT
-#endif /* LWIP_TCP_KEEPALIVE */
-
-const char * const tcp_state_str[] = {
-  "CLOSED",      
-  "LISTEN",      
-  "SYN_SENT",    
-  "SYN_RCVD",    
-  "ESTABLISHED", 
-  "FIN_WAIT_1",  
-  "FIN_WAIT_2",  
-  "CLOSE_WAIT",  
-  "CLOSING",     
-  "LAST_ACK",    
-  "TIME_WAIT"   
-};
-
-/* last local TCP port */
-static u16_t tcp_port = TCP_LOCAL_PORT_RANGE_START;
-
-/* Incremented every coarse grained timer shot (typically every 500 ms). */
-u32_t tcp_ticks;
-const u8_t tcp_backoff[13] =
-    { 1, 2, 3, 4, 5, 6, 7, 7, 7, 7, 7, 7, 7};
- /* Times per slowtmr hits */
-const u8_t tcp_persist_backoff[7] = { 3, 6, 12, 24, 48, 96, 120 };
-
-/* The TCP PCB lists. */
-
-/** List of all TCP PCBs bound but not yet (connected || listening) */
-struct tcp_pcb *tcp_bound_pcbs;
-/** List of all TCP PCBs in LISTEN state */
-union tcp_listen_pcbs_t tcp_listen_pcbs;
-/** List of all TCP PCBs that are in a state in which
- * they accept or send data. */
-struct tcp_pcb *tcp_active_pcbs;
-/** List of all TCP PCBs in TIME-WAIT state */
-struct tcp_pcb *tcp_tw_pcbs;
-
-#define NUM_TCP_PCB_LISTS               4
-#define NUM_TCP_PCB_LISTS_NO_TIME_WAIT  3
-/** An array with all (non-temporary) PCB lists, mainly used for smaller code size */
-struct tcp_pcb ** const tcp_pcb_lists[] = {&tcp_listen_pcbs.pcbs, &tcp_bound_pcbs,
-  &tcp_active_pcbs, &tcp_tw_pcbs};
-
-/** Only used for temporary storage. */
-struct tcp_pcb *tcp_tmp_pcb;
-
-u8_t tcp_active_pcbs_changed;
-
-/** Timer counter to handle calling slow-timer from tcp_tmr() */ 
-static u8_t tcp_timer;
-static u8_t tcp_timer_ctr;
-static u16_t tcp_new_port(void);
-
-/**
- * Initialize this module.
- */
-void
-tcp_init(void)
-{
-#if LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS && defined(LWIP_RAND)
-  tcp_port = TCP_ENSURE_LOCAL_PORT_RANGE(LWIP_RAND());
-#endif /* LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS && defined(LWIP_RAND) */
-}
-
-/**
- * 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 TX side of a connection held by the PCB.
- * For tcp_close(), a RST is sent if the application didn't receive all data
- * (tcp_recved() not called for all data passed to recv callback).
- *
- * Listening pcbs are freed and may not be referenced any more.
- * Connection pcbs are freed if not yet connected and may not be referenced
- * any more. If a connection is established (at least SYN received or in
- * a closing state), the connection is closed, and put in a closing state.
- * The pcb is then automatically freed in tcp_slowtmr(). It is therefore
- * unsafe to reference it.
- *
- * @param pcb the tcp_pcb to close
- * @return ERR_OK if connection has been closed
- *         another err_t if closing failed and pcb is not freed
- */
-static err_t
-tcp_close_shutdown(struct tcp_pcb *pcb, u8_t rst_on_unacked_data)
-{
-  err_t err;
-
-  if (rst_on_unacked_data && ((pcb->state == ESTABLISHED) || (pcb->state == CLOSE_WAIT))) {
-    if ((pcb->refused_data != NULL) || (pcb->rcv_wnd != TCP_WND)) {
-      /* Not all data received by application, send RST to tell the remote
-         side about this. */
-      LWIP_ASSERT("pcb->flags & TF_RXCLOSED", pcb->flags & TF_RXCLOSED);
-
-      /* don't call tcp_abort here: we must not deallocate the pcb since
-         that might not be expected when calling tcp_close */
-      tcp_rst(pcb->snd_nxt, pcb->rcv_nxt, &pcb->local_ip, &pcb->remote_ip,
-        pcb->local_port, pcb->remote_port);
-
-      tcp_pcb_purge(pcb);
-      TCP_RMV_ACTIVE(pcb);
-      if (pcb->state == ESTABLISHED) {
-        /* move to TIME_WAIT since we close actively */
-        pcb->state = TIME_WAIT;
-        TCP_REG(&tcp_tw_pcbs, pcb);
-      } else {
-        /* CLOSE_WAIT: deallocate the pcb since we already sent a RST for it */
-        memp_free(MEMP_TCP_PCB, pcb);
-      }
-      return ERR_OK;
-    }
-  }
-
-  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;
-    if (pcb->local_port != 0) {
-      TCP_RMV(&tcp_bound_pcbs, pcb);
-    }
-    memp_free(MEMP_TCP_PCB, pcb);
-    pcb = NULL;
-    break;
-  case LISTEN:
-    err = ERR_OK;
-    tcp_pcb_remove(&tcp_listen_pcbs.pcbs, pcb);
-    memp_free(MEMP_TCP_PCB_LISTEN, pcb);
-    pcb = NULL;
-    break;
-  case SYN_SENT:
-    err = ERR_OK;
-    TCP_PCB_REMOVE_ACTIVE(pcb);
-    memp_free(MEMP_TCP_PCB, pcb);
-    pcb = NULL;
-    snmp_inc_tcpattemptfails();
-    break;
-  case SYN_RCVD:
-    err = tcp_send_fin(pcb);
-    if (err == ERR_OK) {
-      snmp_inc_tcpattemptfails();
-      pcb->state = FIN_WAIT_1;
-    }
-    break;
-  case ESTABLISHED:
-    err = tcp_send_fin(pcb);
-    if (err == ERR_OK) {
-      snmp_inc_tcpestabresets();
-      pcb->state = FIN_WAIT_1;
-    }
-    break;
-  case CLOSE_WAIT:
-    err = tcp_send_fin(pcb);
-    if (err == ERR_OK) {
-      snmp_inc_tcpestabresets();
-      pcb->state = LAST_ACK;
-    }
-    break;
-  default:
-    /* Has already been closed, do nothing. */
-    err = ERR_OK;
-    pcb = NULL;
-    break;
-  }
-
-  if (pcb != NULL && err == ERR_OK) {
-    /* To ensure all data has been sent when tcp_close returns, we have
-       to make sure tcp_output doesn't fail.
-       Since we don't really have to ensure all data has been sent when tcp_close
-       returns (unsent data is sent from tcp timer functions, also), we don't care
-       for the return value of tcp_output for now. */
-    /* @todo: When implementing SO_LINGER, this must be changed somehow:
-       If SOF_LINGER is set, the data should be sent and acked before close returns.
-       This can only be valid for sequential APIs, not for the raw API. */
-    tcp_output(pcb);
-  }
-  return err;
-}
-
-/**
- * Closes the connection held by the PCB.
- *
- * Listening pcbs are freed and may not be referenced any more.
- * Connection pcbs are freed if not yet connected and may not be referenced
- * any more. If a connection is established (at least SYN received or in
- * a closing state), the connection is closed, and put in a closing state.
- * The pcb is then automatically freed in tcp_slowtmr(). It is therefore
- * unsafe to reference it (unless an error is returned).
- *
- * @param pcb the tcp_pcb to close
- * @return ERR_OK if connection has been closed
- *         another err_t if closing failed and pcb is not freed
- */
-err_t
-tcp_close(struct tcp_pcb *pcb)
-{
-#if TCP_DEBUG
-  LWIP_DEBUGF(TCP_DEBUG, ("tcp_close: closing in "));
-  tcp_debug_print_state(pcb->state);
-#endif /* TCP_DEBUG */
-
-  if (pcb->state != LISTEN) {
-    /* Set a flag not to receive any more data... */
-    pcb->flags |= TF_RXCLOSED;
-  }
-  /* ... and close */
-  return tcp_close_shutdown(pcb, 1);
-}
-
-/**
- * Causes all or part of a full-duplex connection of this PCB to be shut down.
- * This doesn't deallocate the PCB unless shutting down both sides!
- * Shutting down both sides is the same as calling tcp_close, so if it succeds,
- * the PCB should not be referenced any more.
- *
- * @param pcb PCB to shutdown
- * @param shut_rx shut down receive side if this is != 0
- * @param shut_tx shut down send side if this is != 0
- * @return ERR_OK if shutdown succeeded (or the PCB has already been shut down)
- *         another err_t on error.
- */
-err_t
-tcp_shutdown(struct tcp_pcb *pcb, int shut_rx, int shut_tx)
-{
-  if (pcb->state == LISTEN) {
-    return ERR_CONN;
-  }
-  if (shut_rx) {
-    /* shut down the receive side: set a flag not to receive any more data... */
-    pcb->flags |= TF_RXCLOSED;
-    if (shut_tx) {
-      /* shutting down the tx AND rx side is the same as closing for the raw API */
-      return tcp_close_shutdown(pcb, 1);
-    }
-    /* ... and free buffered data */
-    if (pcb->refused_data != NULL) {
-      pbuf_free(pcb->refused_data);
-      pcb->refused_data = NULL;
-    }
-  }
-  if (shut_tx) {
-    /* This can't happen twice since if it succeeds, the pcb's state is changed.
-       Only close in these states as the others directly deallocate the PCB */
-    switch (pcb->state) {
-    case SYN_RCVD:
-    case ESTABLISHED:
-    case CLOSE_WAIT:
-      return tcp_close_shutdown(pcb, shut_rx);
-    default:
-      /* Not (yet?) connected, cannot shutdown the TX side as that would bring us
-        into CLOSED state, where the PCB is deallocated. */
-      return ERR_CONN;
-    }
-  }
-  return ERR_OK;
-}
-
-/**
- * Abandons a connection and optionally sends a RST to the remote
- * host.  Deletes the local protocol control block. This is done when
- * a connection is killed because of shortage of memory.
- *
- * @param pcb the tcp_pcb to abort
- * @param reset boolean to indicate whether a reset should be sent
- */
-void
-tcp_abandon(struct tcp_pcb *pcb, int reset)
-{
-  u32_t seqno, ackno;
-#if LWIP_CALLBACK_API  
-  tcp_err_fn errf;
-#endif /* LWIP_CALLBACK_API */
-  void *errf_arg;
-
-  /* pcb->state LISTEN not allowed here */
-  LWIP_ASSERT("don't call tcp_abort/tcp_abandon for listen-pcbs",
-    pcb->state != LISTEN);
-  /* Figure out on which TCP PCB list we are, and remove us. If we
-     are in an active state, call the receive function associated with
-     the PCB with a NULL argument, and send an RST to the remote end. */
-  if (pcb->state == TIME_WAIT) {
-    tcp_pcb_remove(&tcp_tw_pcbs, pcb);
-    memp_free(MEMP_TCP_PCB, pcb);
-  } else {
-    seqno = pcb->snd_nxt;
-    ackno = pcb->rcv_nxt;
-#if LWIP_CALLBACK_API
-    errf = pcb->errf;
-#endif /* LWIP_CALLBACK_API */
-    errf_arg = pcb->callback_arg;
-    TCP_PCB_REMOVE_ACTIVE(pcb);
-    if (pcb->unacked != NULL) {
-      tcp_segs_free(pcb->unacked);
-    }
-    if (pcb->unsent != NULL) {
-      tcp_segs_free(pcb->unsent);
-    }
-#if TCP_QUEUE_OOSEQ    
-    if (pcb->ooseq != NULL) {
-      tcp_segs_free(pcb->ooseq);
-    }
-#endif /* TCP_QUEUE_OOSEQ */
-    if (reset) {
-      LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_abandon: sending RST\n"));
-      tcp_rst(seqno, ackno, &pcb->local_ip, &pcb->remote_ip, pcb->local_port, pcb->remote_port);
-    }
-    memp_free(MEMP_TCP_PCB, pcb);
-    TCP_EVENT_ERR(errf, errf_arg, ERR_ABRT);
-  }
-}
-
-/**
- * Aborts the connection by sending a RST (reset) segment to the remote
- * host. The pcb is deallocated. This function never fails.
- *
- * ATTENTION: When calling this from one of the TCP callbacks, make
- * sure you always return ERR_ABRT (and never return ERR_ABRT otherwise
- * or you will risk accessing deallocated memory or memory leaks!
- *
- * @param pcb the tcp pcb to abort
- */
-void
-tcp_abort(struct tcp_pcb *pcb)
-{
-  tcp_abandon(pcb, 1);
-}
-
-/**
- * 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.
- *
- * @param pcb the tcp_pcb to bind (no check is done whether this pcb is
- *        already bound!)
- * @param ipaddr the local ip address to bind to (use IP_ADDR_ANY to bind
- *        to any local address
- * @param port the local port to bind to
- * @return ERR_USE if the port is already in use
- *         ERR_VAL if bind failed because the PCB is not in a valid state
- *         ERR_OK if bound
- */
-err_t
-tcp_bind(struct tcp_pcb *pcb, ip_addr_t *ipaddr, u16_t port)
-{
-  int i;
-  int max_pcb_list = NUM_TCP_PCB_LISTS;
-  struct tcp_pcb *cpcb;
-
-  LWIP_ERROR("tcp_bind: can only bind in state CLOSED", pcb->state == CLOSED, return ERR_VAL);
-
-#if SO_REUSE
-  /* Unless the REUSEADDR flag is set,
-     we have to check the pcbs in TIME-WAIT state, also.
-     We do not dump TIME_WAIT pcb's; they can still be matched by incoming
-     packets using both local and remote IP addresses and ports to distinguish.
-   */
-  if (ip_get_option(pcb, SOF_REUSEADDR)) {
-    max_pcb_list = NUM_TCP_PCB_LISTS_NO_TIME_WAIT;
-  }
-#endif /* SO_REUSE */
-
-  if (port == 0) {
-    port = tcp_new_port();
-    if (port == 0) {
-      return ERR_BUF;
-    }
-  }
-
-  /* Check if the address already is in use (on all lists) */
-  for (i = 0; i < max_pcb_list; i++) {
-    for(cpcb = *tcp_pcb_lists[i]; cpcb != NULL; cpcb = cpcb->next) {
-      if (cpcb->local_port == port) {
-#if SO_REUSE
-        /* Omit checking for the same port if both pcbs have REUSEADDR set.
-           For SO_REUSEADDR, the duplicate-check for a 5-tuple is done in
-           tcp_connect. */
-        if (!ip_get_option(pcb, SOF_REUSEADDR) ||
-            !ip_get_option(cpcb, SOF_REUSEADDR))
-#endif /* SO_REUSE */
-        {
-          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;
-  TCP_REG(&tcp_bound_pcbs, pcb);
-  LWIP_DEBUGF(TCP_DEBUG, ("tcp_bind: bind to port %"U16_F"\n", port));
-  return ERR_OK;
-}
-#if LWIP_CALLBACK_API
-/**
- * Default accept callback if no accept callback is specified by the user.
- */
-static err_t
-tcp_accept_null(void *arg, struct tcp_pcb *pcb, err_t err)
-{
-  LWIP_UNUSED_ARG(arg);
-  LWIP_UNUSED_ARG(pcb);
-  LWIP_UNUSED_ARG(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.
- *
- * @param pcb the original tcp_pcb
- * @param backlog the incoming connections queue limit
- * @return tcp_pcb used for listening, consumes less memory.
- *
- * @note The original tcp_pcb is freed. This function therefore has to be
- *       called like this:
- *             tpcb = tcp_listen(tpcb);
- */
-struct tcp_pcb *
-tcp_listen_with_backlog(struct tcp_pcb *pcb, u8_t backlog)
-{
-  struct tcp_pcb_listen *lpcb;
-
-  LWIP_UNUSED_ARG(backlog);
-  LWIP_ERROR("tcp_listen: pcb already connected", pcb->state == CLOSED, return NULL);
-
-  /* already listening? */
-  if (pcb->state == LISTEN) {
-    return pcb;
-  }
-#if SO_REUSE
-  if (ip_get_option(pcb, SOF_REUSEADDR)) {
-    /* Since SOF_REUSEADDR allows reusing a local address before the pcb's usage
-       is declared (listen-/connection-pcb), we have to make sure now that
-       this port is only used once for every local IP. */
-    for(lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) {
-      if (lpcb->local_port == pcb->local_port) {
-        if (ip_addr_cmp(&lpcb->local_ip, &pcb->local_ip)) {
-          /* this address/port is already used */
-          return NULL;
-        }
-      }
-    }
-  }
-#endif /* SO_REUSE */
-  lpcb = (struct tcp_pcb_listen *)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->prio = pcb->prio;
-  lpcb->so_options = pcb->so_options;
-  ip_set_option(lpcb, SOF_ACCEPTCONN);
-  lpcb->ttl = pcb->ttl;
-  lpcb->tos = pcb->tos;
-  ip_addr_copy(lpcb->local_ip, pcb->local_ip);
-  if (pcb->local_port != 0) {
-    TCP_RMV(&tcp_bound_pcbs, pcb);
-  }
-  memp_free(MEMP_TCP_PCB, pcb);
-#if LWIP_CALLBACK_API
-  lpcb->accept = tcp_accept_null;
-#endif /* LWIP_CALLBACK_API */
-#if TCP_LISTEN_BACKLOG
-  lpcb->accepts_pending = 0;
-  lpcb->backlog = (backlog ? backlog : 1);
-#endif /* TCP_LISTEN_BACKLOG */
-  TCP_REG(&tcp_listen_pcbs.pcbs, (struct tcp_pcb *)lpcb);
-  return (struct tcp_pcb *)lpcb;
-}
-
-/** 
- * Update the state that tracks the available window space to advertise.
- *
- * Returns how much extra window would be advertised if we sent an
- * update now.
- */
-u32_t tcp_update_rcv_ann_wnd(struct tcp_pcb *pcb)
-{
-  u32_t new_right_edge = pcb->rcv_nxt + pcb->rcv_wnd;
-
-  if (TCP_SEQ_GEQ(new_right_edge, pcb->rcv_ann_right_edge + LWIP_MIN((TCP_WND / 2), pcb->mss))) {
-    /* we can advertise more window */
-    pcb->rcv_ann_wnd = pcb->rcv_wnd;
-    return new_right_edge - pcb->rcv_ann_right_edge;
-  } else {
-    if (TCP_SEQ_GT(pcb->rcv_nxt, pcb->rcv_ann_right_edge)) {
-      /* Can happen due to other end sending out of advertised window,
-       * but within actual available (but not yet advertised) window */
-      pcb->rcv_ann_wnd = 0;
-    } else {
-      /* keep the right edge of window constant */
-      u32_t new_rcv_ann_wnd = pcb->rcv_ann_right_edge - pcb->rcv_nxt;
-      LWIP_ASSERT("new_rcv_ann_wnd <= 0xffff", new_rcv_ann_wnd <= 0xffff);
-      pcb->rcv_ann_wnd = (u16_t)new_rcv_ann_wnd;
-    }
-    return 0;
-  }
-}
-
-/**
- * This function should be called by the application when it has
- * processed the data. The purpose is to advertise a larger window
- * when the data has been processed.
- *
- * @param pcb the tcp_pcb for which data is read
- * @param len the amount of bytes that have been read by the application
- */
-void
-tcp_recved(struct tcp_pcb *pcb, u16_t len)
-{
-  int wnd_inflation;
-
-  /* pcb->state LISTEN not allowed here */
-  LWIP_ASSERT("don't call tcp_recved for listen-pcbs",
-    pcb->state != LISTEN);
-  LWIP_ASSERT("tcp_recved: len would wrap rcv_wnd\n",
-              len <= 0xffff - pcb->rcv_wnd );
-
-  pcb->rcv_wnd += len;
-  if (pcb->rcv_wnd > TCP_WND) {
-    pcb->rcv_wnd = TCP_WND;
-  }
-
-  wnd_inflation = tcp_update_rcv_ann_wnd(pcb);
-
-  /* If the change in the right edge of window is significant (default
-   * watermark is TCP_WND/4), then send an explicit update now.
-   * Otherwise wait for a packet to be sent in the normal course of
-   * events (or more window to be available later) */
-  if (wnd_inflation >= TCP_WND_UPDATE_THRESHOLD) {
-    tcp_ack_now(pcb);
-    tcp_output(pcb);
-  }
-
-  LWIP_DEBUGF(TCP_DEBUG, ("tcp_recved: recveived %"U16_F" bytes, wnd %"U16_F" (%"U16_F").\n",
-         len, pcb->rcv_wnd, TCP_WND - pcb->rcv_wnd));
-}
-
-/**
- * Allocate a new local TCP port.
- *
- * @return a new (free) local TCP port number
- */
-static u16_t
-tcp_new_port(void)
-{
-  u8_t i;
-  u16_t n = 0;
-  struct tcp_pcb *pcb;
-  
-again:
-  if (tcp_port++ == TCP_LOCAL_PORT_RANGE_END) {
-    tcp_port = TCP_LOCAL_PORT_RANGE_START;
-  }
-  /* Check all PCB lists. */
-  for (i = 0; i < NUM_TCP_PCB_LISTS; i++) {
-    for(pcb = *tcp_pcb_lists[i]; pcb != NULL; pcb = pcb->next) {
-      if (pcb->local_port == tcp_port) {
-        if (++n > (TCP_LOCAL_PORT_RANGE_END - TCP_LOCAL_PORT_RANGE_START)) {
-          return 0;
-        }
-        goto again;
-      }
-    }
-  }
-  return tcp_port;
-}
-
-/**
- * Connects to another host. The function given as the "connected"
- * argument will be called when the connection has been established.
- *
- * @param pcb the tcp_pcb used to establish the connection
- * @param ipaddr the remote ip address to connect to
- * @param port the remote tcp port to connect to
- * @param connected callback function to call when connected (or on error)
- * @return ERR_VAL if invalid arguments are given
- *         ERR_OK if connect request has been sent
- *         other err_t values if connect request couldn't be sent
- */
-err_t
-tcp_connect(struct tcp_pcb *pcb, ip_addr_t *ipaddr, u16_t port,
-      tcp_connected_fn connected)
-{
-  err_t ret;
-  u32_t iss;
-  u16_t old_local_port;
-
-  LWIP_ERROR("tcp_connect: can only connect from state CLOSED", pcb->state == CLOSED, return ERR_ISCONN);
-
-  LWIP_DEBUGF(TCP_DEBUG, ("tcp_connect to port %"U16_F"\n", port));
-  if (ipaddr != NULL) {
-    pcb->remote_ip = *ipaddr;
-  } else {
-    return ERR_VAL;
-  }
-  pcb->remote_port = port;
-
-  /* check if we have a route to the remote host */
-  if (ip_addr_isany(&(pcb->local_ip))) {
-    /* no local IP address set, yet. */
-    struct netif *netif = ip_route(&(pcb->remote_ip));
-    if (netif == NULL) {
-      /* Don't even try to send a SYN packet if we have no route
-         since that will fail. */
-      return ERR_RTE;
-    }
-    /* Use the netif's IP address as local address. */
-    ip_addr_copy(pcb->local_ip, netif->ip_addr);
-  }
-
-  old_local_port = pcb->local_port;
-  if (pcb->local_port == 0) {
-    pcb->local_port = tcp_new_port();
-    if (pcb->local_port == 0) {
-      return ERR_BUF;
-    }
-  }
-#if SO_REUSE
-  if (ip_get_option(pcb, SOF_REUSEADDR)) {
-    /* Since SOF_REUSEADDR allows reusing a local address, we have to make sure
-       now that the 5-tuple is unique. */
-    struct tcp_pcb *cpcb;
-    int i;
-    /* Don't check listen- and bound-PCBs, check active- and TIME-WAIT PCBs. */
-    for (i = 2; i < NUM_TCP_PCB_LISTS; i++) {
-      for(cpcb = *tcp_pcb_lists[i]; cpcb != NULL; cpcb = cpcb->next) {
-        if ((cpcb->local_port == pcb->local_port) &&
-            (cpcb->remote_port == port) &&
-            ip_addr_cmp(&cpcb->local_ip, &pcb->local_ip) &&
-            ip_addr_cmp(&cpcb->remote_ip, ipaddr)) {
-          /* linux returns EISCONN here, but ERR_USE should be OK for us */
-          return ERR_USE;
-        }
-      }
-    }
-  }
-#endif /* SO_REUSE */
-  iss = tcp_next_iss();
-  pcb->rcv_nxt = 0;
-  pcb->snd_nxt = iss;
-  pcb->lastack = iss - 1;
-  pcb->snd_lbb = iss - 1;
-  pcb->rcv_wnd = TCP_WND;
-  pcb->rcv_ann_wnd = TCP_WND;
-  pcb->rcv_ann_right_edge = pcb->rcv_nxt;
-  pcb->snd_wnd = TCP_WND;
-  /* As initial send MSS, we use TCP_MSS but limit it to 536.
-     The send MSS is updated when an MSS option is received. */
-  pcb->mss = (TCP_MSS > 536) ? 536 : TCP_MSS;
-#if TCP_CALCULATE_EFF_SEND_MSS
-  pcb->mss = tcp_eff_send_mss(pcb->mss, ipaddr);
-#endif /* TCP_CALCULATE_EFF_SEND_MSS */
-  pcb->cwnd = 1;
-  pcb->ssthresh = pcb->mss * 10;
-#if LWIP_CALLBACK_API
-  pcb->connected = connected;
-#else /* LWIP_CALLBACK_API */  
-  LWIP_UNUSED_ARG(connected);
-#endif /* LWIP_CALLBACK_API */
-
-  /* Send a SYN together with the MSS option. */
-  ret = tcp_enqueue_flags(pcb, TCP_SYN);
-  if (ret == ERR_OK) {
-    /* SYN segment was enqueued, changed the pcbs state now */
-    pcb->state = SYN_SENT;
-    if (old_local_port != 0) {
-      TCP_RMV(&tcp_bound_pcbs, pcb);
-    }
-    TCP_REG_ACTIVE(pcb);
-    snmp_inc_tcpactiveopens();
-
-    tcp_output(pcb);
-  }
-  return ret;
-}
-
-/**
- * Called every 500 ms and implements the retransmission timer and the timer that
- * removes PCBs that have been in TIME-WAIT for enough time. It also increments
- * various timers such as the inactivity timer in each PCB.
- *
- * Automatically called from tcp_tmr().
- */
-void
-tcp_slowtmr(void)
-{
-  struct tcp_pcb *pcb, *prev;
-  u16_t eff_wnd;
-  u8_t pcb_remove;      /* flag if a PCB should be removed */
-  u8_t pcb_reset;       /* flag if a RST should be sent when removing */
-  err_t err;
-
-  err = ERR_OK;
-
-  ++tcp_ticks;
-  ++tcp_timer_ctr;
-
-tcp_slowtmr_start:
-  /* Steps through all of the active PCBs. */
-  prev = NULL;
-  pcb = tcp_active_pcbs;
-  if (pcb == NULL) {
-    LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: no active pcbs\n"));
-  }
-  while (pcb != NULL) {
-    LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: processing active pcb\n"));
-    LWIP_ASSERT("tcp_slowtmr: active pcb->state != CLOSED\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);
-    if (pcb->last_timer == tcp_timer_ctr) {
-      /* skip this pcb, we have already processed it */
-      pcb = pcb->next;
-      continue;
-    }
-    pcb->last_timer = tcp_timer_ctr;
-
-    pcb_remove = 0;
-    pcb_reset = 0;
-
-    if (pcb->state == SYN_SENT && pcb->nrtx == TCP_SYNMAXRTX) {
-      ++pcb_remove;
-      LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: max SYN retries reached\n"));
-    }
-    else if (pcb->nrtx == TCP_MAXRTX) {
-      ++pcb_remove;
-      LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: max DATA retries reached\n"));
-    } else {
-      if (pcb->persist_backoff > 0) {
-        /* If snd_wnd is zero, use persist timer to send 1 byte probes
-         * instead of using the standard retransmission mechanism. */
-        pcb->persist_cnt++;
-        if (pcb->persist_cnt >= tcp_persist_backoff[pcb->persist_backoff-1]) {
-          pcb->persist_cnt = 0;
-          if (pcb->persist_backoff < sizeof(tcp_persist_backoff)) {
-            pcb->persist_backoff++;
-          }
-          tcp_zero_window_probe(pcb);
-        }
-      } else {
-        /* Increase the retransmission timer if it is running */
-        if(pcb->rtime >= 0) {
-          ++pcb->rtime;
-        }
-
-        if (pcb->unacked != NULL && pcb->rtime >= pcb->rto) {
-          /* Time for a retransmission. */
-          LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_slowtmr: rtime %"S16_F
-                                      " pcb->rto %"S16_F"\n",
-                                      pcb->rtime, pcb->rto));
-
-          /* 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];
-          }
-
-          /* Reset the retransmission timer. */
-          pcb->rtime = 0;
-
-          /* Reduce congestion window and ssthresh. */
-          eff_wnd = LWIP_MIN(pcb->cwnd, pcb->snd_wnd);
-          pcb->ssthresh = eff_wnd >> 1;
-          if (pcb->ssthresh < (pcb->mss << 1)) {
-            pcb->ssthresh = (pcb->mss << 1);
-          }
-          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 this PCB is in FIN_WAIT_2 because of SHUT_WR don't let it time out. */
-      if (pcb->flags & TF_RXCLOSED) {
-        /* PCB was fully closed (either through close() or SHUT_RDWR):
-           normal FIN-WAIT timeout handling. */
-        if ((u32_t)(tcp_ticks - pcb->tmr) >
-            TCP_FIN_WAIT_TIMEOUT / TCP_SLOW_INTERVAL) {
-          ++pcb_remove;
-          LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: removing pcb stuck in FIN-WAIT-2\n"));
-        }
-      }
-    }
-
-    /* Check if KEEPALIVE should be sent */
-    if(ip_get_option(pcb, SOF_KEEPALIVE) &&
-       ((pcb->state == ESTABLISHED) ||
-        (pcb->state == CLOSE_WAIT))) {
-      if((u32_t)(tcp_ticks - pcb->tmr) >
-         (pcb->keep_idle + TCP_KEEP_DUR(pcb)) / TCP_SLOW_INTERVAL)
-      {
-        LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: KEEPALIVE timeout. Aborting connection to %"U16_F".%"U16_F".%"U16_F".%"U16_F".\n",
-                                ip4_addr1_16(&pcb->remote_ip), ip4_addr2_16(&pcb->remote_ip),
-                                ip4_addr3_16(&pcb->remote_ip), ip4_addr4_16(&pcb->remote_ip)));
-        
-        ++pcb_remove;
-        ++pcb_reset;
-      }
-      else if((u32_t)(tcp_ticks - pcb->tmr) > 
-              (pcb->keep_idle + pcb->keep_cnt_sent * TCP_KEEP_INTVL(pcb))
-              / TCP_SLOW_INTERVAL)
-      {
-        tcp_keepalive(pcb);
-        pcb->keep_cnt_sent++;
-      }
-    }
-
-    /* If this PCB has queued out of sequence data, but has been
-       inactive for too long, will drop the data (it will eventually
-       be retransmitted). */
-#if TCP_QUEUE_OOSEQ
-    if (pcb->ooseq != NULL &&
-        (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) {
-      struct tcp_pcb *pcb2;
-      tcp_err_fn err_fn;
-      void *err_arg;
-      tcp_pcb_purge(pcb);
-      /* Remove PCB from tcp_active_pcbs list. */
-      if (prev != NULL) {
-        LWIP_ASSERT("tcp_slowtmr: middle tcp != tcp_active_pcbs", pcb != tcp_active_pcbs);
-        prev->next = pcb->next;
-      } else {
-        /* This PCB was the first. */
-        LWIP_ASSERT("tcp_slowtmr: first pcb == tcp_active_pcbs", tcp_active_pcbs == pcb);
-        tcp_active_pcbs = pcb->next;
-      }
-
-      if (pcb_reset) {
-        tcp_rst(pcb->snd_nxt, pcb->rcv_nxt, &pcb->local_ip, &pcb->remote_ip,
-          pcb->local_port, pcb->remote_port);
-      }
-
-      err_fn = pcb->errf;
-      err_arg = pcb->callback_arg;
-      pcb2 = pcb;
-      pcb = pcb->next;
-      memp_free(MEMP_TCP_PCB, pcb2);
-
-      tcp_active_pcbs_changed = 0;
-      TCP_EVENT_ERR(err_fn, err_arg, ERR_ABRT);
-      if (tcp_active_pcbs_changed) {
-        goto tcp_slowtmr_start;
-      }
-    } else {
-      /* get the 'next' element now and work with 'prev' below (in case of abort) */
-      prev = pcb;
-      pcb = pcb->next;
-
-      /* We check if we should poll the connection. */
-      ++prev->polltmr;
-      if (prev->polltmr >= prev->pollinterval) {
-        prev->polltmr = 0;
-        LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: polling application\n"));
-        tcp_active_pcbs_changed = 0;
-        TCP_EVENT_POLL(prev, err);
-        if (tcp_active_pcbs_changed) {
-          goto tcp_slowtmr_start;
-        }
-        /* if err == ERR_ABRT, 'prev' is already deallocated */
-        if (err == ERR_OK) {
-          tcp_output(prev);
-        }
-      }
-    }
-  }
-
-  
-  /* Steps through all of the TIME-WAIT PCBs. */
-  prev = NULL;
-  pcb = tcp_tw_pcbs;
-  while (pcb != NULL) {
-    LWIP_ASSERT("tcp_slowtmr: TIME-WAIT pcb->state == TIME-WAIT", pcb->state == TIME_WAIT);
-    pcb_remove = 0;
-
-    /* Check if this PCB has stayed long enough in TIME-WAIT */
-    if ((u32_t)(tcp_ticks - pcb->tmr) > 2 * TCP_MSL / TCP_SLOW_INTERVAL) {
-      ++pcb_remove;
-    }
-    
-
-
-    /* If the PCB should be removed, do it. */
-    if (pcb_remove) {
-      struct tcp_pcb *pcb2;
-      tcp_pcb_purge(pcb);
-      /* Remove PCB from tcp_tw_pcbs list. */
-      if (prev != NULL) {
-        LWIP_ASSERT("tcp_slowtmr: middle tcp != tcp_tw_pcbs", pcb != tcp_tw_pcbs);
-        prev->next = pcb->next;
-      } else {
-        /* This PCB was the first. */
-        LWIP_ASSERT("tcp_slowtmr: first pcb == tcp_tw_pcbs", tcp_tw_pcbs == pcb);
-        tcp_tw_pcbs = pcb->next;
-      }
-      pcb2 = pcb;
-      pcb = pcb->next;
-      memp_free(MEMP_TCP_PCB, pcb2);
-    } else {
-      prev = pcb;
-      pcb = pcb->next;
-    }
-  }
-}
-
-/**
- * Is called every TCP_FAST_INTERVAL (250 ms) and process data previously
- * "refused" by upper layer (application) and sends delayed ACKs.
- *
- * Automatically called from tcp_tmr().
- */
-void
-tcp_fasttmr(void)
-{
-  struct tcp_pcb *pcb;
-
-  ++tcp_timer_ctr;
-
-tcp_fasttmr_start:
-  pcb = tcp_active_pcbs;
-
-  while(pcb != NULL) {
-    if (pcb->last_timer != tcp_timer_ctr) {
-      struct tcp_pcb *next;
-      pcb->last_timer = tcp_timer_ctr;
-      /* send delayed ACKs */
-      if (pcb->flags & TF_ACK_DELAY) {
-        LWIP_DEBUGF(TCP_DEBUG, ("tcp_fasttmr: delayed ACK\n"));
-        tcp_ack_now(pcb);
-        tcp_output(pcb);
-        pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW);
-      }
-
-      next = pcb->next;
-
-      /* If there is data which was previously "refused" by upper layer */
-      if (pcb->refused_data != NULL) {
-        tcp_active_pcbs_changed = 0;
-        tcp_process_refused_data(pcb);
-        if (tcp_active_pcbs_changed) {
-          /* application callback has changed the pcb list: restart the loop */
-          goto tcp_fasttmr_start;
-        }
-      }
-      pcb = next;
-    }
-  }
-}
-
-/** Pass pcb->refused_data to the recv callback */
-err_t
-tcp_process_refused_data(struct tcp_pcb *pcb)
-{
-  err_t err;
-  u8_t refused_flags = pcb->refused_data->flags;
-  /* set pcb->refused_data to NULL in case the callback frees it and then
-     closes the pcb */
-  struct pbuf *refused_data = pcb->refused_data;
-  pcb->refused_data = NULL;
-  /* Notify again application with data previously received. */
-  LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: notify kept packet\n"));
-  TCP_EVENT_RECV(pcb, refused_data, ERR_OK, err);
-  if (err == ERR_OK) {
-    /* did refused_data include a FIN? */
-    if (refused_flags & PBUF_FLAG_TCP_FIN) {
-      /* correct rcv_wnd as the application won't call tcp_recved()
-         for the FIN's seqno */
-      if (pcb->rcv_wnd != TCP_WND) {
-        pcb->rcv_wnd++;
-      }
-      TCP_EVENT_CLOSED(pcb, err);
-      if (err == ERR_ABRT) {
-        return ERR_ABRT;
-      }
-    }
-  } else if (err == ERR_ABRT) {
-    /* if err == ERR_ABRT, 'pcb' is already deallocated */
-    /* Drop incoming packets because pcb is "full" (only if the incoming
-       segment contains data). */
-    LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: drop incoming packets, because pcb is \"full\"\n"));
-    return ERR_ABRT;
-  } else {
-    /* data is still refused, pbuf is still valid (go on for ACK-only packets) */
-    pcb->refused_data = refused_data;
-  }
-  return ERR_OK;
-}
-
-/**
- * Deallocates a list of TCP segments (tcp_seg structures).
- *
- * @param seg tcp_seg list of TCP segments to free
- */
-void
-tcp_segs_free(struct tcp_seg *seg)
-{
-  while (seg != NULL) {
-    struct tcp_seg *next = seg->next;
-    tcp_seg_free(seg);
-    seg = next;
-  }
-}
-
-/**
- * Frees a TCP segment (tcp_seg structure).
- *
- * @param seg single tcp_seg to free
- */
-void
-tcp_seg_free(struct tcp_seg *seg)
-{
-  if (seg != NULL) {
-    if (seg->p != NULL) {
-      pbuf_free(seg->p);
-#if TCP_DEBUG
-      seg->p = NULL;
-#endif /* TCP_DEBUG */
-    }
-    memp_free(MEMP_TCP_SEG, seg);
-  }
-}
-
-/**
- * Sets the priority of a connection.
- *
- * @param pcb the tcp_pcb to manipulate
- * @param prio new priority
- */
-void
-tcp_setprio(struct tcp_pcb *pcb, u8_t prio)
-{
-  pcb->prio = prio;
-}
-
-#if TCP_QUEUE_OOSEQ
-/**
- * Returns a copy of the given TCP segment.
- * The pbuf and data are not copied, only the pointers
- *
- * @param seg the old tcp_seg
- * @return a copy of seg
- */ 
-struct tcp_seg *
-tcp_seg_copy(struct tcp_seg *seg)
-{
-  struct tcp_seg *cseg;
-
-  cseg = (struct tcp_seg *)memp_malloc(MEMP_TCP_SEG);
-  if (cseg == NULL) {
-    return NULL;
-  }
-  SMEMCPY((u8_t *)cseg, (const u8_t *)seg, sizeof(struct tcp_seg)); 
-  pbuf_ref(cseg->p);
-  return cseg;
-}
-#endif /* TCP_QUEUE_OOSEQ */
-
-#if LWIP_CALLBACK_API
-/**
- * Default receive callback that is called if the user didn't register
- * a recv callback for the pcb.
- */
-err_t
-tcp_recv_null(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
-{
-  LWIP_UNUSED_ARG(arg);
-  if (p != NULL) {
-    tcp_recved(pcb, p->tot_len);
-    pbuf_free(p);
-  } else if (err == ERR_OK) {
-    return tcp_close(pcb);
-  }
-  return ERR_OK;
-}
-#endif /* LWIP_CALLBACK_API */
-
-/**
- * Kills the oldest active connection that has the same or lower priority than
- * 'prio'.
- *
- * @param prio minimum priority
- */
-static void
-tcp_kill_prio(u8_t prio)
-{
-  struct tcp_pcb *pcb, *inactive;
-  u32_t inactivity;
-  u8_t mprio;
-
-
-  mprio = 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);
-  }
-}
-
-/**
- * Kills the oldest connection that is in TIME_WAIT state.
- * Called from tcp_alloc() if no more connections are available.
- */
-static void
-tcp_kill_timewait(void)
-{
-  struct tcp_pcb *pcb, *inactive;
-  u32_t inactivity;
-
-  inactivity = 0;
-  inactive = NULL;
-  /* Go through the list of TIME_WAIT pcbs and get the oldest pcb. */
-  for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) {
-    if ((u32_t)(tcp_ticks - pcb->tmr) >= inactivity) {
-      inactivity = tcp_ticks - pcb->tmr;
-      inactive = pcb;
-    }
-  }
-  if (inactive != NULL) {
-    LWIP_DEBUGF(TCP_DEBUG, ("tcp_kill_timewait: killing oldest TIME-WAIT PCB %p (%"S32_F")\n",
-           (void *)inactive, inactivity));
-    tcp_abort(inactive);
-  }
-}
-
-/**
- * Allocate a new tcp_pcb structure.
- *
- * @param prio priority for the new pcb
- * @return a new tcp_pcb that initially is in state CLOSED
- */
-struct tcp_pcb *
-tcp_alloc(u8_t prio)
-{
-  struct tcp_pcb *pcb;
-  u32_t iss;
-  
-  pcb = (struct tcp_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();
-    /* Try to allocate a tcp_pcb again. */
-    pcb = (struct tcp_pcb *)memp_malloc(MEMP_TCP_PCB);
-    if (pcb == NULL) {
-      /* Try killing active connections with lower priority than the new one. */
-      LWIP_DEBUGF(TCP_DEBUG, ("tcp_alloc: killing connection with prio lower than %d\n", prio));
-      tcp_kill_prio(prio);
-      /* Try to allocate a tcp_pcb again. */
-      pcb = (struct tcp_pcb *)memp_malloc(MEMP_TCP_PCB);
-      if (pcb != NULL) {
-        /* adjust err stats: memp_malloc failed twice before */
-        MEMP_STATS_DEC(err, MEMP_TCP_PCB);
-      }
-    }
-    if (pcb != NULL) {
-      /* adjust err stats: timewait PCB was freed above */
-      MEMP_STATS_DEC(err, MEMP_TCP_PCB);
-    }
-  }
-  if (pcb != NULL) {
-    memset(pcb, 0, sizeof(struct tcp_pcb));
-    pcb->prio = prio;
-    pcb->snd_buf = TCP_SND_BUF;
-    pcb->snd_queuelen = 0;
-    pcb->rcv_wnd = TCP_WND;
-    pcb->rcv_ann_wnd = TCP_WND;
-    pcb->tos = 0;
-    pcb->ttl = TCP_TTL;
-    /* As initial send MSS, we use TCP_MSS but limit it to 536.
-       The send MSS is updated when an MSS option is received. */
-    pcb->mss = (TCP_MSS > 536) ? 536 : TCP_MSS;
-    pcb->rto = 3000 / TCP_SLOW_INTERVAL;
-    pcb->sa = 0;
-    pcb->sv = 3000 / TCP_SLOW_INTERVAL;
-    pcb->rtime = -1;
-    pcb->cwnd = 1;
-    iss = tcp_next_iss();
-    pcb->snd_wl2 = iss;
-    pcb->snd_nxt = iss;
-    pcb->lastack = iss;
-    pcb->snd_lbb = iss;   
-    pcb->tmr = tcp_ticks;
-    pcb->last_timer = tcp_timer_ctr;
-
-    pcb->polltmr = 0;
-
-#if LWIP_CALLBACK_API
-    pcb->recv = tcp_recv_null;
-#endif /* LWIP_CALLBACK_API */  
-    
-    /* Init KEEPALIVE timer */
-    pcb->keep_idle  = TCP_KEEPIDLE_DEFAULT;
-    
-#if LWIP_TCP_KEEPALIVE
-    pcb->keep_intvl = TCP_KEEPINTVL_DEFAULT;
-    pcb->keep_cnt   = TCP_KEEPCNT_DEFAULT;
-#endif /* LWIP_TCP_KEEPALIVE */
-
-    pcb->keep_cnt_sent = 0;
-  }
-  return pcb;
-}
-
-/**
- * Creates a new TCP protocol control block but doesn't place it on
- * any of the TCP PCB lists.
- * The pcb is not put on any list until binding using tcp_bind().
- *
- * @internal: Maybe there should be a idle TCP PCB list where these
- * PCBs are put on. Port reservation using tcp_bind() is implemented but
- * allocated pcbs that are not bound can't be killed automatically if wanting
- * to allocate a pcb with higher prio (@see tcp_kill_prio())
- *
- * @return a new tcp_pcb that initially is in state CLOSED
- */
-struct tcp_pcb *
-tcp_new(void)
-{
-  return tcp_alloc(TCP_PRIO_NORMAL);
-}
-
-/**
- * Used to specify the argument that should be passed callback
- * functions.
- *
- * @param pcb tcp_pcb to set the callback argument
- * @param arg void pointer argument to pass to callback functions
- */ 
-void
-tcp_arg(struct tcp_pcb *pcb, void *arg)
-{
-  /* This function is allowed to be called for both listen pcbs and
-     connection pcbs. */
-  pcb->callback_arg = arg;
-}
-#if LWIP_CALLBACK_API
-
-/**
- * Used to specify the function that should be called when a TCP
- * connection receives data.
- *
- * @param pcb tcp_pcb to set the recv callback
- * @param recv callback function to call for this pcb when data is received
- */ 
-void
-tcp_recv(struct tcp_pcb *pcb, tcp_recv_fn recv)
-{
-  LWIP_ASSERT("invalid socket state for recv callback", pcb->state != LISTEN);
-  pcb->recv = recv;
-}
-
-/**
- * Used to specify the function that should be called when TCP data
- * has been successfully delivered to the remote host.
- *
- * @param pcb tcp_pcb to set the sent callback
- * @param sent callback function to call for this pcb when data is successfully sent
- */ 
-void
-tcp_sent(struct tcp_pcb *pcb, tcp_sent_fn sent)
-{
-  LWIP_ASSERT("invalid socket state for sent callback", pcb->state != LISTEN);
-  pcb->sent = sent;
-}
-
-/**
- * Used to specify the function that should be called when a fatal error
- * has occured on the connection.
- *
- * @param pcb tcp_pcb to set the err callback
- * @param err callback function to call for this pcb when a fatal error
- *        has occured on the connection
- */ 
-void
-tcp_err(struct tcp_pcb *pcb, tcp_err_fn err)
-{
-  LWIP_ASSERT("invalid socket state for err callback", pcb->state != LISTEN);
-  pcb->errf = err;
-}
-
-/**
- * Used for specifying the function that should be called when a
- * LISTENing connection has been connected to another host.
- *
- * @param pcb tcp_pcb to set the accept callback
- * @param accept callback function to call for this pcb when LISTENing
- *        connection has been connected to another host
- */ 
-void
-tcp_accept(struct tcp_pcb *pcb, tcp_accept_fn accept)
-{
-  /* This function is allowed to be called for both listen pcbs and
-     connection pcbs. */
-  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, tcp_poll_fn poll, u8_t interval)
-{
-  LWIP_ASSERT("invalid socket state for poll", pcb->state != LISTEN);
-#if LWIP_CALLBACK_API
-  pcb->poll = poll;
-#else /* LWIP_CALLBACK_API */  
-  LWIP_UNUSED_ARG(poll);
-#endif /* LWIP_CALLBACK_API */  
-  pcb->pollinterval = interval;
-}
-
-/**
- * Purges a TCP PCB. Removes any buffered data and frees the buffer memory
- * (pcb->ooseq, pcb->unsent and pcb->unacked are freed).
- *
- * @param pcb tcp_pcb to purge. The pcb itself is not deallocated!
- */
-void
-tcp_pcb_purge(struct tcp_pcb *pcb)
-{
-  if (pcb->state != CLOSED &&
-     pcb->state != TIME_WAIT &&
-     pcb->state != LISTEN) {
-
-    LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge\n"));
-
-#if TCP_LISTEN_BACKLOG
-    if (pcb->state == SYN_RCVD) {
-      /* Need to find the corresponding listen_pcb and decrease its accepts_pending */
-      struct tcp_pcb_listen *lpcb;
-      LWIP_ASSERT("tcp_pcb_purge: pcb->state == SYN_RCVD but tcp_listen_pcbs is NULL",
-        tcp_listen_pcbs.listen_pcbs != NULL);
-      for (lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) {
-        if ((lpcb->local_port == pcb->local_port) &&
-            (ip_addr_isany(&lpcb->local_ip) ||
-             ip_addr_cmp(&pcb->local_ip, &lpcb->local_ip))) {
-            /* port and address of the listen pcb match the timed-out pcb */
-            LWIP_ASSERT("tcp_pcb_purge: listen pcb does not have accepts pending",
-              lpcb->accepts_pending > 0);
-            lpcb->accepts_pending--;
-            break;
-          }
-      }
-    }
-#endif /* TCP_LISTEN_BACKLOG */
-
-
-    if (pcb->refused_data != NULL) {
-      LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: data left on ->refused_data\n"));
-      pbuf_free(pcb->refused_data);
-      pcb->refused_data = NULL;
-    }
-    if (pcb->unsent != NULL) {
-      LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: not all data sent\n"));
-    }
-    if (pcb->unacked != NULL) {
-      LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: data left on ->unacked\n"));
-    }
-#if TCP_QUEUE_OOSEQ
-    if (pcb->ooseq != NULL) {
-      LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: data left on ->ooseq\n"));
-    }
-    tcp_segs_free(pcb->ooseq);
-    pcb->ooseq = NULL;
-#endif /* TCP_QUEUE_OOSEQ */
-
-    /* Stop the retransmission timer as it will expect data on unacked
-       queue if it fires */
-    pcb->rtime = -1;
-
-    tcp_segs_free(pcb->unsent);
-    tcp_segs_free(pcb->unacked);
-    pcb->unacked = pcb->unsent = NULL;
-#if TCP_OVERSIZE
-    pcb->unsent_oversize = 0;
-#endif /* TCP_OVERSIZE */
-  }
-}
-
-/**
- * Purges the PCB and removes it from a PCB list. Any delayed ACKs are sent first.
- *
- * @param pcblist PCB list to purge.
- * @param pcb tcp_pcb to purge. The pcb itself is NOT deallocated!
- */
-void
-tcp_pcb_remove(struct tcp_pcb **pcblist, struct tcp_pcb *pcb)
-{
-  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);
-  }
-
-  if (pcb->state != LISTEN) {
-    LWIP_ASSERT("unsent segments leaking", pcb->unsent == NULL);
-    LWIP_ASSERT("unacked segments leaking", pcb->unacked == NULL);
-#if TCP_QUEUE_OOSEQ
-    LWIP_ASSERT("ooseq segments leaking", pcb->ooseq == NULL);
-#endif /* TCP_QUEUE_OOSEQ */
-  }
-
-  pcb->state = CLOSED;
-
-  LWIP_ASSERT("tcp_pcb_remove: tcp_pcbs_sane()", tcp_pcbs_sane());
-}
-
-/**
- * Calculates a new initial sequence number for new connections.
- *
- * @return u32_t pseudo random sequence number
- */
-u32_t
-tcp_next_iss(void)
-{
-  static u32_t iss = 6510;
-  
-  iss += tcp_ticks;       /* XXX */
-  return iss;
-}
-
-#if TCP_CALCULATE_EFF_SEND_MSS
-/**
- * Calcluates the effective send mss that can be used for a specific IP address
- * by using ip_route to determin the netif used to send to the address and
- * calculating the minimum of TCP_MSS and that netif's mtu (if set).
- */
-u16_t
-tcp_eff_send_mss(u16_t sendmss, ip_addr_t *addr)
-{
-  u16_t mss_s;
-  struct netif *outif;
-
-  outif = ip_route(addr);
-  if ((outif != NULL) && (outif->mtu != 0)) {
-    mss_s = outif->mtu - IP_HLEN - TCP_HLEN;
-    /* RFC 1122, chap 4.2.2.6:
-     * Eff.snd.MSS = min(SendMSS+20, MMS_S) - TCPhdrsize - IPoptionsize
-     * We correct for TCP options in tcp_write(), and don't support IP options.
-     */
-    sendmss = LWIP_MIN(sendmss, mss_s);
-  }
-  return sendmss;
-}
-#endif /* TCP_CALCULATE_EFF_SEND_MSS */
-
-const char*
-tcp_debug_state_str(enum tcp_state s)
-{
-  return tcp_state_str[s];
-}
-
-#if TCP_DEBUG || TCP_INPUT_DEBUG || TCP_OUTPUT_DEBUG
-/**
- * Print a tcp header for debugging purposes.
- *
- * @param tcphdr pointer to a struct tcp_hdr
- */
-void
-tcp_debug_print(struct tcp_hdr *tcphdr)
-{
-  LWIP_DEBUGF(TCP_DEBUG, ("TCP header:\n"));
-  LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n"));
-  LWIP_DEBUGF(TCP_DEBUG, ("|    %5"U16_F"      |    %5"U16_F"      | (src port, dest port)\n",
-         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"));
-}
-
-/**
- * Print a tcp state for debugging purposes.
- *
- * @param s enum tcp_state to print
- */
-void
-tcp_debug_print_state(enum tcp_state s)
-{
-  LWIP_DEBUGF(TCP_DEBUG, ("State: %s\n", tcp_state_str[s]));
-}
-
-/**
- * Print tcp flags for debugging purposes.
- *
- * @param flags tcp flags, all active flags are printed
- */
-void
-tcp_debug_print_flags(u8_t flags)
-{
-  if (flags & TCP_FIN) {
-    LWIP_DEBUGF(TCP_DEBUG, ("FIN "));
-  }
-  if (flags & TCP_SYN) {
-    LWIP_DEBUGF(TCP_DEBUG, ("SYN "));
-  }
-  if (flags & TCP_RST) {
-    LWIP_DEBUGF(TCP_DEBUG, ("RST "));
-  }
-  if (flags & TCP_PSH) {
-    LWIP_DEBUGF(TCP_DEBUG, ("PSH "));
-  }
-  if (flags & TCP_ACK) {
-    LWIP_DEBUGF(TCP_DEBUG, ("ACK "));
-  }
-  if (flags & TCP_URG) {
-    LWIP_DEBUGF(TCP_DEBUG, ("URG "));
-  }
-  if (flags & TCP_ECE) {
-    LWIP_DEBUGF(TCP_DEBUG, ("ECE "));
-  }
-  if (flags & TCP_CWR) {
-    LWIP_DEBUGF(TCP_DEBUG, ("CWR "));
-  }
-  LWIP_DEBUGF(TCP_DEBUG, ("\n"));
-}
-
-/**
- * Print all tcp_pcbs in every list for debugging purposes.
- */
-void
-tcp_debug_print_pcbs(void)
-{
-  struct tcp_pcb *pcb;
-  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);
-  }    
-}
-
-/**
- * Check state consistency of the tcp_pcb lists.
- */
-s16_t
-tcp_pcbs_sane(void)
-{
-  struct tcp_pcb *pcb;
-  for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
-    LWIP_ASSERT("tcp_pcbs_sane: active pcb->state != CLOSED", pcb->state != CLOSED);
-    LWIP_ASSERT("tcp_pcbs_sane: active pcb->state != LISTEN", pcb->state != LISTEN);
-    LWIP_ASSERT("tcp_pcbs_sane: active pcb->state != TIME-WAIT", pcb->state != TIME_WAIT);
-  }
-  for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) {
-    LWIP_ASSERT("tcp_pcbs_sane: tw pcb->state == TIME-WAIT", pcb->state == TIME_WAIT);
-  }
-  return 1;
-}
-#endif /* TCP_DEBUG */
-
-#endif /* LWIP_TCP */
+/**
+ * @file 
+ * Transmission Control Protocol for IP
+ * See also @ref tcp_raw
+ *
+ * @defgroup tcp_raw TCP
+ * @ingroup callbackstyle_api
+ * Transmission Control Protocol for IP\n
+ * @see @ref raw_api and @ref netconn
+ *
+ * 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.\n
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/def.h"
+#include "lwip/mem.h"
+#include "lwip/memp.h"
+#include "lwip/tcp.h"
+#include "lwip/priv/tcp_priv.h"
+#include "lwip/debug.h"
+#include "lwip/stats.h"
+#include "lwip/ip6.h"
+#include "lwip/ip6_addr.h"
+#include "lwip/nd6.h"
+
+#include <string.h>
+
+#ifdef LWIP_HOOK_FILENAME
+#include LWIP_HOOK_FILENAME
+#endif
+
+#ifndef TCP_LOCAL_PORT_RANGE_START
+/* From http://www.iana.org/assignments/port-numbers:
+   "The Dynamic and/or Private Ports are those from 49152 through 65535" */
+#define TCP_LOCAL_PORT_RANGE_START        0xc000
+#define TCP_LOCAL_PORT_RANGE_END          0xffff
+#define TCP_ENSURE_LOCAL_PORT_RANGE(port) ((u16_t)(((port) & ~TCP_LOCAL_PORT_RANGE_START) + TCP_LOCAL_PORT_RANGE_START))
+#endif
+
+#if LWIP_TCP_KEEPALIVE
+#define TCP_KEEP_DUR(pcb)   ((pcb)->keep_cnt * (pcb)->keep_intvl)
+#define TCP_KEEP_INTVL(pcb) ((pcb)->keep_intvl)
+#else /* LWIP_TCP_KEEPALIVE */
+#define TCP_KEEP_DUR(pcb)   TCP_MAXIDLE
+#define TCP_KEEP_INTVL(pcb) TCP_KEEPINTVL_DEFAULT
+#endif /* LWIP_TCP_KEEPALIVE */
+
+/* As initial send MSS, we use TCP_MSS but limit it to 536. */
+#if TCP_MSS > 536
+#define INITIAL_MSS 536
+#else
+#define INITIAL_MSS TCP_MSS
+#endif
+
+static const char * const tcp_state_str[] = {
+  "CLOSED",
+  "LISTEN",
+  "SYN_SENT",
+  "SYN_RCVD",
+  "ESTABLISHED",
+  "FIN_WAIT_1",
+  "FIN_WAIT_2",
+  "CLOSE_WAIT",
+  "CLOSING",
+  "LAST_ACK",
+  "TIME_WAIT"
+};
+
+/* last local TCP port */
+static u16_t tcp_port = TCP_LOCAL_PORT_RANGE_START;
+
+/* Incremented every coarse grained timer shot (typically every 500 ms). */
+u32_t tcp_ticks;
+static const u8_t tcp_backoff[13] =
+    { 1, 2, 3, 4, 5, 6, 7, 7, 7, 7, 7, 7, 7};
+ /* Times per slowtmr hits */
+static const u8_t tcp_persist_backoff[7] = { 3, 6, 12, 24, 48, 96, 120 };
+
+/* The TCP PCB lists. */
+
+/** List of all TCP PCBs bound but not yet (connected || listening) */
+struct tcp_pcb *tcp_bound_pcbs;
+/** List of all TCP PCBs in LISTEN state */
+union tcp_listen_pcbs_t tcp_listen_pcbs;
+/** List of all TCP PCBs that are in a state in which
+ * they accept or send data. */
+struct tcp_pcb *tcp_active_pcbs;
+/** List of all TCP PCBs in TIME-WAIT state */
+struct tcp_pcb *tcp_tw_pcbs;
+
+/** An array with all (non-temporary) PCB lists, mainly used for smaller code size */
+struct tcp_pcb ** const tcp_pcb_lists[] = {&tcp_listen_pcbs.pcbs, &tcp_bound_pcbs,
+  &tcp_active_pcbs, &tcp_tw_pcbs};
+
+u8_t tcp_active_pcbs_changed;
+
+/** Timer counter to handle calling slow-timer from tcp_tmr() */
+static u8_t tcp_timer;
+static u8_t tcp_timer_ctr;
+static u16_t tcp_new_port(void);
+
+static err_t tcp_close_shutdown_fin(struct tcp_pcb *pcb);
+
+/**
+ * Initialize this module.
+ */
+void
+tcp_init(void)
+{
+#if LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS && defined(LWIP_RAND)
+  tcp_port = TCP_ENSURE_LOCAL_PORT_RANGE(LWIP_RAND());
+#endif /* LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS && defined(LWIP_RAND) */
+}
+
+/**
+ * Called periodically to dispatch TCP timers.
+ */
+void
+tcp_tmr(void)
+{
+  /* Call tcp_fasttmr() every 250 ms */
+  tcp_fasttmr();
+
+  if (++tcp_timer & 1) {
+    /* Call tcp_slowtmr() every 500 ms, i.e., every other timer
+       tcp_tmr() is called. */
+    tcp_slowtmr();
+  }
+}
+
+#if LWIP_CALLBACK_API || TCP_LISTEN_BACKLOG
+/** Called when a listen pcb is closed. Iterates one pcb list and removes the
+ * closed listener pcb from pcb->listener if matching.
+ */
+static void
+tcp_remove_listener(struct tcp_pcb *list, struct tcp_pcb_listen *lpcb)
+{
+   struct tcp_pcb *pcb;
+   for (pcb = list; pcb != NULL; pcb = pcb->next) {
+      if (pcb->listener == lpcb) {
+         pcb->listener = NULL;
+      }
+   }
+}
+#endif
+
+/** Called when a listen pcb is closed. Iterates all pcb lists and removes the
+ * closed listener pcb from pcb->listener if matching.
+ */
+static void
+tcp_listen_closed(struct tcp_pcb *pcb)
+{
+#if LWIP_CALLBACK_API || TCP_LISTEN_BACKLOG
+  size_t i;
+  LWIP_ASSERT("pcb != NULL", pcb != NULL);
+  LWIP_ASSERT("pcb->state == LISTEN", pcb->state == LISTEN);
+  for (i = 1; i < LWIP_ARRAYSIZE(tcp_pcb_lists); i++) {
+    tcp_remove_listener(*tcp_pcb_lists[i], (struct tcp_pcb_listen*)pcb);
+  }
+#endif
+  LWIP_UNUSED_ARG(pcb);
+}
+
+#if TCP_LISTEN_BACKLOG
+/** @ingroup tcp_raw
+ * Delay accepting a connection in respect to the listen backlog:
+ * the number of outstanding connections is increased until
+ * tcp_backlog_accepted() is called.
+ *
+ * ATTENTION: the caller is responsible for calling tcp_backlog_accepted()
+ * or else the backlog feature will get out of sync!
+ *
+ * @param pcb the connection pcb which is not fully accepted yet
+ */
+void
+tcp_backlog_delayed(struct tcp_pcb* pcb)
+{
+  LWIP_ASSERT("pcb != NULL", pcb != NULL);
+  if ((pcb->flags & TF_BACKLOGPEND) == 0) {
+    if (pcb->listener != NULL) {
+      pcb->listener->accepts_pending++;
+      LWIP_ASSERT("accepts_pending != 0", pcb->listener->accepts_pending != 0);
+      pcb->flags |= TF_BACKLOGPEND;
+    }
+  }
+}
+
+/** @ingroup tcp_raw
+ * A delayed-accept a connection is accepted (or closed/aborted): decreases
+ * the number of outstanding connections after calling tcp_backlog_delayed().
+ *
+ * ATTENTION: the caller is responsible for calling tcp_backlog_accepted()
+ * or else the backlog feature will get out of sync!
+ *
+ * @param pcb the connection pcb which is now fully accepted (or closed/aborted)
+ */
+void
+tcp_backlog_accepted(struct tcp_pcb* pcb)
+{
+  LWIP_ASSERT("pcb != NULL", pcb != NULL);
+  if ((pcb->flags & TF_BACKLOGPEND) != 0) {
+    if (pcb->listener != NULL) {
+      LWIP_ASSERT("accepts_pending != 0", pcb->listener->accepts_pending != 0);
+      pcb->listener->accepts_pending--;
+      pcb->flags &= ~TF_BACKLOGPEND;
+    }
+  }
+}
+#endif /* TCP_LISTEN_BACKLOG */
+
+/**
+ * Closes the TX side of a connection held by the PCB.
+ * For tcp_close(), a RST is sent if the application didn't receive all data
+ * (tcp_recved() not called for all data passed to recv callback).
+ *
+ * Listening pcbs are freed and may not be referenced any more.
+ * Connection pcbs are freed if not yet connected and may not be referenced
+ * any more. If a connection is established (at least SYN received or in
+ * a closing state), the connection is closed, and put in a closing state.
+ * The pcb is then automatically freed in tcp_slowtmr(). It is therefore
+ * unsafe to reference it.
+ *
+ * @param pcb the tcp_pcb to close
+ * @return ERR_OK if connection has been closed
+ *         another err_t if closing failed and pcb is not freed
+ */
+static err_t
+tcp_close_shutdown(struct tcp_pcb *pcb, u8_t rst_on_unacked_data)
+{
+  if (rst_on_unacked_data && ((pcb->state == ESTABLISHED) || (pcb->state == CLOSE_WAIT))) {
+    if ((pcb->refused_data != NULL) || (pcb->rcv_wnd != TCP_WND_MAX(pcb))) {
+      /* Not all data received by application, send RST to tell the remote
+         side about this. */
+      LWIP_ASSERT("pcb->flags & TF_RXCLOSED", pcb->flags & TF_RXCLOSED);
+
+      /* don't call tcp_abort here: we must not deallocate the pcb since
+         that might not be expected when calling tcp_close */
+      tcp_rst(pcb->snd_nxt, pcb->rcv_nxt, &pcb->local_ip, &pcb->remote_ip,
+               pcb->local_port, pcb->remote_port);
+
+      tcp_pcb_purge(pcb);
+      TCP_RMV_ACTIVE(pcb);
+      if (pcb->state == ESTABLISHED) {
+        /* move to TIME_WAIT since we close actively */
+        pcb->state = TIME_WAIT;
+        TCP_REG(&tcp_tw_pcbs, pcb);
+      } else {
+        /* CLOSE_WAIT: deallocate the pcb since we already sent a RST for it */
+        if (tcp_input_pcb == pcb) {
+          /* prevent using a deallocated pcb: free it from tcp_input later */
+          tcp_trigger_input_pcb_close();
+        } else {
+          memp_free(MEMP_TCP_PCB, pcb);
+        }
+      }
+      return ERR_OK;
+    }
+  }
+
+  /* - states which free the pcb are handled here,
+     - states which send FIN and change state are handled in tcp_close_shutdown_fin() */
+  switch (pcb->state) {
+  case CLOSED:
+    /* Closing a pcb in the CLOSED state might seem erroneous,
+     * however, it is in this state once allocated and as yet unused
+     * and the user needs some way to free it should the need arise.
+     * Calling tcp_close() with a pcb that has already been closed, (i.e. twice)
+     * or for a pcb that has been used and then entered the CLOSED state
+     * is erroneous, but this should never happen as the pcb has in those cases
+     * been freed, and so any remaining handles are bogus. */
+    if (pcb->local_port != 0) {
+      TCP_RMV(&tcp_bound_pcbs, pcb);
+    }
+    memp_free(MEMP_TCP_PCB, pcb);
+    break;
+  case LISTEN:
+    tcp_listen_closed(pcb);
+    tcp_pcb_remove(&tcp_listen_pcbs.pcbs, pcb);
+    memp_free(MEMP_TCP_PCB_LISTEN, pcb);
+    break;
+  case SYN_SENT:
+    TCP_PCB_REMOVE_ACTIVE(pcb);
+    memp_free(MEMP_TCP_PCB, pcb);
+    MIB2_STATS_INC(mib2.tcpattemptfails);
+    break;
+  default:
+    return tcp_close_shutdown_fin(pcb);
+  }
+  return ERR_OK;
+}
+
+static err_t
+tcp_close_shutdown_fin(struct tcp_pcb *pcb)
+{
+  err_t err;
+  LWIP_ASSERT("pcb != NULL", pcb != NULL);
+
+  switch (pcb->state) {
+  case SYN_RCVD:
+    err = tcp_send_fin(pcb);
+    if (err == ERR_OK) {
+      tcp_backlog_accepted(pcb);
+      MIB2_STATS_INC(mib2.tcpattemptfails);
+      pcb->state = FIN_WAIT_1;
+    }
+    break;
+  case ESTABLISHED:
+    err = tcp_send_fin(pcb);
+    if (err == ERR_OK) {
+      MIB2_STATS_INC(mib2.tcpestabresets);
+      pcb->state = FIN_WAIT_1;
+    }
+    break;
+  case CLOSE_WAIT:
+    err = tcp_send_fin(pcb);
+    if (err == ERR_OK) {
+      MIB2_STATS_INC(mib2.tcpestabresets);
+      pcb->state = LAST_ACK;
+    }
+    break;
+  default:
+    /* Has already been closed, do nothing. */
+    return ERR_OK;
+    break;
+  }
+
+  if (err == ERR_OK) {
+    /* To ensure all data has been sent when tcp_close returns, we have
+       to make sure tcp_output doesn't fail.
+       Since we don't really have to ensure all data has been sent when tcp_close
+       returns (unsent data is sent from tcp timer functions, also), we don't care
+       for the return value of tcp_output for now. */
+    tcp_output(pcb);
+  } else if (err == ERR_MEM) {
+    /* Mark this pcb for closing. Closing is retried from tcp_tmr. */
+    pcb->flags |= TF_CLOSEPEND;
+  }
+  return err;
+}
+
+/**
+ * @ingroup tcp_raw
+ * Closes the connection held by the PCB.
+ *
+ * Listening pcbs are freed and may not be referenced any more.
+ * Connection pcbs are freed if not yet connected and may not be referenced
+ * any more. If a connection is established (at least SYN received or in
+ * a closing state), the connection is closed, and put in a closing state.
+ * The pcb is then automatically freed in tcp_slowtmr(). It is therefore
+ * unsafe to reference it (unless an error is returned).
+ *
+ * @param pcb the tcp_pcb to close
+ * @return ERR_OK if connection has been closed
+ *         another err_t if closing failed and pcb is not freed
+ */
+err_t
+tcp_close(struct tcp_pcb *pcb)
+{
+  LWIP_DEBUGF(TCP_DEBUG, ("tcp_close: closing in "));
+  tcp_debug_print_state(pcb->state);
+
+  if (pcb->state != LISTEN) {
+    /* Set a flag not to receive any more data... */
+    pcb->flags |= TF_RXCLOSED;
+  }
+  /* ... and close */
+  return tcp_close_shutdown(pcb, 1);
+}
+
+/**
+ * @ingroup tcp_raw
+ * Causes all or part of a full-duplex connection of this PCB to be shut down.
+ * This doesn't deallocate the PCB unless shutting down both sides!
+ * Shutting down both sides is the same as calling tcp_close, so if it succeds,
+ * the PCB should not be referenced any more.
+ *
+ * @param pcb PCB to shutdown
+ * @param shut_rx shut down receive side if this is != 0
+ * @param shut_tx shut down send side if this is != 0
+ * @return ERR_OK if shutdown succeeded (or the PCB has already been shut down)
+ *         another err_t on error.
+ */
+err_t
+tcp_shutdown(struct tcp_pcb *pcb, int shut_rx, int shut_tx)
+{
+  if (pcb->state == LISTEN) {
+    return ERR_CONN;
+  }
+  if (shut_rx) {
+    /* shut down the receive side: set a flag not to receive any more data... */
+    pcb->flags |= TF_RXCLOSED;
+    if (shut_tx) {
+      /* shutting down the tx AND rx side is the same as closing for the raw API */
+      return tcp_close_shutdown(pcb, 1);
+    }
+    /* ... and free buffered data */
+    if (pcb->refused_data != NULL) {
+      pbuf_free(pcb->refused_data);
+      pcb->refused_data = NULL;
+    }
+  }
+  if (shut_tx) {
+    /* This can't happen twice since if it succeeds, the pcb's state is changed.
+       Only close in these states as the others directly deallocate the PCB */
+    switch (pcb->state) {
+    case SYN_RCVD:
+    case ESTABLISHED:
+    case CLOSE_WAIT:
+      return tcp_close_shutdown(pcb, (u8_t)shut_rx);
+    default:
+      /* Not (yet?) connected, cannot shutdown the TX side as that would bring us
+        into CLOSED state, where the PCB is deallocated. */
+      return ERR_CONN;
+    }
+  }
+  return ERR_OK;
+}
+
+/**
+ * Abandons a connection and optionally sends a RST to the remote
+ * host.  Deletes the local protocol control block. This is done when
+ * a connection is killed because of shortage of memory.
+ *
+ * @param pcb the tcp_pcb to abort
+ * @param reset boolean to indicate whether a reset should be sent
+ */
+void
+tcp_abandon(struct tcp_pcb *pcb, int reset)
+{
+  u32_t seqno, ackno;
+#if LWIP_CALLBACK_API
+  tcp_err_fn errf;
+#endif /* LWIP_CALLBACK_API */
+  void *errf_arg;
+
+  /* pcb->state LISTEN not allowed here */
+  LWIP_ASSERT("don't call tcp_abort/tcp_abandon for listen-pcbs",
+    pcb->state != LISTEN);
+  /* Figure out on which TCP PCB list we are, and remove us. If we
+     are in an active state, call the receive function associated with
+     the PCB with a NULL argument, and send an RST to the remote end. */
+  if (pcb->state == TIME_WAIT) {
+    tcp_pcb_remove(&tcp_tw_pcbs, pcb);
+    memp_free(MEMP_TCP_PCB, pcb);
+  } else {
+    int send_rst = 0;
+    u16_t local_port = 0;
+    enum tcp_state last_state;
+    seqno = pcb->snd_nxt;
+    ackno = pcb->rcv_nxt;
+#if LWIP_CALLBACK_API
+    errf = pcb->errf;
+#endif /* LWIP_CALLBACK_API */
+    errf_arg = pcb->callback_arg;
+    if (pcb->state == CLOSED) {
+      if (pcb->local_port != 0) {
+        /* bound, not yet opened */
+        TCP_RMV(&tcp_bound_pcbs, pcb);
+      }
+    } else {
+      send_rst = reset;
+      local_port = pcb->local_port;
+      TCP_PCB_REMOVE_ACTIVE(pcb);
+    }
+    if (pcb->unacked != NULL) {
+      tcp_segs_free(pcb->unacked);
+    }
+    if (pcb->unsent != NULL) {
+      tcp_segs_free(pcb->unsent);
+    }
+#if TCP_QUEUE_OOSEQ
+    if (pcb->ooseq != NULL) {
+      tcp_segs_free(pcb->ooseq);
+    }
+#endif /* TCP_QUEUE_OOSEQ */
+    tcp_backlog_accepted(pcb);
+    if (send_rst) {
+      LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_abandon: sending RST\n"));
+      tcp_rst(seqno, ackno, &pcb->local_ip, &pcb->remote_ip, local_port, pcb->remote_port);
+    }
+    last_state = pcb->state;
+    memp_free(MEMP_TCP_PCB, pcb);
+    TCP_EVENT_ERR(last_state, errf, errf_arg, ERR_ABRT);
+  }
+}
+
+/**
+ * @ingroup tcp_raw
+ * Aborts the connection by sending a RST (reset) segment to the remote
+ * host. The pcb is deallocated. This function never fails.
+ *
+ * ATTENTION: When calling this from one of the TCP callbacks, make
+ * sure you always return ERR_ABRT (and never return ERR_ABRT otherwise
+ * or you will risk accessing deallocated memory or memory leaks!
+ *
+ * @param pcb the tcp pcb to abort
+ */
+void
+tcp_abort(struct tcp_pcb *pcb)
+{
+  tcp_abandon(pcb, 1);
+}
+
+/**
+ * @ingroup tcp_raw
+ * Binds the connection to a local port number and IP address. If the
+ * IP address is not given (i.e., ipaddr == NULL), the IP address of
+ * the outgoing network interface is used instead.
+ *
+ * @param pcb the tcp_pcb to bind (no check is done whether this pcb is
+ *        already bound!)
+ * @param ipaddr the local ip address to bind to (use IP4_ADDR_ANY to bind
+ *        to any local address
+ * @param port the local port to bind to
+ * @return ERR_USE if the port is already in use
+ *         ERR_VAL if bind failed because the PCB is not in a valid state
+ *         ERR_OK if bound
+ */
+err_t
+tcp_bind(struct tcp_pcb *pcb, const ip_addr_t *ipaddr, u16_t port)
+{
+  int i;
+  int max_pcb_list = NUM_TCP_PCB_LISTS;
+  struct tcp_pcb *cpcb;
+
+#if LWIP_IPV4
+  /* Don't propagate NULL pointer (IPv4 ANY) to subsequent functions */
+  if (ipaddr == NULL) {
+    ipaddr = IP4_ADDR_ANY;
+  }
+#endif /* LWIP_IPV4 */
+
+  /* still need to check for ipaddr == NULL in IPv6 only case */
+  if ((pcb == NULL) || (ipaddr == NULL)) {
+    return ERR_VAL;
+  }
+
+  LWIP_ERROR("tcp_bind: can only bind in state CLOSED", pcb->state == CLOSED, return ERR_VAL);
+
+#if SO_REUSE
+  /* Unless the REUSEADDR flag is set,
+     we have to check the pcbs in TIME-WAIT state, also.
+     We do not dump TIME_WAIT pcb's; they can still be matched by incoming
+     packets using both local and remote IP addresses and ports to distinguish.
+   */
+  if (ip_get_option(pcb, SOF_REUSEADDR)) {
+    max_pcb_list = NUM_TCP_PCB_LISTS_NO_TIME_WAIT;
+  }
+#endif /* SO_REUSE */
+
+  if (port == 0) {
+    port = tcp_new_port();
+    if (port == 0) {
+      return ERR_BUF;
+    }
+  } else {
+    /* Check if the address already is in use (on all lists) */
+    for (i = 0; i < max_pcb_list; i++) {
+      for (cpcb = *tcp_pcb_lists[i]; cpcb != NULL; cpcb = cpcb->next) {
+        if (cpcb->local_port == port) {
+#if SO_REUSE
+          /* Omit checking for the same port if both pcbs have REUSEADDR set.
+             For SO_REUSEADDR, the duplicate-check for a 5-tuple is done in
+             tcp_connect. */
+          if (!ip_get_option(pcb, SOF_REUSEADDR) ||
+              !ip_get_option(cpcb, SOF_REUSEADDR))
+#endif /* SO_REUSE */
+          {
+            /* @todo: check accept_any_ip_version */
+            if ((IP_IS_V6(ipaddr) == IP_IS_V6_VAL(cpcb->local_ip)) &&
+                (ip_addr_isany(&cpcb->local_ip) ||
+                ip_addr_isany(ipaddr) ||
+                ip_addr_cmp(&cpcb->local_ip, ipaddr))) {
+              return ERR_USE;
+            }
+          }
+        }
+      }
+    }
+  }
+
+  if (!ip_addr_isany(ipaddr)) {
+    ip_addr_set(&pcb->local_ip, ipaddr);
+  }
+  pcb->local_port = port;
+  TCP_REG(&tcp_bound_pcbs, pcb);
+  LWIP_DEBUGF(TCP_DEBUG, ("tcp_bind: bind to port %"U16_F"\n", port));
+  return ERR_OK;
+}
+#if LWIP_CALLBACK_API
+/**
+ * Default accept callback if no accept callback is specified by the user.
+ */
+static err_t
+tcp_accept_null(void *arg, struct tcp_pcb *pcb, err_t err)
+{
+  LWIP_UNUSED_ARG(arg);
+  LWIP_UNUSED_ARG(err);
+
+  tcp_abort(pcb);
+
+  return ERR_ABRT;
+}
+#endif /* LWIP_CALLBACK_API */
+
+/**
+ * @ingroup tcp_raw
+ * Set the state of the connection to be LISTEN, which means that it
+ * is able to accept incoming connections. The protocol control block
+ * is reallocated in order to consume less memory. Setting the
+ * connection to LISTEN is an irreversible process.
+ *
+ * @param pcb the original tcp_pcb
+ * @param backlog the incoming connections queue limit
+ * @return tcp_pcb used for listening, consumes less memory.
+ *
+ * @note The original tcp_pcb is freed. This function therefore has to be
+ *       called like this:
+ *             tpcb = tcp_listen_with_backlog(tpcb, backlog);
+ */
+struct tcp_pcb *
+tcp_listen_with_backlog(struct tcp_pcb *pcb, u8_t backlog)
+{
+  return tcp_listen_with_backlog_and_err(pcb, backlog, NULL);
+}
+
+/**
+ * @ingroup tcp_raw
+ * Set the state of the connection to be LISTEN, which means that it
+ * is able to accept incoming connections. The protocol control block
+ * is reallocated in order to consume less memory. Setting the
+ * connection to LISTEN is an irreversible process.
+ *
+ * @param pcb the original tcp_pcb
+ * @param backlog the incoming connections queue limit
+ * @param err when NULL is returned, this contains the error reason
+ * @return tcp_pcb used for listening, consumes less memory.
+ *
+ * @note The original tcp_pcb is freed. This function therefore has to be
+ *       called like this:
+ *             tpcb = tcp_listen_with_backlog_and_err(tpcb, backlog, &err);
+ */
+struct tcp_pcb *
+tcp_listen_with_backlog_and_err(struct tcp_pcb *pcb, u8_t backlog, err_t *err)
+{
+  struct tcp_pcb_listen *lpcb = NULL;
+  err_t res;
+
+  LWIP_UNUSED_ARG(backlog);
+  LWIP_ERROR("tcp_listen: pcb already connected", pcb->state == CLOSED, res = ERR_CLSD; goto done);
+
+  /* already listening? */
+  if (pcb->state == LISTEN) {
+    lpcb = (struct tcp_pcb_listen*)pcb;
+    res = ERR_ALREADY;
+    goto done;
+  }
+#if SO_REUSE
+  if (ip_get_option(pcb, SOF_REUSEADDR)) {
+    /* Since SOF_REUSEADDR allows reusing a local address before the pcb's usage
+       is declared (listen-/connection-pcb), we have to make sure now that
+       this port is only used once for every local IP. */
+    for (lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) {
+      if ((lpcb->local_port == pcb->local_port) &&
+          ip_addr_cmp(&lpcb->local_ip, &pcb->local_ip)) {
+        /* this address/port is already used */
+        lpcb = NULL;
+        res = ERR_USE;
+        goto done;
+      }
+    }
+  }
+#endif /* SO_REUSE */
+  lpcb = (struct tcp_pcb_listen *)memp_malloc(MEMP_TCP_PCB_LISTEN);
+  if (lpcb == NULL) {
+    res = ERR_MEM;
+    goto done;
+  }
+  lpcb->callback_arg = pcb->callback_arg;
+  lpcb->local_port = pcb->local_port;
+  lpcb->state = LISTEN;
+  lpcb->prio = pcb->prio;
+  lpcb->so_options = pcb->so_options;
+  lpcb->ttl = pcb->ttl;
+  lpcb->tos = pcb->tos;
+#if LWIP_IPV4 && LWIP_IPV6
+  IP_SET_TYPE_VAL(lpcb->remote_ip, pcb->local_ip.type);
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+  ip_addr_copy(lpcb->local_ip, pcb->local_ip);
+  if (pcb->local_port != 0) {
+    TCP_RMV(&tcp_bound_pcbs, pcb);
+  }
+  memp_free(MEMP_TCP_PCB, pcb);
+#if LWIP_CALLBACK_API
+  lpcb->accept = tcp_accept_null;
+#endif /* LWIP_CALLBACK_API */
+#if TCP_LISTEN_BACKLOG
+  lpcb->accepts_pending = 0;
+  tcp_backlog_set(lpcb, backlog);
+#endif /* TCP_LISTEN_BACKLOG */
+  TCP_REG(&tcp_listen_pcbs.pcbs, (struct tcp_pcb *)lpcb);
+  res = ERR_OK;
+done:
+  if (err != NULL) {
+    *err = res;
+  }
+  return (struct tcp_pcb *)lpcb;
+}
+
+/**
+ * Update the state that tracks the available window space to advertise.
+ *
+ * Returns how much extra window would be advertised if we sent an
+ * update now.
+ */
+u32_t
+tcp_update_rcv_ann_wnd(struct tcp_pcb *pcb)
+{
+  u32_t new_right_edge = pcb->rcv_nxt + pcb->rcv_wnd;
+
+  if (TCP_SEQ_GEQ(new_right_edge, pcb->rcv_ann_right_edge + LWIP_MIN((TCP_WND / 2), pcb->mss))) {
+    /* we can advertise more window */
+    pcb->rcv_ann_wnd = pcb->rcv_wnd;
+    return new_right_edge - pcb->rcv_ann_right_edge;
+  } else {
+    if (TCP_SEQ_GT(pcb->rcv_nxt, pcb->rcv_ann_right_edge)) {
+      /* Can happen due to other end sending out of advertised window,
+       * but within actual available (but not yet advertised) window */
+      pcb->rcv_ann_wnd = 0;
+    } else {
+      /* keep the right edge of window constant */
+      u32_t new_rcv_ann_wnd = pcb->rcv_ann_right_edge - pcb->rcv_nxt;
+#if !LWIP_WND_SCALE
+      LWIP_ASSERT("new_rcv_ann_wnd <= 0xffff", new_rcv_ann_wnd <= 0xffff);
+#endif
+      pcb->rcv_ann_wnd = (tcpwnd_size_t)new_rcv_ann_wnd;
+    }
+    return 0;
+  }
+}
+
+/**
+ * @ingroup tcp_raw
+ * This function should be called by the application when it has
+ * processed the data. The purpose is to advertise a larger window
+ * when the data has been processed.
+ *
+ * @param pcb the tcp_pcb for which data is read
+ * @param len the amount of bytes that have been read by the application
+ */
+void
+tcp_recved(struct tcp_pcb *pcb, u16_t len)
+{
+  int wnd_inflation;
+
+  /* pcb->state LISTEN not allowed here */
+  LWIP_ASSERT("don't call tcp_recved for listen-pcbs",
+    pcb->state != LISTEN);
+
+  pcb->rcv_wnd += len;
+  if (pcb->rcv_wnd > TCP_WND_MAX(pcb)) {
+    pcb->rcv_wnd = TCP_WND_MAX(pcb);
+  } else if (pcb->rcv_wnd == 0) {
+    /* rcv_wnd overflowed */
+    if ((pcb->state == CLOSE_WAIT) || (pcb->state == LAST_ACK)) {
+      /* In passive close, we allow this, since the FIN bit is added to rcv_wnd
+         by the stack itself, since it is not mandatory for an application
+         to call tcp_recved() for the FIN bit, but e.g. the netconn API does so. */
+      pcb->rcv_wnd = TCP_WND_MAX(pcb);
+    } else {
+      LWIP_ASSERT("tcp_recved: len wrapped rcv_wnd\n", 0);
+    }
+  }
+
+  wnd_inflation = tcp_update_rcv_ann_wnd(pcb);
+
+  /* If the change in the right edge of window is significant (default
+   * watermark is TCP_WND/4), then send an explicit update now.
+   * Otherwise wait for a packet to be sent in the normal course of
+   * events (or more window to be available later) */
+  if (wnd_inflation >= TCP_WND_UPDATE_THRESHOLD) {
+    tcp_ack_now(pcb);
+    tcp_output(pcb);
+  }
+
+  LWIP_DEBUGF(TCP_DEBUG, ("tcp_recved: received %"U16_F" bytes, wnd %"TCPWNDSIZE_F" (%"TCPWNDSIZE_F").\n",
+         len, pcb->rcv_wnd, (u16_t)(TCP_WND_MAX(pcb) - pcb->rcv_wnd)));
+}
+
+/**
+ * Allocate a new local TCP port.
+ *
+ * @return a new (free) local TCP port number
+ */
+static u16_t
+tcp_new_port(void)
+{
+  u8_t i;
+  u16_t n = 0;
+  struct tcp_pcb *pcb;
+
+again:
+  if (tcp_port++ == TCP_LOCAL_PORT_RANGE_END) {
+    tcp_port = TCP_LOCAL_PORT_RANGE_START;
+  }
+  /* Check all PCB lists. */
+  for (i = 0; i < NUM_TCP_PCB_LISTS; i++) {
+    for (pcb = *tcp_pcb_lists[i]; pcb != NULL; pcb = pcb->next) {
+      if (pcb->local_port == tcp_port) {
+        if (++n > (TCP_LOCAL_PORT_RANGE_END - TCP_LOCAL_PORT_RANGE_START)) {
+          return 0;
+        }
+        goto again;
+      }
+    }
+  }
+  return tcp_port;
+}
+
+/**
+ * @ingroup tcp_raw
+ * Connects to another host. The function given as the "connected"
+ * argument will be called when the connection has been established.
+ *
+ * @param pcb the tcp_pcb used to establish the connection
+ * @param ipaddr the remote ip address to connect to
+ * @param port the remote tcp port to connect to
+ * @param connected callback function to call when connected (on error,
+                    the err calback will be called)
+ * @return ERR_VAL if invalid arguments are given
+ *         ERR_OK if connect request has been sent
+ *         other err_t values if connect request couldn't be sent
+ */
+err_t
+tcp_connect(struct tcp_pcb *pcb, const ip_addr_t *ipaddr, u16_t port,
+      tcp_connected_fn connected)
+{
+  err_t ret;
+  u32_t iss;
+  u16_t old_local_port;
+
+  if ((pcb == NULL) || (ipaddr == NULL)) {
+    return ERR_VAL;
+  }
+
+  LWIP_ERROR("tcp_connect: can only connect from state CLOSED", pcb->state == CLOSED, return ERR_ISCONN);
+
+  LWIP_DEBUGF(TCP_DEBUG, ("tcp_connect to port %"U16_F"\n", port));
+  ip_addr_set(&pcb->remote_ip, ipaddr);
+  pcb->remote_port = port;
+
+  /* check if we have a route to the remote host */
+  if (ip_addr_isany(&pcb->local_ip)) {
+    /* no local IP address set, yet. */
+    struct netif *netif;
+    const ip_addr_t *local_ip;
+    ip_route_get_local_ip(&pcb->local_ip, &pcb->remote_ip, netif, local_ip);
+    if ((netif == NULL) || (local_ip == NULL)) {
+      /* Don't even try to send a SYN packet if we have no route
+         since that will fail. */
+      return ERR_RTE;
+    }
+    /* Use the address as local address of the pcb. */
+    ip_addr_copy(pcb->local_ip, *local_ip);
+  }
+
+  old_local_port = pcb->local_port;
+  if (pcb->local_port == 0) {
+    pcb->local_port = tcp_new_port();
+    if (pcb->local_port == 0) {
+      return ERR_BUF;
+    }
+  } else {
+#if SO_REUSE
+    if (ip_get_option(pcb, SOF_REUSEADDR)) {
+      /* Since SOF_REUSEADDR allows reusing a local address, we have to make sure
+         now that the 5-tuple is unique. */
+      struct tcp_pcb *cpcb;
+      int i;
+      /* Don't check listen- and bound-PCBs, check active- and TIME-WAIT PCBs. */
+      for (i = 2; i < NUM_TCP_PCB_LISTS; i++) {
+        for (cpcb = *tcp_pcb_lists[i]; cpcb != NULL; cpcb = cpcb->next) {
+          if ((cpcb->local_port == pcb->local_port) &&
+              (cpcb->remote_port == port) &&
+              ip_addr_cmp(&cpcb->local_ip, &pcb->local_ip) &&
+              ip_addr_cmp(&cpcb->remote_ip, ipaddr)) {
+            /* linux returns EISCONN here, but ERR_USE should be OK for us */
+            return ERR_USE;
+          }
+        }
+      }
+    }
+#endif /* SO_REUSE */
+  }
+
+  iss = tcp_next_iss(pcb);
+  pcb->rcv_nxt = 0;
+  pcb->snd_nxt = iss;
+  pcb->lastack = iss - 1;
+  pcb->snd_wl2 = iss - 1;
+  pcb->snd_lbb = iss - 1;
+  /* Start with a window that does not need scaling. When window scaling is
+     enabled and used, the window is enlarged when both sides agree on scaling. */
+  pcb->rcv_wnd = pcb->rcv_ann_wnd = TCPWND_MIN16(TCP_WND);
+  pcb->rcv_ann_right_edge = pcb->rcv_nxt;
+  pcb->snd_wnd = TCP_WND;
+  /* As initial send MSS, we use TCP_MSS but limit it to 536.
+     The send MSS is updated when an MSS option is received. */
+  pcb->mss = INITIAL_MSS;
+#if TCP_CALCULATE_EFF_SEND_MSS
+  pcb->mss = tcp_eff_send_mss(pcb->mss, &pcb->local_ip, &pcb->remote_ip);
+#endif /* TCP_CALCULATE_EFF_SEND_MSS */
+  pcb->cwnd = 1;
+#if LWIP_CALLBACK_API
+  pcb->connected = connected;
+#else /* LWIP_CALLBACK_API */
+  LWIP_UNUSED_ARG(connected);
+#endif /* LWIP_CALLBACK_API */
+
+  /* Send a SYN together with the MSS option. */
+  ret = tcp_enqueue_flags(pcb, TCP_SYN);
+  if (ret == ERR_OK) {
+    /* SYN segment was enqueued, changed the pcbs state now */
+    pcb->state = SYN_SENT;
+    if (old_local_port != 0) {
+      TCP_RMV(&tcp_bound_pcbs, pcb);
+    }
+    TCP_REG_ACTIVE(pcb);
+    MIB2_STATS_INC(mib2.tcpactiveopens);
+
+    tcp_output(pcb);
+  }
+  return ret;
+}
+
+/**
+ * Called every 500 ms and implements the retransmission timer and the timer that
+ * removes PCBs that have been in TIME-WAIT for enough time. It also increments
+ * various timers such as the inactivity timer in each PCB.
+ *
+ * Automatically called from tcp_tmr().
+ */
+void
+tcp_slowtmr(void)
+{
+  struct tcp_pcb *pcb, *prev;
+  tcpwnd_size_t eff_wnd;
+  u8_t pcb_remove;      /* flag if a PCB should be removed */
+  u8_t pcb_reset;       /* flag if a RST should be sent when removing */
+  err_t err;
+
+  err = ERR_OK;
+
+  ++tcp_ticks;
+  ++tcp_timer_ctr;
+
+tcp_slowtmr_start:
+  /* Steps through all of the active PCBs. */
+  prev = NULL;
+  pcb = tcp_active_pcbs;
+  if (pcb == NULL) {
+    LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: no active pcbs\n"));
+  }
+  while (pcb != NULL) {
+    LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: processing active pcb\n"));
+    LWIP_ASSERT("tcp_slowtmr: active pcb->state != CLOSED\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);
+    if (pcb->last_timer == tcp_timer_ctr) {
+      /* skip this pcb, we have already processed it */
+      pcb = pcb->next;
+      continue;
+    }
+    pcb->last_timer = tcp_timer_ctr;
+
+    pcb_remove = 0;
+    pcb_reset = 0;
+
+    if (pcb->state == SYN_SENT && pcb->nrtx >= TCP_SYNMAXRTX) {
+      ++pcb_remove;
+      LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: max SYN retries reached\n"));
+    }
+    else if (pcb->nrtx >= TCP_MAXRTX) {
+      ++pcb_remove;
+      LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: max DATA retries reached\n"));
+    } else {
+      if (pcb->persist_backoff > 0) {
+        /* If snd_wnd is zero, use persist timer to send 1 byte probes
+         * instead of using the standard retransmission mechanism. */
+        u8_t backoff_cnt = tcp_persist_backoff[pcb->persist_backoff-1];
+        if (pcb->persist_cnt < backoff_cnt) {
+          pcb->persist_cnt++;
+        }
+        if (pcb->persist_cnt >= backoff_cnt) {
+          if (tcp_zero_window_probe(pcb) == ERR_OK) {
+            pcb->persist_cnt = 0;
+            if (pcb->persist_backoff < sizeof(tcp_persist_backoff)) {
+              pcb->persist_backoff++;
+            }
+          }
+        }
+      } else {
+        /* Increase the retransmission timer if it is running */
+        if (pcb->rtime >= 0) {
+          ++pcb->rtime;
+        }
+
+        if (pcb->unacked != NULL && pcb->rtime >= pcb->rto) {
+          /* Time for a retransmission. */
+          LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_slowtmr: rtime %"S16_F
+                                      " pcb->rto %"S16_F"\n",
+                                      pcb->rtime, pcb->rto));
+
+          /* Double retransmission time-out unless we are trying to
+           * connect to somebody (i.e., we are in SYN_SENT). */
+          if (pcb->state != SYN_SENT) {
+            u8_t backoff_idx = LWIP_MIN(pcb->nrtx, sizeof(tcp_backoff)-1);
+            pcb->rto = ((pcb->sa >> 3) + pcb->sv) << tcp_backoff[backoff_idx];
+          }
+
+          /* Reset the retransmission timer. */
+          pcb->rtime = 0;
+
+          /* Reduce congestion window and ssthresh. */
+          eff_wnd = LWIP_MIN(pcb->cwnd, pcb->snd_wnd);
+          pcb->ssthresh = eff_wnd >> 1;
+          if (pcb->ssthresh < (tcpwnd_size_t)(pcb->mss << 1)) {
+            pcb->ssthresh = (pcb->mss << 1);
+          }
+          pcb->cwnd = pcb->mss;
+          LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_slowtmr: cwnd %"TCPWNDSIZE_F
+                                       " ssthresh %"TCPWNDSIZE_F"\n",
+                                       pcb->cwnd, pcb->ssthresh));
+
+          /* 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 this PCB is in FIN_WAIT_2 because of SHUT_WR don't let it time out. */
+      if (pcb->flags & TF_RXCLOSED) {
+        /* PCB was fully closed (either through close() or SHUT_RDWR):
+           normal FIN-WAIT timeout handling. */
+        if ((u32_t)(tcp_ticks - pcb->tmr) >
+            TCP_FIN_WAIT_TIMEOUT / TCP_SLOW_INTERVAL) {
+          ++pcb_remove;
+          LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: removing pcb stuck in FIN-WAIT-2\n"));
+        }
+      }
+    }
+
+    /* Check if KEEPALIVE should be sent */
+    if (ip_get_option(pcb, SOF_KEEPALIVE) &&
+       ((pcb->state == ESTABLISHED) ||
+        (pcb->state == CLOSE_WAIT))) {
+      if ((u32_t)(tcp_ticks - pcb->tmr) >
+         (pcb->keep_idle + TCP_KEEP_DUR(pcb)) / TCP_SLOW_INTERVAL)
+      {
+        LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: KEEPALIVE timeout. Aborting connection to "));
+        ip_addr_debug_print(TCP_DEBUG, &pcb->remote_ip);
+        LWIP_DEBUGF(TCP_DEBUG, ("\n"));
+
+        ++pcb_remove;
+        ++pcb_reset;
+      } else if ((u32_t)(tcp_ticks - pcb->tmr) >
+                (pcb->keep_idle + pcb->keep_cnt_sent * TCP_KEEP_INTVL(pcb))
+                / TCP_SLOW_INTERVAL)
+      {
+        err = tcp_keepalive(pcb);
+        if (err == ERR_OK) {
+          pcb->keep_cnt_sent++;
+        }
+      }
+    }
+
+    /* If this PCB has queued out of sequence data, but has been
+       inactive for too long, will drop the data (it will eventually
+       be retransmitted). */
+#if TCP_QUEUE_OOSEQ
+    if (pcb->ooseq != NULL &&
+        (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) {
+      struct tcp_pcb *pcb2;
+#if LWIP_CALLBACK_API
+      tcp_err_fn err_fn = pcb->errf;
+#endif /* LWIP_CALLBACK_API */
+      void *err_arg;
+      enum tcp_state last_state;
+      tcp_pcb_purge(pcb);
+      /* Remove PCB from tcp_active_pcbs list. */
+      if (prev != NULL) {
+        LWIP_ASSERT("tcp_slowtmr: middle tcp != tcp_active_pcbs", pcb != tcp_active_pcbs);
+        prev->next = pcb->next;
+      } else {
+        /* This PCB was the first. */
+        LWIP_ASSERT("tcp_slowtmr: first pcb == tcp_active_pcbs", tcp_active_pcbs == pcb);
+        tcp_active_pcbs = pcb->next;
+      }
+
+      if (pcb_reset) {
+        tcp_rst(pcb->snd_nxt, pcb->rcv_nxt, &pcb->local_ip, &pcb->remote_ip,
+                 pcb->local_port, pcb->remote_port);
+      }
+
+      err_arg = pcb->callback_arg;
+      last_state = pcb->state;
+      pcb2 = pcb;
+      pcb = pcb->next;
+      memp_free(MEMP_TCP_PCB, pcb2);
+
+      tcp_active_pcbs_changed = 0;
+      TCP_EVENT_ERR(last_state, err_fn, err_arg, ERR_ABRT);
+      if (tcp_active_pcbs_changed) {
+        goto tcp_slowtmr_start;
+      }
+    } else {
+      /* get the 'next' element now and work with 'prev' below (in case of abort) */
+      prev = pcb;
+      pcb = pcb->next;
+
+      /* We check if we should poll the connection. */
+      ++prev->polltmr;
+      if (prev->polltmr >= prev->pollinterval) {
+        prev->polltmr = 0;
+        LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: polling application\n"));
+        tcp_active_pcbs_changed = 0;
+        TCP_EVENT_POLL(prev, err);
+        if (tcp_active_pcbs_changed) {
+          goto tcp_slowtmr_start;
+        }
+        /* if err == ERR_ABRT, 'prev' is already deallocated */
+        if (err == ERR_OK) {
+          tcp_output(prev);
+        }
+      }
+    }
+  }
+
+
+  /* Steps through all of the TIME-WAIT PCBs. */
+  prev = NULL;
+  pcb = tcp_tw_pcbs;
+  while (pcb != NULL) {
+    LWIP_ASSERT("tcp_slowtmr: TIME-WAIT pcb->state == TIME-WAIT", pcb->state == TIME_WAIT);
+    pcb_remove = 0;
+
+    /* Check if this PCB has stayed long enough in TIME-WAIT */
+    if ((u32_t)(tcp_ticks - pcb->tmr) > 2 * TCP_MSL / TCP_SLOW_INTERVAL) {
+      ++pcb_remove;
+    }
+
+    /* If the PCB should be removed, do it. */
+    if (pcb_remove) {
+      struct tcp_pcb *pcb2;
+      tcp_pcb_purge(pcb);
+      /* Remove PCB from tcp_tw_pcbs list. */
+      if (prev != NULL) {
+        LWIP_ASSERT("tcp_slowtmr: middle tcp != tcp_tw_pcbs", pcb != tcp_tw_pcbs);
+        prev->next = pcb->next;
+      } else {
+        /* This PCB was the first. */
+        LWIP_ASSERT("tcp_slowtmr: first pcb == tcp_tw_pcbs", tcp_tw_pcbs == pcb);
+        tcp_tw_pcbs = pcb->next;
+      }
+      pcb2 = pcb;
+      pcb = pcb->next;
+      memp_free(MEMP_TCP_PCB, pcb2);
+    } else {
+      prev = pcb;
+      pcb = pcb->next;
+    }
+  }
+}
+
+/**
+ * Is called every TCP_FAST_INTERVAL (250 ms) and process data previously
+ * "refused" by upper layer (application) and sends delayed ACKs.
+ *
+ * Automatically called from tcp_tmr().
+ */
+void
+tcp_fasttmr(void)
+{
+  struct tcp_pcb *pcb;
+
+  ++tcp_timer_ctr;
+
+tcp_fasttmr_start:
+  pcb = tcp_active_pcbs;
+
+  while (pcb != NULL) {
+    if (pcb->last_timer != tcp_timer_ctr) {
+      struct tcp_pcb *next;
+      pcb->last_timer = tcp_timer_ctr;
+      /* send delayed ACKs */
+      if (pcb->flags & TF_ACK_DELAY) {
+        LWIP_DEBUGF(TCP_DEBUG, ("tcp_fasttmr: delayed ACK\n"));
+        tcp_ack_now(pcb);
+        tcp_output(pcb);
+        pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW);
+      }
+      /* send pending FIN */
+      if (pcb->flags & TF_CLOSEPEND) {
+        LWIP_DEBUGF(TCP_DEBUG, ("tcp_fasttmr: pending FIN\n"));
+        pcb->flags &= ~(TF_CLOSEPEND);
+        tcp_close_shutdown_fin(pcb);
+      }
+
+      next = pcb->next;
+
+      /* If there is data which was previously "refused" by upper layer */
+      if (pcb->refused_data != NULL) {
+        tcp_active_pcbs_changed = 0;
+        tcp_process_refused_data(pcb);
+        if (tcp_active_pcbs_changed) {
+          /* application callback has changed the pcb list: restart the loop */
+          goto tcp_fasttmr_start;
+        }
+      }
+      pcb = next;
+    } else {
+      pcb = pcb->next;
+    }
+  }
+}
+
+/** Call tcp_output for all active pcbs that have TF_NAGLEMEMERR set */
+void
+tcp_txnow(void)
+{
+  struct tcp_pcb *pcb;
+
+  for (pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
+    if (pcb->flags & TF_NAGLEMEMERR) {
+      tcp_output(pcb);
+    }
+  }
+}
+
+/** Pass pcb->refused_data to the recv callback */
+err_t
+tcp_process_refused_data(struct tcp_pcb *pcb)
+{
+#if TCP_QUEUE_OOSEQ && LWIP_WND_SCALE
+  struct pbuf *rest;
+  while (pcb->refused_data != NULL)
+#endif /* TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */
+  {
+    err_t err;
+    u8_t refused_flags = pcb->refused_data->flags;
+    /* set pcb->refused_data to NULL in case the callback frees it and then
+       closes the pcb */
+    struct pbuf *refused_data = pcb->refused_data;
+#if TCP_QUEUE_OOSEQ && LWIP_WND_SCALE
+    pbuf_split_64k(refused_data, &rest);
+    pcb->refused_data = rest;
+#else /* TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */
+    pcb->refused_data = NULL;
+#endif /* TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */
+    /* Notify again application with data previously received. */
+    LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: notify kept packet\n"));
+    TCP_EVENT_RECV(pcb, refused_data, ERR_OK, err);
+    if (err == ERR_OK) {
+      /* did refused_data include a FIN? */
+      if (refused_flags & PBUF_FLAG_TCP_FIN
+#if TCP_QUEUE_OOSEQ && LWIP_WND_SCALE
+          && (rest == NULL)
+#endif /* TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */
+         ) {
+        /* correct rcv_wnd as the application won't call tcp_recved()
+           for the FIN's seqno */
+        if (pcb->rcv_wnd != TCP_WND_MAX(pcb)) {
+          pcb->rcv_wnd++;
+        }
+        TCP_EVENT_CLOSED(pcb, err);
+        if (err == ERR_ABRT) {
+          return ERR_ABRT;
+        }
+      }
+    } else if (err == ERR_ABRT) {
+      /* if err == ERR_ABRT, 'pcb' is already deallocated */
+      /* Drop incoming packets because pcb is "full" (only if the incoming
+         segment contains data). */
+      LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: drop incoming packets, because pcb is \"full\"\n"));
+      return ERR_ABRT;
+    } else {
+      /* data is still refused, pbuf is still valid (go on for ACK-only packets) */
+#if TCP_QUEUE_OOSEQ && LWIP_WND_SCALE
+      if (rest != NULL) {
+        pbuf_cat(refused_data, rest);
+      }
+#endif /* TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */
+      pcb->refused_data = refused_data;
+      return ERR_INPROGRESS;
+    }
+  }
+  return ERR_OK;
+}
+
+/**
+ * Deallocates a list of TCP segments (tcp_seg structures).
+ *
+ * @param seg tcp_seg list of TCP segments to free
+ */
+void
+tcp_segs_free(struct tcp_seg *seg)
+{
+  while (seg != NULL) {
+    struct tcp_seg *next = seg->next;
+    tcp_seg_free(seg);
+    seg = next;
+  }
+}
+
+/**
+ * Frees a TCP segment (tcp_seg structure).
+ *
+ * @param seg single tcp_seg to free
+ */
+void
+tcp_seg_free(struct tcp_seg *seg)
+{
+  if (seg != NULL) {
+    if (seg->p != NULL) {
+      pbuf_free(seg->p);
+#if TCP_DEBUG
+      seg->p = NULL;
+#endif /* TCP_DEBUG */
+    }
+    memp_free(MEMP_TCP_SEG, seg);
+  }
+}
+
+/**
+ * Sets the priority of a connection.
+ *
+ * @param pcb the tcp_pcb to manipulate
+ * @param prio new priority
+ */
+void
+tcp_setprio(struct tcp_pcb *pcb, u8_t prio)
+{
+  pcb->prio = prio;
+}
+
+#if TCP_QUEUE_OOSEQ
+/**
+ * Returns a copy of the given TCP segment.
+ * The pbuf and data are not copied, only the pointers
+ *
+ * @param seg the old tcp_seg
+ * @return a copy of seg
+ */
+struct tcp_seg *
+tcp_seg_copy(struct tcp_seg *seg)
+{
+  struct tcp_seg *cseg;
+
+  cseg = (struct tcp_seg *)memp_malloc(MEMP_TCP_SEG);
+  if (cseg == NULL) {
+    return NULL;
+  }
+  SMEMCPY((u8_t *)cseg, (const u8_t *)seg, sizeof(struct tcp_seg));
+  pbuf_ref(cseg->p);
+  return cseg;
+}
+#endif /* TCP_QUEUE_OOSEQ */
+
+#if LWIP_CALLBACK_API
+/**
+ * Default receive callback that is called if the user didn't register
+ * a recv callback for the pcb.
+ */
+err_t
+tcp_recv_null(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
+{
+  LWIP_UNUSED_ARG(arg);
+  if (p != NULL) {
+    tcp_recved(pcb, p->tot_len);
+    pbuf_free(p);
+  } else if (err == ERR_OK) {
+    return tcp_close(pcb);
+  }
+  return ERR_OK;
+}
+#endif /* LWIP_CALLBACK_API */
+
+/**
+ * Kills the oldest active connection that has the same or lower priority than
+ * 'prio'.
+ *
+ * @param prio minimum priority
+ */
+static void
+tcp_kill_prio(u8_t prio)
+{
+  struct tcp_pcb *pcb, *inactive;
+  u32_t inactivity;
+  u8_t mprio;
+
+  mprio = LWIP_MIN(TCP_PRIO_MAX, prio);
+
+  /* We 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 <= mprio &&
+       (u32_t)(tcp_ticks - pcb->tmr) >= inactivity) {
+      inactivity = tcp_ticks - pcb->tmr;
+      inactive = pcb;
+      mprio = pcb->prio;
+    }
+  }
+  if (inactive != NULL) {
+    LWIP_DEBUGF(TCP_DEBUG, ("tcp_kill_prio: killing oldest PCB %p (%"S32_F")\n",
+           (void *)inactive, inactivity));
+    tcp_abort(inactive);
+  }
+}
+
+/**
+ * Kills the oldest connection that is in specific state.
+ * Called from tcp_alloc() for LAST_ACK and CLOSING if no more connections are available.
+ */
+static void
+tcp_kill_state(enum tcp_state state)
+{
+  struct tcp_pcb *pcb, *inactive;
+  u32_t inactivity;
+
+  LWIP_ASSERT("invalid state", (state == CLOSING) || (state == LAST_ACK));
+
+  inactivity = 0;
+  inactive = NULL;
+  /* Go through the list of active pcbs and get the oldest pcb that is in state
+     CLOSING/LAST_ACK. */
+  for (pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
+    if (pcb->state == state) {
+      if ((u32_t)(tcp_ticks - pcb->tmr) >= inactivity) {
+        inactivity = tcp_ticks - pcb->tmr;
+        inactive = pcb;
+      }
+    }
+  }
+  if (inactive != NULL) {
+    LWIP_DEBUGF(TCP_DEBUG, ("tcp_kill_closing: killing oldest %s PCB %p (%"S32_F")\n",
+           tcp_state_str[state], (void *)inactive, inactivity));
+    /* Don't send a RST, since no data is lost. */
+    tcp_abandon(inactive, 0);
+  }
+}
+
+/**
+ * Kills the oldest connection that is in TIME_WAIT state.
+ * Called from tcp_alloc() if no more connections are available.
+ */
+static void
+tcp_kill_timewait(void)
+{
+  struct tcp_pcb *pcb, *inactive;
+  u32_t inactivity;
+
+  inactivity = 0;
+  inactive = NULL;
+  /* Go through the list of TIME_WAIT pcbs and get the oldest pcb. */
+  for (pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) {
+    if ((u32_t)(tcp_ticks - pcb->tmr) >= inactivity) {
+      inactivity = tcp_ticks - pcb->tmr;
+      inactive = pcb;
+    }
+  }
+  if (inactive != NULL) {
+    LWIP_DEBUGF(TCP_DEBUG, ("tcp_kill_timewait: killing oldest TIME-WAIT PCB %p (%"S32_F")\n",
+           (void *)inactive, inactivity));
+    tcp_abort(inactive);
+  }
+}
+
+/**
+ * Allocate a new tcp_pcb structure.
+ *
+ * @param prio priority for the new pcb
+ * @return a new tcp_pcb that initially is in state CLOSED
+ */
+struct tcp_pcb *
+tcp_alloc(u8_t prio)
+{
+  struct tcp_pcb *pcb;
+
+  pcb = (struct tcp_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();
+    /* Try to allocate a tcp_pcb again. */
+    pcb = (struct tcp_pcb *)memp_malloc(MEMP_TCP_PCB);
+    if (pcb == NULL) {
+      /* Try killing oldest connection in LAST-ACK (these wouldn't go to TIME-WAIT). */
+      LWIP_DEBUGF(TCP_DEBUG, ("tcp_alloc: killing off oldest LAST-ACK connection\n"));
+      tcp_kill_state(LAST_ACK);
+      /* Try to allocate a tcp_pcb again. */
+      pcb = (struct tcp_pcb *)memp_malloc(MEMP_TCP_PCB);
+      if (pcb == NULL) {
+        /* Try killing oldest connection in CLOSING. */
+        LWIP_DEBUGF(TCP_DEBUG, ("tcp_alloc: killing off oldest CLOSING connection\n"));
+        tcp_kill_state(CLOSING);
+        /* Try to allocate a tcp_pcb again. */
+        pcb = (struct tcp_pcb *)memp_malloc(MEMP_TCP_PCB);
+        if (pcb == NULL) {
+          /* Try killing active connections with lower priority than the new one. */
+          LWIP_DEBUGF(TCP_DEBUG, ("tcp_alloc: killing connection with prio lower than %d\n", prio));
+          tcp_kill_prio(prio);
+          /* Try to allocate a tcp_pcb again. */
+          pcb = (struct tcp_pcb *)memp_malloc(MEMP_TCP_PCB);
+          if (pcb != NULL) {
+            /* adjust err stats: memp_malloc failed multiple times before */
+            MEMP_STATS_DEC(err, MEMP_TCP_PCB);
+          }
+        }
+        if (pcb != NULL) {
+          /* adjust err stats: memp_malloc failed multiple times before */
+          MEMP_STATS_DEC(err, MEMP_TCP_PCB);
+        }
+      }
+      if (pcb != NULL) {
+        /* adjust err stats: memp_malloc failed multiple times before */
+        MEMP_STATS_DEC(err, MEMP_TCP_PCB);
+      }
+    }
+    if (pcb != NULL) {
+      /* adjust err stats: memp_malloc failed above */
+      MEMP_STATS_DEC(err, MEMP_TCP_PCB);
+    }
+  }
+  if (pcb != NULL) {
+    /* zero out the whole pcb, so there is no need to initialize members to zero */
+    memset(pcb, 0, sizeof(struct tcp_pcb));
+    pcb->prio = prio;
+    pcb->snd_buf = TCP_SND_BUF;
+    /* Start with a window that does not need scaling. When window scaling is
+       enabled and used, the window is enlarged when both sides agree on scaling. */
+    pcb->rcv_wnd = pcb->rcv_ann_wnd = TCPWND_MIN16(TCP_WND);
+    pcb->ttl = TCP_TTL;
+    /* As initial send MSS, we use TCP_MSS but limit it to 536.
+       The send MSS is updated when an MSS option is received. */
+    pcb->mss = INITIAL_MSS;
+    pcb->rto = 3000 / TCP_SLOW_INTERVAL;
+    pcb->sv = 3000 / TCP_SLOW_INTERVAL;
+    pcb->rtime = -1;
+    pcb->cwnd = 1;
+    pcb->tmr = tcp_ticks;
+    pcb->last_timer = tcp_timer_ctr;
+
+    /* RFC 5681 recommends setting ssthresh abritrarily high and gives an example
+    of using the largest advertised receive window.  We've seen complications with
+    receiving TCPs that use window scaling and/or window auto-tuning where the
+    initial advertised window is very small and then grows rapidly once the
+    connection is established. To avoid these complications, we set ssthresh to the
+    largest effective cwnd (amount of in-flight data) that the sender can have. */
+    pcb->ssthresh = TCP_SND_BUF;
+
+#if LWIP_CALLBACK_API
+    pcb->recv = tcp_recv_null;
+#endif /* LWIP_CALLBACK_API */
+
+    /* Init KEEPALIVE timer */
+    pcb->keep_idle  = TCP_KEEPIDLE_DEFAULT;
+
+#if LWIP_TCP_KEEPALIVE
+    pcb->keep_intvl = TCP_KEEPINTVL_DEFAULT;
+    pcb->keep_cnt   = TCP_KEEPCNT_DEFAULT;
+#endif /* LWIP_TCP_KEEPALIVE */
+  }
+  return pcb;
+}
+
+/**
+ * @ingroup tcp_raw
+ * Creates a new TCP protocol control block but doesn't place it on
+ * any of the TCP PCB lists.
+ * The pcb is not put on any list until binding using tcp_bind().
+ *
+ * @internal: Maybe there should be a idle TCP PCB list where these
+ * PCBs are put on. Port reservation using tcp_bind() is implemented but
+ * allocated pcbs that are not bound can't be killed automatically if wanting
+ * to allocate a pcb with higher prio (@see tcp_kill_prio())
+ *
+ * @return a new tcp_pcb that initially is in state CLOSED
+ */
+struct tcp_pcb *
+tcp_new(void)
+{
+  return tcp_alloc(TCP_PRIO_NORMAL);
+}
+
+/**
+ * @ingroup tcp_raw
+ * Creates a new TCP protocol control block but doesn't
+ * place it on any of the TCP PCB lists.
+ * The pcb is not put on any list until binding using tcp_bind().
+ *
+ * @param type IP address type, see @ref lwip_ip_addr_type definitions.
+ * If you want to listen to IPv4 and IPv6 (dual-stack) connections,
+ * supply @ref IPADDR_TYPE_ANY as argument and bind to @ref IP_ANY_TYPE.
+ * @return a new tcp_pcb that initially is in state CLOSED
+ */
+struct tcp_pcb *
+tcp_new_ip_type(u8_t type)
+{
+  struct tcp_pcb * pcb;
+  pcb = tcp_alloc(TCP_PRIO_NORMAL);
+#if LWIP_IPV4 && LWIP_IPV6
+  if (pcb != NULL) {
+    IP_SET_TYPE_VAL(pcb->local_ip, type);
+    IP_SET_TYPE_VAL(pcb->remote_ip, type);
+  }
+#else
+  LWIP_UNUSED_ARG(type);
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+  return pcb;
+}
+
+/**
+ * @ingroup tcp_raw
+ * Used to specify the argument that should be passed callback
+ * functions.
+ *
+ * @param pcb tcp_pcb to set the callback argument
+ * @param arg void pointer argument to pass to callback functions
+ */
+void
+tcp_arg(struct tcp_pcb *pcb, void *arg)
+{
+  /* This function is allowed to be called for both listen pcbs and
+     connection pcbs. */
+  if (pcb != NULL) {
+    pcb->callback_arg = arg;
+  }
+}
+#if LWIP_CALLBACK_API
+
+/**
+ * @ingroup tcp_raw
+ * Used to specify the function that should be called when a TCP
+ * connection receives data.
+ *
+ * @param pcb tcp_pcb to set the recv callback
+ * @param recv callback function to call for this pcb when data is received
+ */
+void
+tcp_recv(struct tcp_pcb *pcb, tcp_recv_fn recv)
+{
+  if (pcb != NULL) {
+    LWIP_ASSERT("invalid socket state for recv callback", pcb->state != LISTEN);
+    pcb->recv = recv;
+  }
+}
+
+/**
+ * @ingroup tcp_raw
+ * Used to specify the function that should be called when TCP data
+ * has been successfully delivered to the remote host.
+ *
+ * @param pcb tcp_pcb to set the sent callback
+ * @param sent callback function to call for this pcb when data is successfully sent
+ */
+void
+tcp_sent(struct tcp_pcb *pcb, tcp_sent_fn sent)
+{
+  if (pcb != NULL) {
+    LWIP_ASSERT("invalid socket state for sent callback", pcb->state != LISTEN);
+    pcb->sent = sent;
+  }
+}
+
+/**
+ * @ingroup tcp_raw
+ * Used to specify the function that should be called when a fatal error
+ * has occurred on the connection.
+ *
+ * @note The corresponding pcb is already freed when this callback is called!
+ * 
+ * @param pcb tcp_pcb to set the err callback
+ * @param err callback function to call for this pcb when a fatal error
+ *        has occurred on the connection
+ */
+void
+tcp_err(struct tcp_pcb *pcb, tcp_err_fn err)
+{
+  if (pcb != NULL) {
+    LWIP_ASSERT("invalid socket state for err callback", pcb->state != LISTEN);
+    pcb->errf = err;
+  }
+}
+
+/**
+ * @ingroup tcp_raw
+ * Used for specifying the function that should be called when a
+ * LISTENing connection has been connected to another host.
+ *
+ * @param pcb tcp_pcb to set the accept callback
+ * @param accept callback function to call for this pcb when LISTENing
+ *        connection has been connected to another host
+ */
+void
+tcp_accept(struct tcp_pcb *pcb, tcp_accept_fn accept)
+{
+  if ((pcb != NULL) && (pcb->state == LISTEN)) {
+    struct tcp_pcb_listen *lpcb = (struct tcp_pcb_listen*)pcb;
+    lpcb->accept = accept;
+  }
+}
+#endif /* LWIP_CALLBACK_API */
+
+
+/**
+ * @ingroup tcp_raw
+ * 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, tcp_poll_fn poll, u8_t interval)
+{
+  LWIP_ASSERT("invalid socket state for poll", pcb->state != LISTEN);
+#if LWIP_CALLBACK_API
+  pcb->poll = poll;
+#else /* LWIP_CALLBACK_API */
+  LWIP_UNUSED_ARG(poll);
+#endif /* LWIP_CALLBACK_API */
+  pcb->pollinterval = interval;
+}
+
+/**
+ * Purges a TCP PCB. Removes any buffered data and frees the buffer memory
+ * (pcb->ooseq, pcb->unsent and pcb->unacked are freed).
+ *
+ * @param pcb tcp_pcb to purge. The pcb itself is not deallocated!
+ */
+void
+tcp_pcb_purge(struct tcp_pcb *pcb)
+{
+  if (pcb->state != CLOSED &&
+     pcb->state != TIME_WAIT &&
+     pcb->state != LISTEN) {
+
+    LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge\n"));
+
+    tcp_backlog_accepted(pcb);
+
+    if (pcb->refused_data != NULL) {
+      LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: data left on ->refused_data\n"));
+      pbuf_free(pcb->refused_data);
+      pcb->refused_data = NULL;
+    }
+    if (pcb->unsent != NULL) {
+      LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: not all data sent\n"));
+    }
+    if (pcb->unacked != NULL) {
+      LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: data left on ->unacked\n"));
+    }
+#if TCP_QUEUE_OOSEQ
+    if (pcb->ooseq != NULL) {
+      LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: data left on ->ooseq\n"));
+    }
+    tcp_segs_free(pcb->ooseq);
+    pcb->ooseq = NULL;
+#endif /* TCP_QUEUE_OOSEQ */
+
+    /* Stop the retransmission timer as it will expect data on unacked
+       queue if it fires */
+    pcb->rtime = -1;
+
+    tcp_segs_free(pcb->unsent);
+    tcp_segs_free(pcb->unacked);
+    pcb->unacked = pcb->unsent = NULL;
+#if TCP_OVERSIZE
+    pcb->unsent_oversize = 0;
+#endif /* TCP_OVERSIZE */
+  }
+}
+
+/**
+ * Purges the PCB and removes it from a PCB list. Any delayed ACKs are sent first.
+ *
+ * @param pcblist PCB list to purge.
+ * @param pcb tcp_pcb to purge. The pcb itself is NOT deallocated!
+ */
+void
+tcp_pcb_remove(struct tcp_pcb **pcblist, struct tcp_pcb *pcb)
+{
+  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);
+  }
+
+  if (pcb->state != LISTEN) {
+    LWIP_ASSERT("unsent segments leaking", pcb->unsent == NULL);
+    LWIP_ASSERT("unacked segments leaking", pcb->unacked == NULL);
+#if TCP_QUEUE_OOSEQ
+    LWIP_ASSERT("ooseq segments leaking", pcb->ooseq == NULL);
+#endif /* TCP_QUEUE_OOSEQ */
+  }
+
+  pcb->state = CLOSED;
+  /* reset the local port to prevent the pcb from being 'bound' */
+  pcb->local_port = 0;
+
+  LWIP_ASSERT("tcp_pcb_remove: tcp_pcbs_sane()", tcp_pcbs_sane());
+}
+
+/**
+ * Calculates a new initial sequence number for new connections.
+ *
+ * @return u32_t pseudo random sequence number
+ */
+u32_t
+tcp_next_iss(struct tcp_pcb *pcb)
+{
+#ifdef LWIP_HOOK_TCP_ISN
+  return LWIP_HOOK_TCP_ISN(&pcb->local_ip, pcb->local_port, &pcb->remote_ip, pcb->remote_port);
+#else /* LWIP_HOOK_TCP_ISN */
+  static u32_t iss = 6510;
+
+  LWIP_UNUSED_ARG(pcb);
+
+  iss += tcp_ticks;       /* XXX */
+  return iss;
+#endif /* LWIP_HOOK_TCP_ISN */
+}
+
+#if TCP_CALCULATE_EFF_SEND_MSS
+/**
+ * Calculates the effective send mss that can be used for a specific IP address
+ * by using ip_route to determine the netif used to send to the address and
+ * calculating the minimum of TCP_MSS and that netif's mtu (if set).
+ */
+u16_t
+tcp_eff_send_mss_impl(u16_t sendmss, const ip_addr_t *dest
+#if LWIP_IPV6 || LWIP_IPV4_SRC_ROUTING
+                     , const ip_addr_t *src
+#endif /* LWIP_IPV6 || LWIP_IPV4_SRC_ROUTING */
+                     )
+{
+  u16_t mss_s;
+  struct netif *outif;
+  s16_t mtu;
+
+  outif = ip_route(src, dest);
+#if LWIP_IPV6
+#if LWIP_IPV4
+  if (IP_IS_V6(dest))
+#endif /* LWIP_IPV4 */
+  {
+    /* First look in destination cache, to see if there is a Path MTU. */
+    mtu = nd6_get_destination_mtu(ip_2_ip6(dest), outif);
+  }
+#if LWIP_IPV4
+  else
+#endif /* LWIP_IPV4 */
+#endif /* LWIP_IPV6 */
+#if LWIP_IPV4
+  {
+    if (outif == NULL) {
+      return sendmss;
+    }
+    mtu = outif->mtu;
+  }
+#endif /* LWIP_IPV4 */
+
+  if (mtu != 0) {
+#if LWIP_IPV6
+#if LWIP_IPV4
+    if (IP_IS_V6(dest))
+#endif /* LWIP_IPV4 */
+    {
+      mss_s = mtu - IP6_HLEN - TCP_HLEN;
+    }
+#if LWIP_IPV4
+    else
+#endif /* LWIP_IPV4 */
+#endif /* LWIP_IPV6 */
+#if LWIP_IPV4
+    {
+      mss_s = mtu - IP_HLEN - TCP_HLEN;
+    }
+#endif /* LWIP_IPV4 */
+    /* RFC 1122, chap 4.2.2.6:
+     * Eff.snd.MSS = min(SendMSS+20, MMS_S) - TCPhdrsize - IPoptionsize
+     * We correct for TCP options in tcp_write(), and don't support IP options.
+     */
+    sendmss = LWIP_MIN(sendmss, mss_s);
+  }
+  return sendmss;
+}
+#endif /* TCP_CALCULATE_EFF_SEND_MSS */
+
+/** Helper function for tcp_netif_ip_addr_changed() that iterates a pcb list */
+static void
+tcp_netif_ip_addr_changed_pcblist(const ip_addr_t* old_addr, struct tcp_pcb* pcb_list)
+{
+  struct tcp_pcb *pcb;
+  pcb = pcb_list;
+  while (pcb != NULL) {
+    /* PCB bound to current local interface address? */
+    if (ip_addr_cmp(&pcb->local_ip, old_addr)
+#if LWIP_AUTOIP
+      /* connections to link-local addresses must persist (RFC3927 ch. 1.9) */
+      && (!IP_IS_V4_VAL(pcb->local_ip) || !ip4_addr_islinklocal(ip_2_ip4(&pcb->local_ip)))
+#endif /* LWIP_AUTOIP */
+      ) {
+      /* this connection must be aborted */
+      struct tcp_pcb *next = pcb->next;
+      LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_STATE, ("netif_set_ipaddr: aborting TCP pcb %p\n", (void *)pcb));
+      tcp_abort(pcb);
+      pcb = next;
+    } else {
+      pcb = pcb->next;
+    }
+  }
+}
+
+/** This function is called from netif.c when address is changed or netif is removed
+ *
+ * @param old_addr IP address of the netif before change
+ * @param new_addr IP address of the netif after change or NULL if netif has been removed
+ */
+void
+tcp_netif_ip_addr_changed(const ip_addr_t* old_addr, const ip_addr_t* new_addr)
+{
+  struct tcp_pcb_listen *lpcb, *next;
+
+  if (!ip_addr_isany(old_addr)) {
+    tcp_netif_ip_addr_changed_pcblist(old_addr, tcp_active_pcbs);
+    tcp_netif_ip_addr_changed_pcblist(old_addr, tcp_bound_pcbs);
+
+    if (!ip_addr_isany(new_addr)) {
+      /* PCB bound to current local interface address? */
+      for (lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = next) {
+        next = lpcb->next;
+        /* PCB bound to current local interface address? */
+        if (ip_addr_cmp(&lpcb->local_ip, old_addr)) {
+          /* The PCB is listening to the old ipaddr and
+            * is set to listen to the new one instead */
+          ip_addr_copy(lpcb->local_ip, *new_addr);
+        }
+      }
+    }
+  }
+}
+
+const char*
+tcp_debug_state_str(enum tcp_state s)
+{
+  return tcp_state_str[s];
+}
+
+#if TCP_DEBUG || TCP_INPUT_DEBUG || TCP_OUTPUT_DEBUG
+/**
+ * Print a tcp header for debugging purposes.
+ *
+ * @param tcphdr pointer to a struct tcp_hdr
+ */
+void
+tcp_debug_print(struct tcp_hdr *tcphdr)
+{
+  LWIP_DEBUGF(TCP_DEBUG, ("TCP header:\n"));
+  LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n"));
+  LWIP_DEBUGF(TCP_DEBUG, ("|    %5"U16_F"      |    %5"U16_F"      | (src port, dest port)\n",
+         lwip_ntohs(tcphdr->src), lwip_ntohs(tcphdr->dest)));
+  LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n"));
+  LWIP_DEBUGF(TCP_DEBUG, ("|           %010"U32_F"          | (seq no)\n",
+          lwip_ntohl(tcphdr->seqno)));
+  LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n"));
+  LWIP_DEBUGF(TCP_DEBUG, ("|           %010"U32_F"          | (ack no)\n",
+         lwip_ntohl(tcphdr->ackno)));
+  LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n"));
+  LWIP_DEBUGF(TCP_DEBUG, ("| %2"U16_F" |   |%"U16_F"%"U16_F"%"U16_F"%"U16_F"%"U16_F"%"U16_F"|     %5"U16_F"     | (hdrlen, flags (",
+       TCPH_HDRLEN(tcphdr),
+         (u16_t)(TCPH_FLAGS(tcphdr) >> 5 & 1),
+         (u16_t)(TCPH_FLAGS(tcphdr) >> 4 & 1),
+         (u16_t)(TCPH_FLAGS(tcphdr) >> 3 & 1),
+         (u16_t)(TCPH_FLAGS(tcphdr) >> 2 & 1),
+         (u16_t)(TCPH_FLAGS(tcphdr) >> 1 & 1),
+         (u16_t)(TCPH_FLAGS(tcphdr)      & 1),
+         lwip_ntohs(tcphdr->wnd)));
+  tcp_debug_print_flags(TCPH_FLAGS(tcphdr));
+  LWIP_DEBUGF(TCP_DEBUG, ("), win)\n"));
+  LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n"));
+  LWIP_DEBUGF(TCP_DEBUG, ("|    0x%04"X16_F"     |     %5"U16_F"     | (chksum, urgp)\n",
+         lwip_ntohs(tcphdr->chksum), lwip_ntohs(tcphdr->urgp)));
+  LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n"));
+}
+
+/**
+ * Print a tcp state for debugging purposes.
+ *
+ * @param s enum tcp_state to print
+ */
+void
+tcp_debug_print_state(enum tcp_state s)
+{
+  LWIP_DEBUGF(TCP_DEBUG, ("State: %s\n", tcp_state_str[s]));
+}
+
+/**
+ * Print tcp flags for debugging purposes.
+ *
+ * @param flags tcp flags, all active flags are printed
+ */
+void
+tcp_debug_print_flags(u8_t flags)
+{
+  if (flags & TCP_FIN) {
+    LWIP_DEBUGF(TCP_DEBUG, ("FIN "));
+  }
+  if (flags & TCP_SYN) {
+    LWIP_DEBUGF(TCP_DEBUG, ("SYN "));
+  }
+  if (flags & TCP_RST) {
+    LWIP_DEBUGF(TCP_DEBUG, ("RST "));
+  }
+  if (flags & TCP_PSH) {
+    LWIP_DEBUGF(TCP_DEBUG, ("PSH "));
+  }
+  if (flags & TCP_ACK) {
+    LWIP_DEBUGF(TCP_DEBUG, ("ACK "));
+  }
+  if (flags & TCP_URG) {
+    LWIP_DEBUGF(TCP_DEBUG, ("URG "));
+  }
+  if (flags & TCP_ECE) {
+    LWIP_DEBUGF(TCP_DEBUG, ("ECE "));
+  }
+  if (flags & TCP_CWR) {
+    LWIP_DEBUGF(TCP_DEBUG, ("CWR "));
+  }
+  LWIP_DEBUGF(TCP_DEBUG, ("\n"));
+}
+
+/**
+ * Print all tcp_pcbs in every list for debugging purposes.
+ */
+void
+tcp_debug_print_pcbs(void)
+{
+  struct tcp_pcb *pcb;
+  struct tcp_pcb_listen *pcbl;
+
+  LWIP_DEBUGF(TCP_DEBUG, ("Active PCB states:\n"));
+  for (pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
+    LWIP_DEBUGF(TCP_DEBUG, ("Local port %"U16_F", foreign port %"U16_F" snd_nxt %"U32_F" rcv_nxt %"U32_F" ",
+                       pcb->local_port, pcb->remote_port,
+                       pcb->snd_nxt, pcb->rcv_nxt));
+    tcp_debug_print_state(pcb->state);
+  }
+
+  LWIP_DEBUGF(TCP_DEBUG, ("Listen PCB states:\n"));
+  for (pcbl = tcp_listen_pcbs.listen_pcbs; pcbl != NULL; pcbl = pcbl->next) {
+    LWIP_DEBUGF(TCP_DEBUG, ("Local port %"U16_F" ", pcbl->local_port));
+    tcp_debug_print_state(pcbl->state);
+  }
+
+  LWIP_DEBUGF(TCP_DEBUG, ("TIME-WAIT PCB states:\n"));
+  for (pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) {
+    LWIP_DEBUGF(TCP_DEBUG, ("Local port %"U16_F", foreign port %"U16_F" snd_nxt %"U32_F" rcv_nxt %"U32_F" ",
+                       pcb->local_port, pcb->remote_port,
+                       pcb->snd_nxt, pcb->rcv_nxt));
+    tcp_debug_print_state(pcb->state);
+  }
+}
+
+/**
+ * Check state consistency of the tcp_pcb lists.
+ */
+s16_t
+tcp_pcbs_sane(void)
+{
+  struct tcp_pcb *pcb;
+  for (pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
+    LWIP_ASSERT("tcp_pcbs_sane: active pcb->state != CLOSED", pcb->state != CLOSED);
+    LWIP_ASSERT("tcp_pcbs_sane: active pcb->state != LISTEN", pcb->state != LISTEN);
+    LWIP_ASSERT("tcp_pcbs_sane: active pcb->state != TIME-WAIT", pcb->state != TIME_WAIT);
+  }
+  for (pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) {
+    LWIP_ASSERT("tcp_pcbs_sane: tw pcb->state == TIME-WAIT", pcb->state == TIME_WAIT);
+  }
+  return 1;
+}
+#endif /* TCP_DEBUG */
+
+#endif /* LWIP_TCP */

+ 1818 - 1619
thirdparty/lwip/src/core/tcp_in.c

@@ -1,1619 +1,1818 @@
-/**
- * @file
- * Transmission Control Protocol, incoming traffic
- *
- * The input processing functions of the TCP layer.
- *
- * These functions are generally called in the order (ip_input() ->)
- * tcp_input() -> * tcp_process() -> tcp_receive() (-> application).
- * 
- */
-
-/*
- * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * This file is part of the lwIP TCP/IP stack.
- *
- * Author: Adam Dunkels <adam@sics.se>
- *
- */
-
-#include "lwip/opt.h"
-
-#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */
-
-#include "lwip/tcp_impl.h"
-#include "lwip/def.h"
-#include "lwip/ip_addr.h"
-#include "lwip/netif.h"
-#include "lwip/mem.h"
-#include "lwip/memp.h"
-#include "lwip/inet_chksum.h"
-#include "lwip/stats.h"
-#include "lwip/snmp.h"
-#include "arch/perf.h"
-
-/* 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);
-
-/**
- * The initial input processing of TCP. It verifies the TCP header, demultiplexes
- * the segment between the PCBs and passes it on to tcp_process(), which implements
- * the TCP finite state machine. This function is called by the IP layer (in
- * ip_input()).
- *
- * @param p received TCP segment to process (p->payload pointing to the IP header)
- * @param inp network interface on which this segment was received
- */
-void
-tcp_input(struct pbuf *p, struct netif *inp)
-{
-  struct tcp_pcb *pcb, *prev;
-  struct tcp_pcb_listen *lpcb;
-#if SO_REUSE
-  struct tcp_pcb *lpcb_prev = NULL;
-  struct tcp_pcb_listen *lpcb_any = NULL;
-#endif /* SO_REUSE */
-  u8_t hdrlen;
-  err_t err;
-
-  PERF_START;
-
-  TCP_STATS_INC(tcp.recv);
-  snmp_inc_tcpinsegs();
-
-  iphdr = (struct ip_hdr *)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);
-    goto dropped;
-  }
-
-  /* Don't even process incoming broadcasts/multicasts. */
-  if (ip_addr_isbroadcast(&current_iphdr_dest, inp) ||
-      ip_addr_ismulticast(&current_iphdr_dest)) {
-    TCP_STATS_INC(tcp.proterr);
-    goto dropped;
-  }
-
-#if CHECKSUM_CHECK_TCP
-  /* Verify TCP checksum. */
-  if (inet_chksum_pseudo(p, ip_current_src_addr(), ip_current_dest_addr(),
-      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, ip_current_src_addr(), ip_current_dest_addr(),
-      IP_PROTO_TCP, p->tot_len)));
-#if TCP_DEBUG
-    tcp_debug_print(tcphdr);
-#endif /* TCP_DEBUG */
-    TCP_STATS_INC(tcp.chkerr);
-    goto dropped;
-  }
-#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);
-  if(pbuf_header(p, -(hdrlen * 4))){
-    /* drop short packets */
-    LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: short packet\n"));
-    TCP_STATS_INC(tcp.lenerr);
-    goto dropped;
-  }
-
-  /* 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);
-  tcplen = p->tot_len + ((flags & (TCP_FIN | 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), &current_iphdr_src) &&
-       ip_addr_cmp(&(pcb->local_ip), &current_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), &current_iphdr_src) &&
-         ip_addr_cmp(&(pcb->local_ip), &current_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 (lpcb->local_port == tcphdr->dest) {
-#if SO_REUSE
-        if (ip_addr_cmp(&(lpcb->local_ip), &current_iphdr_dest)) {
-          /* found an exact match */
-          break;
-        } else if(ip_addr_isany(&(lpcb->local_ip))) {
-          /* found an ANY-match */
-          lpcb_any = lpcb;
-          lpcb_prev = prev;
-        }
-#else /* SO_REUSE */
-        if (ip_addr_cmp(&(lpcb->local_ip), &current_iphdr_dest) ||
-            ip_addr_isany(&(lpcb->local_ip))) {
-          /* found a match */
-          break;
-        }
-#endif /* SO_REUSE */
-      }
-      prev = (struct tcp_pcb *)lpcb;
-    }
-#if SO_REUSE
-    /* first try specific local IP */
-    if (lpcb == NULL) {
-      /* only pass to ANY if no specific local IP has been found */
-      lpcb = lpcb_any;
-      prev = lpcb_prev;
-    }
-#endif /* SO_REUSE */
-    if (lpcb != NULL) {
-      /* Move this PCB to the front of the list so that subsequent
-         lookups will be faster (we exploit locality in TCP segment
-         arrivals). */
-      if (prev != NULL) {
-        ((struct tcp_pcb_listen *)prev)->next = lpcb->next;
-              /* our successor is the remainder of the listening list */
-        lpcb->next = tcp_listen_pcbs.listen_pcbs;
-              /* put this listening pcb at the head of the listening list */
-        tcp_listen_pcbs.listen_pcbs = lpcb;
-      }
-    
-      LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packed for LISTENing connection.\n"));
-      tcp_listen_input(lpcb);
-      pbuf_free(p);
-      return;
-    }
-  }
-
-#if TCP_INPUT_DEBUG
-  LWIP_DEBUGF(TCP_INPUT_DEBUG, ("+-+-+-+-+-+-+-+-+-+-+-+-+-+- tcp_input: flags "));
-  tcp_debug_print_flags(TCPH_FLAGS(tcphdr));
-  LWIP_DEBUGF(TCP_INPUT_DEBUG, ("-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n"));
-#endif /* TCP_INPUT_DEBUG */
-
-
-  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.p = p;
-    inseg.tcphdr = tcphdr;
-
-    recv_data = NULL;
-    recv_flags = 0;
-
-    if (flags & TCP_PSH) {
-      p->flags |= PBUF_FLAG_PUSH;
-    }
-
-    /* If there is data which was previously "refused" by upper layer */
-    if (pcb->refused_data != NULL) {
-      if ((tcp_process_refused_data(pcb) == ERR_ABRT) ||
-        ((pcb->refused_data != NULL) && (tcplen > 0))) {
-        /* pcb has been aborted or refused data is still refused and the new
-           segment contains data */
-        TCP_STATS_INC(tcp.drop);
-        snmp_inc_tcpinerrs();
-        goto aborted;
-      }
-    }
-    tcp_input_pcb = pcb;
-    err = tcp_process(pcb);
-    /* A return value of ERR_ABRT means that tcp_abort() was called
-       and that the pcb has been freed. If so, we don't do anything. */
-    if (err != ERR_ABRT) {
-      if (recv_flags & TF_RESET) {
-        /* TF_RESET means that the connection was reset by the other
-           end. We then call the error callback to inform the
-           application that the connection is dead before we
-           deallocate the PCB. */
-        TCP_EVENT_ERR(pcb->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. */
-        if (!(pcb->flags & TF_RXCLOSED)) {
-          /* Connection closed although the application has only shut down the
-             tx side: call the PCB's err callback and indicate the closure to
-             ensure the application doesn't continue using the PCB. */
-          TCP_EVENT_ERR(pcb->errf, pcb->callback_arg, ERR_CLSD);
-        }
-        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 (err == ERR_ABRT) {
-            goto aborted;
-          }
-        }
-
-        if (recv_data != NULL) {
-          LWIP_ASSERT("pcb->refused_data == NULL", pcb->refused_data == NULL);
-          if (pcb->flags & TF_RXCLOSED) {
-            /* received data although already closed -> abort (send RST) to
-               notify the remote host that not all data has been processed */
-            pbuf_free(recv_data);
-            tcp_abort(pcb);
-            goto aborted;
-          }
-
-          /* Notify application that data has been received. */
-          TCP_EVENT_RECV(pcb, recv_data, ERR_OK, err);
-          if (err == ERR_ABRT) {
-            goto aborted;
-          }
-
-          /* If the upper layer can't receive this data, store it */
-          if (err != ERR_OK) {
-            pcb->refused_data = recv_data;
-            LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: keep incoming packet, because pcb is \"full\"\n"));
-          }
-        }
-
-        /* If a FIN segment was received, we call the callback
-           function with a NULL buffer to indicate EOF. */
-        if (recv_flags & TF_GOT_FIN) {
-          if (pcb->refused_data != NULL) {
-            /* Delay this if we have refused data. */
-            pcb->refused_data->flags |= PBUF_FLAG_TCP_FIN;
-          } else {
-            /* correct rcv_wnd as the application won't call tcp_recved()
-               for the FIN's seqno */
-            if (pcb->rcv_wnd != TCP_WND) {
-              pcb->rcv_wnd++;
-            }
-            TCP_EVENT_CLOSED(pcb, err);
-            if (err == ERR_ABRT) {
-              goto aborted;
-            }
-          }
-        }
-
-        tcp_input_pcb = NULL;
-        /* Try to send something out. */
-        tcp_output(pcb);
-#if TCP_INPUT_DEBUG
-#if TCP_DEBUG
-        tcp_debug_print_state(pcb->state);
-#endif /* TCP_DEBUG */
-#endif /* TCP_INPUT_DEBUG */
-      }
-    }
-    /* Jump target if pcb has been aborted in a callback (by calling tcp_abort()).
-       Below this line, 'pcb' may not be dereferenced! */
-aborted:
-    tcp_input_pcb = NULL;
-    recv_data = NULL;
-
-    /* give up our reference to inseg.p */
-    if (inseg.p != NULL)
-    {
-      pbuf_free(inseg.p);
-      inseg.p = NULL;
-    }
-  } else {
-
-    /* If no matching PCB was found, send a TCP RST (reset) to the
-       sender. */
-    LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_input: no PCB match found, resetting.\n"));
-    if (!(TCPH_FLAGS(tcphdr) & TCP_RST)) {
-      TCP_STATS_INC(tcp.proterr);
-      TCP_STATS_INC(tcp.drop);
-      tcp_rst(ackno, seqno + tcplen,
-        ip_current_dest_addr(), ip_current_src_addr(),
-        tcphdr->dest, tcphdr->src);
-    }
-    pbuf_free(p);
-  }
-
-  LWIP_ASSERT("tcp_input: tcp_pcbs_sane()", tcp_pcbs_sane());
-  PERF_STOP("tcp_input");
-  return;
-dropped:
-  TCP_STATS_INC(tcp.drop);
-  snmp_inc_tcpinerrs();
-  pbuf_free(p);
-}
-
-/**
- * Called by tcp_input() when a segment arrives for a listening
- * connection (from tcp_input()).
- *
- * @param pcb the tcp_pcb_listen for which a segment arrived
- * @return ERR_OK if the segment was processed
- *         another err_t on error
- *
- * @note the return value is not (yet?) used in tcp_input()
- * @note the segment which arrived is saved in global variables, therefore only the pcb
- *       involved is passed as a parameter to this function
- */
-static err_t
-tcp_listen_input(struct tcp_pcb_listen *pcb)
-{
-  struct tcp_pcb *npcb;
-  err_t rc;
-
-  if (flags & TCP_RST) {
-    /* An incoming RST should be ignored. Return. */
-    return ERR_OK;
-  }
-
-  /* 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, seqno + tcplen, ip_current_dest_addr(),
-      ip_current_src_addr(), tcphdr->dest, tcphdr->src);
-  } else if (flags & TCP_SYN) {
-    LWIP_DEBUGF(TCP_DEBUG, ("TCP connection request %"U16_F" -> %"U16_F".\n", tcphdr->src, tcphdr->dest));
-#if TCP_LISTEN_BACKLOG
-    if (pcb->accepts_pending >= pcb->backlog) {
-      LWIP_DEBUGF(TCP_DEBUG, ("tcp_listen_input: listen backlog exceeded for port %"U16_F"\n", tcphdr->dest));
-      return ERR_ABRT;
-    }
-#endif /* TCP_LISTEN_BACKLOG */
-    npcb = tcp_alloc(pcb->prio);
-    /* If a new PCB could not be created (probably due to lack of memory),
-       we don't do anything, but rely on the sender will retransmit the
-       SYN at a time when we have more memory available. */
-    if (npcb == NULL) {
-      LWIP_DEBUGF(TCP_DEBUG, ("tcp_listen_input: could not allocate PCB\n"));
-      TCP_STATS_INC(tcp.memerr);
-      return ERR_MEM;
-    }
-#if TCP_LISTEN_BACKLOG
-    pcb->accepts_pending++;
-#endif /* TCP_LISTEN_BACKLOG */
-    /* Set up the new PCB. */
-    ip_addr_copy(npcb->local_ip, current_iphdr_dest);
-    npcb->local_port = pcb->local_port;
-    ip_addr_copy(npcb->remote_ip, current_iphdr_src);
-    npcb->remote_port = tcphdr->src;
-    npcb->state = SYN_RCVD;
-    npcb->rcv_nxt = seqno + 1;
-    npcb->rcv_ann_right_edge = npcb->rcv_nxt;
-    npcb->snd_wnd = tcphdr->wnd;
-    npcb->snd_wnd_max = 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_INHERITED;
-    /* Register the new PCB so that we can begin receiving segments
-       for it. */
-    TCP_REG_ACTIVE(npcb);
-
-    /* Parse any options in the SYN. */
-    tcp_parseopt(npcb);
-#if TCP_CALCULATE_EFF_SEND_MSS
-    npcb->mss = tcp_eff_send_mss(npcb->mss, &(npcb->remote_ip));
-#endif /* TCP_CALCULATE_EFF_SEND_MSS */
-
-    snmp_inc_tcppassiveopens();
-
-    /* Send a SYN|ACK together with the MSS option. */
-    rc = tcp_enqueue_flags(npcb, TCP_SYN | TCP_ACK);
-    if (rc != ERR_OK) {
-      tcp_abandon(npcb, 0);
-      return rc;
-    }
-    return tcp_output(npcb);
-  }
-  return ERR_OK;
-}
-
-/**
- * Called by tcp_input() when a segment arrives for a connection in
- * TIME_WAIT.
- *
- * @param pcb the tcp_pcb for which a segment arrived
- *
- * @note the segment which arrived is saved in global variables, therefore only the pcb
- *       involved is passed as a parameter to this function
- */
-static err_t
-tcp_timewait_input(struct tcp_pcb *pcb)
-{
-  /* RFC 1337: in TIME_WAIT, ignore RST and ACK FINs + any 'acceptable' segments */
-  /* RFC 793 3.9 Event Processing - Segment Arrives:
-   * - first check sequence number - we skip that one in TIME_WAIT (always
-   *   acceptable since we only send ACKs)
-   * - second check the RST bit (... return) */
-  if (flags & TCP_RST)  {
-    return ERR_OK;
-  }
-  /* - fourth, check the SYN bit, */
-  if (flags & TCP_SYN) {
-    /* If an incoming segment is not acceptable, an acknowledgment
-       should be sent in reply */
-    if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, pcb->rcv_nxt+pcb->rcv_wnd)) {
-      /* If the SYN is in the window it is an error, send a reset */
-      tcp_rst(ackno, seqno + tcplen, ip_current_dest_addr(), ip_current_src_addr(),
-        tcphdr->dest, tcphdr->src);
-      return ERR_OK;
-    }
-  } else if (flags & TCP_FIN) {
-    /* - eighth, check the FIN bit: Remain in the TIME-WAIT state.
-         Restart the 2 MSL time-wait timeout.*/
-    pcb->tmr = tcp_ticks;
-  }
-
-  if ((tcplen > 0))  {
-    /* Acknowledge data, FIN or out-of-window SYN */
-    pcb->flags |= TF_ACK_NOW;
-    return tcp_output(pcb);
-  }
-  return ERR_OK;
-}
-
-/**
- * Implements the TCP state machine. Called by tcp_input. In some
- * states tcp_receive() is called to receive data. The tcp_seg
- * argument will be freed by the caller (tcp_input()) unless the
- * recv_data pointer in the pcb is set.
- *
- * @param pcb the tcp_pcb for which a segment arrived
- *
- * @note the segment which arrived is saved in global variables, therefore only the pcb
- *       involved is passed as a parameter to this function
- */
-static err_t
-tcp_process(struct tcp_pcb *pcb)
-{
-  struct tcp_seg *rseg;
-  u8_t acceptable = 0;
-  err_t err;
-
-  err = ERR_OK;
-
-  /* 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_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;
-    }
-  }
-
-  if ((flags & TCP_SYN) && (pcb->state != SYN_SENT && pcb->state != SYN_RCVD)) { 
-    /* Cope with new connection attempt after remote end crashed */
-    tcp_ack_now(pcb);
-    return ERR_OK;
-  }
-  
-  if ((pcb->flags & TF_RXCLOSED) == 0) {
-    /* Update the PCB (in)activity timer unless rx is closed (see tcp_shutdown) */
-    pcb->tmr = tcp_ticks;
-  }
-  pcb->keep_cnt_sent = 0;
-
-  tcp_parseopt(pcb);
-
-  /* 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->rcv_ann_right_edge = pcb->rcv_nxt;
-      pcb->lastack = ackno;
-      pcb->snd_wnd = tcphdr->wnd;
-      pcb->snd_wnd_max = tcphdr->wnd;
-      pcb->snd_wl1 = seqno - 1; /* initialise to seqno - 1 to force window update */
-      pcb->state = ESTABLISHED;
-
-#if TCP_CALCULATE_EFF_SEND_MSS
-      pcb->mss = tcp_eff_send_mss(pcb->mss, &(pcb->remote_ip));
-#endif /* TCP_CALCULATE_EFF_SEND_MSS */
-
-      /* Set ssthresh again after changing pcb->mss (already set in tcp_connect
-       * but for the default value of pcb->mss) */
-      pcb->ssthresh = pcb->mss * 10;
-
-      pcb->cwnd = ((pcb->cwnd == 1) ? (pcb->mss * 2) : pcb->mss);
-      LWIP_ASSERT("pcb->snd_queuelen > 0", (pcb->snd_queuelen > 0));
-      --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);
-
-      /* If there's nothing left to acknowledge, stop the retransmit
-         timer, otherwise reset it to start again */
-      if(pcb->unacked == NULL)
-        pcb->rtime = -1;
-      else {
-        pcb->rtime = 0;
-        pcb->nrtx = 0;
-      }
-
-      /* Call the user specified function to call when sucessfully
-       * connected. */
-      TCP_EVENT_CONNECTED(pcb, ERR_OK, err);
-      if (err == ERR_ABRT) {
-        return ERR_ABRT;
-      }
-      tcp_ack_now(pcb);
-    }
-    /* received ACK? possibly a half-open connection */
-    else if (flags & TCP_ACK) {
-      /* send a RST to bring the other side in a non-synchronized state. */
-      tcp_rst(ackno, seqno + tcplen, ip_current_dest_addr(), ip_current_src_addr(),
-        tcphdr->dest, tcphdr->src);
-    }
-    break;
-  case SYN_RCVD:
-    if (flags & TCP_ACK) {
-      /* expected ACK number? */
-      if (TCP_SEQ_BETWEEN(ackno, pcb->lastack+1, pcb->snd_nxt)) {
-        u16_t old_cwnd;
-        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. */
-          /* Already aborted? */
-          if (err != ERR_ABRT) {
-            tcp_abort(pcb);
-          }
-          return ERR_ABRT;
-        }
-        old_cwnd = pcb->cwnd;
-        /* If there was any data contained within this ACK,
-         * we'd better pass it on to the application as well. */
-        tcp_receive(pcb);
-
-        /* Prevent ACK for SYN to generate a sent event */
-        if (pcb->acked != 0) {
-          pcb->acked--;
-        }
-
-        pcb->cwnd = ((old_cwnd == 1) ? (pcb->mss * 2) : pcb->mss);
-
-        if (recv_flags & TF_GOT_FIN) {
-          tcp_ack_now(pcb);
-          pcb->state = CLOSE_WAIT;
-        }
-      } else {
-        /* incorrect ACK number, send RST */
-        tcp_rst(ackno, seqno + tcplen, ip_current_dest_addr(), ip_current_src_addr(),
-                tcphdr->dest, tcphdr->src);
-      }
-    } else if ((flags & TCP_SYN) && (seqno == pcb->rcv_nxt - 1)) {
-      /* Looks like another copy of the SYN - retransmit our SYN-ACK */
-      tcp_rexmit(pcb);
-    }
-    break;
-  case CLOSE_WAIT:
-    /* FALLTHROUGH */
-  case ESTABLISHED:
-    tcp_receive(pcb);
-    if (recv_flags & TF_GOT_FIN) { /* passive close */
-      tcp_ack_now(pcb);
-      pcb->state = CLOSE_WAIT;
-    }
-    break;
-  case FIN_WAIT_1:
-    tcp_receive(pcb);
-    if (recv_flags & TF_GOT_FIN) {
-      if ((flags & TCP_ACK) && (ackno == pcb->snd_nxt)) {
-        LWIP_DEBUGF(TCP_DEBUG,
-          ("TCP connection closed: FIN_WAIT_1 %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
-        tcp_ack_now(pcb);
-        tcp_pcb_purge(pcb);
-        TCP_RMV_ACTIVE(pcb);
-        pcb->state = TIME_WAIT;
-        TCP_REG(&tcp_tw_pcbs, pcb);
-      } else {
-        tcp_ack_now(pcb);
-        pcb->state = CLOSING;
-      }
-    } else if ((flags & TCP_ACK) && (ackno == pcb->snd_nxt)) {
-      pcb->state = FIN_WAIT_2;
-    }
-    break;
-  case FIN_WAIT_2:
-    tcp_receive(pcb);
-    if (recv_flags & TF_GOT_FIN) {
-      LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: FIN_WAIT_2 %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
-      tcp_ack_now(pcb);
-      tcp_pcb_purge(pcb);
-      TCP_RMV_ACTIVE(pcb);
-      pcb->state = TIME_WAIT;
-      TCP_REG(&tcp_tw_pcbs, pcb);
-    }
-    break;
-  case CLOSING:
-    tcp_receive(pcb);
-    if (flags & TCP_ACK && ackno == pcb->snd_nxt) {
-      LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: CLOSING %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
-      tcp_pcb_purge(pcb);
-      TCP_RMV_ACTIVE(pcb);
-      pcb->state = TIME_WAIT;
-      TCP_REG(&tcp_tw_pcbs, pcb);
-    }
-    break;
-  case LAST_ACK:
-    tcp_receive(pcb);
-    if (flags & TCP_ACK && ackno == pcb->snd_nxt) {
-      LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: LAST_ACK %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
-      /* bugfix #21699: don't set pcb->state to CLOSED here or we risk leaking segments */
-      recv_flags |= TF_CLOSED;
-    }
-    break;
-  default:
-    break;
-  }
-  return ERR_OK;
-}
-
-#if TCP_QUEUE_OOSEQ
-/**
- * Insert segment into the list (segments covered with new one will be deleted)
- *
- * Called from tcp_receive()
- */
-static void
-tcp_oos_insert_segment(struct tcp_seg *cseg, struct tcp_seg *next)
-{
-  struct tcp_seg *old_seg;
-
-  if (TCPH_FLAGS(cseg->tcphdr) & TCP_FIN) {
-    /* received segment overlaps all following segments */
-    tcp_segs_free(next);
-    next = NULL;
-  }
-  else {
-    /* delete some following segments
-       oos queue may have segments with FIN flag */
-    while (next &&
-           TCP_SEQ_GEQ((seqno + cseg->len),
-                      (next->tcphdr->seqno + next->len))) {
-      /* cseg with FIN already processed */
-      if (TCPH_FLAGS(next->tcphdr) & TCP_FIN) {
-        TCPH_SET_FLAG(cseg->tcphdr, TCP_FIN);
-      }
-      old_seg = next;
-      next = next->next;
-      tcp_seg_free(old_seg);
-    }
-    if (next &&
-        TCP_SEQ_GT(seqno + cseg->len, next->tcphdr->seqno)) {
-      /* We need to trim the incoming segment. */
-      cseg->len = (u16_t)(next->tcphdr->seqno - seqno);
-      pbuf_realloc(cseg->p, cseg->len);
-    }
-  }
-  cseg->next = next;
-}
-#endif /* TCP_QUEUE_OOSEQ */
-
-/**
- * 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
- * it has been removed from the buffer.
- *
- * If the incoming segment constitutes an ACK for a segment that was used for RTT
- * estimation, the RTT is estimated here as well.
- *
- * Called from tcp_process().
- */
-static void
-tcp_receive(struct tcp_pcb *pcb)
-{
-  struct tcp_seg *next;
-#if TCP_QUEUE_OOSEQ
-  struct tcp_seg *prev, *cseg;
-#endif /* TCP_QUEUE_OOSEQ */
-  struct pbuf *p;
-  s32_t off;
-  s16_t m;
-  u32_t right_wnd_edge;
-  u16_t new_tot_len;
-  int found_dupack = 0;
-#if TCP_OOSEQ_MAX_BYTES || TCP_OOSEQ_MAX_PBUFS
-  u32_t ooseq_blen;
-  u16_t ooseq_qlen;
-#endif /* TCP_OOSEQ_MAX_BYTES || TCP_OOSEQ_MAX_PBUFS */
-
-  LWIP_ASSERT("tcp_receive: wrong state", pcb->state >= ESTABLISHED);
-
-  if (flags & TCP_ACK) {
-    right_wnd_edge = pcb->snd_wnd + pcb->snd_wl2;
-
-    /* Update window. */
-    if (TCP_SEQ_LT(pcb->snd_wl1, seqno) ||
-       (pcb->snd_wl1 == seqno && TCP_SEQ_LT(pcb->snd_wl2, ackno)) ||
-       (pcb->snd_wl2 == ackno && tcphdr->wnd > pcb->snd_wnd)) {
-      pcb->snd_wnd = tcphdr->wnd;
-      /* keep track of the biggest window announced by the remote host to calculate
-         the maximum segment size */
-      if (pcb->snd_wnd_max < tcphdr->wnd) {
-        pcb->snd_wnd_max = tcphdr->wnd;
-      }
-      pcb->snd_wl1 = seqno;
-      pcb->snd_wl2 = ackno;
-      if (pcb->snd_wnd == 0) {
-        if (pcb->persist_backoff == 0) {
-          /* start persist timer */
-          pcb->persist_cnt = 0;
-          pcb->persist_backoff = 1;
-        }
-      } else if (pcb->persist_backoff > 0) {
-        /* stop persist timer */
-          pcb->persist_backoff = 0;
-      }
-      LWIP_DEBUGF(TCP_WND_DEBUG, ("tcp_receive: window update %"U16_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" ackno %"
-                     U32_F" wl1 %"U32_F" seqno %"U32_F" wl2 %"U32_F"\n",
-                     pcb->lastack, ackno, pcb->snd_wl1, seqno, pcb->snd_wl2));
-      }
-#endif /* TCP_WND_DEBUG */
-    }
-
-    /* (From Stevens TCP/IP Illustrated Vol II, p970.) Its only a
-     * duplicate ack if:
-     * 1) It doesn't ACK new data 
-     * 2) length of received packet is zero (i.e. no payload) 
-     * 3) the advertised window hasn't changed 
-     * 4) There is outstanding unacknowledged data (retransmission timer running)
-     * 5) The ACK is == biggest ACK sequence number so far seen (snd_una)
-     * 
-     * If it passes all five, should process as a dupack: 
-     * a) dupacks < 3: do nothing 
-     * b) dupacks == 3: fast retransmit 
-     * c) dupacks > 3: increase cwnd 
-     * 
-     * If it only passes 1-3, should reset dupack counter (and add to
-     * stats, which we don't do in lwIP)
-     *
-     * If it only passes 1, should reset dupack counter
-     *
-     */
-
-    /* Clause 1 */
-    if (TCP_SEQ_LEQ(ackno, pcb->lastack)) {
-      pcb->acked = 0;
-      /* Clause 2 */
-      if (tcplen == 0) {
-        /* Clause 3 */
-        if (pcb->snd_wl2 + pcb->snd_wnd == right_wnd_edge){
-          /* Clause 4 */
-          if (pcb->rtime >= 0) {
-            /* Clause 5 */
-            if (pcb->lastack == ackno) {
-              found_dupack = 1;
-              if ((u8_t)(pcb->dupacks + 1) > pcb->dupacks) {
-                ++pcb->dupacks;
-              }
-              if (pcb->dupacks > 3) {
-                /* 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 if (pcb->dupacks == 3) {
-                /* Do fast retransmit */
-                tcp_rexmit_fast(pcb);
-              }
-            }
-          }
-        }
-      }
-      /* If Clause (1) or more is true, but not a duplicate ack, reset
-       * count of consecutive duplicate acks */
-      if (!found_dupack) {
-        pcb->dupacks = 0;
-      }
-    } else if (TCP_SEQ_BETWEEN(ackno, pcb->lastack+1, pcb->snd_nxt)){
-      /* 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. Diff between the two can never exceed 64K? */
-      pcb->acked = (u16_t)(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));
-        LWIP_ASSERT("pcb->snd_queuelen >= pbuf_clen(next->p)", (pcb->snd_queuelen >= pbuf_clen(next->p)));
-        /* Prevent ACK for FIN to generate a sent event */
-        if ((pcb->acked != 0) && ((TCPH_FLAGS(next->tcphdr) & TCP_FIN) != 0)) {
-          pcb->acked--;
-        }
-
-        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);
-        }
-      }
-
-      /* If there's nothing left to acknowledge, stop the retransmit
-         timer, otherwise reset it to start again */
-      if(pcb->unacked == NULL)
-        pcb->rtime = -1;
-      else
-        pcb->rtime = 0;
-
-      pcb->polltmr = 0;
-    } else {
-      /* Fix bug bug #21582: out of sequence ACK, didn't really ack anything */
-      pcb->acked = 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_BETWEEN(ackno, ntohl(pcb->unsent->tcphdr->seqno) + 
-                           TCP_TCPLEN(pcb->unsent), pcb->snd_nxt)) {
-      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;
-#if TCP_OVERSIZE
-      if (pcb->unsent == NULL) {
-        pcb->unsent_oversize = 0;
-      }
-#endif /* TCP_OVERSIZE */ 
-      LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_receive: queuelen %"U16_F" ... ", (u16_t)pcb->snd_queuelen));
-      LWIP_ASSERT("pcb->snd_queuelen >= pbuf_clen(next->p)", (pcb->snd_queuelen >= pbuf_clen(next->p)));
-      /* Prevent ACK for FIN to generate a sent event */
-      if ((pcb->acked != 0) && ((TCPH_FLAGS(next->tcphdr) & TCP_FIN) != 0)) {
-        pcb->acked--;
-      }
-      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);
-      }
-    }
-    /* 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)) {
-      /* diff between this shouldn't exceed 32K since this are tcp timer ticks
-         and a round-trip shouldn't be that long... */
-      m = (s16_t)(tcp_ticks - pcb->rttest);
-
-      LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: experienced rtt %"U16_F" ticks (%"U16_F" msec).\n",
-                                  m, 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" milliseconds)\n",
-                                  pcb->rto, pcb->rto * TCP_SLOW_INTERVAL));
-
-      pcb->rttest = 0;
-    }
-  }
-
-  /* If the incoming segment contains data, we must process it
-     further unless the pcb already received a FIN.
-     (RFC 793, chapeter 3.9, "SEGMENT ARRIVES" in states CLOSE-WAIT, CLOSING,
-     LAST-ACK and TIME-WAIT: "Ignore the segment text.") */
-  if ((tcplen > 0) && (pcb->state < CLOSE_WAIT)) {
-    /* This code basically does three things:
-
-    +) If the incoming segment contains data that is the next
-    in-sequence data, this data is passed to the application. This
-    might involve trimming the first edge of the data. The rcv_nxt
-    variable and the advertised window are adjusted.
-
-    +) If the incoming segment has data that is above the next
-    sequence number expected (->rcv_nxt), the segment is placed on
-    the ->ooseq queue. This is done by finding the appropriate
-    place in the ->ooseq queue (which is ordered by sequence
-    number) and trim the segment in both ends if needed. An
-    immediate ACK is sent to indicate that we received an
-    out-of-sequence segment.
-
-    +) Finally, we check if the first segment on the ->ooseq queue
-    now is in sequence (i.e., if rcv_nxt >= ooseq->seqno). If
-    rcv_nxt > ooseq->seqno, we must trim the first edge of the
-    segment on ->ooseq before we adjust rcv_nxt. The data in the
-    segments that are now on sequence are chained onto the
-    incoming segment so that we only need to call the application
-    once.
-    */
-
-    /* First, we check if we must trim the first edge. We have to do
-       this if the sequence number of the incoming segment is less
-       than rcv_nxt, and the sequence number plus the length of the
-       segment is larger than rcv_nxt. */
-    /*    if (TCP_SEQ_LT(seqno, pcb->rcv_nxt)){
-          if (TCP_SEQ_LT(pcb->rcv_nxt, seqno + tcplen)) {*/
-    if (TCP_SEQ_BETWEEN(pcb->rcv_nxt, seqno + 1, seqno + tcplen - 1)){
-      /* Trimming the first edge is done by pushing the payload
-         pointer in the pbuf downwards. This is somewhat tricky since
-         we do not want to discard the full contents of the pbuf up to
-         the new starting point of the data since we have to keep the
-         TCP header which is present in the first pbuf in the chain.
-
-         What is done is really quite a nasty hack: the first pbuf in
-         the pbuf chain is pointed to by inseg.p. Since we need to be
-         able to deallocate the whole pbuf, we cannot change this
-         inseg.p pointer to point to any of the later pbufs in the
-         chain. Instead, we point the ->payload pointer in the first
-         pbuf to data in one of the later pbufs. We also set the
-         inseg.data pointer to point to the right place. This way, the
-         ->p pointer will still point to the first pbuf, but the
-         ->p->payload pointer will point to data in another pbuf.
-
-         After we are done with adjusting the pbuf pointers we must
-         adjust the ->data pointer in the seg and the segment
-         length.*/
-
-      off = pcb->rcv_nxt - seqno;
-      p = inseg.p;
-      LWIP_ASSERT("inseg.p != NULL", inseg.p);
-      LWIP_ASSERT("insane offset!", (off < 0x7fff));
-      if (inseg.p->len < off) {
-        LWIP_ASSERT("pbuf too short!", (((s32_t)inseg.p->tot_len) >= off));
-        new_tot_len = (u16_t)(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;
-        }
-        if(pbuf_header(p, (s16_t)-off)) {
-          /* Do we need to cope with this failing?  Assert for now */
-          LWIP_ASSERT("pbuf_header failed", 0);
-        }
-      } else {
-        if(pbuf_header(inseg.p, (s16_t)-off)) {
-          /* Do we need to cope with this failing?  Assert for now */
-          LWIP_ASSERT("pbuf_header failed", 0);
-        }
-      }
-      inseg.len -= (u16_t)(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_BETWEEN(seqno, pcb->rcv_nxt, 
-                        pcb->rcv_nxt + pcb->rcv_wnd - 1)){
-      if (pcb->rcv_nxt == seqno) {
-        /* The incoming segment is the next in sequence. We check if
-           we have to trim the end of the segment and update rcv_nxt
-           and pass the data to the application. */
-        tcplen = TCP_TCPLEN(&inseg);
-
-        if (tcplen > pcb->rcv_wnd) {
-          LWIP_DEBUGF(TCP_INPUT_DEBUG, 
-                      ("tcp_receive: other end overran receive window"
-                       "seqno %"U32_F" len %"U16_F" right edge %"U32_F"\n",
-                       seqno, tcplen, pcb->rcv_nxt + pcb->rcv_wnd));
-          if (TCPH_FLAGS(inseg.tcphdr) & TCP_FIN) {
-            /* Must remove the FIN from the header as we're trimming 
-             * that byte of sequence-space from the packet */
-            TCPH_FLAGS_SET(inseg.tcphdr, TCPH_FLAGS(inseg.tcphdr) &~ TCP_FIN);
-          }
-          /* Adjust length of segment to fit in the window. */
-          inseg.len = pcb->rcv_wnd;
-          if (TCPH_FLAGS(inseg.tcphdr) & TCP_SYN) {
-            inseg.len -= 1;
-          }
-          pbuf_realloc(inseg.p, inseg.len);
-          tcplen = TCP_TCPLEN(&inseg);
-          LWIP_ASSERT("tcp_receive: segment not trimmed correctly to rcv_wnd\n",
-                      (seqno + tcplen) == (pcb->rcv_nxt + pcb->rcv_wnd));
-        }
-#if TCP_QUEUE_OOSEQ
-        /* Received in-sequence data, adjust ooseq data if:
-           - FIN has been received or
-           - inseq overlaps with ooseq */
-        if (pcb->ooseq != NULL) {
-          if (TCPH_FLAGS(inseg.tcphdr) & TCP_FIN) {
-            LWIP_DEBUGF(TCP_INPUT_DEBUG, 
-                        ("tcp_receive: received in-order FIN, binning ooseq queue\n"));
-            /* Received in-order FIN means anything that was received
-             * out of order must now have been received in-order, so
-             * bin the ooseq queue */
-            while (pcb->ooseq != NULL) {
-              struct tcp_seg *old_ooseq = pcb->ooseq;
-              pcb->ooseq = pcb->ooseq->next;
-              tcp_seg_free(old_ooseq);
-            }
-          } else {
-            next = pcb->ooseq;
-            /* Remove all segments on ooseq that are covered by inseg already.
-             * FIN is copied from ooseq to inseg if present. */
-            while (next &&
-                   TCP_SEQ_GEQ(seqno + tcplen,
-                               next->tcphdr->seqno + next->len)) {
-              /* inseg cannot have FIN here (already processed above) */
-              if (TCPH_FLAGS(next->tcphdr) & TCP_FIN &&
-                  (TCPH_FLAGS(inseg.tcphdr) & TCP_SYN) == 0) {
-                TCPH_SET_FLAG(inseg.tcphdr, TCP_FIN);
-                tcplen = TCP_TCPLEN(&inseg);
-              }
-              prev = next;
-              next = next->next;
-              tcp_seg_free(prev);
-            }
-            /* Now trim right side of inseg if it overlaps with the first
-             * segment on ooseq */
-            if (next &&
-                TCP_SEQ_GT(seqno + tcplen,
-                           next->tcphdr->seqno)) {
-              /* inseg cannot have FIN here (already processed above) */
-              inseg.len = (u16_t)(next->tcphdr->seqno - seqno);
-              if (TCPH_FLAGS(inseg.tcphdr) & TCP_SYN) {
-                inseg.len -= 1;
-              }
-              pbuf_realloc(inseg.p, inseg.len);
-              tcplen = TCP_TCPLEN(&inseg);
-              LWIP_ASSERT("tcp_receive: segment not trimmed correctly to ooseq queue\n",
-                          (seqno + tcplen) == next->tcphdr->seqno);
-            }
-            pcb->ooseq = next;
-          }
-        }
-#endif /* TCP_QUEUE_OOSEQ */
-
-        pcb->rcv_nxt = seqno + tcplen;
-
-        /* Update the receiver's (our) window. */
-        LWIP_ASSERT("tcp_receive: tcplen > rcv_wnd\n", pcb->rcv_wnd >= tcplen);
-        pcb->rcv_wnd -= tcplen;
-
-        tcp_update_rcv_ann_wnd(pcb);
-
-        /* If there is data in the segment, we make preparations to
-           pass this up to the application. The ->recv_data variable
-           is used for holding the pbuf that goes to the
-           application. The code for reassembling out-of-sequence data
-           chains its data on this pbuf as well.
-
-           If the segment was a FIN, we set the TF_GOT_FIN flag that will
-           be used to indicate to the application that the remote side has
-           closed its end of the connection. */
-        if (inseg.p->tot_len > 0) {
-          recv_data = inseg.p;
-          /* Since this pbuf now is the responsibility of the
-             application, we delete our reference to it so that we won't
-             (mistakingly) deallocate it. */
-          inseg.p = NULL;
-        }
-        if (TCPH_FLAGS(inseg.tcphdr) & TCP_FIN) {
-          LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: received FIN.\n"));
-          recv_flags |= TF_GOT_FIN;
-        }
-
-#if TCP_QUEUE_OOSEQ
-        /* We now check if we have segments on the ->ooseq queue that
-           are now in sequence. */
-        while (pcb->ooseq != NULL &&
-               pcb->ooseq->tcphdr->seqno == pcb->rcv_nxt) {
-
-          cseg = pcb->ooseq;
-          seqno = pcb->ooseq->tcphdr->seqno;
-
-          pcb->rcv_nxt += TCP_TCPLEN(cseg);
-          LWIP_ASSERT("tcp_receive: ooseq tcplen > rcv_wnd\n",
-                      pcb->rcv_wnd >= TCP_TCPLEN(cseg));
-          pcb->rcv_wnd -= TCP_TCPLEN(cseg);
-
-          tcp_update_rcv_ann_wnd(pcb);
-
-          if (cseg->p->tot_len > 0) {
-            /* Chain this pbuf onto the pbuf that we will pass to
-               the application. */
-            if (recv_data) {
-              pbuf_cat(recv_data, cseg->p);
-            } else {
-              recv_data = cseg->p;
-            }
-            cseg->p = NULL;
-          }
-          if (TCPH_FLAGS(cseg->tcphdr) & TCP_FIN) {
-            LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: dequeued FIN.\n"));
-            recv_flags |= TF_GOT_FIN;
-            if (pcb->state == ESTABLISHED) { /* force passive close or we can move to active close */
-              pcb->state = CLOSE_WAIT;
-            } 
-          }
-
-          pcb->ooseq = cseg->next;
-          tcp_seg_free(cseg);
-        }
-#endif /* TCP_QUEUE_OOSEQ */
-
-
-        /* Acknowledge the segment(s). */
-        tcp_ack(pcb);
-
-      } else {
-        /* We get here if the incoming segment is out-of-sequence. */
-        tcp_send_empty_ack(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 some segments with the new
-                   one. */
-                cseg = tcp_seg_copy(&inseg);
-                if (cseg != NULL) {
-                  if (prev != NULL) {
-                    prev->next = cseg;
-                  } else {
-                    pcb->ooseq = cseg;
-                  }
-                  tcp_oos_insert_segment(cseg, next);
-                }
-                break;
-              } else {
-                /* Either the 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. */
-                  cseg = tcp_seg_copy(&inseg);
-                  if (cseg != NULL) {
-                    pcb->ooseq = cseg;
-                    tcp_oos_insert_segment(cseg, next);
-                  }
-                  break;
-                }
-              } else {
-                /*if (TCP_SEQ_LT(prev->tcphdr->seqno, seqno) &&
-                  TCP_SEQ_LT(seqno, next->tcphdr->seqno)) {*/
-                if (TCP_SEQ_BETWEEN(seqno, prev->tcphdr->seqno+1, next->tcphdr->seqno-1)) {
-                  /* The sequence number of the incoming segment is in
-                     between the sequence numbers of the previous and
-                     the next segment on ->ooseq. We trim trim the previous
-                     segment, delete next segments that included in received segment
-                     and trim received, if needed. */
-                  cseg = tcp_seg_copy(&inseg);
-                  if (cseg != NULL) {
-                    if (TCP_SEQ_GT(prev->tcphdr->seqno + prev->len, seqno)) {
-                      /* We need to trim the prev segment. */
-                      prev->len = (u16_t)(seqno - prev->tcphdr->seqno);
-                      pbuf_realloc(prev->p, prev->len);
-                    }
-                    prev->next = cseg;
-                    tcp_oos_insert_segment(cseg, next);
-                  }
-                  break;
-                }
-              }
-              /* If the "next" segment is the last segment on the
-                 ooseq queue, we add the incoming segment to the end
-                 of the list. */
-              if (next->next == NULL &&
-                  TCP_SEQ_GT(seqno, next->tcphdr->seqno)) {
-                if (TCPH_FLAGS(next->tcphdr) & TCP_FIN) {
-                  /* segment "next" already contains all data */
-                  break;
-                }
-                next->next = tcp_seg_copy(&inseg);
-                if (next->next != NULL) {
-                  if (TCP_SEQ_GT(next->tcphdr->seqno + next->len, seqno)) {
-                    /* We need to trim the last segment. */
-                    next->len = (u16_t)(seqno - next->tcphdr->seqno);
-                    pbuf_realloc(next->p, next->len);
-                  }
-                  /* check if the remote side overruns our receive window */
-                  if ((u32_t)tcplen + seqno > pcb->rcv_nxt + (u32_t)pcb->rcv_wnd) {
-                    LWIP_DEBUGF(TCP_INPUT_DEBUG, 
-                                ("tcp_receive: other end overran receive window"
-                                 "seqno %"U32_F" len %"U16_F" right edge %"U32_F"\n",
-                                 seqno, tcplen, pcb->rcv_nxt + pcb->rcv_wnd));
-                    if (TCPH_FLAGS(next->next->tcphdr) & TCP_FIN) {
-                      /* Must remove the FIN from the header as we're trimming 
-                       * that byte of sequence-space from the packet */
-                      TCPH_FLAGS_SET(next->next->tcphdr, TCPH_FLAGS(next->next->tcphdr) &~ TCP_FIN);
-                    }
-                    /* Adjust length of segment to fit in the window. */
-                    next->next->len = pcb->rcv_nxt + pcb->rcv_wnd - seqno;
-                    pbuf_realloc(next->next->p, next->next->len);
-                    tcplen = TCP_TCPLEN(next->next);
-                    LWIP_ASSERT("tcp_receive: segment not trimmed correctly to rcv_wnd\n",
-                                (seqno + tcplen) == (pcb->rcv_nxt + pcb->rcv_wnd));
-                  }
-                }
-                break;
-              }
-            }
-            prev = next;
-          }
-        }
-#if TCP_OOSEQ_MAX_BYTES || TCP_OOSEQ_MAX_PBUFS
-        /* Check that the data on ooseq doesn't exceed one of the limits
-           and throw away everything above that limit. */
-        ooseq_blen = 0;
-        ooseq_qlen = 0;
-        prev = NULL;
-        for(next = pcb->ooseq; next != NULL; prev = next, next = next->next) {
-          struct pbuf *p = next->p;
-          ooseq_blen += p->tot_len;
-          ooseq_qlen += pbuf_clen(p);
-          if ((ooseq_blen > TCP_OOSEQ_MAX_BYTES) ||
-              (ooseq_qlen > TCP_OOSEQ_MAX_PBUFS)) {
-             /* too much ooseq data, dump this and everything after it */
-             tcp_segs_free(next);
-             if (prev == NULL) {
-               /* first ooseq segment is too much, dump the whole queue */
-               pcb->ooseq = NULL;
-             } else {
-               /* just dump 'next' and everything after it */
-               prev->next = NULL;
-             }
-             break;
-          }
-        }
-#endif /* TCP_OOSEQ_MAX_BYTES || TCP_OOSEQ_MAX_PBUFS */
-#endif /* TCP_QUEUE_OOSEQ */
-      }
-    } else {
-      /* The incoming segment is not withing the window. */
-      tcp_send_empty_ack(pcb);
-    }
-  } else {
-    /* Segments with length 0 is taken care of here. Segments that
-       fall out of the window are ACKed. */
-    /*if (TCP_SEQ_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);
-    }
-  }
-}
-
-/**
- * Parses the options contained in the incoming segment. 
- *
- * Called from tcp_listen_input() and tcp_process().
- * Currently, only the MSS option is supported!
- *
- * @param pcb the tcp_pcb for which a segment arrived
- */
-static void
-tcp_parseopt(struct tcp_pcb *pcb)
-{
-  u16_t c, max_c;
-  u16_t mss;
-  u8_t *opts, opt;
-#if LWIP_TCP_TIMESTAMPS
-  u32_t tsval;
-#endif
-
-  opts = (u8_t *)tcphdr + TCP_HLEN;
-
-  /* Parse the TCP MSS option, if present. */
-  if(TCPH_HDRLEN(tcphdr) > 0x5) {
-    max_c = (TCPH_HDRLEN(tcphdr) - 5) << 2;
-    for (c = 0; c < max_c; ) {
-      opt = opts[c];
-      switch (opt) {
-      case 0x00:
-        /* End of options. */
-        LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: EOL\n"));
-        return;
-      case 0x01:
-        /* NOP option. */
-        ++c;
-        LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: NOP\n"));
-        break;
-      case 0x02:
-        LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: MSS\n"));
-        if (opts[c + 1] != 0x04 || c + 0x04 > max_c) {
-          /* Bad length */
-          LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n"));
-          return;
-        }
-        /* An MSS option with the right option length. */
-        mss = (opts[c + 2] << 8) | opts[c + 3];
-        /* Limit the mss to the configured TCP_MSS and prevent division by zero */
-        pcb->mss = ((mss > TCP_MSS) || (mss == 0)) ? TCP_MSS : mss;
-        /* Advance to next option */
-        c += 0x04;
-        break;
-#if LWIP_TCP_TIMESTAMPS
-      case 0x08:
-        LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: TS\n"));
-        if (opts[c + 1] != 0x0A || c + 0x0A > max_c) {
-          /* Bad length */
-          LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n"));
-          return;
-        }
-        /* TCP timestamp option with valid length */
-        tsval = (opts[c+2]) | (opts[c+3] << 8) | 
-          (opts[c+4] << 16) | (opts[c+5] << 24);
-        if (flags & TCP_SYN) {
-          pcb->ts_recent = ntohl(tsval);
-          pcb->flags |= TF_TIMESTAMP;
-        } else if (TCP_SEQ_BETWEEN(pcb->ts_lastacksent, seqno, seqno+tcplen)) {
-          pcb->ts_recent = ntohl(tsval);
-        }
-        /* Advance to next option */
-        c += 0x0A;
-        break;
-#endif
-      default:
-        LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: other\n"));
-        if (opts[c + 1] == 0) {
-          LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n"));
-          /* If the length field is zero, the options are malformed
-             and we don't process them further. */
-          return;
-        }
-        /* All other options have a length field, so that we easily
-           can skip past them. */
-        c += opts[c + 1];
-      }
-    }
-  }
-}
-
-#endif /* LWIP_TCP */
+/**
+ * @file
+ * Transmission Control Protocol, incoming traffic
+ *
+ * The input processing functions of the TCP layer.
+ *
+ * These functions are generally called in the order (ip_input() ->)
+ * tcp_input() -> * tcp_process() -> tcp_receive() (-> application).
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/priv/tcp_priv.h"
+#include "lwip/def.h"
+#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+#include "lwip/mem.h"
+#include "lwip/memp.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/stats.h"
+#include "lwip/ip6.h"
+#include "lwip/ip6_addr.h"
+#if LWIP_ND6_TCP_REACHABILITY_HINTS
+#include "lwip/nd6.h"
+#endif /* LWIP_ND6_TCP_REACHABILITY_HINTS */
+
+/** Initial CWND calculation as defined RFC 2581 */
+#define LWIP_TCP_CALC_INITIAL_CWND(mss) LWIP_MIN((4U * (mss)), LWIP_MAX((2U * (mss)), 4380U));
+
+/* These variables are global to all functions involved in the input
+   processing of TCP segments. They are set by the tcp_input()
+   function. */
+static struct tcp_seg inseg;
+static struct tcp_hdr *tcphdr;
+static u16_t tcphdr_optlen;
+static u16_t tcphdr_opt1len;
+static u8_t* tcphdr_opt2;
+static u16_t tcp_optidx;
+static u32_t seqno, ackno;
+static tcpwnd_size_t recv_acked;
+static u16_t tcplen;
+static u8_t flags;
+
+static u8_t recv_flags;
+static struct pbuf *recv_data;
+
+struct tcp_pcb *tcp_input_pcb;
+
+/* Forward declarations. */
+static err_t tcp_process(struct tcp_pcb *pcb);
+static void tcp_receive(struct tcp_pcb *pcb);
+static void tcp_parseopt(struct tcp_pcb *pcb);
+
+static void tcp_listen_input(struct tcp_pcb_listen *pcb);
+static void tcp_timewait_input(struct tcp_pcb *pcb);
+
+/**
+ * The initial input processing of TCP. It verifies the TCP header, demultiplexes
+ * the segment between the PCBs and passes it on to tcp_process(), which implements
+ * the TCP finite state machine. This function is called by the IP layer (in
+ * ip_input()).
+ *
+ * @param p received TCP segment to process (p->payload pointing to the TCP header)
+ * @param inp network interface on which this segment was received
+ */
+void
+tcp_input(struct pbuf *p, struct netif *inp)
+{
+  struct tcp_pcb *pcb, *prev;
+  struct tcp_pcb_listen *lpcb;
+#if SO_REUSE
+  struct tcp_pcb *lpcb_prev = NULL;
+  struct tcp_pcb_listen *lpcb_any = NULL;
+#endif /* SO_REUSE */
+  u8_t hdrlen_bytes;
+  err_t err;
+
+  LWIP_UNUSED_ARG(inp);
+
+  PERF_START;
+
+  TCP_STATS_INC(tcp.recv);
+  MIB2_STATS_INC(mib2.tcpinsegs);
+
+  tcphdr = (struct tcp_hdr *)p->payload;
+
+#if TCP_INPUT_DEBUG
+  tcp_debug_print(tcphdr);
+#endif
+
+  /* Check that TCP header fits in payload */
+  if (p->len < TCP_HLEN) {
+    /* drop short packets */
+    LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: short packet (%"U16_F" bytes) discarded\n", p->tot_len));
+    TCP_STATS_INC(tcp.lenerr);
+    goto dropped;
+  }
+
+  /* Don't even process incoming broadcasts/multicasts. */
+  if (ip_addr_isbroadcast(ip_current_dest_addr(), ip_current_netif()) ||
+      ip_addr_ismulticast(ip_current_dest_addr())) {
+    TCP_STATS_INC(tcp.proterr);
+    goto dropped;
+  }
+
+#if CHECKSUM_CHECK_TCP
+  IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_CHECK_TCP) {
+    /* Verify TCP checksum. */
+    u16_t chksum = ip_chksum_pseudo(p, IP_PROTO_TCP, p->tot_len,
+                               ip_current_src_addr(), ip_current_dest_addr());
+    if (chksum != 0) {
+        LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packet discarded due to failing checksum 0x%04"X16_F"\n",
+          chksum));
+      tcp_debug_print(tcphdr);
+      TCP_STATS_INC(tcp.chkerr);
+      goto dropped;
+    }
+  }
+#endif /* CHECKSUM_CHECK_TCP */
+
+  /* sanity-check header length */
+  hdrlen_bytes = TCPH_HDRLEN(tcphdr) * 4;
+  if ((hdrlen_bytes < TCP_HLEN) || (hdrlen_bytes > p->tot_len)) {
+    LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: invalid header length (%"U16_F")\n", (u16_t)hdrlen_bytes));
+    TCP_STATS_INC(tcp.lenerr);
+    goto dropped;
+  }
+
+  /* Move the payload pointer in the pbuf so that it points to the
+     TCP data instead of the TCP header. */
+  tcphdr_optlen = hdrlen_bytes - TCP_HLEN;
+  tcphdr_opt2 = NULL;
+  if (p->len >= hdrlen_bytes) {
+    /* all options are in the first pbuf */
+    tcphdr_opt1len = tcphdr_optlen;
+    pbuf_header(p, -(s16_t)hdrlen_bytes); /* cannot fail */
+  } else {
+    u16_t opt2len;
+    /* TCP header fits into first pbuf, options don't - data is in the next pbuf */
+    /* there must be a next pbuf, due to hdrlen_bytes sanity check above */
+    LWIP_ASSERT("p->next != NULL", p->next != NULL);
+
+    /* advance over the TCP header (cannot fail) */
+    pbuf_header(p, -TCP_HLEN);
+
+    /* determine how long the first and second parts of the options are */
+    tcphdr_opt1len = p->len;
+    opt2len = tcphdr_optlen - tcphdr_opt1len;
+
+    /* options continue in the next pbuf: set p to zero length and hide the
+        options in the next pbuf (adjusting p->tot_len) */
+    pbuf_header(p, -(s16_t)tcphdr_opt1len);
+
+    /* check that the options fit in the second pbuf */
+    if (opt2len > p->next->len) {
+      /* drop short packets */
+      LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: options overflow second pbuf (%"U16_F" bytes)\n", p->next->len));
+      TCP_STATS_INC(tcp.lenerr);
+      goto dropped;
+    }
+
+    /* remember the pointer to the second part of the options */
+    tcphdr_opt2 = (u8_t*)p->next->payload;
+
+    /* advance p->next to point after the options, and manually
+        adjust p->tot_len to keep it consistent with the changed p->next */
+    pbuf_header(p->next, -(s16_t)opt2len);
+    p->tot_len -= opt2len;
+
+    LWIP_ASSERT("p->len == 0", p->len == 0);
+    LWIP_ASSERT("p->tot_len == p->next->tot_len", p->tot_len == p->next->tot_len);
+  }
+
+  /* Convert fields in TCP header to host byte order. */
+  tcphdr->src = lwip_ntohs(tcphdr->src);
+  tcphdr->dest = lwip_ntohs(tcphdr->dest);
+  seqno = tcphdr->seqno = lwip_ntohl(tcphdr->seqno);
+  ackno = tcphdr->ackno = lwip_ntohl(tcphdr->ackno);
+  tcphdr->wnd = lwip_ntohs(tcphdr->wnd);
+
+  flags = TCPH_FLAGS(tcphdr);
+  tcplen = p->tot_len + ((flags & (TCP_FIN | 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, ip_current_src_addr()) &&
+        ip_addr_cmp(&pcb->local_ip, ip_current_dest_addr())) {
+      /* Move this PCB to the front of the list so that subsequent
+         lookups will be faster (we exploit locality in TCP segment
+         arrivals). */
+      LWIP_ASSERT("tcp_input: pcb->next != pcb (before cache)", pcb->next != pcb);
+      if (prev != NULL) {
+        prev->next = pcb->next;
+        pcb->next = tcp_active_pcbs;
+        tcp_active_pcbs = pcb;
+      } else {
+        TCP_STATS_INC(tcp.cachehit);
+      }
+      LWIP_ASSERT("tcp_input: pcb->next != pcb (after cache)", pcb->next != pcb);
+      break;
+    }
+    prev = pcb;
+  }
+
+  if (pcb == NULL) {
+    /* If it did not go to an active connection, we check the connections
+       in the TIME-WAIT state. */
+    for (pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) {
+      LWIP_ASSERT("tcp_input: TIME-WAIT pcb->state == TIME-WAIT", pcb->state == TIME_WAIT);
+      if (pcb->remote_port == tcphdr->src &&
+          pcb->local_port == tcphdr->dest &&
+          ip_addr_cmp(&pcb->remote_ip, ip_current_src_addr()) &&
+          ip_addr_cmp(&pcb->local_ip, ip_current_dest_addr())) {
+        /* We don't really care enough to move this PCB to the front
+           of the list since we are not very likely to receive that
+           many segments for connections in TIME-WAIT. */
+        LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packed for TIME_WAITing connection.\n"));
+        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 (lpcb->local_port == tcphdr->dest) {
+        if (IP_IS_ANY_TYPE_VAL(lpcb->local_ip)) {
+          /* found an ANY TYPE (IPv4/IPv6) match */
+#if SO_REUSE
+          lpcb_any = lpcb;
+          lpcb_prev = prev;
+#else /* SO_REUSE */
+          break;
+#endif /* SO_REUSE */
+        } else if (IP_ADDR_PCB_VERSION_MATCH_EXACT(lpcb, ip_current_dest_addr())) {
+          if (ip_addr_cmp(&lpcb->local_ip, ip_current_dest_addr())) {
+            /* found an exact match */
+            break;
+          } else if (ip_addr_isany(&lpcb->local_ip)) {
+            /* found an ANY-match */
+#if SO_REUSE
+            lpcb_any = lpcb;
+            lpcb_prev = prev;
+#else /* SO_REUSE */
+            break;
+ #endif /* SO_REUSE */
+          }
+        }
+      }
+      prev = (struct tcp_pcb *)lpcb;
+    }
+#if SO_REUSE
+    /* first try specific local IP */
+    if (lpcb == NULL) {
+      /* only pass to ANY if no specific local IP has been found */
+      lpcb = lpcb_any;
+      prev = lpcb_prev;
+    }
+#endif /* SO_REUSE */
+    if (lpcb != NULL) {
+      /* Move this PCB to the front of the list so that subsequent
+         lookups will be faster (we exploit locality in TCP segment
+         arrivals). */
+      if (prev != NULL) {
+        ((struct tcp_pcb_listen *)prev)->next = lpcb->next;
+              /* our successor is the remainder of the listening list */
+        lpcb->next = tcp_listen_pcbs.listen_pcbs;
+              /* put this listening pcb at the head of the listening list */
+        tcp_listen_pcbs.listen_pcbs = lpcb;
+      } else {
+        TCP_STATS_INC(tcp.cachehit);
+      }
+
+      LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packed for LISTENing connection.\n"));
+      tcp_listen_input(lpcb);
+      pbuf_free(p);
+      return;
+    }
+  }
+
+#if TCP_INPUT_DEBUG
+  LWIP_DEBUGF(TCP_INPUT_DEBUG, ("+-+-+-+-+-+-+-+-+-+-+-+-+-+- tcp_input: flags "));
+  tcp_debug_print_flags(TCPH_FLAGS(tcphdr));
+  LWIP_DEBUGF(TCP_INPUT_DEBUG, ("-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n"));
+#endif /* TCP_INPUT_DEBUG */
+
+
+  if (pcb != NULL) {
+    /* The incoming segment belongs to a connection. */
+#if TCP_INPUT_DEBUG
+    tcp_debug_print_state(pcb->state);
+#endif /* TCP_INPUT_DEBUG */
+
+    /* Set up a tcp_seg structure. */
+    inseg.next = NULL;
+    inseg.len = p->tot_len;
+    inseg.p = p;
+    inseg.tcphdr = tcphdr;
+
+    recv_data = NULL;
+    recv_flags = 0;
+    recv_acked = 0;
+
+    if (flags & TCP_PSH) {
+      p->flags |= PBUF_FLAG_PUSH;
+    }
+
+    /* If there is data which was previously "refused" by upper layer */
+    if (pcb->refused_data != NULL) {
+      if ((tcp_process_refused_data(pcb) == ERR_ABRT) ||
+        ((pcb->refused_data != NULL) && (tcplen > 0))) {
+        /* pcb has been aborted or refused data is still refused and the new
+           segment contains data */
+        if (pcb->rcv_ann_wnd == 0) {
+          /* this is a zero-window probe, we respond to it with current RCV.NXT
+          and drop the data segment */
+          tcp_send_empty_ack(pcb);
+        }
+        TCP_STATS_INC(tcp.drop);
+        MIB2_STATS_INC(mib2.tcpinerrs);
+        goto aborted;
+      }
+    }
+    tcp_input_pcb = pcb;
+    err = tcp_process(pcb);
+    /* A return value of ERR_ABRT means that tcp_abort() was called
+       and that the pcb has been freed. If so, we don't do anything. */
+    if (err != ERR_ABRT) {
+      if (recv_flags & TF_RESET) {
+        /* TF_RESET means that the connection was reset by the other
+           end. We then call the error callback to inform the
+           application that the connection is dead before we
+           deallocate the PCB. */
+        TCP_EVENT_ERR(pcb->state, pcb->errf, pcb->callback_arg, ERR_RST);
+        tcp_pcb_remove(&tcp_active_pcbs, pcb);
+        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 (recv_acked > 0) {
+          u16_t acked16;
+#if LWIP_WND_SCALE
+          /* recv_acked is u32_t but the sent callback only takes a u16_t,
+             so we might have to call it multiple times. */
+          u32_t acked = recv_acked;
+          while (acked > 0) {
+            acked16 = (u16_t)LWIP_MIN(acked, 0xffffu);
+            acked -= acked16;
+#else
+          {
+            acked16 = recv_acked;
+#endif
+            TCP_EVENT_SENT(pcb, (u16_t)acked16, err);
+            if (err == ERR_ABRT) {
+              goto aborted;
+            }
+          }
+          recv_acked = 0;
+        }
+        if (recv_flags & TF_CLOSED) {
+          /* The connection has been closed and we will deallocate the
+             PCB. */
+          if (!(pcb->flags & TF_RXCLOSED)) {
+            /* Connection closed although the application has only shut down the
+               tx side: call the PCB's err callback and indicate the closure to
+               ensure the application doesn't continue using the PCB. */
+            TCP_EVENT_ERR(pcb->state, pcb->errf, pcb->callback_arg, ERR_CLSD);
+          }
+          tcp_pcb_remove(&tcp_active_pcbs, pcb);
+          memp_free(MEMP_TCP_PCB, pcb);
+          goto aborted;
+        }
+#if TCP_QUEUE_OOSEQ && LWIP_WND_SCALE
+        while (recv_data != NULL) {
+          struct pbuf *rest = NULL;
+          pbuf_split_64k(recv_data, &rest);
+#else /* TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */
+        if (recv_data != NULL) {
+#endif /* TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */
+
+          LWIP_ASSERT("pcb->refused_data == NULL", pcb->refused_data == NULL);
+          if (pcb->flags & TF_RXCLOSED) {
+            /* received data although already closed -> abort (send RST) to
+               notify the remote host that not all data has been processed */
+            pbuf_free(recv_data);
+#if TCP_QUEUE_OOSEQ && LWIP_WND_SCALE
+            if (rest != NULL) {
+              pbuf_free(rest);
+            }
+#endif /* TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */
+            tcp_abort(pcb);
+            goto aborted;
+          }
+
+          /* Notify application that data has been received. */
+          TCP_EVENT_RECV(pcb, recv_data, ERR_OK, err);
+          if (err == ERR_ABRT) {
+#if TCP_QUEUE_OOSEQ && LWIP_WND_SCALE
+            if (rest != NULL) {
+              pbuf_free(rest);
+            }
+#endif /* TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */
+            goto aborted;
+          }
+
+          /* If the upper layer can't receive this data, store it */
+          if (err != ERR_OK) {
+#if TCP_QUEUE_OOSEQ && LWIP_WND_SCALE
+            if (rest != NULL) {
+              pbuf_cat(recv_data, rest);
+            }
+#endif /* TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */
+            pcb->refused_data = recv_data;
+            LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: keep incoming packet, because pcb is \"full\"\n"));
+#if TCP_QUEUE_OOSEQ && LWIP_WND_SCALE
+            break;
+          } else {
+            /* Upper layer received the data, go on with the rest if > 64K */
+            recv_data = rest;
+#endif /* TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */
+          }
+        }
+
+        /* If a FIN segment was received, we call the callback
+           function with a NULL buffer to indicate EOF. */
+        if (recv_flags & TF_GOT_FIN) {
+          if (pcb->refused_data != NULL) {
+            /* Delay this if we have refused data. */
+            pcb->refused_data->flags |= PBUF_FLAG_TCP_FIN;
+          } else {
+            /* correct rcv_wnd as the application won't call tcp_recved()
+               for the FIN's seqno */
+            if (pcb->rcv_wnd != TCP_WND_MAX(pcb)) {
+              pcb->rcv_wnd++;
+            }
+            TCP_EVENT_CLOSED(pcb, err);
+            if (err == ERR_ABRT) {
+              goto aborted;
+            }
+          }
+        }
+
+        tcp_input_pcb = NULL;
+        /* Try to send something out. */
+        tcp_output(pcb);
+#if TCP_INPUT_DEBUG
+#if TCP_DEBUG
+        tcp_debug_print_state(pcb->state);
+#endif /* TCP_DEBUG */
+#endif /* TCP_INPUT_DEBUG */
+      }
+    }
+    /* Jump target if pcb has been aborted in a callback (by calling tcp_abort()).
+       Below this line, 'pcb' may not be dereferenced! */
+aborted:
+    tcp_input_pcb = NULL;
+    recv_data = NULL;
+
+    /* give up our reference to inseg.p */
+    if (inseg.p != NULL)
+    {
+      pbuf_free(inseg.p);
+      inseg.p = NULL;
+    }
+  } else {
+
+    /* If no matching PCB was found, send a TCP RST (reset) to the
+       sender. */
+    LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_input: no PCB match found, resetting.\n"));
+    if (!(TCPH_FLAGS(tcphdr) & TCP_RST)) {
+      TCP_STATS_INC(tcp.proterr);
+      TCP_STATS_INC(tcp.drop);
+      tcp_rst(ackno, seqno + tcplen, ip_current_dest_addr(),
+        ip_current_src_addr(), tcphdr->dest, tcphdr->src);
+    }
+    pbuf_free(p);
+  }
+
+  LWIP_ASSERT("tcp_input: tcp_pcbs_sane()", tcp_pcbs_sane());
+  PERF_STOP("tcp_input");
+  return;
+dropped:
+  TCP_STATS_INC(tcp.drop);
+  MIB2_STATS_INC(mib2.tcpinerrs);
+  pbuf_free(p);
+}
+
+/**
+ * Called by tcp_input() when a segment arrives for a listening
+ * connection (from tcp_input()).
+ *
+ * @param pcb the tcp_pcb_listen for which a segment arrived
+ *
+ * @note the segment which arrived is saved in global variables, therefore only the pcb
+ *       involved is passed as a parameter to this function
+ */
+static void
+tcp_listen_input(struct tcp_pcb_listen *pcb)
+{
+  struct tcp_pcb *npcb;
+  u32_t iss;
+  err_t rc;
+
+  if (flags & TCP_RST) {
+    /* An incoming RST should be ignored. Return. */
+    return;
+  }
+
+  /* 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, seqno + tcplen, ip_current_dest_addr(),
+      ip_current_src_addr(), tcphdr->dest, tcphdr->src);
+  } else if (flags & TCP_SYN) {
+    LWIP_DEBUGF(TCP_DEBUG, ("TCP connection request %"U16_F" -> %"U16_F".\n", tcphdr->src, tcphdr->dest));
+#if TCP_LISTEN_BACKLOG
+    if (pcb->accepts_pending >= pcb->backlog) {
+      LWIP_DEBUGF(TCP_DEBUG, ("tcp_listen_input: listen backlog exceeded for port %"U16_F"\n", tcphdr->dest));
+      return;
+    }
+#endif /* TCP_LISTEN_BACKLOG */
+    npcb = tcp_alloc(pcb->prio);
+    /* If a new PCB could not be created (probably due to lack of memory),
+       we don't do anything, but rely on the sender will retransmit the
+       SYN at a time when we have more memory available. */
+    if (npcb == NULL) {
+      err_t err;
+      LWIP_DEBUGF(TCP_DEBUG, ("tcp_listen_input: could not allocate PCB\n"));
+      TCP_STATS_INC(tcp.memerr);
+      TCP_EVENT_ACCEPT(pcb, NULL, pcb->callback_arg, ERR_MEM, err);
+      LWIP_UNUSED_ARG(err); /* err not useful here */
+      return;
+    }
+#if TCP_LISTEN_BACKLOG
+    pcb->accepts_pending++;
+    npcb->flags |= TF_BACKLOGPEND;
+#endif /* TCP_LISTEN_BACKLOG */
+    /* Set up the new PCB. */
+    ip_addr_copy(npcb->local_ip, *ip_current_dest_addr());
+    ip_addr_copy(npcb->remote_ip, *ip_current_src_addr());
+    npcb->local_port = pcb->local_port;
+    npcb->remote_port = tcphdr->src;
+    npcb->state = SYN_RCVD;
+    npcb->rcv_nxt = seqno + 1;
+    npcb->rcv_ann_right_edge = npcb->rcv_nxt;
+    iss = tcp_next_iss(npcb);
+    npcb->snd_wl2 = iss;
+    npcb->snd_nxt = iss;
+    npcb->lastack = iss;
+    npcb->snd_lbb = iss;
+    npcb->snd_wl1 = seqno - 1;/* initialise to seqno-1 to force window update */
+    npcb->callback_arg = pcb->callback_arg;
+#if LWIP_CALLBACK_API || TCP_LISTEN_BACKLOG
+    npcb->listener = pcb;
+#endif /* LWIP_CALLBACK_API || TCP_LISTEN_BACKLOG */
+    /* inherit socket options */
+    npcb->so_options = pcb->so_options & SOF_INHERITED;
+    /* Register the new PCB so that we can begin receiving segments
+       for it. */
+    TCP_REG_ACTIVE(npcb);
+
+    /* Parse any options in the SYN. */
+    tcp_parseopt(npcb);
+    npcb->snd_wnd = tcphdr->wnd;
+    npcb->snd_wnd_max = npcb->snd_wnd;
+
+#if TCP_CALCULATE_EFF_SEND_MSS
+    npcb->mss = tcp_eff_send_mss(npcb->mss, &npcb->local_ip, &npcb->remote_ip);
+#endif /* TCP_CALCULATE_EFF_SEND_MSS */
+
+    MIB2_STATS_INC(mib2.tcppassiveopens);
+
+    /* Send a SYN|ACK together with the MSS option. */
+    rc = tcp_enqueue_flags(npcb, TCP_SYN | TCP_ACK);
+    if (rc != ERR_OK) {
+      tcp_abandon(npcb, 0);
+      return;
+    }
+    tcp_output(npcb);
+  }
+  return;
+}
+
+/**
+ * Called by tcp_input() when a segment arrives for a connection in
+ * TIME_WAIT.
+ *
+ * @param pcb the tcp_pcb for which a segment arrived
+ *
+ * @note the segment which arrived is saved in global variables, therefore only the pcb
+ *       involved is passed as a parameter to this function
+ */
+static void
+tcp_timewait_input(struct tcp_pcb *pcb)
+{
+  /* RFC 1337: in TIME_WAIT, ignore RST and ACK FINs + any 'acceptable' segments */
+  /* RFC 793 3.9 Event Processing - Segment Arrives:
+   * - first check sequence number - we skip that one in TIME_WAIT (always
+   *   acceptable since we only send ACKs)
+   * - second check the RST bit (... return) */
+  if (flags & TCP_RST) {
+    return;
+  }
+  /* - fourth, check the SYN bit, */
+  if (flags & TCP_SYN) {
+    /* If an incoming segment is not acceptable, an acknowledgment
+       should be sent in reply */
+    if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, pcb->rcv_nxt + pcb->rcv_wnd)) {
+      /* If the SYN is in the window it is an error, send a reset */
+      tcp_rst(ackno, seqno + tcplen, ip_current_dest_addr(),
+        ip_current_src_addr(), tcphdr->dest, tcphdr->src);
+      return;
+    }
+  } else if (flags & TCP_FIN) {
+    /* - eighth, check the FIN bit: Remain in the TIME-WAIT state.
+         Restart the 2 MSL time-wait timeout.*/
+    pcb->tmr = tcp_ticks;
+  }
+
+  if ((tcplen > 0)) {
+    /* Acknowledge data, FIN or out-of-window SYN */
+    pcb->flags |= TF_ACK_NOW;
+    tcp_output(pcb);
+  }
+  return;
+}
+
+/**
+ * Implements the TCP state machine. Called by tcp_input. In some
+ * states tcp_receive() is called to receive data. The tcp_seg
+ * argument will be freed by the caller (tcp_input()) unless the
+ * recv_data pointer in the pcb is set.
+ *
+ * @param pcb the tcp_pcb for which a segment arrived
+ *
+ * @note the segment which arrived is saved in global variables, therefore only the pcb
+ *       involved is passed as a parameter to this function
+ */
+static err_t
+tcp_process(struct tcp_pcb *pcb)
+{
+  struct tcp_seg *rseg;
+  u8_t acceptable = 0;
+  err_t err;
+
+  err = ERR_OK;
+
+  /* Process incoming RST segments. */
+  if (flags & TCP_RST) {
+    /* First, determine if the reset is acceptable. */
+    if (pcb->state == SYN_SENT) {
+      /* "In the SYN-SENT state (a RST received in response to an initial SYN),
+          the RST is acceptable if the ACK field acknowledges the SYN." */
+      if (ackno == pcb->snd_nxt) {
+        acceptable = 1;
+      }
+    } else {
+      /* "In all states except SYN-SENT, all reset (RST) segments are validated
+          by checking their SEQ-fields." */
+      if (seqno == pcb->rcv_nxt) {
+        acceptable = 1;
+      } else  if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt,
+                                  pcb->rcv_nxt + pcb->rcv_wnd)) {
+        /* If the sequence number is inside the window, we only send an ACK
+           and wait for a re-send with matching sequence number.
+           This violates RFC 793, but is required to protection against
+           CVE-2004-0230 (RST spoofing attack). */
+        tcp_ack_now(pcb);
+      }
+    }
+
+    if (acceptable) {
+      LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_process: Connection RESET\n"));
+      LWIP_ASSERT("tcp_input: pcb->state != CLOSED", pcb->state != CLOSED);
+      recv_flags |= TF_RESET;
+      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;
+    }
+  }
+
+  if ((flags & TCP_SYN) && (pcb->state != SYN_SENT && pcb->state != SYN_RCVD)) {
+    /* Cope with new connection attempt after remote end crashed */
+    tcp_ack_now(pcb);
+    return ERR_OK;
+  }
+
+  if ((pcb->flags & TF_RXCLOSED) == 0) {
+    /* Update the PCB (in)activity timer unless rx is closed (see tcp_shutdown) */
+    pcb->tmr = tcp_ticks;
+  }
+  pcb->keep_cnt_sent = 0;
+
+  tcp_parseopt(pcb);
+
+  /* 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, lwip_ntohl(pcb->unacked->tcphdr->seqno)));
+    /* received SYN ACK with expected sequence number? */
+    if ((flags & TCP_ACK) && (flags & TCP_SYN)
+        && (ackno == pcb->lastack + 1)) {
+      pcb->rcv_nxt = seqno + 1;
+      pcb->rcv_ann_right_edge = pcb->rcv_nxt;
+      pcb->lastack = ackno;
+      pcb->snd_wnd = tcphdr->wnd;
+      pcb->snd_wnd_max = pcb->snd_wnd;
+      pcb->snd_wl1 = seqno - 1; /* initialise to seqno - 1 to force window update */
+      pcb->state = ESTABLISHED;
+
+#if TCP_CALCULATE_EFF_SEND_MSS
+      pcb->mss = tcp_eff_send_mss(pcb->mss, &pcb->local_ip, &pcb->remote_ip);
+#endif /* TCP_CALCULATE_EFF_SEND_MSS */
+
+      pcb->cwnd = LWIP_TCP_CALC_INITIAL_CWND(pcb->mss);
+      LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_process (SENT): cwnd %"TCPWNDSIZE_F
+                                   " ssthresh %"TCPWNDSIZE_F"\n",
+                                   pcb->cwnd, pcb->ssthresh));
+      LWIP_ASSERT("pcb->snd_queuelen > 0", (pcb->snd_queuelen > 0));
+      --pcb->snd_queuelen;
+      LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_process: SYN-SENT --queuelen %"TCPWNDSIZE_F"\n", (tcpwnd_size_t)pcb->snd_queuelen));
+      rseg = pcb->unacked;
+      if (rseg == NULL) {
+        /* might happen if tcp_output fails in tcp_rexmit_rto()
+           in which case the segment is on the unsent list */
+        rseg = pcb->unsent;
+        LWIP_ASSERT("no segment to free", rseg != NULL);
+        pcb->unsent = rseg->next;
+      } else {
+        pcb->unacked = rseg->next;
+      }
+      tcp_seg_free(rseg);
+
+      /* If there's nothing left to acknowledge, stop the retransmit
+         timer, otherwise reset it to start again */
+      if (pcb->unacked == NULL) {
+        pcb->rtime = -1;
+      } else {
+        pcb->rtime = 0;
+        pcb->nrtx = 0;
+      }
+
+      /* Call the user specified function to call when successfully
+       * connected. */
+      TCP_EVENT_CONNECTED(pcb, ERR_OK, err);
+      if (err == ERR_ABRT) {
+        return ERR_ABRT;
+      }
+      tcp_ack_now(pcb);
+    }
+    /* received ACK? possibly a half-open connection */
+    else if (flags & TCP_ACK) {
+      /* send a RST to bring the other side in a non-synchronized state. */
+      tcp_rst(ackno, seqno + tcplen, ip_current_dest_addr(),
+        ip_current_src_addr(), tcphdr->dest, tcphdr->src);
+      /* Resend SYN immediately (don't wait for rto timeout) to establish
+        connection faster, but do not send more SYNs than we otherwise would
+        have, or we might get caught in a loop on loopback interfaces. */
+      if (pcb->nrtx < TCP_SYNMAXRTX) {
+        pcb->rtime = 0;
+        tcp_rexmit_rto(pcb);
+      }
+    }
+    break;
+  case SYN_RCVD:
+    if (flags & TCP_ACK) {
+      /* expected ACK number? */
+      if (TCP_SEQ_BETWEEN(ackno, pcb->lastack+1, pcb->snd_nxt)) {
+        pcb->state = ESTABLISHED;
+        LWIP_DEBUGF(TCP_DEBUG, ("TCP connection established %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
+#if LWIP_CALLBACK_API || TCP_LISTEN_BACKLOG
+#if LWIP_CALLBACK_API
+        LWIP_ASSERT("pcb->listener->accept != NULL",
+          (pcb->listener == NULL) || (pcb->listener->accept != NULL));
+#endif
+        if (pcb->listener == NULL) {
+          /* listen pcb might be closed by now */
+          err = ERR_VAL;
+        } else
+#endif /* LWIP_CALLBACK_API || TCP_LISTEN_BACKLOG */
+        {
+          tcp_backlog_accepted(pcb);
+          /* Call the accept function. */
+          TCP_EVENT_ACCEPT(pcb->listener, pcb, pcb->callback_arg, ERR_OK, err);
+        }
+        if (err != ERR_OK) {
+          /* If the accept function returns with an error, we abort
+           * the connection. */
+          /* Already aborted? */
+          if (err != ERR_ABRT) {
+            tcp_abort(pcb);
+          }
+          return ERR_ABRT;
+        }
+        /* If there was any data contained within this ACK,
+         * we'd better pass it on to the application as well. */
+        tcp_receive(pcb);
+
+        /* Prevent ACK for SYN to generate a sent event */
+        if (recv_acked != 0) {
+          recv_acked--;
+        }
+
+        pcb->cwnd = LWIP_TCP_CALC_INITIAL_CWND(pcb->mss);
+        LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_process (SYN_RCVD): cwnd %"TCPWNDSIZE_F
+                                     " ssthresh %"TCPWNDSIZE_F"\n",
+                                     pcb->cwnd, pcb->ssthresh));
+
+        if (recv_flags & TF_GOT_FIN) {
+          tcp_ack_now(pcb);
+          pcb->state = CLOSE_WAIT;
+        }
+      } else {
+        /* incorrect ACK number, send RST */
+        tcp_rst(ackno, seqno + tcplen, ip_current_dest_addr(),
+          ip_current_src_addr(), tcphdr->dest, tcphdr->src);
+      }
+    } else if ((flags & TCP_SYN) && (seqno == pcb->rcv_nxt - 1)) {
+      /* Looks like another copy of the SYN - retransmit our SYN-ACK */
+      tcp_rexmit(pcb);
+    }
+    break;
+  case CLOSE_WAIT:
+    /* FALLTHROUGH */
+  case ESTABLISHED:
+    tcp_receive(pcb);
+    if (recv_flags & TF_GOT_FIN) { /* passive close */
+      tcp_ack_now(pcb);
+      pcb->state = CLOSE_WAIT;
+    }
+    break;
+  case FIN_WAIT_1:
+    tcp_receive(pcb);
+    if (recv_flags & TF_GOT_FIN) {
+      if ((flags & TCP_ACK) && (ackno == pcb->snd_nxt) &&
+          pcb->unsent == NULL) {
+        LWIP_DEBUGF(TCP_DEBUG,
+          ("TCP connection closed: FIN_WAIT_1 %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
+        tcp_ack_now(pcb);
+        tcp_pcb_purge(pcb);
+        TCP_RMV_ACTIVE(pcb);
+        pcb->state = TIME_WAIT;
+        TCP_REG(&tcp_tw_pcbs, pcb);
+      } else {
+        tcp_ack_now(pcb);
+        pcb->state = CLOSING;
+      }
+    } else if ((flags & TCP_ACK) && (ackno == pcb->snd_nxt) &&
+               pcb->unsent == NULL) {
+      pcb->state = FIN_WAIT_2;
+    }
+    break;
+  case FIN_WAIT_2:
+    tcp_receive(pcb);
+    if (recv_flags & TF_GOT_FIN) {
+      LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: FIN_WAIT_2 %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
+      tcp_ack_now(pcb);
+      tcp_pcb_purge(pcb);
+      TCP_RMV_ACTIVE(pcb);
+      pcb->state = TIME_WAIT;
+      TCP_REG(&tcp_tw_pcbs, pcb);
+    }
+    break;
+  case CLOSING:
+    tcp_receive(pcb);
+    if ((flags & TCP_ACK) && ackno == pcb->snd_nxt && pcb->unsent == NULL) {
+      LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: CLOSING %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
+      tcp_pcb_purge(pcb);
+      TCP_RMV_ACTIVE(pcb);
+      pcb->state = TIME_WAIT;
+      TCP_REG(&tcp_tw_pcbs, pcb);
+    }
+    break;
+  case LAST_ACK:
+    tcp_receive(pcb);
+    if ((flags & TCP_ACK) && ackno == pcb->snd_nxt && pcb->unsent == NULL) {
+      LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: LAST_ACK %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
+      /* bugfix #21699: don't set pcb->state to CLOSED here or we risk leaking segments */
+      recv_flags |= TF_CLOSED;
+    }
+    break;
+  default:
+    break;
+  }
+  return ERR_OK;
+}
+
+#if TCP_QUEUE_OOSEQ
+/**
+ * Insert segment into the list (segments covered with new one will be deleted)
+ *
+ * Called from tcp_receive()
+ */
+static void
+tcp_oos_insert_segment(struct tcp_seg *cseg, struct tcp_seg *next)
+{
+  struct tcp_seg *old_seg;
+
+  if (TCPH_FLAGS(cseg->tcphdr) & TCP_FIN) {
+    /* received segment overlaps all following segments */
+    tcp_segs_free(next);
+    next = NULL;
+  } else {
+    /* delete some following segments
+       oos queue may have segments with FIN flag */
+    while (next &&
+           TCP_SEQ_GEQ((seqno + cseg->len),
+                      (next->tcphdr->seqno + next->len))) {
+      /* cseg with FIN already processed */
+      if (TCPH_FLAGS(next->tcphdr) & TCP_FIN) {
+        TCPH_SET_FLAG(cseg->tcphdr, TCP_FIN);
+      }
+      old_seg = next;
+      next = next->next;
+      tcp_seg_free(old_seg);
+    }
+    if (next &&
+        TCP_SEQ_GT(seqno + cseg->len, next->tcphdr->seqno)) {
+      /* We need to trim the incoming segment. */
+      cseg->len = (u16_t)(next->tcphdr->seqno - seqno);
+      pbuf_realloc(cseg->p, cseg->len);
+    }
+  }
+  cseg->next = next;
+}
+#endif /* TCP_QUEUE_OOSEQ */
+
+/**
+ * Called by tcp_process. Checks if the given segment is an ACK for outstanding
+ * data, and if so frees the memory of the buffered data. Next, it places the
+ * segment on any of the receive queues (pcb->recved or pcb->ooseq). If the segment
+ * is buffered, the pbuf is referenced by pbuf_ref so that it will not be freed until
+ * it has been removed from the buffer.
+ *
+ * If the incoming segment constitutes an ACK for a segment that was used for RTT
+ * estimation, the RTT is estimated here as well.
+ *
+ * Called from tcp_process().
+ */
+static void
+tcp_receive(struct tcp_pcb *pcb)
+{
+  struct tcp_seg *next;
+#if TCP_QUEUE_OOSEQ
+  struct tcp_seg *prev, *cseg;
+#endif /* TCP_QUEUE_OOSEQ */
+  s32_t off;
+  s16_t m;
+  u32_t right_wnd_edge;
+  u16_t new_tot_len;
+  int found_dupack = 0;
+#if TCP_OOSEQ_MAX_BYTES || TCP_OOSEQ_MAX_PBUFS
+  u32_t ooseq_blen;
+  u16_t ooseq_qlen;
+#endif /* TCP_OOSEQ_MAX_BYTES || TCP_OOSEQ_MAX_PBUFS */
+
+  LWIP_ASSERT("tcp_receive: wrong state", pcb->state >= ESTABLISHED);
+
+  if (flags & TCP_ACK) {
+    right_wnd_edge = pcb->snd_wnd + pcb->snd_wl2;
+
+    /* Update window. */
+    if (TCP_SEQ_LT(pcb->snd_wl1, seqno) ||
+       (pcb->snd_wl1 == seqno && TCP_SEQ_LT(pcb->snd_wl2, ackno)) ||
+       (pcb->snd_wl2 == ackno && (u32_t)SND_WND_SCALE(pcb, tcphdr->wnd) > pcb->snd_wnd)) {
+      pcb->snd_wnd = SND_WND_SCALE(pcb, tcphdr->wnd);
+      /* keep track of the biggest window announced by the remote host to calculate
+         the maximum segment size */
+      if (pcb->snd_wnd_max < pcb->snd_wnd) {
+        pcb->snd_wnd_max = pcb->snd_wnd;
+      }
+      pcb->snd_wl1 = seqno;
+      pcb->snd_wl2 = ackno;
+      if (pcb->snd_wnd == 0) {
+        if (pcb->persist_backoff == 0) {
+          /* start persist timer */
+          pcb->persist_cnt = 0;
+          pcb->persist_backoff = 1;
+        }
+      } else if (pcb->persist_backoff > 0) {
+        /* stop persist timer */
+          pcb->persist_backoff = 0;
+      }
+      LWIP_DEBUGF(TCP_WND_DEBUG, ("tcp_receive: window update %"TCPWNDSIZE_F"\n", pcb->snd_wnd));
+#if TCP_WND_DEBUG
+    } else {
+      if (pcb->snd_wnd != (tcpwnd_size_t)SND_WND_SCALE(pcb, tcphdr->wnd)) {
+        LWIP_DEBUGF(TCP_WND_DEBUG,
+                    ("tcp_receive: no window update lastack %"U32_F" ackno %"
+                     U32_F" wl1 %"U32_F" seqno %"U32_F" wl2 %"U32_F"\n",
+                     pcb->lastack, ackno, pcb->snd_wl1, seqno, pcb->snd_wl2));
+      }
+#endif /* TCP_WND_DEBUG */
+    }
+
+    /* (From Stevens TCP/IP Illustrated Vol II, p970.) Its only a
+     * duplicate ack if:
+     * 1) It doesn't ACK new data
+     * 2) length of received packet is zero (i.e. no payload)
+     * 3) the advertised window hasn't changed
+     * 4) There is outstanding unacknowledged data (retransmission timer running)
+     * 5) The ACK is == biggest ACK sequence number so far seen (snd_una)
+     *
+     * If it passes all five, should process as a dupack:
+     * a) dupacks < 3: do nothing
+     * b) dupacks == 3: fast retransmit
+     * c) dupacks > 3: increase cwnd
+     *
+     * If it only passes 1-3, should reset dupack counter (and add to
+     * stats, which we don't do in lwIP)
+     *
+     * If it only passes 1, should reset dupack counter
+     *
+     */
+
+    /* Clause 1 */
+    if (TCP_SEQ_LEQ(ackno, pcb->lastack)) {
+      /* Clause 2 */
+      if (tcplen == 0) {
+        /* Clause 3 */
+        if (pcb->snd_wl2 + pcb->snd_wnd == right_wnd_edge) {
+          /* Clause 4 */
+          if (pcb->rtime >= 0) {
+            /* Clause 5 */
+            if (pcb->lastack == ackno) {
+              found_dupack = 1;
+              if ((u8_t)(pcb->dupacks + 1) > pcb->dupacks) {
+                ++pcb->dupacks;
+              }
+              if (pcb->dupacks > 3) {
+                /* Inflate the congestion window, but not if it means that
+                   the value overflows. */
+                if ((tcpwnd_size_t)(pcb->cwnd + pcb->mss) > pcb->cwnd) {
+                  pcb->cwnd += pcb->mss;
+                }
+              } else if (pcb->dupacks == 3) {
+                /* Do fast retransmit */
+                tcp_rexmit_fast(pcb);
+              }
+            }
+          }
+        }
+      }
+      /* If Clause (1) or more is true, but not a duplicate ack, reset
+       * count of consecutive duplicate acks */
+      if (!found_dupack) {
+        pcb->dupacks = 0;
+      }
+    } else if (TCP_SEQ_BETWEEN(ackno, pcb->lastack+1, pcb->snd_nxt)) {
+      /* 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;
+
+      /* 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 ((tcpwnd_size_t)(pcb->cwnd + pcb->mss) > pcb->cwnd) {
+            pcb->cwnd += pcb->mss;
+          }
+          LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_receive: slow start cwnd %"TCPWNDSIZE_F"\n", pcb->cwnd));
+        } else {
+          tcpwnd_size_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 %"TCPWNDSIZE_F"\n", pcb->cwnd));
+        }
+      }
+      LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: ACK for %"U32_F", unacked->seqno %"U32_F":%"U32_F"\n",
+                                    ackno,
+                                    pcb->unacked != NULL?
+                                    lwip_ntohl(pcb->unacked->tcphdr->seqno): 0,
+                                    pcb->unacked != NULL?
+                                    lwip_ntohl(pcb->unacked->tcphdr->seqno) + TCP_TCPLEN(pcb->unacked): 0));
+
+      /* Remove segment from the unacknowledged list if the incoming
+         ACK acknowledges them. */
+      while (pcb->unacked != NULL &&
+             TCP_SEQ_LEQ(lwip_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",
+                                      lwip_ntohl(pcb->unacked->tcphdr->seqno),
+                                      lwip_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 %"TCPWNDSIZE_F" ... ", (tcpwnd_size_t)pcb->snd_queuelen));
+        LWIP_ASSERT("pcb->snd_queuelen >= pbuf_clen(next->p)", (pcb->snd_queuelen >= pbuf_clen(next->p)));
+
+        pcb->snd_queuelen -= pbuf_clen(next->p);
+        recv_acked += next->len;
+        tcp_seg_free(next);
+
+        LWIP_DEBUGF(TCP_QLEN_DEBUG, ("%"TCPWNDSIZE_F" (after freeing unacked)\n", (tcpwnd_size_t)pcb->snd_queuelen));
+        if (pcb->snd_queuelen != 0) {
+          LWIP_ASSERT("tcp_receive: valid queue length", pcb->unacked != NULL ||
+                      pcb->unsent != NULL);
+        }
+      }
+
+      /* If there's nothing left to acknowledge, stop the retransmit
+         timer, otherwise reset it to start again */
+      if (pcb->unacked == NULL) {
+        pcb->rtime = -1;
+      } else {
+        pcb->rtime = 0;
+      }
+
+      pcb->polltmr = 0;
+
+#if LWIP_IPV6 && LWIP_ND6_TCP_REACHABILITY_HINTS
+      if (ip_current_is_v6()) {
+        /* Inform neighbor reachability of forward progress. */
+        nd6_reachability_hint(ip6_current_src_addr());
+      }
+#endif /* LWIP_IPV6 && LWIP_ND6_TCP_REACHABILITY_HINTS*/
+    } else {
+      /* Out of sequence ACK, didn't really ack anything */
+      tcp_send_empty_ack(pcb);
+    }
+
+    /* 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_BETWEEN(ackno, lwip_ntohl(pcb->unsent->tcphdr->seqno) +
+                           TCP_TCPLEN(pcb->unsent), pcb->snd_nxt)) {
+      LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: removing %"U32_F":%"U32_F" from pcb->unsent\n",
+                                    lwip_ntohl(pcb->unsent->tcphdr->seqno), lwip_ntohl(pcb->unsent->tcphdr->seqno) +
+                                    TCP_TCPLEN(pcb->unsent)));
+
+      next = pcb->unsent;
+      pcb->unsent = pcb->unsent->next;
+#if TCP_OVERSIZE
+      if (pcb->unsent == NULL) {
+        pcb->unsent_oversize = 0;
+      }
+#endif /* TCP_OVERSIZE */
+      LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_receive: queuelen %"TCPWNDSIZE_F" ... ", (tcpwnd_size_t)pcb->snd_queuelen));
+      LWIP_ASSERT("pcb->snd_queuelen >= pbuf_clen(next->p)", (pcb->snd_queuelen >= pbuf_clen(next->p)));
+      /* Prevent ACK for FIN to generate a sent event */
+      pcb->snd_queuelen -= pbuf_clen(next->p);
+      recv_acked += next->len;
+      tcp_seg_free(next);
+      LWIP_DEBUGF(TCP_QLEN_DEBUG, ("%"TCPWNDSIZE_F" (after freeing unsent)\n", (tcpwnd_size_t)pcb->snd_queuelen));
+      if (pcb->snd_queuelen != 0) {
+        LWIP_ASSERT("tcp_receive: valid queue length",
+          pcb->unacked != NULL || pcb->unsent != NULL);
+      }
+    }
+    pcb->snd_buf += recv_acked;
+    /* 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)) {
+      /* diff between this shouldn't exceed 32K since this are tcp timer ticks
+         and a round-trip shouldn't be that long... */
+      m = (s16_t)(tcp_ticks - pcb->rttest);
+
+      LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: experienced rtt %"U16_F" ticks (%"U16_F" msec).\n",
+                                  m, (u16_t)(m * TCP_SLOW_INTERVAL)));
+
+      /* This is taken directly from VJs original code in his paper */
+      m = 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" milliseconds)\n",
+                                  pcb->rto, (u16_t)(pcb->rto * TCP_SLOW_INTERVAL)));
+
+      pcb->rttest = 0;
+    }
+  }
+
+  /* If the incoming segment contains data, we must process it
+     further unless the pcb already received a FIN.
+     (RFC 793, chapter 3.9, "SEGMENT ARRIVES" in states CLOSE-WAIT, CLOSING,
+     LAST-ACK and TIME-WAIT: "Ignore the segment text.") */
+  if ((tcplen > 0) && (pcb->state < CLOSE_WAIT)) {
+    /* This code basically does three things:
+
+    +) If the incoming segment contains data that is the next
+    in-sequence data, this data is passed to the application. This
+    might involve trimming the first edge of the data. The rcv_nxt
+    variable and the advertised window are adjusted.
+
+    +) If the incoming segment has data that is above the next
+    sequence number expected (->rcv_nxt), the segment is placed on
+    the ->ooseq queue. This is done by finding the appropriate
+    place in the ->ooseq queue (which is ordered by sequence
+    number) and trim the segment in both ends if needed. An
+    immediate ACK is sent to indicate that we received an
+    out-of-sequence segment.
+
+    +) Finally, we check if the first segment on the ->ooseq queue
+    now is in sequence (i.e., if rcv_nxt >= ooseq->seqno). If
+    rcv_nxt > ooseq->seqno, we must trim the first edge of the
+    segment on ->ooseq before we adjust rcv_nxt. The data in the
+    segments that are now on sequence are chained onto the
+    incoming segment so that we only need to call the application
+    once.
+    */
+
+    /* First, we check if we must trim the first edge. We have to do
+       this if the sequence number of the incoming segment is less
+       than rcv_nxt, and the sequence number plus the length of the
+       segment is larger than rcv_nxt. */
+    /*    if (TCP_SEQ_LT(seqno, pcb->rcv_nxt)) {
+          if (TCP_SEQ_LT(pcb->rcv_nxt, seqno + tcplen)) {*/
+    if (TCP_SEQ_BETWEEN(pcb->rcv_nxt, seqno + 1, seqno + tcplen - 1)) {
+      /* Trimming the first edge is done by pushing the payload
+         pointer in the pbuf downwards. This is somewhat tricky since
+         we do not want to discard the full contents of the pbuf up to
+         the new starting point of the data since we have to keep the
+         TCP header which is present in the first pbuf in the chain.
+
+         What is done is really quite a nasty hack: the first pbuf in
+         the pbuf chain is pointed to by inseg.p. Since we need to be
+         able to deallocate the whole pbuf, we cannot change this
+         inseg.p pointer to point to any of the later pbufs in the
+         chain. Instead, we point the ->payload pointer in the first
+         pbuf to data in one of the later pbufs. We also set the
+         inseg.data pointer to point to the right place. This way, the
+         ->p pointer will still point to the first pbuf, but the
+         ->p->payload pointer will point to data in another pbuf.
+
+         After we are done with adjusting the pbuf pointers we must
+         adjust the ->data pointer in the seg and the segment
+         length.*/
+
+      struct pbuf *p = inseg.p;
+      off = pcb->rcv_nxt - seqno;
+      LWIP_ASSERT("inseg.p != NULL", inseg.p);
+      LWIP_ASSERT("insane offset!", (off < 0x7fff));
+      if (inseg.p->len < off) {
+        LWIP_ASSERT("pbuf too short!", (((s32_t)inseg.p->tot_len) >= off));
+        new_tot_len = (u16_t)(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;
+        }
+        if (pbuf_header(p, (s16_t)-off)) {
+          /* Do we need to cope with this failing?  Assert for now */
+          LWIP_ASSERT("pbuf_header failed", 0);
+        }
+      } else {
+        if (pbuf_header(inseg.p, (s16_t)-off)) {
+          /* Do we need to cope with this failing?  Assert for now */
+          LWIP_ASSERT("pbuf_header failed", 0);
+        }
+      }
+      inseg.len -= (u16_t)(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_BETWEEN(seqno, pcb->rcv_nxt,
+                        pcb->rcv_nxt + pcb->rcv_wnd - 1)) {
+      if (pcb->rcv_nxt == seqno) {
+        /* The incoming segment is the next in sequence. We check if
+           we have to trim the end of the segment and update rcv_nxt
+           and pass the data to the application. */
+        tcplen = TCP_TCPLEN(&inseg);
+
+        if (tcplen > pcb->rcv_wnd) {
+          LWIP_DEBUGF(TCP_INPUT_DEBUG,
+                      ("tcp_receive: other end overran receive window"
+                       "seqno %"U32_F" len %"U16_F" right edge %"U32_F"\n",
+                       seqno, tcplen, pcb->rcv_nxt + pcb->rcv_wnd));
+          if (TCPH_FLAGS(inseg.tcphdr) & TCP_FIN) {
+            /* Must remove the FIN from the header as we're trimming
+             * that byte of sequence-space from the packet */
+            TCPH_FLAGS_SET(inseg.tcphdr, TCPH_FLAGS(inseg.tcphdr) & ~(unsigned int)TCP_FIN);
+          }
+          /* Adjust length of segment to fit in the window. */
+          TCPWND_CHECK16(pcb->rcv_wnd);
+          inseg.len = (u16_t)pcb->rcv_wnd;
+          if (TCPH_FLAGS(inseg.tcphdr) & TCP_SYN) {
+            inseg.len -= 1;
+          }
+          pbuf_realloc(inseg.p, inseg.len);
+          tcplen = TCP_TCPLEN(&inseg);
+          LWIP_ASSERT("tcp_receive: segment not trimmed correctly to rcv_wnd\n",
+                      (seqno + tcplen) == (pcb->rcv_nxt + pcb->rcv_wnd));
+        }
+#if TCP_QUEUE_OOSEQ
+        /* Received in-sequence data, adjust ooseq data if:
+           - FIN has been received or
+           - inseq overlaps with ooseq */
+        if (pcb->ooseq != NULL) {
+          if (TCPH_FLAGS(inseg.tcphdr) & TCP_FIN) {
+            LWIP_DEBUGF(TCP_INPUT_DEBUG,
+                        ("tcp_receive: received in-order FIN, binning ooseq queue\n"));
+            /* Received in-order FIN means anything that was received
+             * out of order must now have been received in-order, so
+             * bin the ooseq queue */
+            while (pcb->ooseq != NULL) {
+              struct tcp_seg *old_ooseq = pcb->ooseq;
+              pcb->ooseq = pcb->ooseq->next;
+              tcp_seg_free(old_ooseq);
+            }
+          } else {
+            next = pcb->ooseq;
+            /* Remove all segments on ooseq that are covered by inseg already.
+             * FIN is copied from ooseq to inseg if present. */
+            while (next &&
+                   TCP_SEQ_GEQ(seqno + tcplen,
+                               next->tcphdr->seqno + next->len)) {
+              /* inseg cannot have FIN here (already processed above) */
+              if ((TCPH_FLAGS(next->tcphdr) & TCP_FIN) != 0 &&
+                  (TCPH_FLAGS(inseg.tcphdr) & TCP_SYN) == 0) {
+                TCPH_SET_FLAG(inseg.tcphdr, TCP_FIN);
+                tcplen = TCP_TCPLEN(&inseg);
+              }
+              prev = next;
+              next = next->next;
+              tcp_seg_free(prev);
+            }
+            /* Now trim right side of inseg if it overlaps with the first
+             * segment on ooseq */
+            if (next &&
+                TCP_SEQ_GT(seqno + tcplen,
+                           next->tcphdr->seqno)) {
+              /* inseg cannot have FIN here (already processed above) */
+              inseg.len = (u16_t)(next->tcphdr->seqno - seqno);
+              if (TCPH_FLAGS(inseg.tcphdr) & TCP_SYN) {
+                inseg.len -= 1;
+              }
+              pbuf_realloc(inseg.p, inseg.len);
+              tcplen = TCP_TCPLEN(&inseg);
+              LWIP_ASSERT("tcp_receive: segment not trimmed correctly to ooseq queue\n",
+                          (seqno + tcplen) == next->tcphdr->seqno);
+            }
+            pcb->ooseq = next;
+          }
+        }
+#endif /* TCP_QUEUE_OOSEQ */
+
+        pcb->rcv_nxt = seqno + tcplen;
+
+        /* Update the receiver's (our) window. */
+        LWIP_ASSERT("tcp_receive: tcplen > rcv_wnd\n", pcb->rcv_wnd >= tcplen);
+        pcb->rcv_wnd -= tcplen;
+
+        tcp_update_rcv_ann_wnd(pcb);
+
+        /* If there is data in the segment, we make preparations to
+           pass this up to the application. The ->recv_data variable
+           is used for holding the pbuf that goes to the
+           application. The code for reassembling out-of-sequence data
+           chains its data on this pbuf as well.
+
+           If the segment was a FIN, we set the TF_GOT_FIN flag that will
+           be used to indicate to the application that the remote side has
+           closed its end of the connection. */
+        if (inseg.p->tot_len > 0) {
+          recv_data = inseg.p;
+          /* Since this pbuf now is the responsibility of the
+             application, we delete our reference to it so that we won't
+             (mistakingly) deallocate it. */
+          inseg.p = NULL;
+        }
+        if (TCPH_FLAGS(inseg.tcphdr) & TCP_FIN) {
+          LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: received FIN.\n"));
+          recv_flags |= TF_GOT_FIN;
+        }
+
+#if TCP_QUEUE_OOSEQ
+        /* We now check if we have segments on the ->ooseq queue that
+           are now in sequence. */
+        while (pcb->ooseq != NULL &&
+               pcb->ooseq->tcphdr->seqno == pcb->rcv_nxt) {
+
+          cseg = pcb->ooseq;
+          seqno = pcb->ooseq->tcphdr->seqno;
+
+          pcb->rcv_nxt += TCP_TCPLEN(cseg);
+          LWIP_ASSERT("tcp_receive: ooseq tcplen > rcv_wnd\n",
+                      pcb->rcv_wnd >= TCP_TCPLEN(cseg));
+          pcb->rcv_wnd -= TCP_TCPLEN(cseg);
+
+          tcp_update_rcv_ann_wnd(pcb);
+
+          if (cseg->p->tot_len > 0) {
+            /* Chain this pbuf onto the pbuf that we will pass to
+               the application. */
+            /* With window scaling, this can overflow recv_data->tot_len, but
+               that's not a problem since we explicitly fix that before passing
+               recv_data to the application. */
+            if (recv_data) {
+              pbuf_cat(recv_data, cseg->p);
+            } else {
+              recv_data = cseg->p;
+            }
+            cseg->p = NULL;
+          }
+          if (TCPH_FLAGS(cseg->tcphdr) & TCP_FIN) {
+            LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: dequeued FIN.\n"));
+            recv_flags |= TF_GOT_FIN;
+            if (pcb->state == ESTABLISHED) { /* force passive close or we can move to active close */
+              pcb->state = CLOSE_WAIT;
+            }
+          }
+
+          pcb->ooseq = cseg->next;
+          tcp_seg_free(cseg);
+        }
+#endif /* TCP_QUEUE_OOSEQ */
+
+
+        /* Acknowledge the segment(s). */
+        tcp_ack(pcb);
+
+#if LWIP_IPV6 && LWIP_ND6_TCP_REACHABILITY_HINTS
+        if (ip_current_is_v6()) {
+          /* Inform neighbor reachability of forward progress. */
+          nd6_reachability_hint(ip6_current_src_addr());
+        }
+#endif /* LWIP_IPV6 && LWIP_ND6_TCP_REACHABILITY_HINTS*/
+
+      } else {
+        /* We get here if the incoming segment is out-of-sequence. */
+        tcp_send_empty_ack(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 some segments with the new
+                   one. */
+                cseg = tcp_seg_copy(&inseg);
+                if (cseg != NULL) {
+                  if (prev != NULL) {
+                    prev->next = cseg;
+                  } else {
+                    pcb->ooseq = cseg;
+                  }
+                  tcp_oos_insert_segment(cseg, next);
+                }
+                break;
+              } else {
+                /* Either the lengths are the same or the incoming
+                   segment was smaller than the old one; in either
+                   case, we ditch the incoming segment. */
+                break;
+              }
+            } else {
+              if (prev == NULL) {
+                if (TCP_SEQ_LT(seqno, next->tcphdr->seqno)) {
+                  /* The sequence number of the incoming segment is lower
+                     than the sequence number of the first segment on the
+                     queue. We put the incoming segment first on the
+                     queue. */
+                  cseg = tcp_seg_copy(&inseg);
+                  if (cseg != NULL) {
+                    pcb->ooseq = cseg;
+                    tcp_oos_insert_segment(cseg, next);
+                  }
+                  break;
+                }
+              } else {
+                /*if (TCP_SEQ_LT(prev->tcphdr->seqno, seqno) &&
+                  TCP_SEQ_LT(seqno, next->tcphdr->seqno)) {*/
+                if (TCP_SEQ_BETWEEN(seqno, prev->tcphdr->seqno+1, next->tcphdr->seqno-1)) {
+                  /* The sequence number of the incoming segment is in
+                     between the sequence numbers of the previous and
+                     the next segment on ->ooseq. We trim trim the previous
+                     segment, delete next segments that included in received segment
+                     and trim received, if needed. */
+                  cseg = tcp_seg_copy(&inseg);
+                  if (cseg != NULL) {
+                    if (TCP_SEQ_GT(prev->tcphdr->seqno + prev->len, seqno)) {
+                      /* We need to trim the prev segment. */
+                      prev->len = (u16_t)(seqno - prev->tcphdr->seqno);
+                      pbuf_realloc(prev->p, prev->len);
+                    }
+                    prev->next = cseg;
+                    tcp_oos_insert_segment(cseg, next);
+                  }
+                  break;
+                }
+              }
+              /* If the "next" segment is the last segment on the
+                 ooseq queue, we add the incoming segment to the end
+                 of the list. */
+              if (next->next == NULL &&
+                  TCP_SEQ_GT(seqno, next->tcphdr->seqno)) {
+                if (TCPH_FLAGS(next->tcphdr) & TCP_FIN) {
+                  /* segment "next" already contains all data */
+                  break;
+                }
+                next->next = tcp_seg_copy(&inseg);
+                if (next->next != NULL) {
+                  if (TCP_SEQ_GT(next->tcphdr->seqno + next->len, seqno)) {
+                    /* We need to trim the last segment. */
+                    next->len = (u16_t)(seqno - next->tcphdr->seqno);
+                    pbuf_realloc(next->p, next->len);
+                  }
+                  /* check if the remote side overruns our receive window */
+                  if (TCP_SEQ_GT((u32_t)tcplen + seqno, pcb->rcv_nxt + (u32_t)pcb->rcv_wnd)) {
+                    LWIP_DEBUGF(TCP_INPUT_DEBUG,
+                                ("tcp_receive: other end overran receive window"
+                                 "seqno %"U32_F" len %"U16_F" right edge %"U32_F"\n",
+                                 seqno, tcplen, pcb->rcv_nxt + pcb->rcv_wnd));
+                    if (TCPH_FLAGS(next->next->tcphdr) & TCP_FIN) {
+                      /* Must remove the FIN from the header as we're trimming
+                       * that byte of sequence-space from the packet */
+                      TCPH_FLAGS_SET(next->next->tcphdr, TCPH_FLAGS(next->next->tcphdr) & ~TCP_FIN);
+                    }
+                    /* Adjust length of segment to fit in the window. */
+                    next->next->len = (u16_t)(pcb->rcv_nxt + pcb->rcv_wnd - seqno);
+                    pbuf_realloc(next->next->p, next->next->len);
+                    tcplen = TCP_TCPLEN(next->next);
+                    LWIP_ASSERT("tcp_receive: segment not trimmed correctly to rcv_wnd\n",
+                                (seqno + tcplen) == (pcb->rcv_nxt + pcb->rcv_wnd));
+                  }
+                }
+                break;
+              }
+            }
+            prev = next;
+          }
+        }
+#if TCP_OOSEQ_MAX_BYTES || TCP_OOSEQ_MAX_PBUFS
+        /* Check that the data on ooseq doesn't exceed one of the limits
+           and throw away everything above that limit. */
+        ooseq_blen = 0;
+        ooseq_qlen = 0;
+        prev = NULL;
+        for (next = pcb->ooseq; next != NULL; prev = next, next = next->next) {
+          struct pbuf *p = next->p;
+          ooseq_blen += p->tot_len;
+          ooseq_qlen += pbuf_clen(p);
+          if ((ooseq_blen > TCP_OOSEQ_MAX_BYTES) ||
+              (ooseq_qlen > TCP_OOSEQ_MAX_PBUFS)) {
+             /* too much ooseq data, dump this and everything after it */
+             tcp_segs_free(next);
+             if (prev == NULL) {
+               /* first ooseq segment is too much, dump the whole queue */
+               pcb->ooseq = NULL;
+             } else {
+               /* just dump 'next' and everything after it */
+               prev->next = NULL;
+             }
+             break;
+          }
+        }
+#endif /* TCP_OOSEQ_MAX_BYTES || TCP_OOSEQ_MAX_PBUFS */
+#endif /* TCP_QUEUE_OOSEQ */
+      }
+    } else {
+      /* The incoming segment is not within the window. */
+      tcp_send_empty_ack(pcb);
+    }
+  } else {
+    /* Segments with length 0 is taken care of here. Segments that
+       fall out of the window are ACKed. */
+    if (!TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, pcb->rcv_nxt + pcb->rcv_wnd - 1)) {
+      tcp_ack_now(pcb);
+    }
+  }
+}
+
+static u8_t
+tcp_getoptbyte(void)
+{
+  if ((tcphdr_opt2 == NULL) || (tcp_optidx < tcphdr_opt1len)) {
+    u8_t* opts = (u8_t *)tcphdr + TCP_HLEN;
+    return opts[tcp_optidx++];
+  } else {
+    u8_t idx = (u8_t)(tcp_optidx++ - tcphdr_opt1len);
+    return tcphdr_opt2[idx];
+  }
+}
+
+/**
+ * Parses the options contained in the incoming segment.
+ *
+ * Called from tcp_listen_input() and tcp_process().
+ * Currently, only the MSS option is supported!
+ *
+ * @param pcb the tcp_pcb for which a segment arrived
+ */
+static void
+tcp_parseopt(struct tcp_pcb *pcb)
+{
+  u8_t data;
+  u16_t mss;
+#if LWIP_TCP_TIMESTAMPS
+  u32_t tsval;
+#endif
+
+  /* Parse the TCP MSS option, if present. */
+  if (tcphdr_optlen != 0) {
+    for (tcp_optidx = 0; tcp_optidx < tcphdr_optlen; ) {
+      u8_t opt = tcp_getoptbyte();
+      switch (opt) {
+      case LWIP_TCP_OPT_EOL:
+        /* End of options. */
+        LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: EOL\n"));
+        return;
+      case LWIP_TCP_OPT_NOP:
+        /* NOP option. */
+        LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: NOP\n"));
+        break;
+      case LWIP_TCP_OPT_MSS:
+        LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: MSS\n"));
+        if (tcp_getoptbyte() != LWIP_TCP_OPT_LEN_MSS || (tcp_optidx - 2 + LWIP_TCP_OPT_LEN_MSS) > tcphdr_optlen) {
+          /* Bad length */
+          LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n"));
+          return;
+        }
+        /* An MSS option with the right option length. */
+        mss = (tcp_getoptbyte() << 8);
+        mss |= tcp_getoptbyte();
+        /* Limit the mss to the configured TCP_MSS and prevent division by zero */
+        pcb->mss = ((mss > TCP_MSS) || (mss == 0)) ? TCP_MSS : mss;
+        break;
+#if LWIP_WND_SCALE
+      case LWIP_TCP_OPT_WS:
+        LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: WND_SCALE\n"));
+        if (tcp_getoptbyte() != LWIP_TCP_OPT_LEN_WS || (tcp_optidx - 2 + LWIP_TCP_OPT_LEN_WS) > tcphdr_optlen) {
+          /* Bad length */
+          LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n"));
+          return;
+        }
+        /* If syn was received with wnd scale option,
+           activate wnd scale opt, but only if this is not a retransmission */
+        if ((flags & TCP_SYN) && !(pcb->flags & TF_WND_SCALE)) {
+          /* An WND_SCALE option with the right option length. */
+          data = tcp_getoptbyte();
+          pcb->snd_scale = data;
+          if (pcb->snd_scale > 14U) {
+            pcb->snd_scale = 14U;
+          }
+          pcb->rcv_scale = TCP_RCV_SCALE;
+          pcb->flags |= TF_WND_SCALE;
+          /* window scaling is enabled, we can use the full receive window */
+          LWIP_ASSERT("window not at default value", pcb->rcv_wnd == TCPWND_MIN16(TCP_WND));
+          LWIP_ASSERT("window not at default value", pcb->rcv_ann_wnd == TCPWND_MIN16(TCP_WND));
+          pcb->rcv_wnd = pcb->rcv_ann_wnd = TCP_WND;
+        }
+        break;
+#endif
+#if LWIP_TCP_TIMESTAMPS
+      case LWIP_TCP_OPT_TS:
+        LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: TS\n"));
+        if (tcp_getoptbyte() != LWIP_TCP_OPT_LEN_TS || (tcp_optidx - 2 + LWIP_TCP_OPT_LEN_TS) > tcphdr_optlen) {
+          /* Bad length */
+          LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n"));
+          return;
+        }
+        /* TCP timestamp option with valid length */
+        tsval = tcp_getoptbyte();
+        tsval |= (tcp_getoptbyte() << 8);
+        tsval |= (tcp_getoptbyte() << 16);
+        tsval |= (tcp_getoptbyte() << 24);
+        if (flags & TCP_SYN) {
+          pcb->ts_recent = lwip_ntohl(tsval);
+          /* Enable sending timestamps in every segment now that we know
+             the remote host supports it. */
+          pcb->flags |= TF_TIMESTAMP;
+        } else if (TCP_SEQ_BETWEEN(pcb->ts_lastacksent, seqno, seqno+tcplen)) {
+          pcb->ts_recent = lwip_ntohl(tsval);
+        }
+        /* Advance to next option (6 bytes already read) */
+        tcp_optidx += LWIP_TCP_OPT_LEN_TS - 6;
+        break;
+#endif
+      default:
+        LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: other\n"));
+        data = tcp_getoptbyte();
+        if (data < 2) {
+          LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n"));
+          /* If the length field is zero, the options are malformed
+             and we don't process them further. */
+          return;
+        }
+        /* All other options have a length field, so that we easily
+           can skip past them. */
+        tcp_optidx += data - 2;
+      }
+    }
+  }
+}
+
+void
+tcp_trigger_input_pcb_close(void)
+{
+  recv_flags |= TF_CLOSED;
+}
+
+#endif /* LWIP_TCP */

+ 1671 - 1485
thirdparty/lwip/src/core/tcp_out.c

@@ -1,1485 +1,1671 @@
-/**
- * @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 "lwip/opt.h"
-
-#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */
-
-#include "lwip/tcp_impl.h"
-#include "lwip/def.h"
-#include "lwip/mem.h"
-#include "lwip/memp.h"
-#include "lwip/ip_addr.h"
-#include "lwip/netif.h"
-#include "lwip/inet_chksum.h"
-#include "lwip/stats.h"
-#include "lwip/snmp.h"
-#if LWIP_TCP_TIMESTAMPS
-#include "lwip/sys.h"
-#endif
-
-#include <string.h>
-
-/* Define some copy-macros for checksum-on-copy so that the code looks
-   nicer by preventing too many ifdef's. */
-#if TCP_CHECKSUM_ON_COPY
-#define TCP_DATA_COPY(dst, src, len, seg) do { \
-  tcp_seg_add_chksum(LWIP_CHKSUM_COPY(dst, src, len), \
-                     len, &seg->chksum, &seg->chksum_swapped); \
-  seg->flags |= TF_SEG_DATA_CHECKSUMMED; } while(0)
-#define TCP_DATA_COPY2(dst, src, len, chksum, chksum_swapped)  \
-  tcp_seg_add_chksum(LWIP_CHKSUM_COPY(dst, src, len), len, chksum, chksum_swapped);
-#else /* TCP_CHECKSUM_ON_COPY*/
-#define TCP_DATA_COPY(dst, src, len, seg)                     MEMCPY(dst, src, len)
-#define TCP_DATA_COPY2(dst, src, len, chksum, chksum_swapped) MEMCPY(dst, src, len)
-#endif /* TCP_CHECKSUM_ON_COPY*/
-
-/** Define this to 1 for an extra check that the output checksum is valid
- * (usefule when the checksum is generated by the application, not the stack) */
-#ifndef TCP_CHECKSUM_ON_COPY_SANITY_CHECK
-#define TCP_CHECKSUM_ON_COPY_SANITY_CHECK   0
-#endif
-
-/* Forward declarations.*/
-static void tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb);
-
-/** Allocate a pbuf and create a tcphdr at p->payload, used for output
- * functions other than the default tcp_output -> tcp_output_segment
- * (e.g. tcp_send_empty_ack, etc.)
- *
- * @param pcb tcp pcb for which to send a packet (used to initialize tcp_hdr)
- * @param optlen length of header-options
- * @param datalen length of tcp data to reserve in pbuf
- * @param seqno_be seqno in network byte order (big-endian)
- * @return pbuf with p->payload being the tcp_hdr
- */
-static struct pbuf *
-tcp_output_alloc_header(struct tcp_pcb *pcb, u16_t optlen, u16_t datalen,
-                      u32_t seqno_be /* already in network byte order */)
-{
-  struct tcp_hdr *tcphdr;
-  struct pbuf *p = pbuf_alloc(PBUF_IP, TCP_HLEN + optlen + datalen, PBUF_RAM);
-  if (p != NULL) {
-    LWIP_ASSERT("check that first pbuf can hold struct tcp_hdr",
-                 (p->len >= TCP_HLEN + optlen));
-    tcphdr = (struct tcp_hdr *)p->payload;
-    tcphdr->src = htons(pcb->local_port);
-    tcphdr->dest = htons(pcb->remote_port);
-    tcphdr->seqno = seqno_be;
-    tcphdr->ackno = htonl(pcb->rcv_nxt);
-    TCPH_HDRLEN_FLAGS_SET(tcphdr, (5 + optlen / 4), TCP_ACK);
-    tcphdr->wnd = htons(pcb->rcv_ann_wnd);
-    tcphdr->chksum = 0;
-    tcphdr->urgp = 0;
-
-    /* If we're sending a packet, update the announced right window edge */
-    pcb->rcv_ann_right_edge = pcb->rcv_nxt + pcb->rcv_ann_wnd;
-  }
-  return p;
-}
-
-/**
- * Called by tcp_close() to send a segment including FIN flag but not data.
- *
- * @param pcb the tcp_pcb over which to send a segment
- * @return ERR_OK if sent, another err_t otherwise
- */
-err_t
-tcp_send_fin(struct tcp_pcb *pcb)
-{
-  /* first, try to add the fin to the last unsent segment */
-  if (pcb->unsent != NULL) {
-    struct tcp_seg *last_unsent;
-    for (last_unsent = pcb->unsent; last_unsent->next != NULL;
-         last_unsent = last_unsent->next);
-
-    if ((TCPH_FLAGS(last_unsent->tcphdr) & (TCP_SYN | TCP_FIN | TCP_RST)) == 0) {
-      /* no SYN/FIN/RST flag in the header, we can add the FIN flag */
-      TCPH_SET_FLAG(last_unsent->tcphdr, TCP_FIN);
-      pcb->flags |= TF_FIN;
-      return ERR_OK;
-    }
-  }
-  /* no data, no length, flags, copy=1, no optdata */
-  return tcp_enqueue_flags(pcb, TCP_FIN);
-}
-
-/**
- * Create a TCP segment with prefilled header.
- *
- * Called by tcp_write and tcp_enqueue_flags.
- *
- * @param pcb Protocol control block for the TCP connection.
- * @param p pbuf that is used to hold the TCP header.
- * @param flags TCP flags for header.
- * @param seqno TCP sequence number of this packet
- * @param optflags options to include in TCP header
- * @return a new tcp_seg pointing to p, or NULL.
- * The TCP header is filled in except ackno and wnd.
- * p is freed on failure.
- */
-static struct tcp_seg *
-tcp_create_segment(struct tcp_pcb *pcb, struct pbuf *p, u8_t flags, u32_t seqno, u8_t optflags)
-{
-  struct tcp_seg *seg;
-  u8_t optlen = LWIP_TCP_OPT_LENGTH(optflags);
-
-  if ((seg = (struct tcp_seg *)memp_malloc(MEMP_TCP_SEG)) == NULL) {
-    LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_create_segment: no memory.\n"));
-    pbuf_free(p);
-    return NULL;
-  }
-  seg->flags = optflags;
-  seg->next = NULL;
-  seg->p = p;
-  seg->len = p->tot_len - optlen;
-#if TCP_OVERSIZE_DBGCHECK
-  seg->oversize_left = 0;
-#endif /* TCP_OVERSIZE_DBGCHECK */
-#if TCP_CHECKSUM_ON_COPY
-  seg->chksum = 0;
-  seg->chksum_swapped = 0;
-  /* check optflags */
-  LWIP_ASSERT("invalid optflags passed: TF_SEG_DATA_CHECKSUMMED",
-              (optflags & TF_SEG_DATA_CHECKSUMMED) == 0);
-#endif /* TCP_CHECKSUM_ON_COPY */
-
-  /* build TCP header */
-  if (pbuf_header(p, TCP_HLEN)) {
-    LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_create_segment: no room for TCP header in pbuf.\n"));
-    TCP_STATS_INC(tcp.err);
-    tcp_seg_free(seg);
-    return NULL;
-  }
-  seg->tcphdr = (struct tcp_hdr *)seg->p->payload;
-  seg->tcphdr->src = htons(pcb->local_port);
-  seg->tcphdr->dest = htons(pcb->remote_port);
-  seg->tcphdr->seqno = htonl(seqno);
-  /* ackno is set in tcp_output */
-  TCPH_HDRLEN_FLAGS_SET(seg->tcphdr, (5 + optlen / 4), flags);
-  /* wnd and chksum are set in tcp_output */
-  seg->tcphdr->urgp = 0;
-  return seg;
-} 
-
-/**
- * Allocate a PBUF_RAM pbuf, perhaps with extra space at the end.
- *
- * This function is like pbuf_alloc(layer, length, PBUF_RAM) except
- * there may be extra bytes available at the end.
- *
- * @param layer flag to define header size.
- * @param length size of the pbuf's payload.
- * @param max_length maximum usable size of payload+oversize.
- * @param oversize pointer to a u16_t that will receive the number of usable tail bytes.
- * @param pcb The TCP connection that willo enqueue the pbuf.
- * @param apiflags API flags given to tcp_write.
- * @param first_seg true when this pbuf will be used in the first enqueued segment.
- * @param 
- */
-#if TCP_OVERSIZE
-static struct pbuf *
-tcp_pbuf_prealloc(pbuf_layer layer, u16_t length, u16_t max_length,
-                  u16_t *oversize, struct tcp_pcb *pcb, u8_t apiflags,
-                  u8_t first_seg)
-{
-  struct pbuf *p;
-  u16_t alloc = length;
-
-#if LWIP_NETIF_TX_SINGLE_PBUF
-  LWIP_UNUSED_ARG(max_length);
-  LWIP_UNUSED_ARG(pcb);
-  LWIP_UNUSED_ARG(apiflags);
-  LWIP_UNUSED_ARG(first_seg);
-  /* always create MSS-sized pbufs */
-  alloc = max_length;
-#else /* LWIP_NETIF_TX_SINGLE_PBUF */
-  if (length < max_length) {
-    /* Should we allocate an oversized pbuf, or just the minimum
-     * length required? If tcp_write is going to be called again
-     * before this segment is transmitted, we want the oversized
-     * buffer. If the segment will be transmitted immediately, we can
-     * save memory by allocating only length. We use a simple
-     * heuristic based on the following information:
-     *
-     * Did the user set TCP_WRITE_FLAG_MORE?
-     *
-     * Will the Nagle algorithm defer transmission of this segment?
-     */
-    if ((apiflags & TCP_WRITE_FLAG_MORE) ||
-        (!(pcb->flags & TF_NODELAY) &&
-         (!first_seg ||
-          pcb->unsent != NULL ||
-          pcb->unacked != NULL))) {
-      alloc = LWIP_MIN(max_length, LWIP_MEM_ALIGN_SIZE(length + TCP_OVERSIZE));
-    }
-  }
-#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
-  p = pbuf_alloc(layer, alloc, PBUF_RAM);
-  if (p == NULL) {
-    return NULL;
-  }
-  LWIP_ASSERT("need unchained pbuf", p->next == NULL);
-  *oversize = p->len - length;
-  /* trim p->len to the currently used size */
-  p->len = p->tot_len = length;
-  return p;
-}
-#else /* TCP_OVERSIZE */
-#define tcp_pbuf_prealloc(layer, length, mx, os, pcb, api, fst) pbuf_alloc((layer), (length), PBUF_RAM)
-#endif /* TCP_OVERSIZE */
-
-#if TCP_CHECKSUM_ON_COPY
-/** Add a checksum of newly added data to the segment */
-static void
-tcp_seg_add_chksum(u16_t chksum, u16_t len, u16_t *seg_chksum,
-                   u8_t *seg_chksum_swapped)
-{
-  u32_t helper;
-  /* add chksum to old chksum and fold to u16_t */
-  helper = chksum + *seg_chksum;
-  chksum = FOLD_U32T(helper);
-  if ((len & 1) != 0) {
-    *seg_chksum_swapped = 1 - *seg_chksum_swapped;
-    chksum = SWAP_BYTES_IN_WORD(chksum);
-  }
-  *seg_chksum = chksum;
-}
-#endif /* TCP_CHECKSUM_ON_COPY */
-
-/** Checks if tcp_write is allowed or not (checks state, snd_buf and snd_queuelen).
- *
- * @param pcb the tcp pcb to check for
- * @param len length of data to send (checked agains snd_buf)
- * @return ERR_OK if tcp_write is allowed to proceed, another err_t otherwise
- */
-static err_t
-tcp_write_checks(struct tcp_pcb *pcb, u16_t len)
-{
-  /* connection is in invalid state for data transmission? */
-  if ((pcb->state != ESTABLISHED) &&
-      (pcb->state != CLOSE_WAIT) &&
-      (pcb->state != SYN_SENT) &&
-      (pcb->state != SYN_RCVD)) {
-    LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_STATE | LWIP_DBG_LEVEL_SEVERE, ("tcp_write() called in invalid state\n"));
-    return ERR_CONN;
-  } else if (len == 0) {
-    return ERR_OK;
-  }
-
-  /* fail on too much data */
-  if (len > pcb->snd_buf) {
-    LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_write: too much data (len=%"U16_F" > snd_buf=%"U16_F")\n",
-      len, pcb->snd_buf));
-    pcb->flags |= TF_NAGLEMEMERR;
-    return ERR_MEM;
-  }
-
-  LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_write: 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 */
-  /* check for configured max queuelen and possible overflow */
-  if ((pcb->snd_queuelen >= TCP_SND_QUEUELEN) || (pcb->snd_queuelen > TCP_SNDQUEUELEN_OVERFLOW)) {
-    LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_write: too long queue %"U16_F" (max %"U16_F")\n",
-      pcb->snd_queuelen, TCP_SND_QUEUELEN));
-    TCP_STATS_INC(tcp.memerr);
-    pcb->flags |= TF_NAGLEMEMERR;
-    return ERR_MEM;
-  }
-  if (pcb->snd_queuelen != 0) {
-    LWIP_ASSERT("tcp_write: pbufs on queue => at least one queue non-empty",
-      pcb->unacked != NULL || pcb->unsent != NULL);
-  } else {
-    LWIP_ASSERT("tcp_write: no pbufs on queue => both queues empty",
-      pcb->unacked == NULL && pcb->unsent == NULL);
-  }
-  return ERR_OK;
-}
-
-/**
- * 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().
- *
- * @param pcb Protocol control block for the TCP connection to enqueue data for.
- * @param arg Pointer to the data to be enqueued for sending.
- * @param len Data length in bytes
- * @param apiflags combination of following flags :
- * - TCP_WRITE_FLAG_COPY (0x01) data will be copied into memory belonging to the stack
- * - TCP_WRITE_FLAG_MORE (0x02) for TCP connection, PSH flag will be set on last segment sent,
- * @return ERR_OK if enqueued, another err_t on error
- */
-err_t
-tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags)
-{
-  struct pbuf *concat_p = NULL;
-  struct tcp_seg *last_unsent = NULL, *seg = NULL, *prev_seg = NULL, *queue = NULL;
-  u16_t pos = 0; /* position in 'arg' data */
-  u16_t queuelen;
-  u8_t optlen = 0;
-  u8_t optflags = 0;
-#if TCP_OVERSIZE
-  u16_t oversize = 0;
-  u16_t oversize_used = 0;
-#endif /* TCP_OVERSIZE */
-#if TCP_CHECKSUM_ON_COPY
-  u16_t concat_chksum = 0;
-  u8_t concat_chksum_swapped = 0;
-  u16_t concat_chksummed = 0;
-#endif /* TCP_CHECKSUM_ON_COPY */
-  err_t err;
-  /* don't allocate segments bigger than half the maximum window we ever received */
-  u16_t mss_local = LWIP_MIN(pcb->mss, pcb->snd_wnd_max/2);
-
-#if LWIP_NETIF_TX_SINGLE_PBUF
-  /* Always copy to try to create single pbufs for TX */
-  apiflags |= TCP_WRITE_FLAG_COPY;
-#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
-
-  LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_write(pcb=%p, data=%p, len=%"U16_F", apiflags=%"U16_F")\n",
-    (void *)pcb, arg, len, (u16_t)apiflags));
-  LWIP_ERROR("tcp_write: arg == NULL (programmer violates API)", 
-             arg != NULL, return ERR_ARG;);
-
-  err = tcp_write_checks(pcb, len);
-  if (err != ERR_OK) {
-    return err;
-  }
-  queuelen = pcb->snd_queuelen;
-
-#if LWIP_TCP_TIMESTAMPS
-  if ((pcb->flags & TF_TIMESTAMP)) {
-    optflags = TF_SEG_OPTS_TS;
-    optlen = LWIP_TCP_OPT_LENGTH(TF_SEG_OPTS_TS);
-  }
-#endif /* LWIP_TCP_TIMESTAMPS */
-
-
-  /*
-   * TCP segmentation is done in three phases with increasing complexity:
-   *
-   * 1. Copy data directly into an oversized pbuf.
-   * 2. Chain a new pbuf to the end of pcb->unsent.
-   * 3. Create new segments.
-   *
-   * We may run out of memory at any point. In that case we must
-   * return ERR_MEM and not change anything in pcb. Therefore, all
-   * changes are recorded in local variables and committed at the end
-   * of the function. Some pcb fields are maintained in local copies:
-   *
-   * queuelen = pcb->snd_queuelen
-   * oversize = pcb->unsent_oversize
-   *
-   * These variables are set consistently by the phases:
-   *
-   * seg points to the last segment tampered with.
-   *
-   * pos records progress as data is segmented.
-   */
-
-  /* Find the tail of the unsent queue. */
-  if (pcb->unsent != NULL) {
-    u16_t space;
-    u16_t unsent_optlen;
-
-    /* @todo: this could be sped up by keeping last_unsent in the pcb */
-    for (last_unsent = pcb->unsent; last_unsent->next != NULL;
-         last_unsent = last_unsent->next);
-
-    /* Usable space at the end of the last unsent segment */
-    unsent_optlen = LWIP_TCP_OPT_LENGTH(last_unsent->flags);
-    space = mss_local - (last_unsent->len + unsent_optlen);
-
-    /*
-     * Phase 1: Copy data directly into an oversized pbuf.
-     *
-     * The number of bytes copied is recorded in the oversize_used
-     * variable. The actual copying is done at the bottom of the
-     * function.
-     */
-#if TCP_OVERSIZE
-#if TCP_OVERSIZE_DBGCHECK
-    /* check that pcb->unsent_oversize matches last_unsent->unsent_oversize */
-    LWIP_ASSERT("unsent_oversize mismatch (pcb vs. last_unsent)",
-                pcb->unsent_oversize == last_unsent->oversize_left);
-#endif /* TCP_OVERSIZE_DBGCHECK */
-    oversize = pcb->unsent_oversize;
-    if (oversize > 0) {
-      LWIP_ASSERT("inconsistent oversize vs. space", oversize_used <= space);
-      seg = last_unsent;
-      oversize_used = oversize < len ? oversize : len;
-      pos += oversize_used;
-      oversize -= oversize_used;
-      space -= oversize_used;
-    }
-    /* now we are either finished or oversize is zero */
-    LWIP_ASSERT("inconsistend oversize vs. len", (oversize == 0) || (pos == len));
-#endif /* TCP_OVERSIZE */
-
-    /*
-     * Phase 2: Chain a new pbuf to the end of pcb->unsent.
-     *
-     * We don't extend segments containing SYN/FIN flags or options
-     * (len==0). The new pbuf is kept in concat_p and pbuf_cat'ed at
-     * the end.
-     */
-    if ((pos < len) && (space > 0) && (last_unsent->len > 0)) {
-      u16_t seglen = space < len - pos ? space : len - pos;
-      seg = last_unsent;
-
-      /* Create a pbuf with a copy or reference to seglen bytes. We
-       * can use PBUF_RAW here since the data appears in the middle of
-       * a segment. A header will never be prepended. */
-      if (apiflags & TCP_WRITE_FLAG_COPY) {
-        /* Data is copied */
-        if ((concat_p = tcp_pbuf_prealloc(PBUF_RAW, seglen, space, &oversize, pcb, apiflags, 1)) == NULL) {
-          LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2,
-                      ("tcp_write : could not allocate memory for pbuf copy size %"U16_F"\n",
-                       seglen));
-          goto memerr;
-        }
-#if TCP_OVERSIZE_DBGCHECK
-        last_unsent->oversize_left += oversize;
-#endif /* TCP_OVERSIZE_DBGCHECK */
-        TCP_DATA_COPY2(concat_p->payload, (u8_t*)arg + pos, seglen, &concat_chksum, &concat_chksum_swapped);
-#if TCP_CHECKSUM_ON_COPY
-        concat_chksummed += seglen;
-#endif /* TCP_CHECKSUM_ON_COPY */
-      } else {
-        /* Data is not copied */
-        if ((concat_p = pbuf_alloc(PBUF_RAW, seglen, PBUF_ROM)) == NULL) {
-          LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2,
-                      ("tcp_write: could not allocate memory for zero-copy pbuf\n"));
-          goto memerr;
-        }
-#if TCP_CHECKSUM_ON_COPY
-        /* calculate the checksum of nocopy-data */
-        tcp_seg_add_chksum(~inet_chksum((u8_t*)arg + pos, seglen), seglen,
-          &concat_chksum, &concat_chksum_swapped);
-        concat_chksummed += seglen;
-#endif /* TCP_CHECKSUM_ON_COPY */
-        /* reference the non-volatile payload data */
-        concat_p->payload = (u8_t*)arg + pos;
-      }
-
-      pos += seglen;
-      queuelen += pbuf_clen(concat_p);
-    }
-  } else {
-#if TCP_OVERSIZE
-    LWIP_ASSERT("unsent_oversize mismatch (pcb->unsent is NULL)",
-                pcb->unsent_oversize == 0);
-#endif /* TCP_OVERSIZE */
-  }
-
-  /*
-   * Phase 3: Create new segments.
-   *
-   * The new segments are chained together in the local 'queue'
-   * variable, ready to be appended to pcb->unsent.
-   */
-  while (pos < len) {
-    struct pbuf *p;
-    u16_t left = len - pos;
-    u16_t max_len = mss_local - optlen;
-    u16_t seglen = left > max_len ? max_len : left;
-#if TCP_CHECKSUM_ON_COPY
-    u16_t chksum = 0;
-    u8_t chksum_swapped = 0;
-#endif /* TCP_CHECKSUM_ON_COPY */
-
-    if (apiflags & TCP_WRITE_FLAG_COPY) {
-      /* If copy is set, memory should be allocated and data copied
-       * into pbuf */
-      if ((p = tcp_pbuf_prealloc(PBUF_TRANSPORT, seglen + optlen, mss_local, &oversize, pcb, apiflags, queue == NULL)) == NULL) {
-        LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_write : could not allocate memory for pbuf copy size %"U16_F"\n", seglen));
-        goto memerr;
-      }
-      LWIP_ASSERT("tcp_write: check that first pbuf can hold the complete seglen",
-                  (p->len >= seglen));
-      TCP_DATA_COPY2((char *)p->payload + optlen, (u8_t*)arg + pos, seglen, &chksum, &chksum_swapped);
-    } else {
-      /* Copy is not set: First allocate a pbuf for holding the data.
-       * Since the referenced data is available at least until it is
-       * sent out on the link (as it has to be ACKed by the remote
-       * party) we can safely use PBUF_ROM instead of PBUF_REF here.
-       */
-      struct pbuf *p2;
-#if TCP_OVERSIZE
-      LWIP_ASSERT("oversize == 0", oversize == 0);
-#endif /* TCP_OVERSIZE */
-      if ((p2 = pbuf_alloc(PBUF_TRANSPORT, seglen, PBUF_ROM)) == NULL) {
-        LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_write: could not allocate memory for zero-copy pbuf\n"));
-        goto memerr;
-      }
-#if TCP_CHECKSUM_ON_COPY
-      /* calculate the checksum of nocopy-data */
-      chksum = ~inet_chksum((u8_t*)arg + pos, seglen);
-#endif /* TCP_CHECKSUM_ON_COPY */
-      /* reference the non-volatile payload data */
-      p2->payload = (u8_t*)arg + pos;
-
-      /* Second, allocate a pbuf for the headers. */
-      if ((p = pbuf_alloc(PBUF_TRANSPORT, optlen, PBUF_RAM)) == NULL) {
-        /* If allocation fails, we have to deallocate the data pbuf as
-         * well. */
-        pbuf_free(p2);
-        LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_write: could not allocate memory for header pbuf\n"));
-        goto memerr;
-      }
-      /* Concatenate the headers and data pbufs together. */
-      pbuf_cat(p/*header*/, p2/*data*/);
-    }
-
-    queuelen += pbuf_clen(p);
-
-    /* Now that there are more segments queued, we check again if the
-     * length of the queue exceeds the configured maximum or
-     * overflows. */
-    if ((queuelen > TCP_SND_QUEUELEN) || (queuelen > TCP_SNDQUEUELEN_OVERFLOW)) {
-      LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_write: queue too long %"U16_F" (%"U16_F")\n", queuelen, TCP_SND_QUEUELEN));
-      pbuf_free(p);
-      goto memerr;
-    }
-
-    if ((seg = tcp_create_segment(pcb, p, 0, pcb->snd_lbb + pos, optflags)) == NULL) {
-      goto memerr;
-    }
-#if TCP_OVERSIZE_DBGCHECK
-    seg->oversize_left = oversize;
-#endif /* TCP_OVERSIZE_DBGCHECK */
-#if TCP_CHECKSUM_ON_COPY
-    seg->chksum = chksum;
-    seg->chksum_swapped = chksum_swapped;
-    seg->flags |= TF_SEG_DATA_CHECKSUMMED;
-#endif /* TCP_CHECKSUM_ON_COPY */
-
-    /* first segment of to-be-queued data? */
-    if (queue == NULL) {
-      queue = seg;
-    } else {
-      /* Attach the segment to the end of the queued segments */
-      LWIP_ASSERT("prev_seg != NULL", prev_seg != NULL);
-      prev_seg->next = seg;
-    }
-    /* remember last segment of to-be-queued data for next iteration */
-    prev_seg = seg;
-
-    LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_TRACE, ("tcp_write: queueing %"U32_F":%"U32_F"\n",
-      ntohl(seg->tcphdr->seqno),
-      ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg)));
-
-    pos += seglen;
-  }
-
-  /*
-   * All three segmentation phases were successful. We can commit the
-   * transaction.
-   */
-
-  /*
-   * Phase 1: If data has been added to the preallocated tail of
-   * last_unsent, we update the length fields of the pbuf chain.
-   */
-#if TCP_OVERSIZE
-  if (oversize_used > 0) {
-    struct pbuf *p;
-    /* Bump tot_len of whole chain, len of tail */
-    for (p = last_unsent->p; p; p = p->next) {
-      p->tot_len += oversize_used;
-      if (p->next == NULL) {
-        TCP_DATA_COPY((char *)p->payload + p->len, arg, oversize_used, last_unsent);
-        p->len += oversize_used;
-      }
-    }
-    last_unsent->len += oversize_used;
-#if TCP_OVERSIZE_DBGCHECK
-    LWIP_ASSERT("last_unsent->oversize_left >= oversize_used",
-                last_unsent->oversize_left >= oversize_used);
-    last_unsent->oversize_left -= oversize_used;
-#endif /* TCP_OVERSIZE_DBGCHECK */
-  }
-  pcb->unsent_oversize = oversize;
-#endif /* TCP_OVERSIZE */
-
-  /*
-   * Phase 2: concat_p can be concatenated onto last_unsent->p
-   */
-  if (concat_p != NULL) {
-    LWIP_ASSERT("tcp_write: cannot concatenate when pcb->unsent is empty",
-      (last_unsent != NULL));
-    pbuf_cat(last_unsent->p, concat_p);
-    last_unsent->len += concat_p->tot_len;
-#if TCP_CHECKSUM_ON_COPY
-    if (concat_chksummed) {
-      tcp_seg_add_chksum(concat_chksum, concat_chksummed, &last_unsent->chksum,
-        &last_unsent->chksum_swapped);
-      last_unsent->flags |= TF_SEG_DATA_CHECKSUMMED;
-    }
-#endif /* TCP_CHECKSUM_ON_COPY */
-  }
-
-  /*
-   * Phase 3: Append queue to pcb->unsent. Queue may be NULL, but that
-   * is harmless
-   */
-  if (last_unsent == NULL) {
-    pcb->unsent = queue;
-  } else {
-    last_unsent->next = queue;
-  }
-
-  /*
-   * Finally update the pcb state.
-   */
-  pcb->snd_lbb += len;
-  pcb->snd_buf -= len;
-  pcb->snd_queuelen = queuelen;
-
-  LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_write: %"S16_F" (after enqueued)\n",
-    pcb->snd_queuelen));
-  if (pcb->snd_queuelen != 0) {
-    LWIP_ASSERT("tcp_write: valid queue length",
-                pcb->unacked != NULL || pcb->unsent != NULL);
-  }
-
-  /* Set the PSH flag in the last segment that we enqueued. */
-  if (seg != NULL && seg->tcphdr != NULL && ((apiflags & TCP_WRITE_FLAG_MORE)==0)) {
-    TCPH_SET_FLAG(seg->tcphdr, TCP_PSH);
-  }
-
-  return ERR_OK;
-memerr:
-  pcb->flags |= TF_NAGLEMEMERR;
-  TCP_STATS_INC(tcp.memerr);
-
-  if (concat_p != NULL) {
-    pbuf_free(concat_p);
-  }
-  if (queue != NULL) {
-    tcp_segs_free(queue);
-  }
-  if (pcb->snd_queuelen != 0) {
-    LWIP_ASSERT("tcp_write: valid queue length", pcb->unacked != NULL ||
-      pcb->unsent != NULL);
-  }
-  LWIP_DEBUGF(TCP_QLEN_DEBUG | LWIP_DBG_STATE, ("tcp_write: %"S16_F" (with mem err)\n", pcb->snd_queuelen));
-  return ERR_MEM;
-}
-
-/**
- * Enqueue TCP options for transmission.
- *
- * Called by tcp_connect(), tcp_listen_input(), and tcp_send_ctrl().
- *
- * @param pcb Protocol control block for the TCP connection.
- * @param flags TCP header flags to set in the outgoing segment.
- * @param optdata pointer to TCP options, or NULL.
- * @param optlen length of TCP options in bytes.
- */
-err_t
-tcp_enqueue_flags(struct tcp_pcb *pcb, u8_t flags)
-{
-  struct pbuf *p;
-  struct tcp_seg *seg;
-  u8_t optflags = 0;
-  u8_t optlen = 0;
-
-  LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue_flags: queuelen: %"U16_F"\n", (u16_t)pcb->snd_queuelen));
-
-  LWIP_ASSERT("tcp_enqueue_flags: need either TCP_SYN or TCP_FIN in flags (programmer violates API)",
-              (flags & (TCP_SYN | TCP_FIN)) != 0);
-
-  /* check for configured max queuelen and possible overflow */
-  if ((pcb->snd_queuelen >= TCP_SND_QUEUELEN) || (pcb->snd_queuelen > TCP_SNDQUEUELEN_OVERFLOW)) {
-    LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_enqueue_flags: too long queue %"U16_F" (max %"U16_F")\n",
-                                       pcb->snd_queuelen, TCP_SND_QUEUELEN));
-    TCP_STATS_INC(tcp.memerr);
-    pcb->flags |= TF_NAGLEMEMERR;
-    return ERR_MEM;
-  }
-
-  if (flags & TCP_SYN) {
-    optflags = TF_SEG_OPTS_MSS;
-  }
-#if LWIP_TCP_TIMESTAMPS
-  if ((pcb->flags & TF_TIMESTAMP)) {
-    optflags |= TF_SEG_OPTS_TS;
-  }
-#endif /* LWIP_TCP_TIMESTAMPS */
-  optlen = LWIP_TCP_OPT_LENGTH(optflags);
-
-  /* tcp_enqueue_flags is always called with either SYN or FIN in flags.
-   * We need one available snd_buf byte to do that.
-   * This means we can't send FIN while snd_buf==0. A better fix would be to
-   * not include SYN and FIN sequence numbers in the snd_buf count. */
-  if (pcb->snd_buf == 0) {
-    LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_enqueue_flags: no send buffer available\n"));
-    TCP_STATS_INC(tcp.memerr);
-    return ERR_MEM;
-  }
-
-  /* Allocate pbuf with room for TCP header + options */
-  if ((p = pbuf_alloc(PBUF_TRANSPORT, optlen, PBUF_RAM)) == NULL) {
-    pcb->flags |= TF_NAGLEMEMERR;
-    TCP_STATS_INC(tcp.memerr);
-    return ERR_MEM;
-  }
-  LWIP_ASSERT("tcp_enqueue_flags: check that first pbuf can hold optlen",
-              (p->len >= optlen));
-
-  /* Allocate memory for tcp_seg, and fill in fields. */
-  if ((seg = tcp_create_segment(pcb, p, flags, pcb->snd_lbb, optflags)) == NULL) {
-    pcb->flags |= TF_NAGLEMEMERR;
-    TCP_STATS_INC(tcp.memerr);
-    return ERR_MEM;
-  }
-  LWIP_ASSERT("seg->tcphdr not aligned", ((mem_ptr_t)seg->tcphdr % MEM_ALIGNMENT) == 0);
-  LWIP_ASSERT("tcp_enqueue_flags: invalid segment length", seg->len == 0);
-
-  LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_TRACE,
-              ("tcp_enqueue_flags: queueing %"U32_F":%"U32_F" (0x%"X16_F")\n",
-               ntohl(seg->tcphdr->seqno),
-               ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg),
-               (u16_t)flags));
-
-  /* Now append seg to pcb->unsent queue */
-  if (pcb->unsent == NULL) {
-    pcb->unsent = seg;
-  } else {
-    struct tcp_seg *useg;
-    for (useg = pcb->unsent; useg->next != NULL; useg = useg->next);
-    useg->next = seg;
-  }
-#if TCP_OVERSIZE
-  /* The new unsent tail has no space */
-  pcb->unsent_oversize = 0;
-#endif /* TCP_OVERSIZE */
-
-  /* SYN and FIN bump the sequence number */
-  if ((flags & TCP_SYN) || (flags & TCP_FIN)) {
-    pcb->snd_lbb++;
-    /* optlen does not influence snd_buf */
-    pcb->snd_buf--;
-  }
-  if (flags & TCP_FIN) {
-    pcb->flags |= TF_FIN;
-  }
-
-  /* update number of segments on the queues */
-  pcb->snd_queuelen += pbuf_clen(seg->p);
-  LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue_flags: %"S16_F" (after enqueued)\n", pcb->snd_queuelen));
-  if (pcb->snd_queuelen != 0) {
-    LWIP_ASSERT("tcp_enqueue_flags: invalid queue length",
-      pcb->unacked != NULL || pcb->unsent != NULL);
-  }
-
-  return ERR_OK;
-}
-
-#if LWIP_TCP_TIMESTAMPS
-/* Build a timestamp option (12 bytes long) at the specified options pointer)
- *
- * @param pcb tcp_pcb
- * @param opts option pointer where to store the timestamp option
- */
-static void
-tcp_build_timestamp_option(struct tcp_pcb *pcb, u32_t *opts)
-{
-  /* Pad with two NOP options to make everything nicely aligned */
-  opts[0] = PP_HTONL(0x0101080A);
-  opts[1] = htonl(sys_now());
-  opts[2] = htonl(pcb->ts_recent);
-}
-#endif
-
-/** Send an ACK without data.
- *
- * @param pcb Protocol control block for the TCP connection to send the ACK
- */
-err_t
-tcp_send_empty_ack(struct tcp_pcb *pcb)
-{
-  struct pbuf *p;
-  struct tcp_hdr *tcphdr;
-  u8_t optlen = 0;
-
-#if LWIP_TCP_TIMESTAMPS
-  if (pcb->flags & TF_TIMESTAMP) {
-    optlen = LWIP_TCP_OPT_LENGTH(TF_SEG_OPTS_TS);
-  }
-#endif
-
-  p = tcp_output_alloc_header(pcb, optlen, 0, htonl(pcb->snd_nxt));
-  if (p == NULL) {
-    LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: (ACK) could not allocate pbuf\n"));
-    return ERR_BUF;
-  }
-  tcphdr = (struct tcp_hdr *)p->payload;
-  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);
-
-  /* NB. MSS option is only sent on SYNs, so ignore it here */
-#if LWIP_TCP_TIMESTAMPS
-  pcb->ts_lastacksent = pcb->rcv_nxt;
-
-  if (pcb->flags & TF_TIMESTAMP) {
-    tcp_build_timestamp_option(pcb, (u32_t *)(tcphdr + 1));
-  }
-#endif 
-
-#if CHECKSUM_GEN_TCP
-  tcphdr->chksum = inet_chksum_pseudo(p, &(pcb->local_ip), &(pcb->remote_ip),
-        IP_PROTO_TCP, p->tot_len);
-#endif
-#if LWIP_NETIF_HWADDRHINT
-  ip_output_hinted(p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, pcb->tos,
-      IP_PROTO_TCP, &(pcb->addr_hint));
-#else /* LWIP_NETIF_HWADDRHINT*/
-  ip_output(p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, pcb->tos,
-      IP_PROTO_TCP);
-#endif /* LWIP_NETIF_HWADDRHINT*/
-  pbuf_free(p);
-
-  return ERR_OK;
-}
-
-/**
- * Find out what we can send and send it
- *
- * @param pcb Protocol control block for the TCP connection to send data
- * @return ERR_OK if data has been sent or nothing to send
- *         another err_t on error
- */
-err_t
-tcp_output(struct tcp_pcb *pcb)
-{
-  struct tcp_seg *seg, *useg;
-  u32_t wnd, snd_nxt;
-#if TCP_CWND_DEBUG
-  s16_t i = 0;
-#endif /* TCP_CWND_DEBUG */
-
-  /* pcb->state LISTEN not allowed here */
-  LWIP_ASSERT("don't call tcp_output for listen-pcbs",
-    pcb->state != LISTEN);
-
-  /* First, check if we are invoked by the TCP input processing
-     code. If so, we do not output anything. Instead, we rely on the
-     input processing code to call us when input processing is done
-     with. */
-  if (tcp_input_pcb == pcb) {
-    return ERR_OK;
-  }
-
-  wnd = LWIP_MIN(pcb->snd_wnd, pcb->cwnd);
-
-  seg = pcb->unsent;
-
-  /* If 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)) {
-     return tcp_send_empty_ack(pcb);
-  }
-
-  /* useg should point to last segment on unacked queue */
-  useg = pcb->unacked;
-  if (useg != NULL) {
-    for (; useg->next != NULL; useg = useg->next);
-  }
-
-#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 %"U16_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 %"U16_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) {
-    LWIP_ASSERT("RST not expected here!", 
-                (TCPH_FLAGS(seg->tcphdr) & TCP_RST) == 0);
-    /* Stop sending if the nagle algorithm would prevent it
-     * Don't stop:
-     * - if tcp_write had a memory error before (prevent delayed ACK timeout) or
-     * - if FIN was already enqueued for this PCB (SYN is always alone in a segment -
-     *   either seg->next != NULL or pcb->unacked == NULL;
-     *   RST is no sent using tcp_write/tcp_output.
-     */
-    if((tcp_do_output_nagle(pcb) == 0) &&
-      ((pcb->flags & (TF_NAGLEMEMERR | TF_FIN)) == 0)){
-      break;
-    }
-#if TCP_CWND_DEBUG
-    LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_output: snd_wnd %"U16_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);
-    snd_nxt = ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg);
-    if (TCP_SEQ_LT(pcb->snd_nxt, snd_nxt)) {
-      pcb->snd_nxt = snd_nxt;
-    }
-    /* put segment on unacknowledged list if length > 0 */
-    if (TCP_TCPLEN(seg) > 0) {
-      seg->next = NULL;
-      /* unacked list is empty? */
-      if (pcb->unacked == NULL) {
-        pcb->unacked = seg;
-        useg = seg;
-      /* unacked list is not empty? */
-      } else {
-        /* In the case of fast retransmit, the packet should not go to the tail
-         * of the unacked queue, but rather somewhere before it. We need to check for
-         * this case. -STJ Jul 27, 2004 */
-        if (TCP_SEQ_LT(ntohl(seg->tcphdr->seqno), ntohl(useg->tcphdr->seqno))) {
-          /* add segment to before tail of unacked list, keeping the list sorted */
-          struct tcp_seg **cur_seg = &(pcb->unacked);
-          while (*cur_seg &&
-            TCP_SEQ_LT(ntohl((*cur_seg)->tcphdr->seqno), ntohl(seg->tcphdr->seqno))) {
-              cur_seg = &((*cur_seg)->next );
-          }
-          seg->next = (*cur_seg);
-          (*cur_seg) = seg;
-        } else {
-          /* add segment to tail of unacked list */
-          useg->next = seg;
-          useg = useg->next;
-        }
-      }
-    /* do not queue empty segments on the unacked list */
-    } else {
-      tcp_seg_free(seg);
-    }
-    seg = pcb->unsent;
-  }
-#if TCP_OVERSIZE
-  if (pcb->unsent == NULL) {
-    /* last unsent has been removed, reset unsent_oversize */
-    pcb->unsent_oversize = 0;
-  }
-#endif /* TCP_OVERSIZE */
-
-  pcb->flags &= ~TF_NAGLEMEMERR;
-  return ERR_OK;
-}
-
-/**
- * Called by tcp_output() to actually send a TCP segment over IP.
- *
- * @param seg the tcp_seg to send
- * @param pcb the tcp_pcb for the TCP connection used to send the segment
- */
-static void
-tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb)
-{
-  u16_t len;
-  struct netif *netif;
-  u32_t *opts;
-
-  /** @bug Exclude retransmitted segments from this count. */
-  snmp_inc_tcpoutsegs();
-
-  /* The TCP header has already been constructed, but the ackno and
-   wnd fields remain. */
-  seg->tcphdr->ackno = htonl(pcb->rcv_nxt);
-
-  /* advertise our receive window size in this TCP segment */
-  seg->tcphdr->wnd = htons(pcb->rcv_ann_wnd);
-
-  pcb->rcv_ann_right_edge = pcb->rcv_nxt + pcb->rcv_ann_wnd;
-
-  /* Add any requested options.  NB MSS option is only set on SYN
-     packets, so ignore it here */
-  opts = (u32_t *)(void *)(seg->tcphdr + 1);
-  if (seg->flags & TF_SEG_OPTS_MSS) {
-    u16_t mss;
-#if TCP_CALCULATE_EFF_SEND_MSS
-    mss = tcp_eff_send_mss(TCP_MSS, &pcb->remote_ip);
-#else /* TCP_CALCULATE_EFF_SEND_MSS */
-    mss = TCP_MSS;
-#endif /* TCP_CALCULATE_EFF_SEND_MSS */
-    *opts = TCP_BUILD_MSS_OPTION(mss);
-    opts += 1;
-  }
-#if LWIP_TCP_TIMESTAMPS
-  pcb->ts_lastacksent = pcb->rcv_nxt;
-
-  if (seg->flags & TF_SEG_OPTS_TS) {
-    tcp_build_timestamp_option(pcb, opts);
-    opts += 3;
-  }
-#endif
-
-  /* Set retransmission timer running if it is not currently enabled 
-     This must be set before checking the route. */
-  if (pcb->rtime == -1) {
-    pcb->rtime = 0;
-  }
-
-  /* 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_copy(pcb->local_ip, netif->ip_addr);
-  }
-
-  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
-#if TCP_CHECKSUM_ON_COPY
-  {
-    u32_t acc;
-#if TCP_CHECKSUM_ON_COPY_SANITY_CHECK
-    u16_t chksum_slow = inet_chksum_pseudo(seg->p, &(pcb->local_ip),
-           &(pcb->remote_ip),
-           IP_PROTO_TCP, seg->p->tot_len);
-#endif /* TCP_CHECKSUM_ON_COPY_SANITY_CHECK */
-    if ((seg->flags & TF_SEG_DATA_CHECKSUMMED) == 0) {
-      LWIP_ASSERT("data included but not checksummed",
-        seg->p->tot_len == (TCPH_HDRLEN(seg->tcphdr) * 4));
-    }
-
-    /* rebuild TCP header checksum (TCP header changes for retransmissions!) */
-    acc = inet_chksum_pseudo_partial(seg->p, &(pcb->local_ip),
-             &(pcb->remote_ip),
-             IP_PROTO_TCP, seg->p->tot_len, TCPH_HDRLEN(seg->tcphdr) * 4);
-    /* add payload checksum */
-    if (seg->chksum_swapped) {
-      seg->chksum = SWAP_BYTES_IN_WORD(seg->chksum);
-      seg->chksum_swapped = 0;
-    }
-    acc += (u16_t)~(seg->chksum);
-    seg->tcphdr->chksum = FOLD_U32T(acc);
-#if TCP_CHECKSUM_ON_COPY_SANITY_CHECK
-    if (chksum_slow != seg->tcphdr->chksum) {
-      LWIP_DEBUGF(TCP_DEBUG | LWIP_DBG_LEVEL_WARNING,
-                  ("tcp_output_segment: calculated checksum is %"X16_F" instead of %"X16_F"\n",
-                  seg->tcphdr->chksum, chksum_slow));
-      seg->tcphdr->chksum = chksum_slow;
-    }
-#endif /* TCP_CHECKSUM_ON_COPY_SANITY_CHECK */
-  }
-#else /* TCP_CHECKSUM_ON_COPY */
-  seg->tcphdr->chksum = inet_chksum_pseudo(seg->p, &(pcb->local_ip),
-         &(pcb->remote_ip),
-         IP_PROTO_TCP, seg->p->tot_len);
-#endif /* TCP_CHECKSUM_ON_COPY */
-#endif /* CHECKSUM_GEN_TCP */
-  TCP_STATS_INC(tcp.xmit);
-
-#if LWIP_NETIF_HWADDRHINT
-  ip_output_hinted(seg->p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, pcb->tos,
-      IP_PROTO_TCP, &(pcb->addr_hint));
-#else /* LWIP_NETIF_HWADDRHINT*/
-  ip_output(seg->p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, pcb->tos,
-      IP_PROTO_TCP);
-#endif /* LWIP_NETIF_HWADDRHINT*/
-}
-
-/**
- * Send a TCP RESET packet (empty segment with RST flag set) either to
- * abort a connection or to show that there is no matching local connection
- * for a received segment.
- *
- * Called by tcp_abort() (to abort a local connection), tcp_input() (if no
- * matching local pcb was found), tcp_listen_input() (if incoming segment
- * has ACK flag set) and tcp_process() (received segment in the wrong state)
- *
- * Since a RST segment is in most cases not sent for an active connection,
- * tcp_rst() has a number of arguments that are taken from a tcp_pcb for
- * most other segment output functions.
- *
- * @param seqno the sequence number to use for the outgoing segment
- * @param ackno the acknowledge number to use for the outgoing segment
- * @param local_ip the local IP address to send the segment from
- * @param remote_ip the remote IP address to send the segment to
- * @param local_port the local TCP port to send the segment from
- * @param remote_port the remote TCP port to send the segment to
- */
-void
-tcp_rst(u32_t seqno, u32_t ackno,
-  ip_addr_t *local_ip, ip_addr_t *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;
-  }
-  LWIP_ASSERT("check that first pbuf can hold struct tcp_hdr",
-              (p->len >= sizeof(struct tcp_hdr)));
-
-  tcphdr = (struct tcp_hdr *)p->payload;
-  tcphdr->src = htons(local_port);
-  tcphdr->dest = htons(remote_port);
-  tcphdr->seqno = htonl(seqno);
-  tcphdr->ackno = htonl(ackno);
-  TCPH_HDRLEN_FLAGS_SET(tcphdr, TCP_HLEN/4, TCP_RST | TCP_ACK);
-  tcphdr->wnd = PP_HTONS(TCP_WND);
-  tcphdr->chksum = 0;
-  tcphdr->urgp = 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);
-  snmp_inc_tcpoutrsts();
-   /* 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
- *
- * Called by tcp_slowtmr() for slow retransmission.
- *
- * @param pcb the tcp_pcb for which to re-enqueue all unacked segments
- */
-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;
-  /* last unsent hasn't changed, no need to reset unsent_oversize */
-
-  /* increment number of retransmissions */
-  ++pcb->nrtx;
-
-  /* Don't take any RTT measurements after retransmitting. */
-  pcb->rttest = 0;
-
-  /* Do the actual retransmission */
-  tcp_output(pcb);
-}
-
-/**
- * Requeue the first unacked segment for retransmission
- *
- * Called by tcp_receive() for fast retramsmit.
- *
- * @param pcb the tcp_pcb for which to retransmit the first unacked segment
- */
-void
-tcp_rexmit(struct tcp_pcb *pcb)
-{
-  struct tcp_seg *seg;
-  struct tcp_seg **cur_seg;
-
-  if (pcb->unacked == NULL) {
-    return;
-  }
-
-  /* Move the first unacked segment to the unsent queue */
-  /* Keep the unsent queue sorted. */
-  seg = pcb->unacked;
-  pcb->unacked = seg->next;
-
-  cur_seg = &(pcb->unsent);
-  while (*cur_seg &&
-    TCP_SEQ_LT(ntohl((*cur_seg)->tcphdr->seqno), ntohl(seg->tcphdr->seqno))) {
-      cur_seg = &((*cur_seg)->next );
-  }
-  seg->next = *cur_seg;
-  *cur_seg = seg;
-#if TCP_OVERSIZE
-  if (seg->next == NULL) {
-    /* the retransmitted segment is last in unsent, so reset unsent_oversize */
-    pcb->unsent_oversize = 0;
-  }
-#endif /* TCP_OVERSIZE */
-
-  ++pcb->nrtx;
-
-  /* Don't take any rtt measurements after retransmitting. */
-  pcb->rttest = 0;
-
-  /* Do the actual retransmission. */
-  snmp_inc_tcpretranssegs();
-  /* No need to call tcp_output: we are always called from tcp_input()
-     and thus tcp_output directly returns. */
-}
-
-
-/**
- * Handle retransmission after three dupacks received
- *
- * @param pcb the tcp_pcb for which to retransmit the first unacked segment
- */
-void 
-tcp_rexmit_fast(struct tcp_pcb *pcb)
-{
-  if (pcb->unacked != NULL && !(pcb->flags & TF_INFR)) {
-    /* This is fast retransmit. Retransmit the first unacked segment. */
-    LWIP_DEBUGF(TCP_FR_DEBUG, 
-                ("tcp_receive: dupacks %"U16_F" (%"U32_F
-                 "), fast retransmit %"U32_F"\n",
-                 (u16_t)pcb->dupacks, pcb->lastack,
-                 ntohl(pcb->unacked->tcphdr->seqno)));
-    tcp_rexmit(pcb);
-
-    /* Set ssthresh to half of the minimum of the current
-     * cwnd and the advertised window */
-    if (pcb->cwnd > pcb->snd_wnd) {
-      pcb->ssthresh = pcb->snd_wnd / 2;
-    } else {
-      pcb->ssthresh = pcb->cwnd / 2;
-    }
-    
-    /* The minimum value for ssthresh should be 2 MSS */
-    if (pcb->ssthresh < 2*pcb->mss) {
-      LWIP_DEBUGF(TCP_FR_DEBUG, 
-                  ("tcp_receive: The minimum value for ssthresh %"U16_F
-                   " should be min 2 mss %"U16_F"...\n",
-                   pcb->ssthresh, 2*pcb->mss));
-      pcb->ssthresh = 2*pcb->mss;
-    }
-    
-    pcb->cwnd = pcb->ssthresh + 3 * pcb->mss;
-    pcb->flags |= TF_INFR;
-  } 
-}
-
-
-/**
- * Send keepalive packets to keep a connection active although
- * no data is sent over it.
- *
- * Called by tcp_slowtmr()
- *
- * @param pcb the tcp_pcb for which to send a keepalive packet
- */
-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_16(&pcb->remote_ip), ip4_addr2_16(&pcb->remote_ip),
-                          ip4_addr3_16(&pcb->remote_ip), ip4_addr4_16(&pcb->remote_ip)));
-
-  LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: tcp_ticks %"U32_F"   pcb->tmr %"U32_F" pcb->keep_cnt_sent %"U16_F"\n", 
-                          tcp_ticks, pcb->tmr, pcb->keep_cnt_sent));
-   
-  p = tcp_output_alloc_header(pcb, 0, 0, htonl(pcb->snd_nxt - 1));
-  if(p == NULL) {
-    LWIP_DEBUGF(TCP_DEBUG, 
-                ("tcp_keepalive: could not allocate memory for pbuf\n"));
-    return;
-  }
-  tcphdr = (struct tcp_hdr *)p->payload;
-
-#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 */
-#if LWIP_NETIF_HWADDRHINT
-  ip_output_hinted(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP,
-    &(pcb->addr_hint));
-#else /* LWIP_NETIF_HWADDRHINT*/
-  ip_output(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP);
-#endif /* LWIP_NETIF_HWADDRHINT*/
-
-  pbuf_free(p);
-
-  LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: seqno %"U32_F" ackno %"U32_F".\n",
-                          pcb->snd_nxt - 1, pcb->rcv_nxt));
-}
-
-
-/**
- * Send persist timer zero-window probes to keep a connection active
- * when a window update is lost.
- *
- * Called by tcp_slowtmr()
- *
- * @param pcb the tcp_pcb for which to send a zero-window probe packet
- */
-void
-tcp_zero_window_probe(struct tcp_pcb *pcb)
-{
-  struct pbuf *p;
-  struct tcp_hdr *tcphdr;
-  struct tcp_seg *seg;
-  u16_t len;
-  u8_t is_fin;
-
-  LWIP_DEBUGF(TCP_DEBUG, 
-              ("tcp_zero_window_probe: sending ZERO WINDOW probe to %"
-               U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
-               ip4_addr1_16(&pcb->remote_ip), ip4_addr2_16(&pcb->remote_ip),
-               ip4_addr3_16(&pcb->remote_ip), ip4_addr4_16(&pcb->remote_ip)));
-
-  LWIP_DEBUGF(TCP_DEBUG, 
-              ("tcp_zero_window_probe: tcp_ticks %"U32_F
-               "   pcb->tmr %"U32_F" pcb->keep_cnt_sent %"U16_F"\n", 
-               tcp_ticks, pcb->tmr, pcb->keep_cnt_sent));
-
-  seg = pcb->unacked;
-
-  if(seg == NULL) {
-    seg = pcb->unsent;
-  }
-  if(seg == NULL) {
-    return;
-  }
-
-  is_fin = ((TCPH_FLAGS(seg->tcphdr) & TCP_FIN) != 0) && (seg->len == 0);
-  /* we want to send one seqno: either FIN or data (no options) */
-  len = is_fin ? 0 : 1;
-
-  p = tcp_output_alloc_header(pcb, 0, len, seg->tcphdr->seqno);
-  if(p == NULL) {
-    LWIP_DEBUGF(TCP_DEBUG, ("tcp_zero_window_probe: no memory for pbuf\n"));
-    return;
-  }
-  tcphdr = (struct tcp_hdr *)p->payload;
-
-  if (is_fin) {
-    /* FIN segment, no data */
-    TCPH_FLAGS_SET(tcphdr, TCP_ACK | TCP_FIN);
-  } else {
-    /* Data segment, copy in one byte from the head of the unacked queue */
-    char *d = ((char *)p->payload + TCP_HLEN);
-    /* Depending on whether the segment has already been sent (unacked) or not
-       (unsent), seg->p->payload points to the IP header or TCP header.
-       Ensure we copy the first TCP data byte: */
-    pbuf_copy_partial(seg->p, d, 1, seg->p->tot_len - seg->len);
-  }
-
-#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 */
-#if LWIP_NETIF_HWADDRHINT
-  ip_output_hinted(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP,
-    &(pcb->addr_hint));
-#else /* LWIP_NETIF_HWADDRHINT*/
-  ip_output(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP);
-#endif /* LWIP_NETIF_HWADDRHINT*/
-
-  pbuf_free(p);
-
-  LWIP_DEBUGF(TCP_DEBUG, ("tcp_zero_window_probe: seqno %"U32_F
-                          " ackno %"U32_F".\n",
-                          pcb->snd_nxt - 1, pcb->rcv_nxt));
-}
-#endif /* LWIP_TCP */
+/**
+ * @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 "lwip/opt.h"
+
+#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/priv/tcp_priv.h"
+#include "lwip/def.h"
+#include "lwip/mem.h"
+#include "lwip/memp.h"
+#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/stats.h"
+#include "lwip/ip6.h"
+#include "lwip/ip6_addr.h"
+#if LWIP_TCP_TIMESTAMPS
+#include "lwip/sys.h"
+#endif
+
+#include <string.h>
+
+/* Define some copy-macros for checksum-on-copy so that the code looks
+   nicer by preventing too many ifdef's. */
+#if TCP_CHECKSUM_ON_COPY
+#define TCP_DATA_COPY(dst, src, len, seg) do { \
+  tcp_seg_add_chksum(LWIP_CHKSUM_COPY(dst, src, len), \
+                     len, &seg->chksum, &seg->chksum_swapped); \
+  seg->flags |= TF_SEG_DATA_CHECKSUMMED; } while(0)
+#define TCP_DATA_COPY2(dst, src, len, chksum, chksum_swapped)  \
+  tcp_seg_add_chksum(LWIP_CHKSUM_COPY(dst, src, len), len, chksum, chksum_swapped);
+#else /* TCP_CHECKSUM_ON_COPY*/
+#define TCP_DATA_COPY(dst, src, len, seg)                     MEMCPY(dst, src, len)
+#define TCP_DATA_COPY2(dst, src, len, chksum, chksum_swapped) MEMCPY(dst, src, len)
+#endif /* TCP_CHECKSUM_ON_COPY*/
+
+/** Define this to 1 for an extra check that the output checksum is valid
+ * (usefule when the checksum is generated by the application, not the stack) */
+#ifndef TCP_CHECKSUM_ON_COPY_SANITY_CHECK
+#define TCP_CHECKSUM_ON_COPY_SANITY_CHECK   0
+#endif
+/* Allow to override the failure of sanity check from warning to e.g. hard failure */
+#if TCP_CHECKSUM_ON_COPY_SANITY_CHECK
+#ifndef TCP_CHECKSUM_ON_COPY_SANITY_CHECK_FAIL
+#define TCP_CHECKSUM_ON_COPY_SANITY_CHECK_FAIL(msg) LWIP_DEBUGF(TCP_DEBUG | LWIP_DBG_LEVEL_WARNING, msg)
+#endif
+#endif
+
+#if TCP_OVERSIZE
+/** The size of segment pbufs created when TCP_OVERSIZE is enabled */
+#ifndef TCP_OVERSIZE_CALC_LENGTH
+#define TCP_OVERSIZE_CALC_LENGTH(length) ((length) + TCP_OVERSIZE)
+#endif
+#endif
+
+/* Forward declarations.*/
+static err_t tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb, struct netif *netif);
+
+/** Allocate a pbuf and create a tcphdr at p->payload, used for output
+ * functions other than the default tcp_output -> tcp_output_segment
+ * (e.g. tcp_send_empty_ack, etc.)
+ *
+ * @param pcb tcp pcb for which to send a packet (used to initialize tcp_hdr)
+ * @param optlen length of header-options
+ * @param datalen length of tcp data to reserve in pbuf
+ * @param seqno_be seqno in network byte order (big-endian)
+ * @return pbuf with p->payload being the tcp_hdr
+ */
+static struct pbuf *
+tcp_output_alloc_header(struct tcp_pcb *pcb, u16_t optlen, u16_t datalen,
+                      u32_t seqno_be /* already in network byte order */)
+{
+  struct tcp_hdr *tcphdr;
+  struct pbuf *p = pbuf_alloc(PBUF_IP, TCP_HLEN + optlen + datalen, PBUF_RAM);
+  if (p != NULL) {
+    LWIP_ASSERT("check that first pbuf can hold struct tcp_hdr",
+                 (p->len >= TCP_HLEN + optlen));
+    tcphdr = (struct tcp_hdr *)p->payload;
+    tcphdr->src = lwip_htons(pcb->local_port);
+    tcphdr->dest = lwip_htons(pcb->remote_port);
+    tcphdr->seqno = seqno_be;
+    tcphdr->ackno = lwip_htonl(pcb->rcv_nxt);
+    TCPH_HDRLEN_FLAGS_SET(tcphdr, (5 + optlen / 4), TCP_ACK);
+    tcphdr->wnd = lwip_htons(TCPWND_MIN16(RCV_WND_SCALE(pcb, pcb->rcv_ann_wnd)));
+    tcphdr->chksum = 0;
+    tcphdr->urgp = 0;
+
+    /* If we're sending a packet, update the announced right window edge */
+    pcb->rcv_ann_right_edge = pcb->rcv_nxt + pcb->rcv_ann_wnd;
+  }
+  return p;
+}
+
+/**
+ * Called by tcp_close() to send a segment including FIN flag but not data.
+ *
+ * @param pcb the tcp_pcb over which to send a segment
+ * @return ERR_OK if sent, another err_t otherwise
+ */
+err_t
+tcp_send_fin(struct tcp_pcb *pcb)
+{
+  /* first, try to add the fin to the last unsent segment */
+  if (pcb->unsent != NULL) {
+    struct tcp_seg *last_unsent;
+    for (last_unsent = pcb->unsent; last_unsent->next != NULL;
+         last_unsent = last_unsent->next);
+
+    if ((TCPH_FLAGS(last_unsent->tcphdr) & (TCP_SYN | TCP_FIN | TCP_RST)) == 0) {
+      /* no SYN/FIN/RST flag in the header, we can add the FIN flag */
+      TCPH_SET_FLAG(last_unsent->tcphdr, TCP_FIN);
+      pcb->flags |= TF_FIN;
+      return ERR_OK;
+    }
+  }
+  /* no data, no length, flags, copy=1, no optdata */
+  return tcp_enqueue_flags(pcb, TCP_FIN);
+}
+
+/**
+ * Create a TCP segment with prefilled header.
+ *
+ * Called by tcp_write and tcp_enqueue_flags.
+ *
+ * @param pcb Protocol control block for the TCP connection.
+ * @param p pbuf that is used to hold the TCP header.
+ * @param flags TCP flags for header.
+ * @param seqno TCP sequence number of this packet
+ * @param optflags options to include in TCP header
+ * @return a new tcp_seg pointing to p, or NULL.
+ * The TCP header is filled in except ackno and wnd.
+ * p is freed on failure.
+ */
+static struct tcp_seg *
+tcp_create_segment(struct tcp_pcb *pcb, struct pbuf *p, u8_t flags, u32_t seqno, u8_t optflags)
+{
+  struct tcp_seg *seg;
+  u8_t optlen = LWIP_TCP_OPT_LENGTH(optflags);
+
+  if ((seg = (struct tcp_seg *)memp_malloc(MEMP_TCP_SEG)) == NULL) {
+    LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("tcp_create_segment: no memory.\n"));
+    pbuf_free(p);
+    return NULL;
+  }
+  seg->flags = optflags;
+  seg->next = NULL;
+  seg->p = p;
+  LWIP_ASSERT("p->tot_len >= optlen", p->tot_len >= optlen);
+  seg->len = p->tot_len - optlen;
+#if TCP_OVERSIZE_DBGCHECK
+  seg->oversize_left = 0;
+#endif /* TCP_OVERSIZE_DBGCHECK */
+#if TCP_CHECKSUM_ON_COPY
+  seg->chksum = 0;
+  seg->chksum_swapped = 0;
+  /* check optflags */
+  LWIP_ASSERT("invalid optflags passed: TF_SEG_DATA_CHECKSUMMED",
+              (optflags & TF_SEG_DATA_CHECKSUMMED) == 0);
+#endif /* TCP_CHECKSUM_ON_COPY */
+
+  /* build TCP header */
+  if (pbuf_header(p, TCP_HLEN)) {
+    LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("tcp_create_segment: no room for TCP header in pbuf.\n"));
+    TCP_STATS_INC(tcp.err);
+    tcp_seg_free(seg);
+    return NULL;
+  }
+  seg->tcphdr = (struct tcp_hdr *)seg->p->payload;
+  seg->tcphdr->src = lwip_htons(pcb->local_port);
+  seg->tcphdr->dest = lwip_htons(pcb->remote_port);
+  seg->tcphdr->seqno = lwip_htonl(seqno);
+  /* ackno is set in tcp_output */
+  TCPH_HDRLEN_FLAGS_SET(seg->tcphdr, (5 + optlen / 4), flags);
+  /* wnd and chksum are set in tcp_output */
+  seg->tcphdr->urgp = 0;
+  return seg;
+}
+
+/**
+ * Allocate a PBUF_RAM pbuf, perhaps with extra space at the end.
+ *
+ * This function is like pbuf_alloc(layer, length, PBUF_RAM) except
+ * there may be extra bytes available at the end.
+ *
+ * @param layer flag to define header size.
+ * @param length size of the pbuf's payload.
+ * @param max_length maximum usable size of payload+oversize.
+ * @param oversize pointer to a u16_t that will receive the number of usable tail bytes.
+ * @param pcb The TCP connection that will enqueue the pbuf.
+ * @param apiflags API flags given to tcp_write.
+ * @param first_seg true when this pbuf will be used in the first enqueued segment.
+ */
+#if TCP_OVERSIZE
+static struct pbuf *
+tcp_pbuf_prealloc(pbuf_layer layer, u16_t length, u16_t max_length,
+                  u16_t *oversize, struct tcp_pcb *pcb, u8_t apiflags,
+                  u8_t first_seg)
+{
+  struct pbuf *p;
+  u16_t alloc = length;
+
+#if LWIP_NETIF_TX_SINGLE_PBUF
+  LWIP_UNUSED_ARG(max_length);
+  LWIP_UNUSED_ARG(pcb);
+  LWIP_UNUSED_ARG(apiflags);
+  LWIP_UNUSED_ARG(first_seg);
+  alloc = max_length;
+#else /* LWIP_NETIF_TX_SINGLE_PBUF */
+  if (length < max_length) {
+    /* Should we allocate an oversized pbuf, or just the minimum
+     * length required? If tcp_write is going to be called again
+     * before this segment is transmitted, we want the oversized
+     * buffer. If the segment will be transmitted immediately, we can
+     * save memory by allocating only length. We use a simple
+     * heuristic based on the following information:
+     *
+     * Did the user set TCP_WRITE_FLAG_MORE?
+     *
+     * Will the Nagle algorithm defer transmission of this segment?
+     */
+    if ((apiflags & TCP_WRITE_FLAG_MORE) ||
+        (!(pcb->flags & TF_NODELAY) &&
+         (!first_seg ||
+          pcb->unsent != NULL ||
+          pcb->unacked != NULL))) {
+      alloc = LWIP_MIN(max_length, LWIP_MEM_ALIGN_SIZE(TCP_OVERSIZE_CALC_LENGTH(length)));
+    }
+  }
+#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
+  p = pbuf_alloc(layer, alloc, PBUF_RAM);
+  if (p == NULL) {
+    return NULL;
+  }
+  LWIP_ASSERT("need unchained pbuf", p->next == NULL);
+  *oversize = p->len - length;
+  /* trim p->len to the currently used size */
+  p->len = p->tot_len = length;
+  return p;
+}
+#else /* TCP_OVERSIZE */
+#define tcp_pbuf_prealloc(layer, length, mx, os, pcb, api, fst) pbuf_alloc((layer), (length), PBUF_RAM)
+#endif /* TCP_OVERSIZE */
+
+#if TCP_CHECKSUM_ON_COPY
+/** Add a checksum of newly added data to the segment */
+static void
+tcp_seg_add_chksum(u16_t chksum, u16_t len, u16_t *seg_chksum,
+                   u8_t *seg_chksum_swapped)
+{
+  u32_t helper;
+  /* add chksum to old chksum and fold to u16_t */
+  helper = chksum + *seg_chksum;
+  chksum = FOLD_U32T(helper);
+  if ((len & 1) != 0) {
+    *seg_chksum_swapped = 1 - *seg_chksum_swapped;
+    chksum = SWAP_BYTES_IN_WORD(chksum);
+  }
+  *seg_chksum = chksum;
+}
+#endif /* TCP_CHECKSUM_ON_COPY */
+
+/** Checks if tcp_write is allowed or not (checks state, snd_buf and snd_queuelen).
+ *
+ * @param pcb the tcp pcb to check for
+ * @param len length of data to send (checked agains snd_buf)
+ * @return ERR_OK if tcp_write is allowed to proceed, another err_t otherwise
+ */
+static err_t
+tcp_write_checks(struct tcp_pcb *pcb, u16_t len)
+{
+  /* connection is in invalid state for data transmission? */
+  if ((pcb->state != ESTABLISHED) &&
+      (pcb->state != CLOSE_WAIT) &&
+      (pcb->state != SYN_SENT) &&
+      (pcb->state != SYN_RCVD)) {
+    LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_STATE | LWIP_DBG_LEVEL_SEVERE, ("tcp_write() called in invalid state\n"));
+    return ERR_CONN;
+  } else if (len == 0) {
+    return ERR_OK;
+  }
+
+  /* fail on too much data */
+  if (len > pcb->snd_buf) {
+    LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SEVERE, ("tcp_write: too much data (len=%"U16_F" > snd_buf=%"TCPWNDSIZE_F")\n",
+      len, pcb->snd_buf));
+    pcb->flags |= TF_NAGLEMEMERR;
+    return ERR_MEM;
+  }
+
+  LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_write: queuelen: %"TCPWNDSIZE_F"\n", (tcpwnd_size_t)pcb->snd_queuelen));
+
+  /* If total number of pbufs on the unsent/unacked queues exceeds the
+   * configured maximum, return an error */
+  /* check for configured max queuelen and possible overflow */
+  if ((pcb->snd_queuelen >= TCP_SND_QUEUELEN) || (pcb->snd_queuelen > TCP_SNDQUEUELEN_OVERFLOW)) {
+    LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SEVERE, ("tcp_write: too long queue %"U16_F" (max %"U16_F")\n",
+      pcb->snd_queuelen, (u16_t)TCP_SND_QUEUELEN));
+    TCP_STATS_INC(tcp.memerr);
+    pcb->flags |= TF_NAGLEMEMERR;
+    return ERR_MEM;
+  }
+  if (pcb->snd_queuelen != 0) {
+    LWIP_ASSERT("tcp_write: pbufs on queue => at least one queue non-empty",
+      pcb->unacked != NULL || pcb->unsent != NULL);
+  } else {
+    LWIP_ASSERT("tcp_write: no pbufs on queue => both queues empty",
+      pcb->unacked == NULL && pcb->unsent == NULL);
+  }
+  return ERR_OK;
+}
+
+/**
+ * @ingroup tcp_raw
+ * Write data for sending (but does not send it immediately).
+ *
+ * It waits in the expectation of more data being sent soon (as
+ * it can send them more efficiently by combining them together).
+ * To prompt the system to send data now, call tcp_output() after
+ * calling tcp_write().
+ *
+ * @param pcb Protocol control block for the TCP connection to enqueue data for.
+ * @param arg Pointer to the data to be enqueued for sending.
+ * @param len Data length in bytes
+ * @param apiflags combination of following flags :
+ * - TCP_WRITE_FLAG_COPY (0x01) data will be copied into memory belonging to the stack
+ * - TCP_WRITE_FLAG_MORE (0x02) for TCP connection, PSH flag will not be set on last segment sent,
+ * @return ERR_OK if enqueued, another err_t on error
+ */
+err_t
+tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags)
+{
+  struct pbuf *concat_p = NULL;
+  struct tcp_seg *last_unsent = NULL, *seg = NULL, *prev_seg = NULL, *queue = NULL;
+  u16_t pos = 0; /* position in 'arg' data */
+  u16_t queuelen;
+  u8_t optlen = 0;
+  u8_t optflags = 0;
+#if TCP_OVERSIZE
+  u16_t oversize = 0;
+  u16_t oversize_used = 0;
+#if TCP_OVERSIZE_DBGCHECK
+  u16_t oversize_add = 0;
+#endif /* TCP_OVERSIZE_DBGCHECK*/
+#endif /* TCP_OVERSIZE */
+  u16_t extendlen = 0;
+#if TCP_CHECKSUM_ON_COPY
+  u16_t concat_chksum = 0;
+  u8_t concat_chksum_swapped = 0;
+  u16_t concat_chksummed = 0;
+#endif /* TCP_CHECKSUM_ON_COPY */
+  err_t err;
+  /* don't allocate segments bigger than half the maximum window we ever received */
+  u16_t mss_local = LWIP_MIN(pcb->mss, TCPWND_MIN16(pcb->snd_wnd_max/2));
+  mss_local = mss_local ? mss_local : pcb->mss;
+
+#if LWIP_NETIF_TX_SINGLE_PBUF
+  /* Always copy to try to create single pbufs for TX */
+  apiflags |= TCP_WRITE_FLAG_COPY;
+#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
+
+  LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_write(pcb=%p, data=%p, len=%"U16_F", apiflags=%"U16_F")\n",
+    (void *)pcb, arg, len, (u16_t)apiflags));
+  LWIP_ERROR("tcp_write: arg == NULL (programmer violates API)",
+             arg != NULL, return ERR_ARG;);
+
+  err = tcp_write_checks(pcb, len);
+  if (err != ERR_OK) {
+    return err;
+  }
+  queuelen = pcb->snd_queuelen;
+
+#if LWIP_TCP_TIMESTAMPS
+  if ((pcb->flags & TF_TIMESTAMP)) {
+    /* Make sure the timestamp option is only included in data segments if we
+       agreed about it with the remote host. */
+    optflags = TF_SEG_OPTS_TS;
+    optlen = LWIP_TCP_OPT_LENGTH(TF_SEG_OPTS_TS);
+    /* ensure that segments can hold at least one data byte... */
+    mss_local = LWIP_MAX(mss_local, LWIP_TCP_OPT_LEN_TS + 1);
+  }
+#endif /* LWIP_TCP_TIMESTAMPS */
+
+
+  /*
+   * TCP segmentation is done in three phases with increasing complexity:
+   *
+   * 1. Copy data directly into an oversized pbuf.
+   * 2. Chain a new pbuf to the end of pcb->unsent.
+   * 3. Create new segments.
+   *
+   * We may run out of memory at any point. In that case we must
+   * return ERR_MEM and not change anything in pcb. Therefore, all
+   * changes are recorded in local variables and committed at the end
+   * of the function. Some pcb fields are maintained in local copies:
+   *
+   * queuelen = pcb->snd_queuelen
+   * oversize = pcb->unsent_oversize
+   *
+   * These variables are set consistently by the phases:
+   *
+   * seg points to the last segment tampered with.
+   *
+   * pos records progress as data is segmented.
+   */
+
+  /* Find the tail of the unsent queue. */
+  if (pcb->unsent != NULL) {
+    u16_t space;
+    u16_t unsent_optlen;
+
+    /* @todo: this could be sped up by keeping last_unsent in the pcb */
+    for (last_unsent = pcb->unsent; last_unsent->next != NULL;
+         last_unsent = last_unsent->next);
+
+    /* Usable space at the end of the last unsent segment */
+    unsent_optlen = LWIP_TCP_OPT_LENGTH(last_unsent->flags);
+    LWIP_ASSERT("mss_local is too small", mss_local >= last_unsent->len + unsent_optlen);
+    space = mss_local - (last_unsent->len + unsent_optlen);
+
+    /*
+     * Phase 1: Copy data directly into an oversized pbuf.
+     *
+     * The number of bytes copied is recorded in the oversize_used
+     * variable. The actual copying is done at the bottom of the
+     * function.
+     */
+#if TCP_OVERSIZE
+#if TCP_OVERSIZE_DBGCHECK
+    /* check that pcb->unsent_oversize matches last_unsent->oversize_left */
+    LWIP_ASSERT("unsent_oversize mismatch (pcb vs. last_unsent)",
+                pcb->unsent_oversize == last_unsent->oversize_left);
+#endif /* TCP_OVERSIZE_DBGCHECK */
+    oversize = pcb->unsent_oversize;
+    if (oversize > 0) {
+      LWIP_ASSERT("inconsistent oversize vs. space", oversize <= space);
+      seg = last_unsent;
+      oversize_used = LWIP_MIN(space, LWIP_MIN(oversize, len));
+      pos += oversize_used;
+      oversize -= oversize_used;
+      space -= oversize_used;
+    }
+    /* now we are either finished or oversize is zero */
+    LWIP_ASSERT("inconsistent oversize vs. len", (oversize == 0) || (pos == len));
+#endif /* TCP_OVERSIZE */
+
+    /*
+     * Phase 2: Chain a new pbuf to the end of pcb->unsent.
+     *
+     * As an exception when NOT copying the data, if the given data buffer
+     * directly follows the last unsent data buffer in memory, extend the last
+     * ROM pbuf reference to the buffer, thus saving a ROM pbuf allocation.
+     *
+     * We don't extend segments containing SYN/FIN flags or options
+     * (len==0). The new pbuf is kept in concat_p and pbuf_cat'ed at
+     * the end.
+     */
+    if ((pos < len) && (space > 0) && (last_unsent->len > 0)) {
+      u16_t seglen = LWIP_MIN(space, len - pos);
+      seg = last_unsent;
+
+      /* Create a pbuf with a copy or reference to seglen bytes. We
+       * can use PBUF_RAW here since the data appears in the middle of
+       * a segment. A header will never be prepended. */
+      if (apiflags & TCP_WRITE_FLAG_COPY) {
+        /* Data is copied */
+        if ((concat_p = tcp_pbuf_prealloc(PBUF_RAW, seglen, space, &oversize, pcb, apiflags, 1)) == NULL) {
+          LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+                      ("tcp_write : could not allocate memory for pbuf copy size %"U16_F"\n",
+                       seglen));
+          goto memerr;
+        }
+#if TCP_OVERSIZE_DBGCHECK
+        oversize_add = oversize;
+#endif /* TCP_OVERSIZE_DBGCHECK */
+        TCP_DATA_COPY2(concat_p->payload, (const u8_t*)arg + pos, seglen, &concat_chksum, &concat_chksum_swapped);
+#if TCP_CHECKSUM_ON_COPY
+        concat_chksummed += seglen;
+#endif /* TCP_CHECKSUM_ON_COPY */
+        queuelen += pbuf_clen(concat_p);
+      } else {
+        /* Data is not copied */
+        /* If the last unsent pbuf is of type PBUF_ROM, try to extend it. */
+        struct pbuf *p;
+        for (p = last_unsent->p; p->next != NULL; p = p->next);
+        if (p->type == PBUF_ROM && (const u8_t *)p->payload + p->len == (const u8_t *)arg) {
+          LWIP_ASSERT("tcp_write: ROM pbufs cannot be oversized", pos == 0);
+          extendlen = seglen;
+        } else {
+          if ((concat_p = pbuf_alloc(PBUF_RAW, seglen, PBUF_ROM)) == NULL) {
+            LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+                        ("tcp_write: could not allocate memory for zero-copy pbuf\n"));
+            goto memerr;
+          }
+          /* reference the non-volatile payload data */
+          ((struct pbuf_rom*)concat_p)->payload = (const u8_t*)arg + pos;
+          queuelen += pbuf_clen(concat_p);
+        }
+#if TCP_CHECKSUM_ON_COPY
+        /* calculate the checksum of nocopy-data */
+        tcp_seg_add_chksum(~inet_chksum((const u8_t*)arg + pos, seglen), seglen,
+          &concat_chksum, &concat_chksum_swapped);
+        concat_chksummed += seglen;
+#endif /* TCP_CHECKSUM_ON_COPY */
+      }
+
+      pos += seglen;
+    }
+  } else {
+#if TCP_OVERSIZE
+    LWIP_ASSERT("unsent_oversize mismatch (pcb->unsent is NULL)",
+                pcb->unsent_oversize == 0);
+#endif /* TCP_OVERSIZE */
+  }
+
+  /*
+   * Phase 3: Create new segments.
+   *
+   * The new segments are chained together in the local 'queue'
+   * variable, ready to be appended to pcb->unsent.
+   */
+  while (pos < len) {
+    struct pbuf *p;
+    u16_t left = len - pos;
+    u16_t max_len = mss_local - optlen;
+    u16_t seglen = LWIP_MIN(left, max_len);
+#if TCP_CHECKSUM_ON_COPY
+    u16_t chksum = 0;
+    u8_t chksum_swapped = 0;
+#endif /* TCP_CHECKSUM_ON_COPY */
+
+    if (apiflags & TCP_WRITE_FLAG_COPY) {
+      /* If copy is set, memory should be allocated and data copied
+       * into pbuf */
+      if ((p = tcp_pbuf_prealloc(PBUF_TRANSPORT, seglen + optlen, mss_local, &oversize, pcb, apiflags, queue == NULL)) == NULL) {
+        LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("tcp_write : could not allocate memory for pbuf copy size %"U16_F"\n", seglen));
+        goto memerr;
+      }
+      LWIP_ASSERT("tcp_write: check that first pbuf can hold the complete seglen",
+                  (p->len >= seglen));
+      TCP_DATA_COPY2((char *)p->payload + optlen, (const u8_t*)arg + pos, seglen, &chksum, &chksum_swapped);
+    } else {
+      /* Copy is not set: First allocate a pbuf for holding the data.
+       * Since the referenced data is available at least until it is
+       * sent out on the link (as it has to be ACKed by the remote
+       * party) we can safely use PBUF_ROM instead of PBUF_REF here.
+       */
+      struct pbuf *p2;
+#if TCP_OVERSIZE
+      LWIP_ASSERT("oversize == 0", oversize == 0);
+#endif /* TCP_OVERSIZE */
+      if ((p2 = pbuf_alloc(PBUF_TRANSPORT, seglen, PBUF_ROM)) == NULL) {
+        LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("tcp_write: could not allocate memory for zero-copy pbuf\n"));
+        goto memerr;
+      }
+#if TCP_CHECKSUM_ON_COPY
+      /* calculate the checksum of nocopy-data */
+      chksum = ~inet_chksum((const u8_t*)arg + pos, seglen);
+      if (seglen & 1) {
+        chksum_swapped = 1;
+        chksum = SWAP_BYTES_IN_WORD(chksum);
+      }
+#endif /* TCP_CHECKSUM_ON_COPY */
+      /* reference the non-volatile payload data */
+      ((struct pbuf_rom*)p2)->payload = (const u8_t*)arg + pos;
+
+      /* Second, allocate a pbuf for the headers. */
+      if ((p = pbuf_alloc(PBUF_TRANSPORT, optlen, PBUF_RAM)) == NULL) {
+        /* If allocation fails, we have to deallocate the data pbuf as
+         * well. */
+        pbuf_free(p2);
+        LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("tcp_write: could not allocate memory for header pbuf\n"));
+        goto memerr;
+      }
+      /* Concatenate the headers and data pbufs together. */
+      pbuf_cat(p/*header*/, p2/*data*/);
+    }
+
+    queuelen += pbuf_clen(p);
+
+    /* Now that there are more segments queued, we check again if the
+     * length of the queue exceeds the configured maximum or
+     * overflows. */
+    if ((queuelen > TCP_SND_QUEUELEN) || (queuelen > TCP_SNDQUEUELEN_OVERFLOW)) {
+      LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("tcp_write: queue too long %"U16_F" (%d)\n",
+        queuelen, (int)TCP_SND_QUEUELEN));
+      pbuf_free(p);
+      goto memerr;
+    }
+
+    if ((seg = tcp_create_segment(pcb, p, 0, pcb->snd_lbb + pos, optflags)) == NULL) {
+      goto memerr;
+    }
+#if TCP_OVERSIZE_DBGCHECK
+    seg->oversize_left = oversize;
+#endif /* TCP_OVERSIZE_DBGCHECK */
+#if TCP_CHECKSUM_ON_COPY
+    seg->chksum = chksum;
+    seg->chksum_swapped = chksum_swapped;
+    seg->flags |= TF_SEG_DATA_CHECKSUMMED;
+#endif /* TCP_CHECKSUM_ON_COPY */
+
+    /* first segment of to-be-queued data? */
+    if (queue == NULL) {
+      queue = seg;
+    } else {
+      /* Attach the segment to the end of the queued segments */
+      LWIP_ASSERT("prev_seg != NULL", prev_seg != NULL);
+      prev_seg->next = seg;
+    }
+    /* remember last segment of to-be-queued data for next iteration */
+    prev_seg = seg;
+
+    LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_TRACE, ("tcp_write: queueing %"U32_F":%"U32_F"\n",
+      lwip_ntohl(seg->tcphdr->seqno),
+      lwip_ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg)));
+
+    pos += seglen;
+  }
+
+  /*
+   * All three segmentation phases were successful. We can commit the
+   * transaction.
+   */
+#if TCP_OVERSIZE_DBGCHECK
+  if ((last_unsent != NULL) && (oversize_add != 0)) {
+    last_unsent->oversize_left += oversize_add;
+  }
+#endif /* TCP_OVERSIZE_DBGCHECK */
+
+  /*
+   * Phase 1: If data has been added to the preallocated tail of
+   * last_unsent, we update the length fields of the pbuf chain.
+   */
+#if TCP_OVERSIZE
+  if (oversize_used > 0) {
+    struct pbuf *p;
+    /* Bump tot_len of whole chain, len of tail */
+    for (p = last_unsent->p; p; p = p->next) {
+      p->tot_len += oversize_used;
+      if (p->next == NULL) {
+        TCP_DATA_COPY((char *)p->payload + p->len, arg, oversize_used, last_unsent);
+        p->len += oversize_used;
+      }
+    }
+    last_unsent->len += oversize_used;
+#if TCP_OVERSIZE_DBGCHECK
+    LWIP_ASSERT("last_unsent->oversize_left >= oversize_used",
+                last_unsent->oversize_left >= oversize_used);
+    last_unsent->oversize_left -= oversize_used;
+#endif /* TCP_OVERSIZE_DBGCHECK */
+  }
+  pcb->unsent_oversize = oversize;
+#endif /* TCP_OVERSIZE */
+
+  /*
+   * Phase 2: concat_p can be concatenated onto last_unsent->p, unless we
+   * determined that the last ROM pbuf can be extended to include the new data.
+   */
+  if (concat_p != NULL) {
+    LWIP_ASSERT("tcp_write: cannot concatenate when pcb->unsent is empty",
+      (last_unsent != NULL));
+    pbuf_cat(last_unsent->p, concat_p);
+    last_unsent->len += concat_p->tot_len;
+  } else if (extendlen > 0) {
+    struct pbuf *p;
+    LWIP_ASSERT("tcp_write: extension of reference requires reference",
+      last_unsent != NULL && last_unsent->p != NULL);
+    for (p = last_unsent->p; p->next != NULL; p = p->next) {
+      p->tot_len += extendlen;
+    }
+    p->tot_len += extendlen;
+    p->len += extendlen;
+    last_unsent->len += extendlen;
+  }
+
+#if TCP_CHECKSUM_ON_COPY
+  if (concat_chksummed) {
+    LWIP_ASSERT("tcp_write: concat checksum needs concatenated data",
+        concat_p != NULL || extendlen > 0);
+    /*if concat checksumm swapped - swap it back */
+    if (concat_chksum_swapped) {
+      concat_chksum = SWAP_BYTES_IN_WORD(concat_chksum);
+    }
+    tcp_seg_add_chksum(concat_chksum, concat_chksummed, &last_unsent->chksum,
+      &last_unsent->chksum_swapped);
+    last_unsent->flags |= TF_SEG_DATA_CHECKSUMMED;
+  }
+#endif /* TCP_CHECKSUM_ON_COPY */
+
+  /*
+   * Phase 3: Append queue to pcb->unsent. Queue may be NULL, but that
+   * is harmless
+   */
+  if (last_unsent == NULL) {
+    pcb->unsent = queue;
+  } else {
+    last_unsent->next = queue;
+  }
+
+  /*
+   * Finally update the pcb state.
+   */
+  pcb->snd_lbb += len;
+  pcb->snd_buf -= len;
+  pcb->snd_queuelen = queuelen;
+
+  LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_write: %"S16_F" (after enqueued)\n",
+    pcb->snd_queuelen));
+  if (pcb->snd_queuelen != 0) {
+    LWIP_ASSERT("tcp_write: valid queue length",
+                pcb->unacked != NULL || pcb->unsent != NULL);
+  }
+
+  /* Set the PSH flag in the last segment that we enqueued. */
+  if (seg != NULL && seg->tcphdr != NULL && ((apiflags & TCP_WRITE_FLAG_MORE)==0)) {
+    TCPH_SET_FLAG(seg->tcphdr, TCP_PSH);
+  }
+
+  return ERR_OK;
+memerr:
+  pcb->flags |= TF_NAGLEMEMERR;
+  TCP_STATS_INC(tcp.memerr);
+
+  if (concat_p != NULL) {
+    pbuf_free(concat_p);
+  }
+  if (queue != NULL) {
+    tcp_segs_free(queue);
+  }
+  if (pcb->snd_queuelen != 0) {
+    LWIP_ASSERT("tcp_write: valid queue length", pcb->unacked != NULL ||
+      pcb->unsent != NULL);
+  }
+  LWIP_DEBUGF(TCP_QLEN_DEBUG | LWIP_DBG_STATE, ("tcp_write: %"S16_F" (with mem err)\n", pcb->snd_queuelen));
+  return ERR_MEM;
+}
+
+/**
+ * Enqueue TCP options for transmission.
+ *
+ * Called by tcp_connect(), tcp_listen_input(), and tcp_send_ctrl().
+ *
+ * @param pcb Protocol control block for the TCP connection.
+ * @param flags TCP header flags to set in the outgoing segment.
+ */
+err_t
+tcp_enqueue_flags(struct tcp_pcb *pcb, u8_t flags)
+{
+  struct pbuf *p;
+  struct tcp_seg *seg;
+  u8_t optflags = 0;
+  u8_t optlen = 0;
+
+  LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue_flags: queuelen: %"U16_F"\n", (u16_t)pcb->snd_queuelen));
+
+  LWIP_ASSERT("tcp_enqueue_flags: need either TCP_SYN or TCP_FIN in flags (programmer violates API)",
+              (flags & (TCP_SYN | TCP_FIN)) != 0);
+
+  /* check for configured max queuelen and possible overflow (FIN flag should always come through!) */
+  if (((pcb->snd_queuelen >= TCP_SND_QUEUELEN) || (pcb->snd_queuelen > TCP_SNDQUEUELEN_OVERFLOW)) &&
+      ((flags & TCP_FIN) == 0)) {
+    LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SEVERE, ("tcp_enqueue_flags: too long queue %"U16_F" (max %"U16_F")\n",
+                                       pcb->snd_queuelen, (u16_t)TCP_SND_QUEUELEN));
+    TCP_STATS_INC(tcp.memerr);
+    pcb->flags |= TF_NAGLEMEMERR;
+    return ERR_MEM;
+  }
+
+  if (flags & TCP_SYN) {
+    optflags = TF_SEG_OPTS_MSS;
+#if LWIP_WND_SCALE
+    if ((pcb->state != SYN_RCVD) || (pcb->flags & TF_WND_SCALE)) {
+      /* In a <SYN,ACK> (sent in state SYN_RCVD), the window scale option may only
+         be sent if we received a window scale option from the remote host. */
+      optflags |= TF_SEG_OPTS_WND_SCALE;
+    }
+#endif /* LWIP_WND_SCALE */
+  }
+#if LWIP_TCP_TIMESTAMPS
+  if ((pcb->flags & TF_TIMESTAMP)) {
+    /* Make sure the timestamp option is only included in data segments if we
+       agreed about it with the remote host. */
+    optflags |= TF_SEG_OPTS_TS;
+  }
+#endif /* LWIP_TCP_TIMESTAMPS */
+  optlen = LWIP_TCP_OPT_LENGTH(optflags);
+
+  /* Allocate pbuf with room for TCP header + options */
+  if ((p = pbuf_alloc(PBUF_TRANSPORT, optlen, PBUF_RAM)) == NULL) {
+    pcb->flags |= TF_NAGLEMEMERR;
+    TCP_STATS_INC(tcp.memerr);
+    return ERR_MEM;
+  }
+  LWIP_ASSERT("tcp_enqueue_flags: check that first pbuf can hold optlen",
+              (p->len >= optlen));
+
+  /* Allocate memory for tcp_seg, and fill in fields. */
+  if ((seg = tcp_create_segment(pcb, p, flags, pcb->snd_lbb, optflags)) == NULL) {
+    pcb->flags |= TF_NAGLEMEMERR;
+    TCP_STATS_INC(tcp.memerr);
+    return ERR_MEM;
+  }
+  LWIP_ASSERT("seg->tcphdr not aligned", ((mem_ptr_t)seg->tcphdr % LWIP_MIN(MEM_ALIGNMENT, 4)) == 0);
+  LWIP_ASSERT("tcp_enqueue_flags: invalid segment length", seg->len == 0);
+
+  LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_TRACE,
+              ("tcp_enqueue_flags: queueing %"U32_F":%"U32_F" (0x%"X16_F")\n",
+               lwip_ntohl(seg->tcphdr->seqno),
+               lwip_ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg),
+               (u16_t)flags));
+
+  /* Now append seg to pcb->unsent queue */
+  if (pcb->unsent == NULL) {
+    pcb->unsent = seg;
+  } else {
+    struct tcp_seg *useg;
+    for (useg = pcb->unsent; useg->next != NULL; useg = useg->next);
+    useg->next = seg;
+  }
+#if TCP_OVERSIZE
+  /* The new unsent tail has no space */
+  pcb->unsent_oversize = 0;
+#endif /* TCP_OVERSIZE */
+
+  /* SYN and FIN bump the sequence number */
+  if ((flags & TCP_SYN) || (flags & TCP_FIN)) {
+    pcb->snd_lbb++;
+    /* optlen does not influence snd_buf */
+  }
+  if (flags & TCP_FIN) {
+    pcb->flags |= TF_FIN;
+  }
+
+  /* update number of segments on the queues */
+  pcb->snd_queuelen += pbuf_clen(seg->p);
+  LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue_flags: %"S16_F" (after enqueued)\n", pcb->snd_queuelen));
+  if (pcb->snd_queuelen != 0) {
+    LWIP_ASSERT("tcp_enqueue_flags: invalid queue length",
+      pcb->unacked != NULL || pcb->unsent != NULL);
+  }
+
+  return ERR_OK;
+}
+
+#if LWIP_TCP_TIMESTAMPS
+/* Build a timestamp option (12 bytes long) at the specified options pointer)
+ *
+ * @param pcb tcp_pcb
+ * @param opts option pointer where to store the timestamp option
+ */
+static void
+tcp_build_timestamp_option(struct tcp_pcb *pcb, u32_t *opts)
+{
+  /* Pad with two NOP options to make everything nicely aligned */
+  opts[0] = PP_HTONL(0x0101080A);
+  opts[1] = lwip_htonl(sys_now());
+  opts[2] = lwip_htonl(pcb->ts_recent);
+}
+#endif
+
+#if LWIP_WND_SCALE
+/** Build a window scale option (3 bytes long) at the specified options pointer)
+ *
+ * @param opts option pointer where to store the window scale option
+ */
+static void
+tcp_build_wnd_scale_option(u32_t *opts)
+{
+  /* Pad with one NOP option to make everything nicely aligned */
+  opts[0] = PP_HTONL(0x01030300 | TCP_RCV_SCALE);
+}
+#endif
+
+/**
+ * Send an ACK without data.
+ *
+ * @param pcb Protocol control block for the TCP connection to send the ACK
+ */
+err_t
+tcp_send_empty_ack(struct tcp_pcb *pcb)
+{
+  err_t err;
+  struct pbuf *p;
+  u8_t optlen = 0;
+  struct netif *netif;
+#if LWIP_TCP_TIMESTAMPS || CHECKSUM_GEN_TCP
+  struct tcp_hdr *tcphdr;
+#endif /* LWIP_TCP_TIMESTAMPS || CHECKSUM_GEN_TCP */
+
+#if LWIP_TCP_TIMESTAMPS
+  if (pcb->flags & TF_TIMESTAMP) {
+    optlen = LWIP_TCP_OPT_LENGTH(TF_SEG_OPTS_TS);
+  }
+#endif
+
+  p = tcp_output_alloc_header(pcb, optlen, 0, lwip_htonl(pcb->snd_nxt));
+  if (p == NULL) {
+    /* let tcp_fasttmr retry sending this ACK */
+    pcb->flags |= (TF_ACK_DELAY | TF_ACK_NOW);
+    LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: (ACK) could not allocate pbuf\n"));
+    return ERR_BUF;
+  }
+#if LWIP_TCP_TIMESTAMPS || CHECKSUM_GEN_TCP
+  tcphdr = (struct tcp_hdr *)p->payload;
+#endif /* LWIP_TCP_TIMESTAMPS || CHECKSUM_GEN_TCP */
+  LWIP_DEBUGF(TCP_OUTPUT_DEBUG,
+              ("tcp_output: sending ACK for %"U32_F"\n", pcb->rcv_nxt));
+
+  /* NB. MSS and window scale options are only sent on SYNs, so ignore them here */
+#if LWIP_TCP_TIMESTAMPS
+  pcb->ts_lastacksent = pcb->rcv_nxt;
+
+  if (pcb->flags & TF_TIMESTAMP) {
+    tcp_build_timestamp_option(pcb, (u32_t *)(tcphdr + 1));
+  }
+#endif
+
+  netif = ip_route(&pcb->local_ip, &pcb->remote_ip);
+  if (netif == NULL) {
+    err = ERR_RTE;
+  } else {
+#if CHECKSUM_GEN_TCP
+    IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_TCP) {
+      tcphdr->chksum = ip_chksum_pseudo(p, IP_PROTO_TCP, p->tot_len,
+        &pcb->local_ip, &pcb->remote_ip);
+    }
+#endif
+    NETIF_SET_HWADDRHINT(netif, &(pcb->addr_hint));
+    err = ip_output_if(p, &pcb->local_ip, &pcb->remote_ip,
+      pcb->ttl, pcb->tos, IP_PROTO_TCP, netif);
+    NETIF_SET_HWADDRHINT(netif, NULL);
+  }
+  pbuf_free(p);
+
+  if (err != ERR_OK) {
+    /* let tcp_fasttmr retry sending this ACK */
+    pcb->flags |= (TF_ACK_DELAY | TF_ACK_NOW);
+  } else {
+    /* remove ACK flags from the PCB, as we sent an empty ACK now */
+    pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW);
+  }
+
+  return err;
+}
+
+/**
+ * @ingroup tcp_raw
+ * Find out what we can send and send it
+ *
+ * @param pcb Protocol control block for the TCP connection to send data
+ * @return ERR_OK if data has been sent or nothing to send
+ *         another err_t on error
+ */
+err_t
+tcp_output(struct tcp_pcb *pcb)
+{
+  struct tcp_seg *seg, *useg;
+  u32_t wnd, snd_nxt;
+  err_t err;
+  struct netif *netif;
+#if TCP_CWND_DEBUG
+  s16_t i = 0;
+#endif /* TCP_CWND_DEBUG */
+
+  /* pcb->state LISTEN not allowed here */
+  LWIP_ASSERT("don't call tcp_output for listen-pcbs",
+    pcb->state != LISTEN);
+
+  /* First, check if we are invoked by the TCP input processing
+     code. If so, we do not output anything. Instead, we rely on the
+     input processing code to call us when input processing is done
+     with. */
+  if (tcp_input_pcb == pcb) {
+    return ERR_OK;
+  }
+
+  wnd = LWIP_MIN(pcb->snd_wnd, pcb->cwnd);
+
+  seg = pcb->unsent;
+
+  /* If 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 ||
+      lwip_ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len > wnd)) {
+     return tcp_send_empty_ack(pcb);
+  }
+
+  /* useg should point to last segment on unacked queue */
+  useg = pcb->unacked;
+  if (useg != NULL) {
+    for (; useg->next != NULL; useg = useg->next);
+  }
+
+  netif = ip_route(&pcb->local_ip, &pcb->remote_ip);
+  if (netif == NULL) {
+    return ERR_RTE;
+  }
+
+  /* If we don't have a local IP address, we get one from netif */
+  if (ip_addr_isany(&pcb->local_ip)) {
+    const ip_addr_t *local_ip = ip_netif_get_local_ip(netif, &pcb->remote_ip);
+    if (local_ip == NULL) {
+      return ERR_RTE;
+    }
+    ip_addr_copy(pcb->local_ip, *local_ip);
+  }
+
+#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 %"TCPWNDSIZE_F
+                                 ", cwnd %"TCPWNDSIZE_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 %"TCPWNDSIZE_F", cwnd %"TCPWNDSIZE_F", wnd %"U32_F
+                 ", effwnd %"U32_F", seq %"U32_F", ack %"U32_F"\n",
+                 pcb->snd_wnd, pcb->cwnd, wnd,
+                 lwip_ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len,
+                 lwip_ntohl(seg->tcphdr->seqno), pcb->lastack));
+  }
+#endif /* TCP_CWND_DEBUG */
+  /* Check if we need to start the persistent timer when the next unsent segment
+   * does not fit within the remaining send window and RTO timer is not running (we
+   * have no in-flight data). A traditional approach would fill the remaining window
+   * with part of the unsent segment (which will engage zero-window probing upon
+   * reception of the zero window update from the receiver). This ensures the
+   * subsequent window update is reliably received. With the goal of being lightweight,
+   * we avoid splitting the unsent segment and treat the window as already zero.
+   */
+  if (seg != NULL &&
+      lwip_ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len > wnd &&
+      wnd > 0 && wnd == pcb->snd_wnd && pcb->unacked == NULL) {
+    /* Start the persist timer */
+    if (pcb->persist_backoff == 0) {
+      pcb->persist_cnt = 0;
+      pcb->persist_backoff = 1;
+    }
+    goto output_done;
+  }
+  /* data available and window allows it to be sent? */
+  while (seg != NULL &&
+         lwip_ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len <= wnd) {
+    LWIP_ASSERT("RST not expected here!",
+                (TCPH_FLAGS(seg->tcphdr) & TCP_RST) == 0);
+    /* Stop sending if the nagle algorithm would prevent it
+     * Don't stop:
+     * - if tcp_write had a memory error before (prevent delayed ACK timeout) or
+     * - if FIN was already enqueued for this PCB (SYN is always alone in a segment -
+     *   either seg->next != NULL or pcb->unacked == NULL;
+     *   RST is no sent using tcp_write/tcp_output.
+     */
+    if ((tcp_do_output_nagle(pcb) == 0) &&
+      ((pcb->flags & (TF_NAGLEMEMERR | TF_FIN)) == 0)) {
+      break;
+    }
+#if TCP_CWND_DEBUG
+    LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_output: snd_wnd %"TCPWNDSIZE_F", cwnd %"TCPWNDSIZE_F", wnd %"U32_F", effwnd %"U32_F", seq %"U32_F", ack %"U32_F", i %"S16_F"\n",
+                            pcb->snd_wnd, pcb->cwnd, wnd,
+                            lwip_ntohl(seg->tcphdr->seqno) + seg->len -
+                            pcb->lastack,
+                            lwip_ntohl(seg->tcphdr->seqno), pcb->lastack, i));
+    ++i;
+#endif /* TCP_CWND_DEBUG */
+
+    if (pcb->state != SYN_SENT) {
+      TCPH_SET_FLAG(seg->tcphdr, TCP_ACK);
+    }
+
+#if TCP_OVERSIZE_DBGCHECK
+    seg->oversize_left = 0;
+#endif /* TCP_OVERSIZE_DBGCHECK */
+    err = tcp_output_segment(seg, pcb, netif);
+    if (err != ERR_OK) {
+      /* segment could not be sent, for whatever reason */
+      pcb->flags |= TF_NAGLEMEMERR;
+      return err;
+    }
+    pcb->unsent = seg->next;
+    if (pcb->state != SYN_SENT) {
+      pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW);
+    }
+    snd_nxt = lwip_ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg);
+    if (TCP_SEQ_LT(pcb->snd_nxt, snd_nxt)) {
+      pcb->snd_nxt = snd_nxt;
+    }
+    /* put segment on unacknowledged list if length > 0 */
+    if (TCP_TCPLEN(seg) > 0) {
+      seg->next = NULL;
+      /* unacked list is empty? */
+      if (pcb->unacked == NULL) {
+        pcb->unacked = seg;
+        useg = seg;
+      /* unacked list is not empty? */
+      } else {
+        /* In the case of fast retransmit, the packet should not go to the tail
+         * of the unacked queue, but rather somewhere before it. We need to check for
+         * this case. -STJ Jul 27, 2004 */
+        if (TCP_SEQ_LT(lwip_ntohl(seg->tcphdr->seqno), lwip_ntohl(useg->tcphdr->seqno))) {
+          /* add segment to before tail of unacked list, keeping the list sorted */
+          struct tcp_seg **cur_seg = &(pcb->unacked);
+          while (*cur_seg &&
+            TCP_SEQ_LT(lwip_ntohl((*cur_seg)->tcphdr->seqno), lwip_ntohl(seg->tcphdr->seqno))) {
+              cur_seg = &((*cur_seg)->next );
+          }
+          seg->next = (*cur_seg);
+          (*cur_seg) = seg;
+        } else {
+          /* add segment to tail of unacked list */
+          useg->next = seg;
+          useg = useg->next;
+        }
+      }
+    /* do not queue empty segments on the unacked list */
+    } else {
+      tcp_seg_free(seg);
+    }
+    seg = pcb->unsent;
+  }
+output_done:
+#if TCP_OVERSIZE
+  if (pcb->unsent == NULL) {
+    /* last unsent has been removed, reset unsent_oversize */
+    pcb->unsent_oversize = 0;
+  }
+#endif /* TCP_OVERSIZE */
+
+  pcb->flags &= ~TF_NAGLEMEMERR;
+  return ERR_OK;
+}
+
+/**
+ * Called by tcp_output() to actually send a TCP segment over IP.
+ *
+ * @param seg the tcp_seg to send
+ * @param pcb the tcp_pcb for the TCP connection used to send the segment
+ * @param netif the netif used to send the segment
+ */
+static err_t
+tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb, struct netif *netif)
+{
+  err_t err;
+  u16_t len;
+  u32_t *opts;
+
+  if (seg->p->ref != 1) {
+    /* This can happen if the pbuf of this segment is still referenced by the
+       netif driver due to deferred transmission. Since this function modifies
+       p->len, we must not continue in this case. */
+    return ERR_OK;
+  }
+
+  /* The TCP header has already been constructed, but the ackno and
+   wnd fields remain. */
+  seg->tcphdr->ackno = lwip_htonl(pcb->rcv_nxt);
+
+  /* advertise our receive window size in this TCP segment */
+#if LWIP_WND_SCALE
+  if (seg->flags & TF_SEG_OPTS_WND_SCALE) {
+    /* The Window field in a SYN segment itself (the only type where we send
+       the window scale option) is never scaled. */
+    seg->tcphdr->wnd = lwip_htons(TCPWND_MIN16(pcb->rcv_ann_wnd));
+  } else
+#endif /* LWIP_WND_SCALE */
+  {
+    seg->tcphdr->wnd = lwip_htons(TCPWND_MIN16(RCV_WND_SCALE(pcb, pcb->rcv_ann_wnd)));
+  }
+
+  pcb->rcv_ann_right_edge = pcb->rcv_nxt + pcb->rcv_ann_wnd;
+
+  /* Add any requested options.  NB MSS option is only set on SYN
+     packets, so ignore it here */
+  /* cast through void* to get rid of alignment warnings */
+  opts = (u32_t *)(void *)(seg->tcphdr + 1);
+  if (seg->flags & TF_SEG_OPTS_MSS) {
+    u16_t mss;
+#if TCP_CALCULATE_EFF_SEND_MSS
+    mss = tcp_eff_send_mss(TCP_MSS, &pcb->local_ip, &pcb->remote_ip);
+#else /* TCP_CALCULATE_EFF_SEND_MSS */
+    mss = TCP_MSS;
+#endif /* TCP_CALCULATE_EFF_SEND_MSS */
+    *opts = TCP_BUILD_MSS_OPTION(mss);
+    opts += 1;
+  }
+#if LWIP_TCP_TIMESTAMPS
+  pcb->ts_lastacksent = pcb->rcv_nxt;
+
+  if (seg->flags & TF_SEG_OPTS_TS) {
+    tcp_build_timestamp_option(pcb, opts);
+    opts += 3;
+  }
+#endif
+#if LWIP_WND_SCALE
+  if (seg->flags & TF_SEG_OPTS_WND_SCALE) {
+    tcp_build_wnd_scale_option(opts);
+    opts += 1;
+  }
+#endif
+
+  /* Set retransmission timer running if it is not currently enabled
+     This must be set before checking the route. */
+  if (pcb->rtime < 0) {
+    pcb->rtime = 0;
+  }
+
+  if (pcb->rttest == 0) {
+    pcb->rttest = tcp_ticks;
+    pcb->rtseq = lwip_ntohl(seg->tcphdr->seqno);
+
+    LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_output_segment: rtseq %"U32_F"\n", pcb->rtseq));
+  }
+  LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output_segment: %"U32_F":%"U32_F"\n",
+          lwip_htonl(seg->tcphdr->seqno), lwip_htonl(seg->tcphdr->seqno) +
+          seg->len));
+
+  len = (u16_t)((u8_t *)seg->tcphdr - (u8_t *)seg->p->payload);
+  if (len == 0) {
+    /** Exclude retransmitted segments from this count. */
+    MIB2_STATS_INC(mib2.tcpoutsegs);
+  }
+
+  seg->p->len -= len;
+  seg->p->tot_len -= len;
+
+  seg->p->payload = seg->tcphdr;
+
+  seg->tcphdr->chksum = 0;
+#if CHECKSUM_GEN_TCP
+  IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_TCP) {
+#if TCP_CHECKSUM_ON_COPY
+    u32_t acc;
+#if TCP_CHECKSUM_ON_COPY_SANITY_CHECK
+    u16_t chksum_slow = ip_chksum_pseudo(seg->p, IP_PROTO_TCP,
+      seg->p->tot_len, &pcb->local_ip, &pcb->remote_ip);
+#endif /* TCP_CHECKSUM_ON_COPY_SANITY_CHECK */
+    if ((seg->flags & TF_SEG_DATA_CHECKSUMMED) == 0) {
+      LWIP_ASSERT("data included but not checksummed",
+        seg->p->tot_len == (TCPH_HDRLEN(seg->tcphdr) * 4));
+    }
+
+    /* rebuild TCP header checksum (TCP header changes for retransmissions!) */
+    acc = ip_chksum_pseudo_partial(seg->p, IP_PROTO_TCP,
+      seg->p->tot_len, TCPH_HDRLEN(seg->tcphdr) * 4, &pcb->local_ip, &pcb->remote_ip);
+    /* add payload checksum */
+    if (seg->chksum_swapped) {
+      seg->chksum = SWAP_BYTES_IN_WORD(seg->chksum);
+      seg->chksum_swapped = 0;
+    }
+    acc += (u16_t)~(seg->chksum);
+    seg->tcphdr->chksum = FOLD_U32T(acc);
+#if TCP_CHECKSUM_ON_COPY_SANITY_CHECK
+    if (chksum_slow != seg->tcphdr->chksum) {
+      TCP_CHECKSUM_ON_COPY_SANITY_CHECK_FAIL(
+                  ("tcp_output_segment: calculated checksum is %"X16_F" instead of %"X16_F"\n",
+                  seg->tcphdr->chksum, chksum_slow));
+      seg->tcphdr->chksum = chksum_slow;
+    }
+#endif /* TCP_CHECKSUM_ON_COPY_SANITY_CHECK */
+#else /* TCP_CHECKSUM_ON_COPY */
+    seg->tcphdr->chksum = ip_chksum_pseudo(seg->p, IP_PROTO_TCP,
+      seg->p->tot_len, &pcb->local_ip, &pcb->remote_ip);
+#endif /* TCP_CHECKSUM_ON_COPY */
+  }
+#endif /* CHECKSUM_GEN_TCP */
+  TCP_STATS_INC(tcp.xmit);
+
+  NETIF_SET_HWADDRHINT(netif, &(pcb->addr_hint));
+  err = ip_output_if(seg->p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl,
+    pcb->tos, IP_PROTO_TCP, netif);
+  NETIF_SET_HWADDRHINT(netif, NULL);
+  return err;
+}
+
+/**
+ * Send a TCP RESET packet (empty segment with RST flag set) either to
+ * abort a connection or to show that there is no matching local connection
+ * for a received segment.
+ *
+ * Called by tcp_abort() (to abort a local connection), tcp_input() (if no
+ * matching local pcb was found), tcp_listen_input() (if incoming segment
+ * has ACK flag set) and tcp_process() (received segment in the wrong state)
+ *
+ * Since a RST segment is in most cases not sent for an active connection,
+ * tcp_rst() has a number of arguments that are taken from a tcp_pcb for
+ * most other segment output functions.
+ *
+ * @param seqno the sequence number to use for the outgoing segment
+ * @param ackno the acknowledge number to use for the outgoing segment
+ * @param local_ip the local IP address to send the segment from
+ * @param remote_ip the remote IP address to send the segment to
+ * @param local_port the local TCP port to send the segment from
+ * @param remote_port the remote TCP port to send the segment to
+ */
+void
+tcp_rst(u32_t seqno, u32_t ackno,
+  const ip_addr_t *local_ip, const ip_addr_t *remote_ip,
+  u16_t local_port, u16_t remote_port)
+{
+  struct pbuf *p;
+  struct tcp_hdr *tcphdr;
+  struct netif *netif;
+  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;
+  }
+  LWIP_ASSERT("check that first pbuf can hold struct tcp_hdr",
+              (p->len >= sizeof(struct tcp_hdr)));
+
+  tcphdr = (struct tcp_hdr *)p->payload;
+  tcphdr->src = lwip_htons(local_port);
+  tcphdr->dest = lwip_htons(remote_port);
+  tcphdr->seqno = lwip_htonl(seqno);
+  tcphdr->ackno = lwip_htonl(ackno);
+  TCPH_HDRLEN_FLAGS_SET(tcphdr, TCP_HLEN/4, TCP_RST | TCP_ACK);
+#if LWIP_WND_SCALE
+  tcphdr->wnd = PP_HTONS(((TCP_WND >> TCP_RCV_SCALE) & 0xFFFF));
+#else
+  tcphdr->wnd = PP_HTONS(TCP_WND);
+#endif
+  tcphdr->chksum = 0;
+  tcphdr->urgp = 0;
+
+  TCP_STATS_INC(tcp.xmit);
+  MIB2_STATS_INC(mib2.tcpoutrsts);
+
+  netif = ip_route(local_ip, remote_ip);
+  if (netif != NULL) {
+#if CHECKSUM_GEN_TCP
+    IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_TCP) {
+      tcphdr->chksum = ip_chksum_pseudo(p, IP_PROTO_TCP, p->tot_len,
+                                        local_ip, remote_ip);
+    }
+#endif
+    /* Send output with hardcoded TTL/HL since we have no access to the pcb */
+    ip_output_if(p, local_ip, remote_ip, TCP_TTL, 0, IP_PROTO_TCP, netif);
+  }
+  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
+ *
+ * Called by tcp_slowtmr() for slow retransmission.
+ *
+ * @param pcb the tcp_pcb for which to re-enqueue all unacked segments
+ */
+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;
+#if TCP_OVERSIZE_DBGCHECK
+  /* if last unsent changed, we need to update unsent_oversize */
+  if (pcb->unsent == NULL) {
+    pcb->unsent_oversize = seg->oversize_left;
+  }
+#endif /* TCP_OVERSIZE_DBGCHECK */
+  /* unsent queue is the concatenated queue (of unacked, unsent) */
+  pcb->unsent = pcb->unacked;
+  /* unacked queue is now empty */
+  pcb->unacked = NULL;
+
+  /* increment number of retransmissions */
+  if (pcb->nrtx < 0xFF) {
+    ++pcb->nrtx;
+  }
+
+  /* Don't take any RTT measurements after retransmitting. */
+  pcb->rttest = 0;
+
+  /* Do the actual retransmission */
+  tcp_output(pcb);
+}
+
+/**
+ * Requeue the first unacked segment for retransmission
+ *
+ * Called by tcp_receive() for fast retransmit.
+ *
+ * @param pcb the tcp_pcb for which to retransmit the first unacked segment
+ */
+void
+tcp_rexmit(struct tcp_pcb *pcb)
+{
+  struct tcp_seg *seg;
+  struct tcp_seg **cur_seg;
+
+  if (pcb->unacked == NULL) {
+    return;
+  }
+
+  /* Move the first unacked segment to the unsent queue */
+  /* Keep the unsent queue sorted. */
+  seg = pcb->unacked;
+  pcb->unacked = seg->next;
+
+  cur_seg = &(pcb->unsent);
+  while (*cur_seg &&
+    TCP_SEQ_LT(lwip_ntohl((*cur_seg)->tcphdr->seqno), lwip_ntohl(seg->tcphdr->seqno))) {
+      cur_seg = &((*cur_seg)->next );
+  }
+  seg->next = *cur_seg;
+  *cur_seg = seg;
+#if TCP_OVERSIZE
+  if (seg->next == NULL) {
+    /* the retransmitted segment is last in unsent, so reset unsent_oversize */
+    pcb->unsent_oversize = 0;
+  }
+#endif /* TCP_OVERSIZE */
+
+  if (pcb->nrtx < 0xFF) {
+    ++pcb->nrtx;
+  }
+
+  /* Don't take any rtt measurements after retransmitting. */
+  pcb->rttest = 0;
+
+  /* Do the actual retransmission. */
+  MIB2_STATS_INC(mib2.tcpretranssegs);
+  /* No need to call tcp_output: we are always called from tcp_input()
+     and thus tcp_output directly returns. */
+}
+
+
+/**
+ * Handle retransmission after three dupacks received
+ *
+ * @param pcb the tcp_pcb for which to retransmit the first unacked segment
+ */
+void
+tcp_rexmit_fast(struct tcp_pcb *pcb)
+{
+  if (pcb->unacked != NULL && !(pcb->flags & TF_INFR)) {
+    /* This is fast retransmit. Retransmit the first unacked segment. */
+    LWIP_DEBUGF(TCP_FR_DEBUG,
+                ("tcp_receive: dupacks %"U16_F" (%"U32_F
+                 "), fast retransmit %"U32_F"\n",
+                 (u16_t)pcb->dupacks, pcb->lastack,
+                 lwip_ntohl(pcb->unacked->tcphdr->seqno)));
+    tcp_rexmit(pcb);
+
+    /* Set ssthresh to half of the minimum of the current
+     * cwnd and the advertised window */
+    pcb->ssthresh = LWIP_MIN(pcb->cwnd, pcb->snd_wnd) / 2;
+
+    /* The minimum value for ssthresh should be 2 MSS */
+    if (pcb->ssthresh < (2U * pcb->mss)) {
+      LWIP_DEBUGF(TCP_FR_DEBUG,
+                  ("tcp_receive: The minimum value for ssthresh %"TCPWNDSIZE_F
+                   " should be min 2 mss %"U16_F"...\n",
+                   pcb->ssthresh, (u16_t)(2*pcb->mss)));
+      pcb->ssthresh = 2*pcb->mss;
+    }
+
+    pcb->cwnd = pcb->ssthresh + 3 * pcb->mss;
+    pcb->flags |= TF_INFR;
+
+    /* Reset the retransmission timer to prevent immediate rto retransmissions */
+    pcb->rtime = 0;
+  }
+}
+
+
+/**
+ * Send keepalive packets to keep a connection active although
+ * no data is sent over it.
+ *
+ * Called by tcp_slowtmr()
+ *
+ * @param pcb the tcp_pcb for which to send a keepalive packet
+ */
+err_t
+tcp_keepalive(struct tcp_pcb *pcb)
+{
+  err_t err;
+  struct pbuf *p;
+  struct netif *netif;
+
+  LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: sending KEEPALIVE probe to "));
+  ip_addr_debug_print(TCP_DEBUG, &pcb->remote_ip);
+  LWIP_DEBUGF(TCP_DEBUG, ("\n"));
+
+  LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: tcp_ticks %"U32_F"   pcb->tmr %"U32_F" pcb->keep_cnt_sent %"U16_F"\n",
+                          tcp_ticks, pcb->tmr, (u16_t)pcb->keep_cnt_sent));
+
+  p = tcp_output_alloc_header(pcb, 0, 0, lwip_htonl(pcb->snd_nxt - 1));
+  if (p == NULL) {
+    LWIP_DEBUGF(TCP_DEBUG,
+                ("tcp_keepalive: could not allocate memory for pbuf\n"));
+    return ERR_MEM;
+  }
+  netif = ip_route(&pcb->local_ip, &pcb->remote_ip);
+  if (netif == NULL) {
+    err = ERR_RTE;
+  } else {
+#if CHECKSUM_GEN_TCP
+    IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_TCP) {
+      struct tcp_hdr *tcphdr = (struct tcp_hdr *)p->payload;
+      tcphdr->chksum = ip_chksum_pseudo(p, IP_PROTO_TCP, p->tot_len,
+        &pcb->local_ip, &pcb->remote_ip);
+    }
+#endif /* CHECKSUM_GEN_TCP */
+    TCP_STATS_INC(tcp.xmit);
+
+    /* Send output to IP */
+    NETIF_SET_HWADDRHINT(netif, &(pcb->addr_hint));
+    err = ip_output_if(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP, netif);
+    NETIF_SET_HWADDRHINT(netif, NULL);
+  }
+  pbuf_free(p);
+
+  LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: seqno %"U32_F" ackno %"U32_F" err %d.\n",
+                          pcb->snd_nxt - 1, pcb->rcv_nxt, (int)err));
+  return err;
+}
+
+
+/**
+ * Send persist timer zero-window probes to keep a connection active
+ * when a window update is lost.
+ *
+ * Called by tcp_slowtmr()
+ *
+ * @param pcb the tcp_pcb for which to send a zero-window probe packet
+ */
+err_t
+tcp_zero_window_probe(struct tcp_pcb *pcb)
+{
+  err_t err;
+  struct pbuf *p;
+  struct tcp_hdr *tcphdr;
+  struct tcp_seg *seg;
+  u16_t len;
+  u8_t is_fin;
+  u32_t snd_nxt;
+  struct netif *netif;
+
+  LWIP_DEBUGF(TCP_DEBUG, ("tcp_zero_window_probe: sending ZERO WINDOW probe to "));
+  ip_addr_debug_print(TCP_DEBUG, &pcb->remote_ip);
+  LWIP_DEBUGF(TCP_DEBUG, ("\n"));
+
+  LWIP_DEBUGF(TCP_DEBUG,
+              ("tcp_zero_window_probe: tcp_ticks %"U32_F
+               "   pcb->tmr %"U32_F" pcb->keep_cnt_sent %"U16_F"\n",
+               tcp_ticks, pcb->tmr, (u16_t)pcb->keep_cnt_sent));
+
+  seg = pcb->unacked;
+
+  if (seg == NULL) {
+    seg = pcb->unsent;
+  }
+  if (seg == NULL) {
+    /* nothing to send, zero window probe not needed */
+    return ERR_OK;
+  }
+
+  is_fin = ((TCPH_FLAGS(seg->tcphdr) & TCP_FIN) != 0) && (seg->len == 0);
+  /* we want to send one seqno: either FIN or data (no options) */
+  len = is_fin ? 0 : 1;
+
+  p = tcp_output_alloc_header(pcb, 0, len, seg->tcphdr->seqno);
+  if (p == NULL) {
+    LWIP_DEBUGF(TCP_DEBUG, ("tcp_zero_window_probe: no memory for pbuf\n"));
+    return ERR_MEM;
+  }
+  tcphdr = (struct tcp_hdr *)p->payload;
+
+  if (is_fin) {
+    /* FIN segment, no data */
+    TCPH_FLAGS_SET(tcphdr, TCP_ACK | TCP_FIN);
+  } else {
+    /* Data segment, copy in one byte from the head of the unacked queue */
+    char *d = ((char *)p->payload + TCP_HLEN);
+    /* Depending on whether the segment has already been sent (unacked) or not
+       (unsent), seg->p->payload points to the IP header or TCP header.
+       Ensure we copy the first TCP data byte: */
+    pbuf_copy_partial(seg->p, d, 1, seg->p->tot_len - seg->len);
+  }
+
+  /* The byte may be acknowledged without the window being opened. */
+  snd_nxt = lwip_ntohl(seg->tcphdr->seqno) + 1;
+  if (TCP_SEQ_LT(pcb->snd_nxt, snd_nxt)) {
+    pcb->snd_nxt = snd_nxt;
+  }
+
+  netif = ip_route(&pcb->local_ip, &pcb->remote_ip);
+  if (netif == NULL) {
+    err = ERR_RTE;
+  } else {
+#if CHECKSUM_GEN_TCP
+    IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_TCP) {
+      tcphdr->chksum = ip_chksum_pseudo(p, IP_PROTO_TCP, p->tot_len,
+        &pcb->local_ip, &pcb->remote_ip);
+    }
+#endif
+    TCP_STATS_INC(tcp.xmit);
+
+    /* Send output to IP */
+    NETIF_SET_HWADDRHINT(netif, &(pcb->addr_hint));
+    err = ip_output_if(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl,
+      0, IP_PROTO_TCP, netif);
+    NETIF_SET_HWADDRHINT(netif, NULL);
+  }
+
+  pbuf_free(p);
+
+  LWIP_DEBUGF(TCP_DEBUG, ("tcp_zero_window_probe: seqno %"U32_F
+                          " ackno %"U32_F" err %d.\n",
+                          pcb->snd_nxt - 1, pcb->rcv_nxt, (int)err));
+  return err;
+}
+#endif /* LWIP_TCP */

+ 433 - 487
thirdparty/lwip/src/core/timers.c → thirdparty/lwip/src/core/timeouts.c

@@ -1,487 +1,433 @@
-/**
- * @file
- * Stack-internal timers implementation.
- * This file includes timer callbacks for stack-internal timers as well as
- * functions to set up or stop timers and check for expired timers.
- *
- */
-
-/*
- * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * This file is part of the lwIP TCP/IP stack.
- *
- * Author: Adam Dunkels <adam@sics.se>
- *         Simon Goldschmidt
- *
- */
-
-#include "lwip/opt.h"
-
-#include "lwip/timers.h"
-#include "lwip/tcp_impl.h"
-
-#if LWIP_TIMERS
-
-#include "lwip/def.h"
-#include "lwip/memp.h"
-#include "lwip/tcpip.h"
-
-#include "lwip/ip_frag.h"
-#include "netif/etharp.h"
-#include "lwip/dhcp.h"
-#include "lwip/autoip.h"
-#include "lwip/igmp.h"
-#include "lwip/dns.h"
-#include "lwip/sys.h"
-#include "lwip/pbuf.h"
-
-
-/** The one and only timeout list */
-static struct sys_timeo *next_timeout;
-#if NO_SYS
-static u32_t timeouts_last_time;
-#endif /* NO_SYS */
-
-#if LWIP_TCP
-/** global variable that shows if the tcp timer is currently scheduled or not */
-static int tcpip_tcp_timer_active;
-
-/**
- * Timer callback function that calls tcp_tmr() and reschedules itself.
- *
- * @param arg unused argument
- */
-static void
-tcpip_tcp_timer(void *arg)
-{
-  LWIP_UNUSED_ARG(arg);
-
-  /* call TCP timer handler */
-  tcp_tmr();
-  /* timer still needed? */
-  if (tcp_active_pcbs || tcp_tw_pcbs) {
-    /* restart timer */
-    sys_timeout(TCP_TMR_INTERVAL, tcpip_tcp_timer, NULL);
-  } else {
-    /* disable timer */
-    tcpip_tcp_timer_active = 0;
-  }
-}
-
-/**
- * Called from TCP_REG when registering a new PCB:
- * the reason is to have the TCP timer only running when
- * there are active (or time-wait) PCBs.
- */
-void
-tcp_timer_needed(void)
-{
-  /* timer is off but needed again? */
-  if (!tcpip_tcp_timer_active && (tcp_active_pcbs || tcp_tw_pcbs)) {
-    /* enable and start timer */
-    tcpip_tcp_timer_active = 1;
-    sys_timeout(TCP_TMR_INTERVAL, tcpip_tcp_timer, NULL);
-  }
-}
-#endif /* LWIP_TCP */
-
-#if IP_REASSEMBLY
-/**
- * Timer callback function that calls ip_reass_tmr() and reschedules itself.
- *
- * @param arg unused argument
- */
-static void
-ip_reass_timer(void *arg)
-{
-  LWIP_UNUSED_ARG(arg);
-  LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: ip_reass_tmr()\n"));
-  ip_reass_tmr();
-  sys_timeout(IP_TMR_INTERVAL, ip_reass_timer, NULL);
-}
-#endif /* IP_REASSEMBLY */
-
-#if LWIP_ARP
-/**
- * Timer callback function that calls etharp_tmr() and reschedules itself.
- *
- * @param arg unused argument
- */
-static void
-arp_timer(void *arg)
-{
-  LWIP_UNUSED_ARG(arg);
-  LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: etharp_tmr()\n"));
-  etharp_tmr();
-  sys_timeout(ARP_TMR_INTERVAL, arp_timer, NULL);
-}
-#endif /* LWIP_ARP */
-
-#if LWIP_DHCP
-/**
- * Timer callback function that calls dhcp_coarse_tmr() and reschedules itself.
- *
- * @param arg unused argument
- */
-static void
-dhcp_timer_coarse(void *arg)
-{
-  LWIP_UNUSED_ARG(arg);
-  LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: dhcp_coarse_tmr()\n"));
-  dhcp_coarse_tmr();
-  sys_timeout(DHCP_COARSE_TIMER_MSECS, dhcp_timer_coarse, NULL);
-}
-
-/**
- * Timer callback function that calls dhcp_fine_tmr() and reschedules itself.
- *
- * @param arg unused argument
- */
-static void
-dhcp_timer_fine(void *arg)
-{
-  LWIP_UNUSED_ARG(arg);
-  LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: dhcp_fine_tmr()\n"));
-  dhcp_fine_tmr();
-  sys_timeout(DHCP_FINE_TIMER_MSECS, dhcp_timer_fine, NULL);
-}
-#endif /* LWIP_DHCP */
-
-#if LWIP_AUTOIP
-/**
- * Timer callback function that calls autoip_tmr() and reschedules itself.
- *
- * @param arg unused argument
- */
-static void
-autoip_timer(void *arg)
-{
-  LWIP_UNUSED_ARG(arg);
-  LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: autoip_tmr()\n"));
-  autoip_tmr();
-  sys_timeout(AUTOIP_TMR_INTERVAL, autoip_timer, NULL);
-}
-#endif /* LWIP_AUTOIP */
-
-#if LWIP_IGMP
-/**
- * Timer callback function that calls igmp_tmr() and reschedules itself.
- *
- * @param arg unused argument
- */
-static void
-igmp_timer(void *arg)
-{
-  LWIP_UNUSED_ARG(arg);
-  LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: igmp_tmr()\n"));
-  igmp_tmr();
-  sys_timeout(IGMP_TMR_INTERVAL, igmp_timer, NULL);
-}
-#endif /* LWIP_IGMP */
-
-#if LWIP_DNS
-/**
- * Timer callback function that calls dns_tmr() and reschedules itself.
- *
- * @param arg unused argument
- */
-static void
-dns_timer(void *arg)
-{
-  LWIP_UNUSED_ARG(arg);
-  LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: dns_tmr()\n"));
-  dns_tmr();
-  sys_timeout(DNS_TMR_INTERVAL, dns_timer, NULL);
-}
-#endif /* LWIP_DNS */
-
-/** Initialize this module */
-void sys_timeouts_init(void)
-{
-#if IP_REASSEMBLY
-  sys_timeout(IP_TMR_INTERVAL, ip_reass_timer, NULL);
-#endif /* IP_REASSEMBLY */
-#if LWIP_ARP
-  sys_timeout(ARP_TMR_INTERVAL, arp_timer, NULL);
-#endif /* LWIP_ARP */
-#if LWIP_DHCP
-  sys_timeout(DHCP_COARSE_TIMER_MSECS, dhcp_timer_coarse, NULL);
-  sys_timeout(DHCP_FINE_TIMER_MSECS, dhcp_timer_fine, NULL);
-#endif /* LWIP_DHCP */
-#if LWIP_AUTOIP
-  sys_timeout(AUTOIP_TMR_INTERVAL, autoip_timer, NULL);
-#endif /* LWIP_AUTOIP */
-#if LWIP_IGMP
-  sys_timeout(IGMP_TMR_INTERVAL, igmp_timer, NULL);
-#endif /* LWIP_IGMP */
-#if LWIP_DNS
-  sys_timeout(DNS_TMR_INTERVAL, dns_timer, NULL);
-#endif /* LWIP_DNS */
-
-#if NO_SYS
-  /* Initialise timestamp for sys_check_timeouts */
-  timeouts_last_time = sys_now();
-#endif
-}
-
-/**
- * Create a one-shot timer (aka timeout). Timeouts are processed in the
- * following cases:
- * - while waiting for a message using sys_timeouts_mbox_fetch()
- * - by calling sys_check_timeouts() (NO_SYS==1 only)
- *
- * @param msecs time in milliseconds after that the timer should expire
- * @param handler callback function to call when msecs have elapsed
- * @param arg argument to pass to the callback function
- */
-#if LWIP_DEBUG_TIMERNAMES
-void
-sys_timeout_debug(u32_t msecs, sys_timeout_handler handler, void *arg, const char* handler_name)
-#else /* LWIP_DEBUG_TIMERNAMES */
-void
-sys_timeout(u32_t msecs, sys_timeout_handler handler, void *arg)
-#endif /* LWIP_DEBUG_TIMERNAMES */
-{
-  struct sys_timeo *timeout, *t;
-
-  timeout = (struct sys_timeo *)memp_malloc(MEMP_SYS_TIMEOUT);
-  if (timeout == NULL) {
-    LWIP_ASSERT("sys_timeout: timeout != NULL, pool MEMP_SYS_TIMEOUT is empty", timeout != NULL);
-    return;
-  }
-  timeout->next = NULL;
-  timeout->h = handler;
-  timeout->arg = arg;
-  timeout->time = msecs;
-#if LWIP_DEBUG_TIMERNAMES
-  timeout->handler_name = handler_name;
-  LWIP_DEBUGF(TIMERS_DEBUG, ("sys_timeout: %p msecs=%"U32_F" handler=%s arg=%p\n",
-    (void *)timeout, msecs, handler_name, (void *)arg));
-#endif /* LWIP_DEBUG_TIMERNAMES */
-
-  if (next_timeout == NULL) {
-    next_timeout = timeout;
-    return;
-  }
-
-  if (next_timeout->time > msecs) {
-    next_timeout->time -= msecs;
-    timeout->next = next_timeout;
-    next_timeout = timeout;
-  } else {
-    for(t = next_timeout; 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.
- *
- * @note This function only works as expected if there is only one timeout
- * calling 'handler' in the list of timeouts.
- *
- * @param handler callback function that would be called by the timeout
- * @param arg callback argument that would be passed to handler
-*/
-void
-sys_untimeout(sys_timeout_handler handler, void *arg)
-{
-  struct sys_timeo *prev_t, *t;
-
-  if (next_timeout == NULL) {
-    return;
-  }
-
-  for (t = next_timeout, prev_t = NULL; t != NULL; prev_t = t, t = t->next) {
-    if ((t->h == handler) && (t->arg == arg)) {
-      /* We have a match */
-      /* Unlink from previous in list */
-      if (prev_t == NULL) {
-        next_timeout = t->next;
-      } else {
-        prev_t->next = t->next;
-      }
-      /* 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;
-}
-
-#if NO_SYS
-
-/** Handle timeouts for NO_SYS==1 (i.e. without using
- * tcpip_thread/sys_timeouts_mbox_fetch(). Uses sys_now() to call timeout
- * handler functions when timeouts expire.
- *
- * Must be called periodically from your main loop.
- */
-void
-sys_check_timeouts(void)
-{
-  if (next_timeout) {
-    struct sys_timeo *tmptimeout;
-    u32_t diff;
-    sys_timeout_handler handler;
-    void *arg;
-    u8_t had_one;
-    u32_t now;
-
-    now = sys_now();
-    /* this cares for wraparounds */
-    diff = now - timeouts_last_time;
-    do
-    {
-#if PBUF_POOL_FREE_OOSEQ
-      PBUF_CHECK_FREE_OOSEQ();
-#endif /* PBUF_POOL_FREE_OOSEQ */
-      had_one = 0;
-      tmptimeout = next_timeout;
-      if (tmptimeout && (tmptimeout->time <= diff)) {
-        /* timeout has expired */
-        had_one = 1;
-        timeouts_last_time = now;
-        diff -= tmptimeout->time;
-        next_timeout = tmptimeout->next;
-        handler = tmptimeout->h;
-        arg = tmptimeout->arg;
-#if LWIP_DEBUG_TIMERNAMES
-        if (handler != NULL) {
-          LWIP_DEBUGF(TIMERS_DEBUG, ("sct calling h=%s arg=%p\n",
-            tmptimeout->handler_name, arg));
-        }
-#endif /* LWIP_DEBUG_TIMERNAMES */
-        memp_free(MEMP_SYS_TIMEOUT, tmptimeout);
-        if (handler != NULL) {
-          handler(arg);
-        }
-      }
-    /* repeat until all expired timers have been called */
-    }while(had_one);
-  }
-}
-
-/** Set back the timestamp of the last call to sys_check_timeouts()
- * This is necessary if sys_check_timeouts() hasn't been called for a long
- * time (e.g. while saving energy) to prevent all timer functions of that
- * period being called.
- */
-void
-sys_restart_timeouts(void)
-{
-  timeouts_last_time = sys_now();
-}
-
-#else /* NO_SYS */
-
-/**
- * Wait (forever) for a message to arrive in an mbox.
- * While waiting, timeouts are processed.
- *
- * @param mbox the mbox to fetch the message from
- * @param msg the place to store the message
- */
-void
-sys_timeouts_mbox_fetch(sys_mbox_t *mbox, void **msg)
-{
-  u32_t time_needed;
-  struct sys_timeo *tmptimeout;
-  sys_timeout_handler handler;
-  void *arg;
-
- again:
-  if (!next_timeout) {
-    time_needed = sys_arch_mbox_fetch(mbox, msg, 0);
-  } else {
-    if (next_timeout->time > 0) {
-      time_needed = sys_arch_mbox_fetch(mbox, msg, next_timeout->time);
-    } else {
-      time_needed = SYS_ARCH_TIMEOUT;
-    }
-
-    if (time_needed == 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 = next_timeout;
-      next_timeout = tmptimeout->next;
-      handler = tmptimeout->h;
-      arg = tmptimeout->arg;
-#if LWIP_DEBUG_TIMERNAMES
-      if (handler != NULL) {
-        LWIP_DEBUGF(TIMERS_DEBUG, ("stmf calling h=%s arg=%p\n",
-          tmptimeout->handler_name, arg));
-      }
-#endif /* LWIP_DEBUG_TIMERNAMES */
-      memp_free(MEMP_SYS_TIMEOUT, tmptimeout);
-      if (handler != NULL) {
-        /* For LWIP_TCPIP_CORE_LOCKING, lock the core before calling the
-           timeout handler function. */
-        LOCK_TCPIP_CORE();
-        handler(arg);
-        UNLOCK_TCPIP_CORE();
-      }
-      LWIP_TCPIP_THREAD_ALIVE();
-
-      /* 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_needed < next_timeout->time) {
-        next_timeout->time -= time_needed;
-      } else {
-        next_timeout->time = 0;
-      }
-    }
-  }
-}
-
-#endif /* NO_SYS */
-
-#else /* LWIP_TIMERS */
-/* Satisfy the TCP code which calls this function */
-void
-tcp_timer_needed(void)
-{
-}
-#endif /* LWIP_TIMERS */
+/**
+ * @file
+ * Stack-internal timers implementation.
+ * This file includes timer callbacks for stack-internal timers as well as
+ * functions to set up or stop timers and check for expired timers.
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *         Simon Goldschmidt
+ *
+ */
+
+#include "lwip/opt.h"
+
+#include "lwip/timeouts.h"
+#include "lwip/priv/tcp_priv.h"
+
+#include "lwip/def.h"
+#include "lwip/memp.h"
+#include "lwip/priv/tcpip_priv.h"
+
+#include "lwip/ip4_frag.h"
+#include "lwip/etharp.h"
+#include "lwip/dhcp.h"
+#include "lwip/autoip.h"
+#include "lwip/igmp.h"
+#include "lwip/dns.h"
+#include "lwip/nd6.h"
+#include "lwip/ip6_frag.h"
+#include "lwip/mld6.h"
+#include "lwip/sys.h"
+#include "lwip/pbuf.h"
+
+#if LWIP_DEBUG_TIMERNAMES
+#define HANDLER(x) x, #x
+#else /* LWIP_DEBUG_TIMERNAMES */
+#define HANDLER(x) x
+#endif /* LWIP_DEBUG_TIMERNAMES */
+
+/** This array contains all stack-internal cyclic timers. To get the number of
+ * timers, use LWIP_ARRAYSIZE() */
+const struct lwip_cyclic_timer lwip_cyclic_timers[] = {
+#if LWIP_TCP
+  /* The TCP timer is a special case: it does not have to run always and
+     is triggered to start from TCP using tcp_timer_needed() */
+  {TCP_TMR_INTERVAL, HANDLER(tcp_tmr)},
+#endif /* LWIP_TCP */
+#if LWIP_IPV4
+#if IP_REASSEMBLY
+  {IP_TMR_INTERVAL, HANDLER(ip_reass_tmr)},
+#endif /* IP_REASSEMBLY */
+#if LWIP_ARP
+  {ARP_TMR_INTERVAL, HANDLER(etharp_tmr)},
+#endif /* LWIP_ARP */
+#if LWIP_DHCP
+  {DHCP_COARSE_TIMER_MSECS, HANDLER(dhcp_coarse_tmr)},
+  {DHCP_FINE_TIMER_MSECS, HANDLER(dhcp_fine_tmr)},
+#endif /* LWIP_DHCP */
+#if LWIP_AUTOIP
+  {AUTOIP_TMR_INTERVAL, HANDLER(autoip_tmr)},
+#endif /* LWIP_AUTOIP */
+#if LWIP_IGMP
+  {IGMP_TMR_INTERVAL, HANDLER(igmp_tmr)},
+#endif /* LWIP_IGMP */
+#endif /* LWIP_IPV4 */
+#if LWIP_DNS
+  {DNS_TMR_INTERVAL, HANDLER(dns_tmr)},
+#endif /* LWIP_DNS */
+#if LWIP_IPV6
+  {ND6_TMR_INTERVAL, HANDLER(nd6_tmr)},
+#if LWIP_IPV6_REASS
+  {IP6_REASS_TMR_INTERVAL, HANDLER(ip6_reass_tmr)},
+#endif /* LWIP_IPV6_REASS */
+#if LWIP_IPV6_MLD
+  {MLD6_TMR_INTERVAL, HANDLER(mld6_tmr)},
+#endif /* LWIP_IPV6_MLD */
+#endif /* LWIP_IPV6 */
+};
+
+#if LWIP_TIMERS && !LWIP_TIMERS_CUSTOM
+
+/** The one and only timeout list */
+static struct sys_timeo *next_timeout;
+static u32_t timeouts_last_time;
+
+#if LWIP_TCP
+/** global variable that shows if the tcp timer is currently scheduled or not */
+static int tcpip_tcp_timer_active;
+
+/**
+ * Timer callback function that calls tcp_tmr() and reschedules itself.
+ *
+ * @param arg unused argument
+ */
+static void
+tcpip_tcp_timer(void *arg)
+{
+  LWIP_UNUSED_ARG(arg);
+
+  /* call TCP timer handler */
+  tcp_tmr();
+  /* timer still needed? */
+  if (tcp_active_pcbs || tcp_tw_pcbs) {
+    /* restart timer */
+    sys_timeout(TCP_TMR_INTERVAL, tcpip_tcp_timer, NULL);
+  } else {
+    /* disable timer */
+    tcpip_tcp_timer_active = 0;
+  }
+}
+
+/**
+ * Called from TCP_REG when registering a new PCB:
+ * the reason is to have the TCP timer only running when
+ * there are active (or time-wait) PCBs.
+ */
+void
+tcp_timer_needed(void)
+{
+  /* timer is off but needed again? */
+  if (!tcpip_tcp_timer_active && (tcp_active_pcbs || tcp_tw_pcbs)) {
+    /* enable and start timer */
+    tcpip_tcp_timer_active = 1;
+    sys_timeout(TCP_TMR_INTERVAL, tcpip_tcp_timer, NULL);
+  }
+}
+#endif /* LWIP_TCP */
+
+/**
+ * Timer callback function that calls mld6_tmr() and reschedules itself.
+ *
+ * @param arg unused argument
+ */
+static void
+cyclic_timer(void *arg)
+{
+  const struct lwip_cyclic_timer* cyclic = (const struct lwip_cyclic_timer*)arg;
+#if LWIP_DEBUG_TIMERNAMES
+  LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: %s()\n", cyclic->handler_name));
+#endif
+  cyclic->handler();
+  sys_timeout(cyclic->interval_ms, cyclic_timer, arg);
+}
+
+/** Initialize this module */
+void sys_timeouts_init(void)
+{
+  size_t i;
+  /* tcp_tmr() at index 0 is started on demand */
+  for (i = 1; i < LWIP_ARRAYSIZE(lwip_cyclic_timers); i++) {
+    /* we have to cast via size_t to get rid of const warning
+      (this is OK as cyclic_timer() casts back to const* */
+    sys_timeout(lwip_cyclic_timers[i].interval_ms, cyclic_timer, LWIP_CONST_CAST(void*, &lwip_cyclic_timers[i]));
+  }
+
+  /* Initialise timestamp for sys_check_timeouts */
+  timeouts_last_time = sys_now();
+}
+
+/**
+ * Create a one-shot timer (aka timeout). Timeouts are processed in the
+ * following cases:
+ * - while waiting for a message using sys_timeouts_mbox_fetch()
+ * - by calling sys_check_timeouts() (NO_SYS==1 only)
+ *
+ * @param msecs time in milliseconds after that the timer should expire
+ * @param handler callback function to call when msecs have elapsed
+ * @param arg argument to pass to the callback function
+ */
+#if LWIP_DEBUG_TIMERNAMES
+void
+sys_timeout_debug(u32_t msecs, sys_timeout_handler handler, void *arg, const char* handler_name)
+#else /* LWIP_DEBUG_TIMERNAMES */
+void
+sys_timeout(u32_t msecs, sys_timeout_handler handler, void *arg)
+#endif /* LWIP_DEBUG_TIMERNAMES */
+{
+  struct sys_timeo *timeout, *t;
+  u32_t now, diff;
+
+  timeout = (struct sys_timeo *)memp_malloc(MEMP_SYS_TIMEOUT);
+  if (timeout == NULL) {
+    LWIP_ASSERT("sys_timeout: timeout != NULL, pool MEMP_SYS_TIMEOUT is empty", timeout != NULL);
+    return;
+  }
+
+  now = sys_now();
+  if (next_timeout == NULL) {
+    diff = 0;
+    timeouts_last_time = now;
+  } else {
+    diff = now - timeouts_last_time;
+  }
+
+  timeout->next = NULL;
+  timeout->h = handler;
+  timeout->arg = arg;
+  timeout->time = msecs + diff;
+#if LWIP_DEBUG_TIMERNAMES
+  timeout->handler_name = handler_name;
+  LWIP_DEBUGF(TIMERS_DEBUG, ("sys_timeout: %p msecs=%"U32_F" handler=%s arg=%p\n",
+    (void *)timeout, msecs, handler_name, (void *)arg));
+#endif /* LWIP_DEBUG_TIMERNAMES */
+
+  if (next_timeout == NULL) {
+    next_timeout = timeout;
+    return;
+  }
+
+  if (next_timeout->time > msecs) {
+    next_timeout->time -= msecs;
+    timeout->next = next_timeout;
+    next_timeout = timeout;
+  } else {
+    for (t = next_timeout; 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;
+        } else if (timeout->time > msecs) {
+          /* If this is the case, 'timeouts_last_time' and 'now' differs too much.
+             This can be due to sys_check_timeouts() not being called at the right
+             times, but also when stopping in a breakpoint. Anyway, let's assume
+             this is not wanted, so add the first timer's time instead of 'diff' */
+          timeout->time = msecs + next_timeout->time;
+        }
+        timeout->next = t->next;
+        t->next = timeout;
+        break;
+      }
+    }
+  }
+}
+
+/**
+ * Go through timeout list (for this task only) and remove the first matching
+ * entry (subsequent entries remain untouched), even though the timeout has not
+ * triggered yet.
+ *
+ * @param handler callback function that would be called by the timeout
+ * @param arg callback argument that would be passed to handler
+*/
+void
+sys_untimeout(sys_timeout_handler handler, void *arg)
+{
+  struct sys_timeo *prev_t, *t;
+
+  if (next_timeout == NULL) {
+    return;
+  }
+
+  for (t = next_timeout, prev_t = NULL; t != NULL; prev_t = t, t = t->next) {
+    if ((t->h == handler) && (t->arg == arg)) {
+      /* We have a match */
+      /* Unlink from previous in list */
+      if (prev_t == NULL) {
+        next_timeout = t->next;
+      } else {
+        prev_t->next = t->next;
+      }
+      /* 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;
+}
+
+/**
+ * @ingroup lwip_nosys
+ * Handle timeouts for NO_SYS==1 (i.e. without using
+ * tcpip_thread/sys_timeouts_mbox_fetch(). Uses sys_now() to call timeout
+ * handler functions when timeouts expire.
+ *
+ * Must be called periodically from your main loop.
+ */
+#if !NO_SYS && !defined __DOXYGEN__
+static
+#endif /* !NO_SYS */
+void
+sys_check_timeouts(void)
+{
+  if (next_timeout) {
+    struct sys_timeo *tmptimeout;
+    u32_t diff;
+    sys_timeout_handler handler;
+    void *arg;
+    u8_t had_one;
+    u32_t now;
+
+    now = sys_now();
+    /* this cares for wraparounds */
+    diff = now - timeouts_last_time;
+    do {
+      PBUF_CHECK_FREE_OOSEQ();
+      had_one = 0;
+      tmptimeout = next_timeout;
+      if (tmptimeout && (tmptimeout->time <= diff)) {
+        /* timeout has expired */
+        had_one = 1;
+        timeouts_last_time += tmptimeout->time;
+        diff -= tmptimeout->time;
+        next_timeout = tmptimeout->next;
+        handler = tmptimeout->h;
+        arg = tmptimeout->arg;
+#if LWIP_DEBUG_TIMERNAMES
+        if (handler != NULL) {
+          LWIP_DEBUGF(TIMERS_DEBUG, ("sct calling h=%s arg=%p\n",
+            tmptimeout->handler_name, arg));
+        }
+#endif /* LWIP_DEBUG_TIMERNAMES */
+        memp_free(MEMP_SYS_TIMEOUT, tmptimeout);
+        if (handler != NULL) {
+#if !NO_SYS
+          /* For LWIP_TCPIP_CORE_LOCKING, lock the core before calling the
+             timeout handler function. */
+          LOCK_TCPIP_CORE();
+#endif /* !NO_SYS */
+          handler(arg);
+#if !NO_SYS
+          UNLOCK_TCPIP_CORE();
+#endif /* !NO_SYS */
+        }
+        LWIP_TCPIP_THREAD_ALIVE();
+      }
+    /* repeat until all expired timers have been called */
+    } while (had_one);
+  }
+}
+
+/** Set back the timestamp of the last call to sys_check_timeouts()
+ * This is necessary if sys_check_timeouts() hasn't been called for a long
+ * time (e.g. while saving energy) to prevent all timer functions of that
+ * period being called.
+ */
+void
+sys_restart_timeouts(void)
+{
+  timeouts_last_time = sys_now();
+}
+
+/** Return the time left before the next timeout is due. If no timeouts are
+ * enqueued, returns 0xffffffff
+ */
+#if !NO_SYS
+static
+#endif /* !NO_SYS */
+u32_t
+sys_timeouts_sleeptime(void)
+{
+  u32_t diff;
+  if (next_timeout == NULL) {
+    return 0xffffffff;
+  }
+  diff = sys_now() - timeouts_last_time;
+  if (diff > next_timeout->time) {
+    return 0;
+  } else {
+    return next_timeout->time - diff;
+  }
+}
+
+#if !NO_SYS
+
+/**
+ * Wait (forever) for a message to arrive in an mbox.
+ * While waiting, timeouts are processed.
+ *
+ * @param mbox the mbox to fetch the message from
+ * @param msg the place to store the message
+ */
+void
+sys_timeouts_mbox_fetch(sys_mbox_t *mbox, void **msg)
+{
+  u32_t sleeptime;
+
+again:
+  if (!next_timeout) {
+    sys_arch_mbox_fetch(mbox, msg, 0);
+    return;
+  }
+
+  sleeptime = sys_timeouts_sleeptime();
+  if (sleeptime == 0 || sys_arch_mbox_fetch(mbox, msg, sleeptime) == SYS_ARCH_TIMEOUT) {
+    /* If a SYS_ARCH_TIMEOUT value is returned, a timeout occurred
+       before a message could be fetched. */
+    sys_check_timeouts();
+    /* We try again to fetch a message from the mbox. */
+    goto again;
+  }
+}
+
+#endif /* NO_SYS */
+
+#else /* LWIP_TIMERS && !LWIP_TIMERS_CUSTOM */
+/* Satisfy the TCP code which calls this function */
+void
+tcp_timer_needed(void)
+{
+}
+#endif /* LWIP_TIMERS && !LWIP_TIMERS_CUSTOM */

+ 1191 - 1013
thirdparty/lwip/src/core/udp.c

@@ -1,1013 +1,1191 @@
-/**
- * @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 & UDPLite (RFC 3828).
- *
- */
-
-/* @todo Check the use of '(struct udp_pcb).chksum_len_rx'!
- */
-
-#include "lwip/opt.h"
-
-#if LWIP_UDP /* don't build if not configured for use in lwipopts.h */
-
-#include "lwip/udp.h"
-#include "lwip/def.h"
-#include "lwip/memp.h"
-#include "lwip/inet_chksum.h"
-#include "lwip/ip_addr.h"
-#include "lwip/netif.h"
-#include "lwip/icmp.h"
-#include "lwip/stats.h"
-#include "lwip/snmp.h"
-#include "arch/perf.h"
-#include "lwip/dhcp.h"
-
-#include <string.h>
-
-#ifndef UDP_LOCAL_PORT_RANGE_START
-/* From http://www.iana.org/assignments/port-numbers:
-   "The Dynamic and/or Private Ports are those from 49152 through 65535" */
-#define UDP_LOCAL_PORT_RANGE_START  0xc000
-#define UDP_LOCAL_PORT_RANGE_END    0xffff
-#define UDP_ENSURE_LOCAL_PORT_RANGE(port) (((port) & ~UDP_LOCAL_PORT_RANGE_START) + UDP_LOCAL_PORT_RANGE_START)
-#endif
-
-/* last local UDP port */
-static u16_t udp_port = UDP_LOCAL_PORT_RANGE_START;
-
-/* The list of UDP PCBs */
-/* exported in udp.h (was static) */
-struct udp_pcb *udp_pcbs;
-
-/**
- * Initialize this module.
- */
-void
-udp_init(void)
-{
-#if LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS && defined(LWIP_RAND)
-  udp_port = UDP_ENSURE_LOCAL_PORT_RANGE(LWIP_RAND());
-#endif /* LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS && defined(LWIP_RAND) */
-}
-
-/**
- * Allocate a new local UDP port.
- *
- * @return a new (free) local UDP port number
- */
-static u16_t
-udp_new_port(void)
-{
-  u16_t n = 0;
-  struct udp_pcb *pcb;
-  
-again:
-  if (udp_port++ == UDP_LOCAL_PORT_RANGE_END) {
-    udp_port = UDP_LOCAL_PORT_RANGE_START;
-  }
-  /* Check all PCBs. */
-  for(pcb = udp_pcbs; pcb != NULL; pcb = pcb->next) {
-    if (pcb->local_port == udp_port) {
-      if (++n > (UDP_LOCAL_PORT_RANGE_END - UDP_LOCAL_PORT_RANGE_START)) {
-        return 0;
-      }
-      goto again;
-    }
-  }
-  return udp_port;
-#if 0
-  struct udp_pcb *ipcb = udp_pcbs;
-  while ((ipcb != NULL) && (udp_port != UDP_LOCAL_PORT_RANGE_END)) {
-    if (ipcb->local_port == udp_port) {
-      /* port is already used by another udp_pcb */
-      udp_port++;
-      /* restart scanning all udp pcbs */
-      ipcb = udp_pcbs;
-    } else {
-      /* go on with next udp pcb */
-      ipcb = ipcb->next;
-    }
-  }
-  if (ipcb != NULL) {
-    return 0;
-  }
-  return udp_port;
-#endif
-}
-
-/**
- * Process an incoming UDP datagram.
- *
- * Given an incoming UDP datagram (as a chain of pbufs) this function
- * finds a corresponding UDP PCB and hands over the pbuf to the pcbs
- * recv function. If no pcb is found or the datagram is incorrect, the
- * pbuf is freed.
- *
- * @param p pbuf to be demultiplexed to a UDP PCB.
- * @param inp network interface on which the datagram was received.
- *
- */
-void
-udp_input(struct pbuf *p, struct netif *inp)
-{
-  struct udp_hdr *udphdr;
-  struct udp_pcb *pcb, *prev;
-  struct udp_pcb *uncon_pcb;
-  struct ip_hdr *iphdr;
-  u16_t src, dest;
-  u8_t local_match;
-  u8_t broadcast;
-
-  PERF_START;
-
-  UDP_STATS_INC(udp.recv);
-
-  iphdr = (struct ip_hdr *)p->payload;
-
-  /* Check minimum length (IP header + UDP header)
-   * and move payload pointer to UDP header */
-  if (p->tot_len < (IPH_HL(iphdr) * 4 + UDP_HLEN) || pbuf_header(p, -(s16_t)(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 *)p->payload;
-
-  /* is broadcast packet ? */
-  broadcast = ip_addr_isbroadcast(&current_iphdr_dest, inp);
-
-  LWIP_DEBUGF(UDP_DEBUG, ("udp_input: received datagram of length %"U16_F"\n", p->tot_len));
-
-  /* convert src and dest ports to host byte order */
-  src = 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_16(&iphdr->dest), ip4_addr2_16(&iphdr->dest),
-               ip4_addr3_16(&iphdr->dest), ip4_addr4_16(&iphdr->dest), ntohs(udphdr->dest),
-               ip4_addr1_16(&iphdr->src), ip4_addr2_16(&iphdr->src),
-               ip4_addr3_16(&iphdr->src), ip4_addr4_16(&iphdr->src), ntohs(udphdr->src)));
-
-#if LWIP_DHCP
-  pcb = NULL;
-  /* when LWIP_DHCP is active, packets to DHCP_CLIENT_PORT may only be processed by
-     the dhcp module, no other UDP pcb may use the local UDP port DHCP_CLIENT_PORT */
-  if (dest == DHCP_CLIENT_PORT) {
-    /* all packets for DHCP_CLIENT_PORT not coming from DHCP_SERVER_PORT are dropped! */
-    if (src == DHCP_SERVER_PORT) {
-      if ((inp->dhcp != NULL) && (inp->dhcp->pcb != NULL)) {
-        /* accept the packe if 
-           (- broadcast or directed to us) -> DHCP is link-layer-addressed, local ip is always ANY!
-           - inp->dhcp->pcb->remote == ANY or iphdr->src */
-        if ((ip_addr_isany(&inp->dhcp->pcb->remote_ip) ||
-           ip_addr_cmp(&(inp->dhcp->pcb->remote_ip), &current_iphdr_src))) {
-          pcb = inp->dhcp->pcb;
-        }
-      }
-    }
-  } else
-#endif /* LWIP_DHCP */
-  {
-    prev = NULL;
-    local_match = 0;
-    uncon_pcb = NULL;
-    /* Iterate through the UDP pcb list for a matching pcb.
-     * 'Perfect match' pcbs (connected to the remote port & ip address) are
-     * preferred. If no perfect match is found, the first unconnected pcb that
-     * matches the local port and ip address gets the datagram. */
-    for (pcb = udp_pcbs; pcb != NULL; pcb = pcb->next) {
-      local_match = 0;
-      /* 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_16(&pcb->local_ip), ip4_addr2_16(&pcb->local_ip),
-                   ip4_addr3_16(&pcb->local_ip), ip4_addr4_16(&pcb->local_ip), pcb->local_port,
-                   ip4_addr1_16(&pcb->remote_ip), ip4_addr2_16(&pcb->remote_ip),
-                   ip4_addr3_16(&pcb->remote_ip), ip4_addr4_16(&pcb->remote_ip), pcb->remote_port));
-
-      /* compare PCB local addr+port to UDP destination addr+port */
-      if (pcb->local_port == dest) {
-        if (
-           (!broadcast && ip_addr_isany(&pcb->local_ip)) ||
-           ip_addr_cmp(&(pcb->local_ip), &current_iphdr_dest) ||
-#if LWIP_IGMP
-           ip_addr_ismulticast(&current_iphdr_dest) ||
-#endif /* LWIP_IGMP */
-#if IP_SOF_BROADCAST_RECV
-            (broadcast && ip_get_option(pcb, SOF_BROADCAST) &&
-             (ip_addr_isany(&pcb->local_ip) ||
-              ip_addr_netcmp(&pcb->local_ip, ip_current_dest_addr(), &inp->netmask)))) {
-#else /* IP_SOF_BROADCAST_RECV */
-            (broadcast &&
-             (ip_addr_isany(&pcb->local_ip) ||
-              ip_addr_netcmp(&pcb->local_ip, ip_current_dest_addr(), &inp->netmask)))) {
-#endif /* IP_SOF_BROADCAST_RECV */ 
-          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), &current_iphdr_src))) {
-        /* the first fully matching PCB */
-        if (prev != NULL) {
-          /* move the pcb to the front of udp_pcbs so that is
-             found faster next time */
-          prev->next = pcb->next;
-          pcb->next = udp_pcbs;
-          udp_pcbs = pcb;
-        } else {
-          UDP_STATS_INC(udp.cachehit);
-        }
-        break;
-      }
-      prev = pcb;
-    }
-    /* no fully matching pcb found? then look for an unconnected pcb */
-    if (pcb == NULL) {
-      pcb = uncon_pcb;
-    }
-  }
-
-  /* Check checksum if this is a match or if it was directed at us. */
-  if (pcb != NULL || ip_addr_cmp(&inp->ip_addr, &current_iphdr_dest)) {
-    LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_input: calculating checksum\n"));
-#if LWIP_UDPLITE
-    if (IPH_PROTO(iphdr) == IP_PROTO_UDPLITE) {
-      /* Do the UDP Lite checksum */
-#if CHECKSUM_CHECK_UDP
-      u16_t chklen = ntohs(udphdr->len);
-      if (chklen < sizeof(struct udp_hdr)) {
-        if (chklen == 0) {
-          /* For UDP-Lite, checksum length of 0 means checksum
-             over the complete packet (See RFC 3828 chap. 3.1) */
-          chklen = p->tot_len;
-        } else {
-          /* At least the UDP-Lite header must be covered by the
-             checksum! (Again, see RFC 3828 chap. 3.1) */
-          UDP_STATS_INC(udp.chkerr);
-          UDP_STATS_INC(udp.drop);
-          snmp_inc_udpinerrors();
-          pbuf_free(p);
-          goto end;
-        }
-      }
-      if (inet_chksum_pseudo_partial(p, &current_iphdr_src, &current_iphdr_dest,
-                             IP_PROTO_UDPLITE, p->tot_len, chklen) != 0) {
-       LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
-                   ("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 /* CHECKSUM_CHECK_UDP */
-    } else
-#endif /* LWIP_UDPLITE */
-    {
-#if CHECKSUM_CHECK_UDP
-      if (udphdr->chksum != 0) {
-        if (inet_chksum_pseudo(p, ip_current_src_addr(), ip_current_dest_addr(),
-                               IP_PROTO_UDP, p->tot_len) != 0) {
-          LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
-                      ("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 /* CHECKSUM_CHECK_UDP */
-    }
-    if(pbuf_header(p, -UDP_HLEN)) {
-      /* Can we cope with this failing? Just assert for now */
-      LWIP_ASSERT("pbuf_header failed\n", 0);
-      UDP_STATS_INC(udp.drop);
-      snmp_inc_udpinerrors();
-      pbuf_free(p);
-      goto end;
-    }
-    if (pcb != NULL) {
-      snmp_inc_udpindatagrams();
-#if SO_REUSE && SO_REUSE_RXTOALL
-      if ((broadcast || ip_addr_ismulticast(&current_iphdr_dest)) &&
-          ip_get_option(pcb, SOF_REUSEADDR)) {
-        /* pass broadcast- or multicast packets to all multicast pcbs
-           if SOF_REUSEADDR is set on the first match */
-        struct udp_pcb *mpcb;
-        u8_t p_header_changed = 0;
-        for (mpcb = udp_pcbs; mpcb != NULL; mpcb = mpcb->next) {
-          if (mpcb != pcb) {
-            /* compare PCB local addr+port to UDP destination addr+port */
-            if ((mpcb->local_port == dest) &&
-                ((!broadcast && ip_addr_isany(&mpcb->local_ip)) ||
-                 ip_addr_cmp(&(mpcb->local_ip), &current_iphdr_dest) ||
-#if LWIP_IGMP
-                 ip_addr_ismulticast(&current_iphdr_dest) ||
-#endif /* LWIP_IGMP */
-#if IP_SOF_BROADCAST_RECV
-                 (broadcast && ip_get_option(mpcb, SOF_BROADCAST)))) {
-#else  /* IP_SOF_BROADCAST_RECV */
-                 (broadcast))) {
-#endif /* IP_SOF_BROADCAST_RECV */
-              /* pass a copy of the packet to all local matches */
-              if (mpcb->recv != NULL) {
-                struct pbuf *q;
-                /* for that, move payload to IP header again */
-                if (p_header_changed == 0) {
-                  pbuf_header(p, (s16_t)((IPH_HL(iphdr) * 4) + UDP_HLEN));
-                  p_header_changed = 1;
-                }
-                q = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM);
-                if (q != NULL) {
-                  err_t err = pbuf_copy(q, p);
-                  if (err == ERR_OK) {
-                    /* move payload to UDP data */
-                    pbuf_header(q, -(s16_t)((IPH_HL(iphdr) * 4) + UDP_HLEN));
-                    mpcb->recv(mpcb->recv_arg, mpcb, q, ip_current_src_addr(), src);
-                  }
-                }
-              }
-            }
-          }
-        }
-        if (p_header_changed) {
-          /* and move payload to UDP data again */
-          pbuf_header(p, -(s16_t)((IPH_HL(iphdr) * 4) + UDP_HLEN));
-        }
-      }
-#endif /* SO_REUSE && SO_REUSE_RXTOALL */
-      /* callback */
-      if (pcb->recv != NULL) {
-        /* now the recv function is responsible for freeing p */
-        pcb->recv(pcb->recv_arg, pcb, p, ip_current_src_addr(), src);
-      } else {
-        /* no recv function registered? then we have to free the pbuf! */
-        pbuf_free(p);
-        goto end;
-      }
-    } else {
-      LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_input: not for us.\n"));
-
-#if LWIP_ICMP
-      /* No match was found, send ICMP destination port unreachable unless
-         destination address was broadcast/multicast. */
-      if (!broadcast &&
-          !ip_addr_ismulticast(&current_iphdr_dest)) {
-        /* move payload pointer back to ip header */
-        pbuf_header(p, (IPH_HL(iphdr) * 4) + UDP_HLEN);
-        LWIP_ASSERT("p->payload == iphdr", (p->payload == iphdr));
-        icmp_dest_unreach(p, ICMP_DUR_PORT);
-      }
-#endif /* LWIP_ICMP */
-      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 using UDP.
- *
- * @param pcb UDP PCB used to send the data.
- * @param p chain of pbuf's to be sent.
- *
- * The datagram will be sent to the current remote_ip & remote_port
- * stored in pcb. If the pcb is not bound to a port, it will
- * automatically be bound to a random port.
- *
- * @return lwIP error code.
- * - ERR_OK. Successful. No error occured.
- * - ERR_MEM. Out of memory.
- * - ERR_RTE. Could not find route to destination address.
- * - More errors could be returned by lower protocol layers.
- *
- * @see udp_disconnect() udp_sendto()
- */
-err_t
-udp_send(struct udp_pcb *pcb, struct pbuf *p)
-{
-  /* send to the packet using remote ip and port stored in the pcb */
-  return udp_sendto(pcb, p, &pcb->remote_ip, pcb->remote_port);
-}
-
-#if LWIP_CHECKSUM_ON_COPY
-/** Same as udp_send() but with checksum
- */
-err_t
-udp_send_chksum(struct udp_pcb *pcb, struct pbuf *p,
-                u8_t have_chksum, u16_t chksum)
-{
-  /* send to the packet using remote ip and port stored in the pcb */
-  return udp_sendto_chksum(pcb, p, &pcb->remote_ip, pcb->remote_port,
-    have_chksum, chksum);
-}
-#endif /* LWIP_CHECKSUM_ON_COPY */
-
-/**
- * Send data to a specified address using UDP.
- *
- * @param pcb UDP PCB used to send the data.
- * @param p chain of pbuf's to be sent.
- * @param dst_ip Destination IP address.
- * @param dst_port Destination UDP port.
- *
- * dst_ip & dst_port are expected to be in the same byte order as in the pcb.
- *
- * If the PCB already has a remote address association, it will
- * be restored after the data is sent.
- * 
- * @return lwIP error code (@see udp_send for possible error codes)
- *
- * @see udp_disconnect() udp_send()
- */
-err_t
-udp_sendto(struct udp_pcb *pcb, struct pbuf *p,
-  ip_addr_t *dst_ip, u16_t dst_port)
-{
-#if LWIP_CHECKSUM_ON_COPY
-  return udp_sendto_chksum(pcb, p, dst_ip, dst_port, 0, 0);
-}
-
-/** Same as udp_sendto(), but with checksum */
-err_t
-udp_sendto_chksum(struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *dst_ip,
-                  u16_t dst_port, u8_t have_chksum, u16_t chksum)
-{
-#endif /* LWIP_CHECKSUM_ON_COPY */
-  struct netif *netif;
-
-  LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_send\n"));
-
-  /* find the outgoing network interface for this packet */
-#if LWIP_IGMP
-  netif = ip_route((ip_addr_ismulticast(dst_ip))?(&(pcb->multicast_ip)):(dst_ip));
-#else
-  netif = ip_route(dst_ip);
-#endif /* LWIP_IGMP */
-
-  /* no outgoing network interface could be found? */
-  if (netif == NULL) {
-    LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("udp_send: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
-      ip4_addr1_16(dst_ip), ip4_addr2_16(dst_ip), ip4_addr3_16(dst_ip), ip4_addr4_16(dst_ip)));
-    UDP_STATS_INC(udp.rterr);
-    return ERR_RTE;
-  }
-#if LWIP_CHECKSUM_ON_COPY
-  return udp_sendto_if_chksum(pcb, p, dst_ip, dst_port, netif, have_chksum, chksum);
-#else /* LWIP_CHECKSUM_ON_COPY */
-  return udp_sendto_if(pcb, p, dst_ip, dst_port, netif);
-#endif /* LWIP_CHECKSUM_ON_COPY */
-}
-
-/**
- * Send data to a specified address using UDP.
- * The netif used for sending can be specified.
- *
- * This function exists mainly for DHCP, to be able to send UDP packets
- * on a netif that is still down.
- *
- * @param pcb UDP PCB used to send the data.
- * @param p chain of pbuf's to be sent.
- * @param dst_ip Destination IP address.
- * @param dst_port Destination UDP port.
- * @param netif the netif used for sending.
- *
- * dst_ip & dst_port are expected to be in the same byte order as in the pcb.
- *
- * @return lwIP error code (@see udp_send for possible error codes)
- *
- * @see udp_disconnect() udp_send()
- */
-err_t
-udp_sendto_if(struct udp_pcb *pcb, struct pbuf *p,
-  ip_addr_t *dst_ip, u16_t dst_port, struct netif *netif)
-{
-#if LWIP_CHECKSUM_ON_COPY
-  return udp_sendto_if_chksum(pcb, p, dst_ip, dst_port, netif, 0, 0);
-}
-
-/** Same as udp_sendto_if(), but with checksum */
-err_t
-udp_sendto_if_chksum(struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *dst_ip,
-                     u16_t dst_port, struct netif *netif, u8_t have_chksum,
-                     u16_t chksum)
-{
-#endif /* LWIP_CHECKSUM_ON_COPY */
-  struct udp_hdr *udphdr;
-  ip_addr_t *src_ip;
-  err_t err;
-  struct pbuf *q; /* q will be sent down the stack */
-
-#if IP_SOF_BROADCAST
-  /* broadcast filter? */
-  if (!ip_get_option(pcb, SOF_BROADCAST) && ip_addr_isbroadcast(dst_ip, netif)) {
-    LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
-      ("udp_sendto_if: SOF_BROADCAST not enabled on pcb %p\n", (void *)pcb));
-    return ERR_VAL;
-  }
-#endif /* IP_SOF_BROADCAST */
-
-  /* if the PCB is not yet bound to a port, bind it here */
-  if (pcb->local_port == 0) {
-    LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_send: not yet bound to a port, binding now\n"));
-    err = udp_bind(pcb, &pcb->local_ip, pcb->local_port);
-    if (err != ERR_OK) {
-      LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("udp_send: forced port bind failed\n"));
-      return err;
-    }
-  }
-
-  /* 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 separate new pbuf */
-    q = pbuf_alloc(PBUF_IP, UDP_HLEN, PBUF_RAM);
-    /* new header pbuf could not be allocated? */
-    if (q == NULL) {
-      LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("udp_send: could not allocate header\n"));
-      return ERR_MEM;
-    }
-    if (p->tot_len != 0) {
-      /* chain header q in front of given pbuf p (only if p contains data) */
-      pbuf_chain(q, p);
-    }
-    /* first pbuf q points to header pbuf */
-    LWIP_DEBUGF(UDP_DEBUG,
-                ("udp_send: added header pbuf %p before given pbuf %p\n", (void *)q, (void *)p));
-  } else {
-    /* adding space for header within p succeeded */
-    /* first pbuf q equals given pbuf */
-    q = p;
-    LWIP_DEBUGF(UDP_DEBUG, ("udp_send: added header in given pbuf %p\n", (void *)p));
-  }
-  LWIP_ASSERT("check that first pbuf can hold struct udp_hdr",
-              (q->len >= sizeof(struct udp_hdr)));
-  /* q now represents the packet to be sent */
-  udphdr = (struct udp_hdr *)q->payload;
-  udphdr->src = htons(pcb->local_port);
-  udphdr->dest = htons(dst_port);
-  /* in UDP, 0 checksum means 'no checksum' */
-  udphdr->chksum = 0x0000; 
-
-  /* Multicast Loop? */
-#if LWIP_IGMP
-  if (ip_addr_ismulticast(dst_ip) && ((pcb->flags & UDP_FLAGS_MULTICAST_LOOP) != 0)) {
-    q->flags |= PBUF_FLAG_MCASTLOOP;
-  }
-#endif /* LWIP_IGMP */
-
-
-  /* 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 {
-    /* check if UDP PCB local IP address is correct
-     * this could be an old address if netif->ip_addr has changed */
-    if (!ip_addr_cmp(&(pcb->local_ip), &(netif->ip_addr))) {
-      /* local_ip doesn't match, drop the packet */
-      if (q != p) {
-        /* free the header pbuf */
-        pbuf_free(q);
-        q = NULL;
-        /* p is still referenced by the caller, and will live on */
-      }
-      return ERR_VAL;
-    }
-    /* 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));
-
-#if LWIP_UDPLITE
-  /* UDP Lite protocol? */
-  if (pcb->flags & UDP_FLAGS_UDPLITE) {
-    u16_t chklen, chklen_hdr;
-    LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP LITE packet length %"U16_F"\n", q->tot_len));
-    /* set UDP message length in UDP header */
-    chklen_hdr = chklen = pcb->chksum_len_tx;
-    if ((chklen < sizeof(struct udp_hdr)) || (chklen > q->tot_len)) {
-      if (chklen != 0) {
-        LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP LITE pcb->chksum_len is illegal: %"U16_F"\n", chklen));
-      }
-      /* For UDP-Lite, checksum length of 0 means checksum
-         over the complete packet. (See RFC 3828 chap. 3.1)
-         At least the UDP-Lite header must be covered by the
-         checksum, therefore, if chksum_len has an illegal
-         value, we generate the checksum over the complete
-         packet to be safe. */
-      chklen_hdr = 0;
-      chklen = q->tot_len;
-    }
-    udphdr->len = htons(chklen_hdr);
-    /* calculate checksum */
-#if CHECKSUM_GEN_UDP
-    udphdr->chksum = inet_chksum_pseudo_partial(q, src_ip, dst_ip,
-      IP_PROTO_UDPLITE, q->tot_len,
-#if !LWIP_CHECKSUM_ON_COPY
-      chklen);
-#else /* !LWIP_CHECKSUM_ON_COPY */
-      (have_chksum ? UDP_HLEN : chklen));
-    if (have_chksum) {
-      u32_t acc;
-      acc = udphdr->chksum + (u16_t)~(chksum);
-      udphdr->chksum = FOLD_U32T(acc);
-    }
-#endif /* !LWIP_CHECKSUM_ON_COPY */
-
-    /* chksum zero must become 0xffff, as zero means 'no checksum' */
-    if (udphdr->chksum == 0x0000) {
-      udphdr->chksum = 0xffff;
-    }
-#endif /* CHECKSUM_GEN_UDP */
-    /* output to IP */
-    LWIP_DEBUGF(UDP_DEBUG, ("udp_send: ip_output_if (,,,,IP_PROTO_UDPLITE,)\n"));
-    NETIF_SET_HWADDRHINT(netif, &pcb->addr_hint);
-    err = ip_output_if(q, src_ip, dst_ip, pcb->ttl, pcb->tos, IP_PROTO_UDPLITE, netif);
-    NETIF_SET_HWADDRHINT(netif, NULL);
-  } else
-#endif /* LWIP_UDPLITE */
-  {      /* UDP */
-    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) {
-      u16_t udpchksum;
-#if LWIP_CHECKSUM_ON_COPY
-      if (have_chksum) {
-        u32_t acc;
-        udpchksum = inet_chksum_pseudo_partial(q, src_ip, dst_ip, IP_PROTO_UDP,
-          q->tot_len, UDP_HLEN);
-        acc = udpchksum + (u16_t)~(chksum);
-        udpchksum = FOLD_U32T(acc);
-      } else
-#endif /* LWIP_CHECKSUM_ON_COPY */
-      {
-        udpchksum = inet_chksum_pseudo(q, src_ip, dst_ip, IP_PROTO_UDP, q->tot_len);
-      }
-
-      /* chksum zero must become 0xffff, as zero means 'no checksum' */
-      if (udpchksum == 0x0000) {
-        udpchksum = 0xffff;
-      }
-      udphdr->chksum = udpchksum;
-    }
-#endif /* CHECKSUM_GEN_UDP */
-    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 */
-    NETIF_SET_HWADDRHINT(netif, &pcb->addr_hint);
-    err = ip_output_if(q, src_ip, dst_ip, pcb->ttl, pcb->tos, IP_PROTO_UDP, netif);
-    NETIF_SET_HWADDRHINT(netif, NULL);
-  }
-  /* TODO: must this be increased even if error occured? */
-  snmp_inc_udpoutdatagrams();
-
-  /* did we chain a separate header pbuf earlier? */
-  if (q != p) {
-    /* free the header pbuf */
-    pbuf_free(q);
-    q = NULL;
-    /* p is still referenced by the caller, and will live on */
-  }
-
-  UDP_STATS_INC(udp.xmit);
-  return err;
-}
-
-/**
- * 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. Use 0 to automatically bind
- * to a random port between UDP_LOCAL_PORT_RANGE_START and
- * UDP_LOCAL_PORT_RANGE_END.
- *
- * ipaddr & port are expected to be in the same byte order as in the pcb.
- *
- * @return lwIP error code.
- * - ERR_OK. Successful. No error 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, ip_addr_t *ipaddr, u16_t port)
-{
-  struct udp_pcb *ipcb;
-  u8_t rebind;
-
-  LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_bind(ipaddr = "));
-  ip_addr_debug_print(UDP_DEBUG, ipaddr);
-  LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, (", port = %"U16_F")\n", port));
-
-  rebind = 0;
-  /* Check for double bind and rebind of the same pcb */
-  for (ipcb = udp_pcbs; ipcb != NULL; ipcb = ipcb->next) {
-    /* is this UDP PCB already on active list? */
-    if (pcb == ipcb) {
-      /* pcb may occur at most once in active list */
-      LWIP_ASSERT("rebind == 0", rebind == 0);
-      /* pcb already in list, just rebind */
-      rebind = 1;
-    }
-
-    /* By default, we don't allow to bind to a port that any other udp
-       PCB is alread bound to, unless *all* PCBs with that port have tha
-       REUSEADDR flag set. */
-#if SO_REUSE
-    else if (!ip_get_option(pcb, SOF_REUSEADDR) &&
-             !ip_get_option(ipcb, SOF_REUSEADDR)) {
-#else /* SO_REUSE */
-    /* port matches that of PCB in list and REUSEADDR not set -> reject */
-    else {
-#endif /* SO_REUSE */
-      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;
-      }
-    }
-  }
-
-  ip_addr_set(&pcb->local_ip, ipaddr);
-
-  /* no port specified? */
-  if (port == 0) {
-    port = udp_new_port();
-    if (port == 0) {
-      /* no more ports available in local range */
-      LWIP_DEBUGF(UDP_DEBUG, ("udp_bind: out of free UDP ports\n"));
-      return ERR_USE;
-    }
-  }
-  pcb->local_port = port;
-  snmp_insert_udpidx_tree(pcb);
-  /* pcb not active yet? */
-  if (rebind == 0) {
-    /* place the PCB on the active list if not already there */
-    pcb->next = udp_pcbs;
-    udp_pcbs = pcb;
-  }
-  LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
-              ("udp_bind: bound to %"U16_F".%"U16_F".%"U16_F".%"U16_F", port %"U16_F"\n",
-               ip4_addr1_16(&pcb->local_ip), ip4_addr2_16(&pcb->local_ip),
-               ip4_addr3_16(&pcb->local_ip), ip4_addr4_16(&pcb->local_ip),
-               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
- *
- * ipaddr & port are expected to be in the same byte order as in the pcb.
- *
- * The udp pcb is bound to a random local port if not already bound.
- *
- * @see udp_disconnect()
- */
-err_t
-udp_connect(struct udp_pcb *pcb, ip_addr_t *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 | LWIP_DBG_TRACE | LWIP_DBG_STATE,
-              ("udp_connect: connected to %"U16_F".%"U16_F".%"U16_F".%"U16_F",port %"U16_F"\n",
-               ip4_addr1_16(&pcb->local_ip), ip4_addr2_16(&pcb->local_ip),
-               ip4_addr3_16(&pcb->local_ip), ip4_addr4_16(&pcb->local_ip),
-               pcb->local_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;
-}
-
-/**
- * Disconnect a UDP PCB
- *
- * @param pcb the udp pcb to disconnect.
- */
-void
-udp_disconnect(struct udp_pcb *pcb)
-{
-  /* reset remote address association */
-  ip_addr_set_any(&pcb->remote_ip);
-  pcb->remote_port = 0;
-  /* mark PCB as unconnected */
-  pcb->flags &= ~UDP_FLAGS_CONNECTED;
-}
-
-/**
- * Set a receive callback for a UDP PCB
- *
- * This callback will be called when receiving a datagram for the pcb.
- *
- * @param pcb the pcb for wich to set the recv callback
- * @param recv function pointer of the callback function
- * @param recv_arg additional argument to pass to the callback function
- */
-void
-udp_recv(struct udp_pcb *pcb, udp_recv_fn recv, void *recv_arg)
-{
-  /* 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;
-
-  snmp_delete_udpidx_tree(pcb);
-  /* pcb to be removed is first in list? */
-  if (udp_pcbs == pcb) {
-    /* make list start at 2nd pcb */
-    udp_pcbs = udp_pcbs->next;
-    /* pcb not 1st in list */
-  } else {
-    for (pcb2 = udp_pcbs; pcb2 != NULL; pcb2 = pcb2->next) {
-      /* find pcb in udp_pcbs list */
-      if (pcb2->next != NULL && pcb2->next == pcb) {
-        /* remove pcb from list */
-        pcb2->next = pcb->next;
-      }
-    }
-  }
-  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 = (struct udp_pcb *)memp_malloc(MEMP_UDP_PCB);
-  /* could allocate UDP PCB? */
-  if (pcb != NULL) {
-    /* UDP Lite: by initializing to all zeroes, chksum_len is set to 0
-     * which means checksum is generated over the whole datagram per default
-     * (recommended as default by RFC 3828). */
-    /* initialize PCB to all zeroes */
-    memset(pcb, 0, sizeof(struct udp_pcb));
-    pcb->ttl = UDP_TTL;
-  }
-  return pcb;
-}
-
-#if UDP_DEBUG
-/**
- * Print UDP header information for debug purposes.
- *
- * @param udphdr pointer to the udp header in memory.
- */
-void
-udp_debug_print(struct udp_hdr *udphdr)
-{
-  LWIP_DEBUGF(UDP_DEBUG, ("UDP header:\n"));
-  LWIP_DEBUGF(UDP_DEBUG, ("+-------------------------------+\n"));
-  LWIP_DEBUGF(UDP_DEBUG, ("|     %5"U16_F"     |     %5"U16_F"     | (src port, dest port)\n",
-                          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 */
+/**
+ * @file
+ * User Datagram Protocol module\n
+ * The code for the User Datagram Protocol UDP & UDPLite (RFC 3828).\n
+ * See also @ref udp_raw
+ * 
+ * @defgroup udp_raw UDP
+ * @ingroup callbackstyle_api
+ * User Datagram Protocol module\n
+ * @see @ref raw_api and @ref netconn
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+/* @todo Check the use of '(struct udp_pcb).chksum_len_rx'!
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_UDP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/udp.h"
+#include "lwip/def.h"
+#include "lwip/memp.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/ip_addr.h"
+#include "lwip/ip6.h"
+#include "lwip/ip6_addr.h"
+#include "lwip/netif.h"
+#include "lwip/icmp.h"
+#include "lwip/icmp6.h"
+#include "lwip/stats.h"
+#include "lwip/snmp.h"
+#include "lwip/dhcp.h"
+
+#include <string.h>
+
+#ifndef UDP_LOCAL_PORT_RANGE_START
+/* From http://www.iana.org/assignments/port-numbers:
+   "The Dynamic and/or Private Ports are those from 49152 through 65535" */
+#define UDP_LOCAL_PORT_RANGE_START  0xc000
+#define UDP_LOCAL_PORT_RANGE_END    0xffff
+#define UDP_ENSURE_LOCAL_PORT_RANGE(port) ((u16_t)(((port) & ~UDP_LOCAL_PORT_RANGE_START) + UDP_LOCAL_PORT_RANGE_START))
+#endif
+
+/* last local UDP port */
+static u16_t udp_port = UDP_LOCAL_PORT_RANGE_START;
+
+/* The list of UDP PCBs */
+/* exported in udp.h (was static) */
+struct udp_pcb *udp_pcbs;
+
+/**
+ * Initialize this module.
+ */
+void
+udp_init(void)
+{
+#if LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS && defined(LWIP_RAND)
+  udp_port = UDP_ENSURE_LOCAL_PORT_RANGE(LWIP_RAND());
+#endif /* LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS && defined(LWIP_RAND) */
+}
+
+/**
+ * Allocate a new local UDP port.
+ *
+ * @return a new (free) local UDP port number
+ */
+static u16_t
+udp_new_port(void)
+{
+  u16_t n = 0;
+  struct udp_pcb *pcb;
+
+again:
+  if (udp_port++ == UDP_LOCAL_PORT_RANGE_END) {
+    udp_port = UDP_LOCAL_PORT_RANGE_START;
+  }
+  /* Check all PCBs. */
+  for (pcb = udp_pcbs; pcb != NULL; pcb = pcb->next) {
+    if (pcb->local_port == udp_port) {
+      if (++n > (UDP_LOCAL_PORT_RANGE_END - UDP_LOCAL_PORT_RANGE_START)) {
+        return 0;
+      }
+      goto again;
+    }
+  }
+  return udp_port;
+}
+
+/** Common code to see if the current input packet matches the pcb
+ * (current input packet is accessed via ip(4/6)_current_* macros)
+ *
+ * @param pcb pcb to check
+ * @param inp network interface on which the datagram was received (only used for IPv4)
+ * @param broadcast 1 if his is an IPv4 broadcast (global or subnet-only), 0 otherwise (only used for IPv4)
+ * @return 1 on match, 0 otherwise
+ */
+static u8_t
+udp_input_local_match(struct udp_pcb *pcb, struct netif *inp, u8_t broadcast)
+{
+  LWIP_UNUSED_ARG(inp);       /* in IPv6 only case */
+  LWIP_UNUSED_ARG(broadcast); /* in IPv6 only case */
+
+  /* Dual-stack: PCBs listening to any IP type also listen to any IP address */
+  if (IP_IS_ANY_TYPE_VAL(pcb->local_ip)) {
+#if LWIP_IPV4 && IP_SOF_BROADCAST_RECV
+    if ((broadcast != 0) && !ip_get_option(pcb, SOF_BROADCAST)) {
+      return 0;
+    }
+#endif /* LWIP_IPV4 && IP_SOF_BROADCAST_RECV */
+    return 1;
+  }
+
+  /* Only need to check PCB if incoming IP version matches PCB IP version */
+  if (IP_ADDR_PCB_VERSION_MATCH_EXACT(pcb, ip_current_dest_addr())) {
+#if LWIP_IPV4
+    /* Special case: IPv4 broadcast: all or broadcasts in my subnet
+     * Note: broadcast variable can only be 1 if it is an IPv4 broadcast */
+    if (broadcast != 0) {
+#if IP_SOF_BROADCAST_RECV
+      if (ip_get_option(pcb, SOF_BROADCAST))
+#endif /* IP_SOF_BROADCAST_RECV */
+      {
+        if (ip4_addr_isany(ip_2_ip4(&pcb->local_ip)) ||
+          ((ip4_current_dest_addr()->addr == IPADDR_BROADCAST)) ||
+           ip4_addr_netcmp(ip_2_ip4(&pcb->local_ip), ip4_current_dest_addr(), netif_ip4_netmask(inp))) {
+          return 1;
+        }
+      }
+    } else
+#endif /* LWIP_IPV4 */
+    /* Handle IPv4 and IPv6: all or exact match */
+    if (ip_addr_isany(&pcb->local_ip) || ip_addr_cmp(&pcb->local_ip, ip_current_dest_addr())) {
+      return 1;
+    }
+  }
+
+  return 0;
+}
+
+/**
+ * Process an incoming UDP datagram.
+ *
+ * Given an incoming UDP datagram (as a chain of pbufs) this function
+ * finds a corresponding UDP PCB and hands over the pbuf to the pcbs
+ * recv function. If no pcb is found or the datagram is incorrect, the
+ * pbuf is freed.
+ *
+ * @param p pbuf to be demultiplexed to a UDP PCB (p->payload pointing to the UDP header)
+ * @param inp network interface on which the datagram was received.
+ *
+ */
+void
+udp_input(struct pbuf *p, struct netif *inp)
+{
+  struct udp_hdr *udphdr;
+  struct udp_pcb *pcb, *prev;
+  struct udp_pcb *uncon_pcb;
+  u16_t src, dest;
+  u8_t broadcast;
+  u8_t for_us = 0;
+
+  LWIP_UNUSED_ARG(inp);
+
+  PERF_START;
+
+  UDP_STATS_INC(udp.recv);
+
+  /* Check minimum length (UDP header) */
+  if (p->len < UDP_HLEN) {
+    /* drop short packets */
+    LWIP_DEBUGF(UDP_DEBUG,
+                ("udp_input: short UDP datagram (%"U16_F" bytes) discarded\n", p->tot_len));
+    UDP_STATS_INC(udp.lenerr);
+    UDP_STATS_INC(udp.drop);
+    MIB2_STATS_INC(mib2.udpinerrors);
+    pbuf_free(p);
+    goto end;
+  }
+
+  udphdr = (struct udp_hdr *)p->payload;
+
+  /* is broadcast packet ? */
+  broadcast = ip_addr_isbroadcast(ip_current_dest_addr(), ip_current_netif());
+
+  LWIP_DEBUGF(UDP_DEBUG, ("udp_input: received datagram of length %"U16_F"\n", p->tot_len));
+
+  /* convert src and dest ports to host byte order */
+  src = lwip_ntohs(udphdr->src);
+  dest = lwip_ntohs(udphdr->dest);
+
+  udp_debug_print(udphdr);
+
+  /* print the UDP source and destination */
+  LWIP_DEBUGF(UDP_DEBUG, ("udp ("));
+  ip_addr_debug_print(UDP_DEBUG, ip_current_dest_addr());
+  LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F") <-- (", lwip_ntohs(udphdr->dest)));
+  ip_addr_debug_print(UDP_DEBUG, ip_current_src_addr());
+  LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F")\n", lwip_ntohs(udphdr->src)));
+
+  pcb = NULL;
+  prev = NULL;
+  uncon_pcb = NULL;
+  /* Iterate through the UDP pcb list for a matching pcb.
+   * 'Perfect match' pcbs (connected to the remote port & ip address) are
+   * preferred. If no perfect match is found, the first unconnected pcb that
+   * matches the local port and ip address gets the datagram. */
+  for (pcb = udp_pcbs; pcb != NULL; pcb = pcb->next) {
+    /* print the PCB local and remote address */
+    LWIP_DEBUGF(UDP_DEBUG, ("pcb ("));
+    ip_addr_debug_print(UDP_DEBUG, &pcb->local_ip);
+    LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F") <-- (", pcb->local_port));
+    ip_addr_debug_print(UDP_DEBUG, &pcb->remote_ip);
+    LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F")\n", pcb->remote_port));
+
+    /* compare PCB local addr+port to UDP destination addr+port */
+    if ((pcb->local_port == dest) &&
+        (udp_input_local_match(pcb, inp, broadcast) != 0)) {
+      if (((pcb->flags & UDP_FLAGS_CONNECTED) == 0) &&
+          ((uncon_pcb == NULL)
+#if SO_REUSE
+          /* prefer specific IPs over cath-all */
+          || !ip_addr_isany(&pcb->local_ip)
+#endif /* SO_REUSE */
+          )) {
+        /* the first unconnected matching PCB */
+        uncon_pcb = pcb;
+      }
+
+      /* compare PCB remote addr+port to UDP source addr+port */
+      if ((pcb->remote_port == src) &&
+          (ip_addr_isany_val(pcb->remote_ip) ||
+          ip_addr_cmp(&pcb->remote_ip, ip_current_src_addr()))) {
+        /* the first fully matching PCB */
+        if (prev != NULL) {
+          /* move the pcb to the front of udp_pcbs so that is
+             found faster next time */
+          prev->next = pcb->next;
+          pcb->next = udp_pcbs;
+          udp_pcbs = pcb;
+        } else {
+          UDP_STATS_INC(udp.cachehit);
+        }
+        break;
+      }
+    }
+
+    prev = pcb;
+  }
+  /* no fully matching pcb found? then look for an unconnected pcb */
+  if (pcb == NULL) {
+    pcb = uncon_pcb;
+  }
+
+  /* Check checksum if this is a match or if it was directed at us. */
+  if (pcb != NULL) {
+    for_us = 1;
+  } else {
+#if LWIP_IPV6
+    if (ip_current_is_v6()) {
+      for_us = netif_get_ip6_addr_match(inp, ip6_current_dest_addr()) >= 0;
+    }
+#endif /* LWIP_IPV6 */
+#if LWIP_IPV4
+    if (!ip_current_is_v6()) {
+      for_us = ip4_addr_cmp(netif_ip4_addr(inp), ip4_current_dest_addr());
+    }
+#endif /* LWIP_IPV4 */
+  }
+
+  if (for_us) {
+    LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_input: calculating checksum\n"));
+#if CHECKSUM_CHECK_UDP
+    IF__NETIF_CHECKSUM_ENABLED(inp, CHECKSUM_CHECK_UDP) {
+#if LWIP_UDPLITE
+      if (ip_current_header_proto() == IP_PROTO_UDPLITE) {
+        /* Do the UDP Lite checksum */
+        u16_t chklen = lwip_ntohs(udphdr->len);
+        if (chklen < sizeof(struct udp_hdr)) {
+          if (chklen == 0) {
+            /* For UDP-Lite, checksum length of 0 means checksum
+               over the complete packet (See RFC 3828 chap. 3.1) */
+            chklen = p->tot_len;
+          } else {
+            /* At least the UDP-Lite header must be covered by the
+               checksum! (Again, see RFC 3828 chap. 3.1) */
+            goto chkerr;
+          }
+        }
+        if (ip_chksum_pseudo_partial(p, IP_PROTO_UDPLITE,
+                     p->tot_len, chklen,
+                     ip_current_src_addr(), ip_current_dest_addr()) != 0) {
+          goto chkerr;
+        }
+      } else
+#endif /* LWIP_UDPLITE */
+      {
+        if (udphdr->chksum != 0) {
+          if (ip_chksum_pseudo(p, IP_PROTO_UDP, p->tot_len,
+                               ip_current_src_addr(),
+                               ip_current_dest_addr()) != 0) {
+            goto chkerr;
+          }
+        }
+      }
+    }
+#endif /* CHECKSUM_CHECK_UDP */
+    if (pbuf_header(p, -UDP_HLEN)) {
+      /* Can we cope with this failing? Just assert for now */
+      LWIP_ASSERT("pbuf_header failed\n", 0);
+      UDP_STATS_INC(udp.drop);
+      MIB2_STATS_INC(mib2.udpinerrors);
+      pbuf_free(p);
+      goto end;
+    }
+
+    if (pcb != NULL) {
+      MIB2_STATS_INC(mib2.udpindatagrams);
+#if SO_REUSE && SO_REUSE_RXTOALL
+      if (ip_get_option(pcb, SOF_REUSEADDR) &&
+          (broadcast || ip_addr_ismulticast(ip_current_dest_addr()))) {
+        /* pass broadcast- or multicast packets to all multicast pcbs
+           if SOF_REUSEADDR is set on the first match */
+        struct udp_pcb *mpcb;
+        u8_t p_header_changed = 0;
+        s16_t hdrs_len = (s16_t)(ip_current_header_tot_len() + UDP_HLEN);
+        for (mpcb = udp_pcbs; mpcb != NULL; mpcb = mpcb->next) {
+          if (mpcb != pcb) {
+            /* compare PCB local addr+port to UDP destination addr+port */
+            if ((mpcb->local_port == dest) &&
+                (udp_input_local_match(mpcb, inp, broadcast) != 0)) {
+              /* pass a copy of the packet to all local matches */
+              if (mpcb->recv != NULL) {
+                struct pbuf *q;
+                /* for that, move payload to IP header again */
+                if (p_header_changed == 0) {
+                  pbuf_header_force(p, hdrs_len);
+                  p_header_changed = 1;
+                }
+                q = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM);
+                if (q != NULL) {
+                  err_t err = pbuf_copy(q, p);
+                  if (err == ERR_OK) {
+                    /* move payload to UDP data */
+                    pbuf_header(q, -hdrs_len);
+                    mpcb->recv(mpcb->recv_arg, mpcb, q, ip_current_src_addr(), src);
+                  }
+                }
+              }
+            }
+          }
+        }
+        if (p_header_changed) {
+          /* and move payload to UDP data again */
+          pbuf_header(p, -hdrs_len);
+        }
+      }
+#endif /* SO_REUSE && SO_REUSE_RXTOALL */
+      /* callback */
+      if (pcb->recv != NULL) {
+        /* now the recv function is responsible for freeing p */
+        pcb->recv(pcb->recv_arg, pcb, p, ip_current_src_addr(), src);
+      } else {
+        /* no recv function registered? then we have to free the pbuf! */
+        pbuf_free(p);
+        goto end;
+      }
+    } else {
+      LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_input: not for us.\n"));
+
+#if LWIP_ICMP || LWIP_ICMP6
+      /* No match was found, send ICMP destination port unreachable unless
+         destination address was broadcast/multicast. */
+      if (!broadcast && !ip_addr_ismulticast(ip_current_dest_addr())) {
+        /* move payload pointer back to ip header */
+        pbuf_header_force(p, (s16_t)(ip_current_header_tot_len() + UDP_HLEN));
+        icmp_port_unreach(ip_current_is_v6(), p);
+      }
+#endif /* LWIP_ICMP || LWIP_ICMP6 */
+      UDP_STATS_INC(udp.proterr);
+      UDP_STATS_INC(udp.drop);
+      MIB2_STATS_INC(mib2.udpnoports);
+      pbuf_free(p);
+    }
+  } else {
+    pbuf_free(p);
+  }
+end:
+  PERF_STOP("udp_input");
+  return;
+#if CHECKSUM_CHECK_UDP
+chkerr:
+  LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+              ("udp_input: UDP (or UDP Lite) datagram discarded due to failing checksum\n"));
+  UDP_STATS_INC(udp.chkerr);
+  UDP_STATS_INC(udp.drop);
+  MIB2_STATS_INC(mib2.udpinerrors);
+  pbuf_free(p);
+  PERF_STOP("udp_input");
+#endif /* CHECKSUM_CHECK_UDP */
+}
+
+/**
+ * @ingroup udp_raw
+ * Send data using UDP.
+ *
+ * @param pcb UDP PCB used to send the data.
+ * @param p chain of pbuf's to be sent.
+ *
+ * The datagram will be sent to the current remote_ip & remote_port
+ * stored in pcb. If the pcb is not bound to a port, it will
+ * automatically be bound to a random port.
+ *
+ * @return lwIP error code.
+ * - ERR_OK. Successful. No error occurred.
+ * - ERR_MEM. Out of memory.
+ * - ERR_RTE. Could not find route to destination address.
+ * - ERR_VAL. No PCB or PCB is dual-stack
+ * - More errors could be returned by lower protocol layers.
+ *
+ * @see udp_disconnect() udp_sendto()
+ */
+err_t
+udp_send(struct udp_pcb *pcb, struct pbuf *p)
+{
+  if ((pcb == NULL) || IP_IS_ANY_TYPE_VAL(pcb->remote_ip)) {
+    return ERR_VAL;
+  }
+
+  /* send to the packet using remote ip and port stored in the pcb */
+  return udp_sendto(pcb, p, &pcb->remote_ip, pcb->remote_port);
+}
+
+#if LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP
+/** @ingroup udp_raw
+ * Same as udp_send() but with checksum
+ */
+err_t
+udp_send_chksum(struct udp_pcb *pcb, struct pbuf *p,
+                u8_t have_chksum, u16_t chksum)
+{
+  if ((pcb == NULL) || IP_IS_ANY_TYPE_VAL(pcb->remote_ip)) {
+    return ERR_VAL;
+  }
+
+  /* send to the packet using remote ip and port stored in the pcb */
+  return udp_sendto_chksum(pcb, p, &pcb->remote_ip, pcb->remote_port,
+    have_chksum, chksum);
+}
+#endif /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */
+
+/**
+ * @ingroup udp_raw
+ * Send data to a specified address using UDP.
+ *
+ * @param pcb UDP PCB used to send the data.
+ * @param p chain of pbuf's to be sent.
+ * @param dst_ip Destination IP address.
+ * @param dst_port Destination UDP port.
+ *
+ * dst_ip & dst_port are expected to be in the same byte order as in the pcb.
+ *
+ * If the PCB already has a remote address association, it will
+ * be restored after the data is sent.
+ *
+ * @return lwIP error code (@see udp_send for possible error codes)
+ *
+ * @see udp_disconnect() udp_send()
+ */
+err_t
+udp_sendto(struct udp_pcb *pcb, struct pbuf *p,
+  const ip_addr_t *dst_ip, u16_t dst_port)
+{
+#if LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP
+  return udp_sendto_chksum(pcb, p, dst_ip, dst_port, 0, 0);
+}
+
+/** @ingroup udp_raw
+ * Same as udp_sendto(), but with checksum */
+err_t
+udp_sendto_chksum(struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *dst_ip,
+                  u16_t dst_port, u8_t have_chksum, u16_t chksum)
+{
+#endif /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */
+  struct netif *netif;
+  const ip_addr_t *dst_ip_route = dst_ip;
+
+  if ((pcb == NULL) || (dst_ip == NULL) || !IP_ADDR_PCB_VERSION_MATCH(pcb, dst_ip)) {
+    return ERR_VAL;
+  }
+
+  LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_send\n"));
+
+#if LWIP_IPV6 || (LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS)
+  if (ip_addr_ismulticast(dst_ip_route)) {
+#if LWIP_IPV6
+    if (IP_IS_V6(dst_ip)) {
+      /* For multicast, find a netif based on source address. */
+      dst_ip_route = &pcb->local_ip;
+    } else
+#endif /* LWIP_IPV6 */
+    {
+#if LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS
+      /* IPv4 does not use source-based routing by default, so we use an
+         administratively selected interface for multicast by default.
+         However, this can be overridden by setting an interface address
+         in pcb->multicast_ip that is used for routing. */
+      if (!ip_addr_isany_val(pcb->multicast_ip) &&
+          !ip4_addr_cmp(ip_2_ip4(&pcb->multicast_ip), IP4_ADDR_BROADCAST)) {
+        dst_ip_route = &pcb->multicast_ip;
+      }
+#endif /* LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS */
+    }
+  }
+#endif /* LWIP_IPV6 || (LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS) */
+
+  /* find the outgoing network interface for this packet */
+  if(IP_IS_ANY_TYPE_VAL(pcb->local_ip)) {
+    /* Don't call ip_route() with IP_ANY_TYPE */
+    netif = ip_route(IP46_ADDR_ANY(IP_GET_TYPE(dst_ip_route)), dst_ip_route);
+  } else {
+    netif = ip_route(&pcb->local_ip, dst_ip_route);
+  }
+
+  /* no outgoing network interface could be found? */
+  if (netif == NULL) {
+    LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("udp_send: No route to "));
+    ip_addr_debug_print(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, dst_ip);
+    LWIP_DEBUGF(UDP_DEBUG, ("\n"));
+    UDP_STATS_INC(udp.rterr);
+    return ERR_RTE;
+  }
+#if LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP
+  return udp_sendto_if_chksum(pcb, p, dst_ip, dst_port, netif, have_chksum, chksum);
+#else /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */
+  return udp_sendto_if(pcb, p, dst_ip, dst_port, netif);
+#endif /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */
+}
+
+/**
+ * @ingroup udp_raw
+ * Send data to a specified address using UDP.
+ * The netif used for sending can be specified.
+ *
+ * This function exists mainly for DHCP, to be able to send UDP packets
+ * on a netif that is still down.
+ *
+ * @param pcb UDP PCB used to send the data.
+ * @param p chain of pbuf's to be sent.
+ * @param dst_ip Destination IP address.
+ * @param dst_port Destination UDP port.
+ * @param netif the netif used for sending.
+ *
+ * dst_ip & dst_port are expected to be in the same byte order as in the pcb.
+ *
+ * @return lwIP error code (@see udp_send for possible error codes)
+ *
+ * @see udp_disconnect() udp_send()
+ */
+err_t
+udp_sendto_if(struct udp_pcb *pcb, struct pbuf *p,
+  const ip_addr_t *dst_ip, u16_t dst_port, struct netif *netif)
+{
+#if LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP
+  return udp_sendto_if_chksum(pcb, p, dst_ip, dst_port, netif, 0, 0);
+}
+
+/** Same as udp_sendto_if(), but with checksum */
+err_t
+udp_sendto_if_chksum(struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *dst_ip,
+                     u16_t dst_port, struct netif *netif, u8_t have_chksum,
+                     u16_t chksum)
+{
+#endif /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */
+  const ip_addr_t *src_ip;
+
+  if ((pcb == NULL) || (dst_ip == NULL) || !IP_ADDR_PCB_VERSION_MATCH(pcb, dst_ip)) {
+    return ERR_VAL;
+  }
+
+  /* PCB local address is IP_ANY_ADDR? */
+#if LWIP_IPV6
+  if (IP_IS_V6(dst_ip)) {
+    if (ip6_addr_isany(ip_2_ip6(&pcb->local_ip))) {
+      src_ip = ip6_select_source_address(netif, ip_2_ip6(dst_ip));
+      if (src_ip == NULL) {
+        /* No suitable source address was found. */
+        return ERR_RTE;
+      }
+    } else {
+      /* use UDP PCB local IPv6 address as source address, if still valid. */
+      if (netif_get_ip6_addr_match(netif, ip_2_ip6(&pcb->local_ip)) < 0) {
+        /* Address isn't valid anymore. */
+        return ERR_RTE;
+      }
+      src_ip = &pcb->local_ip;
+    }
+  }
+#endif /* LWIP_IPV6 */
+#if LWIP_IPV4 && LWIP_IPV6
+  else
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+#if LWIP_IPV4
+  if (ip4_addr_isany(ip_2_ip4(&pcb->local_ip)) ||
+      ip4_addr_ismulticast(ip_2_ip4(&pcb->local_ip))) {
+    /* if the local_ip is any or multicast
+     * use the outgoing network interface IP address as source address */
+    src_ip = netif_ip_addr4(netif);
+  } else {
+    /* check if UDP PCB local IP address is correct
+     * this could be an old address if netif->ip_addr has changed */
+    if (!ip4_addr_cmp(ip_2_ip4(&(pcb->local_ip)), netif_ip4_addr(netif))) {
+      /* local_ip doesn't match, drop the packet */
+      return ERR_RTE;
+    }
+    /* use UDP PCB local IP address as source address */
+    src_ip = &pcb->local_ip;
+  }
+#endif /* LWIP_IPV4 */
+#if LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP
+  return udp_sendto_if_src_chksum(pcb, p, dst_ip, dst_port, netif, have_chksum, chksum, src_ip);
+#else /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */
+  return udp_sendto_if_src(pcb, p, dst_ip, dst_port, netif, src_ip);
+#endif /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */
+}
+
+/** @ingroup udp_raw
+ * Same as @ref udp_sendto_if, but with source address */
+err_t
+udp_sendto_if_src(struct udp_pcb *pcb, struct pbuf *p,
+  const ip_addr_t *dst_ip, u16_t dst_port, struct netif *netif, const ip_addr_t *src_ip)
+{
+#if LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP
+  return udp_sendto_if_src_chksum(pcb, p, dst_ip, dst_port, netif, 0, 0, src_ip);
+}
+
+/** Same as udp_sendto_if_src(), but with checksum */
+err_t
+udp_sendto_if_src_chksum(struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *dst_ip,
+                     u16_t dst_port, struct netif *netif, u8_t have_chksum,
+                     u16_t chksum, const ip_addr_t *src_ip)
+{
+#endif /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */
+  struct udp_hdr *udphdr;
+  err_t err;
+  struct pbuf *q; /* q will be sent down the stack */
+  u8_t ip_proto;
+  u8_t ttl;
+
+  if ((pcb == NULL) || (dst_ip == NULL) || !IP_ADDR_PCB_VERSION_MATCH(pcb, src_ip) ||
+      !IP_ADDR_PCB_VERSION_MATCH(pcb, dst_ip)) {
+    return ERR_VAL;
+  }
+
+#if LWIP_IPV4 && IP_SOF_BROADCAST
+  /* broadcast filter? */
+  if (!ip_get_option(pcb, SOF_BROADCAST) &&
+#if LWIP_IPV6
+      IP_IS_V4(dst_ip) &&
+#endif /* LWIP_IPV6 */
+      ip_addr_isbroadcast(dst_ip, netif)) {
+    LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+      ("udp_sendto_if: SOF_BROADCAST not enabled on pcb %p\n", (void *)pcb));
+    return ERR_VAL;
+  }
+#endif /* LWIP_IPV4 && IP_SOF_BROADCAST */
+
+  /* if the PCB is not yet bound to a port, bind it here */
+  if (pcb->local_port == 0) {
+    LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_send: not yet bound to a port, binding now\n"));
+    err = udp_bind(pcb, &pcb->local_ip, pcb->local_port);
+    if (err != ERR_OK) {
+      LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("udp_send: forced port bind failed\n"));
+      return err;
+    }
+  }
+
+  /* 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 separate new pbuf */
+    q = pbuf_alloc(PBUF_IP, UDP_HLEN, PBUF_RAM);
+    /* new header pbuf could not be allocated? */
+    if (q == NULL) {
+      LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("udp_send: could not allocate header\n"));
+      return ERR_MEM;
+    }
+    if (p->tot_len != 0) {
+      /* chain header q in front of given pbuf p (only if p contains data) */
+      pbuf_chain(q, p);
+    }
+    /* first pbuf q points to header pbuf */
+    LWIP_DEBUGF(UDP_DEBUG,
+                ("udp_send: added header pbuf %p before given pbuf %p\n", (void *)q, (void *)p));
+  } else {
+    /* adding space for header within p succeeded */
+    /* first pbuf q equals given pbuf */
+    q = p;
+    LWIP_DEBUGF(UDP_DEBUG, ("udp_send: added header in given pbuf %p\n", (void *)p));
+  }
+  LWIP_ASSERT("check that first pbuf can hold struct udp_hdr",
+              (q->len >= sizeof(struct udp_hdr)));
+  /* q now represents the packet to be sent */
+  udphdr = (struct udp_hdr *)q->payload;
+  udphdr->src = lwip_htons(pcb->local_port);
+  udphdr->dest = lwip_htons(dst_port);
+  /* in UDP, 0 checksum means 'no checksum' */
+  udphdr->chksum = 0x0000;
+
+  /* Multicast Loop? */
+#if (LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS) || (LWIP_IPV6 && LWIP_IPV6_MLD)
+  if (((pcb->flags & UDP_FLAGS_MULTICAST_LOOP) != 0) && ip_addr_ismulticast(dst_ip)) {
+    q->flags |= PBUF_FLAG_MCASTLOOP;
+  }
+#endif /* (LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS) || (LWIP_IPV6 && LWIP_IPV6_MLD) */
+
+  LWIP_DEBUGF(UDP_DEBUG, ("udp_send: sending datagram of length %"U16_F"\n", q->tot_len));
+
+#if LWIP_UDPLITE
+  /* UDP Lite protocol? */
+  if (pcb->flags & UDP_FLAGS_UDPLITE) {
+    u16_t chklen, chklen_hdr;
+    LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP LITE packet length %"U16_F"\n", q->tot_len));
+    /* set UDP message length in UDP header */
+    chklen_hdr = chklen = pcb->chksum_len_tx;
+    if ((chklen < sizeof(struct udp_hdr)) || (chklen > q->tot_len)) {
+      if (chklen != 0) {
+        LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP LITE pcb->chksum_len is illegal: %"U16_F"\n", chklen));
+      }
+      /* For UDP-Lite, checksum length of 0 means checksum
+         over the complete packet. (See RFC 3828 chap. 3.1)
+         At least the UDP-Lite header must be covered by the
+         checksum, therefore, if chksum_len has an illegal
+         value, we generate the checksum over the complete
+         packet to be safe. */
+      chklen_hdr = 0;
+      chklen = q->tot_len;
+    }
+    udphdr->len = lwip_htons(chklen_hdr);
+    /* calculate checksum */
+#if CHECKSUM_GEN_UDP
+    IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_UDP) {
+#if LWIP_CHECKSUM_ON_COPY
+      if (have_chksum) {
+        chklen = UDP_HLEN;
+      }
+#endif /* LWIP_CHECKSUM_ON_COPY */
+      udphdr->chksum = ip_chksum_pseudo_partial(q, IP_PROTO_UDPLITE,
+        q->tot_len, chklen, src_ip, dst_ip);
+#if LWIP_CHECKSUM_ON_COPY
+      if (have_chksum) {
+        u32_t acc;
+        acc = udphdr->chksum + (u16_t)~(chksum);
+        udphdr->chksum = FOLD_U32T(acc);
+      }
+#endif /* LWIP_CHECKSUM_ON_COPY */
+
+      /* chksum zero must become 0xffff, as zero means 'no checksum' */
+      if (udphdr->chksum == 0x0000) {
+        udphdr->chksum = 0xffff;
+      }
+    }
+#endif /* CHECKSUM_GEN_UDP */
+
+    ip_proto = IP_PROTO_UDPLITE;
+  } else
+#endif /* LWIP_UDPLITE */
+  {      /* UDP */
+    LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP packet length %"U16_F"\n", q->tot_len));
+    udphdr->len = lwip_htons(q->tot_len);
+    /* calculate checksum */
+#if CHECKSUM_GEN_UDP
+    IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_UDP) {
+      /* Checksum is mandatory over IPv6. */
+      if (IP_IS_V6(dst_ip) || (pcb->flags & UDP_FLAGS_NOCHKSUM) == 0) {
+        u16_t udpchksum;
+#if LWIP_CHECKSUM_ON_COPY
+        if (have_chksum) {
+          u32_t acc;
+          udpchksum = ip_chksum_pseudo_partial(q, IP_PROTO_UDP,
+            q->tot_len, UDP_HLEN, src_ip, dst_ip);
+          acc = udpchksum + (u16_t)~(chksum);
+          udpchksum = FOLD_U32T(acc);
+        } else
+#endif /* LWIP_CHECKSUM_ON_COPY */
+        {
+          udpchksum = ip_chksum_pseudo(q, IP_PROTO_UDP, q->tot_len,
+            src_ip, dst_ip);
+        }
+
+        /* chksum zero must become 0xffff, as zero means 'no checksum' */
+        if (udpchksum == 0x0000) {
+          udpchksum = 0xffff;
+        }
+        udphdr->chksum = udpchksum;
+      }
+    }
+#endif /* CHECKSUM_GEN_UDP */
+    ip_proto = IP_PROTO_UDP;
+  }
+
+  /* Determine TTL to use */
+#if LWIP_MULTICAST_TX_OPTIONS
+  ttl = (ip_addr_ismulticast(dst_ip) ? udp_get_multicast_ttl(pcb) : pcb->ttl);
+#else /* LWIP_MULTICAST_TX_OPTIONS */
+  ttl = pcb->ttl;
+#endif /* LWIP_MULTICAST_TX_OPTIONS */
+
+  LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP checksum 0x%04"X16_F"\n", udphdr->chksum));
+  LWIP_DEBUGF(UDP_DEBUG, ("udp_send: ip_output_if (,,,,0x%02"X16_F",)\n", (u16_t)ip_proto));
+  /* output to IP */
+  NETIF_SET_HWADDRHINT(netif, &(pcb->addr_hint));
+  err = ip_output_if_src(q, src_ip, dst_ip, ttl, pcb->tos, ip_proto, netif);
+  NETIF_SET_HWADDRHINT(netif, NULL);
+
+  /* @todo: must this be increased even if error occurred? */
+  MIB2_STATS_INC(mib2.udpoutdatagrams);
+
+  /* did we chain a separate header pbuf earlier? */
+  if (q != p) {
+    /* free the header pbuf */
+    pbuf_free(q);
+    q = NULL;
+    /* p is still referenced by the caller, and will live on */
+  }
+
+  UDP_STATS_INC(udp.xmit);
+  return err;
+}
+
+/**
+ * @ingroup udp_raw
+ * Bind an UDP PCB.
+ *
+ * @param pcb UDP PCB to be bound with a local address ipaddr and port.
+ * @param ipaddr local IP address to bind with. Use IP4_ADDR_ANY to
+ * bind to all local interfaces.
+ * @param port local UDP port to bind with. Use 0 to automatically bind
+ * to a random port between UDP_LOCAL_PORT_RANGE_START and
+ * UDP_LOCAL_PORT_RANGE_END.
+ *
+ * ipaddr & port are expected to be in the same byte order as in the pcb.
+ *
+ * @return lwIP error code.
+ * - ERR_OK. Successful. No error occurred.
+ * - ERR_USE. The specified ipaddr and port are already bound to by
+ * another UDP PCB.
+ *
+ * @see udp_disconnect()
+ */
+err_t
+udp_bind(struct udp_pcb *pcb, const ip_addr_t *ipaddr, u16_t port)
+{
+  struct udp_pcb *ipcb;
+  u8_t rebind;
+
+#if LWIP_IPV4
+  /* Don't propagate NULL pointer (IPv4 ANY) to subsequent functions */
+  if (ipaddr == NULL) {
+    ipaddr = IP4_ADDR_ANY;
+  }
+#endif /* LWIP_IPV4 */
+
+  /* still need to check for ipaddr == NULL in IPv6 only case */
+  if ((pcb == NULL) || (ipaddr == NULL)) {
+    return ERR_VAL;
+  }
+
+  LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_bind(ipaddr = "));
+  ip_addr_debug_print(UDP_DEBUG | LWIP_DBG_TRACE, ipaddr);
+  LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, (", port = %"U16_F")\n", port));
+
+  rebind = 0;
+  /* Check for double bind and rebind of the same pcb */
+  for (ipcb = udp_pcbs; ipcb != NULL; ipcb = ipcb->next) {
+    /* is this UDP PCB already on active list? */
+    if (pcb == ipcb) {
+      rebind = 1;
+      break;
+    }
+  }
+
+  /* no port specified? */
+  if (port == 0) {
+    port = udp_new_port();
+    if (port == 0) {
+      /* no more ports available in local range */
+      LWIP_DEBUGF(UDP_DEBUG, ("udp_bind: out of free UDP ports\n"));
+      return ERR_USE;
+    }
+  } else {
+    for (ipcb = udp_pcbs; ipcb != NULL; ipcb = ipcb->next) {
+      if (pcb != ipcb) {
+      /* By default, we don't allow to bind to a port that any other udp
+         PCB is already bound to, unless *all* PCBs with that port have tha
+         REUSEADDR flag set. */
+#if SO_REUSE
+        if (!ip_get_option(pcb, SOF_REUSEADDR) ||
+            !ip_get_option(ipcb, SOF_REUSEADDR))
+#endif /* SO_REUSE */
+        {
+          /* port matches that of PCB in list and REUSEADDR not set -> reject */
+          if ((ipcb->local_port == port) &&
+              /* IP address matches? */
+              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;
+          }
+        }
+      }
+    }
+  }
+
+  ip_addr_set_ipaddr(&pcb->local_ip, ipaddr);
+
+  pcb->local_port = port;
+  mib2_udp_bind(pcb);
+  /* pcb not active yet? */
+  if (rebind == 0) {
+    /* place the PCB on the active list if not already there */
+    pcb->next = udp_pcbs;
+    udp_pcbs = pcb;
+  }
+  LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("udp_bind: bound to "));
+  ip_addr_debug_print(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, &pcb->local_ip);
+  LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, (", port %"U16_F")\n", pcb->local_port));
+  return ERR_OK;
+}
+
+/**
+ * @ingroup udp_raw
+ * 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
+ *
+ * ipaddr & port are expected to be in the same byte order as in the pcb.
+ *
+ * The udp pcb is bound to a random local port if not already bound.
+ *
+ * @see udp_disconnect()
+ */
+err_t
+udp_connect(struct udp_pcb *pcb, const ip_addr_t *ipaddr, u16_t port)
+{
+  struct udp_pcb *ipcb;
+
+  if ((pcb == NULL) || (ipaddr == NULL)) {
+    return ERR_VAL;
+  }
+
+  if (pcb->local_port == 0) {
+    err_t err = udp_bind(pcb, &pcb->local_ip, pcb->local_port);
+    if (err != ERR_OK) {
+      return err;
+    }
+  }
+
+  ip_addr_set_ipaddr(&pcb->remote_ip, ipaddr);
+  pcb->remote_port = port;
+  pcb->flags |= UDP_FLAGS_CONNECTED;
+
+  LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("udp_connect: connected to "));
+  ip_addr_debug_print(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+                      &pcb->remote_ip);
+  LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, (", port %"U16_F")\n", pcb->remote_port));
+
+  /* Insert UDP PCB into the list of active UDP PCBs. */
+  for (ipcb = udp_pcbs; ipcb != NULL; ipcb = ipcb->next) {
+    if (pcb == ipcb) {
+      /* already on the list, just return */
+      return ERR_OK;
+    }
+  }
+  /* PCB not yet on the list, add PCB now */
+  pcb->next = udp_pcbs;
+  udp_pcbs = pcb;
+  return ERR_OK;
+}
+
+/**
+ * @ingroup udp_raw
+ * Disconnect a UDP PCB
+ *
+ * @param pcb the udp pcb to disconnect.
+ */
+void
+udp_disconnect(struct udp_pcb *pcb)
+{
+  /* reset remote address association */
+#if LWIP_IPV4 && LWIP_IPV6
+  if (IP_IS_ANY_TYPE_VAL(pcb->local_ip)) {
+    ip_addr_copy(pcb->remote_ip, *IP_ANY_TYPE);
+  } else {
+#endif
+    ip_addr_set_any(IP_IS_V6_VAL(pcb->remote_ip), &pcb->remote_ip);
+#if LWIP_IPV4 && LWIP_IPV6
+  }
+#endif
+  pcb->remote_port = 0;
+  /* mark PCB as unconnected */
+  pcb->flags &= ~UDP_FLAGS_CONNECTED;
+}
+
+/**
+ * @ingroup udp_raw
+ * Set a receive callback for a UDP PCB
+ *
+ * This callback will be called when receiving a datagram for the pcb.
+ *
+ * @param pcb the pcb for which to set the recv callback
+ * @param recv function pointer of the callback function
+ * @param recv_arg additional argument to pass to the callback function
+ */
+void
+udp_recv(struct udp_pcb *pcb, udp_recv_fn recv, void *recv_arg)
+{
+  /* remember recv() callback and user data */
+  pcb->recv = recv;
+  pcb->recv_arg = recv_arg;
+}
+
+/**
+ * @ingroup udp_raw
+ * 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;
+
+  mib2_udp_unbind(pcb);
+  /* pcb to be removed is first in list? */
+  if (udp_pcbs == pcb) {
+    /* make list start at 2nd pcb */
+    udp_pcbs = udp_pcbs->next;
+    /* pcb not 1st in list */
+  } else {
+    for (pcb2 = udp_pcbs; pcb2 != NULL; pcb2 = pcb2->next) {
+      /* find pcb in udp_pcbs list */
+      if (pcb2->next != NULL && pcb2->next == pcb) {
+        /* remove pcb from list */
+        pcb2->next = pcb->next;
+        break;
+      }
+    }
+  }
+  memp_free(MEMP_UDP_PCB, pcb);
+}
+
+/**
+ * @ingroup udp_raw
+ * 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 = (struct udp_pcb *)memp_malloc(MEMP_UDP_PCB);
+  /* could allocate UDP PCB? */
+  if (pcb != NULL) {
+    /* UDP Lite: by initializing to all zeroes, chksum_len is set to 0
+     * which means checksum is generated over the whole datagram per default
+     * (recommended as default by RFC 3828). */
+    /* initialize PCB to all zeroes */
+    memset(pcb, 0, sizeof(struct udp_pcb));
+    pcb->ttl = UDP_TTL;
+#if LWIP_MULTICAST_TX_OPTIONS
+    udp_set_multicast_ttl(pcb, UDP_TTL);
+#endif /* LWIP_MULTICAST_TX_OPTIONS */
+  }
+  return pcb;
+}
+
+/**
+ * @ingroup udp_raw
+ * Create a UDP PCB for specific IP type.
+ *
+ * @param type IP address type, see @ref lwip_ip_addr_type definitions.
+ * If you want to listen to IPv4 and IPv6 (dual-stack) packets,
+ * supply @ref IPADDR_TYPE_ANY as argument and bind to @ref IP_ANY_TYPE.
+ * @return The UDP PCB which was created. NULL if the PCB data structure
+ * could not be allocated.
+ *
+ * @see udp_remove()
+ */
+struct udp_pcb *
+udp_new_ip_type(u8_t type)
+{
+  struct udp_pcb *pcb;
+  pcb = udp_new();
+#if LWIP_IPV4 && LWIP_IPV6
+  if (pcb != NULL) {
+    IP_SET_TYPE_VAL(pcb->local_ip,  type);
+    IP_SET_TYPE_VAL(pcb->remote_ip, type);
+  }
+#else
+  LWIP_UNUSED_ARG(type);
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+  return pcb;
+}
+
+/** This function is called from netif.c when address is changed
+ *
+ * @param old_addr IP address of the netif before change
+ * @param new_addr IP address of the netif after change
+ */
+void udp_netif_ip_addr_changed(const ip_addr_t* old_addr, const ip_addr_t* new_addr)
+{
+  struct udp_pcb* upcb;
+
+  if (!ip_addr_isany(old_addr) && !ip_addr_isany(new_addr)) {
+    for (upcb = udp_pcbs; upcb != NULL; upcb = upcb->next) {
+      /* PCB bound to current local interface address? */
+      if (ip_addr_cmp(&upcb->local_ip, old_addr)) {
+        /* The PCB is bound to the old ipaddr and
+         * is set to bound to the new one instead */
+        ip_addr_copy(upcb->local_ip, *new_addr);
+      }
+    }
+  }
+}
+
+#if UDP_DEBUG
+/**
+ * Print UDP header information for debug purposes.
+ *
+ * @param udphdr pointer to the udp header in memory.
+ */
+void
+udp_debug_print(struct udp_hdr *udphdr)
+{
+  LWIP_DEBUGF(UDP_DEBUG, ("UDP header:\n"));
+  LWIP_DEBUGF(UDP_DEBUG, ("+-------------------------------+\n"));
+  LWIP_DEBUGF(UDP_DEBUG, ("|     %5"U16_F"     |     %5"U16_F"     | (src port, dest port)\n",
+                          lwip_ntohs(udphdr->src), lwip_ntohs(udphdr->dest)));
+  LWIP_DEBUGF(UDP_DEBUG, ("+-------------------------------+\n"));
+  LWIP_DEBUGF(UDP_DEBUG, ("|     %5"U16_F"     |     0x%04"X16_F"    | (len, chksum)\n",
+                          lwip_ntohs(udphdr->len), lwip_ntohs(udphdr->chksum)));
+  LWIP_DEBUGF(UDP_DEBUG, ("+-------------------------------+\n"));
+}
+#endif /* UDP_DEBUG */
+
+#endif /* LWIP_UDP */

二進制
thirdparty/lwip/src/include/ipv4/.DS_Store


+ 0 - 118
thirdparty/lwip/src/include/ipv4/lwip/autoip.h

@@ -1,118 +0,0 @@
-/**
- * @file
- *
- * AutoIP Automatic LinkLocal IP Configuration
- */
-
-/*
- *
- * Copyright (c) 2007 Dominik Spies <kontakt@dspies.de>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * Author: Dominik Spies <kontakt@dspies.de>
- *
- * This is a AutoIP implementation for the lwIP TCP/IP stack. It aims to conform
- * with RFC 3927.
- *
- *
- * Please coordinate changes and requests with Dominik Spies
- * <kontakt@dspies.de>
- */
- 
-#ifndef __LWIP_AUTOIP_H__
-#define __LWIP_AUTOIP_H__
-
-#include "lwip/opt.h"
-
-#if LWIP_AUTOIP /* don't build if not configured for use in lwipopts.h */
-
-#include "lwip/netif.h"
-#include "lwip/udp.h"
-#include "netif/etharp.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* AutoIP Timing */
-#define AUTOIP_TMR_INTERVAL      100
-#define AUTOIP_TICKS_PER_SECOND (1000 / AUTOIP_TMR_INTERVAL)
-
-/* RFC 3927 Constants */
-#define PROBE_WAIT               1   /* second   (initial random delay)                 */
-#define PROBE_MIN                1   /* second   (minimum delay till repeated probe)    */
-#define PROBE_MAX                2   /* seconds  (maximum delay till repeated probe)    */
-#define PROBE_NUM                3   /*          (number of probe packets)              */
-#define ANNOUNCE_NUM             2   /*          (number of announcement packets)       */
-#define ANNOUNCE_INTERVAL        2   /* seconds  (time between announcement packets)    */
-#define ANNOUNCE_WAIT            2   /* seconds  (delay before announcing)              */
-#define MAX_CONFLICTS            10  /*          (max conflicts before rate limiting)   */
-#define RATE_LIMIT_INTERVAL      60  /* seconds  (delay between successive attempts)    */
-#define DEFEND_INTERVAL          10  /* seconds  (min. wait between defensive ARPs)     */
-
-/* AutoIP client states */
-#define AUTOIP_STATE_OFF         0
-#define AUTOIP_STATE_PROBING     1
-#define AUTOIP_STATE_ANNOUNCING  2
-#define AUTOIP_STATE_BOUND       3
-
-struct autoip
-{
-  ip_addr_t llipaddr;       /* the currently selected, probed, announced or used LL IP-Address */
-  u8_t state;               /* current AutoIP state machine state */
-  u8_t sent_num;            /* sent number of probes or announces, dependent on state */
-  u16_t ttw;                /* ticks to wait, tick is AUTOIP_TMR_INTERVAL long */
-  u8_t lastconflict;        /* ticks until a conflict can be solved by defending */
-  u8_t tried_llipaddr;      /* total number of probed/used Link Local IP-Addresses */
-};
-
-
-#define autoip_init() /* Compatibility define, no init needed. */
-
-/** Set a struct autoip allocated by the application to work with */
-void autoip_set_struct(struct netif *netif, struct autoip *autoip);
-
-/** Start AutoIP client */
-err_t autoip_start(struct netif *netif);
-
-/** Stop AutoIP client */
-err_t autoip_stop(struct netif *netif);
-
-/** Handles every incoming ARP Packet, called by etharp_arp_input */
-void autoip_arp_reply(struct netif *netif, struct etharp_hdr *hdr);
-
-/** Has to be called in loop every AUTOIP_TMR_INTERVAL milliseconds */
-void autoip_tmr(void);
-
-/** Handle a possible change in the network configuration */
-void autoip_network_changed(struct netif *netif);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* LWIP_AUTOIP */
-
-#endif /* __LWIP_AUTOIP_H__ */

+ 0 - 107
thirdparty/lwip/src/include/ipv4/lwip/inet.h

@@ -1,107 +0,0 @@
-/*
- * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
- * All rights reserved. 
- * 
- * Redistribution and use in source and binary forms, with or without modification, 
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission. 
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
- * OF SUCH DAMAGE.
- *
- * This file is part of the lwIP TCP/IP stack.
- * 
- * Author: Adam Dunkels <adam@sics.se>
- *
- */
-#ifndef __LWIP_INET_H__
-#define __LWIP_INET_H__
-
-#include "lwip/opt.h"
-#include "lwip/def.h"
-#include "lwip/ip_addr.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/** For compatibility with BSD code */
-struct in_addr {
-  u32_t s_addr;
-};
-
-/** 255.255.255.255 */
-#define INADDR_NONE         IPADDR_NONE
-/** 127.0.0.1 */
-#define INADDR_LOOPBACK     IPADDR_LOOPBACK
-/** 0.0.0.0 */
-#define INADDR_ANY          IPADDR_ANY
-/** 255.255.255.255 */
-#define INADDR_BROADCAST    IPADDR_BROADCAST
-
-/* Definitions of the bits in an Internet address integer.
-
-   On subnets, host and network parts are found according to
-   the subnet mask, not these masks.  */
-#define IN_CLASSA(a)        IP_CLASSA(a)
-#define IN_CLASSA_NET       IP_CLASSA_NET
-#define IN_CLASSA_NSHIFT    IP_CLASSA_NSHIFT
-#define IN_CLASSA_HOST      IP_CLASSA_HOST
-#define IN_CLASSA_MAX       IP_CLASSA_MAX
-
-#define IN_CLASSB(b)        IP_CLASSB(b)
-#define IN_CLASSB_NET       IP_CLASSB_NET
-#define IN_CLASSB_NSHIFT    IP_CLASSB_NSHIFT
-#define IN_CLASSB_HOST      IP_CLASSB_HOST
-#define IN_CLASSB_MAX       IP_CLASSB_MAX
-
-#define IN_CLASSC(c)        IP_CLASSC(c)
-#define IN_CLASSC_NET       IP_CLASSC_NET
-#define IN_CLASSC_NSHIFT    IP_CLASSC_NSHIFT
-#define IN_CLASSC_HOST      IP_CLASSC_HOST
-#define IN_CLASSC_MAX       IP_CLASSC_MAX
-
-#define IN_CLASSD(d)        IP_CLASSD(d)
-#define IN_CLASSD_NET       IP_CLASSD_NET     /* These ones aren't really */
-#define IN_CLASSD_NSHIFT    IP_CLASSD_NSHIFT  /*   net and host fields, but */
-#define IN_CLASSD_HOST      IP_CLASSD_HOST    /*   routing needn't know. */
-#define IN_CLASSD_MAX       IP_CLASSD_MAX
-
-#define IN_MULTICAST(a)     IP_MULTICAST(a)
-
-#define IN_EXPERIMENTAL(a)  IP_EXPERIMENTAL(a)
-#define IN_BADCLASS(a)      IP_BADCLASS(a)
-
-#define IN_LOOPBACKNET      IP_LOOPBACKNET
-
-#define inet_addr_from_ipaddr(target_inaddr, source_ipaddr) ((target_inaddr)->s_addr = ip4_addr_get_u32(source_ipaddr))
-#define inet_addr_to_ipaddr(target_ipaddr, source_inaddr)   (ip4_addr_set_u32(target_ipaddr, (source_inaddr)->s_addr))
-/* ATTENTION: the next define only works because both s_addr and ip_addr_t are an u32_t effectively! */
-#define inet_addr_to_ipaddr_p(target_ipaddr_p, source_inaddr)   ((target_ipaddr_p) = (ip_addr_t*)&((source_inaddr)->s_addr))
-
-/* directly map this to the lwip internal functions */
-#define inet_addr(cp)         ipaddr_addr(cp)
-#define inet_aton(cp, addr)   ipaddr_aton(cp, (ip_addr_t*)addr)
-#define inet_ntoa(addr)       ipaddr_ntoa((ip_addr_t*)&(addr))
-#define inet_ntoa_r(addr, buf, buflen) ipaddr_ntoa_r((ip_addr_t*)&(addr), buf, buflen)
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* __LWIP_INET_H__ */

+ 0 - 223
thirdparty/lwip/src/include/ipv4/lwip/ip.h

@@ -1,223 +0,0 @@
-/*
- * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
- * All rights reserved. 
- * 
- * Redistribution and use in source and binary forms, with or without modification, 
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission. 
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
- * OF SUCH DAMAGE.
- *
- * This file is part of the lwIP TCP/IP stack.
- * 
- * Author: Adam Dunkels <adam@sics.se>
- *
- */
-#ifndef __LWIP_IP_H__
-#define __LWIP_IP_H__
-
-#include "lwip/opt.h"
-
-#include "lwip/def.h"
-#include "lwip/pbuf.h"
-#include "lwip/ip_addr.h"
-#include "lwip/err.h"
-#include "lwip/netif.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/** Currently, the function ip_output_if_opt() is only used with IGMP */
-#define IP_OPTIONS_SEND   LWIP_IGMP
-
-#define IP_HLEN 20
-
-#define IP_PROTO_ICMP    1
-#define IP_PROTO_IGMP    2
-#define IP_PROTO_UDP     17
-#define IP_PROTO_UDPLITE 136
-#define IP_PROTO_TCP     6
-
-/* This is passed as the destination address to ip_output_if (not
-   to ip_output), meaning that an IP header already is constructed
-   in the pbuf. This is used when TCP retransmits. */
-#ifdef IP_HDRINCL
-#undef IP_HDRINCL
-#endif /* IP_HDRINCL */
-#define IP_HDRINCL  NULL
-
-#if LWIP_NETIF_HWADDRHINT
-#define IP_PCB_ADDRHINT ;u8_t addr_hint
-#else
-#define IP_PCB_ADDRHINT
-#endif /* LWIP_NETIF_HWADDRHINT */
-
-/* This is the common part of all PCB types. It needs to be at the
-   beginning of a PCB type definition. It is located here so that
-   changes to this common part are made in one location instead of
-   having to change all PCB structs. */
-#define IP_PCB \
-  /* ip addresses in network byte order */ \
-  ip_addr_t local_ip; \
-  ip_addr_t remote_ip; \
-   /* Socket options */  \
-  u8_t so_options;      \
-   /* Type Of Service */ \
-  u8_t tos;              \
-  /* Time To Live */     \
-  u8_t ttl               \
-  /* link layer address resolution hint */ \
-  IP_PCB_ADDRHINT
-
-struct ip_pcb {
-/* Common members of all PCB types */
-  IP_PCB;
-};
-
-/*
- * Option flags per-socket. These are the same like SO_XXX.
- */
-/*#define SOF_DEBUG       0x01U     Unimplemented: turn on debugging info recording */
-#define SOF_ACCEPTCONN    0x02U  /* socket has had listen() */
-#define SOF_REUSEADDR     0x04U  /* allow local address reuse */
-#define SOF_KEEPALIVE     0x08U  /* keep connections alive */
-/*#define SOF_DONTROUTE   0x10U     Unimplemented: just use interface addresses */
-#define SOF_BROADCAST     0x20U  /* permit to send and to receive broadcast messages (see IP_SOF_BROADCAST option) */
-/*#define SOF_USELOOPBACK 0x40U     Unimplemented: bypass hardware when possible */
-#define SOF_LINGER        0x80U  /* linger on close if data present */
-/*#define SOF_OOBINLINE   0x0100U   Unimplemented: leave received OOB data in line */
-/*#define SOF_REUSEPORT   0x0200U   Unimplemented: allow local address & port reuse */
-
-/* These flags are inherited (e.g. from a listen-pcb to a connection-pcb): */
-#define SOF_INHERITED   (SOF_REUSEADDR|SOF_KEEPALIVE|SOF_LINGER/*|SOF_DEBUG|SOF_DONTROUTE|SOF_OOBINLINE*/)
-
-
-#ifdef PACK_STRUCT_USE_INCLUDES
-#  include "arch/bpstruct.h"
-#endif
-PACK_STRUCT_BEGIN
-struct ip_hdr {
-  /* version / header length */
-  PACK_STRUCT_FIELD(u8_t _v_hl);
-  /* type of service */
-  PACK_STRUCT_FIELD(u8_t _tos);
-  /* total length */
-  PACK_STRUCT_FIELD(u16_t _len);
-  /* identification */
-  PACK_STRUCT_FIELD(u16_t _id);
-  /* fragment offset field */
-  PACK_STRUCT_FIELD(u16_t _offset);
-#define IP_RF 0x8000U        /* reserved fragment flag */
-#define IP_DF 0x4000U        /* dont fragment flag */
-#define IP_MF 0x2000U        /* more fragments flag */
-#define IP_OFFMASK 0x1fffU   /* mask for fragmenting bits */
-  /* time to live */
-  PACK_STRUCT_FIELD(u8_t _ttl);
-  /* protocol*/
-  PACK_STRUCT_FIELD(u8_t _proto);
-  /* checksum */
-  PACK_STRUCT_FIELD(u16_t _chksum);
-  /* source and destination IP addresses */
-  PACK_STRUCT_FIELD(ip_addr_p_t src);
-  PACK_STRUCT_FIELD(ip_addr_p_t dest); 
-} PACK_STRUCT_STRUCT;
-PACK_STRUCT_END
-#ifdef PACK_STRUCT_USE_INCLUDES
-#  include "arch/epstruct.h"
-#endif
-
-#define IPH_V(hdr)  ((hdr)->_v_hl >> 4)
-#define IPH_HL(hdr) ((hdr)->_v_hl & 0x0f)
-#define IPH_TOS(hdr) ((hdr)->_tos)
-#define IPH_LEN(hdr) ((hdr)->_len)
-#define IPH_ID(hdr) ((hdr)->_id)
-#define IPH_OFFSET(hdr) ((hdr)->_offset)
-#define IPH_TTL(hdr) ((hdr)->_ttl)
-#define IPH_PROTO(hdr) ((hdr)->_proto)
-#define IPH_CHKSUM(hdr) ((hdr)->_chksum)
-
-#define IPH_VHL_SET(hdr, v, hl) (hdr)->_v_hl = (((v) << 4) | (hl))
-#define IPH_TOS_SET(hdr, tos) (hdr)->_tos = (tos)
-#define IPH_LEN_SET(hdr, len) (hdr)->_len = (len)
-#define IPH_ID_SET(hdr, id) (hdr)->_id = (id)
-#define IPH_OFFSET_SET(hdr, off) (hdr)->_offset = (off)
-#define IPH_TTL_SET(hdr, ttl) (hdr)->_ttl = (u8_t)(ttl)
-#define IPH_PROTO_SET(hdr, proto) (hdr)->_proto = (u8_t)(proto)
-#define IPH_CHKSUM_SET(hdr, chksum) (hdr)->_chksum = (chksum)
-
-/** The interface that provided the packet for the current callback invocation. */
-extern struct netif *current_netif;
-/** Header of the input packet currently being processed. */
-extern const struct ip_hdr *current_header;
-/** Source IP address of current_header */
-extern ip_addr_t current_iphdr_src;
-/** Destination IP address of current_header */
-extern ip_addr_t current_iphdr_dest;
-
-#define ip_init() /* Compatibility define, not init needed. */
-struct netif *ip_route(ip_addr_t *dest);
-err_t ip_input(struct pbuf *p, struct netif *inp);
-err_t ip_output(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest,
-       u8_t ttl, u8_t tos, u8_t proto);
-err_t ip_output_if(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest,
-       u8_t ttl, u8_t tos, u8_t proto,
-       struct netif *netif);
-#if LWIP_NETIF_HWADDRHINT
-err_t ip_output_hinted(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest,
-       u8_t ttl, u8_t tos, u8_t proto, u8_t *addr_hint);
-#endif /* LWIP_NETIF_HWADDRHINT */
-#if IP_OPTIONS_SEND
-err_t ip_output_if_opt(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest,
-       u8_t ttl, u8_t tos, u8_t proto, struct netif *netif, void *ip_options,
-       u16_t optlen);
-#endif /* IP_OPTIONS_SEND */
-/** Get the interface that received the current packet.
- * This function must only be called from a receive callback (udp_recv,
- * raw_recv, tcp_accept). It will return NULL otherwise. */
-#define ip_current_netif()  (current_netif)
-/** Get the IP header of the current packet.
- * This function must only be called from a receive callback (udp_recv,
- * raw_recv, tcp_accept). It will return NULL otherwise. */
-#define ip_current_header() (current_header)
-/** Source IP address of current_header */
-#define ip_current_src_addr()  (&current_iphdr_src)
-/** Destination IP address of current_header */
-#define ip_current_dest_addr() (&current_iphdr_dest)
-
-/** Gets an IP pcb option (SOF_* flags) */
-#define ip_get_option(pcb, opt)   ((pcb)->so_options & (opt))
-/** Sets an IP pcb option (SOF_* flags) */
-#define ip_set_option(pcb, opt)   ((pcb)->so_options |= (opt))
-/** Resets an IP pcb option (SOF_* flags) */
-#define ip_reset_option(pcb, opt) ((pcb)->so_options &= ~(opt))
-
-#if IP_DEBUG
-void ip_debug_print(struct pbuf *p);
-#else
-#define ip_debug_print(p)
-#endif /* IP_DEBUG */
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* __LWIP_IP_H__ */
-
-

+ 0 - 130
thirdparty/lwip/src/include/ipv6/lwip/ip.h

@@ -1,130 +0,0 @@
-/*
- * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
- * All rights reserved. 
- * 
- * Redistribution and use in source and binary forms, with or without modification, 
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission. 
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
- * OF SUCH DAMAGE.
- *
- * This file is part of the lwIP TCP/IP stack.
- * 
- * Author: Adam Dunkels <adam@sics.se>
- *
- */
-#ifndef __LWIP_IP_H__
-#define __LWIP_IP_H__
-
-#include "lwip/opt.h"
-#include "lwip/def.h"
-#include "lwip/pbuf.h"
-#include "lwip/ip_addr.h"
-
-#include "lwip/err.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define IP_HLEN 40
-
-#define IP_PROTO_ICMP    58
-#define IP_PROTO_UDP     17
-#define IP_PROTO_UDPLITE 136
-#define IP_PROTO_TCP     6
-
-/* This is passed as the destination address to ip_output_if (not
-   to ip_output), meaning that an IP header already is constructed
-   in the pbuf. This is used when TCP retransmits. */
-#ifdef IP_HDRINCL
-#undef IP_HDRINCL
-#endif /* IP_HDRINCL */
-#define IP_HDRINCL  NULL
-
-#if LWIP_NETIF_HWADDRHINT
-#define IP_PCB_ADDRHINT ;u8_t addr_hint
-#else
-#define IP_PCB_ADDRHINT
-#endif /* LWIP_NETIF_HWADDRHINT */
-
-/* This is the common part of all PCB types. It needs to be at the
-   beginning of a PCB type definition. It is located here so that
-   changes to this common part are made in one location instead of
-   having to change all PCB structs. */
-#define IP_PCB struct ip_addr local_ip; \
-  struct ip_addr remote_ip; \
-   /* Socket options */  \
-  u16_t so_options;      \
-   /* Type Of Service */ \
-  u8_t tos;              \
-  /* Time To Live */     \
-  u8_t ttl;              \
-  /* link layer address resolution hint */ \
-  IP_PCB_ADDRHINT
-
-
-/* The IPv6 header. */
-struct ip_hdr {
-#if BYTE_ORDER == LITTLE_ENDIAN
-  u8_t tclass1:4, v:4;
-  u8_t flow1:4, tclass2:4;  
-#else
-  u8_t v:4, tclass1:4;
-  u8_t tclass2:8, flow1:4;
-#endif
-  u16_t flow2;
-  u16_t len;                /* payload length */
-  u8_t nexthdr;             /* next header */
-  u8_t hoplim;              /* hop limit (TTL) */
-  struct ip_addr src, dest;          /* source and destination IP addresses */
-};
-
-#define IPH_PROTO(hdr) (iphdr->nexthdr)
-
-void ip_init(void);
-
-#include "lwip/netif.h"
-
-struct netif *ip_route(struct ip_addr *dest);
-
-void ip_input(struct pbuf *p, struct netif *inp);
-
-/* source and destination addresses in network byte order, please */
-err_t ip_output(struct pbuf *p, struct ip_addr *src, struct ip_addr *dest,
-         u8_t ttl, u8_t proto);
-
-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);
-
-#define ip_current_netif() NULL
-#define ip_current_header() NULL
-
-#if IP_DEBUG
-void ip_debug_print(struct pbuf *p);
-#endif /* IP_DEBUG */
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* __LWIP_IP_H__ */
-
-

+ 0 - 97
thirdparty/lwip/src/include/ipv6/lwip/ip_addr.h

@@ -1,97 +0,0 @@
-/*
- * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
- * All rights reserved. 
- * 
- * Redistribution and use in source and binary forms, with or without modification, 
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission. 
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
- * OF SUCH DAMAGE.
- *
- * This file is part of the lwIP TCP/IP stack.
- * 
- * Author: Adam Dunkels <adam@sics.se>
- *
- */
-#ifndef __LWIP_IP_ADDR_H__
-#define __LWIP_IP_ADDR_H__
-
-#include "lwip/opt.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define IP_ADDR_ANY 0
-
-#ifdef PACK_STRUCT_USE_INCLUDES
-#  include "arch/bpstruct.h"
-#endif
-PACK_STRUCT_BEGIN
- struct ip_addr {
-  PACK_STRUCT_FIELD(u32_t addr[4]);
-} PACK_STRUCT_STRUCT;
-PACK_STRUCT_END
-#ifdef PACK_STRUCT_USE_INCLUDES
-#  include "arch/epstruct.h"
-#endif
-
-/*
- * struct ipaddr2 is used in the definition of the ARP packet format in
- * order to support compilers that don't have structure packing.
- */
-#ifdef PACK_STRUCT_USE_INCLUDES
-#  include "arch/bpstruct.h"
-#endif
-PACK_STRUCT_BEGIN
-struct ip_addr2 {
-  PACK_STRUCT_FIELD(u16_t addrw[2]);
-} PACK_STRUCT_STRUCT;
-PACK_STRUCT_END
-#ifdef PACK_STRUCT_USE_INCLUDES
-#  include "arch/epstruct.h"
-#endif
-
-#define IP6_ADDR(ipaddr, a,b,c,d,e,f,g,h) do { (ipaddr)->addr[0] = htonl((u32_t)((a & 0xffff) << 16) | (b & 0xffff)); \
-                                               (ipaddr)->addr[1] = htonl(((c & 0xffff) << 16) | (d & 0xffff)); \
-                                               (ipaddr)->addr[2] = htonl(((e & 0xffff) << 16) | (f & 0xffff)); \
-                                               (ipaddr)->addr[3] = htonl(((g & 0xffff) << 16) | (h & 0xffff)); } while(0)
-
-u8_t ip_addr_netcmp(struct ip_addr *addr1, struct ip_addr *addr2,
-        struct ip_addr *mask);
-u8_t ip_addr_cmp(struct ip_addr *addr1, struct ip_addr *addr2);
-void ip_addr_set(struct ip_addr *dest, struct ip_addr *src);
-u8_t ip_addr_isany(struct ip_addr *addr);
-
-#define ip_addr_debug_print(debug, ipaddr) \
-        LWIP_DEBUGF(debug, ("%"X32_F":%"X32_F":%"X32_F":%"X32_F":%"X32_F":%"X32_F":%"X32_F":%"X32_F"\n", \
-         (ntohl(ipaddr->addr[0]) >> 16) & 0xffff, \
-         ntohl(ipaddr->addr[0]) & 0xffff, \
-         (ntohl(ipaddr->addr[1]) >> 16) & 0xffff, \
-         ntohl(ipaddr->addr[1]) & 0xffff, \
-         (ntohl(ipaddr->addr[2]) >> 16) & 0xffff, \
-         ntohl(ipaddr->addr[2]) & 0xffff, \
-         (ntohl(ipaddr->addr[3]) >> 16) & 0xffff, \
-         ntohl(ipaddr->addr[3]) & 0xffff));
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* __LWIP_IP_ADDR_H__ */

+ 400 - 297
thirdparty/lwip/src/include/lwip/api.h

@@ -1,297 +1,400 @@
-/*
- * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
- * All rights reserved. 
- * 
- * Redistribution and use in source and binary forms, with or without modification, 
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission. 
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
- * OF SUCH DAMAGE.
- *
- * This file is part of the lwIP TCP/IP stack.
- * 
- * Author: Adam Dunkels <adam@sics.se>
- *
- */
-#ifndef __LWIP_API_H__
-#define __LWIP_API_H__
-
-#include "lwip/opt.h"
-
-#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */
-
-#include <stddef.h> /* for size_t */
-
-#include "lwip/netbuf.h"
-#include "lwip/sys.h"
-#include "lwip/ip_addr.h"
-#include "lwip/err.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* Throughout this file, IP addresses and port numbers are expected to be in
- * the same byte order as in the corresponding pcb.
- */
-
-/* Flags for netconn_write (u8_t) */
-#define NETCONN_NOFLAG    0x00
-#define NETCONN_NOCOPY    0x00 /* Only for source code compatibility */
-#define NETCONN_COPY      0x01
-#define NETCONN_MORE      0x02
-#define NETCONN_DONTBLOCK 0x04
-
-/* Flags for struct netconn.flags (u8_t) */
-/** TCP: when data passed to netconn_write doesn't fit into the send buffer,
-    this temporarily stores whether to wake up the original application task
-    if data couldn't be sent in the first try. */
-#define NETCONN_FLAG_WRITE_DELAYED            0x01
-/** Should this netconn avoid blocking? */
-#define NETCONN_FLAG_NON_BLOCKING             0x02
-/** Was the last connect action a non-blocking one? */
-#define NETCONN_FLAG_IN_NONBLOCKING_CONNECT   0x04
-/** If this is set, a TCP netconn must call netconn_recved() to update
-    the TCP receive window (done automatically if not set). */
-#define NETCONN_FLAG_NO_AUTO_RECVED           0x08
-/** If a nonblocking write has been rejected before, poll_tcp needs to
-    check if the netconn is writable again */
-#define NETCONN_FLAG_CHECK_WRITESPACE         0x10
-
-
-/* Helpers to process several netconn_types by the same code */
-#define NETCONNTYPE_GROUP(t)    (t&0xF0)
-#define NETCONNTYPE_DATAGRAM(t) (t&0xE0)
-
-/** Protocol family and type of the netconn */
-enum netconn_type {
-  NETCONN_INVALID    = 0,
-  /* NETCONN_TCP Group */
-  NETCONN_TCP        = 0x10,
-  /* NETCONN_UDP Group */
-  NETCONN_UDP        = 0x20,
-  NETCONN_UDPLITE    = 0x21,
-  NETCONN_UDPNOCHKSUM= 0x22,
-  /* NETCONN_RAW Group */
-  NETCONN_RAW        = 0x40
-};
-
-/** Current state of the netconn. Non-TCP netconns are always
- * in state NETCONN_NONE! */
-enum netconn_state {
-  NETCONN_NONE,
-  NETCONN_WRITE,
-  NETCONN_LISTEN,
-  NETCONN_CONNECT,
-  NETCONN_CLOSE
-};
-
-/** Use to inform the callback function about changes */
-enum netconn_evt {
-  NETCONN_EVT_RCVPLUS,
-  NETCONN_EVT_RCVMINUS,
-  NETCONN_EVT_SENDPLUS,
-  NETCONN_EVT_SENDMINUS,
-  NETCONN_EVT_ERROR
-};
-
-#if LWIP_IGMP
-/** Used for netconn_join_leave_group() */
-enum netconn_igmp {
-  NETCONN_JOIN,
-  NETCONN_LEAVE
-};
-#endif /* LWIP_IGMP */
-
-/* forward-declare some structs to avoid to include their headers */
-struct ip_pcb;
-struct tcp_pcb;
-struct udp_pcb;
-struct raw_pcb;
-struct netconn;
-struct api_msg_msg;
-
-/** A callback prototype to inform about events for a netconn */
-typedef void (* netconn_callback)(struct netconn *, enum netconn_evt, u16_t len);
-
-/** A netconn descriptor */
-struct netconn {
-  /** type of the netconn (TCP, UDP or RAW) */
-  enum netconn_type type;
-  /** current state of the netconn */
-  enum netconn_state state;
-  /** the lwIP internal protocol control block */
-  union {
-    struct ip_pcb  *ip;
-    struct tcp_pcb *tcp;
-    struct udp_pcb *udp;
-    struct raw_pcb *raw;
-  } pcb;
-  /** the last error this netconn had */
-  err_t last_err;
-  /** sem that is used to synchroneously execute functions in the core context */
-  sys_sem_t op_completed;
-  /** mbox where received packets are stored until they are fetched
-      by the netconn application thread (can grow quite big) */
-  sys_mbox_t recvmbox;
-#if LWIP_TCP
-  /** mbox where new connections are stored until processed
-      by the application thread */
-  sys_mbox_t acceptmbox;
-#endif /* LWIP_TCP */
-  /** only used for socket layer */
-#if LWIP_SOCKET
-  int socket;
-#endif /* LWIP_SOCKET */
-#if LWIP_SO_SNDTIMEO
-  /** timeout to wait for sending data (which means enqueueing data for sending
-      in internal buffers) */
-  s32_t send_timeout;
-#endif /* LWIP_SO_RCVTIMEO */
-#if LWIP_SO_RCVTIMEO
-  /** timeout to wait for new data to be received
-      (or connections to arrive for listening netconns) */
-  int recv_timeout;
-#endif /* LWIP_SO_RCVTIMEO */
-#if LWIP_SO_RCVBUF
-  /** maximum amount of bytes queued in recvmbox
-      not used for TCP: adjust TCP_WND instead! */
-  int recv_bufsize;
-  /** number of bytes currently in recvmbox to be received,
-      tested against recv_bufsize to limit bytes on recvmbox
-      for UDP and RAW, used for FIONREAD */
-  s16_t recv_avail;
-#endif /* LWIP_SO_RCVBUF */
-  /** flags holding more netconn-internal state, see NETCONN_FLAG_* defines */
-  u8_t flags;
-#if LWIP_TCP
-  /** TCP: when data passed to netconn_write doesn't fit into the send buffer,
-      this temporarily stores how much is already sent. */
-  size_t write_offset;
-  /** TCP: when data passed to netconn_write doesn't fit into the send buffer,
-      this temporarily stores the message.
-      Also used during connect and close. */
-  struct api_msg_msg *current_msg;
-#endif /* LWIP_TCP */
-  /** A callback function that is informed about events for this netconn */
-  netconn_callback callback;
-};
-
-/** Register an Network connection event */
-#define API_EVENT(c,e,l) if (c->callback) {         \
-                           (*c->callback)(c, e, l); \
-                         }
-
-/** Set conn->last_err to err but don't overwrite fatal errors */
-#define NETCONN_SET_SAFE_ERR(conn, err) do { \
-  SYS_ARCH_DECL_PROTECT(lev); \
-  SYS_ARCH_PROTECT(lev); \
-  if (!ERR_IS_FATAL((conn)->last_err)) { \
-    (conn)->last_err = err; \
-  } \
-  SYS_ARCH_UNPROTECT(lev); \
-} while(0);
-
-/* Network connection functions: */
-#define netconn_new(t)                  netconn_new_with_proto_and_callback(t, 0, NULL)
-#define netconn_new_with_callback(t, c) netconn_new_with_proto_and_callback(t, 0, c)
-struct
-netconn *netconn_new_with_proto_and_callback(enum netconn_type t, u8_t proto,
-                                             netconn_callback callback);
-err_t   netconn_delete(struct netconn *conn);
-/** Get the type of a netconn (as enum netconn_type). */
-#define netconn_type(conn) (conn->type)
-
-err_t   netconn_getaddr(struct netconn *conn, ip_addr_t *addr,
-                        u16_t *port, u8_t local);
-#define netconn_peer(c,i,p) netconn_getaddr(c,i,p,0)
-#define netconn_addr(c,i,p) netconn_getaddr(c,i,p,1)
-
-err_t   netconn_bind(struct netconn *conn, ip_addr_t *addr, u16_t port);
-err_t   netconn_connect(struct netconn *conn, ip_addr_t *addr, u16_t port);
-err_t   netconn_disconnect (struct netconn *conn);
-err_t   netconn_listen_with_backlog(struct netconn *conn, u8_t backlog);
-#define netconn_listen(conn) netconn_listen_with_backlog(conn, TCP_DEFAULT_LISTEN_BACKLOG)
-err_t   netconn_accept(struct netconn *conn, struct netconn **new_conn);
-err_t   netconn_recv(struct netconn *conn, struct netbuf **new_buf);
-err_t   netconn_recv_tcp_pbuf(struct netconn *conn, struct pbuf **new_buf);
-void    netconn_recved(struct netconn *conn, u32_t length);
-err_t   netconn_sendto(struct netconn *conn, struct netbuf *buf,
-                       ip_addr_t *addr, u16_t port);
-err_t   netconn_send(struct netconn *conn, struct netbuf *buf);
-err_t   netconn_write_partly(struct netconn *conn, const void *dataptr, size_t size,
-                             u8_t apiflags, size_t *bytes_written);
-#define netconn_write(conn, dataptr, size, apiflags) \
-          netconn_write_partly(conn, dataptr, size, apiflags, NULL)
-err_t   netconn_close(struct netconn *conn);
-err_t   netconn_shutdown(struct netconn *conn, u8_t shut_rx, u8_t shut_tx);
-
-#if LWIP_IGMP
-err_t   netconn_join_leave_group(struct netconn *conn, ip_addr_t *multiaddr,
-                                 ip_addr_t *netif_addr, enum netconn_igmp join_or_leave);
-#endif /* LWIP_IGMP */
-#if LWIP_DNS
-err_t   netconn_gethostbyname(const char *name, ip_addr_t *addr);
-#endif /* LWIP_DNS */
-
-#define netconn_err(conn)               ((conn)->last_err)
-#define netconn_recv_bufsize(conn)      ((conn)->recv_bufsize)
-
-/** Set the blocking status of netconn calls (@todo: write/send is missing) */
-#define netconn_set_nonblocking(conn, val)  do { if(val) { \
-  (conn)->flags |= NETCONN_FLAG_NON_BLOCKING; \
-} else { \
-  (conn)->flags &= ~ NETCONN_FLAG_NON_BLOCKING; }} while(0)
-/** Get the blocking status of netconn calls (@todo: write/send is missing) */
-#define netconn_is_nonblocking(conn)        (((conn)->flags & NETCONN_FLAG_NON_BLOCKING) != 0)
-
-/** TCP: Set the no-auto-recved status of netconn calls (see NETCONN_FLAG_NO_AUTO_RECVED) */
-#define netconn_set_noautorecved(conn, val)  do { if(val) { \
-  (conn)->flags |= NETCONN_FLAG_NO_AUTO_RECVED; \
-} else { \
-  (conn)->flags &= ~ NETCONN_FLAG_NO_AUTO_RECVED; }} while(0)
-/** TCP: Get the no-auto-recved status of netconn calls (see NETCONN_FLAG_NO_AUTO_RECVED) */
-#define netconn_get_noautorecved(conn)        (((conn)->flags & NETCONN_FLAG_NO_AUTO_RECVED) != 0)
-
-#if LWIP_SO_SNDTIMEO
-/** Set the send timeout in milliseconds */
-#define netconn_set_sendtimeout(conn, timeout)      ((conn)->send_timeout = (timeout))
-/** Get the send timeout in milliseconds */
-#define netconn_get_sendtimeout(conn)               ((conn)->send_timeout)
-#endif /* LWIP_SO_SNDTIMEO */
-#if LWIP_SO_RCVTIMEO
-/** Set the receive timeout in milliseconds */
-#define netconn_set_recvtimeout(conn, timeout)      ((conn)->recv_timeout = (timeout))
-/** Get the receive timeout in milliseconds */
-#define netconn_get_recvtimeout(conn)               ((conn)->recv_timeout)
-#endif /* LWIP_SO_RCVTIMEO */
-#if LWIP_SO_RCVBUF
-/** Set the receive buffer in bytes */
-#define netconn_set_recvbufsize(conn, recvbufsize)  ((conn)->recv_bufsize = (recvbufsize))
-/** Get the receive buffer in bytes */
-#define netconn_get_recvbufsize(conn)               ((conn)->recv_bufsize)
-#endif /* LWIP_SO_RCVBUF*/
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* LWIP_NETCONN */
-
-#endif /* __LWIP_API_H__ */
+/**
+ * @file
+ * netconn API (to be used from non-TCPIP threads)
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef LWIP_HDR_API_H
+#define LWIP_HDR_API_H
+
+#include "lwip/opt.h"
+
+#if LWIP_NETCONN || LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */
+/* Note: Netconn API is always available when sockets are enabled -
+ * sockets are implemented on top of them */
+
+#include "lwip/arch.h"
+#include "lwip/netbuf.h"
+#include "lwip/sys.h"
+#include "lwip/ip_addr.h"
+#include "lwip/err.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Throughout this file, IP addresses and port numbers are expected to be in
+ * the same byte order as in the corresponding pcb.
+ */
+
+/* Flags for netconn_write (u8_t) */
+#define NETCONN_NOFLAG    0x00
+#define NETCONN_NOCOPY    0x00 /* Only for source code compatibility */
+#define NETCONN_COPY      0x01
+#define NETCONN_MORE      0x02
+#define NETCONN_DONTBLOCK 0x04
+
+/* Flags for struct netconn.flags (u8_t) */
+/** Should this netconn avoid blocking? */
+#define NETCONN_FLAG_NON_BLOCKING             0x02
+/** Was the last connect action a non-blocking one? */
+#define NETCONN_FLAG_IN_NONBLOCKING_CONNECT   0x04
+/** If a nonblocking write has been rejected before, poll_tcp needs to
+    check if the netconn is writable again */
+#define NETCONN_FLAG_CHECK_WRITESPACE         0x10
+#if LWIP_IPV6
+/** If this flag is set then only IPv6 communication is allowed on the
+    netconn. As per RFC#3493 this features defaults to OFF allowing
+    dual-stack usage by default. */
+#define NETCONN_FLAG_IPV6_V6ONLY              0x20
+#endif /* LWIP_IPV6 */
+
+
+/* Helpers to process several netconn_types by the same code */
+#define NETCONNTYPE_GROUP(t)         ((t)&0xF0)
+#define NETCONNTYPE_DATAGRAM(t)      ((t)&0xE0)
+#if LWIP_IPV6
+#define NETCONN_TYPE_IPV6            0x08
+#define NETCONNTYPE_ISIPV6(t)        (((t)&NETCONN_TYPE_IPV6) != 0)
+#define NETCONNTYPE_ISUDPLITE(t)     (((t)&0xF3) == NETCONN_UDPLITE)
+#define NETCONNTYPE_ISUDPNOCHKSUM(t) (((t)&0xF3) == NETCONN_UDPNOCHKSUM)
+#else /* LWIP_IPV6 */
+#define NETCONNTYPE_ISIPV6(t)        (0)
+#define NETCONNTYPE_ISUDPLITE(t)     ((t) == NETCONN_UDPLITE)
+#define NETCONNTYPE_ISUDPNOCHKSUM(t) ((t) == NETCONN_UDPNOCHKSUM)
+#endif /* LWIP_IPV6 */
+
+/** @ingroup netconn_common
+ * Protocol family and type of the netconn
+ */
+enum netconn_type {
+  NETCONN_INVALID     = 0,
+  /** TCP IPv4 */
+  NETCONN_TCP         = 0x10,
+#if LWIP_IPV6
+  /** TCP IPv6 */
+  NETCONN_TCP_IPV6    = NETCONN_TCP | NETCONN_TYPE_IPV6 /* 0x18 */,
+#endif /* LWIP_IPV6 */
+  /** UDP IPv4 */
+  NETCONN_UDP         = 0x20,
+  /** UDP IPv4 lite */
+  NETCONN_UDPLITE     = 0x21,
+  /** UDP IPv4 no checksum */
+  NETCONN_UDPNOCHKSUM = 0x22,
+
+#if LWIP_IPV6
+  /** UDP IPv6 (dual-stack by default, unless you call @ref netconn_set_ipv6only) */
+  NETCONN_UDP_IPV6         = NETCONN_UDP | NETCONN_TYPE_IPV6 /* 0x28 */,
+  /** UDP IPv6 lite (dual-stack by default, unless you call @ref netconn_set_ipv6only) */
+  NETCONN_UDPLITE_IPV6     = NETCONN_UDPLITE | NETCONN_TYPE_IPV6 /* 0x29 */,
+  /** UDP IPv6 no checksum (dual-stack by default, unless you call @ref netconn_set_ipv6only) */
+  NETCONN_UDPNOCHKSUM_IPV6 = NETCONN_UDPNOCHKSUM | NETCONN_TYPE_IPV6 /* 0x2a */,
+#endif /* LWIP_IPV6 */
+
+  /** Raw connection IPv4 */
+  NETCONN_RAW         = 0x40
+#if LWIP_IPV6
+  /** Raw connection IPv6 (dual-stack by default, unless you call @ref netconn_set_ipv6only) */
+  , NETCONN_RAW_IPV6    = NETCONN_RAW | NETCONN_TYPE_IPV6 /* 0x48 */
+#endif /* LWIP_IPV6 */
+};
+
+/** Current state of the netconn. Non-TCP netconns are always
+ * in state NETCONN_NONE! */
+enum netconn_state {
+  NETCONN_NONE,
+  NETCONN_WRITE,
+  NETCONN_LISTEN,
+  NETCONN_CONNECT,
+  NETCONN_CLOSE
+};
+
+/** Used to inform the callback function about changes
+ * 
+ * Event explanation:
+ * 
+ * In the netconn implementation, there are three ways to block a client:
+ * 
+ * - accept mbox (sys_arch_mbox_fetch(&conn->acceptmbox, &accept_ptr, 0); in netconn_accept())
+ * - receive mbox (sys_arch_mbox_fetch(&conn->recvmbox, &buf, 0); in netconn_recv_data())
+ * - send queue is full (sys_arch_sem_wait(LWIP_API_MSG_SEM(msg), 0); in lwip_netconn_do_write())
+ * 
+ * The events have to be seen as events signaling the state of these mboxes/semaphores. For non-blocking
+ * connections, you need to know in advance whether a call to a netconn function call would block or not,
+ * and these events tell you about that.
+ * 
+ * RCVPLUS events say: Safe to perform a potentially blocking call call once more. 
+ * They are counted in sockets - three RCVPLUS events for accept mbox means you are safe
+ * to call netconn_accept 3 times without being blocked.
+ * Same thing for receive mbox.
+ * 
+ * RCVMINUS events say: Your call to to a possibly blocking function is "acknowledged".
+ * Socket implementation decrements the counter.
+ * 
+ * For TX, there is no need to count, its merely a flag. SENDPLUS means you may send something.
+ * SENDPLUS occurs when enough data was delivered to peer so netconn_send() can be called again.
+ * A SENDMINUS event occurs when the next call to a netconn_send() would be blocking.
+ */
+enum netconn_evt {
+  NETCONN_EVT_RCVPLUS,
+  NETCONN_EVT_RCVMINUS,
+  NETCONN_EVT_SENDPLUS,
+  NETCONN_EVT_SENDMINUS,
+  NETCONN_EVT_ERROR
+};
+
+#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD)
+/** Used for netconn_join_leave_group() */
+enum netconn_igmp {
+  NETCONN_JOIN,
+  NETCONN_LEAVE
+};
+#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */
+
+#if LWIP_DNS
+/* Used for netconn_gethostbyname_addrtype(), these should match the DNS_ADDRTYPE defines in dns.h */
+#define NETCONN_DNS_DEFAULT   NETCONN_DNS_IPV4_IPV6
+#define NETCONN_DNS_IPV4      0
+#define NETCONN_DNS_IPV6      1
+#define NETCONN_DNS_IPV4_IPV6 2 /* try to resolve IPv4 first, try IPv6 if IPv4 fails only */
+#define NETCONN_DNS_IPV6_IPV4 3 /* try to resolve IPv6 first, try IPv4 if IPv6 fails only */
+#endif /* LWIP_DNS */
+
+/* forward-declare some structs to avoid to include their headers */
+struct ip_pcb;
+struct tcp_pcb;
+struct udp_pcb;
+struct raw_pcb;
+struct netconn;
+struct api_msg;
+
+/** A callback prototype to inform about events for a netconn */
+typedef void (* netconn_callback)(struct netconn *, enum netconn_evt, u16_t len);
+
+/** A netconn descriptor */
+struct netconn {
+  /** type of the netconn (TCP, UDP or RAW) */
+  enum netconn_type type;
+  /** current state of the netconn */
+  enum netconn_state state;
+  /** the lwIP internal protocol control block */
+  union {
+    struct ip_pcb  *ip;
+    struct tcp_pcb *tcp;
+    struct udp_pcb *udp;
+    struct raw_pcb *raw;
+  } pcb;
+  /** the last error this netconn had */
+  err_t last_err;
+#if !LWIP_NETCONN_SEM_PER_THREAD
+  /** sem that is used to synchronously execute functions in the core context */
+  sys_sem_t op_completed;
+#endif
+  /** mbox where received packets are stored until they are fetched
+      by the netconn application thread (can grow quite big) */
+  sys_mbox_t recvmbox;
+#if LWIP_TCP
+  /** mbox where new connections are stored until processed
+      by the application thread */
+  sys_mbox_t acceptmbox;
+#endif /* LWIP_TCP */
+  /** only used for socket layer */
+#if LWIP_SOCKET
+  int socket;
+#endif /* LWIP_SOCKET */
+#if LWIP_SO_SNDTIMEO
+  /** timeout to wait for sending data (which means enqueueing data for sending
+      in internal buffers) in milliseconds */
+  s32_t send_timeout;
+#endif /* LWIP_SO_RCVTIMEO */
+#if LWIP_SO_RCVTIMEO
+  /** timeout in milliseconds to wait for new data to be received
+      (or connections to arrive for listening netconns) */
+  int recv_timeout;
+#endif /* LWIP_SO_RCVTIMEO */
+#if LWIP_SO_RCVBUF
+  /** maximum amount of bytes queued in recvmbox
+      not used for TCP: adjust TCP_WND instead! */
+  int recv_bufsize;
+  /** number of bytes currently in recvmbox to be received,
+      tested against recv_bufsize to limit bytes on recvmbox
+      for UDP and RAW, used for FIONREAD */
+  int recv_avail;
+#endif /* LWIP_SO_RCVBUF */
+#if LWIP_SO_LINGER
+   /** values <0 mean linger is disabled, values > 0 are seconds to linger */
+  s16_t linger;
+#endif /* LWIP_SO_LINGER */
+  /** flags holding more netconn-internal state, see NETCONN_FLAG_* defines */
+  u8_t flags;
+#if LWIP_TCP
+  /** TCP: when data passed to netconn_write doesn't fit into the send buffer,
+      this temporarily stores how much is already sent. */
+  size_t write_offset;
+  /** TCP: when data passed to netconn_write doesn't fit into the send buffer,
+      this temporarily stores the message.
+      Also used during connect and close. */
+  struct api_msg *current_msg;
+#endif /* LWIP_TCP */
+  /** A callback function that is informed about events for this netconn */
+  netconn_callback callback;
+};
+
+/** Register an Network connection event */
+#define API_EVENT(c,e,l) if (c->callback) {         \
+                           (*c->callback)(c, e, l); \
+                         }
+
+/** Set conn->last_err to err but don't overwrite fatal errors */
+#define NETCONN_SET_SAFE_ERR(conn, err) do { if ((conn) != NULL) { \
+  SYS_ARCH_DECL_PROTECT(netconn_set_safe_err_lev); \
+  SYS_ARCH_PROTECT(netconn_set_safe_err_lev); \
+  if (!ERR_IS_FATAL((conn)->last_err)) { \
+    (conn)->last_err = err; \
+  } \
+  SYS_ARCH_UNPROTECT(netconn_set_safe_err_lev); \
+}} while(0);
+
+/* Network connection functions: */
+
+/** @ingroup netconn_common
+ * Create new netconn connection
+ * @param t @ref netconn_type */
+#define netconn_new(t)                  netconn_new_with_proto_and_callback(t, 0, NULL)
+#define netconn_new_with_callback(t, c) netconn_new_with_proto_and_callback(t, 0, c)
+struct netconn *netconn_new_with_proto_and_callback(enum netconn_type t, u8_t proto,
+                                             netconn_callback callback);
+err_t   netconn_delete(struct netconn *conn);
+/** Get the type of a netconn (as enum netconn_type). */
+#define netconn_type(conn) (conn->type)
+
+err_t   netconn_getaddr(struct netconn *conn, ip_addr_t *addr,
+                        u16_t *port, u8_t local);
+/** @ingroup netconn_common */
+#define netconn_peer(c,i,p) netconn_getaddr(c,i,p,0)
+/** @ingroup netconn_common */
+#define netconn_addr(c,i,p) netconn_getaddr(c,i,p,1)
+
+err_t   netconn_bind(struct netconn *conn, const ip_addr_t *addr, u16_t port);
+err_t   netconn_connect(struct netconn *conn, const ip_addr_t *addr, u16_t port);
+err_t   netconn_disconnect (struct netconn *conn);
+err_t   netconn_listen_with_backlog(struct netconn *conn, u8_t backlog);
+/** @ingroup netconn_tcp */
+#define netconn_listen(conn) netconn_listen_with_backlog(conn, TCP_DEFAULT_LISTEN_BACKLOG)
+err_t   netconn_accept(struct netconn *conn, struct netconn **new_conn);
+err_t   netconn_recv(struct netconn *conn, struct netbuf **new_buf);
+err_t   netconn_recv_tcp_pbuf(struct netconn *conn, struct pbuf **new_buf);
+err_t   netconn_sendto(struct netconn *conn, struct netbuf *buf,
+                             const ip_addr_t *addr, u16_t port);
+err_t   netconn_send(struct netconn *conn, struct netbuf *buf);
+err_t   netconn_write_partly(struct netconn *conn, const void *dataptr, size_t size,
+                             u8_t apiflags, size_t *bytes_written);
+/** @ingroup netconn_tcp */
+#define netconn_write(conn, dataptr, size, apiflags) \
+          netconn_write_partly(conn, dataptr, size, apiflags, NULL)
+err_t   netconn_close(struct netconn *conn);
+err_t   netconn_shutdown(struct netconn *conn, u8_t shut_rx, u8_t shut_tx);
+
+#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD)
+err_t   netconn_join_leave_group(struct netconn *conn, const ip_addr_t *multiaddr,
+                             const ip_addr_t *netif_addr, enum netconn_igmp join_or_leave);
+#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */
+#if LWIP_DNS
+#if LWIP_IPV4 && LWIP_IPV6
+err_t   netconn_gethostbyname_addrtype(const char *name, ip_addr_t *addr, u8_t dns_addrtype);
+#define netconn_gethostbyname(name, addr) netconn_gethostbyname_addrtype(name, addr, NETCONN_DNS_DEFAULT)
+#else /* LWIP_IPV4 && LWIP_IPV6 */
+err_t   netconn_gethostbyname(const char *name, ip_addr_t *addr);
+#define netconn_gethostbyname_addrtype(name, addr, dns_addrtype) netconn_gethostbyname(name, addr)
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+#endif /* LWIP_DNS */
+
+#define netconn_err(conn)               ((conn)->last_err)
+#define netconn_recv_bufsize(conn)      ((conn)->recv_bufsize)
+
+/** Set the blocking status of netconn calls (@todo: write/send is missing) */
+#define netconn_set_nonblocking(conn, val)  do { if(val) { \
+  (conn)->flags |= NETCONN_FLAG_NON_BLOCKING; \
+} else { \
+  (conn)->flags &= ~ NETCONN_FLAG_NON_BLOCKING; }} while(0)
+/** Get the blocking status of netconn calls (@todo: write/send is missing) */
+#define netconn_is_nonblocking(conn)        (((conn)->flags & NETCONN_FLAG_NON_BLOCKING) != 0)
+
+#if LWIP_IPV6
+/** @ingroup netconn_common
+ * TCP: Set the IPv6 ONLY status of netconn calls (see NETCONN_FLAG_IPV6_V6ONLY)
+ */
+#define netconn_set_ipv6only(conn, val)  do { if(val) { \
+  (conn)->flags |= NETCONN_FLAG_IPV6_V6ONLY; \
+} else { \
+  (conn)->flags &= ~ NETCONN_FLAG_IPV6_V6ONLY; }} while(0)
+/** @ingroup netconn_common
+ * TCP: Get the IPv6 ONLY status of netconn calls (see NETCONN_FLAG_IPV6_V6ONLY)
+ */
+#define netconn_get_ipv6only(conn)        (((conn)->flags & NETCONN_FLAG_IPV6_V6ONLY) != 0)
+#endif /* LWIP_IPV6 */
+
+#if LWIP_SO_SNDTIMEO
+/** Set the send timeout in milliseconds */
+#define netconn_set_sendtimeout(conn, timeout)      ((conn)->send_timeout = (timeout))
+/** Get the send timeout in milliseconds */
+#define netconn_get_sendtimeout(conn)               ((conn)->send_timeout)
+#endif /* LWIP_SO_SNDTIMEO */
+#if LWIP_SO_RCVTIMEO
+/** Set the receive timeout in milliseconds */
+#define netconn_set_recvtimeout(conn, timeout)      ((conn)->recv_timeout = (timeout))
+/** Get the receive timeout in milliseconds */
+#define netconn_get_recvtimeout(conn)               ((conn)->recv_timeout)
+#endif /* LWIP_SO_RCVTIMEO */
+#if LWIP_SO_RCVBUF
+/** Set the receive buffer in bytes */
+#define netconn_set_recvbufsize(conn, recvbufsize)  ((conn)->recv_bufsize = (recvbufsize))
+/** Get the receive buffer in bytes */
+#define netconn_get_recvbufsize(conn)               ((conn)->recv_bufsize)
+#endif /* LWIP_SO_RCVBUF*/
+
+#if LWIP_NETCONN_SEM_PER_THREAD
+void netconn_thread_init(void);
+void netconn_thread_cleanup(void);
+#else /* LWIP_NETCONN_SEM_PER_THREAD */
+#define netconn_thread_init()
+#define netconn_thread_cleanup()
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_NETCONN || LWIP_SOCKET */
+
+#endif /* LWIP_HDR_API_H */

+ 0 - 177
thirdparty/lwip/src/include/lwip/api_msg.h

@@ -1,177 +0,0 @@
-/*
- * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
- * All rights reserved. 
- * 
- * Redistribution and use in source and binary forms, with or without modification, 
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission. 
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
- * OF SUCH DAMAGE.
- *
- * This file is part of the lwIP TCP/IP stack.
- * 
- * Author: Adam Dunkels <adam@sics.se>
- *
- */
-#ifndef __LWIP_API_MSG_H__
-#define __LWIP_API_MSG_H__
-
-#include "lwip/opt.h"
-
-#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */
-
-#include <stddef.h> /* for size_t */
-
-#include "lwip/ip_addr.h"
-#include "lwip/err.h"
-#include "lwip/sys.h"
-#include "lwip/igmp.h"
-#include "lwip/api.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* For the netconn API, these values are use as a bitmask! */
-#define NETCONN_SHUT_RD   1
-#define NETCONN_SHUT_WR   2
-#define NETCONN_SHUT_RDWR (NETCONN_SHUT_RD | NETCONN_SHUT_WR)
-
-/* IP addresses and port numbers are expected to be in
- * the same byte order as in the corresponding pcb.
- */
-/** This struct includes everything that is necessary to execute a function
-    for a netconn in another thread context (mainly used to process netconns
-    in the tcpip_thread context to be thread safe). */
-struct api_msg_msg {
-  /** The netconn which to process - always needed: it includes the semaphore
-      which is used to block the application thread until the function finished. */
-  struct netconn *conn;
-  /** The return value of the function executed in tcpip_thread. */
-  err_t err;
-  /** Depending on the executed function, one of these union members is used */
-  union {
-    /** used for do_send */
-    struct netbuf *b;
-    /** used for do_newconn */
-    struct {
-      u8_t proto;
-    } n;
-    /** used for do_bind and do_connect */
-    struct {
-      ip_addr_t *ipaddr;
-      u16_t port;
-    } bc;
-    /** used for do_getaddr */
-    struct {
-      ip_addr_t *ipaddr;
-      u16_t *port;
-      u8_t local;
-    } ad;
-    /** used for do_write */
-    struct {
-      const void *dataptr;
-      size_t len;
-      u8_t apiflags;
-#if LWIP_SO_SNDTIMEO
-      u32_t time_started;
-#endif /* LWIP_SO_SNDTIMEO */
-    } w;
-    /** used for do_recv */
-    struct {
-      u32_t len;
-    } r;
-    /** used for do_close (/shutdown) */
-    struct {
-      u8_t shut;
-    } sd;
-#if LWIP_IGMP
-    /** used for do_join_leave_group */
-    struct {
-      ip_addr_t *multiaddr;
-      ip_addr_t *netif_addr;
-      enum netconn_igmp join_or_leave;
-    } jl;
-#endif /* LWIP_IGMP */
-#if TCP_LISTEN_BACKLOG
-    struct {
-      u8_t backlog;
-    } lb;
-#endif /* TCP_LISTEN_BACKLOG */
-  } msg;
-};
-
-/** This struct contains a function to execute in another thread context and
-    a struct api_msg_msg that serves as an argument for this function.
-    This is passed to tcpip_apimsg to execute functions in tcpip_thread context. */
-struct api_msg {
-  /** function to execute in tcpip_thread context */
-  void (* function)(struct api_msg_msg *msg);
-  /** arguments for this function */
-  struct api_msg_msg msg;
-};
-
-#if LWIP_DNS
-/** As do_gethostbyname requires more arguments but doesn't require a netconn,
-    it has its own struct (to avoid struct api_msg getting bigger than necessary).
-    do_gethostbyname must be called using tcpip_callback instead of tcpip_apimsg
-    (see netconn_gethostbyname). */
-struct dns_api_msg {
-  /** Hostname to query or dotted IP address string */
-  const char *name;
-  /** Rhe resolved address is stored here */
-  ip_addr_t *addr;
-  /** This semaphore is posted when the name is resolved, the application thread
-      should wait on it. */
-  sys_sem_t *sem;
-  /** Errors are given back here */
-  err_t *err;
-};
-#endif /* LWIP_DNS */
-
-void do_newconn         ( struct api_msg_msg *msg);
-void do_delconn         ( struct api_msg_msg *msg);
-void do_bind            ( struct api_msg_msg *msg);
-void do_connect         ( struct api_msg_msg *msg);
-void do_disconnect      ( struct api_msg_msg *msg);
-void do_listen          ( struct api_msg_msg *msg);
-void do_send            ( struct api_msg_msg *msg);
-void do_recv            ( struct api_msg_msg *msg);
-void do_write           ( struct api_msg_msg *msg);
-void do_getaddr         ( struct api_msg_msg *msg);
-void do_close           ( struct api_msg_msg *msg);
-void do_shutdown        ( struct api_msg_msg *msg);
-#if LWIP_IGMP
-void do_join_leave_group( struct api_msg_msg *msg);
-#endif /* LWIP_IGMP */
-
-#if LWIP_DNS
-void do_gethostbyname(void *arg);
-#endif /* LWIP_DNS */
-
-struct netconn* netconn_alloc(enum netconn_type t, netconn_callback callback);
-void netconn_free(struct netconn *conn);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* LWIP_NETCONN */
-
-#endif /* __LWIP_API_MSG_H__ */

+ 103 - 0
thirdparty/lwip/src/include/lwip/apps/fs.h

@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
+ * All rights reserved. 
+ * 
+ * Redistribution and use in source and binary forms, with or without modification, 
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ * 
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef LWIP_HDR_APPS_FS_H
+#define LWIP_HDR_APPS_FS_H
+
+#include "httpd_opts.h"
+#include "lwip/err.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define FS_READ_EOF     -1
+#define FS_READ_DELAYED -2
+
+#if HTTPD_PRECALCULATED_CHECKSUM
+struct fsdata_chksum {
+  u32_t offset;
+  u16_t chksum;
+  u16_t len;
+};
+#endif /* HTTPD_PRECALCULATED_CHECKSUM */
+
+#define FS_FILE_FLAGS_HEADER_INCLUDED     0x01
+#define FS_FILE_FLAGS_HEADER_PERSISTENT   0x02
+
+struct fs_file {
+  const char *data;
+  int len;
+  int index;
+  void *pextension;
+#if HTTPD_PRECALCULATED_CHECKSUM
+  const struct fsdata_chksum *chksum;
+  u16_t chksum_count;
+#endif /* HTTPD_PRECALCULATED_CHECKSUM */
+  u8_t flags;
+#if LWIP_HTTPD_CUSTOM_FILES
+  u8_t is_custom_file;
+#endif /* LWIP_HTTPD_CUSTOM_FILES */
+#if LWIP_HTTPD_FILE_STATE
+  void *state;
+#endif /* LWIP_HTTPD_FILE_STATE */
+};
+
+#if LWIP_HTTPD_FS_ASYNC_READ
+typedef void (*fs_wait_cb)(void *arg);
+#endif /* LWIP_HTTPD_FS_ASYNC_READ */
+
+err_t fs_open(struct fs_file *file, const char *name);
+void fs_close(struct fs_file *file);
+#if LWIP_HTTPD_DYNAMIC_FILE_READ
+#if LWIP_HTTPD_FS_ASYNC_READ
+int fs_read_async(struct fs_file *file, char *buffer, int count, fs_wait_cb callback_fn, void *callback_arg);
+#else /* LWIP_HTTPD_FS_ASYNC_READ */
+int fs_read(struct fs_file *file, char *buffer, int count);
+#endif /* LWIP_HTTPD_FS_ASYNC_READ */
+#endif /* LWIP_HTTPD_DYNAMIC_FILE_READ */
+#if LWIP_HTTPD_FS_ASYNC_READ
+int fs_is_file_ready(struct fs_file *file, fs_wait_cb callback_fn, void *callback_arg);
+#endif /* LWIP_HTTPD_FS_ASYNC_READ */
+int fs_bytes_left(struct fs_file *file);
+
+#if LWIP_HTTPD_FILE_STATE
+/** This user-defined function is called when a file is opened. */
+void *fs_state_init(struct fs_file *file, const char *name);
+/** This user-defined function is called when a file is closed. */
+void fs_state_free(struct fs_file *file, void *state);
+#endif /* #if LWIP_HTTPD_FILE_STATE */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_APPS_FS_H */

+ 236 - 0
thirdparty/lwip/src/include/lwip/apps/httpd.h

@@ -0,0 +1,236 @@
+/**
+ * @file
+ * HTTP server
+ */
+
+/*
+ * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ * This version of the file has been modified by Texas Instruments to offer
+ * simple server-side-include (SSI) and Common Gateway Interface (CGI)
+ * capability.
+ */
+
+#ifndef LWIP_HDR_APPS_HTTPD_H
+#define LWIP_HDR_APPS_HTTPD_H
+
+#include "httpd_opts.h"
+#include "lwip/err.h"
+#include "lwip/pbuf.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if LWIP_HTTPD_CGI
+
+/*
+ * Function pointer for a CGI script handler.
+ *
+ * This function is called each time the HTTPD server is asked for a file
+ * whose name was previously registered as a CGI function using a call to
+ * http_set_cgi_handler. The iIndex parameter provides the index of the
+ * CGI within the ppcURLs array passed to http_set_cgi_handler. Parameters
+ * pcParam and pcValue provide access to the parameters provided along with
+ * the URI. iNumParams provides a count of the entries in the pcParam and
+ * pcValue arrays. Each entry in the pcParam array contains the name of a
+ * parameter with the corresponding entry in the pcValue array containing the
+ * value for that parameter. Note that pcParam may contain multiple elements
+ * with the same name if, for example, a multi-selection list control is used
+ * in the form generating the data.
+ *
+ * The function should return a pointer to a character string which is the
+ * path and filename of the response that is to be sent to the connected
+ * browser, for example "/thanks.htm" or "/response/error.ssi".
+ *
+ * The maximum number of parameters that will be passed to this function via
+ * iNumParams is defined by LWIP_HTTPD_MAX_CGI_PARAMETERS. Any parameters in the incoming
+ * HTTP request above this number will be discarded.
+ *
+ * Requests intended for use by this CGI mechanism must be sent using the GET
+ * method (which encodes all parameters within the URI rather than in a block
+ * later in the request). Attempts to use the POST method will result in the
+ * request being ignored.
+ *
+ */
+typedef const char *(*tCGIHandler)(int iIndex, int iNumParams, char *pcParam[],
+                             char *pcValue[]);
+
+/*
+ * Structure defining the base filename (URL) of a CGI and the associated
+ * function which is to be called when that URL is requested.
+ */
+typedef struct
+{
+    const char *pcCGIName;
+    tCGIHandler pfnCGIHandler;
+} tCGI;
+
+void http_set_cgi_handlers(const tCGI *pCGIs, int iNumHandlers);
+
+#endif /* LWIP_HTTPD_CGI */
+
+#if LWIP_HTTPD_CGI || LWIP_HTTPD_CGI_SSI
+
+#if LWIP_HTTPD_CGI_SSI
+/** Define this generic CGI handler in your application.
+ * It is called once for every URI with parameters.
+ * The parameters can be stored to 
+ */
+extern void httpd_cgi_handler(const char* uri, int iNumParams, char **pcParam, char **pcValue
+#if defined(LWIP_HTTPD_FILE_STATE) && LWIP_HTTPD_FILE_STATE
+                                     , void *connection_state
+#endif /* LWIP_HTTPD_FILE_STATE */
+                                     );
+#endif /* LWIP_HTTPD_CGI_SSI */
+
+#endif /* LWIP_HTTPD_CGI || LWIP_HTTPD_CGI_SSI */
+
+#if LWIP_HTTPD_SSI
+
+/*
+ * Function pointer for the SSI tag handler callback.
+ *
+ * This function will be called each time the HTTPD server detects a tag of the
+ * form <!--#name--> in a .shtml, .ssi or .shtm file where "name" appears as
+ * one of the tags supplied to http_set_ssi_handler in the ppcTags array.  The
+ * returned insert string, which will be appended after the the string
+ * "<!--#name-->" in file sent back to the client,should be written to pointer
+ * pcInsert.  iInsertLen contains the size of the buffer pointed to by
+ * pcInsert.  The iIndex parameter provides the zero-based index of the tag as
+ * found in the ppcTags array and identifies the tag that is to be processed.
+ *
+ * The handler returns the number of characters written to pcInsert excluding
+ * any terminating NULL or a negative number to indicate a failure (tag not
+ * recognized, for example).
+ *
+ * Note that the behavior of this SSI mechanism is somewhat different from the
+ * "normal" SSI processing as found in, for example, the Apache web server.  In
+ * this case, the inserted text is appended following the SSI tag rather than
+ * replacing the tag entirely.  This allows for an implementation that does not
+ * require significant additional buffering of output data yet which will still
+ * offer usable SSI functionality.  One downside to this approach is when
+ * attempting to use SSI within JavaScript.  The SSI tag is structured to
+ * resemble an HTML comment but this syntax does not constitute a comment
+ * within JavaScript and, hence, leaving the tag in place will result in
+ * problems in these cases.  To work around this, any SSI tag which needs to
+ * output JavaScript code must do so in an encapsulated way, sending the whole
+ * HTML <script>...</script> section as a single include.
+ */
+typedef u16_t (*tSSIHandler)(
+#if LWIP_HTTPD_SSI_RAW
+                             const char* ssi_tag_name,
+#else /* LWIP_HTTPD_SSI_RAW */
+                             int iIndex,
+#endif /* LWIP_HTTPD_SSI_RAW */
+                             char *pcInsert, int iInsertLen
+#if LWIP_HTTPD_SSI_MULTIPART
+                             , u16_t current_tag_part, u16_t *next_tag_part
+#endif /* LWIP_HTTPD_SSI_MULTIPART */
+#if defined(LWIP_HTTPD_FILE_STATE) && LWIP_HTTPD_FILE_STATE
+                             , void *connection_state
+#endif /* LWIP_HTTPD_FILE_STATE */
+                             );
+
+/** Set the SSI handler function
+ * (if LWIP_HTTPD_SSI_RAW==1, only the first argument is used)
+ */
+void http_set_ssi_handler(tSSIHandler pfnSSIHandler,
+                          const char **ppcTags, int iNumTags);
+
+/** For LWIP_HTTPD_SSI_RAW==1, return this to indicate the tag is unknown.
+ * In this case, the webserver writes a warning into the page.
+ * You can also just return 0 to write nothing for unknown tags.
+ */
+#define HTTPD_SSI_TAG_UNKNOWN 0xFFFF
+
+#endif /* LWIP_HTTPD_SSI */
+
+#if LWIP_HTTPD_SUPPORT_POST
+
+/* These functions must be implemented by the application */
+
+/** Called when a POST request has been received. The application can decide
+ * whether to accept it or not.
+ *
+ * @param connection Unique connection identifier, valid until httpd_post_end
+ *        is called.
+ * @param uri The HTTP header URI receiving the POST request.
+ * @param http_request The raw HTTP request (the first packet, normally).
+ * @param http_request_len Size of 'http_request'.
+ * @param content_len Content-Length from HTTP header.
+ * @param response_uri Filename of response file, to be filled when denying the
+ *        request
+ * @param response_uri_len Size of the 'response_uri' buffer.
+ * @param post_auto_wnd Set this to 0 to let the callback code handle window
+ *        updates by calling 'httpd_post_data_recved' (to throttle rx speed)
+ *        default is 1 (httpd handles window updates automatically)
+ * @return ERR_OK: Accept the POST request, data may be passed in
+ *         another err_t: Deny the POST request, send back 'bad request'.
+ */
+err_t httpd_post_begin(void *connection, const char *uri, const char *http_request,
+                       u16_t http_request_len, int content_len, char *response_uri,
+                       u16_t response_uri_len, u8_t *post_auto_wnd);
+
+/** Called for each pbuf of data that has been received for a POST.
+ * ATTENTION: The application is responsible for freeing the pbufs passed in!
+ *
+ * @param connection Unique connection identifier.
+ * @param p Received data.
+ * @return ERR_OK: Data accepted.
+ *         another err_t: Data denied, http_post_get_response_uri will be called.
+ */
+err_t httpd_post_receive_data(void *connection, struct pbuf *p);
+
+/** Called when all data is received or when the connection is closed.
+ * The application must return the filename/URI of a file to send in response
+ * to this POST request. If the response_uri buffer is untouched, a 404
+ * response is returned.
+ *
+ * @param connection Unique connection identifier.
+ * @param response_uri Filename of response file, to be filled when denying the request
+ * @param response_uri_len Size of the 'response_uri' buffer.
+ */
+void httpd_post_finished(void *connection, char *response_uri, u16_t response_uri_len);
+
+#if LWIP_HTTPD_POST_MANUAL_WND
+void httpd_post_data_recved(void *connection, u16_t recved_len);
+#endif /* LWIP_HTTPD_POST_MANUAL_WND */
+
+#endif /* LWIP_HTTPD_SUPPORT_POST */
+
+void httpd_init(void);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HTTPD_H */

+ 323 - 0
thirdparty/lwip/src/include/lwip/apps/httpd_opts.h

@@ -0,0 +1,323 @@
+/**
+ * @file
+ * HTTP server options list
+ */
+
+/*
+ * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ * This version of the file has been modified by Texas Instruments to offer
+ * simple server-side-include (SSI) and Common Gateway Interface (CGI)
+ * capability.
+ */
+
+#ifndef LWIP_HDR_APPS_HTTPD_OPTS_H
+#define LWIP_HDR_APPS_HTTPD_OPTS_H
+
+#include "lwip/opt.h"
+
+/**
+ * @defgroup httpd_opts Options
+ * @ingroup httpd
+ * @{
+ */
+
+/** Set this to 1 to support CGI (old style) */
+#if !defined LWIP_HTTPD_CGI || defined __DOXYGEN__
+#define LWIP_HTTPD_CGI            0
+#endif
+
+/** Set this to 1 to support CGI (new style) */
+#if !defined LWIP_HTTPD_CGI_SSI || defined __DOXYGEN__
+#define LWIP_HTTPD_CGI_SSI        0
+#endif
+
+/** Set this to 1 to support SSI (Server-Side-Includes) */
+#if !defined LWIP_HTTPD_SSI || defined __DOXYGEN__
+#define LWIP_HTTPD_SSI            0
+#endif
+
+/** Set this to 1 to implement an SSI tag handler callback that gets a const char*
+ * to the tag (instead of an index into a pre-registered array of known tags) */
+#if !defined LWIP_HTTPD_SSI_RAW || defined __DOXYGEN__
+#define LWIP_HTTPD_SSI_RAW        0
+#endif
+
+/** Set this to 1 to support HTTP POST */
+#if !defined LWIP_HTTPD_SUPPORT_POST || defined __DOXYGEN__
+#define LWIP_HTTPD_SUPPORT_POST   0
+#endif
+
+/* The maximum number of parameters that the CGI handler can be sent. */
+#if !defined LWIP_HTTPD_MAX_CGI_PARAMETERS || defined __DOXYGEN__
+#define LWIP_HTTPD_MAX_CGI_PARAMETERS 16
+#endif
+
+/** LWIP_HTTPD_SSI_MULTIPART==1: SSI handler function is called with 2 more
+ * arguments indicating a counter for insert string that are too long to be
+ * inserted at once: the SSI handler function must then set 'next_tag_part'
+ * which will be passed back to it in the next call. */
+#if !defined LWIP_HTTPD_SSI_MULTIPART || defined __DOXYGEN__
+#define LWIP_HTTPD_SSI_MULTIPART    0
+#endif
+
+/* The maximum length of the string comprising the tag name */
+#if !defined LWIP_HTTPD_MAX_TAG_NAME_LEN || defined __DOXYGEN__
+#define LWIP_HTTPD_MAX_TAG_NAME_LEN 8
+#endif
+
+/* The maximum length of string that can be returned to replace any given tag */
+#if !defined LWIP_HTTPD_MAX_TAG_INSERT_LEN || defined __DOXYGEN__
+#define LWIP_HTTPD_MAX_TAG_INSERT_LEN 192
+#endif
+
+#if !defined LWIP_HTTPD_POST_MANUAL_WND || defined __DOXYGEN__
+#define LWIP_HTTPD_POST_MANUAL_WND  0
+#endif
+
+/** This string is passed in the HTTP header as "Server: " */
+#if !defined HTTPD_SERVER_AGENT || defined __DOXYGEN__
+#define HTTPD_SERVER_AGENT "lwIP/" LWIP_VERSION_STRING " (http://savannah.nongnu.org/projects/lwip)"
+#endif
+
+/** Set this to 1 if you want to include code that creates HTTP headers
+ * at runtime. Default is off: HTTP headers are then created statically
+ * by the makefsdata tool. Static headers mean smaller code size, but
+ * the (readonly) fsdata will grow a bit as every file includes the HTTP
+ * header. */
+#if !defined LWIP_HTTPD_DYNAMIC_HEADERS || defined __DOXYGEN__
+#define LWIP_HTTPD_DYNAMIC_HEADERS 0
+#endif
+
+#if !defined HTTPD_DEBUG || defined __DOXYGEN__
+#define HTTPD_DEBUG         LWIP_DBG_OFF
+#endif
+
+/** Set this to 1 to use a memp pool for allocating 
+ * struct http_state instead of the heap.
+ */
+#if !defined HTTPD_USE_MEM_POOL || defined __DOXYGEN__
+#define HTTPD_USE_MEM_POOL  0
+#endif
+
+/** The server port for HTTPD to use */
+#if !defined HTTPD_SERVER_PORT || defined __DOXYGEN__
+#define HTTPD_SERVER_PORT                   80
+#endif
+
+/** Maximum retries before the connection is aborted/closed.
+ * - number of times pcb->poll is called -> default is 4*500ms = 2s;
+ * - reset when pcb->sent is called
+ */
+#if !defined HTTPD_MAX_RETRIES || defined __DOXYGEN__
+#define HTTPD_MAX_RETRIES                   4
+#endif
+
+/** The poll delay is X*500ms */
+#if !defined HTTPD_POLL_INTERVAL || defined __DOXYGEN__
+#define HTTPD_POLL_INTERVAL                 4
+#endif
+
+/** Priority for tcp pcbs created by HTTPD (very low by default).
+ *  Lower priorities get killed first when running out of memory.
+ */
+#if !defined HTTPD_TCP_PRIO || defined __DOXYGEN__
+#define HTTPD_TCP_PRIO                      TCP_PRIO_MIN
+#endif
+
+/** Set this to 1 to enable timing each file sent */
+#if !defined LWIP_HTTPD_TIMING || defined __DOXYGEN__
+#define LWIP_HTTPD_TIMING                   0
+#endif
+/** Set this to 1 to enable timing each file sent */
+#if !defined HTTPD_DEBUG_TIMING || defined __DOXYGEN__
+#define HTTPD_DEBUG_TIMING                  LWIP_DBG_OFF
+#endif
+
+/** Set this to one to show error pages when parsing a request fails instead
+    of simply closing the connection. */
+#if !defined LWIP_HTTPD_SUPPORT_EXTSTATUS || defined __DOXYGEN__
+#define LWIP_HTTPD_SUPPORT_EXTSTATUS        0
+#endif
+
+/** Set this to 0 to drop support for HTTP/0.9 clients (to save some bytes) */
+#if !defined LWIP_HTTPD_SUPPORT_V09 || defined __DOXYGEN__
+#define LWIP_HTTPD_SUPPORT_V09              1
+#endif
+
+/** Set this to 1 to enable HTTP/1.1 persistent connections.
+ * ATTENTION: If the generated file system includes HTTP headers, these must
+ * include the "Connection: keep-alive" header (pass argument "-11" to makefsdata).
+ */
+#if !defined LWIP_HTTPD_SUPPORT_11_KEEPALIVE || defined __DOXYGEN__
+#define LWIP_HTTPD_SUPPORT_11_KEEPALIVE     0
+#endif
+
+/** Set this to 1 to support HTTP request coming in in multiple packets/pbufs */
+#if !defined LWIP_HTTPD_SUPPORT_REQUESTLIST || defined __DOXYGEN__
+#define LWIP_HTTPD_SUPPORT_REQUESTLIST      1
+#endif
+
+#if LWIP_HTTPD_SUPPORT_REQUESTLIST
+/** Number of rx pbufs to enqueue to parse an incoming request (up to the first
+    newline) */
+#if !defined LWIP_HTTPD_REQ_QUEUELEN || defined __DOXYGEN__
+#define LWIP_HTTPD_REQ_QUEUELEN             5
+#endif
+
+/** Number of (TCP payload-) bytes (in pbufs) to enqueue to parse and incoming
+    request (up to the first double-newline) */
+#if !defined LWIP_HTTPD_REQ_BUFSIZE || defined __DOXYGEN__
+#define LWIP_HTTPD_REQ_BUFSIZE              LWIP_HTTPD_MAX_REQ_LENGTH
+#endif
+
+/** Defines the maximum length of a HTTP request line (up to the first CRLF,
+    copied from pbuf into this a global buffer when pbuf- or packet-queues
+    are received - otherwise the input pbuf is used directly) */
+#if !defined LWIP_HTTPD_MAX_REQ_LENGTH || defined __DOXYGEN__
+#define LWIP_HTTPD_MAX_REQ_LENGTH           LWIP_MIN(1023, (LWIP_HTTPD_REQ_QUEUELEN * PBUF_POOL_BUFSIZE))
+#endif
+#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */
+
+/** This is the size of a static buffer used when URIs end with '/'.
+ * In this buffer, the directory requested is concatenated with all the
+ * configured default file names.
+ * Set to 0 to disable checking default filenames on non-root directories.
+ */
+#if !defined LWIP_HTTPD_MAX_REQUEST_URI_LEN || defined __DOXYGEN__
+#define LWIP_HTTPD_MAX_REQUEST_URI_LEN      63
+#endif
+
+/** Maximum length of the filename to send as response to a POST request,
+ * filled in by the application when a POST is finished.
+ */
+#if !defined LWIP_HTTPD_POST_MAX_RESPONSE_URI_LEN || defined __DOXYGEN__
+#define LWIP_HTTPD_POST_MAX_RESPONSE_URI_LEN 63
+#endif
+
+/** Set this to 0 to not send the SSI tag (default is on, so the tag will
+ * be sent in the HTML page */
+#if !defined LWIP_HTTPD_SSI_INCLUDE_TAG || defined __DOXYGEN__
+#define LWIP_HTTPD_SSI_INCLUDE_TAG           1
+#endif
+
+/** Set this to 1 to call tcp_abort when tcp_close fails with memory error.
+ * This can be used to prevent consuming all memory in situations where the
+ * HTTP server has low priority compared to other communication. */
+#if !defined LWIP_HTTPD_ABORT_ON_CLOSE_MEM_ERROR || defined __DOXYGEN__
+#define LWIP_HTTPD_ABORT_ON_CLOSE_MEM_ERROR  0
+#endif
+
+/** Set this to 1 to kill the oldest connection when running out of
+ * memory for 'struct http_state' or 'struct http_ssi_state'.
+ * ATTENTION: This puts all connections on a linked list, so may be kind of slow.
+ */
+#if !defined LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED || defined __DOXYGEN__
+#define LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED 0
+#endif
+
+/** Set this to 1 to send URIs without extension without headers
+ * (who uses this at all??) */
+#if !defined LWIP_HTTPD_OMIT_HEADER_FOR_EXTENSIONLESS_URI || defined __DOXYGEN__
+#define LWIP_HTTPD_OMIT_HEADER_FOR_EXTENSIONLESS_URI 0
+#endif
+
+/** Default: Tags are sent from struct http_state and are therefore volatile */
+#if !defined HTTP_IS_TAG_VOLATILE || defined __DOXYGEN__
+#define HTTP_IS_TAG_VOLATILE(ptr) TCP_WRITE_FLAG_COPY
+#endif
+
+/* By default, the httpd is limited to send 2*pcb->mss to keep resource usage low
+   when http is not an important protocol in the device. */
+#if !defined HTTPD_LIMIT_SENDING_TO_2MSS || defined __DOXYGEN__
+#define HTTPD_LIMIT_SENDING_TO_2MSS 1
+#endif
+
+/* Define this to a function that returns the maximum amount of data to enqueue.
+   The function have this signature: u16_t fn(struct tcp_pcb* pcb); */
+#if !defined HTTPD_MAX_WRITE_LEN || defined __DOXYGEN__
+#if HTTPD_LIMIT_SENDING_TO_2MSS
+#define HTTPD_MAX_WRITE_LEN(pcb)    (2 * tcp_mss(pcb))
+#endif
+#endif
+
+/*------------------- FS OPTIONS -------------------*/
+
+/** Set this to 1 and provide the functions:
+ * - "int fs_open_custom(struct fs_file *file, const char *name)"
+ *    Called first for every opened file to allow opening files
+ *    that are not included in fsdata(_custom).c
+ * - "void fs_close_custom(struct fs_file *file)"
+ *    Called to free resources allocated by fs_open_custom().
+ */
+#if !defined LWIP_HTTPD_CUSTOM_FILES || defined __DOXYGEN__
+#define LWIP_HTTPD_CUSTOM_FILES       0
+#endif
+
+/** Set this to 1 to support fs_read() to dynamically read file data.
+ * Without this (default=off), only one-block files are supported,
+ * and the contents must be ready after fs_open().
+ */
+#if !defined LWIP_HTTPD_DYNAMIC_FILE_READ || defined __DOXYGEN__
+#define LWIP_HTTPD_DYNAMIC_FILE_READ  0
+#endif
+
+/** Set this to 1 to include an application state argument per file
+ * that is opened. This allows to keep a state per connection/file.
+ */
+#if !defined LWIP_HTTPD_FILE_STATE || defined __DOXYGEN__
+#define LWIP_HTTPD_FILE_STATE         0
+#endif
+
+/** HTTPD_PRECALCULATED_CHECKSUM==1: include precompiled checksums for
+ * predefined (MSS-sized) chunks of the files to prevent having to calculate
+ * the checksums at runtime. */
+#if !defined HTTPD_PRECALCULATED_CHECKSUM || defined __DOXYGEN__
+#define HTTPD_PRECALCULATED_CHECKSUM  0
+#endif
+
+/** LWIP_HTTPD_FS_ASYNC_READ==1: support asynchronous read operations
+ * (fs_read_async returns FS_READ_DELAYED and calls a callback when finished).
+ */
+#if !defined LWIP_HTTPD_FS_ASYNC_READ || defined __DOXYGEN__
+#define LWIP_HTTPD_FS_ASYNC_READ      0
+#endif
+
+/** Set this to 1 to include "fsdata_custom.c" instead of "fsdata.c" for the
+ * file system (to prevent changing the file included in CVS) */
+#if !defined HTTPD_USE_CUSTOM_FSDATA || defined __DOXYGEN__
+#define HTTPD_USE_CUSTOM_FSDATA 0
+#endif
+
+/**
+ * @}
+ */
+
+#endif /* LWIP_HDR_APPS_HTTPD_OPTS_H */

+ 84 - 0
thirdparty/lwip/src/include/lwip/apps/lwiperf.h

@@ -0,0 +1,84 @@
+/**
+ * @file
+ * lwIP iPerf server implementation
+ */
+
+/*
+ * Copyright (c) 2014 Simon Goldschmidt
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt
+ *
+ */
+#ifndef LWIP_HDR_APPS_LWIPERF_H
+#define LWIP_HDR_APPS_LWIPERF_H
+
+#include "lwip/opt.h"
+#include "lwip/ip_addr.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define LWIPERF_TCP_PORT_DEFAULT  5001
+
+/** lwIPerf test results */
+enum lwiperf_report_type
+{
+  /** The server side test is done */
+  LWIPERF_TCP_DONE_SERVER,
+  /** The client side test is done */
+  LWIPERF_TCP_DONE_CLIENT,
+  /** Local error lead to test abort */
+  LWIPERF_TCP_ABORTED_LOCAL,
+  /** Data check error lead to test abort */
+  LWIPERF_TCP_ABORTED_LOCAL_DATAERROR,
+  /** Transmit error lead to test abort */
+  LWIPERF_TCP_ABORTED_LOCAL_TXERROR,
+  /** Remote side aborted the test */
+  LWIPERF_TCP_ABORTED_REMOTE
+};
+
+/** Prototype of a report function that is called when a session is finished.
+    This report function can show the test results.
+    @param report_type contains the test result */
+typedef void (*lwiperf_report_fn)(void *arg, enum lwiperf_report_type report_type,
+  const ip_addr_t* local_addr, u16_t local_port, const ip_addr_t* remote_addr, u16_t remote_port,
+  u32_t bytes_transferred, u32_t ms_duration, u32_t bandwidth_kbitpsec);
+
+
+void* lwiperf_start_tcp_server(const ip_addr_t* local_addr, u16_t local_port,
+                               lwiperf_report_fn report_fn, void* report_arg);
+void* lwiperf_start_tcp_server_default(lwiperf_report_fn report_fn, void* report_arg);
+void  lwiperf_abort(void* lwiperf_session);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_APPS_LWIPERF_H */

+ 69 - 0
thirdparty/lwip/src/include/lwip/apps/mdns.h

@@ -0,0 +1,69 @@
+/**
+ * @file
+ * MDNS responder
+ */
+
+ /*
+ * Copyright (c) 2015 Verisure Innovation AB
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Erik Ekman <erik@kryo.se>
+ *
+ */
+#ifndef LWIP_HDR_MDNS_H
+#define LWIP_HDR_MDNS_H
+
+#include "lwip/apps/mdns_opts.h"
+#include "lwip/netif.h"
+
+#if LWIP_MDNS_RESPONDER
+
+enum mdns_sd_proto {
+  DNSSD_PROTO_UDP = 0,
+  DNSSD_PROTO_TCP = 1
+};
+
+#define MDNS_LABEL_MAXLEN  63
+
+struct mdns_host;
+struct mdns_service;
+
+/** Callback function to add text to a reply, called when generating the reply */
+typedef void (*service_get_txt_fn_t)(struct mdns_service *service, void *txt_userdata);
+
+void mdns_resp_init(void);
+
+err_t mdns_resp_add_netif(struct netif *netif, const char *hostname, u32_t dns_ttl);
+err_t mdns_resp_remove_netif(struct netif *netif);
+
+err_t mdns_resp_add_service(struct netif *netif, const char *name, const char *service, enum mdns_sd_proto proto, u16_t port, u32_t dns_ttl, service_get_txt_fn_t txt_fn, void *txt_userdata);
+err_t mdns_resp_add_service_txtitem(struct mdns_service *service, const char *txt, u8_t txt_len);
+void mdns_resp_netif_settings_changed(struct netif *netif);
+
+#endif /* LWIP_MDNS_RESPONDER */
+
+#endif /* LWIP_HDR_MDNS_H */

+ 74 - 0
thirdparty/lwip/src/include/lwip/apps/mdns_opts.h

@@ -0,0 +1,74 @@
+/**
+ * @file
+ * MDNS responder
+ */
+
+ /*
+ * Copyright (c) 2015 Verisure Innovation AB
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Erik Ekman <erik@kryo.se>
+ *
+ */
+
+#ifndef LWIP_HDR_APPS_MDNS_OPTS_H
+#define LWIP_HDR_APPS_MDNS_OPTS_H
+
+#include "lwip/opt.h"
+
+/**
+ * @defgroup mdns_opts Options
+ * @ingroup mdns
+ * @{
+ */
+
+/**
+ * LWIP_MDNS_RESPONDER==1: Turn on multicast DNS module. UDP must be available for MDNS
+ * transport. IGMP is needed for IPv4 multicast.
+ */
+#ifndef LWIP_MDNS_RESPONDER
+#define LWIP_MDNS_RESPONDER             0
+#endif /* LWIP_MDNS_RESPONDER */
+
+/** The maximum number of services per netif */
+#ifndef MDNS_MAX_SERVICES
+#define MDNS_MAX_SERVICES               1
+#endif
+
+/**
+ * MDNS_DEBUG: Enable debugging for multicast DNS.
+ */
+#ifndef MDNS_DEBUG
+#define MDNS_DEBUG                       LWIP_DBG_OFF
+#endif
+
+/**
+ * @}
+ */
+
+#endif /* LWIP_HDR_APPS_MDNS_OPTS_H */
+

+ 66 - 0
thirdparty/lwip/src/include/lwip/apps/mdns_priv.h

@@ -0,0 +1,66 @@
+/**
+ * @file
+ * MDNS responder private definitions
+ */
+
+ /*
+ * Copyright (c) 2015 Verisure Innovation AB
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Erik Ekman <erik@kryo.se>
+ *
+ */
+#ifndef LWIP_HDR_MDNS_PRIV_H
+#define LWIP_HDR_MDNS_PRIV_H
+
+#include "lwip/apps/mdns_opts.h"
+#include "lwip/pbuf.h"
+
+#if LWIP_MDNS_RESPONDER
+
+/* Domain struct and methods - visible for unit tests */
+
+#define MDNS_DOMAIN_MAXLEN 256
+#define MDNS_READNAME_ERROR 0xFFFF
+
+struct mdns_domain {
+  /* Encoded domain name */
+  u8_t name[MDNS_DOMAIN_MAXLEN];
+  /* Total length of domain name, including zero */
+  u16_t length;
+  /* Set if compression of this domain is not allowed */
+  u8_t skip_compression;
+};
+
+err_t mdns_domain_add_label(struct mdns_domain *domain, const char *label, u8_t len);
+u16_t mdns_readname(struct pbuf *p, u16_t offset, struct mdns_domain *domain);
+int mdns_domain_eq(struct mdns_domain *a, struct mdns_domain *b);
+u16_t mdns_compress_domain(struct pbuf *pbuf, u16_t *offset, struct mdns_domain *domain);
+
+#endif /* LWIP_MDNS_RESPONDER */
+
+#endif /* LWIP_HDR_MDNS_PRIV_H */

+ 244 - 0
thirdparty/lwip/src/include/lwip/apps/mqtt.h

@@ -0,0 +1,244 @@
+/**
+ * @file
+ * MQTT client
+ */
+
+/*
+ * Copyright (c) 2016 Erik Andersson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Erik Andersson
+ *
+ */
+#ifndef LWIP_HDR_APPS_MQTT_CLIENT_H
+#define LWIP_HDR_APPS_MQTT_CLIENT_H
+
+#include "lwip/apps/mqtt_opts.h"
+#include "lwip/err.h"
+#include "lwip/ip_addr.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct mqtt_client_t mqtt_client_t;
+
+/** @ingroup mqtt
+ * Default MQTT port */
+#define MQTT_PORT 1883
+
+/*---------------------------------------------------------------------------------------------- */
+/* Connection with server */
+
+/**
+ * @ingroup mqtt
+ * Client information and connection parameters */
+struct mqtt_connect_client_info_t {
+  /** Client identifier, must be set by caller */
+  const char *client_id;
+  /** User name and password, set to NULL if not used */
+  const char* client_user;
+  const char* client_pass;
+  /** keep alive time in seconds, 0 to disable keep alive functionality*/
+  u16_t keep_alive;
+  /** will topic, set to NULL if will is not to be used,
+      will_msg, will_qos and will retain are then ignored */
+  const char* will_topic;
+  const char* will_msg;
+  u8_t will_qos;
+  u8_t will_retain;
+};
+
+/**
+ * @ingroup mqtt
+ * Connection status codes */
+typedef enum
+{
+  MQTT_CONNECT_ACCEPTED                 = 0,
+  MQTT_CONNECT_REFUSED_PROTOCOL_VERSION = 1,
+  MQTT_CONNECT_REFUSED_IDENTIFIER       = 2,
+  MQTT_CONNECT_REFUSED_SERVER           = 3,
+  MQTT_CONNECT_REFUSED_USERNAME_PASS    = 4,
+  MQTT_CONNECT_REFUSED_NOT_AUTHORIZED_  = 5,
+  MQTT_CONNECT_DISCONNECTED             = 256,
+  MQTT_CONNECT_TIMEOUT                  = 257
+} mqtt_connection_status_t;
+
+/**
+ * @ingroup mqtt
+ * Function prototype for mqtt connection status callback. Called when
+ * client has connected to the server after initiating a mqtt connection attempt by
+ * calling mqtt_connect() or when connection is closed by server or an error
+ *
+ * @param client MQTT client itself
+ * @param arg Additional argument to pass to the callback function
+ * @param status Connect result code or disconnection notification @see mqtt_connection_status_t
+ *
+ */
+typedef void (*mqtt_connection_cb_t)(mqtt_client_t *client, void *arg, mqtt_connection_status_t status);
+
+
+/**
+ * @ingroup mqtt
+ * Data callback flags */
+enum {
+  /** Flag set when last fragment of data arrives in data callback */
+  MQTT_DATA_FLAG_LAST = 1
+};
+
+/** 
+ * @ingroup mqtt
+ * Function prototype for MQTT incoming publish data callback function. Called when data
+ * arrives to a subscribed topic @see mqtt_subscribe
+ *
+ * @param arg Additional argument to pass to the callback function
+ * @param data User data, pointed object, data may not be referenced after callback return,
+          NULL is passed when all publish data are delivered
+ * @param len Length of publish data fragment
+ * @param flags MQTT_DATA_FLAG_LAST set when this call contains the last part of data from publish message
+ *
+ */
+typedef void (*mqtt_incoming_data_cb_t)(void *arg, const u8_t *data, u16_t len, u8_t flags);
+
+
+/** 
+ * @ingroup mqtt
+ * Function prototype for MQTT incoming publish function. Called when an incoming publish
+ * arrives to a subscribed topic @see mqtt_subscribe
+ *
+ * @param arg Additional argument to pass to the callback function
+ * @param topic Zero terminated Topic text string, topic may not be referenced after callback return
+ * @param tot_len Total length of publish data, if set to 0 (no publish payload) data callback will not be invoked
+ */
+typedef void (*mqtt_incoming_publish_cb_t)(void *arg, const char *topic, u32_t tot_len);
+
+
+/**
+ * @ingroup mqtt
+ * Function prototype for mqtt request callback. Called when a subscribe, unsubscribe
+ * or publish request has completed
+ * @param arg Pointer to user data supplied when invoking request
+ * @param err ERR_OK on success
+ *            ERR_TIMEOUT if no response was received within timeout,
+ *            ERR_ABRT if (un)subscribe was denied
+ */
+typedef void (*mqtt_request_cb_t)(void *arg, err_t err);
+
+
+/**
+ * Pending request item, binds application callback to pending server requests
+ */
+struct mqtt_request_t
+{
+  /** Next item in list, NULL means this is the last in chain,
+      next pointing at itself means request is unallocated */
+  struct mqtt_request_t *next;
+  /** Callback to upper layer */
+  mqtt_request_cb_t cb;
+  void *arg;
+  /** MQTT packet identifier */
+  u16_t pkt_id;
+  /** Expire time relative to element before this  */
+  u16_t timeout_diff;
+};
+
+/** Ring buffer */
+struct mqtt_ringbuf_t {
+  u16_t put;
+  u16_t get;
+  u8_t buf[MQTT_OUTPUT_RINGBUF_SIZE];
+};
+
+/** MQTT client */
+struct mqtt_client_t
+{
+  /** Timers and timeouts */
+  u16_t cyclic_tick;
+  u16_t keep_alive;
+  u16_t server_watchdog;
+  /** Packet identifier generator*/
+  u16_t pkt_id_seq;
+  /** Packet identifier of pending incoming publish */
+  u16_t inpub_pkt_id;
+  /** Connection state */
+  u8_t conn_state;
+  struct tcp_pcb *conn;
+  /** Connection callback */
+  void *connect_arg;
+  mqtt_connection_cb_t connect_cb;
+  /** Pending requests to server */
+  struct mqtt_request_t *pend_req_queue;
+  struct mqtt_request_t req_list[MQTT_REQ_MAX_IN_FLIGHT];
+  void *inpub_arg;
+  /** Incoming data callback */
+  mqtt_incoming_data_cb_t data_cb;
+  mqtt_incoming_publish_cb_t pub_cb;
+  /** Input */
+  u32_t msg_idx;
+  u8_t rx_buffer[MQTT_VAR_HEADER_BUFFER_LEN];
+  /** Output ring-buffer */
+  struct mqtt_ringbuf_t output;
+};
+
+
+/** Connect to server */
+err_t mqtt_client_connect(mqtt_client_t *client, const ip_addr_t *ipaddr, u16_t port, mqtt_connection_cb_t cb, void *arg,
+                   const struct mqtt_connect_client_info_t *client_info);
+
+/** Disconnect from server */
+void mqtt_disconnect(mqtt_client_t *client);
+
+/** Create new client */
+mqtt_client_t *mqtt_client_new(void);
+
+/** Check connection status */
+u8_t mqtt_client_is_connected(mqtt_client_t *client);
+
+/** Set callback to call for incoming publish */
+void mqtt_set_inpub_callback(mqtt_client_t *client, mqtt_incoming_publish_cb_t,
+                             mqtt_incoming_data_cb_t data_cb, void *arg);
+
+/** Common function for subscribe and unsubscribe */
+err_t mqtt_sub_unsub(mqtt_client_t *client, const char *topic, u8_t qos, mqtt_request_cb_t cb, void *arg, u8_t sub);
+
+/** @ingroup mqtt
+ *Subscribe to topic */
+#define mqtt_subscribe(client, topic, qos, cb, arg) mqtt_sub_unsub(client, topic, qos, cb, arg, 1)
+/** @ingroup mqtt
+ *  Unsubscribe to topic */
+#define mqtt_unsubscribe(client, topic, cb, arg) mqtt_sub_unsub(client, topic, 0, cb, arg, 0)
+
+
+/** Publish data to topic */
+err_t mqtt_publish(mqtt_client_t *client, const char *topic, const void *payload, u16_t payload_length, u8_t qos, u8_t retain,
+                                    mqtt_request_cb_t cb, void *arg);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_APPS_MQTT_CLIENT_H */

+ 103 - 0
thirdparty/lwip/src/include/lwip/apps/mqtt_opts.h

@@ -0,0 +1,103 @@
+/**
+ * @file
+ * MQTT client options
+ */
+
+/*
+ * Copyright (c) 2016 Erik Andersson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Erik Andersson
+ *
+ */
+#ifndef LWIP_HDR_APPS_MQTT_OPTS_H
+#define LWIP_HDR_APPS_MQTT_OPTS_H
+
+#include "lwip/opt.h"
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+/**
+ * @defgroup mqtt_opts Options
+ * @ingroup mqtt
+ * @{
+ */
+
+/**
+ * Output ring-buffer size, must be able to fit largest outgoing publish message topic+payloads
+ */
+#ifndef MQTT_OUTPUT_RINGBUF_SIZE
+#define MQTT_OUTPUT_RINGBUF_SIZE 256
+#endif
+
+/**
+ * Number of bytes in receive buffer, must be at least the size of the longest incoming topic + 8
+ * If one wants to avoid fragmented incoming publish, set length to max incoming topic length + max payload length + 8
+ */
+#ifndef MQTT_VAR_HEADER_BUFFER_LEN
+#define MQTT_VAR_HEADER_BUFFER_LEN 128
+#endif
+
+/**
+ * Maximum number of pending subscribe, unsubscribe and publish requests to server .
+ */
+#ifndef MQTT_REQ_MAX_IN_FLIGHT
+#define MQTT_REQ_MAX_IN_FLIGHT 4
+#endif
+
+/**
+ * Seconds between each cyclic timer call.
+ */
+#ifndef MQTT_CYCLIC_TIMER_INTERVAL
+#define MQTT_CYCLIC_TIMER_INTERVAL 5
+#endif
+
+/**
+ * Publish, subscribe and unsubscribe request timeout in seconds.
+ */
+#ifndef MQTT_REQ_TIMEOUT
+#define MQTT_REQ_TIMEOUT 30
+#endif
+
+/**
+ * Seconds for MQTT connect response timeout after sending connect request
+ */
+#ifndef MQTT_CONNECT_TIMOUT
+#define MQTT_CONNECT_TIMOUT 100
+#endif
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_APPS_MQTT_OPTS_H */

+ 43 - 0
thirdparty/lwip/src/include/lwip/apps/netbiosns.h

@@ -0,0 +1,43 @@
+/**
+ * @file
+ * NETBIOS name service responder
+ */
+
+/*
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ */
+#ifndef LWIP_HDR_APPS_NETBIOS_H
+#define LWIP_HDR_APPS_NETBIOS_H
+
+#include "lwip/apps/netbiosns_opts.h"
+
+void netbiosns_init(void);
+#ifndef NETBIOS_LWIP_NAME
+void netbiosns_set_name(const char* hostname);
+#endif
+void netbiosns_stop(void);
+
+#endif /* LWIP_HDR_APPS_NETBIOS_H */

+ 59 - 0
thirdparty/lwip/src/include/lwip/apps/netbiosns_opts.h

@@ -0,0 +1,59 @@
+/**
+ * @file
+ * NETBIOS name service responder options
+ */
+
+/*
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ */
+#ifndef LWIP_HDR_APPS_NETBIOS_OPTS_H
+#define LWIP_HDR_APPS_NETBIOS_OPTS_H
+
+#include "lwip/opt.h"
+
+/**
+ * @defgroup netbiosns_opts Options
+ * @ingroup netbiosns
+ * @{
+ */
+
+/** NetBIOS name of lwip device
+ * This must be uppercase until NETBIOS_STRCMP() is defined to a string
+ * comparision function that is case insensitive.
+ * If you want to use the netif's hostname, use this (with LWIP_NETIF_HOSTNAME):
+ * (ip_current_netif() != NULL ? ip_current_netif()->hostname != NULL ? ip_current_netif()->hostname : "" : "")
+ *
+ * If this is not defined, netbiosns_set_name() can be called at runtime to change the name.
+ */
+#ifdef __DOXYGEN__
+#define NETBIOS_LWIP_NAME "NETBIOSLWIPDEV"
+#endif
+
+/**
+ * @}
+ */
+
+#endif /* LWIP_HDR_APPS_NETBIOS_OPTS_H */

+ 128 - 0
thirdparty/lwip/src/include/lwip/apps/snmp.h

@@ -0,0 +1,128 @@
+/**
+ * @file
+ * SNMP server main API - start and basic configuration
+ */
+
+/*
+ * Copyright (c) 2001, 2002 Leon Woestenberg <leon.woestenberg@axon.tv>
+ * Copyright (c) 2001, 2002 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Leon Woestenberg <leon.woestenberg@axon.tv>
+ *         Martin Hentschel <info@cl-soft.de>
+ *
+ */
+#ifndef LWIP_HDR_APPS_SNMP_H
+#define LWIP_HDR_APPS_SNMP_H
+
+#include "lwip/apps/snmp_opts.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/err.h"
+#include "lwip/apps/snmp_core.h"
+
+/** SNMP variable binding descriptor (publically needed for traps) */
+struct snmp_varbind
+{
+  /** pointer to next varbind, NULL for last in list */
+  struct snmp_varbind *next;
+  /** pointer to previous varbind, NULL for first in list */
+  struct snmp_varbind *prev;
+
+  /** object identifier */
+  struct snmp_obj_id oid;
+
+  /** value ASN1 type */
+  u8_t type;
+  /** object value length */
+  u16_t value_len;
+  /** object value */
+  void *value;
+};
+
+/**
+ * @ingroup snmp_core
+ * Agent setup, start listening to port 161.
+ */
+void snmp_init(void);
+void snmp_set_mibs(const struct snmp_mib **mibs, u8_t num_mibs);
+
+void snmp_set_device_enterprise_oid(const struct snmp_obj_id* device_enterprise_oid);
+const struct snmp_obj_id* snmp_get_device_enterprise_oid(void);
+
+void snmp_trap_dst_enable(u8_t dst_idx, u8_t enable);
+void snmp_trap_dst_ip_set(u8_t dst_idx, const ip_addr_t *dst);
+
+/** Generic trap: cold start */
+#define SNMP_GENTRAP_COLDSTART 0
+/** Generic trap: warm start */
+#define SNMP_GENTRAP_WARMSTART 1
+/** Generic trap: link down */
+#define SNMP_GENTRAP_LINKDOWN 2
+/** Generic trap: link up */
+#define SNMP_GENTRAP_LINKUP 3
+/** Generic trap: authentication failure */
+#define SNMP_GENTRAP_AUTH_FAILURE 4
+/** Generic trap: EGP neighbor lost */
+#define SNMP_GENTRAP_EGP_NEIGHBOR_LOSS 5
+/** Generic trap: enterprise specific */
+#define SNMP_GENTRAP_ENTERPRISE_SPECIFIC 6
+
+err_t snmp_send_trap_generic(s32_t generic_trap);
+err_t snmp_send_trap_specific(s32_t specific_trap, struct snmp_varbind *varbinds);
+err_t snmp_send_trap(const struct snmp_obj_id* oid, s32_t generic_trap, s32_t specific_trap, struct snmp_varbind *varbinds);
+
+#define SNMP_AUTH_TRAPS_DISABLED 0
+#define SNMP_AUTH_TRAPS_ENABLED  1
+void snmp_set_auth_traps_enabled(u8_t enable);
+u8_t snmp_get_auth_traps_enabled(void);
+
+const char * snmp_get_community(void);
+const char * snmp_get_community_write(void);
+const char * snmp_get_community_trap(void);
+void snmp_set_community(const char * const community);
+void snmp_set_community_write(const char * const community);
+void snmp_set_community_trap(const char * const community);
+
+void snmp_coldstart_trap(void);
+void snmp_authfail_trap(void);
+
+typedef void (*snmp_write_callback_fct)(const u32_t* oid, u8_t oid_len, void* callback_arg);
+void snmp_set_write_callback(snmp_write_callback_fct write_callback, void* callback_arg);
+
+#endif /* LWIP_SNMP */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_APPS_SNMP_H */

+ 364 - 0
thirdparty/lwip/src/include/lwip/apps/snmp_core.h

@@ -0,0 +1,364 @@
+/**
+ * @file
+ * SNMP core API for implementing MIBs
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Christiaan Simons <christiaan.simons@axon.tv>
+ *         Martin Hentschel <info@cl-soft.de>
+ */
+
+#ifndef LWIP_HDR_APPS_SNMP_CORE_H
+#define LWIP_HDR_APPS_SNMP_CORE_H
+
+#include "lwip/apps/snmp_opts.h"
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/ip_addr.h"
+#include "lwip/err.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* basic ASN1 defines */
+#define SNMP_ASN1_CLASS_UNIVERSAL   0x00
+#define SNMP_ASN1_CLASS_APPLICATION 0x40
+#define SNMP_ASN1_CLASS_CONTEXT     0x80
+#define SNMP_ASN1_CLASS_PRIVATE     0xC0
+
+#define SNMP_ASN1_CONTENTTYPE_PRIMITIVE   0x00
+#define SNMP_ASN1_CONTENTTYPE_CONSTRUCTED 0x20
+
+/* universal tags (from ASN.1 spec.) */
+#define SNMP_ASN1_UNIVERSAL_END_OF_CONTENT  0
+#define SNMP_ASN1_UNIVERSAL_INTEGER         2
+#define SNMP_ASN1_UNIVERSAL_OCTET_STRING    4
+#define SNMP_ASN1_UNIVERSAL_NULL            5
+#define SNMP_ASN1_UNIVERSAL_OBJECT_ID       6
+#define SNMP_ASN1_UNIVERSAL_SEQUENCE_OF    16
+
+/* application specific (SNMP) tags (from SNMPv2-SMI) */
+#define SNMP_ASN1_APPLICATION_IPADDR    0  /* [APPLICATION 0] IMPLICIT OCTET STRING (SIZE (4)) */
+#define SNMP_ASN1_APPLICATION_COUNTER   1  /* [APPLICATION 1] IMPLICIT INTEGER (0..4294967295) => u32_t */
+#define SNMP_ASN1_APPLICATION_GAUGE     2  /* [APPLICATION 2] IMPLICIT INTEGER (0..4294967295) => u32_t */
+#define SNMP_ASN1_APPLICATION_TIMETICKS 3  /* [APPLICATION 3] IMPLICIT INTEGER (0..4294967295) => u32_t */
+#define SNMP_ASN1_APPLICATION_OPAQUE    4  /* [APPLICATION 4] IMPLICIT OCTET STRING */
+#define SNMP_ASN1_APPLICATION_COUNTER64 6  /* [APPLICATION 6] IMPLICIT INTEGER (0..18446744073709551615) */
+
+/* context specific (SNMP) tags (from RFC 1905) */
+#define SNMP_ASN1_CONTEXT_VARBIND_NO_SUCH_INSTANCE 1
+
+/* full ASN1 type defines */
+#define SNMP_ASN1_TYPE_END_OF_CONTENT (SNMP_ASN1_CLASS_UNIVERSAL | SNMP_ASN1_CONTENTTYPE_PRIMITIVE | SNMP_ASN1_UNIVERSAL_END_OF_CONTENT)
+#define SNMP_ASN1_TYPE_INTEGER        (SNMP_ASN1_CLASS_UNIVERSAL | SNMP_ASN1_CONTENTTYPE_PRIMITIVE | SNMP_ASN1_UNIVERSAL_INTEGER)
+#define SNMP_ASN1_TYPE_OCTET_STRING   (SNMP_ASN1_CLASS_UNIVERSAL | SNMP_ASN1_CONTENTTYPE_PRIMITIVE | SNMP_ASN1_UNIVERSAL_OCTET_STRING)
+#define SNMP_ASN1_TYPE_NULL           (SNMP_ASN1_CLASS_UNIVERSAL | SNMP_ASN1_CONTENTTYPE_PRIMITIVE | SNMP_ASN1_UNIVERSAL_NULL)
+#define SNMP_ASN1_TYPE_OBJECT_ID      (SNMP_ASN1_CLASS_UNIVERSAL | SNMP_ASN1_CONTENTTYPE_PRIMITIVE | SNMP_ASN1_UNIVERSAL_OBJECT_ID)
+#define SNMP_ASN1_TYPE_SEQUENCE       (SNMP_ASN1_CLASS_UNIVERSAL | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_UNIVERSAL_SEQUENCE_OF)
+#define SNMP_ASN1_TYPE_IPADDR         (SNMP_ASN1_CLASS_APPLICATION | SNMP_ASN1_CONTENTTYPE_PRIMITIVE | SNMP_ASN1_APPLICATION_IPADDR)
+#define SNMP_ASN1_TYPE_IPADDRESS      SNMP_ASN1_TYPE_IPADDR
+#define SNMP_ASN1_TYPE_COUNTER        (SNMP_ASN1_CLASS_APPLICATION | SNMP_ASN1_CONTENTTYPE_PRIMITIVE | SNMP_ASN1_APPLICATION_COUNTER)
+#define SNMP_ASN1_TYPE_COUNTER32      SNMP_ASN1_TYPE_COUNTER
+#define SNMP_ASN1_TYPE_GAUGE          (SNMP_ASN1_CLASS_APPLICATION | SNMP_ASN1_CONTENTTYPE_PRIMITIVE | SNMP_ASN1_APPLICATION_GAUGE)
+#define SNMP_ASN1_TYPE_GAUGE32        SNMP_ASN1_TYPE_GAUGE
+#define SNMP_ASN1_TYPE_UNSIGNED32     SNMP_ASN1_TYPE_GAUGE
+#define SNMP_ASN1_TYPE_TIMETICKS      (SNMP_ASN1_CLASS_APPLICATION | SNMP_ASN1_CONTENTTYPE_PRIMITIVE | SNMP_ASN1_APPLICATION_TIMETICKS)
+#define SNMP_ASN1_TYPE_OPAQUE         (SNMP_ASN1_CLASS_APPLICATION | SNMP_ASN1_CONTENTTYPE_PRIMITIVE | SNMP_ASN1_APPLICATION_OPAQUE)
+#define SNMP_ASN1_TYPE_COUNTER64      (SNMP_ASN1_CLASS_APPLICATION | SNMP_ASN1_CONTENTTYPE_PRIMITIVE | SNMP_ASN1_APPLICATION_COUNTER64)
+
+#define SNMP_VARBIND_EXCEPTION_OFFSET 0xF0
+#define SNMP_VARBIND_EXCEPTION_MASK   0x0F
+
+/** error codes predefined by SNMP prot. */
+typedef enum {
+  SNMP_ERR_NOERROR             = 0,
+/* 
+outdated v1 error codes. do not use anmore!
+#define SNMP_ERR_NOSUCHNAME 2  use SNMP_ERR_NOSUCHINSTANCE instead
+#define SNMP_ERR_BADVALUE   3  use SNMP_ERR_WRONGTYPE,SNMP_ERR_WRONGLENGTH,SNMP_ERR_WRONGENCODING or SNMP_ERR_WRONGVALUE instead
+#define SNMP_ERR_READONLY   4  use SNMP_ERR_NOTWRITABLE instead
+*/
+  SNMP_ERR_GENERROR            = 5,
+  SNMP_ERR_NOACCESS            = 6,
+  SNMP_ERR_WRONGTYPE           = 7,
+  SNMP_ERR_WRONGLENGTH         = 8,
+  SNMP_ERR_WRONGENCODING       = 9,
+  SNMP_ERR_WRONGVALUE          = 10,
+  SNMP_ERR_NOCREATION          = 11,
+  SNMP_ERR_INCONSISTENTVALUE   = 12,
+  SNMP_ERR_RESOURCEUNAVAILABLE = 13,
+  SNMP_ERR_COMMITFAILED        = 14,
+  SNMP_ERR_UNDOFAILED          = 15,
+  SNMP_ERR_NOTWRITABLE         = 17,
+  SNMP_ERR_INCONSISTENTNAME    = 18,
+
+  SNMP_ERR_NOSUCHINSTANCE      = SNMP_VARBIND_EXCEPTION_OFFSET + SNMP_ASN1_CONTEXT_VARBIND_NO_SUCH_INSTANCE
+} snmp_err_t;
+
+/** internal object identifier representation */
+struct snmp_obj_id
+{
+  u8_t len;
+  u32_t id[SNMP_MAX_OBJ_ID_LEN];
+};
+
+struct snmp_obj_id_const_ref
+{
+  u8_t len;
+  const u32_t* id;
+};
+
+extern const struct snmp_obj_id_const_ref snmp_zero_dot_zero; /* administrative identifier from SNMPv2-SMI */
+
+/** SNMP variant value, used as reference in struct snmp_node_instance and table implementation */
+union snmp_variant_value
+{
+  void* ptr;
+  const void* const_ptr;
+  u32_t u32;
+  s32_t s32;
+};
+
+
+/**
+SNMP MIB node types
+ tree node is the only node the stack can process in order to walk the tree,
+ all other nodes are assumed to be leaf nodes.
+ This cannot be an enum because users may want to define their own node types.
+*/
+#define SNMP_NODE_TREE         0x00
+/* predefined leaf node types */
+#define SNMP_NODE_SCALAR       0x01
+#define SNMP_NODE_SCALAR_ARRAY 0x02
+#define SNMP_NODE_TABLE        0x03
+#define SNMP_NODE_THREADSYNC   0x04
+
+/** node "base class" layout, the mandatory fields for a node  */
+struct snmp_node
+{
+  /** one out of SNMP_NODE_TREE or any leaf node type (like SNMP_NODE_SCALAR) */
+  u8_t node_type;
+  /** the number assigned to this node which used as part of the full OID */
+  u32_t oid;
+};
+
+/** SNMP node instance access types */
+typedef enum {
+  SNMP_NODE_INSTANCE_ACCESS_READ    = 1,
+  SNMP_NODE_INSTANCE_ACCESS_WRITE   = 2,
+  SNMP_NODE_INSTANCE_READ_ONLY      = SNMP_NODE_INSTANCE_ACCESS_READ,
+  SNMP_NODE_INSTANCE_READ_WRITE     = (SNMP_NODE_INSTANCE_ACCESS_READ | SNMP_NODE_INSTANCE_ACCESS_WRITE),
+  SNMP_NODE_INSTANCE_WRITE_ONLY     = SNMP_NODE_INSTANCE_ACCESS_WRITE,
+  SNMP_NODE_INSTANCE_NOT_ACCESSIBLE = 0
+} snmp_access_t;
+
+struct snmp_node_instance;
+
+typedef s16_t (*node_instance_get_value_method)(struct snmp_node_instance*, void*);
+typedef snmp_err_t (*node_instance_set_test_method)(struct snmp_node_instance*, u16_t, void*);
+typedef snmp_err_t (*node_instance_set_value_method)(struct snmp_node_instance*, u16_t, void*);
+typedef void (*node_instance_release_method)(struct snmp_node_instance*);
+
+#define SNMP_GET_VALUE_RAW_DATA 0x8000
+
+/** SNMP node instance */
+struct snmp_node_instance
+{
+  /** prefilled with the node, get_instance() is called on; may be changed by user to any value to pass an arbitrary node between calls to get_instance() and get_value/test_value/set_value */
+  const struct snmp_node* node;
+  /** prefilled with the instance id requested; for get_instance() this is the exact oid requested; for get_next_instance() this is the relative starting point, stack expects relative oid of next node here */
+  struct snmp_obj_id instance_oid;
+
+  /** ASN type for this object (see snmp_asn1.h for definitions) */
+  u8_t asn1_type;
+  /** one out of instance access types defined above (SNMP_NODE_INSTANCE_READ_ONLY,...) */
+  snmp_access_t access;
+
+  /** returns object value for the given object identifier. Return values <0 to indicate an error */
+  node_instance_get_value_method get_value;
+  /** tests length and/or range BEFORE setting */
+  node_instance_set_test_method set_test;
+  /** sets object value, only called when set_test() was successful */
+  node_instance_set_value_method set_value;
+  /** called in any case when the instance is not required anymore by stack (useful for freeing memory allocated in get_instance/get_next_instance methods) */
+  node_instance_release_method release_instance;
+
+  /** reference to pass arbitrary value between calls to get_instance() and get_value/test_value/set_value */
+  union snmp_variant_value reference;
+  /** see reference (if reference is a pointer, the length of underlying data may be stored here or anything else) */
+  u32_t reference_len;
+};
+
+
+/** SNMP tree node */
+struct snmp_tree_node
+{
+  /** inherited "base class" members */
+  struct snmp_node node;
+  u16_t subnode_count;
+  const struct snmp_node* const *subnodes;
+};
+
+#define SNMP_CREATE_TREE_NODE(oid, subnodes) \
+  {{ SNMP_NODE_TREE, (oid) }, \
+  (u16_t)LWIP_ARRAYSIZE(subnodes), (subnodes) }
+
+#define SNMP_CREATE_EMPTY_TREE_NODE(oid) \
+  {{ SNMP_NODE_TREE, (oid) }, \
+  0, NULL }
+
+/** SNMP leaf node */
+struct snmp_leaf_node
+{
+  /** inherited "base class" members */
+  struct snmp_node node;
+  snmp_err_t (*get_instance)(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance);
+  snmp_err_t (*get_next_instance)(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance);
+};
+
+/** represents a single mib with its base oid and root node */
+struct snmp_mib
+{
+  const u32_t *base_oid;
+  u8_t base_oid_len;
+  const struct snmp_node *root_node;
+};
+
+#define SNMP_MIB_CREATE(oid_list, root_node) { (oid_list), (u8_t)LWIP_ARRAYSIZE(oid_list), root_node }
+
+/** OID range structure */
+struct snmp_oid_range
+{
+  u32_t min;
+  u32_t max;
+};
+
+/** checks if incoming OID length and values are in allowed ranges */
+u8_t snmp_oid_in_range(const u32_t *oid_in, u8_t oid_len, const struct snmp_oid_range *oid_ranges, u8_t oid_ranges_len);
+
+typedef enum {
+  SNMP_NEXT_OID_STATUS_SUCCESS,
+  SNMP_NEXT_OID_STATUS_NO_MATCH,
+  SNMP_NEXT_OID_STATUS_BUF_TO_SMALL
+} snmp_next_oid_status_t;
+
+/** state for next_oid_init / next_oid_check functions */
+struct snmp_next_oid_state
+{
+  const u32_t* start_oid;
+  u8_t start_oid_len;
+
+  u32_t* next_oid;
+  u8_t next_oid_len;
+  u8_t next_oid_max_len;
+
+  snmp_next_oid_status_t status;
+  void* reference;
+};
+
+void snmp_next_oid_init(struct snmp_next_oid_state *state,
+  const u32_t *start_oid, u8_t start_oid_len,
+  u32_t *next_oid_buf, u8_t next_oid_max_len);
+u8_t snmp_next_oid_precheck(struct snmp_next_oid_state *state, const u32_t *oid, const u8_t oid_len);
+u8_t snmp_next_oid_check(struct snmp_next_oid_state *state, const u32_t *oid, const u8_t oid_len, void* reference);
+
+void snmp_oid_assign(struct snmp_obj_id* target, const u32_t *oid, u8_t oid_len);
+void snmp_oid_combine(struct snmp_obj_id* target, const u32_t *oid1, u8_t oid1_len, const u32_t *oid2, u8_t oid2_len);
+void snmp_oid_prefix(struct snmp_obj_id* target, const u32_t *oid, u8_t oid_len);
+void snmp_oid_append(struct snmp_obj_id* target, const u32_t *oid, u8_t oid_len);
+u8_t snmp_oid_equal(const u32_t *oid1, u8_t oid1_len, const u32_t *oid2, u8_t oid2_len);
+s8_t snmp_oid_compare(const u32_t *oid1, u8_t oid1_len, const u32_t *oid2, u8_t oid2_len);
+
+#if LWIP_IPV4
+u8_t snmp_oid_to_ip4(const u32_t *oid, ip4_addr_t *ip);
+void snmp_ip4_to_oid(const ip4_addr_t *ip, u32_t *oid);
+#endif /* LWIP_IPV4 */
+#if LWIP_IPV6
+u8_t snmp_oid_to_ip6(const u32_t *oid, ip6_addr_t *ip);
+void snmp_ip6_to_oid(const ip6_addr_t *ip, u32_t *oid);
+#endif /* LWIP_IPV6 */
+#if LWIP_IPV4 || LWIP_IPV6
+u8_t snmp_ip_to_oid(const ip_addr_t *ip, u32_t *oid);
+u8_t snmp_ip_port_to_oid(const ip_addr_t *ip, u16_t port, u32_t *oid);
+
+u8_t snmp_oid_to_ip(const u32_t *oid, u8_t oid_len, ip_addr_t *ip);
+u8_t snmp_oid_to_ip_port(const u32_t *oid, u8_t oid_len, ip_addr_t *ip, u16_t *port);
+#endif /* LWIP_IPV4 || LWIP_IPV6 */
+
+struct netif;
+u8_t netif_to_num(const struct netif *netif);
+
+snmp_err_t snmp_set_test_ok(struct snmp_node_instance* instance, u16_t value_len, void* value); /* generic function which can be used if test is always successful */
+
+err_t snmp_decode_bits(const u8_t *buf, u32_t buf_len, u32_t *bit_value);
+err_t snmp_decode_truthvalue(const s32_t *asn1_value, u8_t *bool_value);
+u8_t  snmp_encode_bits(u8_t *buf, u32_t buf_len, u32_t bit_value, u8_t bit_count);
+u8_t  snmp_encode_truthvalue(s32_t *asn1_value, u32_t bool_value);
+
+struct snmp_statistics
+{
+  u32_t inpkts;
+  u32_t outpkts;
+  u32_t inbadversions;
+  u32_t inbadcommunitynames;
+  u32_t inbadcommunityuses;
+  u32_t inasnparseerrs;
+  u32_t intoobigs;
+  u32_t innosuchnames;
+  u32_t inbadvalues;
+  u32_t inreadonlys;
+  u32_t ingenerrs;
+  u32_t intotalreqvars;
+  u32_t intotalsetvars;
+  u32_t ingetrequests;
+  u32_t ingetnexts;
+  u32_t insetrequests;
+  u32_t ingetresponses;
+  u32_t intraps;
+  u32_t outtoobigs;
+  u32_t outnosuchnames;
+  u32_t outbadvalues;
+  u32_t outgenerrs;
+  u32_t outgetrequests;
+  u32_t outgetnexts;
+  u32_t outsetrequests;
+  u32_t outgetresponses;
+  u32_t outtraps;
+};
+
+extern struct snmp_statistics snmp_stats;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_SNMP */
+
+#endif /* LWIP_HDR_APPS_SNMP_CORE_H */

+ 78 - 0
thirdparty/lwip/src/include/lwip/apps/snmp_mib2.h

@@ -0,0 +1,78 @@
+/**
+ * @file
+ * SNMP MIB2 API
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Dirk Ziegelmeier <dziegel@gmx.de>
+ *
+ */
+#ifndef LWIP_HDR_APPS_SNMP_MIB2_H
+#define LWIP_HDR_APPS_SNMP_MIB2_H
+
+#include "lwip/apps/snmp_opts.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+#if SNMP_LWIP_MIB2
+
+#include "lwip/apps/snmp_core.h"
+
+extern const struct snmp_mib mib2;
+
+#if SNMP_USE_NETCONN
+#include "lwip/apps/snmp_threadsync.h"
+void snmp_mib2_lwip_synchronizer(snmp_threadsync_called_fn fn, void* arg);
+extern struct snmp_threadsync_instance snmp_mib2_lwip_locks;
+#endif
+
+#ifndef SNMP_SYSSERVICES
+#define SNMP_SYSSERVICES ((1 << 6) | (1 << 3) | ((IP_FORWARD) << 2))
+#endif
+
+void snmp_mib2_set_sysdescr(const u8_t* str, const u16_t* len); /* read-only be defintion */
+void snmp_mib2_set_syscontact(u8_t *ocstr, u16_t *ocstrlen, u16_t bufsize);
+void snmp_mib2_set_syscontact_readonly(const u8_t *ocstr, const u16_t *ocstrlen);
+void snmp_mib2_set_sysname(u8_t *ocstr, u16_t *ocstrlen, u16_t bufsize);
+void snmp_mib2_set_sysname_readonly(const u8_t *ocstr, const u16_t *ocstrlen);
+void snmp_mib2_set_syslocation(u8_t *ocstr, u16_t *ocstrlen, u16_t bufsize);
+void snmp_mib2_set_syslocation_readonly(const u8_t *ocstr, const u16_t *ocstrlen);
+
+#endif /* SNMP_LWIP_MIB2 */
+#endif /* LWIP_SNMP */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_APPS_SNMP_MIB2_H */

+ 293 - 0
thirdparty/lwip/src/include/lwip/apps/snmp_opts.h

@@ -0,0 +1,293 @@
+/**
+ * @file
+ * SNMP server options list
+ */
+
+/*
+ * Copyright (c) 2015 Dirk Ziegelmeier
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Dirk Ziegelmeier
+ *
+ */
+#ifndef LWIP_HDR_SNMP_OPTS_H
+#define LWIP_HDR_SNMP_OPTS_H
+
+#include "lwip/opt.h"
+
+/**
+ * @defgroup snmp_opts Options
+ * @ingroup snmp
+ * @{
+ */
+
+/**
+ * LWIP_SNMP==1: This enables the lwIP SNMP agent. UDP must be available
+ * for SNMP transport.
+ * If you want to use your own SNMP agent, leave this disabled.
+ * To integrate MIB2 of an external agent, you need to enable
+ * LWIP_MIB2_CALLBACKS and MIB2_STATS. This will give you the callbacks
+ * and statistics counters you need to get MIB2 working.
+ */
+#if !defined LWIP_SNMP || defined __DOXYGEN__
+#define LWIP_SNMP                       0
+#endif
+
+/**
+ * SNMP_USE_NETCONN: Use netconn API instead of raw API.
+ * Makes SNMP agent run in a worker thread, so blocking operations
+ * can be done in MIB calls.
+ */
+#if !defined SNMP_USE_NETCONN || defined __DOXYGEN__
+#define SNMP_USE_NETCONN           0
+#endif
+
+/**
+ * SNMP_USE_RAW: Use raw API.
+ * SNMP agent does not run in a worker thread, so blocking operations
+ * should not be done in MIB calls.
+ */
+#if !defined SNMP_USE_RAW || defined __DOXYGEN__
+#define SNMP_USE_RAW               1
+#endif
+
+#if SNMP_USE_NETCONN && SNMP_USE_RAW
+#error SNMP stack can use only one of the APIs {raw, netconn}
+#endif
+
+#if LWIP_SNMP && !SNMP_USE_NETCONN && !SNMP_USE_RAW
+#error SNMP stack needs a receive API and UDP {raw, netconn}
+#endif
+
+#if SNMP_USE_NETCONN
+/**
+ * SNMP_STACK_SIZE: Stack size of SNMP netconn worker thread
+ */
+#if !defined SNMP_STACK_SIZE || defined __DOXYGEN__
+#define SNMP_STACK_SIZE            DEFAULT_THREAD_STACKSIZE
+#endif
+
+/**
+ * SNMP_THREAD_PRIO: SNMP netconn worker thread priority
+ */
+#if !defined SNMP_THREAD_PRIO || defined __DOXYGEN__
+#define SNMP_THREAD_PRIO           DEFAULT_THREAD_PRIO
+#endif
+#endif /* SNMP_USE_NETCONN */
+
+/**
+ * SNMP_TRAP_DESTINATIONS: Number of trap destinations. At least one trap
+ * destination is required
+ */
+#if !defined SNMP_TRAP_DESTINATIONS || defined __DOXYGEN__
+#define SNMP_TRAP_DESTINATIONS          1
+#endif
+
+/**
+ * Only allow SNMP write actions that are 'safe' (e.g. disabling netifs is not
+ * a safe action and disabled when SNMP_SAFE_REQUESTS = 1).
+ * Unsafe requests are disabled by default!
+ */
+#if !defined SNMP_SAFE_REQUESTS || defined __DOXYGEN__
+#define SNMP_SAFE_REQUESTS              1
+#endif
+
+/**
+ * The maximum length of strings used.
+ */
+#if !defined SNMP_MAX_OCTET_STRING_LEN || defined __DOXYGEN__
+#define SNMP_MAX_OCTET_STRING_LEN       127
+#endif
+
+/**
+ * The maximum number of Sub ID's inside an object identifier.
+ * Indirectly this also limits the maximum depth of SNMP tree.
+ */
+#if !defined SNMP_MAX_OBJ_ID_LEN || defined __DOXYGEN__
+#define SNMP_MAX_OBJ_ID_LEN             50
+#endif
+
+#if !defined SNMP_MAX_VALUE_SIZE || defined __DOXYGEN__
+/**
+ * The maximum size of a value.
+ */
+#define SNMP_MIN_VALUE_SIZE             (2 * sizeof(u32_t*)) /* size required to store the basic types (8 bytes for counter64) */
+/**
+ * The minimum size of a value.
+ */
+#define SNMP_MAX_VALUE_SIZE             LWIP_MAX(LWIP_MAX((SNMP_MAX_OCTET_STRING_LEN), sizeof(u32_t)*(SNMP_MAX_OBJ_ID_LEN)), SNMP_MIN_VALUE_SIZE)
+#endif
+
+/**
+ * The snmp read-access community. Used for write-access and traps, too
+ * unless SNMP_COMMUNITY_WRITE or SNMP_COMMUNITY_TRAP are enabled, respectively.
+ */
+#if !defined SNMP_COMMUNITY || defined __DOXYGEN__
+#define SNMP_COMMUNITY                  "public"
+#endif
+
+/**
+ * The snmp write-access community.
+ * Set this community to "" in order to disallow any write access.
+ */
+#if !defined SNMP_COMMUNITY_WRITE || defined __DOXYGEN__
+#define SNMP_COMMUNITY_WRITE            "private"
+#endif
+
+/**
+ * The snmp community used for sending traps.
+ */
+#if !defined SNMP_COMMUNITY_TRAP || defined __DOXYGEN__
+#define SNMP_COMMUNITY_TRAP             "public"
+#endif
+
+/**
+ * The maximum length of community string.
+ * If community names shall be adjusted at runtime via snmp_set_community() calls,
+ * enter here the possible maximum length (+1 for terminating null character).
+ */
+#if !defined SNMP_MAX_COMMUNITY_STR_LEN || defined __DOXYGEN__
+#define SNMP_MAX_COMMUNITY_STR_LEN LWIP_MAX(LWIP_MAX(sizeof(SNMP_COMMUNITY), sizeof(SNMP_COMMUNITY_WRITE)), sizeof(SNMP_COMMUNITY_TRAP))
+#endif
+
+/**
+ * The OID identifiying the device. This may be the enterprise OID itself or any OID located below it in tree.
+ */
+#if !defined SNMP_DEVICE_ENTERPRISE_OID || defined __DOXYGEN__
+#define SNMP_LWIP_ENTERPRISE_OID 26381
+/**
+ * IANA assigned enterprise ID for lwIP is 26381
+ * @see http://www.iana.org/assignments/enterprise-numbers
+ *
+ * @note this enterprise ID is assigned to the lwIP project,
+ * all object identifiers living under this ID are assigned
+ * by the lwIP maintainers!
+ * @note don't change this define, use snmp_set_device_enterprise_oid()
+ *
+ * If you need to create your own private MIB you'll need
+ * to apply for your own enterprise ID with IANA:
+ * http://www.iana.org/numbers.html
+ */
+#define SNMP_DEVICE_ENTERPRISE_OID {1, 3, 6, 1, 4, 1, SNMP_LWIP_ENTERPRISE_OID}
+/**
+ * Length of SNMP_DEVICE_ENTERPRISE_OID
+ */
+#define SNMP_DEVICE_ENTERPRISE_OID_LEN 7
+#endif
+
+/**
+ * SNMP_DEBUG: Enable debugging for SNMP messages.
+ */
+#if !defined SNMP_DEBUG || defined __DOXYGEN__
+#define SNMP_DEBUG                      LWIP_DBG_OFF
+#endif
+
+/**
+ * SNMP_MIB_DEBUG: Enable debugging for SNMP MIBs.
+ */
+#if !defined SNMP_MIB_DEBUG || defined __DOXYGEN__
+#define SNMP_MIB_DEBUG                  LWIP_DBG_OFF
+#endif
+
+/**
+ * Indicates if the MIB2 implementation of LWIP SNMP stack is used.
+ */
+#if !defined SNMP_LWIP_MIB2 || defined __DOXYGEN__
+#define SNMP_LWIP_MIB2                      LWIP_SNMP
+#endif
+
+/**
+ * Value return for sysDesc field of MIB2.
+ */
+#if !defined SNMP_LWIP_MIB2_SYSDESC || defined __DOXYGEN__
+#define SNMP_LWIP_MIB2_SYSDESC              "lwIP"
+#endif
+
+/**
+ * Value return for sysName field of MIB2.
+ * To make sysName field settable, call snmp_mib2_set_sysname() to provide the necessary buffers.
+ */
+#if !defined SNMP_LWIP_MIB2_SYSNAME || defined __DOXYGEN__
+#define SNMP_LWIP_MIB2_SYSNAME              "FQDN-unk"
+#endif
+
+/**
+ * Value return for sysContact field of MIB2.
+ * To make sysContact field settable, call snmp_mib2_set_syscontact() to provide the necessary buffers.
+ */
+#if !defined SNMP_LWIP_MIB2_SYSCONTACT || defined __DOXYGEN__
+#define SNMP_LWIP_MIB2_SYSCONTACT           ""
+#endif
+
+/**
+ * Value return for sysLocation field of MIB2.
+ * To make sysLocation field settable, call snmp_mib2_set_syslocation() to provide the necessary buffers.
+ */
+#if !defined SNMP_LWIP_MIB2_SYSLOCATION || defined __DOXYGEN__
+#define SNMP_LWIP_MIB2_SYSLOCATION          ""
+#endif
+
+/**
+ * This value is used to limit the repetitions processed in GetBulk requests (value == 0 means no limitation).
+ * This may be useful to limit the load for a single request.
+ * According to SNMP RFC 1905 it is allowed to not return all requested variables from a GetBulk request if system load would be too high.
+ * so the effect is that the client will do more requests to gather all data.
+ * For the stack this could be useful in case that SNMP processing is done in TCP/IP thread. In this situation a request with many
+ * repetitions could block the thread for a longer time. Setting limit here will keep the stack more responsive.
+ */
+#if !defined SNMP_LWIP_GETBULK_MAX_REPETITIONS || defined __DOXYGEN__
+#define SNMP_LWIP_GETBULK_MAX_REPETITIONS 0
+#endif
+
+/**
+ * @}
+ */
+
+/*
+   ------------------------------------
+   ---------- SNMPv3 options ----------
+   ------------------------------------
+*/
+
+/**
+ * LWIP_SNMP_V3==1: This enables EXPERIMENTAL SNMPv3 support. LWIP_SNMP must
+ * also be enabled.
+ * THIS IS UNDER DEVELOPMENT AND SHOULD NOT BE ENABLED IN PRODUCTS.
+ */
+#ifndef LWIP_SNMP_V3
+#define LWIP_SNMP_V3               0
+#endif
+
+#ifndef LWIP_SNMP_V3_CRYPTO
+#define LWIP_SNMP_V3_CRYPTO        LWIP_SNMP_V3
+#endif
+
+#ifndef LWIP_SNMP_V3_MBEDTLS
+#define LWIP_SNMP_V3_MBEDTLS       LWIP_SNMP_V3
+#endif
+
+#endif /* LWIP_HDR_SNMP_OPTS_H */

+ 113 - 0
thirdparty/lwip/src/include/lwip/apps/snmp_scalar.h

@@ -0,0 +1,113 @@
+/**
+ * @file
+ * SNMP server MIB API to implement scalar nodes
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Martin Hentschel <info@cl-soft.de>
+ *
+ */
+
+#ifndef LWIP_HDR_APPS_SNMP_SCALAR_H
+#define LWIP_HDR_APPS_SNMP_SCALAR_H
+
+#include "lwip/apps/snmp_opts.h"
+#include "lwip/apps/snmp_core.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+/** basic scalar node */
+struct snmp_scalar_node
+{
+  /** inherited "base class" members */
+  struct snmp_leaf_node node;
+  u8_t asn1_type;
+  snmp_access_t access;
+  node_instance_get_value_method get_value;
+  node_instance_set_test_method set_test;
+  node_instance_set_value_method set_value;
+};
+
+
+snmp_err_t snmp_scalar_get_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance);
+snmp_err_t snmp_scalar_get_next_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance);
+
+#define SNMP_SCALAR_CREATE_NODE(oid, access, asn1_type, get_value_method, set_test_method, set_value_method) \
+  {{{ SNMP_NODE_SCALAR, (oid) }, \
+    snmp_scalar_get_instance, \
+    snmp_scalar_get_next_instance }, \
+    (asn1_type), (access), (get_value_method), (set_test_method), (set_value_method) }
+
+#define SNMP_SCALAR_CREATE_NODE_READONLY(oid, asn1_type, get_value_method) SNMP_SCALAR_CREATE_NODE(oid, SNMP_NODE_INSTANCE_READ_ONLY, asn1_type, get_value_method, NULL, NULL)
+
+/** scalar array node - a tree node which contains scalars only as children */
+struct snmp_scalar_array_node_def
+{
+  u32_t         oid;
+  u8_t          asn1_type;
+  snmp_access_t access;
+};
+
+typedef s16_t (*snmp_scalar_array_get_value_method)(const struct snmp_scalar_array_node_def*, void*);
+typedef snmp_err_t (*snmp_scalar_array_set_test_method)(const struct snmp_scalar_array_node_def*, u16_t, void*);
+typedef snmp_err_t (*snmp_scalar_array_set_value_method)(const struct snmp_scalar_array_node_def*, u16_t, void*);
+
+/** basic scalar array node */
+struct snmp_scalar_array_node
+{
+  /** inherited "base class" members */
+  struct snmp_leaf_node node;
+  u16_t array_node_count;
+  const struct snmp_scalar_array_node_def* array_nodes;
+  snmp_scalar_array_get_value_method get_value;
+  snmp_scalar_array_set_test_method set_test;
+  snmp_scalar_array_set_value_method set_value;
+};
+
+snmp_err_t snmp_scalar_array_get_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance);
+snmp_err_t snmp_scalar_array_get_next_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance);
+
+#define SNMP_SCALAR_CREATE_ARRAY_NODE(oid, array_nodes, get_value_method, set_test_method, set_value_method) \
+  {{{ SNMP_NODE_SCALAR_ARRAY, (oid) }, \
+    snmp_scalar_array_get_instance, \
+    snmp_scalar_array_get_next_instance }, \
+    (u16_t)LWIP_ARRAYSIZE(array_nodes), (array_nodes), (get_value_method), (set_test_method), (set_value_method) }
+
+#endif /* LWIP_SNMP */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_APPS_SNMP_SCALAR_H */

+ 134 - 0
thirdparty/lwip/src/include/lwip/apps/snmp_table.h

@@ -0,0 +1,134 @@
+/**
+ * @file
+ * SNMP server MIB API to implement table nodes
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Martin Hentschel <info@cl-soft.de>
+ *
+ */
+
+#ifndef LWIP_HDR_APPS_SNMP_TABLE_H
+#define LWIP_HDR_APPS_SNMP_TABLE_H
+
+#include "lwip/apps/snmp_opts.h"
+#include "lwip/apps/snmp_core.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+/** default (customizable) read/write table */
+struct snmp_table_col_def
+{
+  u32_t index;
+  u8_t asn1_type;
+  snmp_access_t access;
+};
+
+/** table node */
+struct snmp_table_node
+{
+  /** inherited "base class" members */
+  struct snmp_leaf_node node;
+  u16_t column_count;
+  const struct snmp_table_col_def* columns;
+  snmp_err_t (*get_cell_instance)(const u32_t* column, const u32_t* row_oid, u8_t row_oid_len, struct snmp_node_instance* cell_instance);
+  snmp_err_t (*get_next_cell_instance)(const u32_t* column, struct snmp_obj_id* row_oid, struct snmp_node_instance* cell_instance);
+  /** returns object value for the given object identifier */
+  node_instance_get_value_method get_value;
+  /** tests length and/or range BEFORE setting */
+  node_instance_set_test_method set_test;
+  /** sets object value, only called when set_test() was successful */
+  node_instance_set_value_method set_value;
+};
+
+snmp_err_t snmp_table_get_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance);
+snmp_err_t snmp_table_get_next_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance);
+
+#define SNMP_TABLE_CREATE(oid, columns, get_cell_instance_method, get_next_cell_instance_method, get_value_method, set_test_method, set_value_method) \
+  {{{ SNMP_NODE_TABLE, (oid) }, \
+  snmp_table_get_instance, \
+  snmp_table_get_next_instance }, \
+  (u16_t)LWIP_ARRAYSIZE(columns), (columns), \
+  (get_cell_instance_method), (get_next_cell_instance_method), \
+  (get_value_method), (set_test_method), (set_value_method)}
+
+#define SNMP_TABLE_GET_COLUMN_FROM_OID(oid) ((oid)[1]) /* first array value is (fixed) row entry (fixed to 1) and 2nd value is column, follow3ed by instance */
+
+
+/** simple read-only table */
+typedef enum {
+  SNMP_VARIANT_VALUE_TYPE_U32,
+  SNMP_VARIANT_VALUE_TYPE_S32,
+  SNMP_VARIANT_VALUE_TYPE_PTR,
+  SNMP_VARIANT_VALUE_TYPE_CONST_PTR
+} snmp_table_column_data_type_t;
+
+struct snmp_table_simple_col_def
+{
+  u32_t index;
+  u8_t asn1_type;
+  snmp_table_column_data_type_t data_type; /* depending of what union member is used to store the value*/
+};
+
+/** simple read-only table node */
+struct snmp_table_simple_node
+{
+  /* inherited "base class" members */
+  struct snmp_leaf_node node;
+  u16_t column_count;
+  const struct snmp_table_simple_col_def* columns;
+  snmp_err_t (*get_cell_value)(const u32_t* column, const u32_t* row_oid, u8_t row_oid_len, union snmp_variant_value* value, u32_t* value_len);
+  snmp_err_t (*get_next_cell_instance_and_value)(const u32_t* column, struct snmp_obj_id* row_oid, union snmp_variant_value* value, u32_t* value_len);
+};
+
+snmp_err_t snmp_table_simple_get_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance);
+snmp_err_t snmp_table_simple_get_next_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance);
+
+#define SNMP_TABLE_CREATE_SIMPLE(oid, columns, get_cell_value_method, get_next_cell_instance_and_value_method) \
+  {{{ SNMP_NODE_TABLE, (oid) }, \
+  snmp_table_simple_get_instance, \
+  snmp_table_simple_get_next_instance }, \
+  (u16_t)LWIP_ARRAYSIZE(columns), (columns), (get_cell_value_method), (get_next_cell_instance_and_value_method) }
+
+s16_t snmp_table_extract_value_from_s32ref(struct snmp_node_instance* instance, void* value);
+s16_t snmp_table_extract_value_from_u32ref(struct snmp_node_instance* instance, void* value);
+s16_t snmp_table_extract_value_from_refconstptr(struct snmp_node_instance* instance, void* value);
+
+#endif /* LWIP_SNMP */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_APPS_SNMP_TABLE_H */

+ 114 - 0
thirdparty/lwip/src/include/lwip/apps/snmp_threadsync.h

@@ -0,0 +1,114 @@
+/**
+ * @file
+ * SNMP server MIB API to implement thread synchronization
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Dirk Ziegelmeier <dziegel@gmx.de>
+ *
+ */
+
+#ifndef LWIP_HDR_APPS_SNMP_THREADSYNC_H
+#define LWIP_HDR_APPS_SNMP_THREADSYNC_H
+
+#include "lwip/apps/snmp_opts.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/apps/snmp_core.h"
+#include "lwip/sys.h"
+
+typedef void (*snmp_threadsync_called_fn)(void* arg);
+typedef void (*snmp_threadsync_synchronizer_fn)(snmp_threadsync_called_fn fn, void* arg);
+
+
+/** Thread sync runtime data. For internal usage only. */
+struct threadsync_data
+{
+  union {
+    snmp_err_t err;
+    s16_t s16;
+  } retval;
+  union {
+    const u32_t *root_oid;
+    void *value;
+  } arg1;
+  union {
+    u8_t root_oid_len;
+    u16_t len;
+  } arg2;
+  const struct snmp_threadsync_node *threadsync_node;
+  struct snmp_node_instance proxy_instance;
+};
+
+/** Thread sync instance. Needed EXCATLY once for every thread to be synced into. */
+struct snmp_threadsync_instance
+{
+  sys_sem_t                       sem;
+  sys_mutex_t                     sem_usage_mutex;
+  snmp_threadsync_synchronizer_fn sync_fn;
+  struct threadsync_data          data;
+};
+
+/** SNMP thread sync proxy leaf node */
+struct snmp_threadsync_node
+{
+  /* inherited "base class" members */
+  struct snmp_leaf_node           node;
+
+  const struct snmp_leaf_node     *target;
+  struct snmp_threadsync_instance *instance;
+};
+
+snmp_err_t snmp_threadsync_get_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance);
+snmp_err_t snmp_threadsync_get_next_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance);
+
+/** Create thread sync proxy node */
+#define SNMP_CREATE_THREAD_SYNC_NODE(oid, target_leaf_node, threadsync_instance) \
+  {{{ SNMP_NODE_THREADSYNC, (oid) }, \
+    snmp_threadsync_get_instance, \
+    snmp_threadsync_get_next_instance }, \
+    (target_leaf_node), \
+    (threadsync_instance) }
+
+/** Create thread sync instance data */
+void snmp_threadsync_init(struct snmp_threadsync_instance *instance, snmp_threadsync_synchronizer_fn sync_fn);
+
+#endif /* LWIP_SNMP */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_APPS_SNMP_THREADSYNC_H */

+ 90 - 0
thirdparty/lwip/src/include/lwip/apps/snmpv3.h

@@ -0,0 +1,90 @@
+/**
+ * @file
+ * Additional SNMPv3 functionality RFC3414 and RFC3826.
+ */
+
+/*
+ * Copyright (c) 2016 Elias Oenal.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Elias Oenal <lwip@eliasoenal.com>
+ */
+
+#ifndef LWIP_HDR_APPS_SNMP_V3_H
+#define LWIP_HDR_APPS_SNMP_V3_H
+
+#include "lwip/apps/snmp_opts.h"
+#include "lwip/err.h"
+
+#if LWIP_SNMP && LWIP_SNMP_V3
+
+#define SNMP_V3_AUTH_ALGO_INVAL  0
+#define SNMP_V3_AUTH_ALGO_MD5    1
+#define SNMP_V3_AUTH_ALGO_SHA    2
+
+#define SNMP_V3_PRIV_ALGO_INVAL  0
+#define SNMP_V3_PRIV_ALGO_DES    1
+#define SNMP_V3_PRIV_ALGO_AES    2
+
+#define SNMP_V3_PRIV_MODE_DECRYPT  0
+#define SNMP_V3_PRIV_MODE_ENCRYPT  1
+
+/*
+ * The following callback functions must be implemented by the application.
+ * There is a dummy implementation in snmpv3_dummy.c.
+ */
+
+void snmpv3_get_engine_id(const char **id, u8_t *len);
+err_t snmpv3_set_engine_id(const char* id, u8_t len);
+
+u32_t snmpv3_get_engine_boots(void);
+void snmpv3_set_engine_boots(u32_t boots);
+
+u32_t snmpv3_get_engine_time(void);
+void snmpv3_reset_engine_time(void);
+
+err_t snmpv3_get_user(const char* username, u8_t *auth_algo, u8_t *auth_key, u8_t *priv_algo, u8_t *priv_key);
+
+/* The following functions are provided by the SNMPv3 agent */
+
+void snmpv3_engine_id_changed(void);
+
+void snmpv3_password_to_key_md5(
+    const u8_t *password,     /* IN */
+    u8_t        passwordlen,  /* IN */
+    const u8_t *engineID,     /* IN  - pointer to snmpEngineID  */
+    u8_t        engineLength, /* IN  - length of snmpEngineID */
+    u8_t       *key);         /* OUT - pointer to caller 16-octet buffer */
+
+void snmpv3_password_to_key_sha(
+    const u8_t *password,     /* IN */
+    u8_t        passwordlen,  /* IN */
+    const u8_t *engineID,     /* IN  - pointer to snmpEngineID  */
+    u8_t        engineLength, /* IN  - length of snmpEngineID */
+    u8_t       *key);         /* OUT - pointer to caller 20-octet buffer */
+
+#endif
+
+#endif /* LWIP_HDR_APPS_SNMP_V3_H */

+ 76 - 0
thirdparty/lwip/src/include/lwip/apps/sntp.h

@@ -0,0 +1,76 @@
+/**
+ * @file
+ * SNTP client API
+ */
+
+/*
+ * Copyright (c) 2007-2009 Frédéric Bernon, Simon Goldschmidt
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Frédéric Bernon, Simon Goldschmidt
+ *
+ */
+#ifndef LWIP_HDR_APPS_SNTP_H
+#define LWIP_HDR_APPS_SNTP_H
+
+#include "lwip/apps/sntp_opts.h"
+#include "lwip/ip_addr.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* SNTP operating modes: default is to poll using unicast.
+   The mode has to be set before calling sntp_init(). */
+#define SNTP_OPMODE_POLL            0
+#define SNTP_OPMODE_LISTENONLY      1
+void sntp_setoperatingmode(u8_t operating_mode);
+u8_t sntp_getoperatingmode(void);
+
+void sntp_init(void);
+void sntp_stop(void);
+u8_t sntp_enabled(void);
+
+void sntp_setserver(u8_t idx, const ip_addr_t *addr);
+const ip_addr_t* sntp_getserver(u8_t idx);
+
+#if SNTP_SERVER_DNS
+void sntp_setservername(u8_t idx, char *server);
+char *sntp_getservername(u8_t idx);
+#endif /* SNTP_SERVER_DNS */
+
+#if SNTP_GET_SERVERS_FROM_DHCP
+void sntp_servermode_dhcp(int set_servers_from_dhcp);
+#else /* SNTP_GET_SERVERS_FROM_DHCP */
+#define sntp_servermode_dhcp(x)
+#endif /* SNTP_GET_SERVERS_FROM_DHCP */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_APPS_SNTP_H */

+ 173 - 0
thirdparty/lwip/src/include/lwip/apps/sntp_opts.h

@@ -0,0 +1,173 @@
+/**
+ * @file
+ * SNTP client options list
+ */
+
+/*
+ * Copyright (c) 2007-2009 Frédéric Bernon, Simon Goldschmidt
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Frédéric Bernon, Simon Goldschmidt
+ *
+ */
+#ifndef LWIP_HDR_APPS_SNTP_OPTS_H
+#define LWIP_HDR_APPS_SNTP_OPTS_H
+
+#include "lwip/opt.h"
+
+/**
+ * @defgroup sntp_opts Options
+ * @ingroup sntp
+ * @{
+ */
+
+/** SNTP macro to change system time in seconds
+ * Define SNTP_SET_SYSTEM_TIME_US(sec, us) to set the time in microseconds instead of this one
+ * if you need the additional precision.
+ */
+#if !defined SNTP_SET_SYSTEM_TIME || defined __DOXYGEN__
+#define SNTP_SET_SYSTEM_TIME(sec)   LWIP_UNUSED_ARG(sec)
+#endif
+
+/** The maximum number of SNTP servers that can be set */
+#if !defined SNTP_MAX_SERVERS || defined __DOXYGEN__
+#define SNTP_MAX_SERVERS           LWIP_DHCP_MAX_NTP_SERVERS
+#endif
+
+/** Set this to 1 to implement the callback function called by dhcp when
+ * NTP servers are received. */
+#if !defined SNTP_GET_SERVERS_FROM_DHCP || defined __DOXYGEN__
+#define SNTP_GET_SERVERS_FROM_DHCP LWIP_DHCP_GET_NTP_SRV
+#endif
+
+/** Set this to 1 to support DNS names (or IP address strings) to set sntp servers
+ * One server address/name can be defined as default if SNTP_SERVER_DNS == 1:
+ * \#define SNTP_SERVER_ADDRESS "pool.ntp.org"
+ */
+#if !defined SNTP_SERVER_DNS || defined __DOXYGEN__
+#define SNTP_SERVER_DNS            0
+#endif
+
+/**
+ * SNTP_DEBUG: Enable debugging for SNTP.
+ */
+#if !defined SNTP_DEBUG || defined __DOXYGEN__
+#define SNTP_DEBUG                  LWIP_DBG_OFF
+#endif
+
+/** SNTP server port */
+#if !defined SNTP_PORT || defined __DOXYGEN__
+#define SNTP_PORT                   123
+#endif
+
+/** Set this to 1 to allow config of SNTP server(s) by DNS name */
+#if !defined SNTP_SERVER_DNS || defined __DOXYGEN__
+#define SNTP_SERVER_DNS             0
+#endif
+
+/** Sanity check:
+ * Define this to
+ * - 0 to turn off sanity checks (default; smaller code)
+ * - >= 1 to check address and port of the response packet to ensure the
+ *        response comes from the server we sent the request to.
+ * - >= 2 to check returned Originate Timestamp against Transmit Timestamp
+ *        sent to the server (to ensure response to older request).
+ * - >= 3 @todo: discard reply if any of the LI, Stratum, or Transmit Timestamp
+ *        fields is 0 or the Mode field is not 4 (unicast) or 5 (broadcast).
+ * - >= 4 @todo: to check that the Root Delay and Root Dispersion fields are each
+ *        greater than or equal to 0 and less than infinity, where infinity is
+ *        currently a cozy number like one second. This check avoids using a
+ *        server whose synchronization source has expired for a very long time.
+ */
+#if !defined SNTP_CHECK_RESPONSE || defined __DOXYGEN__
+#define SNTP_CHECK_RESPONSE         0
+#endif
+
+/** According to the RFC, this shall be a random delay
+ * between 1 and 5 minutes (in milliseconds) to prevent load peaks.
+ * This can be defined to a random generation function,
+ * which must return the delay in milliseconds as u32_t.
+ * Turned off by default.
+ */
+#if !defined SNTP_STARTUP_DELAY || defined __DOXYGEN__
+#define SNTP_STARTUP_DELAY          0
+#endif
+
+/** If you want the startup delay to be a function, define this
+ * to a function (including the brackets) and define SNTP_STARTUP_DELAY to 1.
+ */
+#if !defined SNTP_STARTUP_DELAY_FUNC || defined __DOXYGEN__
+#define SNTP_STARTUP_DELAY_FUNC     SNTP_STARTUP_DELAY
+#endif
+
+/** SNTP receive timeout - in milliseconds
+ * Also used as retry timeout - this shouldn't be too low.
+ * Default is 3 seconds.
+ */
+#if !defined SNTP_RECV_TIMEOUT || defined __DOXYGEN__
+#define SNTP_RECV_TIMEOUT           3000
+#endif
+
+/** SNTP update delay - in milliseconds
+ * Default is 1 hour. Must not be beolw 15 seconds by specification (i.e. 15000)
+ */
+#if !defined SNTP_UPDATE_DELAY || defined __DOXYGEN__
+#define SNTP_UPDATE_DELAY           3600000
+#endif
+
+/** SNTP macro to get system time, used with SNTP_CHECK_RESPONSE >= 2
+ * to send in request and compare in response.
+ */
+#if !defined SNTP_GET_SYSTEM_TIME || defined __DOXYGEN__
+#define SNTP_GET_SYSTEM_TIME(sec, us)     do { (sec) = 0; (us) = 0; } while(0)
+#endif
+
+/** Default retry timeout (in milliseconds) if the response
+ * received is invalid.
+ * This is doubled with each retry until SNTP_RETRY_TIMEOUT_MAX is reached.
+ */
+#if !defined SNTP_RETRY_TIMEOUT || defined __DOXYGEN__
+#define SNTP_RETRY_TIMEOUT          SNTP_RECV_TIMEOUT
+#endif
+
+/** Maximum retry timeout (in milliseconds). */
+#if !defined SNTP_RETRY_TIMEOUT_MAX || defined __DOXYGEN__
+#define SNTP_RETRY_TIMEOUT_MAX      (SNTP_RETRY_TIMEOUT * 10)
+#endif
+
+/** Increase retry timeout with every retry sent
+ * Default is on to conform to RFC.
+ */
+#if !defined SNTP_RETRY_TIMEOUT_EXP || defined __DOXYGEN__
+#define SNTP_RETRY_TIMEOUT_EXP      1
+#endif
+
+/**
+ * @}
+ */
+
+#endif /* LWIP_HDR_APPS_SNTP_OPTS_H */

+ 105 - 0
thirdparty/lwip/src/include/lwip/apps/tftp_opts.h

@@ -0,0 +1,105 @@
+/****************************************************************//**
+ *
+ * @file tftp_opts.h
+ *
+ * @author   Logan Gunthorpe <logang@deltatee.com>
+ *
+ * @brief    Trivial File Transfer Protocol (RFC 1350) implementation options
+ *
+ * Copyright (c) Deltatee Enterprises Ltd. 2013
+ * All rights reserved.
+ *
+ ********************************************************************/
+
+/* 
+ * Redistribution and use in source and binary forms, with or without
+ * modification,are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Author: Logan Gunthorpe <logang@deltatee.com>
+ *
+ */
+
+#ifndef LWIP_HDR_APPS_TFTP_OPTS_H
+#define LWIP_HDR_APPS_TFTP_OPTS_H
+
+#include "lwip/opt.h"
+
+/**
+ * @defgroup tftp_opts Options
+ * @ingroup tftp
+ * @{
+ */
+
+/**
+ * Enable TFTP debug messages
+ */
+#if !defined TFTP_DEBUG || defined __DOXYGEN__
+#define TFTP_DEBUG            LWIP_DBG_ON
+#endif
+
+/**
+ * TFTP server port
+ */
+#if !defined TFTP_PORT || defined __DOXYGEN__
+#define TFTP_PORT             69
+#endif
+
+/**
+ * TFTP timeout
+ */
+#if !defined TFTP_TIMEOUT_MSECS || defined __DOXYGEN__
+#define TFTP_TIMEOUT_MSECS    10000
+#endif
+
+/**
+ * Max. number of retries when a file is read from server
+ */
+#if !defined TFTP_MAX_RETRIES || defined __DOXYGEN__
+#define TFTP_MAX_RETRIES      5
+#endif
+
+/**
+ * TFTP timer cyclic interval
+ */
+#if !defined TFTP_TIMER_MSECS || defined __DOXYGEN__
+#define TFTP_TIMER_MSECS      50
+#endif
+
+/**
+ * Max. length of TFTP filename
+ */
+#if !defined TFTP_MAX_FILENAME_LEN || defined __DOXYGEN__
+#define TFTP_MAX_FILENAME_LEN 20
+#endif
+
+/**
+ * Max. length of TFTP mode
+ */
+#if !defined TFTP_MAX_MODE_LEN || defined __DOXYGEN__
+#define TFTP_MAX_MODE_LEN     7
+#endif
+
+/**
+ * @}
+ */
+
+#endif /* LWIP_HDR_APPS_TFTP_OPTS_H */

+ 94 - 0
thirdparty/lwip/src/include/lwip/apps/tftp_server.h

@@ -0,0 +1,94 @@
+/****************************************************************//**
+ *
+ * @file tftp_server.h
+ *
+ * @author   Logan Gunthorpe <logang@deltatee.com>
+ *
+ * @brief    Trivial File Transfer Protocol (RFC 1350)
+ *
+ * Copyright (c) Deltatee Enterprises Ltd. 2013
+ * All rights reserved.
+ *
+ ********************************************************************/
+
+/* 
+ * Redistribution and use in source and binary forms, with or without
+ * modification,are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Author: Logan Gunthorpe <logang@deltatee.com>
+ *
+ */
+
+#ifndef LWIP_HDR_APPS_TFTP_SERVER_H
+#define LWIP_HDR_APPS_TFTP_SERVER_H
+
+#include "lwip/apps/tftp_opts.h"
+#include "lwip/err.h"
+#include "lwip/pbuf.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** @ingroup tftp
+ * TFTP context containing callback functions for TFTP transfers
+ */
+struct tftp_context {
+  /**
+   * Open file for read/write.
+   * @param fname Filename
+   * @param mode Mode string from TFTP RFC 1350 (netascii, octet, mail)
+   * @param write Flag indicating read (0) or write (!= 0) access
+   * @returns File handle supplied to other functions
+   */
+  void* (*open)(const char* fname, const char* mode, u8_t write);
+  /**
+   * Close file handle
+   * @param handle File handle returned by open()
+   */
+  void (*close)(void* handle);
+  /**
+   * Read from file 
+   * @param handle File handle returned by open()
+   * @param buf Target buffer to copy read data to
+   * @param bytes Number of bytes to copy to buf
+   * @returns &gt;= 0: Success; &lt; 0: Error
+   */
+  int (*read)(void* handle, void* buf, int bytes);
+  /**
+   * Write to file
+   * @param handle File handle returned by open()
+   * @param pbuf PBUF adjusted such that payload pointer points
+   *             to the beginning of write data. In other words,
+   *             TFTP headers are stripped off.
+   * @returns &gt;= 0: Success; &lt; 0: Error
+   */
+  int (*write)(void* handle, struct pbuf* p);
+};
+
+err_t tftp_init(const struct tftp_context* ctx);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_APPS_TFTP_SERVER_H */

+ 319 - 217
thirdparty/lwip/src/include/lwip/arch.h

@@ -1,217 +1,319 @@
-/*
- * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
- * All rights reserved. 
- * 
- * Redistribution and use in source and binary forms, with or without modification, 
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission. 
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
- * OF SUCH DAMAGE.
- *
- * This file is part of the lwIP TCP/IP stack.
- * 
- * Author: Adam Dunkels <adam@sics.se>
- *
- */
-#ifndef __LWIP_ARCH_H__
-#define __LWIP_ARCH_H__
-
-#ifndef LITTLE_ENDIAN
-#define LITTLE_ENDIAN 1234
-#endif
-
-#ifndef BIG_ENDIAN
-#define BIG_ENDIAN 4321
-#endif
-
-#include "arch/cc.h"
-
-/** Temporary: define format string for size_t if not defined in cc.h */
-#ifndef SZT_F
-#define SZT_F U32_F
-#endif /* SZT_F */
-/** Temporary upgrade helper: define format string for u8_t as hex if not
-    defined in cc.h */
-#ifndef X8_F
-#define X8_F  "02x"
-#endif /* X8_F */
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifndef PACK_STRUCT_BEGIN
-#define PACK_STRUCT_BEGIN
-#endif /* PACK_STRUCT_BEGIN */
-
-#ifndef PACK_STRUCT_END
-#define PACK_STRUCT_END
-#endif /* PACK_STRUCT_END */
-
-#ifndef PACK_STRUCT_FIELD
-#define PACK_STRUCT_FIELD(x) x
-#endif /* PACK_STRUCT_FIELD */
-
-
-#ifndef LWIP_UNUSED_ARG
-#define LWIP_UNUSED_ARG(x) (void)x
-#endif /* LWIP_UNUSED_ARG */ 
-
-
-#ifdef LWIP_PROVIDE_ERRNO
-
-#define  EPERM         1  /* Operation not permitted */
-#define  ENOENT        2  /* No such file or directory */
-#define  ESRCH         3  /* No such process */
-#define  EINTR         4  /* Interrupted system call */
-#define  EIO           5  /* I/O error */
-#define  ENXIO         6  /* No such device or address */
-#define  E2BIG         7  /* Arg list too long */
-#define  ENOEXEC       8  /* Exec format error */
-#define  EBADF         9  /* Bad file number */
-#define  ECHILD       10  /* No child processes */
-#define  EAGAIN       11  /* Try again */
-#define  ENOMEM       12  /* Out of memory */
-#define  EACCES       13  /* Permission denied */
-#define  EFAULT       14  /* Bad address */
-#define  ENOTBLK      15  /* Block device required */
-#define  EBUSY        16  /* Device or resource busy */
-#define  EEXIST       17  /* File exists */
-#define  EXDEV        18  /* Cross-device link */
-#define  ENODEV       19  /* No such device */
-#define  ENOTDIR      20  /* Not a directory */
-#define  EISDIR       21  /* Is a directory */
-#define  EINVAL       22  /* Invalid argument */
-#define  ENFILE       23  /* File table overflow */
-#define  EMFILE       24  /* Too many open files */
-#define  ENOTTY       25  /* Not a typewriter */
-#define  ETXTBSY      26  /* Text file busy */
-#define  EFBIG        27  /* File too large */
-#define  ENOSPC       28  /* No space left on device */
-#define  ESPIPE       29  /* Illegal seek */
-#define  EROFS        30  /* Read-only file system */
-#define  EMLINK       31  /* Too many links */
-#define  EPIPE        32  /* Broken pipe */
-#define  EDOM         33  /* Math argument out of domain of func */
-#define  ERANGE       34  /* Math result not representable */
-#define  EDEADLK      35  /* Resource deadlock would occur */
-#define  ENAMETOOLONG 36  /* File name too long */
-#define  ENOLCK       37  /* No record locks available */
-#define  ENOSYS       38  /* Function not implemented */
-#define  ENOTEMPTY    39  /* Directory not empty */
-#define  ELOOP        40  /* Too many symbolic links encountered */
-#define  EWOULDBLOCK  EAGAIN  /* Operation would block */
-#define  ENOMSG       42  /* No message of desired type */
-#define  EIDRM        43  /* Identifier removed */
-#define  ECHRNG       44  /* Channel number out of range */
-#define  EL2NSYNC     45  /* Level 2 not synchronized */
-#define  EL3HLT       46  /* Level 3 halted */
-#define  EL3RST       47  /* Level 3 reset */
-#define  ELNRNG       48  /* Link number out of range */
-#define  EUNATCH      49  /* Protocol driver not attached */
-#define  ENOCSI       50  /* No CSI structure available */
-#define  EL2HLT       51  /* Level 2 halted */
-#define  EBADE        52  /* Invalid exchange */
-#define  EBADR        53  /* Invalid request descriptor */
-#define  EXFULL       54  /* Exchange full */
-#define  ENOANO       55  /* No anode */
-#define  EBADRQC      56  /* Invalid request code */
-#define  EBADSLT      57  /* Invalid slot */
-
-#define  EDEADLOCK    EDEADLK
-
-#define  EBFONT       59  /* Bad font file format */
-#define  ENOSTR       60  /* Device not a stream */
-#define  ENODATA      61  /* No data available */
-#define  ETIME        62  /* Timer expired */
-#define  ENOSR        63  /* Out of streams resources */
-#define  ENONET       64  /* Machine is not on the network */
-#define  ENOPKG       65  /* Package not installed */
-#define  EREMOTE      66  /* Object is remote */
-#define  ENOLINK      67  /* Link has been severed */
-#define  EADV         68  /* Advertise error */
-#define  ESRMNT       69  /* Srmount error */
-#define  ECOMM        70  /* Communication error on send */
-#define  EPROTO       71  /* Protocol error */
-#define  EMULTIHOP    72  /* Multihop attempted */
-#define  EDOTDOT      73  /* RFS specific error */
-#define  EBADMSG      74  /* Not a data message */
-#define  EOVERFLOW    75  /* Value too large for defined data type */
-#define  ENOTUNIQ     76  /* Name not unique on network */
-#define  EBADFD       77  /* File descriptor in bad state */
-#define  EREMCHG      78  /* Remote address changed */
-#define  ELIBACC      79  /* Can not access a needed shared library */
-#define  ELIBBAD      80  /* Accessing a corrupted shared library */
-#define  ELIBSCN      81  /* .lib section in a.out corrupted */
-#define  ELIBMAX      82  /* Attempting to link in too many shared libraries */
-#define  ELIBEXEC     83  /* Cannot exec a shared library directly */
-#define  EILSEQ       84  /* Illegal byte sequence */
-#define  ERESTART     85  /* Interrupted system call should be restarted */
-#define  ESTRPIPE     86  /* Streams pipe error */
-#define  EUSERS       87  /* Too many users */
-#define  ENOTSOCK     88  /* Socket operation on non-socket */
-#define  EDESTADDRREQ 89  /* Destination address required */
-#define  EMSGSIZE     90  /* Message too long */
-#define  EPROTOTYPE   91  /* Protocol wrong type for socket */
-#define  ENOPROTOOPT  92  /* Protocol not available */
-#define  EPROTONOSUPPORT 93  /* Protocol not supported */
-#define  ESOCKTNOSUPPORT 94  /* Socket type not supported */
-#define  EOPNOTSUPP      95  /* Operation not supported on transport endpoint */
-#define  EPFNOSUPPORT    96  /* Protocol family not supported */
-#define  EAFNOSUPPORT    97  /* Address family not supported by protocol */
-#define  EADDRINUSE      98  /* Address already in use */
-#define  EADDRNOTAVAIL   99  /* Cannot assign requested address */
-#define  ENETDOWN       100  /* Network is down */
-#define  ENETUNREACH    101  /* Network is unreachable */
-#define  ENETRESET      102  /* Network dropped connection because of reset */
-#define  ECONNABORTED   103  /* Software caused connection abort */
-#define  ECONNRESET     104  /* Connection reset by peer */
-#define  ENOBUFS        105  /* No buffer space available */
-#define  EISCONN        106  /* Transport endpoint is already connected */
-#define  ENOTCONN       107  /* Transport endpoint is not connected */
-#define  ESHUTDOWN      108  /* Cannot send after transport endpoint shutdown */
-#define  ETOOMANYREFS   109  /* Too many references: cannot splice */
-#define  ETIMEDOUT      110  /* Connection timed out */
-#define  ECONNREFUSED   111  /* Connection refused */
-#define  EHOSTDOWN      112  /* Host is down */
-#define  EHOSTUNREACH   113  /* No route to host */
-#define  EALREADY       114  /* Operation already in progress */
-#define  EINPROGRESS    115  /* Operation now in progress */
-#define  ESTALE         116  /* Stale NFS file handle */
-#define  EUCLEAN        117  /* Structure needs cleaning */
-#define  ENOTNAM        118  /* Not a XENIX named type file */
-#define  ENAVAIL        119  /* No XENIX semaphores available */
-#define  EISNAM         120  /* Is a named type file */
-#define  EREMOTEIO      121  /* Remote I/O error */
-#define  EDQUOT         122  /* Quota exceeded */
-
-#define  ENOMEDIUM      123  /* No medium found */
-#define  EMEDIUMTYPE    124  /* Wrong medium type */
-
-#ifndef errno
-extern int errno;
-#endif
-
-#endif /* LWIP_PROVIDE_ERRNO */
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* __LWIP_ARCH_H__ */
+/**
+ * @file
+ * Support for different processor and compiler architectures
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef LWIP_HDR_ARCH_H
+#define LWIP_HDR_ARCH_H
+
+#ifndef LITTLE_ENDIAN
+#define LITTLE_ENDIAN 1234
+#endif
+
+#ifndef BIG_ENDIAN
+#define BIG_ENDIAN 4321
+#endif
+
+#include "arch/cc.h"
+
+/**
+ * @defgroup compiler_abstraction Compiler/platform abstraction
+ * @ingroup sys_layer
+ * All defines related to this section must not be placed in lwipopts.h,
+ * but in arch/cc.h!
+ * These options cannot be \#defined in lwipopts.h since they are not options
+ * of lwIP itself, but options of the lwIP port to your system.
+ * @{
+ */
+
+/** Define the byte order of the system.
+ * Needed for conversion of network data to host byte order.
+ * Allowed values: LITTLE_ENDIAN and BIG_ENDIAN
+ */
+#ifndef BYTE_ORDER
+#define BYTE_ORDER LITTLE_ENDIAN
+#endif
+
+/** Define random number generator function of your system */
+#ifdef __DOXYGEN__
+#define LWIP_RAND() ((u32_t)rand())
+#endif
+
+/** Platform specific diagnostic output.\n
+ * Note the default implementation pulls in printf, which may
+ * in turn pull in a lot of standard libary code. In resource-constrained 
+ * systems, this should be defined to something less resource-consuming.
+ */
+#ifndef LWIP_PLATFORM_DIAG
+#define LWIP_PLATFORM_DIAG(x) do {printf x; printf("\r\n");} while(0)
+#include <stdio.h>
+#include <stdlib.h>
+#endif
+
+/** Platform specific assertion handling.\n
+ * Note the default implementation pulls in printf, fflush and abort, which may
+ * in turn pull in a lot of standard libary code. In resource-constrained 
+ * systems, this should be defined to something less resource-consuming.
+ */
+#ifndef LWIP_PLATFORM_ASSERT
+#define LWIP_PLATFORM_ASSERT(x) do {printf("Assertion \"%s\" failed at line %d in %s\n", \
+                                     x, __LINE__, __FILE__); fflush(NULL); abort();} while(0)
+#include <stdio.h>
+#include <stdlib.h>
+#endif
+
+/** Define this to 1 in arch/cc.h of your port if you do not want to
+ * include stddef.h header to get size_t. You need to typedef size_t
+ * by yourself in this case.
+ */
+#ifndef LWIP_NO_STDDEF_H
+#define LWIP_NO_STDDEF_H 0
+#endif
+
+#if !LWIP_NO_STDDEF_H
+#include <stddef.h> /* for size_t */
+#endif
+
+/** Define this to 1 in arch/cc.h of your port if your compiler does not provide
+ * the stdint.h header. You need to typedef the generic types listed in
+ * lwip/arch.h yourself in this case (u8_t, u16_t...).
+ */
+#ifndef LWIP_NO_STDINT_H
+#define LWIP_NO_STDINT_H 0
+#endif
+
+/* Define generic types used in lwIP */
+#if !LWIP_NO_STDINT_H
+#include <stdint.h>
+typedef uint8_t   u8_t;
+typedef int8_t    s8_t;
+typedef uint16_t  u16_t;
+typedef int16_t   s16_t;
+typedef uint32_t  u32_t;
+typedef int32_t   s32_t;
+typedef uintptr_t mem_ptr_t;
+#endif
+
+/** Define this to 1 in arch/cc.h of your port if your compiler does not provide
+ * the inttypes.h header. You need to define the format strings listed in
+ * lwip/arch.h yourself in this case (X8_F, U16_F...).
+ */
+#ifndef LWIP_NO_INTTYPES_H
+#define LWIP_NO_INTTYPES_H 0
+#endif
+
+/* Define (sn)printf formatters for these lwIP types */
+#if !LWIP_NO_INTTYPES_H
+#include <inttypes.h>
+#ifndef X8_F
+#define X8_F  "02" PRIx8
+#endif
+#ifndef U16_F
+#define U16_F PRIu16
+#endif
+#ifndef S16_F
+#define S16_F PRId16
+#endif
+#ifndef X16_F
+#define X16_F PRIx16
+#endif
+#ifndef U32_F
+#define U32_F PRIu32
+#endif
+#ifndef S32_F
+#define S32_F PRId32
+#endif
+#ifndef X32_F
+#define X32_F PRIx32
+#endif
+#ifndef SZT_F
+#define SZT_F PRIuPTR
+#endif
+#endif
+
+/** Define this to 1 in arch/cc.h of your port if your compiler does not provide
+ * the limits.h header. You need to define the type limits yourself in this case
+ * (e.g. INT_MAX).
+ */
+#ifndef LWIP_NO_LIMITS_H
+#define LWIP_NO_LIMITS_H 0
+#endif
+
+/* Include limits.h? */
+#if !LWIP_NO_LIMITS_H
+#include <limits.h>
+#endif
+
+/** C++ const_cast<target_type>(val) equivalent to remove constness from a value (GCC -Wcast-qual) */
+#ifndef LWIP_CONST_CAST
+#define LWIP_CONST_CAST(target_type, val) ((target_type)((ptrdiff_t)val))
+#endif
+
+/** Get rid of alignment cast warnings (GCC -Wcast-align) */
+#ifndef LWIP_ALIGNMENT_CAST
+#define LWIP_ALIGNMENT_CAST(target_type, val) LWIP_CONST_CAST(target_type, val)
+#endif
+
+/** Get rid of warnings related to pointer-to-numeric and vice-versa casts,
+ * e.g. "conversion from 'u8_t' to 'void *' of greater size"
+ */
+#ifndef LWIP_PTR_NUMERIC_CAST
+#define LWIP_PTR_NUMERIC_CAST(target_type, val) LWIP_CONST_CAST(target_type, val)
+#endif
+
+/** Allocates a memory buffer of specified size that is of sufficient size to align
+ * its start address using LWIP_MEM_ALIGN.
+ * You can declare your own version here e.g. to enforce alignment without adding
+ * trailing padding bytes (see LWIP_MEM_ALIGN_BUFFER) or your own section placement
+ * requirements.\n
+ * e.g. if you use gcc and need 32 bit alignment:\n
+ * \#define LWIP_DECLARE_MEMORY_ALIGNED(variable_name, size) u8_t variable_name[size] \_\_attribute\_\_((aligned(4)))\n
+ * or more portable:\n
+ * \#define LWIP_DECLARE_MEMORY_ALIGNED(variable_name, size) u32_t variable_name[(size + sizeof(u32_t) - 1) / sizeof(u32_t)]
+ */
+#ifndef LWIP_DECLARE_MEMORY_ALIGNED
+#define LWIP_DECLARE_MEMORY_ALIGNED(variable_name, size) u8_t variable_name[LWIP_MEM_ALIGN_BUFFER(size)]
+#endif
+
+/** Calculate memory size for an aligned buffer - returns the next highest
+ * multiple of MEM_ALIGNMENT (e.g. LWIP_MEM_ALIGN_SIZE(3) and
+ * LWIP_MEM_ALIGN_SIZE(4) will both yield 4 for MEM_ALIGNMENT == 4).
+ */
+#ifndef LWIP_MEM_ALIGN_SIZE
+#define LWIP_MEM_ALIGN_SIZE(size) (((size) + MEM_ALIGNMENT - 1U) & ~(MEM_ALIGNMENT-1U))
+#endif
+
+/** Calculate safe memory size for an aligned buffer when using an unaligned
+ * type as storage. This includes a safety-margin on (MEM_ALIGNMENT - 1) at the
+ * start (e.g. if buffer is u8_t[] and actual data will be u32_t*)
+ */
+#ifndef LWIP_MEM_ALIGN_BUFFER
+#define LWIP_MEM_ALIGN_BUFFER(size) (((size) + MEM_ALIGNMENT - 1U))
+#endif
+
+/** Align a memory pointer to the alignment defined by MEM_ALIGNMENT
+ * so that ADDR % MEM_ALIGNMENT == 0
+ */
+#ifndef LWIP_MEM_ALIGN
+#define LWIP_MEM_ALIGN(addr) ((void *)(((mem_ptr_t)(addr) + MEM_ALIGNMENT - 1) & ~(mem_ptr_t)(MEM_ALIGNMENT-1)))
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Packed structs support.
+  * Placed BEFORE declaration of a packed struct.\n
+  * For examples of packed struct declarations, see include/lwip/prot/ subfolder.\n
+  * A port to GCC/clang is included in lwIP, if you use these compilers there is nothing to do here.
+  */
+#ifndef PACK_STRUCT_BEGIN
+#define PACK_STRUCT_BEGIN
+#endif /* PACK_STRUCT_BEGIN */
+
+/** Packed structs support.
+  * Placed AFTER declaration of a packed struct.\n
+  * For examples of packed struct declarations, see include/lwip/prot/ subfolder.\n
+  * A port to GCC/clang is included in lwIP, if you use these compilers there is nothing to do here.
+  */
+#ifndef PACK_STRUCT_END
+#define PACK_STRUCT_END
+#endif /* PACK_STRUCT_END */
+
+/** Packed structs support.
+  * Placed between end of declaration of a packed struct and trailing semicolon.\n
+  * For examples of packed struct declarations, see include/lwip/prot/ subfolder.\n
+  * A port to GCC/clang is included in lwIP, if you use these compilers there is nothing to do here.
+  */
+#ifndef PACK_STRUCT_STRUCT
+#if defined(__GNUC__) || defined(__clang__)
+#define PACK_STRUCT_STRUCT __attribute__((packed))
+#else
+#define PACK_STRUCT_STRUCT
+#endif
+#endif /* PACK_STRUCT_STRUCT */
+
+/** Packed structs support.
+  * Wraps u32_t and u16_t members.\n
+  * For examples of packed struct declarations, see include/lwip/prot/ subfolder.\n
+  * A port to GCC/clang is included in lwIP, if you use these compilers there is nothing to do here.
+  */
+#ifndef PACK_STRUCT_FIELD
+#define PACK_STRUCT_FIELD(x) x
+#endif /* PACK_STRUCT_FIELD */
+
+/** Packed structs support.
+  * Wraps u8_t members, where some compilers warn that packing is not necessary.\n
+  * For examples of packed struct declarations, see include/lwip/prot/ subfolder.\n
+  * A port to GCC/clang is included in lwIP, if you use these compilers there is nothing to do here.
+  */
+#ifndef PACK_STRUCT_FLD_8
+#define PACK_STRUCT_FLD_8(x) PACK_STRUCT_FIELD(x)
+#endif /* PACK_STRUCT_FLD_8 */
+
+/** Packed structs support.
+  * Wraps members that are packed structs themselves, where some compilers warn that packing is not necessary.\n
+  * For examples of packed struct declarations, see include/lwip/prot/ subfolder.\n
+  * A port to GCC/clang is included in lwIP, if you use these compilers there is nothing to do here.
+  */
+#ifndef PACK_STRUCT_FLD_S
+#define PACK_STRUCT_FLD_S(x) PACK_STRUCT_FIELD(x)
+#endif /* PACK_STRUCT_FLD_S */
+
+/** Packed structs support using \#include files before and after struct to be packed.\n
+ * The file included BEFORE the struct is "arch/bpstruct.h".\n
+ * The file included AFTER the struct is "arch/epstruct.h".\n
+ * This can be used to implement struct packing on MS Visual C compilers, see
+ * the Win32 port in the lwIP contrib repository for reference.
+ * For examples of packed struct declarations, see include/lwip/prot/ subfolder.\n
+ * A port to GCC/clang is included in lwIP, if you use these compilers there is nothing to do here.
+ */
+#ifdef __DOXYGEN__
+#define PACK_STRUCT_USE_INCLUDES
+#endif
+
+/** Eliminates compiler warning about unused arguments (GCC -Wextra -Wunused). */
+#ifndef LWIP_UNUSED_ARG
+#define LWIP_UNUSED_ARG(x) (void)x
+#endif /* LWIP_UNUSED_ARG */
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_ARCH_H */

Some files were not shown because too many files changed in this diff