Lwip ARP分析(2)
来源:互联网 发布:网络战略游戏图片 编辑:程序博客网 时间:2024/05/23 01:57
查询ARP缓存
Lwip中查询ARP缓存的函数etharp_query声明如下:
err_t
etharp_query(struct netif *netif,constip4_addr_t *ipaddr,struct pbuf *q)
参数ipaddr是需要进行ARP解析的IP地址,此函数的主要功能就是给发送一个关于此IP地址的ARP请求广播包,或一并发送数据包。下面结合函数前面的注释看看对于ipaddr不同情况的处理:
* If the IP address was not yet in the cache, a pending ARP cache entry
* is added and an ARP request is sent for the given address. The packet
* is queued on this entry.
如果IP地址不在ARP缓存表中,一个新的ARP表项会被创建,并设置为pending状态,然后一个请求解析此IP地址对应MAC地址的ARP请求包会被广播出去。需要发送的数据包挂载在此ARP表项的q队列指针上。
* If the IP address was already pending in the cache, a new ARP request
* is sent for the given address. The packet is queued on this entry.
如果IP地址在ARP缓存表中,并且为pending状态,那么也会发送一个ARP请求广播包出去,并且挂载要发送的数据到缓存项的q队列指针上。
* If the IP address was already stable in the cache, and a packet is
* given, it is directly sent and no ARP request is sent out.
*
* If the IP address was already stable in the cache, and no packet is
* given, an ARP request is sent out.
如果IP地址在ARP缓存表中,并且为stable状态,如果有数据包需要发送,则直接发送数据包,不需要发送ARP请求包。如果没有数据包要发送,则发送ARP请求包。
分析完此函数的参数ipaddr,下面来看下函数的主要实现代码:
/* non-unicast address? */
if (ip4_addr_isbroadcast(ipaddr,netif)||
ip4_addr_ismulticast(ipaddr)||
ip4_addr_isany(ipaddr)) {
return ERR_ARG;
}
上述代码是对参数ipaddr进行合法性判断,如果ipaddr为广播地址或者多播地址,或者空的IP地址,那么直接返回ERR_ARG。
i =etharp_find_entry(ipaddr,ETHARP_FLAG_TRY_HARD,netif);
if (i<0) {
if (q) {
ETHARP_STATS_INC(etharp.memerr);
}
return (err_t)i;
}
/*找到一个匹配的表项或者新建一个表项*/
if (arp_table[i].state == ETHARP_STATE_EMPTY) {
is_new_entry =1;
arp_table[i].state = ETHARP_STATE_PENDING;
arp_table[i].netif = netif;
}
首先调用etharp_find_entry函数查找ARP缓存表中是否有ipaddr对应的表项,注意参数flags设置为ETHARP_FLAG_TRY_HARD,意味着即使没有找到空闲的ARP表项,也会回收一个表项。返回值i表示ARP表项的索引值,如果i小于0,在当前这种情况下,应该只有一种可能就是ARP缓存表里面全是静态ARP表项。在没有找到可用的表项情况下,如果还有未发送的数据挂载在q队列上,增加etharp.memerr统计。
如果找到的ARP表项状态为ETHARP_STATE_EMPTY,说明是一个新建的ARP表项,变量is_new_entry置为1,ARP表项状态设置为ETHARP_STATE_PENDING。
if (is_new_entry||(q ==NULL)) {
result =etharp_request(netif,ipaddr);
if (result!= ERR_OK) {
/* ARP request couldn't be sent */
/* We don't re-send arp request in etharp_tmr, but we still queue packets,
since this failure could be temporary, and the next packet calling
etharp_query again could lead to sending the queued packets. */
}
if (q ==NULL) {
return result;
}
}
上面这段代码是调用etharp_request函数发送一个ARP请求包。变量is_new_entry为1代表着是一个新建的ARP缓存表,当然需要发送一个关于ipaddr的ARP请求广播包出去。指针q等于NULL,意味着没有数据需要发送,因此前面发送了ARP请求之后就可以直接返回。
在etharp_request返回值不ok的情况下,不需要再发送ARP请求包,将要发送的数据挂载到q队列上面或者直接发送出去,具体看后面代码如何处理的。
下面的代码片段是处理在q不为NULL,即有数据需要发送的情况下的处理:
if (arp_table[i].state >= ETHARP_STATE_STABLE) {
result =etharp_send_ip(netif,q,srcaddr,&(arp_table[i].ethaddr));
} else if (arp_table[i].state == ETHARP_STATE_PENDING) {
p =q;
while (p) {
if (p->type !=PBUF_ROM) {
copy_needed = 1;
break;
}
p =p->next;
}
if (copy_needed) {
p =pbuf_alloc(PBUF_RAW_TX,p->tot_len,PBUF_RAM);
if (p!= NULL) {
if (pbuf_copy(p,q)!= ERR_OK) {
pbuf_free(p);
p =NULL;
}
}
} else {
p =q;
pbuf_ref(p);
}
如果找到的ARP表项状态为stable,则直接调用etharp_send_ip函数将数据包发送出去。
对于状态为pending的表项需要将缓存数据发送到q队列上。Pbuf有四种类型,对于PBUF_REF、PBUF_POOL和PBUF_RAM这三种类型的pbuf数据包不能直接挂载在队列q上面,需要将数据拷贝到新申请的PBUF_RAM类型的pbuf中,然后将此新的pbuf挂载到q上。
while循环就是查找待发送数据pbuf是否有上述的三种类型中任意一个,找到的话就设置copy_needed为1,并break退出。对于一个数据包它可能使用pbuf任意四种类型,并将它们串在一起。
如果copy_needed为1,则调用pbuf_alloc函数申请一个类型为PBUF_RAM的pbuf p,并将q队列上的数据都拷贝到p上;否则只需要将p的引用计数加1即可。
if (p!= NULL) { /*有数据包需要挂载*/
#if ARP_QUEUEING
struct etharp_q_entry*new_entry;
new_entry =(struct etharp_q_entry*)memp_malloc(MEMP_ARP_QUEUE);
if (new_entry!= NULL) {
new_entry->next = 0;
new_entry->p = p;
if (arp_table[i].q != NULL) {
struct etharp_q_entry*r;
r =arp_table[i].q;
qlen++;
while (r->next !=NULL) {
r =r->next;
qlen++;
}
r->next = new_entry;
} else {
arp_table[i].q = new_entry;
}
#if ARP_QUEUE_LEN
if (qlen>=ARP_QUEUE_LEN) {
struct etharp_q_entry*old;
old =arp_table[i].q;
arp_table[i].q = arp_table[i].q->next;
pbuf_free(old->p);
memp_free(MEMP_ARP_QUEUE,old);
}
#endif
指针p不为NULL,说明有数据需要挂载到此ARP表项的队列q上。首先从内存池中申请一个MEMP_ARP_QUEUE类型的一片内存给new_entry,并将要发送的数据包p挂载到new_entry上。
如果此ARP表项上之前已挂载有未发送的数据,那么将此新的new_entry链入在链表的最后面;如果此ARP表项上之前没有挂载未发送的数据,那么直接将队列q指针指向new_entry即可。
如果挂载了新的new_entry之后,整个队列项的长度qlen超过ARP_QUEUE_LEN,则选择将q链表头的队列项移除,也就是丢掉最老的未发送的数据包。
ARP工作流程
接收到数据包时ARP的工作流程:
如果接收到的数据包是IP包,则首先会调用etharp_update_arp_entry函数更新ARP缓存表,然后将数据传送到IP层。
如果接收到的数据包是ARP包,则首先更新ARP缓存表,然后判断ARP包的类型。如果是ARP应答包,则不作任何处理,因为前面已经更新ARP表了;如果是ARP请求包,则判断是不是给自己的,如果是发给自己的ARP请求包,则回一个相应的ARP应答包,如果不是给自己的,则忽略就行。
- Lwip ARP分析(2)
- lwip源码分析2----ARP
- lwip源码分析2----ARP
- LwIP源码分析2-ARP
- lwIP ARP协议分析
- lwIP ARP协议分析
- lwIP ARP协议分析
- Lwip ARP分析(1)
- Lwip入门(2)--ARP
- lwIP ARP协议分析0
- lwIP ARP协议分析1
- lwip ARP相关处理(2)
- lwIP ARP协议分析1 (转)
- LWIP手记【A】arp流程之2
- lwip之ARP协议
- LWIP[转]LwIP BUG之ARP缓存
- Lwip--ARP协议(ARP数据包处理)
- lwIP分析
- Spring学习02--属性编辑器
- 如何在 Ubuntu 中检查 CPU ,显卡,内存,硬盘的使用情况及温度等信息
- 进程的执行退出函数
- LeetCode 149 Max Points on a Line
- Unity中的贝塞尔曲线思路及实现
- Lwip ARP分析(2)
- 测试
- 深度学习框架Caffe源码解析
- POJ2217——Secretary
- 第十六周项目2-大数据集上排序算法性能的体验
- 二叉树相关的面试题<一>
- P1540 机器翻译 NOIP2010提高D1
- 深入理解计算机系统:链接
- 2016年12月13日 神途发布网新服预告