📄 etharp.c
字号:
/** * @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) and supports * Gratuitious ARP from RFC3220 (IP Mobility Support for IPv4) section 4.6. *//* * 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: * - 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"#include "lwip/ip.h"#include "lwip/stats.h"/* ARP needs to inform DHCP of any ARP replies? */#if (LWIP_DHCP && DHCP_DOES_ARP_CHECK)# include "lwip/dhcp.h"#endif/** 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, (2 * 10) seconds = 20 seconds. */#define ARP_MAXPENDING 2#define HWTYPE_ETHERNET 1/** ARP message types */#define ARP_REQUEST 1#define ARP_REPLY 2#define ARPH_HWLEN(hdr) (ntohs((hdr)->_hwlen_protolen) >> 8)#define ARPH_PROTOLEN(hdr) (ntohs((hdr)->_hwlen_protolen) & 0xff)#define ARPH_HWLEN_SET(hdr, len) (hdr)->_hwlen_protolen = htons(ARPH_PROTOLEN(hdr) | ((len) << 8))#define ARPH_PROTOLEN_SET(hdr, len) (hdr)->_hwlen_protolen = htons((len) | (ARPH_HWLEN(hdr) << 8))enum etharp_state { ETHARP_STATE_EMPTY, ETHARP_STATE_PENDING, ETHARP_STATE_STABLE};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;#endif 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);#define ARP_INSERT_FLAG 1static struct pbuf *update_arp_entry(struct netif *netif, struct ip_addr *ipaddr, struct eth_addr *ethaddr, u8_t flags);#if ARP_QUEUEINGstatic struct pbuf *etharp_enqueue(s8_t i, struct pbuf *q);static u8_t etharp_dequeue(s8_t i);#endif/** * Initializes ARP module. */voidetharp_init(void){ s8_t i; /* clear ARP entries */ for(i = 0; i < ARP_TABLE_SIZE; ++i) { arp_table[i].state = ETHARP_STATE_EMPTY;#if ARP_QUEUEING arp_table[i].p = NULL;#endif arp_table[i].ctime = 0; }}/** * Clears expired entries in the ARP table. * * This function should be called every ETHARP_TMR_INTERVAL microseconds (10 seconds), * in order to expire entries in the ARP table. */voidetharp_tmr(void){ s8_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? */ 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)); goto empty; /* 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)); empty: /* empty old entry */ arp_table[i].state = ETHARP_STATE_EMPTY;#if ARP_QUEUEING /* and empty packet queue */ if (arp_table[i].p != NULL) { /* remove any queued packet */ LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_timer: freeing entry %u, packet queue %p.\n", i, (void *)(arp_table[i].p))); pbuf_free(arp_table[i].p); arp_table[i].p = NULL; }#endif } }}/** * 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. */static s8_tfind_arp_entry(void){ s8_t i, j; u8_t maxtime = 0; 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; } /* clean up the recycled stable entry */ if (arp_table[i].state == ETHARP_STATE_STABLE) {#if ARP_QUEUEING /* free packets on queue */ etharp_dequeue(i);#endif LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("find_arp_entry: recycling oldest stable entry %u\n", i)); arp_table[i].state = ETHARP_STATE_EMPTY; arp_table[i].ctime = 0; } LWIP_DEBUGF(ETHARP_DEBUG, ("find_arp_entry: returning %u\n", i)); return i;}#if ARP_QUEUEING/* * Enqueues a pbuf (chain) on an ARP entry. * * Places the pbuf (chain) on the queue (if space allows). The * caller may safely free the pbuf (chain) afterwards, as the * pbufs will be referenced by the queue and copies are made of * pbufs referencing external payloads. * * @ i the ARP entry index * @arg q the pbuf (chain) to be queued on the ARP entry * * @return Returns the new head of queue of the ARP entry. * */static struct pbuf *etharp_enqueue(s8_t i, struct pbuf *q){ /* any pbuf to queue? */ if (q != NULL) {/* queue later packet over earliers? TODO: Implement multiple pbuf queue */#if ARP_QUEUE_FIRST == 0 /* remove any pbufs on queue */ u8_t deq = etharp_dequeue(i); if (deq > 0) LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE | 3, ("etharp_query: dequeued %u pbufs from ARP entry %u. Should not occur.\n", deq, i));#endif /* packet can be queued? TODO: Implement multiple pbuf queue */ if (arp_table[i].p == NULL) { /* copy any PBUF_REF referenced payloads into PBUF_RAM */ q = pbuf_take(q); /* add pbuf to queue */ arp_table[i].p = q; /* pbuf (chain) now queued, increase the reference count */ pbuf_ref(q); LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE | DBG_STATE, ("etharp_query: queued packet %p on ARP entry %u.\n", (void *)q, i)); } } return arp_table[i].p;}/** * Dequeues any pbufs queued on an ARP entry * * @return number of pbufs removed from the queue * * TODO: decide what is a sensible return value? */static u8_tetharp_dequeue(s8_t i){ /* queued packets on a stable entry (work in progress) */ if (arp_table[i].p != NULL) { /* queue no longer references pbuf */ pbuf_free(arp_table[i].p); arp_table[i].p = NULL; return 1; } else { return 0; }}#endif/** * 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 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, * 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. * * @see pbuf_free() */static struct pbuf *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", 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 (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;/* this is where we will send out queued packets! */#if ARP_QUEUEING /* get the first packet on the queue (if any) */ p = arp_table[i].p; /* (another) queued packet present? */ while (p != NULL) { struct pbuf *q, *n; /* search for second packet on queue (n) */ q = p; while (q->tot_len > q->len) { LWIP_ASSERT("q->next != NULL (while q->tot_len > q->len)", q->next != NULL); /* proceed to next pbuf of this packet */ q = q->next; } /* { q = last pbuf of this packet, q->tot_len == q->len } */ LWIP_ASSERT("q->tot_len == q->len", q->tot_len == q->len); /* remember next packet on queue */ n = q->next; /* { n = first pbuf of next packet, or NULL if no next packet } */ /* terminate this packet pbuf chain */ q->next = NULL; /* 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); /* proceed to next packet on queue */ p = n; } /* NULL attached buffer*/ arp_table[i].p = NULL;#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 an entry? */ if ((ETHARP_ALWAYS_INSERT) || (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) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -