在nieghbour.h文件中的__neigh_lookup函数,其实会在检查邻居表项中是否已经有了,该邻居。如果没有找到相应的邻居表项,就会调用neigh_create()的函数创建一个。
点击(此处)折叠或打开
- static inline struct neighbour *
- __neigh_lookup(struct neigh_table *tbl, const void *pkey, struct net_device *dev, int creat)
- {
- struct neighbour *n = neigh_lookup(tbl, pkey, dev);
- if (n || !creat)
- return n;
- n = neigh_create(tbl, pkey, dev);
- return IS_ERR(n) ? NULL : n;
- }
而neigh_create(tbl, pkey, dev);函数也只有在__neigh_lookup函数中会被调用,别的函数中不会直接调用neigh_create,也就是说如果想创建一个邻居表项的话,必须首先调用__neigh_lookup函数进行查找。
下面是neigh_create函数的具体实现:
点击(此处)折叠或打开
- struct neighbour *neigh_create(struct neigh_table *tbl, const void *pkey,
- struct net_device *dev)
- {
- u32 hash_val;
- int key_len = tbl->key_len;
- int error;
- struct neighbour *n1, *rc, *n = neigh_alloc(tbl);
- if (!n) {
- rc = ERR_PTR(-ENOBUFS);
- goto out;
- }
- memcpy(n->primary_key, pkey, key_len);
- n->dev = dev;
- dev_hold(dev);
- /* Protocol specific setup. */
- if (tbl->constructor && (error = tbl->constructor(n)) < 0) {
- rc = ERR_PTR(error);
- goto out_neigh_release;
- }
- /* Device specific setup. */
- if (n->parms->neigh_setup &&
- (error = n->parms->neigh_setup(n)) < 0) {
- rc = ERR_PTR(error);
- goto out_neigh_release;
- }
- n->confirmed = jiffies - (n->parms->base_reachable_time << 1);
- write_lock_bh(&tbl->lock);
- if (atomic_read(&tbl->entries) > (tbl->hash_mask + 1))
- neigh_hash_grow(tbl, (tbl->hash_mask + 1) << 1);
- hash_val = tbl->hash(pkey, dev) & tbl->hash_mask;
- if (n->parms->dead) {
- rc = ERR_PTR(-EINVAL);
- goto out_tbl_unlock;
- }
- for (n1 = tbl->hash_buckets[hash_val]; n1; n1 = n1->next) {
- if (dev == n1->dev && !memcmp(n1->primary_key, pkey, key_len)) {
- neigh_hold(n1);
- rc = n1;
- goto out_tbl_unlock;
- }
- }
- n->next = tbl->hash_buckets[hash_val];
- tbl->hash_buckets[hash_val] = n;
- n->dead = 0;
- neigh_hold(n);
- write_unlock_bh(&tbl->lock);
- NEIGH_PRINTK2("neigh %p is created.\n", n);
- rc = n;
- out:
- return rc;
- out_tbl_unlock:
- write_unlock_bh(&tbl->lock);
- out_neigh_release:
- neigh_release(n);
- goto out;
- }
首先,会调用neigh_alloc函数分配一个邻居表项的空间。
下面是neigh_alloc函数的具体实现
点击(此处)折叠或打开
- static struct neighbour *neigh_alloc(struct neigh_table *tbl)
- {
- struct neighbour *n = NULL;
- unsigned long now = jiffies;
- int entries;
- entries = atomic_inc_return(&tbl->entries) - 1;
- if (entries >= tbl->gc_thresh3 ||
- (entries >= tbl->gc_thresh2 &&
- time_after(now, tbl->last_flush + 5 * HZ))) {
- if (!neigh_forced_gc(tbl) &&
- entries >= tbl->gc_thresh3)
- goto out_entries;
- }
- n = kmem_cache_alloc(tbl->kmem_cachep, SLAB_ATOMIC);
- if (!n)
- goto out_entries;
- memset(n, 0, tbl->entry_size);
- skb_queue_head_init(&n->arp_queue);
- rwlock_init(&n->lock);
- n->updated = n->used = now;
- n->nud_state = NUD_NONE;-------------------(1)
- n->output = neigh_blackhole; ----------------(2)
- n->parms = neigh_parms_clone(&tbl->parms);
- init_timer(&n->timer);------------------------------(3)
- n->timer.function = neigh_timer_handler;----------------(4)
- n->timer.data = (unsigned long)n;-------------------------(5)
- NEIGH_CACHE_STAT_INC(tbl, allocs);
- n->tbl = tbl;
- atomic_set(&n->refcnt, 1);
- n->dead = 1;
- out:
- return n;
- out_entries:
- atomic_dec(&tbl->entries);
- goto out;
- }
在该函数中比较重要的代码,是用红色标识出来的。
(1) 是设置邻居表项的最开始的状态
(2) 设置发送的函数
(3) 初始化一个定时器,在邻居发现过程中,有五个状态需要相互的切换,这其中就需要定时器,这里也只是对定时器进行了初始化,并没有启动定时器
(4) 定时器的处理函数,neigh_timer_handler
(5) 定时器处理函数所需要的参数
在neigh_create函数中有下面一段代码.
点击(此处)折叠或打开
- /* Protocol specific setup. */
- if (tbl->constructor && (error = tbl->constructor(n)) < 0) {
- rc = ERR_PTR(error);
- goto out_neigh_release;
- }
该段代码会调用ndisc_constructor()函数,对于IPv6来说,如果是IPv4的话,调用arp_constructor()
点击(此处)折叠或打开
- static int ndisc_constructor(struct neighbour *neigh)
- {
- struct in6_addr *addr = (struct in6_addr*)&neigh->primary_key;
- struct net_device *dev = neigh->dev;
- struct inet6_dev *in6_dev;
- struct neigh_parms *parms;
- int is_multicast = ipv6_addr_is_multicast(addr);
- rcu_read_lock();
- in6_dev = in6_dev_get(dev);
- if (in6_dev == NULL) {
- rcu_read_unlock();
- return -EINVAL;
- }
- parms = in6_dev->nd_parms;
- __neigh_parms_put(neigh->parms);
- neigh->parms = neigh_parms_clone(parms);
- rcu_read_unlock();
- neigh->type = is_multicast ? RTN_MULTICAST : RTN_UNICAST;
- if (dev->hard_header == NULL) {
- //不需要ARP时,例如PPPOE
- neigh->nud_state = NUD_NOARP;
- neigh->ops = &ndisc_direct_ops;
- neigh->output = neigh->ops->queue_xmit;
- } else {
- if (is_multicast) {
- neigh->nud_state = NUD_NOARP;
- ndisc_mc_map(addr, neigh->ha, dev, 1);
- } else if (dev->flags&(IFF_NOARP|IFF_LOOPBACK)) {
- neigh->nud_state = NUD_NOARP;
- memcpy(neigh->ha, dev->dev_addr, dev->addr_len);
- if (dev->flags&IFF_LOOPBACK)
- neigh->type = RTN_LOCAL;
- } else if (dev->flags&IFF_POINTOPOINT) {
- neigh->nud_state = NUD_NOARP;
- memcpy(neigh->ha, dev->broadcast, dev->addr_len);
- }
- if (dev->hard_header_cache)
- neigh->ops = &ndisc_hh_ops;
- else
- neigh->ops = &ndisc_generic_ops;
- if (neigh->nud_state&NUD_VALID)
- neigh->output = neigh->ops->connected_output;
- else
- neigh->output = neigh->ops->output;
- }
- in6_dev_put(in6_dev);
- return 0;
- }
该函数主要是对各种状态进行了判断,其中标识红色的代码,设置定时器时间到期时所调用的发送处理函数。这里有对NOARP的一个判断,在PPPOE拨号成功后,在MSG设备上的WAN接口上可以看到“NOARP“的说明信息,说明该接口不需要ARP或者ND协议学习MAC地址。如果是生成另外一个单独的PPPoe接口的话,就只会在新生成的PPP0接口下生成”NOARP”的标识信息。这里如果判断是NOARP时,就是把自身的MAC地址设置为目的MAC地址,也就是发送出来的数据包的,源MAC和目的MAC是相同的。