🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
etharp\_update\_arp\_entry()函数是用于更新ARP缓存表的,它会在收到一个ARP数据包的时候被调用,它会先查找一个ARP表项,如果没有找到这个ARP表项的记录,就会去新建一个ARP表项,然后重置ARP表项的参数(状态、网卡。IP地址与对应的MAC地址以及生存时间等),然后检测ARP表项中是否挂载数据包,如果有就将这些数据包发送出去,其源码具体见代码清单 10‑10。 ``` 1 static err_t 2 etharp_update_arp_entry(struct netif *netif, (1) 3 const ip4_addr_t *ipaddr, (2) 4 struct eth_addr *ethaddr, (3) 5 u8_t flags) (4) 6 { 7 s16_t i; 8 9 /* non-unicast address? */ 10 if (ip4_addr_isany(ipaddr) || 11 ip4_addr_isbroadcast(ipaddr, netif) || 12 ip4_addr_ismulticast(ipaddr)) (5) 13 { 14 return ERR_ARG; 15 } 16 /* 查找或者创建ARP表项,并且返回索引值 */ 17 i = etharp_find_entry(ipaddr, flags, netif); (6) 18 19 /* 如果索引值不合法,更新ARP表项失败 */ 20 if (i < 0) 21 { 22 return (err_t)i; 23 } 24 25 26 /* 设置表项状态为ETHARP_STATE_STABLE */ 27 arp_table[i].state = ETHARP_STATE_STABLE; (7) 28 29 /* 记录网卡 */ 30 arp_table[i].netif = netif; (8) 31 /* 插入ARP索引树 */ 32 mib2_add_arp_entry(netif, &arp_table[i].ipaddr); (9) 33 34 /* 更新缓存表中的MAC地址 */ 35 SMEMCPY(&arp_table[i].ethaddr, ethaddr, ETH_HWADDR_LEN); (10) 36 /* 重置生存时间 */ 37 arp_table[i].ctime = 0; (11) 38 39 /* 如果表项上与未发送的数据包,那就将这些数据包发送出去 */ 40 #if ARP_QUEUEING 41 while (arp_table[i].q != NULL) 42 { 43 struct pbuf *p; 44 /* 定义q指向ARP表项中的数据包缓存队列 */ 45 struct etharp_q_entry *q = arp_table[i].q; (12) 46 /* 指向下一个数据包节点 */ 47 arp_table[i].q = q->next; (13) 48 /* 获取pbuf数据包 */ 49 p = q->p; (14) 50 /* 释放MEMP_ARP_QUEUE类型的内存块 */ 51 memp_free(MEMP_ARP_QUEUE, q); (15) 52 #else /* ARP_QUEUEING */ 53 if (arp_table[i].q != NULL) 54 { 55 struct pbuf *p = arp_table[i].q; (16) 56 arp_table[i].q = NULL; 57 #endif /* ARP_QUEUEING */ 58 /* 发送缓存队列的数据包 */ 59 ethernet_output(netif, p, 60 (struct eth_addr *)(netif->hwaddr), 61 ethaddr, 62 ETHTYPE_IP); (17) 63 /* 释放pbuf数据包的内存空间 */ 64 pbuf_free(p); (18) 65 } 66 return ERR_OK; 67 } ``` (1):对应表项中的网卡。 (2):对应表项中的IP地址。 (3):对应表项中的MAC地址。 (4):表项的更新方式,动态表项有两种方式,分别为ETHARP_FLAG_TRY_HARD和ETHARP_FLAG_FIND_ONLY。前者表示无论如何都要创建一个表项,如果ARP缓存表中没有空间了,那就需要回收较老的表项,将他们删除,然后建立新的表项。而如果是后者,就让内核尽量更新表项,如果ARP缓存表中没有空间了,那么也无能为力,实在是添加不了新的表项。 (5):IP地址验证,ARP数据包中的目标IP地址不能是广播、多播地址。 (6):调用etharp_find_entry()函数查找或者创建ARP表项,并且返回索引值,如果索引值不合法,表示更新ARP表项失败,该函数比较复杂,此处就不做过多讲解,想要了解的可以在源码中查看该函数。 (7):设置表项状态为ETHARP_STATE_STABLE。 (8):记录网卡信息。 (9):插入ARP索引树。 (10):更新表项中的MAC地址。 (11):重置表项的生存时间。 (12):如果表项上与未发送的数据包,那就将这些数据包发送出去。这里通过宏定义ARP_QUEUEING采用哪种方式发送数据包,如果定义了缓存数据包队列,那就需要将队列上的所有数据包发送出去,定义q指向ARP表项中的数据包缓存队列。 (13):指向下一个数据包节点。 (14):获取pbuf数据包。 (15):释放MEMP_ARP_QUEUE类型的内存块。 (16):此处是单个数据包挂载到表项上,无需太多操作,直接将数据包获取到,然后发送出去即可。 (17):调用ethernet_output()函数发送挂载在表项上的数据包。 (18):释放pbuf数据包的内存空间。 整个ARP处理的流程示意图具体见图 10-9。 ![](https://box.kancloud.cn/09d58b2545f1daad91f6e30fc0262af9_573x710.png)