This is the mail archive of the ecos-patches@sourceware.org mailing list for the eCos project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

lwIP DHCP support


Hello,

the following patch adds DHCP support to the eCos port of lwIP stack and updates some files to the actual version of the lwIP CVS.


Uwe Kindler Software Engineering

--

cetoni GmbH
Am Wiesenring 6
D-07554 Korbussen

Tel.: +49 (0) 36602 338 28
Fax:  +49 (0) 36602 338 11
uwe.kindler@cetoni.de
www.cetoni.de
Only in ecos_web_cvs/ecos/packages/io/eth/current: CVS
diff -ru ecos_web_cvs/ecos/packages/io/eth/current/ChangeLog ecos/ecos/packages/io/eth/current/ChangeLog
--- ecos_web_cvs/ecos/packages/io/eth/current/ChangeLog	2006-01-18 16:04:38.000000000 +0100
+++ ecos/ecos/packages/io/eth/current/ChangeLog	2006-03-13 11:27:47.000000000 +0100
@@ -1,3 +1,8 @@
+2006-03-13  Uwe Kindler  <uwe_kindler@web.de>
+
+	* src/lwip/eth_drv.c Add call to lwip_dhcp_init() for
+	start of DHCP client
+
 2006-01-18  Gary Thomas  <gary@mlbassoc.com>
 
 	* src/net/eth_drv.c (eth_drv_send): Better check for overflow
Only in ecos_web_cvs/ecos/packages/io/eth/current/cdl: CVS
Only in ecos_web_cvs/ecos/packages/io/eth/current/doc: CVS
Only in ecos/ecos/packages/io/eth/current/doc: driver_doc
Only in ecos_web_cvs/ecos/packages/io/eth/current/include: CVS
Only in ecos_web_cvs/ecos/packages/io/eth/current/src: CVS
Only in ecos_web_cvs/ecos/packages/io/eth/current/src/lwip: CVS
diff -ru ecos_web_cvs/ecos/packages/io/eth/current/src/lwip/eth_drv.c ecos/ecos/packages/io/eth/current/src/lwip/eth_drv.c
--- ecos_web_cvs/ecos/packages/io/eth/current/src/lwip/eth_drv.c	2004-01-17 15:14:42.000000000 +0100
+++ ecos/ecos/packages/io/eth/current/src/lwip/eth_drv.c	2006-03-12 20:16:12.000000000 +0100
@@ -88,6 +88,7 @@
 
 extern void lwip_dsr_stuff(void);
 extern void lwip_set_addr(struct netif *);
+extern void lwip_dhcp_init(struct netif *);
 
 //DSR called from the low level driver.Signals the input_thread
 void
@@ -126,7 +127,10 @@
         }
     }
 #endif
-
+    //
+    // we call this after the driver was started successfully
+    //
+    lwip_dhcp_init(netif);
 }
 
 //
Only in ecos_web_cvs/ecos/packages/io/eth/current/src/net: CVS
Only in ecos_web_cvs/ecos/packages/io/eth/current/src/stand_alone: CVS
Only in ecos_web_cvs/ecos/packages/net/lwip_tcpip: CVS
Only in ecos_web_cvs/ecos/packages/net/lwip_tcpip/current: CVS
diff -ru ecos_web_cvs/ecos/packages/net/lwip_tcpip/current/ChangeLog ecos/ecos/packages/net/lwip_tcpip/current/ChangeLog
--- ecos_web_cvs/ecos/packages/net/lwip_tcpip/current/ChangeLog	2006-03-02 19:28:12.000000000 +0100
+++ ecos/ecos/packages/net/lwip_tcpip/current/ChangeLog	2006-03-13 11:19:03.000000000 +0100
@@ -1,3 +1,26 @@
+2006-03-13  Uwe Kindler <uwe_kindler@web.de>
+
+	* cdl/lwip_net.cdl Changed default value of 
+	CYGNUM_LWIP_MEMP_NUM_SYS_TIMEOUT (required for DHCP). 
+	Replaced CYGPKG_LWIP_DHCP_OPTIONS with CYGPKG_LWIP_DHCP 
+	and add CYGOPT_LWIP_DHCP_MANAGEMENT for automatic DHCP
+	management.
+	* include/lwip/ip_addr.h Add macro ip_addr_netcmp() 
+	(copied from lwIP CVS).
+	* include/netif/etharp.h Updated file to version of
+	lwIP CVS.
+	* src/ecos/init.c Add include <pkgconf/net_lwip.h>
+	Add lwip_dhcp_fine_tmr() and lwip_dhcp_coarse_tmr()
+	for DHCP processing.
+	Change tcpip_init_done() to start DHCP timers and
+	ARP timer.
+	Changed lwip_set_addr() to properly setup IP adress
+	if DHCP is used.
+	Added lwip_dhcp_init() for start of DHCP client.
+	Removed start of ARP timer from ecosclue_init().
+	* src/netif/etharp.c Updated file to version of
+	lwIP CVS.
+	
 2006-03-02  Andrew Lunn  <andrew.lunn@ascom.ch>
 
 	* cdl/lwip_net.cdl: Add the interfaces CYGPKG_NET_STACK,
Only in ecos_web_cvs/ecos/packages/net/lwip_tcpip/current/cdl: CVS
diff -ru ecos_web_cvs/ecos/packages/net/lwip_tcpip/current/cdl/lwip_net.cdl ecos/ecos/packages/net/lwip_tcpip/current/cdl/lwip_net.cdl
--- ecos_web_cvs/ecos/packages/net/lwip_tcpip/current/cdl/lwip_net.cdl	2006-03-02 19:28:12.000000000 +0100
+++ ecos/ecos/packages/net/lwip_tcpip/current/cdl/lwip_net.cdl	2006-03-13 10:58:51.000000000 +0100
@@ -169,7 +169,7 @@
 		no_define
 		description   	"
 			Tunables for various aspects of memory usage throughout the stack."
-			
+				
 		cdl_option CYGNUM_LWIP_MEM_ALIGNMENT {
 			display		"Memory alignment"
 			flavor		data
@@ -185,7 +185,7 @@
 			default_value 	4000
 			description   	"
 				MEM_SIZE: the size of the heap memory. If the application will send
-				a lot of data that needs to be copied, this should be set high."
+				a lot of data that needs to be copied, this should be set high."		
 		}		 
 		
 		cdl_option CYGNUM_LWIP_MEMP_NUM_PBUF {
@@ -237,7 +237,7 @@
 		cdl_option CYGNUM_LWIP_MEMP_NUM_SYS_TIMEOUT {
 			display		"Simultaneous active timeouts"
 			flavor		data
-			default_value 	3
+			default_value 	CYGPKG_LWIP_DHCP ? 6 : 4
 			description   	"
 				MEMP_NUM_SYS_TIMEOUT: the number of simulateously active
 				timeouts."
@@ -249,7 +249,7 @@
 			no_define
 			description	"
 				The following four are used only with the sequential API and can be
-  			        set to 0 if the application only will use the raw API."
+  			    set to 0 if the application only will use the raw API."
 		
 
 			cdl_option CYGNUM_LWIP_MEMP_NUM_NETBUF {
@@ -315,7 +315,7 @@
 			default_value 	1024
 			description   	"
 			PBUF_POOL_BUFSIZE: the size of each pbuf in the pbuf pool."
-		}		 
+		}		
 		 
 		cdl_option CYGNUM_LWIP_PBUF_LINK_HLEN {
 			display		"Allocation for a link level header"
@@ -332,7 +332,7 @@
 		flavor 		none
 		no_define
 		description   	"
-		Tune the TCP protocol details"
+		           Tune the TCP protocol details"
 			
 		cdl_option CYGFUN_LWIP_TCP {
 			display		"Activate TCP"
@@ -372,7 +372,7 @@
 			default_value 	2048
 			description   	"
 				TCP Maximum segment size."
-		}		 
+		}	
 			 
 		cdl_option CYGNUM_LWIP_TCP_SND_BUF {
 			display		"Sender buffer space"
@@ -486,27 +486,32 @@
 		}
 	}
 
