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


这个队列的数据结构:

  • 在memp.h的MEMP_ARP_QUEUE内存池中有这个数据结构的内存资源 。共有MEMP_NUM_ARP_QUEUE个,默认为30个 。
#if ARP_QUEUEINGstruct etharp_q_entry {struct etharp_q_entry *next; /* 下一个节点 */struct pbuf *p; /* pbuf */};#endif /* ARP_QUEUEING */8.4.5 ARP缓存表entry状态信息u8_t state;
/** ARP states */enum etharp_state {ETHARP_STATE_EMPTY = 0, /* 空闲态 */ETHARP_STATE_PENDING, /* pending态 */ETHARP_STATE_STABLE, /* 有效态 */ETHARP_STATE_STABLE_REREQUESTING_1, /* 有效过渡态1 */ETHARP_STATE_STABLE_REREQUESTING_2 /* 有效过渡态2 */#if ETHARP_SUPPORT_STATIC_ENTRIES, ETHARP_STATE_STATIC /* 静态entry */#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */};当前netry的状态信息:
  • ETHARP_STATE_EMPTY:空状态 。当前entry资源无效,可以被填充使用 。
  • ETHARP_STATE_PENDING:PENDING态 。当前entry正在ARP请求,但是还没收到ARP响应 。
  • ETHARP_STATE_STABLE:有效态 。当前entry记录的IP地址与MAC地址映射有效 。
  • ETHARP_STATE_STABLE_REREQUESTING_1:有效过渡态1 。就是为了防止entry块过期前频繁发起ARP请求 。
  • ETHARP_STATE_STABLE_REREQUESTING_2:有效过渡态2 。
  • ETHARP_STATE_STATIC:静态条目 。手动配置的ARP映射,一直有效 。
当表项是ETHARP_STATE_STABLE的时候又发送一个ARP请求包,那么表项状态会暂时被设置为THARP_STATE_STABLE_REREQUESTING_1,然后被设置为ETHARP_STATE_STABLE_REREQUESTING_2状态,这些是一个过渡状态,当收到ARP应答后,表项又会被设置为ETHARP_STATE_STABLE,这样能保持表项的有效 。
比如,每个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);}

经验总结扩展阅读