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

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

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

neigh_lookup 查找 ARP表项

查找函数很简单,以IP地址和网卡设备(即pkey和dev)计算哈希值hash_val,然后在tbl->hash_buckets查找相应 项。

hash_val = tbl->hash(pkey, dev);     
for (n = tbl->hash_buckets[hash_val & tbl->hash_mask]; n; n = n->next) {     
 if (dev == n->dev && !memcmp(n->primary_key, pkey, key_len)) {     
  neigh_hold(n);     
  NEIGH_CACHE_STAT_INC(tbl, hits);     
  break;     
 }     
}

代理ARP

代理ARP的相关知识查阅google。要明确代理ARP功能是针对陆由器的(或者说是具有转发功能的主机)。开 启ARP代理后,会对查询不在本网段的ARP请求包回应。

回到之前的arp_process代码,处理代理ARP的情况,这实际就是进行 代理ARP的条件,IN_DEV_FORWARD是支持转发,RTN_UNICAST是与路由直连,arp_fwd_proxy表示设备支持代理行为, arp_fwd_pvlan表示支持代理同设备进出,pneigh_lookup表示目的地址的代理。这两种arp_fwd_proxy和arp_fwd_pvlan都只是网 卡设备的一种性质,pneigh_lookup则是一张代理邻居表,它的内容都是手动添加或删除的,三种策略任一一种满足都可以进行 代理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)))

pneigh_lookup 查找或添加代理邻居表项[proxy neighbour]

以[pkey=tip, key_len=4]计算hash值,执行__pneigh_lookup_1在phash_buckets中查找。

u32 

hash_val = pneigh_hash(pkey, key_len);     
n = __pneigh_lookup_1(tbl->phash_buckets[hash_val], net, pkey, key_len, dev);

如果在phash_buckets中查 找到,或者不需要创建新表项,则函数返回,此时它的功能仅仅是lookup。

if (n || !creat)     
 goto out;

而当传入参数create=1时,则它的功能不仅是lookup,还会在表项不存在时create。同neighbour结构一样 ,键值pkey存储在pneigh结构的后面,这样当pkey变化时,修改十分容易。创建操作很直观,为pneigh和pkey分配空间,初始化 些变量,最后插入phash_buckets。

n = kmalloc(sizeof(*n) + key_len, GFP_KERNEL);     
……     
write_pnet(&n->net, hold_net(net));     
memcpy(n->key, pkey, key_len);     
……     
n->next = tbl->phash_buckets[hash_val];     
tbl->phash_buckets[hash_val] = n;

pneigh_enqueue 将报文加入代理队列

首先计算下次调度的时间,这是一 个随机值,记录到sched_next中;设置flags|=LOCALLY_ENQUEUED表明报文是本地加入的。

unsigned long sched_next 

= now + (net_random() % p->proxy_delay);     
……     
NEIGH_CB(skb)->sched_next = sched_next;     
NEIGH_CB(skb)->flags |= LOCALLY_ENQUEUED;

然后将报文加入proxy_queue,并设置定时器proxy_timer,下次超 时时间为刚计算的值sched_next,这样,下次超时时就会处理proxy_queue队列中的报文。

__skb_queue_tail

(&tbl->proxy_queue, skb);     
mod_timer(&tbl->proxy_timer, sched_next);

这里的tbl当然是arp_tbl,它的proxy_timer是在初始化时设置 的arp_init() -> neigh_table_init_no_netlink()中:

setup_timer(&tbl->proxy_timer, neigh_proxy_process, (unsigned long)tbl);

neigh_proxy_process 代理ARP的定时器

skb_queue_walk_safe如同 for循环一样,它遍历proxy_queue,一个个取出其中的报文skb,查看报文的调度时间sched_next与当前时间now的差值。

如 果tdif<=0则表明调度时间已到或已过,报文要被处理了,从proxy_queue上取出该报文,调用tbl->proxy_redo重新发送 报文,tbl->proxy_redo也是在arp初始化时赋值的,实际上就是arp_process()函数。结合上面的分析,它会执行 arp_process中代理ARP处理的else部分,发送响应报文。

如果tdif>0则表明调度时间还未到,else if部分的功能就是记 录下最近要过期的调度时间到sched_next。

skb_queue_walk_safe(&tbl->proxy_queue, skb, n) {     
 long tdif = NEIGH_CB(skb)->sched_next - now;     
         
 if (tdif <= 0) {     
  struct net_device *dev = skb->dev;     
  __skb_unlink(skb, &tbl->proxy_queue);     
  if (tbl->proxy_redo && netif_running(dev))     
   tbl->proxy_redo(skb);     
  else 
   kfree_skb(skb);     
         
  dev_put(dev);     
 } else if (!sched_next || tdif < sched_next)     
  sched_next = tdif;     
}

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

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

热点阅读