-	cdl_component CYGPKG_LWIP_DHCP_OPTIONS {
-		display		"DHCP"
-		flavor		none
-		no_define
-
-
-		cdl_option CYGFUN_LWIP_DHCP {
-			display		"Activate DHCP"
-			flavor		bool
-			default_value 	0
-			description   	"
-				Enable this option if you want DHCP configuration of
-				interfaces."
-			compile core/dhcp.c
-		}
-
-
+	cdl_component CYGPKG_LWIP_DHCP {
+		display		  "Use DHCP"
+		flavor		  bool
+		default_value 0
+		requires  { CYGNUM_LWIP_MEMP_NUM_SYS_TIMEOUT >= 6 }
+		description "
+		    Provide DHCP for initializing the IP address of network interfaces."
+		compile core/dhcp.c
+
+        cdl_option CYGOPT_LWIP_DHCP_MANAGEMENT {
+            display    "DHCP management"
+            flavor     bool
+            default_value 1
+            description "
+                If enabled then the lwIP stack automatically calls dhcp_start(), 
+                dhcp_fine_tmr() and dhcp_coarse_tmr(). The DHCP stuff is handled 
+                in the TCP/IP thread. If this causes trouble on high traffic loads 
+                or if the application need to be aware of the DHCP state then it 
+                is better to disable this option. In this case managing the DHCP 
+                state in an application aware thread is recommended."        
+        }
+        
 		cdl_option CYGOPT_LWIP_DHCP_DOES_ARP_CHECK {
 			display		"Check offered address"
 			flavor		bool
-			default_value 	0
+			default_value 	1
 			description   	"
 				Enable this option if you want to do an ARP check on the offered address
 				(recommended)."
@@ -557,7 +562,7 @@
 			flavor 	data
 			default_value {"\"/dev/ser0\""}
 			description "
-			Which serial port to use SLIP on."
+			        Which serial port to use SLIP on."
 		}
 	}
 	
Only in ecos_web_cvs/ecos/packages/net/lwip_tcpip/current/include: CVS
Only in ecos_web_cvs/ecos/packages/net/lwip_tcpip/current/include/arch: CVS
Only in ecos_web_cvs/ecos/packages/net/lwip_tcpip/current/include/lwip: CVS
diff -ru ecos_web_cvs/ecos/packages/net/lwip_tcpip/current/include/lwip/ip_addr.h ecos/ecos/packages/net/lwip_tcpip/current/include/lwip/ip_addr.h
--- ecos_web_cvs/ecos/packages/net/lwip_tcpip/current/include/lwip/ip_addr.h	2004-05-04 14:32:32.000000000 +0200
+++ ecos/ecos/packages/net/lwip_tcpip/current/include/lwip/ip_addr.h	2006-03-13 01:49:46.000000000 +0100
@@ -142,6 +142,20 @@
 #define ip4_addr2(ipaddr) ((unsigned int)(ntohl((ipaddr)->addr) >> 16) & 0xff)
 #define ip4_addr3(ipaddr) ((unsigned int)(ntohl((ipaddr)->addr) >> 8) & 0xff)
 #define ip4_addr4(ipaddr) ((unsigned int)(ntohl((ipaddr)->addr)) & 0xff)
+
+/**
+ * Determine if two address are on the same network.
+ *
+ * @arg addr1 IP address 1
+ * @arg addr2 IP address 2
+ * @arg mask network identifier mask
+ * @return !0 if the network identifiers of both address match
+ */
+#define ip_addr_netcmp(addr1, addr2, mask) (((addr1)->addr & \
+                                              (mask)->addr) == \
+                                             ((addr2)->addr & \
+                                              (mask)->addr))
+                                              
 #endif /* __LWIP_IP_ADDR_H__ */
 
 
diff -ru ecos_web_cvs/ecos/packages/net/lwip_tcpip/current/include/lwipopts.h ecos/ecos/packages/net/lwip_tcpip/current/include/lwipopts.h
--- ecos_web_cvs/ecos/packages/net/lwip_tcpip/current/include/lwipopts.h	2006-03-02 19:28:12.000000000 +0100
+++ ecos/ecos/packages/net/lwip_tcpip/current/include/lwipopts.h	2006-03-13 10:07:14.000000000 +0100
@@ -158,7 +158,7 @@
 /* Define LWIP_DHCP to 1 if you want DHCP configuration of
    interfaces.*/
 
-#ifdef CYGFUN_LWIP_DHCP
+#ifdef CYGPKG_LWIP_DHCP
   #define LWIP_DHCP               1
 
 /* 1 if you want to do an ARP check on the offered address
@@ -202,7 +202,7 @@
 #else
 #define LWIP_HAVE_LOOPIF	    0
 #endif
-/* ---------- PPP options --------- */  
+/* ---------- PPP options --------- */ 
 #ifdef CYGPKG_LWIP_PPP 
 #define PPP_SUPPORT             1
 #else
Only in ecos_web_cvs/ecos/packages/net/lwip_tcpip/current/include/netif: CVS
diff -ru ecos_web_cvs/ecos/packages/net/lwip_tcpip/current/include/netif/etharp.h ecos/ecos/packages/net/lwip_tcpip/current/include/netif/etharp.h
--- ecos_web_cvs/ecos/packages/net/lwip_tcpip/current/include/netif/etharp.h	2004-05-04 14:32:33.000000000 +0200
+++ ecos/ecos/packages/net/lwip_tcpip/current/include/netif/etharp.h	2004-11-28 19:00:20.000000000 +0100
@@ -35,8 +35,8 @@
 #ifndef __NETIF_ETHARP_H__
 #define __NETIF_ETHARP_H__
 
-#ifndef PAD_ETH_SIZE
-#define PAD_ETH_SIZE 0
+#ifndef ETH_PAD_SIZE
+#define ETH_PAD_SIZE 0
 #endif
 
 #include "lwip/pbuf.h"
@@ -52,18 +52,30 @@
   PACK_STRUCT_FIELD(u8_t addr[6]);
 } PACK_STRUCT_STRUCT;
 PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
 
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
 PACK_STRUCT_BEGIN
 struct eth_hdr {
-#if PAD_ETH_SIZE
-  PACK_STRUCT_FIELD(u8_t padding[PAD_ETH_SIZE]);
+#if ETH_PAD_SIZE
+  PACK_STRUCT_FIELD(u8_t padding[ETH_PAD_SIZE]);
 #endif
   PACK_STRUCT_FIELD(struct eth_addr dest);
   PACK_STRUCT_FIELD(struct eth_addr src);
   PACK_STRUCT_FIELD(u16_t type);
 } PACK_STRUCT_STRUCT;
 PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
 
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
 PACK_STRUCT_BEGIN
 /** the ARP message */
 struct etharp_hdr {
@@ -78,30 +90,37 @@
   PACK_STRUCT_FIELD(struct ip_addr2 dipaddr);
 } PACK_STRUCT_STRUCT;
 PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
 
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
 PACK_STRUCT_BEGIN
 struct ethip_hdr {
   PACK_STRUCT_FIELD(struct eth_hdr eth);
   PACK_STRUCT_FIELD(struct ip_hdr ip);
-};
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
 #ifdef PACK_STRUCT_USE_INCLUDES
 #  include "arch/epstruct.h"
 #endif
 
-#define ARP_TMR_INTERVAL 10000
+/** 5 seconds period */
+#define ARP_TMR_INTERVAL 5000
 
 #define ETHTYPE_ARP 0x0806
 #define ETHTYPE_IP  0x0800
 
 void etharp_init(void);
 void etharp_tmr(void);
-struct pbuf *etharp_ip_input(struct netif *netif, struct pbuf *p);
-struct pbuf *etharp_arp_input(struct netif *netif, struct eth_addr *ethaddr,
+void etharp_ip_input(struct netif *netif, struct pbuf *p);
+void etharp_arp_input(struct netif *netif, struct eth_addr *ethaddr,
          struct pbuf *p);
-struct pbuf *etharp_output(struct netif *netif, struct ip_addr *ipaddr,
+err_t etharp_output(struct netif *netif, struct ip_addr *ipaddr,
          struct pbuf *q);
 err_t etharp_query(struct netif *netif, struct ip_addr *ipaddr, struct pbuf *q);
-
-
+err_t etharp_request(struct netif *netif, struct ip_addr *ipaddr);
 
 #endif /* __NETIF_ARP_H__ */
Only in ecos_web_cvs/ecos/packages/net/lwip_tcpip/current/src: CVS
Only in ecos_web_cvs/ecos/packages/net/lwip_tcpip/current/src/api: CVS
diff -ru ecos_web_cvs/ecos/packages/net/lwip_tcpip/current/src/api/sockets.c ecos/ecos/packages/net/lwip_tcpip/current/src/api/sockets.c
--- ecos_web_cvs/ecos/packages/net/lwip_tcpip/current/src/api/sockets.c	2006-03-02 19:28:12.000000000 +0100
+++ ecos/ecos/packages/net/lwip_tcpip/current/src/api/sockets.c	2006-02-17 23:37:53.000000000 +0100
@@ -499,7 +499,7 @@
     /* deallocated the buffer */
     netbuf_delete(buf);
   }
-    break;
+  break;
 #endif
 #if LWIP_TCP
   case NETCONN_TCP:
Only in ecos_web_cvs/ecos/packages/net/lwip_tcpip/current/src/core: CVS
Only in ecos_web_cvs/ecos/packages/net/lwip_tcpip/current/src/core/ipv4: CVS
Only in ecos_web_cvs/ecos/packages/net/lwip_tcpip/current/src/ecos: CVS
diff -ru ecos_web_cvs/ecos/packages/net/lwip_tcpip/current/src/ecos/init.c ecos/ecos/packages/net/lwip_tcpip/current/src/ecos/init.c
--- ecos_web_cvs/ecos/packages/net/lwip_tcpip/current/src/ecos/init.c	2006-03-02 19:28:12.000000000 +0100
+++ ecos/ecos/packages/net/lwip_tcpip/current/src/ecos/init.c	2006-03-13 10:16:17.000000000 +0100
@@ -34,6 +34,7 @@
  * init.c - misc lwip ecos glue functions 
  */
 #include <pkgconf/system.h>
+#include <pkgconf/net_lwip.h>
 #include "lwip/opt.h"
 #include "lwip/sys.h"
 #include "lwip/memp.h"
@@ -75,12 +76,6 @@
 }
 
 
