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


比如 , 每个IP层的数据包都会先遍历ARP缓存表,如果找到有效的条目后,直接使用该条目 , 然后会继续调用etharp_output_to_arp_index()把数据发送出去,源码如下:

  • 在发送IP包时检查当前被使用的ARP entry是否块过期,如果快过期,需要发起ARP请求更新当前条目,更新有两种级别:
    • ARP_AGE_REREQUEST_USED_UNICAST:默认在条目超时前30秒,发起单播级别的ARP请求,减少不必要的广播 。
    • ARP_AGE_REREQUEST_USED_BROADCAST:默认在条目超时前15秒,发起广播级别的ARP请求 。
/* 为避免由于ARP表项超时导致稳定使用的连接中断,需要在ARP表项过期前重新请求,更新ARP缓存表 */#define ARP_AGE_REREQUEST_USED_UNICAST(ARP_MAXAGE - 30)#define ARP_AGE_REREQUEST_USED_BROADCAST (ARP_MAXAGE - 15)/* Just a small helper function that sends a pbuf to an ethernet address in the arp_table specified by the index 'arp_idx'. */static err_tetharp_output_to_arp_index(struct netif *netif, struct pbuf *q, netif_addr_idx_t arp_idx){LWIP_ASSERT("arp_table[arp_idx].state >= ETHARP_STATE_STABLE",arp_table[arp_idx].state >= ETHARP_STATE_STABLE);/* 在entry快过期前 , 发起ARP请求进行更新 。为了防止在这段时间频繁发起ARP请求,所以引入有效过渡态 。这里为有效态才能发起ARP请求 。*/if (arp_table[arp_idx].state == ETHARP_STATE_STABLE) {if (arp_table[arp_idx].ctime >= ARP_AGE_REREQUEST_USED_BROADCAST) {/* 使用广播级别(过期前15秒) , 发起标准ARP请求 */if (etharp_request(netif, &arp_table[arp_idx].ipaddr) == ERR_OK) {arp_table[arp_idx].state = ETHARP_STATE_STABLE_REREQUESTING_1; /* 更新为有效过渡态1 */}} else if (arp_table[arp_idx].ctime >= ARP_AGE_REREQUEST_USED_UNICAST) {/* 发出单播ARP请求(过期前30秒),以防止不必要的广播 */if (etharp_request_dst(netif, &arp_table[arp_idx].ipaddr, &arp_table[arp_idx].ethaddr) == ERR_OK) {arp_table[arp_idx].state = ETHARP_STATE_STABLE_REREQUESTING_1; /* 更新为有效过渡态1 */}}}/* IP层发送数据 */return ethernet_output(netif, q, (struct eth_addr *)(netif->hwaddr), &arp_table[arp_idx].ethaddr, ETHTYPE_IP);}8.4.6 ARP缓存表超时处理ARP缓存表每条映射记录都是有有效期的(静态除外),在struct etharp_entryu16_t ctime;这个字段中记录这点钱entry的生存时间 。
ARP超时处理函数是etharp_tmr(),是一个周期定时函数 。
相关宏:
ARP_TMR_INTERVAL:该函数的节拍为ARP_TMR_INTERVAL,默认为1000,即是1秒跑一次 。
ARP_MAXAGE:用于限制ARP条目最大生存时间,默认为300 , 且节拍为1秒,所以ARP的entry默认最大生存时间是5分钟 。
ARP_MAXPENDING:限制ARP请求响应超时,也是重发ARP请求的次数,默认为5,且节拍为1秒,所以ARP请求响应默认超时为5秒 。
/** * 清除ARP表中过期的表项 * 该函数应该每隔ARP_TMR_INTERVAL毫秒(1秒)调用一次,以使ARP表项过期 */voidetharp_tmr(void){int i;LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_timer\n"));/* 遍历、删除ARP表中过期的表项 */for (i = 0; i < ARP_TABLE_SIZE; ++i) {u8_t state = arp_table[i].state;if (state != ETHARP_STATE_EMPTY /* 跳过空闲态的entry */#if ETHARP_SUPPORT_STATIC_ENTRIES&& (state != ETHARP_STATE_STATIC) /* 跳过静态的entry */#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */) {arp_table[i].ctime++; /* 记录当前entry的生存时间 */if ((arp_table[i].ctime >= ARP_MAXAGE) ||((arp_table[i].state == ETHARP_STATE_PENDING)&&(arp_table[i].ctime >= ARP_MAXPENDING))) { /* entry生存时间超时或者ARP请求超时都要清空本entry *//* pending or stable entry has become old! */LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_timer: expired %s entry %d.\n",arp_table[i].state >= ETHARP_STATE_STABLE ? "stable" : "pending", i));/* 清理刚刚过期的条目 */etharp_free_entry(i);} else if (arp_table[i].state == ETHARP_STATE_STABLE_REREQUESTING_1) {/* 过渡态1更新到过渡态2,为了防止上层在两秒能发起多次ARP更新请求 */arp_table[i].state = ETHARP_STATE_STABLE_REREQUESTING_2;} else if (arp_table[i].state == ETHARP_STATE_STABLE_REREQUESTING_2) {/* 恢复到有效态 。允许ARP */arp_table[i].state = ETHARP_STATE_STABLE;} else if (arp_table[i].state == ETHARP_STATE_PENDING) {/* 还没收到ARP响应,重发ARP请求 */etharp_request(arp_table[i].netif, &arp_table[i].ipaddr);}}}}8.4.7 ARP缓存表entry更新能触发ARP缓存表entry更新的情况:
  1. ARP超时处理 。把过期或者ARP请求超时的arp entry删除 。
  2. IP层发送数据 。经过ARP协议时: