IPv6 邻居创建和发现流程(一)

1350阅读 0评论2015-06-05 stevewang1979
分类:LINUX

nieghbour.h文件中的__neigh_lookup函数,其实会在检查邻居表项中是否已经有了,该邻居。如果没有找到相应的邻居表项,就会调用neigh_create()的函数创建一个。

点击(此处)折叠或打开

  1. static inline struct neighbour *
  2. __neigh_lookup(struct neigh_table *tbl, const void *pkey, struct net_device *dev, int creat)
  3. {
  4.     struct neighbour *n = neigh_lookup(tbl, pkey, dev);

  5.     if (n || !creat)
  6.         return n;

  7.     n = neigh_create(tbl, pkey, dev);
  8.     return IS_ERR(n) ? NULL : n;
  9. }

neigh_create(tbl, pkey, dev);函数也只有在__neigh_lookup函数中会被调用,别的函数中不会直接调用neigh_create,也就是说如果想创建一个邻居表项的话,必须首先调用__neigh_lookup函数进行查找。

下面是neigh_create函数的具体实现:

点击(此处)折叠或打开

  1. struct neighbour *neigh_create(struct neigh_table *tbl, const void *pkey,
  2.              struct net_device *dev)
  3. {
  4.     u32 hash_val;
  5.     int key_len = tbl->key_len;
  6.     int error;
  7.     struct neighbour *n1, *rc, *n = neigh_alloc(tbl);

  8.     if (!n) {
  9.         rc = ERR_PTR(-ENOBUFS);
  10.         goto out;
  11.     }

  12.     memcpy(n->primary_key, pkey, key_len);
  13.     n->dev = dev;
  14.     dev_hold(dev);

  15.     /* Protocol specific setup. */
  16.     if (tbl->constructor &&    (error = tbl->constructor(n)) < 0) {
  17.         rc = ERR_PTR(error);
  18.         goto out_neigh_release;
  19.     }

  20.     /* Device specific setup. */
  21.     if (n->parms->neigh_setup &&
  22.      (error = n->parms->neigh_setup(n)) < 0) {
  23.         rc = ERR_PTR(error);
  24.         goto out_neigh_release;
  25.     }

  26.     n->confirmed = jiffies - (n->parms->base_reachable_time << 1);

  27.     write_lock_bh(&tbl->lock);

  28.     if (atomic_read(&tbl->entries) > (tbl->hash_mask + 1))
  29.         neigh_hash_grow(tbl, (tbl->hash_mask + 1) << 1);

  30.     hash_val = tbl->hash(pkey, dev) & tbl->hash_mask;

  31.     if (n->parms->dead) {
  32.         rc = ERR_PTR(-EINVAL);
  33.         goto out_tbl_unlock;
  34.     }

  35.     for (n1 = tbl->hash_buckets[hash_val]; n1; n1 = n1->next) {
  36.         if (dev == n1->dev && !memcmp(n1->primary_key, pkey, key_len)) {
  37.             neigh_hold(n1);
  38.             rc = n1;
  39.             goto out_tbl_unlock;
  40.         }
  41.     }

  42.     n->next = tbl->hash_buckets[hash_val];
  43.     tbl->hash_buckets[hash_val] = n;
  44.     n->dead = 0;
  45.     neigh_hold(n);
  46.     write_unlock_bh(&tbl->lock);
  47.     NEIGH_PRINTK2("neigh %p is created.\n", n);
  48.     rc = n;
  49. out:
  50.     return rc;
  51. out_tbl_unlock:
  52.     write_unlock_bh(&tbl->lock);
  53. out_neigh_release:
  54.     neigh_release(n);
  55.     goto out;
  56. }

首先,会调用neigh_alloc函数分配一个邻居表项的空间。

下面是neigh_alloc函数的具体实现

点击(此处)折叠或打开

  1. static struct neighbour *neigh_alloc(struct neigh_table *tbl)
  2. {
  3.     struct neighbour *n = NULL;
  4.     unsigned long now = jiffies;
  5.     int entries;

  6.     entries = atomic_inc_return(&tbl->entries) - 1;
  7.     if (entries >= tbl->gc_thresh3 ||
  8.      (entries >= tbl->gc_thresh2 &&
  9.      time_after(now, tbl->last_flush + 5 * HZ))) {
  10.         if (!neigh_forced_gc(tbl) &&
  11.          entries >= tbl->gc_thresh3)
  12.             goto out_entries;
  13.     }

  14.     n = kmem_cache_alloc(tbl->kmem_cachep, SLAB_ATOMIC);
  15.     if (!n)
  16.         goto out_entries;

  17.     memset(n, 0, tbl->entry_size);

  18.     skb_queue_head_init(&n->arp_queue);
  19.     rwlock_init(&n->lock);
  20.     n->updated     = n->used = now;
  21.     n->nud_state     = NUD_NONE;-------------------(1)
  22.     n->output     = neigh_blackhole; ----------------(2)
  23.     n->parms     = neigh_parms_clone(&tbl->parms);
  24.     init_timer(&n->timer);------------------------------(3)
  25.     n->timer.function = neigh_timer_handler;----------------(4)
  26.     n->timer.data     = (unsigned long)n;-------------------------(5)

  27.     NEIGH_CACHE_STAT_INC(tbl, allocs);
  28.     n->tbl         = tbl;
  29.     atomic_set(&n->refcnt, 1);
  30.     n->dead         = 1;
  31. out:
  32.     return n;

  33. out_entries:
  34.     atomic_dec(&tbl->entries);
  35.     goto out;
  36. }

在该函数中比较重要的代码,是用红色标识出来的。

(1)       是设置邻居表项的最开始的状态

(2)       设置发送的函数

(3)       初始化一个定时器,在邻居发现过程中,有五个状态需要相互的切换,这其中就需要定时器,这里也只是对定时器进行了初始化,并没有启动定时器

(4)       定时器的处理函数,neigh_timer_handler

(5)       定时器处理函数所需要的参数

neigh_create函数中有下面一段代码.

点击(此处)折叠或打开

  1. /* Protocol specific setup. */
  2.     if (tbl->constructor &&    (error = tbl->constructor(n)) < 0) {
  3.         rc = ERR_PTR(error);
  4.         goto out_neigh_release;
  5.     }

该段代码会调用ndisc_constructor()函数,对于IPv6来说,如果是IPv4的话,调用arp_constructor()

点击(此处)折叠或打开

  1. static int ndisc_constructor(struct neighbour *neigh)
  2. {
  3.     struct in6_addr *addr = (struct in6_addr*)&neigh->primary_key;
  4.     struct net_device *dev = neigh->dev;
  5.     struct inet6_dev *in6_dev;
  6.     struct neigh_parms *parms;
  7.     int is_multicast = ipv6_addr_is_multicast(addr);

  8.     rcu_read_lock();
  9.     in6_dev = in6_dev_get(dev);
  10.     if (in6_dev == NULL) {
  11.         rcu_read_unlock();
  12.         return -EINVAL;
  13.     }

  14.     parms = in6_dev->nd_parms;
  15.     __neigh_parms_put(neigh->parms);
  16.     neigh->parms = neigh_parms_clone(parms);
  17.     rcu_read_unlock();

  18.     neigh->type = is_multicast ? RTN_MULTICAST : RTN_UNICAST;
  19.     if (dev->hard_header == NULL) {
  20. //不需要ARP时,例如PPPOE
  21.         neigh->nud_state = NUD_NOARP;
  22.         neigh->ops = &ndisc_direct_ops;
  23.         neigh->output = neigh->ops->queue_xmit;
  24.     } else {
  25.         if (is_multicast) {
  26.             neigh->nud_state = NUD_NOARP;
  27.             ndisc_mc_map(addr, neigh->ha, dev, 1);
  28.         } else if (dev->flags&(IFF_NOARP|IFF_LOOPBACK)) {
  29.             neigh->nud_state = NUD_NOARP;
  30.             memcpy(neigh->ha, dev->dev_addr, dev->addr_len);
  31.             if (dev->flags&IFF_LOOPBACK)
  32.                 neigh->type = RTN_LOCAL;
  33.         } else if (dev->flags&IFF_POINTOPOINT) {
  34.             neigh->nud_state = NUD_NOARP;
  35.             memcpy(neigh->ha, dev->broadcast, dev->addr_len);
  36.         }
  37.         if (dev->hard_header_cache)
  38.             neigh->ops = &ndisc_hh_ops;
  39.         else
  40.             neigh->ops = &ndisc_generic_ops;
  41.         if (neigh->nud_state&NUD_VALID)
  42.             neigh->output = neigh->ops->connected_output;
  43.         else
  44.             neigh->output = neigh->ops->output;
  45.     }
  46.     in6_dev_put(in6_dev);
  47.     return 0;
  48. }

该函数主要是对各种状态进行了判断,其中标识红色的代码,设置定时器时间到期时所调用的发送处理函数。这里有对NOARP的一个判断,在PPPOE拨号成功后,在MSG设备上的WAN接口上可以看到“NOARP“的说明信息,说明该接口不需要ARP或者ND协议学习MAC地址。如果是生成另外一个单独的PPPoe接口的话,就只会在新生成的PPP0接口下生成”NOARP”的标识信息。这里如果判断是NOARP时,就是把自身的MAC地址设置为目的MAC地址,也就是发送出来的数据包的,源MAC和目的MAC是相同的。

上一篇:MDI/MDIX接口
下一篇:ARP状态转换