Linux内核分析 - 网络[九]:邻居表
副标题[/!--empirenews.page--] 内核版本:2.6.34 这部分的重点是三个核心的数据结构-邻居表、邻居缓存、代理邻居表,以及NUD状态转移图。 总的来说,要成功添加一条邻居表项,需要满足两个条件:1. 本机使用该表项;2. 对方主机进行了确认。同时,表项的添加 引入了NUD(Neighbour Unreachability Detection)机制,从创建NUD_NONE到可用NUD_REACHABLE需要经历一系列状态转移,而根 据达到两个条件顺序的不同,可以分为两条路线: 先引用再确认- NUD_NONE -> NUD_INCOMPLETE -> NUD_REACHABLE 先确认再引用- NUD_NONE -> NUD_STALE -> NUD_DELAY -> NUD_PROBE -> NUD_REACHABLE 下面还是从接收函数入手,当匹配号协议号是0x0806,会调用ARP模块的接收函数arp_rcv()。 arp_rcv() ARP接收函数 首先是对arp协议头进行检查,比如大小是否足够,头部各数值是否正确等,这里略过代码,直 接向下看。每个协议处理都一样,如果被多个协议占有,则拷贝一份。 if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) goto out_of_mem; NEIGH_CB(skb)实际就是skb->cb,在skb声明为u8 char[48],它用作每个协议模块的私有数据 区(control buffer),每个协议模块可以根据自身需求在其中存储私有数据。而arp模块就利用了它存储控制结构neighbour_cb ,它声明如下,占8字节。这个控制结构在代理ARP中使用工作队列时会发挥作用,sched_next代表下次被调度的时间,flags是 标志。 memset(NEIGH_CB(skb), 0, sizeof(struct neighbour_cb)); struct neighbour_cb { unsigned long sched_next; unsigned int flags; }; 函数最后调用arp_process,其间插入netfilter(关于netfilter,参见前篇:http://hi.csdn.net/link.php? url=http://blog.csdn.net%2Fqy532846454),作为开始处理ARP报文的起点。 return NF_HOOK(NFPROTO_ARP, NF_ARP_IN, skb, dev, NULL, arp_process); arp_process() 这个函数开始对报 文进行处理,首先会从skb中取出arp报头部分的信息,如sha, sip, tha, tip等,这部分可查阅代码,这里略过。ARP不会查询 环路地址和组播地址,因为它们没有对应的mac地址,因此遇到这两类地址,直接退出。 if (ipv4_is_loopback(tip) || ipv4_is_multicast(tip)) goto out; 如果收到的是重复地址检测报文,并且本机占用了检测了地址,则调用arp_send发送响应。对于重复地址 检测报文(ARP报文中源IP为全0),它所带有的邻居表项信息还没通过检测,此时缓存它显然没有意义,也许下一刻就有其它主机 声明它非法,因此,重复地址检测报文中的信息不会加入邻居表中。 if (sip == 0) { if (arp->ar_op == htons(ARPOP_REQUEST) && inet_addr_type(net, tip) == RTN_LOCAL && !arp_ignore(in_dev, sip, tip)) arp_send(ARPOP_REPLY, ETH_P_ARP, sip, dev, tip, sha, dev->dev_addr, sha); goto out; } 下面要处理的地址解析报文,并且要解析的地址在路由表中存在 if (arp->ar_op == htons (ARPOP_REQUEST) && ip_route_input(skb, tip, sip, 0, dev) == 0) 第一种情况,如果要解析的是本机地址,则调用neigh_event_ns() ,并根据查到的邻居表项n发送ARP响应报文。这里neigh_event_ns的功能是在arp_tbl中查找是否已含有对方主机的地址信息, 如果没有,则进行创建,然后会调用neigh_update来更新状态。收到对方主机的请求报文,会导致状态迁移到NUD_STALE。 if (addr_type == RTN_LOCAL) { …… if (!dont_send) { n = neigh_event_ns(&arp_tbl, sha, &sip, dev); if (n) { arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,dev->dev_addr,sha); neigh_release(n); } } goto out; } #NUD_INCOMPLETE也迁移到NUD_STALE,作何解释? 第二种情况,如果要解析的不是本机地址,则要判断是否支持 转发,是否支持代理ARP(代理ARP是陆由器的功能,因此能转发是先决条件),如果满足条件,那么按照代理ARP流程处理。首先 无论如何,主机得通了存在这样一个邻居,因此要在在arp_tbl中查找并(如果不存在)创建相应邻居表项;然后,对于代理ARP, 这个流程实际上会执行两遍,第一遍走else部分,第二遍走if部分。第一次的else代码段会触发定时器,通过定时器引发报文重 新执行arp_process函数,并走if部分。 -第一遍的else部分:调用pneigh_enqueue()将报文skb加入tbl->proxy_queue队 列,同时设置NEIGH_CB(skb)的值,具体可看后见的代理表项处理。 -第二遍的if部分,发送ARP响应报文,行使代理ARP的功 能。 else if (IN_DEV_FORWARD(in_dev)) { if (addr_type == RTN_UNICAST && (arp_fwd_proxy(in_dev, dev, rt) || arp_fwd_pvlan(in_dev, dev, rt, sip, tip) || pneigh_lookup(&arp_tbl, net, &tip, dev, 0))) { n = neigh_event_ns(&arp_tbl, sha, &sip, dev); if (n) neigh_release(n); if (NEIGH_CB(skb)->flags & LOCALLY_ENQUEUED || skb->pkt_type == PACKET_HOST || in_dev->arp_parms->proxy_delay == 0) { arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,dev->dev_addr,sha); } else { pneigh_enqueue(&arp_tbl, in_dev->arp_parms, skb); in_dev_put(in_dev); return 0; } goto out; } } (编辑:应用网_丽江站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |