【lwip】08-ARP协议一图笔记及源码实现( 七 )

8.8.5 etharp_find_entry():查找可被新建的arp entry

  • 优先寻找匹配IP(和网卡)的arp entry 。
  • 然后再寻找最不重要的arp entry 。优先被覆盖的顺序:empty entry > oldest stable entry > oldest pending entry without queued packets > oldest pending entry with queued packets
/** * Search the ARP table for a matching or new entry. * * If an IP address is given, return a pending or stable ARP entry that matches * the address. If no match is found, create a new entry with this address set, * but in state ETHARP_EMPTY. The caller must check and possibly change the * state of the returned entry. * * If ipaddr is NULL, return a initialized new entry in state ETHARP_EMPTY. * * In all cases, attempt to create new entries from an empty entry. If no * empty entries are available and ETHARP_FLAG_TRY_HARD flag is set, recycle * old entries. Heuristic choose the least important entry for recycling. * * @param ipaddr IP address to find in ARP cache, or to add if not found. * @param flags See @ref etharp_state * @param netif netif related to this address (used for NETIF_HWADDRHINT) * * @return The ARP entry index that matched or is created, ERR_MEM if no * entry is found or could be recycled. */static s16_tetharp_find_entry(const ip4_addr_t *ipaddr, u8_t flags, struct netif *netif){s16_t old_pending = ARP_TABLE_SIZE; /* pending态没有阻塞数据最老的arp entry */s16_t old_stable = ARP_TABLE_SIZE; /* 有效态最老的arp entry */s16_t empty = ARP_TABLE_SIZE;s16_t i = 0;s16_t old_queue = ARP_TABLE_SIZE; /* pending态有阻塞数据最老的arp entry *//* 对应的age */u16_t age_queue = 0, age_pending = 0, age_stable = 0;LWIP_UNUSED_ARG(netif); /* 防止编译警告 *//*** a) do a search through the cache, remember candidates* b) select candidate entry* c) create new entry*//* a) in a single search sweep, do all of this* 1) remember the first empty entry (if any)* 2) remember the oldest stable entry (if any)* 3) remember the oldest pending entry without queued packets (if any)* 4) remember the oldest pending entry with queued packets (if any)* 5) search for a matching IP entry, either pending or stable*until 5 matches, or all entries are searched for.*/for (i = 0; i < ARP_TABLE_SIZE; ++i) { /* 遍历ARP缓存表 */u8_t state = arp_table[i].state;if ((empty == ARP_TABLE_SIZE) && (state == ETHARP_STATE_EMPTY)) {/* 检索到空闲的arp entry */LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_find_entry: found empty entry %d\n", (int)i));/* 记住第一个空闲的arp entry */empty = i;} else if (state != ETHARP_STATE_EMPTY) { /* 当前遍历到的arp entry不是空闲态 */LWIP_ASSERT("state == ETHARP_STATE_PENDING || state >= ETHARP_STATE_STABLE",state == ETHARP_STATE_PENDING || state >= ETHARP_STATE_STABLE);if (ipaddr && ip4_addr_eq(ipaddr, &arp_table[i].ipaddr) /* 匹配当前arp entry的IP */#if ETHARP_TABLE_MATCH_NETIF&& ((netif == NULL) || (netif == arp_table[i].netif)) /* 匹配当前arp entry的网卡 */#endif /* ETHARP_TABLE_MATCH_NETIF */) {LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: found matching entry %d\n", (int)i));/* 已经找到符合要求 , 且已经存在的arp entry了 */return i;}if (state == ETHARP_STATE_PENDING) { /* peding态 */if (arp_table[i].q != NULL) { /* 有阻塞数据包 */if (arp_table[i].ctime >= age_queue) { /* 当前arp entry更老 *//* 更新值 */old_queue = i;age_queue = arp_table[i].ctime;}} else{ /* 没有阻塞数据包 */if (arp_table[i].ctime >= age_pending) { /* 当前arp entry更老 *//* 更新值 */old_pending = i;age_pending = arp_table[i].ctime;}}} else if (state >= ETHARP_STATE_STABLE) { /* 有效态 */#if ETHARP_SUPPORT_STATIC_ENTRIES/* 不要处理静态的arp entry */if (state < ETHARP_STATE_STATIC)#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */{if (arp_table[i].ctime >= age_stable) { /* 当前arp entry更老 *//* 更新值 */old_stable = i;age_stable = arp_table[i].ctime;}}}}}/* 已经遍历ARP缓存表完毕,没找到匹配IP已有的arp entry 。*/if (((flags & ETHARP_FLAG_FIND_ONLY) != 0) || /* 只读模式,没有匹配的arp entry就直接退出 */((empty == ARP_TABLE_SIZE) && ((flags & ETHARP_FLAG_TRY_HARD) == 0))) { /* 非只读模式,但是没找到空闲arp entry,又不能覆盖,也直接退出 */LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: no empty entry found and not allowed to recycle\n"));return (s16_t)ERR_MEM;}/* b) choose the least destructive entry to recycle:* 1) empty entry* 2) oldest stable entry* 3) oldest pending entry without queued packets* 4) oldest pending entry with queued packets** { ETHARP_FLAG_TRY_HARD is set at this point }*/if (empty < ARP_TABLE_SIZE) { /* 找到空闲的arp entry */i = empty;LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: selecting empty entry %d\n", (int)i));} else { /* 没找到空闲的arp entry */if (old_stable < ARP_TABLE_SIZE) { /* 2) 先找最老的有效态arp entry *//* 回收最老的有效态arp entry */i = old_stable;LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: selecting oldest stable entry %d\n", (int)i));/* stable态arp entry上不应该存在排队的数据包 */LWIP_ASSERT("arp_table[i].q == NULL", arp_table[i].q == NULL);} else if (old_pending < ARP_TABLE_SIZE) { /* 3) 再找没有阻塞数据的pending态的arp entry *//* 回收这条arp entry */i = old_pending;LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: selecting oldest pending entry %d (without queue)\n", (int)i));} else if (old_queue < ARP_TABLE_SIZE) {/* 4) 最后才找有阻塞数据的pending态的arp entry *//* 回收这条arp entry */i = old_queue;LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: selecting oldest pending entry %d, freeing packet queue %p\n", (int)i, (void *)(arp_table[i].q)));} else { /* 没找到 */LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: no empty or recyclable entries found\n"));return (s16_t)ERR_MEM;}LWIP_ASSERT("i < ARP_TABLE_SIZE", i < ARP_TABLE_SIZE);/* 找到需要需要回收的arp entry,将其回收 */etharp_free_entry(i);}/* 确保当前arp entry已经为空闲态,且索引不超限制 */LWIP_ASSERT("i < ARP_TABLE_SIZE", i < ARP_TABLE_SIZE);LWIP_ASSERT("arp_table[i].state == ETHARP_STATE_EMPTY",arp_table[i].state == ETHARP_STATE_EMPTY);if (ipaddr != NULL) {/* 保存IP到新建arp entry */ip4_addr_copy(arp_table[i].ipaddr, *ipaddr);}arp_table[i].ctime = 0; /* 重置age */#if ETHARP_TABLE_MATCH_NETIFarp_table[i].netif = netif; /* 保存网卡到新建arp entry */#endif /* ETHARP_TABLE_MATCH_NETIF */return (s16_t)i; /* 返回找到符合要求的arp entry */}

推荐阅读