As etharp already sends packets from the queue asynchronously, also make it send packet submitted through etharp_output().
etharp_output() now returns err_t.
etharp_query() now sends a packet directly if the IP address is stable.
diff --git a/src/include/netif/etharp.h b/src/include/netif/etharp.h
index 83a722f..0f1eeef 100644
--- a/src/include/netif/etharp.h
+++ b/src/include/netif/etharp.h
@@ -98,7 +98,7 @@
 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);
 
diff --git a/src/netif/etharp.c b/src/netif/etharp.c
index 985d978..c3c0bec 100644
--- a/src/netif/etharp.c
+++ b/src/netif/etharp.c
@@ -514,29 +514,33 @@
  *
  * @return If non-NULL, a packet ready to be sent by caller.
  *
+ * @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_RTE No route to destination (no gateway to external networks).
  */
-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;
+  err_t result;
 
-  /* 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;
+    pbuf_free(q);
+    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)) {
@@ -557,86 +561,67 @@
   }
   /* 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 */
+    /* outside local network? */
     if (!ip_addr_maskcmp(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 gateway available? */
-      else
-      {
-        /* IP destination address outside local network, but no gateway available */
-        /* { packet is discarded } */
-        return NULL;
+      /* no default gateway available? */
+      } else {
+        /* destination unreachable, discard packet */
+        pbuf_free(q);
+        return ERR_RTE;
       }
     }
-
-    /* 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 */
-    }
-  }
+    result = etharp_query(netif, ipaddr, q);
+  } /* else unicast */
 
   /* 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;
+    /* send packet */
+    result = netif->linkoutput(netif, q);
   }
   /* never reached; here for safety */
-  return NULL;
+  pbuf_free(q);
+  return result;
 }
 
 /**
  * Send an ARP request for the given IP address.
  *
- * 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 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, the packet is
+ * directly sent. An ARP request is sent out.
+ * 
  * @param netif The lwIP network interface where 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.
  *
- * @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).
+  *
  * @note Might be used in the future by manual IP configuration
  * as well.
  *
@@ -645,39 +630,71 @@
  */
 err_t etharp_query(struct netif *netif, struct ip_addr *ipaddr, struct pbuf *q)
 {
-  struct eth_addr *srcaddr;
-  struct etharp_hdr *hdr;
+  struct pbuf *p;
   err_t result = ERR_OK;
-  s8_t i;
-  u8_t perform_arp_request = 1;
-  /* prevent 'unused argument' compiler warning if ARP_QUEUEING == 0 */
-  (void)q;
-  srcaddr = (struct eth_addr *)netif->hwaddr;
-  /* bail out if this IP address is pending */
+  s8_t i; /* ARP entry index */
+  u8_t k; /* Ethernet address octet index */
+
+  /* Do three things in this order (by design):
+   * 
+   * 1) send out ARP request
+   * 2) find entry in ARP cache
+   * 3) handle the packet
+   */
+
+  /* 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_query: 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_query: could not allocate pbuf for ARP request.\n"));
+  }
+
+  /* search entry of queried IP address in the ARP cache */
   for (i = 0; i < ARP_TABLE_SIZE; ++i) {
     /* valid ARP cache entry with matching IP address? */
     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 | 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 */
+        LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE | DBG_STATE, ("etharp_query: requested IP already pending in entry %u\n", i));
         /* { i != ARP_TABLE_SIZE } */
         break;
       }
-      /* stable entry? */
       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);
+        LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE | DBG_STATE, ("etharp_query: requested IP already stable in entry %u\n", i));
         /* { i != ARP_TABLE_SIZE } */
         break;
       }
@@ -696,56 +713,43 @@
     /* i is available, create ARP entry */
     arp_table[i].state = ETHARP_STATE_PENDING;
     ip_addr_set(&arp_table[i].ipaddr, ipaddr);
+    arp_table[i].p = NULL;
+    LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_query: added pending entry %u for IP address\n", i));
   }
-  /* { i is now valid } */
+
+  /* { i is either a (new or existing) PENDING or STABLE entry } */
+  
+  /* 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_addr * srcaddr = (struct eth_addr *)netif->hwaddr;
+      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);
 #if ARP_QUEUEING /* queue the given q packet */