-void tcpip_init_done(void * arg)
-{
-	sys_sem_t *sem = arg;
-	sys_sem_signal(*sem);
-}
-
 struct netif mynetif, loopif;
 void lwip_set_addr(struct netif *netif);
 #if PPP_SUPPORT
@@ -116,6 +111,48 @@
 struct netif ecos_loopif;
 #endif
 
+
+static void
+arp_timer(void *arg)
+{
+  etharp_tmr();
+  sys_timeout(ARP_TMR_INTERVAL, (sys_timeout_handler) arp_timer, NULL);
+}
+
+#if LWIP_DHCP
+static void lwip_dhcp_fine_tmr(void *arg)
+{
+    dhcp_fine_tmr();
+    sys_timeout(500, (sys_timeout_handler) lwip_dhcp_fine_tmr, NULL);
+}
+
+static void lwip_dhcp_coarse_tmr(void *arg)
+{
+    dhcp_coarse_tmr();
+    sys_timeout(60000, (sys_timeout_handler) lwip_dhcp_coarse_tmr, NULL);
+}
+#endif
+
+
+//
+// This function is called when tcpip thread finished initialisation.
+// We start several timers here - these timers are all handled in the
+// tcpip thread. That means that also the DHCP stuff is handled in the
+// TCPIP thread. If this causes any trouble than it may be necessaray to
+// use an own DHCP thread insted.
+//
+void tcpip_init_done(void * arg)
+{
+    sys_timeout(ARP_TMR_INTERVAL, (sys_timeout_handler) arp_timer, NULL);
+#ifdef CYGOPT_LWIP_DHCP_MANAGEMENT
+	sys_timeout(500, (sys_timeout_handler) lwip_dhcp_fine_tmr, NULL);
+	sys_timeout(60000, (sys_timeout_handler) lwip_dhcp_coarse_tmr, NULL);
+#endif
+	sys_sem_t *sem = arg;
+	sys_sem_signal(*sem);
+}
+
+
 /*
  * Called by the eCos application at startup
  * wraps various init calls
@@ -169,22 +206,43 @@
 	return 0;
 }
 
+
+err_t lwip_dummy_netif_init(struct netif *netif)
+{
+    return ERR_OK; 
+}
+
+
 void
 lwip_set_addr(struct netif *netif)
 {
 	struct ip_addr ipaddr, netmask, gw;
-
+  
+#if LWIP_DHCP
+    IP4_ADDR(&gw, 0,0,0,0);
+    IP4_ADDR(&ipaddr, 0,0,0,0);
+    IP4_ADDR(&netmask, 0,0,0,0);
+
+    netif_add(netif, &ipaddr, &netmask, &gw, netif->state, lwip_dummy_netif_init, tcpip_input);
+    netif_set_default(netif);
+#else
 	IP_ADDR(&gw, CYGDAT_LWIP_SERV_ADDR);
 	IP_ADDR(&ipaddr, CYGDAT_LWIP_MY_ADDR);
 	IP_ADDR(&netmask, CYGDAT_LWIP_NETMASK);
-	netif_set_addr(netif, &ipaddr, &netmask, &gw);
-	netif->next = netif_list;
-	netif_list = netif;
-	
-	netif->input = tcpip_input;
-	//netif->input = ip_input;
+
+	netif_add(netif, &ipaddr, &netmask, &gw, netif->state, lwip_dummy_netif_init, tcpip_input);
+    netif_set_default(netif); 
+#endif 
 }
 
+void lwip_dhcp_init(struct netif *netif)
+{
+#ifdef CYGOPT_LWIP_DHCP_MANAGEMENT
+    dhcp_start(netif);
+#endif
+}
+
+
 #ifdef CYGPKG_LWIP_ETH
 //io eth stuff
 
@@ -224,6 +282,7 @@
 
 }
 
+
 // Initialize all network devices
 static void
 init_hw_drivers(void)
@@ -240,22 +299,15 @@
   }
 }
 
-static void
-arp_timer(void *arg)
-{
-  etharp_tmr();
-  sys_timeout(ARP_TMR_INTERVAL, (sys_timeout_handler) arp_timer, NULL);
-}
-
+extern struct netif *netif_default;
 
 static void
 ecosglue_init(void)
 {
-  cyg_semaphore_init(&delivery, 0);
-  init_hw_drivers();
-  sys_thread_new(input_thread, (void*)0, CYGNUM_LWIP_ETH_THREAD_PRIORITY);
-  etharp_init();
-  sys_timeout(ARP_TMR_INTERVAL, (sys_timeout_handler) arp_timer, NULL);
+    etharp_init();
+    cyg_semaphore_init(&delivery, 0);
+    init_hw_drivers();
+    sys_thread_new(input_thread, (void*)0, CYGNUM_LWIP_ETH_THREAD_PRIORITY);
 }
 
 #endif //CYGPKG_LWIP_ETH
Only in ecos_web_cvs/ecos/packages/net/lwip_tcpip/current/src/netif: CVS
diff -ru ecos_web_cvs/ecos/packages/net/lwip_tcpip/current/src/netif/etharp.c ecos/ecos/packages/net/lwip_tcpip/current/src/netif/etharp.c
--- ecos_web_cvs/ecos/packages/net/lwip_tcpip/current/src/netif/etharp.c	2004-05-04 14:32:33.000000000 +0200
+++ ecos/ecos/packages/net/lwip_tcpip/current/src/netif/etharp.c	2006-03-13 01:44:45.000000000 +0100
@@ -6,8 +6,10 @@
  * 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) and supports
- * Gratuitious ARP from RFC3220 (IP Mobility Support for IPv4) section 4.6.
+ * 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_query(our_netif, its_ip_addr, NULL) upon
+ * address change.
  */
 
 /*
@@ -37,52 +39,11 @@
  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
  * IN ANY WAY OUT OF THE 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:
- * - pbufs should be sent from the queue once an ARP entry state
- *   goes from PENDING to STABLE.
- * - Non-PENDING entries MUST NOT have queued packets.
  */
 
-/*
- * TODO:
- *
-RFC 3220 4.6          IP Mobility Support for IPv4          January 2002
-
-      -  A Gratuitous ARP [45] is an ARP packet sent by a node in order
-         to spontaneously cause other nodes to update an entry in their
-         ARP cache.  A gratuitous ARP MAY use either an ARP Request or
-         an ARP Reply packet.  In either case, the ARP Sender Protocol
-         Address and ARP Target Protocol Address are both set to the IP
-         address of the cache entry to be updated, and the ARP Sender
-         Hardware Address is set to the link-layer address to which this
-         cache entry should be updated.  When using an ARP Reply packet,
-         the Target Hardware Address is also set to the link-layer
-         address to which this cache entry should be updated (this field
-         is not used in an ARP Request packet).
-
-         In either case, for a gratuitous ARP, the ARP packet MUST be
-         transmitted as a local broadcast packet on the local link.  As
-         specified in [36], any node receiving any ARP packet (Request
-         or Reply) MUST update its local ARP cache with the Sender
-         Protocol and Hardware Addresses in the ARP packet, if the
-         receiving node has an entry for that IP address already in its
-         ARP cache.  This requirement in the ARP protocol applies even
-         for ARP Request packets, and for ARP Reply packets that do not
-         match any ARP Request transmitted by the receiving node [36].
-*
-  My suggestion would be to send a ARP request for our newly obtained
-  address upon configuration of an Ethernet interface.
-
-*/
-
 #include "lwip/opt.h"
 #include "lwip/inet.h"
 #include "netif/etharp.h"
@@ -94,13 +55,17 @@
 #  include "lwip/dhcp.h"
 #endif
 
-/* allows new queueing code to be disabled (0) for regression testing */
-#define ARP_NEW_QUEUE 1
-
-/** the time an ARP entry stays valid after its last update, (120 * 10) seconds = 20 minutes. */
-#define ARP_MAXAGE 120
-/** the time an ARP entry stays pending after first request, (1 * 10) seconds = 10 seconds. */
-#define ARP_MAXPENDING 1
+/** the time an ARP entry stays valid after its last update,
+ * (240 * 5) seconds = 20 minutes.
+ */
+#define ARP_MAXAGE 240
+/** the time an ARP entry stays pending after first request,
+ * (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
 
@@ -118,37 +83,40 @@
   ETHARP_STATE_EMPTY,
   ETHARP_STATE_PENDING,
   ETHARP_STATE_STABLE,
-  /** @internal convenience transitional state used in etharp_tmr() */
+  /** @internal transitional state used in etharp_tmr() for convenience*/
   ETHARP_STATE_EXPIRED
 };
 
 struct etharp_entry {
-  struct ip_addr ipaddr;
-  struct eth_addr ethaddr;
-  enum etharp_state state;
 #if ARP_QUEUEING
   /** 
    * Pointer to queue of pending outgoing packets on this ARP entry.
-   * Must be at most a single packet for now. */
-  struct pbuf *p;
+   */
+   struct pbuf *p;
 #endif
+  struct ip_addr ipaddr;
+  struct eth_addr ethaddr;
+  enum etharp_state state;
   u8_t ctime;
 };
 
 static const struct eth_addr ethbroadcast = {{0xff,0xff,0xff,0xff,0xff,0xff}};
 static struct etharp_entry arp_table[ARP_TABLE_SIZE];
 
