加入收藏 | 设为首页 | 会员中心 | 我要投稿 应用网_丽江站长网 (http://www.0888zz.com/)- 科技、建站、数据工具、云上网络、机器学习!
当前位置: 首页 > 服务器 > 搭建环境 > Linux > 正文

Linux内核分析 - 网络[九]:邻居表

发布时间:2016-05-28 22:17:43 所属栏目:Linux 来源:网络整理
导读:内核版本:2.6.34 这部分的重点是三个核心的数据结构-邻居表、邻居缓存、代理邻居表,以及NUD状态转移图。 总的来说,要成功添加一条邻居表项,需要满足两个条
副标题[/!--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;     
 }     
}

(编辑:应用网_丽江站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

热点阅读