-  /* copy any PBUF_REF referenced payloads into PBUF_RAM */
-  /* (the caller assumes the referenced payload can be freed) */
-  q = pbuf_take(q);
-  /* queue packet (even on a stable entry, see above) */
-  pbuf_queue(arp_table[i].p, q);
+    /* pending entry? (either just created or already pending */
+    } else if (arp_table[i].state == ETHARP_STATE_PENDING) {
+      /* copy any PBUF_REF referenced payloads into PBUF_RAM */
+      /* (the caller assumes the referenced payload can be freed) */
+      p = pbuf_take(q);
+      /* queue packet */
+      if (p != NULL) {
+        pbuf_queue(arp_table[i].p, p);
+        LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_query: queued packet %p on ARP entry %d\n", (void *)q, i));
+      } 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; 
+      }
 #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;
-
-      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 (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"));
     }
   }
   return result;
diff --git a/src/netif/ethernetif.c b/src/netif/ethernetif.c
index f7ad781..2b53f65 100644
--- a/src/netif/ethernetif.c
+++ b/src/netif/ethernetif.c
@@ -61,7 +61,6 @@
 static err_t ethernetif_output(struct netif *netif, struct pbuf *p,
              struct ip_addr *ipaddr);
 
-
 static void
 low_level_init(struct netif *netif)
 {
@@ -95,7 +94,6 @@
  *
  */
 
-
 static err_t
 low_level_output(struct ethernetif *ethernetif, struct pbuf *p)
 {
@@ -199,30 +197,10 @@
 ethernetif_output(struct netif *netif, struct pbuf *p,
       struct ip_addr *ipaddr)
 {
-  struct ethernetif *ethernetif;
-  struct pbuf *q;
-  struct eth_hdr *ethhdr;
-  struct eth_addr *dest, mcastaddr;
-  struct ip_addr *queryaddr;
-  err_t err;
-  u8_t i;
   
-  ethernetif = netif->state;
-
-  /* resolve the link destination hardware address */
-  p = etharp_output(netif, ipaddr, p);
-  
-  /* network hardware address obtained? */
-  if (p == NULL)
-  {
-    /* we cannot tell if the packet was sent: the packet could */
-    /* have been queued on an ARP entry that was already pending. */
-  	return ERR_OK;
-  }
-  	
-  /* send out the packet */
-  return low_level_output(ethernetif, p);
-
+ /* resolve hardware address, then send (or queue) packet */
+  return etharp_output(netif, ipaddr, p);
+ 
 }
 
 /*
@@ -240,42 +218,43 @@
 {
   struct ethernetif *ethernetif;
   struct eth_hdr *ethhdr;
-  struct pbuf *p, *q;
+  struct pbuf *p;
 
   ethernetif = netif->state;
   
+  /* move received packet into a new pbuf */
   p = low_level_input(ethernetif);
-
-  if (p != NULL)
-    return;
+  /* no packet could be read, silently ignore this */
+  if (p == NULL) return;
+  /* points to packet payload, which starts with an Ethernet header */
+  ethhdr = p->payload;
 
 #ifdef LINK_STATS
   lwip_stats.link.recv++;
 #endif /* LINK_STATS */
 
   ethhdr = p->payload;
-  q = NULL;
     
   switch (htons(ethhdr->type)) {
-    case ETHTYPE_IP:
-      q = etharp_ip_input(netif, p);
-      pbuf_header(p, -sizeof(struct eth_hdr));
-      netif->input(p, netif);
-      break;
+  /* IP packet? */
+  case ETHTYPE_IP:
+    /* update ARP table */
+    etharp_ip_input(netif, p);
+    /* skip Ethernet header */
+    pbuf_header(p, -sizeof(struct eth_hdr));
+    /* pass to network layer */
+    netif->input(p, netif);
+    break;
       
     case ETHTYPE_ARP:
-      q = etharp_arp_input(netif, ethernetif->ethaddr, p);
+      /* pass p to ARP module  */
+      etharp_arp_input(netif, ethernetif->ethaddr, p);
       break;
     default:
       pbuf_free(p);
       p = NULL;
       break;
   }
-  if (q != NULL) {
-    low_level_output(ethernetif, q);
-    pbuf_free(q);
-    q = NULL;
-  }
 }
 
 static void