/** * 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. * * @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, struct ip_addr *ipaddr, struct pbuf *q) { 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; } /* 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; } /* { 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 } 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; }
/** * 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. * * @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. * * @return NULL. * * @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; } } } /* 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 */ 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); #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; }