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置为1ARP表项状态设置为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_entry1代表一个新建的ARP缓存表,当然需要发送一个关于ipaddrARP请求广播包出去。指针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_needed1,并break退出对于一个数据包它可能使用pbuf任意四种类型并将它们串在一起


如果copy_needed1则调用pbuf_alloc函数申请一个类型为PBUF_RAMpbuf 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应答,如果不是给自己的,则忽略就行




0 0