-static s8_t find_arp_entry(void);
-/** ask update_arp_entry() to add instead of merely update an ARP entry */
-#define ARP_INSERT_FLAG 1
-static struct pbuf *update_arp_entry(struct netif *netif, struct ip_addr *ipaddr, struct eth_addr *ethaddr, u8_t flags);
+/**
+ * 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_TRY_HARD 1
+
+static s8_t find_entry(struct ip_addr *ipaddr, u8_t flags);
+static err_t update_arp_entry(struct netif *netif, struct ip_addr *ipaddr, struct eth_addr *ethaddr, u8_t flags);
 /**
  * Initializes ARP module.
  */
 void
 etharp_init(void)
 {
-  s8_t i;
+  u8_t i;
   /* clear ARP entries */
   for(i = 0; i < ARP_TABLE_SIZE; ++i) {
     arp_table[i].state = ETHARP_STATE_EMPTY;
@@ -162,30 +130,35 @@
 /**
  * Clears expired entries in the ARP table.
  *
- * This function should be called every ETHARP_TMR_INTERVAL microseconds (10 seconds),
+ * This function should be called every ETHARP_TMR_INTERVAL microseconds (5 seconds),
  * in order to expire entries in the ARP table.
  */
 void
 etharp_tmr(void)
 {
-  s8_t i;
+  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) {
     arp_table[i].ctime++;
-    /* a resolved/stable entry? */
+    /* stable entry? */
     if ((arp_table[i].state == ETHARP_STATE_STABLE) &&
          /* entry has become old? */
         (arp_table[i].ctime >= ARP_MAXAGE)) {
-      LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_timer: expired stable entry %u.\n", i));
-      arp_table[i].state = ETHARP_STATE_EXPIRED;
-    /* an unresolved/pending entry? */
-    } else if ((arp_table[i].state == ETHARP_STATE_PENDING) &&
-         /* entry unresolved/pending for too long? */
-        (arp_table[i].ctime >= ARP_MAXPENDING)) {
-      LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_timer: expired pending entry %u.\n", i));
+      LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_timer: expired stable entry %"U16_F".\n", (u16_t)i));
       arp_table[i].state = ETHARP_STATE_EXPIRED;
+    /* pending entry? */
+    } else if (arp_table[i].state == ETHARP_STATE_PENDING) {
+      /* entry unresolved/pending for too long? */
+      if (arp_table[i].ctime >= ARP_MAXPENDING) {
+        LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_timer: expired pending entry %"U16_F".\n", (u16_t)i));
+        arp_table[i].state = ETHARP_STATE_EXPIRED;
+#if ARP_QUEUEING
+      } else if (arp_table[i].p != NULL) {
+        /* resend an ARP query here */
+#endif
+      }
     }
     /* clean up entries that have just been expired */
     if (arp_table[i].state == ETHARP_STATE_EXPIRED) {
@@ -193,7 +166,7 @@
       /* and empty packet queue */
       if (arp_table[i].p != NULL) {
         /* remove all queued packets */
-        LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_timer: freeing entry %u, packet queue %p.\n", i, (void *)(arp_table[i].p)));
+        LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_timer: freeing entry %"U16_F", packet queue %p.\n", (u16_t)i, (void *)(arp_table[i].p)));
         pbuf_free(arp_table[i].p);
         arp_table[i].p = NULL;
       }
@@ -205,59 +178,161 @@
 }
 
 /**
- * Return an empty ARP entry (possibly recycling the oldest stable entry).
- *
- * @return The ARP entry index that is available, ERR_MEM if no usable
- * entry is found.
+ * 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_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
+ * - ETHARP_TRY_HARD: Try hard to create a entry by allowing recycling of
+ * active (stable or pending) entries.
+ *  
+ * @return The ARP entry index that matched or is created, ERR_MEM if no
+ * entry is found or could be recycled.
  */
-static s8_t
-find_arp_entry(void)
+static s8_t find_entry(struct ip_addr *ipaddr, u8_t flags)
 {
-  s8_t i, j;
-  u8_t maxtime = 0;
+  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;
+#if ARP_QUEUEING
+  /* oldest entry with packets on queue */
+  s8_t old_queue = ARP_TABLE_SIZE;
+  /* its age */
+  u8_t age_queue = 0;
+#endif
 
-  j = ARP_TABLE_SIZE;
-  /* search ARP table for an unused or old entry */
-  for (i = 0; i < ARP_TABLE_SIZE; ++i) {
-  	/* empty entry? */
-    if (arp_table[i].state == ETHARP_STATE_EMPTY) {
-      LWIP_DEBUGF(ETHARP_DEBUG, ("find_arp_entry: returning empty entry %u\n", i));
-      return i;
-  	/* stable entry? */
-    } else if (arp_table[i].state == ETHARP_STATE_STABLE) {
-      /* remember entry with oldest stable entry in j */
-      if (arp_table[i].ctime >= maxtime) maxtime = arp_table[j = i].ctime;
-    }
-  }
-  /* no empty entry found? */
-  if (i == ARP_TABLE_SIZE) {
-  	LWIP_DEBUGF(ETHARP_DEBUG, ("find_arp_entry: found oldest stable entry %u\n", j));
-    /* fall-back to oldest stable */
-  	i = j;
-  }
-  /* no available entry found? */
-  if (i == ARP_TABLE_SIZE) {
-    LWIP_DEBUGF(ETHARP_DEBUG, ("find_arp_entry: no replacable entry could be found\n"));
-    /* return failure */
-    return ERR_MEM;
-  }
+  /**
+   * 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.
+   */
 
-  /* clean up the recycled stable entry */
-  if (arp_table[i].state == ETHARP_STATE_STABLE) {
+  for (i = 0; i < ARP_TABLE_SIZE; ++i) {
+    /* no empty entry found yet and now we do find one? */
+    if ((empty == ARP_TABLE_SIZE) && (arp_table[i].state == ETHARP_STATE_EMPTY)) {
+      LWIP_DEBUGF(ETHARP_DEBUG, ("find_entry: found empty entry %"U16_F"\n", (u16_t)i));
+      /* remember first empty entry */
+      empty = i;
+    }
+    /* pending entry? */
+    else if (arp_table[i].state == ETHARP_STATE_PENDING) {
+      /* 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 | DBG_TRACE, ("find_entry: found matching pending entry %"U16_F"\n", (u16_t)i));
+        /* found exact IP address match, simply bail out */
+        return i;
 #if ARP_QUEUEING
-    /* and empty the packet queue */
-    if (arp_table[i].p != NULL) {
-      /* remove all queued packets */
-      LWIP_DEBUGF(ETHARP_DEBUG, ("find_arp_entry: freeing entry %u, packet queue %p.\n", i, (void *)(arp_table[i].p)));
-      pbuf_free(arp_table[i].p);
-      arp_table[i].p = NULL;
+      /* pending with queued packets? */
+      } else if (arp_table[i].p != NULL) {
+        if (arp_table[i].ctime >= age_queue) {
+          old_queue = i;
+          age_queue = arp_table[i].ctime;
+        }
+#endif
+      /* pending without queued packets? */
+      } else {
+        if (arp_table[i].ctime >= age_pending) {
+          old_pending = i;
+          age_pending = arp_table[i].ctime;
+        }
+      }        
+    }
+    /* stable entry? */
+    else if (arp_table[i].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 | DBG_TRACE, ("find_entry: found matching stable entry %"U16_F"\n", (u16_t)i));
+        /* found exact IP address match, simply bail out */
+        return i;
+      /* remember entry with oldest stable entry in oldest, its age in maxtime */
+      } else 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 */
+   
+  /* no empty entry found and not allowed to recycle? */
+  if ((empty == ARP_TABLE_SIZE) && ((flags & ETHARP_TRY_HARD) == 0))
+  {
+  	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 without queued packets
+   * 
+   * { ETHARP_TRY_HARD is set at this point }
+   */ 
+
+  /* 1) empty entry available? */
+  if (empty < ARP_TABLE_SIZE) {
+    i = empty;
+    LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("find_entry: selecting empty entry %"U16_F"\n", (u16_t)i));
+  }
+  /* 2) found recyclable stable entry? */
+  else if (old_stable < ARP_TABLE_SIZE) {
+    /* recycle oldest stable*/
+    i = old_stable;
+    LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("find_entry: selecting oldest stable entry %"U16_F"\n", (u16_t)i));
+#if ARP_QUEUEING
+    /* no queued packets should exist on stable entries */
+    LWIP_ASSERT("arp_table[i].p == NULL", arp_table[i].p == NULL);
 #endif
-    LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("find_arp_entry: recycling oldest stable entry %u\n", i));
-    arp_table[i].state = ETHARP_STATE_EMPTY;
+  /* 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 | DBG_TRACE, ("find_entry: selecting oldest pending entry %"U16_F" (without queue)\n", (u16_t)i));
+#if ARP_QUEUEING
+  /* 4) found recyclable pending entry with queued packets? */
+  } else if (old_queue < ARP_TABLE_SIZE) {
+    /* recycle oldest pending */
+    i = old_queue;
+    LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("find_entry: selecting oldest pending entry %"U16_F", freeing packet queue %p\n", (u16_t)i, (void *)(arp_table[i].p)));
+    pbuf_free(arp_table[i].p);
+    arp_table[i].p = NULL;
+#endif
+    /* no empty or recyclable entries found */
+  } else {
+    return (s8_t)ERR_MEM;
+  }
+
+  /* { empty or recyclable entry found } */
+  LWIP_ASSERT("i < ARP_TABLE_SIZE", i < ARP_TABLE_SIZE);
+
+  /* recycle entry (no-op for an already empty entry) */
+  arp_table[i].state = ETHARP_STATE_EMPTY;
+
+  /* IP address given? */
+  if (ipaddr != NULL) {
+    /* set IP address */
+    ip_addr_set(&arp_table[i].ipaddr, ipaddr);
   }
-  LWIP_DEBUGF(ETHARP_DEBUG, ("find_arp_entry: returning %u\n", i));
-  return i;
+  arp_table[i].ctime = 0;
+  return (err_t)i;
 }
 
 /**
@@ -269,118 +344,72 @@
  * @param ipaddr IP address of the inserted ARP entry.
  * @param ethaddr Ethernet address of the inserted ARP entry.
  * @param flags Defines behaviour:
- * - ARP_INSERT_FLAG Allows ARP to insert this as a new item. If not specified,
+ * - ETHARP_TRY_HARD Allows ARP to insert this as a new item. If not specified,
  * only existing ARP entries will be updated.
  *
- * @return pbuf If non-NULL, a packet that was queued on a pending entry.
- * You should sent it and must call pbuf_free() afterwards.
+ * @return
+ * - ERR_OK Succesfully updated ARP cache.
+ * - ERR_MEM If we could not add a new ARP entry when ETHARP_TRY_HARD was set.
+ * - ERR_ARG Non-unicast address given, those will not appear in ARP cache.
  *
  * @see pbuf_free()
  */
-static struct pbuf *
+static err_t
 update_arp_entry(struct netif *netif, struct ip_addr *ipaddr, struct eth_addr *ethaddr, u8_t flags)
 {
   s8_t i, k;
   LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE | 3, ("update_arp_entry()\n"));
   LWIP_ASSERT("netif->hwaddr_len != 0", netif->hwaddr_len != 0);
-  LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("update_arp_entry: %u.%u.%u.%u - %02x:%02x:%02x:%02x:%02x:%02x\n",
+  LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("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(ipaddr), ip4_addr2(ipaddr), ip4_addr3(ipaddr), ip4_addr4(ipaddr), 
                                         ethaddr->addr[0], ethaddr->addr[1], ethaddr->addr[2],
                                         ethaddr->addr[3], ethaddr->addr[4], ethaddr->addr[5]));
-  /* do not update for 0.0.0.0 addresses */
-  if (ipaddr->addr == 0) {
-    LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("update_arp_entry: will not add 0.0.0.0 to ARP cache\n"));
-    return NULL;
-  }
-  /* Walk through the ARP mapping table and try to find an entry to
-  update. If none is found, the IP -> MAC address mapping is
-  inserted in the ARP table. */
-  for (i = 0; i < ARP_TABLE_SIZE; ++i) {
-    /* Check if the source IP address of the incoming packet matches
-    the IP address in this ARP table entry. */
-    if (arp_table[i].state != ETHARP_STATE_EMPTY &&
-    	ip_addr_cmp(ipaddr, &arp_table[i].ipaddr)) {
-      /* pending entry? */
-      if (arp_table[i].state == ETHARP_STATE_PENDING) {
-        LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("update_arp_entry: pending entry %u goes stable\n", i));
-        /* A pending entry was found, mark it stable */
-        arp_table[i].state = ETHARP_STATE_STABLE;
-        /* fall-through to next if */
-      }
-      /* stable entry? (possibly just marked to become stable) */
-      if (arp_table[i].state == ETHARP_STATE_STABLE) {
-#if ARP_QUEUEING
-        struct pbuf *p;
-        struct eth_hdr *ethhdr;
-#endif
-        LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("update_arp_entry: updating stable entry %u\n", i));
-        /* An old entry found, update this and return. */
-        for (k = 0; k < netif->hwaddr_len; ++k) {
-          arp_table[i].ethaddr.addr[k] = ethaddr->addr[k];
-        }
-        /* reset time stamp */
-        arp_table[i].ctime = 0;
+  /* non-unicast address? */
+  if (ip_addr_isany(ipaddr) ||
+      ip_addr_isbroadcast(ipaddr, netif) ||
+      ip_addr_ismulticast(ipaddr)) {
+    LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("update_arp_entry: will not add non-unicast IP address to ARP cache\n"));
+    return ERR_ARG;
+  }
+  /* find or create ARP entry */
+  i = find_entry(ipaddr, flags);
+  /* bail out if no entry could be found */
+  if (i < 0) return (err_t)i;
+  
+  /* mark it stable */
+  arp_table[i].state = ETHARP_STATE_STABLE;
+
+  LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("update_arp_entry: updating stable entry %"S16_F"\n", (s16_t)i));
+  /* update address */
+  for (k = 0; k < netif->hwaddr_len; ++k) {
+    arp_table[i].ethaddr.addr[k] = ethaddr->addr[k];
+  }
+  /* reset time stamp */
+  arp_table[i].ctime = 0;
 /* this is where we will send out queued packets! */
 #if ARP_QUEUEING
-        while (arp_table[i].p != NULL) {
-          /* get the first packet on the queue (if any) */
-          p = arp_table[i].p;
-          /* remember (and reference) remainder of queue */
-          arp_table[i].p = pbuf_dequeue(p);
-          /* fill-in Ethernet header */
-          ethhdr = p->payload;
-          for (k = 0; k < netif->hwaddr_len; ++k) {
-            ethhdr->dest.addr[k] = ethaddr->addr[k];
-            ethhdr->src.addr[k] = netif->hwaddr[k];
-          }
-          ethhdr->type = htons(ETHTYPE_IP);
-          LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("update_arp_entry: sending queued IP packet %p.\n", (void *)p));
-          /* send the queued IP packet */
-          netif->linkoutput(netif, p);
-          /* free the queued IP packet */
-          pbuf_free(p);
-        }
-#endif
-        /* IP addresses should only occur once in the ARP entry, we are done */
-        return NULL;
-      }
-    } /* if STABLE */
-  } /* for all ARP entries */
-
-  /* no matching ARP entry was found */
-  LWIP_ASSERT("update_arp_entry: i == ARP_TABLE_SIZE", i == ARP_TABLE_SIZE);
-
-  LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("update_arp_entry: IP address not yet in table\n"));
-  /* allowed to insert a new entry? */
-  if (flags & ARP_INSERT_FLAG)
-  {
-    LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("update_arp_entry: adding entry to table\n"));
-    /* find an empty or old entry. */
-    i = find_arp_entry();
-    if (i == ERR_MEM) {
-      LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("update_arp_entry: no available entry found\n"));
-      return NULL;
-    }
-    /* set IP address */
-    ip_addr_set(&arp_table[i].ipaddr, ipaddr);
-    /* set Ethernet hardware address */
+  while (arp_table[i].p != NULL) {
+    /* get the first packet on the queue */
+    struct pbuf *p = arp_table[i].p;
+    /* Ethernet header */
+    struct eth_hdr *ethhdr = p->payload;
+    /* remember (and reference) remainder of queue */
+    /* note: this will also terminate the p pbuf chain */
+    arp_table[i].p = pbuf_dequeue(p);
+    /* fill-in Ethernet header */
     for (k = 0; k < netif->hwaddr_len; ++k) {
-      arp_table[i].ethaddr.addr[k] = ethaddr->addr[k];
+      ethhdr->dest.addr[k] = ethaddr->addr[k];
+      ethhdr->src.addr[k] = netif->hwaddr[k];
     }
-    /* reset time-stamp */
-    arp_table[i].ctime = 0;
-    /* mark as stable */
-    arp_table[i].state = ETHARP_STATE_STABLE;
-    /* no queued packet */
-#if ARP_QUEUEING
-    arp_table[i].p = NULL;
-#endif
-  }
-  else
-  {
-    LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("update_arp_entry: no matching stable entry to update\n"));
+    ethhdr->type = htons(ETHTYPE_IP);
+    LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("update_arp_entry: sending queued IP packet %p.\n", (void *)p));
+    /* send the queued IP packet */
+    netif->linkoutput(netif, p);
+    /* free the queued IP packet */
+    pbuf_free(p);
   }
-  return NULL;
+#endif
+  return ERR_OK;
 }
 
 /**
@@ -398,24 +427,25 @@
  *
  * @see pbuf_free()
  */
-struct pbuf *
+void
 etharp_ip_input(struct netif *netif, struct pbuf *p)
 {
   struct ethip_hdr *hdr;
-
+  LWIP_ASSERT("netif != NULL", netif != NULL);
   /* Only insert an entry if the source IP address of the
      incoming IP packet comes from a host on the local network. */
   hdr = p->payload;
-  /* source is on local network? */
-  if (!ip_addr_maskcmp(&(hdr->ip.src), &(netif->ip_addr), &(netif->netmask))) {
+  /* source is not on the local network? */
+  if (!ip_addr_netcmp(&(hdr->ip.src), &(netif->ip_addr), &(netif->netmask))) {
     /* do nothing */
-    return NULL;
+    return;
   }
 
   LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_ip_input: updating ETHARP table.\n"));
-  /* update ARP table, ask to insert entry */
-  update_arp_entry(netif, &(hdr->ip.src), &(hdr->eth.src), ARP_INSERT_FLAG);
-  return NULL;
+  /* update ARP table */
+  /* @todo We could use ETHARP_TRY_HARD if we think we are going to talk
+   * back soon (for example, if the destination IP address is ours. */
+  update_arp_entry(netif, &(hdr->ip.src), &(hdr->eth.src), 0);
 }
 
 
@@ -434,7 +464,7 @@
  *
  * @see pbuf_free()
  */
-struct pbuf *
+void
 etharp_arp_input(struct netif *netif, struct eth_addr *ethaddr, struct pbuf *p)
 {
   struct etharp_hdr *hdr;
@@ -443,11 +473,13 @@
   u8_t i;
   u8_t for_us;
 
+  LWIP_ASSERT("netif != NULL", netif != NULL);
+  
   /* drop short ARP packets */
   if (p->tot_len < sizeof(struct etharp_hdr)) {
-    LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE | 1, ("etharp_arp_input: packet dropped, too short (%d/%d)\n", p->tot_len, sizeof(struct etharp_hdr)));
+    LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE | 1, ("etharp_arp_input: packet dropped, too short (%"S16_F"/%"S16_F")\n", p->tot_len, sizeof(struct etharp_hdr)));
     pbuf_free(p);
-    return NULL;
+    return;
   }
 
   hdr = p->payload;
@@ -468,7 +500,7 @@
   if (for_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. */
-    update_arp_entry(netif, &sipaddr, &(hdr->shwaddr), ARP_INSERT_FLAG);
+    update_arp_entry(netif, &sipaddr, &(hdr->shwaddr), ETHARP_TRY_HARD);
   /* ARP message not directed to us? */
   } else {
     /* update the source IP address in the cache, if present */
@@ -521,77 +553,70 @@
     }
     break;
   case ARP_REPLY:
-    /* ARP reply. We insert or update the ARP table later. */
+    /* ARP reply. We already updated the ARP cache earlier. */
     LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_arp_input: incoming ARP reply\n"));
 #if (LWIP_DHCP && DHCP_DOES_ARP_CHECK)
-    /* DHCP wants to know about ARP replies to our wanna-have-address */
-    if (for_us) dhcp_arp_reply(netif, &sipaddr);
+    /* 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
     break;
   default:
-    LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_arp_input: ARP unknown opcode type %d\n", htons(hdr->opcode)));
+    LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_arp_input: ARP unknown opcode type %"S16_F"\n", htons(hdr->opcode)));
     break;
   }
   /* free ARP packet */
   pbuf_free(p);
-  p = NULL;
-  /* nothing to send, we did it! */
-  return NULL;
 }
 
 /**
  * Resolve and fill-in Ethernet address header for outgoing packet.
  *
- * If ARP has the Ethernet address in cache, the given packet is
- * returned, ready to be sent.
+ * For IP multicast and broadcast, corresponding Ethernet addresses
+ * are selected and the packet is transmitted on the link.
  *
- * If ARP does not have the Ethernet address in cache the packet is
- * queued (if enabled and space available) and a ARP request is sent.
- * This ARP request is returned as a pbuf, which should be sent by
- * the caller.
- *
- * A returned non-NULL packet should be sent by the caller.
- *
- * If ARP failed to allocate resources, NULL is returned.
+ * 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 ipaddr The IP address of the packet destination.
  * @param pbuf The pbuf(s) containing the IP packet to be sent.
  *
- * @return If non-NULL, a packet ready to be sent by caller.
- *
+ * @return
+ * - ERR_RTE No route to destination (no gateway to external networks),
+ * or the return type of either etharp_query() or netif->linkoutput().
  */
-struct pbuf *
+err_t
 etharp_output(struct netif *netif, struct ip_addr *ipaddr, struct pbuf *q)
 {
   struct eth_addr *dest, *srcaddr, mcastaddr;
   struct eth_hdr *ethhdr;
-  s8_t i;
+  u8_t i;
 
-  /* make room for Ethernet header */
+  /* make room for Ethernet header - should not fail */
   if (pbuf_header(q, sizeof(struct eth_hdr)) != 0) {
-    /* The pbuf_header() call shouldn't fail, and we'll just bail
-    out if it does.. */
+    /* bail out */
     LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE | 2, ("etharp_output: could not allocate room for header.\n"));
     LINK_STATS_INC(link.lenerr);
-    return NULL;
+    return ERR_BUF;
   }
 
   /* assume unresolved Ethernet address */
   dest = NULL;
-  /* Construct Ethernet header. Start with looking up deciding which
-     MAC address to use as a destination address. Broadcasts and
-     multicasts are special, all other addresses are looked up in the
-     ARP table. */
+  /* Determine on destination hardware address. Broadcasts and multicasts
+   * are special, other IP addresses are looked up in the ARP table. */
 
-  /* destination IP address is an IP broadcast address? */
-  if (ip_addr_isany(ipaddr) || ip_addr_isbroadcast(ipaddr, netif)) {
+  /* broadcast destination IP address? */
+  if (ip_addr_isbroadcast(ipaddr, netif)) {
     /* broadcast on Ethernet also */
     dest = (struct eth_addr *)&ethbroadcast;
-  }
-  /* destination IP address is an IP multicast address? */
-  else if (ip_addr_ismulticast(ipaddr)) {
-    /* Hash IP multicast address to MAC address. */
+  /* multicast destination IP address? */
+  } else if (ip_addr_ismulticast(ipaddr)) {
+    /* Hash IP multicast address to MAC address.*/
     mcastaddr.addr[0] = 0x01;
     mcastaddr.addr[1] = 0x00;
     mcastaddr.addr[2] = 0x5e;
@@ -600,192 +625,207 @@
     mcastaddr.addr[5] = ip4_addr4(ipaddr);
     /* destination Ethernet address is multicast */
     dest = &mcastaddr;
-  }
-  /* destination IP address is an IP unicast address */
-  else {
-    /* destination IP network address not on local network?
-     * IP layer wants us to forward to the default gateway */
-    if (!ip_addr_maskcmp(ipaddr, &(netif->ip_addr), &(netif->netmask))) {
+  /* unicast destination IP address? */
+  } else {
+    /* outside local network? */
+    if (!ip_addr_netcmp(ipaddr, &(netif->ip_addr), &(netif->netmask))) {
       /* interface has default gateway? */
-      if (netif->gw.addr != 0)
-      {
-        /* route to default gateway IP address */
+      if (netif->gw.addr != 0) {
+        /* send to hardware address of default gateway IP address */
         ipaddr = &(netif->gw);
+      /* no default gateway available */
+      } else {
+        /* no route to destination error (default gateway missing) */
+        return ERR_RTE;
       }
-      /* no gateway available? */
-      else
-      {
-        /* IP destination address outside local network, but no gateway available */
-        /* { packet is discarded } */
-        return NULL;
-      }
-    }
-
-    /* Ethernet address for IP destination address is in ARP cache? */
-    for (i = 0; i < ARP_TABLE_SIZE; ++i) {
-      /* match found? */
-      if (arp_table[i].state == ETHARP_STATE_STABLE &&
-        ip_addr_cmp(ipaddr, &arp_table[i].ipaddr)) {
-        dest = &arp_table[i].ethaddr;
-        break;
-      }
-    }
-    /* could not find the destination Ethernet address in ARP cache? */
-    if (dest == NULL) {
-      /* ARP query for the IP address, submit this IP packet for queueing */
-      /* TODO: How do we handle netif->ipaddr == ipaddr? */
-      etharp_query(netif, ipaddr, q);
-      /* { packet was queued (ERR_OK), or discarded } */
-      /* return nothing */
-      return NULL;
-    }
-    /* destination Ethernet address resolved from ARP cache */
-    else
-    {
-      /* fallthrough */
     }
+    /* queue on destination Ethernet address belonging to ipaddr */
+    return etharp_query(netif, ipaddr, q);
   }
 
-  /* destination Ethernet address known */
-  if (dest != NULL) {
-    /* obtain source Ethernet address of the given interface */
-    srcaddr = (struct eth_addr *)netif->hwaddr;
-
-    /* A valid IP->MAC address mapping was found, fill in the
-     * Ethernet header for the outgoing packet */
-    ethhdr = q->payload;
-
-    for(i = 0; i < netif->hwaddr_len; i++) {
-      ethhdr->dest.addr[i] = dest->addr[i];
-      ethhdr->src.addr[i] = srcaddr->addr[i];
-    }
-
-    ethhdr->type = htons(ETHTYPE_IP);
-    /* return the outgoing packet */
-    return q;
-  }
-  /* never reached; here for safety */
-  return NULL;
+  /* continuation for multicast/broadcast destinations */
+  /* obtain source Ethernet address of the given interface */
+  srcaddr = (struct eth_addr *)netif->hwaddr;
+  ethhdr = q->payload;
+  for (i = 0; i < netif->hwaddr_len; i++) {
+    ethhdr->dest.addr[i] = dest->addr[i];
+    ethhdr->src.addr[i] = srcaddr->addr[i];
+  }
+  ethhdr->type = htons(ETHTYPE_IP);
+  /* send packet directly on the link */
+  return netif->linkoutput(netif, q);
 }
 
 /**
- * Send an ARP request for the given IP address.
+ * 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.
  *
- * Sends an ARP request for the given IP address, unless
- * a request for this address is already pending. Optionally
- * queues an outgoing packet on the resulting ARP 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.
  *
- * @param netif The lwIP network interface where ipaddr
+ * 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 queued on the
- * ARP entry for the ipaddr IP address.
+ * @param q If non-NULL, a pbuf that must be delivered to the IP address.
+ * q is not freed by this function.
  *
- * @return NULL.
+ * @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.
  *
- * @note Might be used in the future by manual IP configuration
- * as well.
- *
- * TODO: use the ctime field to see how long ago an ARP request was sent,
- * possibly retry.
  */
 err_t etharp_query(struct netif *netif, struct ip_addr *ipaddr, struct pbuf *q)
 {
-  struct eth_addr *srcaddr;
-  struct etharp_hdr *hdr;
-  err_t result = ERR_OK;
-  s8_t i;
-  u8_t perform_arp_request = 1;
-  /* prevent 'unused argument' warning if ARP_QUEUEING == 0 */
-  (void)q;
-  srcaddr = (struct eth_addr *)netif->hwaddr;
-  /* bail out if this IP address is pending */
-  for (i = 0; i < ARP_TABLE_SIZE; ++i) {
-    if (arp_table[i].state != ETHARP_STATE_EMPTY &&
-    	ip_addr_cmp(ipaddr, &arp_table[i].ipaddr)) {
-      if (arp_table[i].state == ETHARP_STATE_PENDING) {
-        LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE | DBG_STATE, ("etharp_query: requested IP already pending as entry %u\n", i));
-        /* break out of for-loop, user may wish to queue a packet on a pending entry */
-        /* TODO: we will issue a new ARP request, which should not occur too often */
-        /* we might want to run a faster timer on ARP to limit this */
-        break;
-      }
-      else if (arp_table[i].state == ETHARP_STATE_STABLE) {
-        LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE | DBG_STATE, ("etharp_query: requested IP already stable as entry %u\n", i));
-        /* User wishes to queue a packet on a stable entry (or does she want to send
-         * out the packet immediately, we will not know), so we force an ARP request.
-         * Upon response we will send out the queued packet in etharp_update().
-         * 
-         * Alternatively, we could accept the stable entry, and just send out the packet
-         * immediately. I chose to implement the former approach.
-         */
-        perform_arp_request = (q?1:0);
-        break;
-      }
-    }
+  struct eth_addr * srcaddr = (struct eth_addr *)netif->hwaddr;
+  err_t result = ERR_MEM;
+  s8_t i; /* ARP entry index */
+  u8_t k; /* Ethernet address octet index */
+
+  /* non-unicast address? */
+  if (ip_addr_isbroadcast(ipaddr, netif) ||
+      ip_addr_ismulticast(ipaddr) ||
+      ip_addr_isany(ipaddr)) {
+    LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_query: will not add non-unicast IP address to ARP cache\n"));
+    return ERR_ARG;
   }
-  /* queried address not yet in ARP table? */
-  if (i == ARP_TABLE_SIZE) {
-    LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_query: IP address not found in ARP table\n"));
-    /* find an available (unused or old) entry */
-    i = find_arp_entry();
-    /* bail out if no ARP entries are available */
-    if (i == ERR_MEM) {
-      LWIP_DEBUGF(ETHARP_DEBUG | 2, ("etharp_query: no more ARP entries available. Should seldom occur.\n"));
-      return ERR_MEM;
-    }
-    /* i is available, create ARP entry */
+
+  /* find entry in ARP cache, ask to create entry if queueing packet */
+  i = find_entry(ipaddr, ETHARP_TRY_HARD);
+
+  /* could not find or create entry? */
+  if (i < 0)
+  {
+    LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_query: could not create ARP entry\n"));
+    if (q) LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_query: packet dropped\n"));
+    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;
-    ip_addr_set(&arp_table[i].ipaddr, ipaddr);
   }
-  /* { i is now valid } */
-#if ARP_QUEUEING /* queue packet (even on a stable entry, see above) */
-  /* copy any PBUF_REF referenced payloads into PBUF_RAM */
-  q = pbuf_take(q);
-  pbuf_queue(arp_table[i].p, q);
+
+  /* { 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);
+  }
+  
+  /* packet given? */
+  if (q != NULL) {
+    /* stable entry? */
+    if (arp_table[i].state == ETHARP_STATE_STABLE) {
+      /* we have a valid IP->Ethernet address mapping,
+       * fill in the Ethernet header for the outgoing packet */
+      struct eth_hdr *ethhdr = q->payload;
+      for(k = 0; k < netif->hwaddr_len; k++) {
+        ethhdr->dest.addr[k] = arp_table[i].ethaddr.addr[k];
+        ethhdr->src.addr[k]  = srcaddr->addr[k];
+      }
+      ethhdr->type = htons(ETHTYPE_IP);
+      LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_query: sending packet %p\n", (void *)q));
+      /* send the packet */
+      result = netif->linkoutput(netif, q);
+    /* pending entry? (either just created or already pending */
+    } else if (arp_table[i].state == ETHARP_STATE_PENDING) {
+#if ARP_QUEUEING /* queue the given q packet */
+      struct pbuf *p;
+      /* copy any PBUF_REF referenced payloads into PBUF_RAM */
+      /* (the caller of lwIP assumes the referenced payload can be
+       * freed after it returns from the lwIP call that brought us here) */
+      p = pbuf_take(q);
+      /* packet could be taken over? */
+      if (p != NULL) {
+        /* queue packet ... */
+        if (arp_table[i].p == NULL) {
+        	/* ... in the empty queue */
+        	pbuf_ref(p);
+        	arp_table[i].p = p;
+#if 0 /* multi-packet-queueing disabled, see bug #11400 */
+        } else {
+        	/* ... at tail of non-empty queue */
+          pbuf_queue(arp_table[i].p, p);
 #endif
-  /* ARP request? */
-  if (perform_arp_request)
-  {
-    struct pbuf *p;
-    /* allocate a pbuf for the outgoing ARP request packet */
-    p = pbuf_alloc(PBUF_LINK, sizeof(struct etharp_hdr), PBUF_RAM);
-    /* could allocate pbuf? */
-    if (p != NULL) {
-      u8_t j;
-      LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_query: sending ARP request.\n"));
-      hdr = p->payload;
-      hdr->opcode = htons(ARP_REQUEST);
-      for (j = 0; j < netif->hwaddr_len; ++j)
-      {
-        hdr->shwaddr.addr[j] = srcaddr->addr[j];
-        /* the hardware address is what we ask for, in
-         * a request it is a don't-care, we use 0's */
-        hdr->dhwaddr.addr[j] = 0x00;
-      }
-      hdr->dipaddr = *(struct ip_addr2 *)ipaddr;
-      hdr->sipaddr = *(struct ip_addr2 *)&netif->ip_addr;
+        }
+        LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_query: queued packet %p on ARP entry %"S16_F"\n", (void *)q, (s16_t)i));
+        result = ERR_OK;
+      } else {
+        LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_query: could not queue a copy of PBUF_REF packet %p (out of memory)\n", (void *)q));
+        /* { result == ERR_MEM } through initialization */
+      }
+#else /* ARP_QUEUEING == 0 */
+      /* q && state == PENDING && ARP_QUEUEING == 0 => result = ERR_MEM */
+      /* { result == ERR_MEM } through initialization */
+      LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_query: Ethernet destination address unknown, queueing disabled, packet %p dropped\n", (void *)q));
+#endif
+    }
+  }
+  return result;
+}
 
-      hdr->hwtype = htons(HWTYPE_ETHERNET);
-      ARPH_HWLEN_SET(hdr, netif->hwaddr_len);
+err_t etharp_request(struct netif *netif, struct ip_addr *ipaddr)
+{
+  struct pbuf *p;
+  struct eth_addr * srcaddr = (struct eth_addr *)netif->hwaddr;
+  err_t result = ERR_OK;
+  u8_t k; /* ARP entry index */
 
-      hdr->proto = htons(ETHTYPE_IP);
-      ARPH_PROTOLEN_SET(hdr, sizeof(struct ip_addr));
-      for (j = 0; j < netif->hwaddr_len; ++j)
-      {
-        hdr->ethhdr.dest.addr[j] = 0xff;
-        hdr->ethhdr.src.addr[j] = srcaddr->addr[j];
-      }
-      hdr->ethhdr.type = htons(ETHTYPE_ARP);
-      /* send ARP query */
-      result = netif->linkoutput(netif, p);
-      /* free ARP query packet */
-      pbuf_free(p);
-      p = NULL;
-    } else {
-      result = ERR_MEM;
-      LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE | 2, ("etharp_query: could not allocate pbuf for ARP request.\n"));
-    }
+  /* allocate a pbuf for the outgoing ARP request packet */
+  p = pbuf_alloc(PBUF_LINK, sizeof(struct etharp_hdr), PBUF_RAM);
+  /* could allocate a pbuf for an ARP request? */
+  if (p != NULL) {
+    struct etharp_hdr *hdr = p->payload;
+    LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_request: sending ARP request.\n"));
+    hdr->opcode = htons(ARP_REQUEST);
+    for (k = 0; k < netif->hwaddr_len; k++)
+    {
+      hdr->shwaddr.addr[k] = srcaddr->addr[k];
+      /* the hardware address is what we ask for, in
+       * a request it is a don't-care value, we use zeroes */
+      hdr->dhwaddr.addr[k] = 0x00;
+    }
+    hdr->dipaddr = *(struct ip_addr2 *)ipaddr;
+    hdr->sipaddr = *(struct ip_addr2 *)&netif->ip_addr;
+
+    hdr->hwtype = htons(HWTYPE_ETHERNET);
+    ARPH_HWLEN_SET(hdr, netif->hwaddr_len);
+
+    hdr->proto = htons(ETHTYPE_IP);
+    ARPH_PROTOLEN_SET(hdr, sizeof(struct ip_addr));
+    for (k = 0; k < netif->hwaddr_len; ++k)
+    {
+      /* broadcast to all network interfaces on the local network */
+      hdr->ethhdr.dest.addr[k] = 0xff;
+      hdr->ethhdr.src.addr[k] = srcaddr->addr[k];
+    }
+    hdr->ethhdr.type = htons(ETHTYPE_ARP);
+    /* send ARP query */
+    result = netif->linkoutput(netif, p);
+    /* free ARP query packet */
+    pbuf_free(p);
+    p = NULL;
+  /* could not allocate pbuf for ARP request */
+  } else {
+    result = ERR_MEM;
+    LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE | 2, ("etharp_request: could not allocate pbuf for ARP request.\n"));
   }
   return result;
 }
Only in ecos_web_cvs/ecos/packages/net/lwip_tcpip/current/src/netif/ppp: CVS
Only in ecos_web_cvs/ecos/packages/net/lwip_tcpip/current/tests: CVS
diff -ru ecos_web_cvs/ecos/packages/net/lwip_tcpip/current/tests/httpd.c ecos/ecos/packages/net/lwip_tcpip/current/tests/httpd.c
--- ecos_web_cvs/ecos/packages/net/lwip_tcpip/current/tests/httpd.c	2006-03-02 19:28:12.000000000 +0100
+++ ecos/ecos/packages/net/lwip_tcpip/current/tests/httpd.c	2004-05-04 14:32:33.000000000 +0200
@@ -250,7 +250,7 @@
   sys_thread_new(httpd_init, (void*)"httpd",7);  
 }
 
-#define STACK_SIZE 0x4000
+#define STACK_SIZE 0x1000
 static char stack[STACK_SIZE];
 static cyg_thread thread_data;
 static cyg_handle_t thread_handle;
diff -ru ecos_web_cvs/ecos/packages/net/lwip_tcpip/current/tests/socket.c ecos/ecos/packages/net/lwip_tcpip/current/tests/socket.c
--- ecos_web_cvs/ecos/packages/net/lwip_tcpip/current/tests/socket.c	2006-03-02 19:28:12.000000000 +0100
+++ ecos/ecos/packages/net/lwip_tcpip/current/tests/socket.c	2005-09-16 13:37:37.000000000 +0200
@@ -68,7 +68,7 @@
   sys_thread_new(socket_thread, (void*)"socket",7);
 }
 
-#define STACK_SIZE 0x4000
+#define STACK_SIZE 0x1000
 static char stack[STACK_SIZE];
 static cyg_thread thread_data;
 static cyg_handle_t thread_handle;
diff -ru ecos_web_cvs/ecos/packages/net/lwip_tcpip/current/tests/tcpecho.c ecos/ecos/packages/net/lwip_tcpip/current/tests/tcpecho.c
--- ecos_web_cvs/ecos/packages/net/lwip_tcpip/current/tests/tcpecho.c	2006-03-02 19:28:12.000000000 +0100
+++ ecos/ecos/packages/net/lwip_tcpip/current/tests/tcpecho.c	2004-05-04 14:32:33.000000000 +0200
@@ -82,7 +82,7 @@
   sys_thread_new(tcpecho_thread, (void*)"tcpecho",7);  
 }
 
-#define STACK_SIZE 0x4000
+#define STACK_SIZE 0x1000
 static char stack[STACK_SIZE];
 static cyg_thread thread_data;
 static cyg_handle_t thread_handle;
diff -ru ecos_web_cvs/ecos/packages/net/lwip_tcpip/current/tests/udpecho.c ecos/ecos/packages/net/lwip_tcpip/current/tests/udpecho.c
--- ecos_web_cvs/ecos/packages/net/lwip_tcpip/current/tests/udpecho.c	2006-03-02 19:28:12.000000000 +0100
+++ ecos/ecos/packages/net/lwip_tcpip/current/tests/udpecho.c	2004-05-04 14:32:33.000000000 +0200
@@ -65,7 +65,7 @@
   sys_thread_new(udpecho_thread, (void*)"udpecho",7);
 }
 
-#define STACK_SIZE 0x4000
+#define STACK_SIZE 0x1000
 static char stack[STACK_SIZE];
 static cyg_thread thread_data;
 static cyg_handle_t thread_handle;

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]