在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是相同的。
下面是ndisc_hh_ops和ndisc_generic_ops结构体的定义
点击(此处)折叠或打开
- static struct neigh_ops ndisc_generic_ops =
{
- .family = AF_INET6,
- .solicit = ndisc_solicit,
- .error_report
= ndisc_error_report,
- .output = neigh_resolve_output,
- .connected_output
= neigh_connected_output,
- .hh_output
= dev_queue_xmit,
- .queue_xmit
= dev_queue_xmit,
- };
- static struct neigh_ops ndisc_hh_ops =
{
- .family = AF_INET6,
- .solicit = ndisc_solicit,
- .error_report
= ndisc_error_report,
- .output = neigh_resolve_output,
- .connected_output
= neigh_resolve_output,
- .hh_output
= dev_queue_xmit,
- .queue_xmit
= dev_queue_xmit,
- };
在上面的两数据结构体中分别,对output函数进行了赋值。
neigh_create函数在创建一个邻居表项成功以后,返回一个struct neighbour *的结构体指针。
在接收的NS数据报文后,也就是下面的函数
static void ndisc_recv_ns(struct sk_buff*skb)
在接收的RA数据报文后,也就是下面的函数
static void ndisc_recv_rs(struct sk_buff*skb)
在路由重定向函数中
static void ndisc_redirect_rcv(structsk_buff *skb)
在路由发现函数中,
static void ndisc_router_discovery(structsk_buff *skb)
都会调用__neigh_lookup函数,创建一个邻居表项。
这里以static void ndisc_recv_ns(struct sk_buff*skb)函数中的代码为例,看看其中的流程
点击(此处)折叠或打开
- neigh = __neigh_lookup(&nd_tbl, saddr, dev,
- !inc
|| lladdr ||
!dev->addr_len);
- if (neigh)
- neigh_update(neigh, lladdr, NUD_STALE,
- NEIGH_UPDATE_F_WEAK_OVERRIDE|
- NEIGH_UPDATE_F_OVERRIDE);
- if (neigh
||
!dev->hard_header)
{
- ndisc_send_na(dev, neigh, saddr,
&msg->target,
- idev->cnf.forwarding,
- 1,
(ifp !=
NULL && inc), inc);
- if (neigh)
- neigh_release(neigh);
- }
在调用__neigh_lookup函数成功,并返回一个邻居表项的指针时,接着调用了
neigh_update()函数,该函数主要是对邻居表项的一个信息进行了填充。这个函数才是真正的把邻居表项建立,这里“真正”的含义,在对邻居表项的很多信息进行了填充,例如重要的邻居的IP地址,MAC地址,状态,生命期等。
点击(此处)折叠或打开
- int neigh_update(struct neighbour
*neigh,
const u8 *lladdr, u8 new,
- u32 flags)
- {
- u8 old;
- int err;
- #ifdef CONFIG_ARPD
- int notify
= 0;
- #endif
- struct net_device *dev;
- int update_isrouter
= 0;
- write_lock_bh(&neigh->lock);
- dev = neigh->dev;
- old = neigh->nud_state;
- err =
-EPERM;
- if (!(flags
& NEIGH_UPDATE_F_ADMIN)
&&
- (old &
(NUD_NOARP | NUD_PERMANENT)))
- goto out;
- if (!(new
& NUD_VALID))
{
- neigh_del_timer(neigh);
- if (old
& NUD_CONNECTED)
- neigh_suspect(neigh);
- neigh->nud_state
= new;
- err = 0;
- #ifdef CONFIG_ARPD
- notify = old
& NUD_VALID;
- #endif
- goto out;
- }
- /* Compare new lladdr with cached one
*/
- if (!dev->addr_len)
{
- /* First
case: device needs no address.
*/
- lladdr = neigh->ha;
- } else
if (lladdr)
{
- /* The
second case:
if something is already cached
- and a new address
is proposed:
- - compare new
& old
- - if they are different, check override flag
- */
- if ((old
& NUD_VALID)
&&
- !memcmp(lladdr, neigh->ha, dev->addr_len))
- lladdr = neigh->ha;
- } else
{
- /* No address
is supplied;
if we know something,
- use it, otherwise discard the request.
- */
- err =
-EINVAL;
- if (!(old
& NUD_VALID))
- goto out;
- lladdr = neigh->ha;
- }
- if (new
& NUD_CONNECTED)
- neigh->confirmed
= jiffies;
- neigh->updated
= jiffies;
- /*
If entry was valid and address
is not changed,
- do not change entry state,
if new one is STALE.
- */
- err = 0;
- update_isrouter = flags
& NEIGH_UPDATE_F_OVERRIDE_ISROUTER;
- if (old
& NUD_VALID)
{
- if (lladdr
!= neigh->ha
&&
!(flags & NEIGH_UPDATE_F_OVERRIDE))
{
- update_isrouter = 0;
- if
((flags & NEIGH_UPDATE_F_WEAK_OVERRIDE)
&&
- (old
& NUD_CONNECTED))
{
- lladdr = neigh->ha;
- new = NUD_STALE;
- }
else
- goto out;
- } else
{
- if
(lladdr == neigh->ha
&& new
== NUD_STALE
&&
- ((flags
& NEIGH_UPDATE_F_WEAK_OVERRIDE)
||
- (old
& NUD_CONNECTED))
- )
- new = old;
- }
- }
- if (new
!= old)
{
- neigh_del_timer(neigh);
- if (new
& NUD_IN_TIMER)
{
- neigh_hold(neigh);
- neigh_add_timer(neigh,
(jiffies +
- ((new
& NUD_REACHABLE)
?
- neigh->parms->reachable_time
:
- 0)));------------------(1)
- }
- neigh->nud_state
= new;
- }
- if (lladdr
!= neigh->ha)
{
- memcpy(&neigh->ha, lladdr, dev->addr_len);
- neigh_update_hhs(neigh);
- if (!(new
& NUD_CONNECTED))
- neigh->confirmed
= jiffies -
- (neigh->parms->base_reachable_time
<< 1);
- #ifdef CONFIG_ARPD
- notify = 1;
- #endif
- }
- if (new
== old)
- goto out;
- if (new
& NUD_CONNECTED)
- neigh_connect(neigh);
- else
- neigh_suspect(neigh);
- if (!(old
& NUD_VALID))
{
- struct sk_buff *skb;
- /* Again: avoid dead
loop if something went wrong
*/
- while
(neigh->nud_state
& NUD_VALID &&
- (skb
= __skb_dequeue(&neigh->arp_queue))
!=
NULL) {
- struct neighbour *n1
= neigh;
- write_unlock_bh(&neigh->lock);
- /*
On shaper/eql skb->dst->neighbour
!= neigh
:( */
- if
(skb->dst
&& skb->dst->neighbour)
- n1 = skb->dst->neighbour;
- n1->output(skb);-----------------------------(2)
- write_lock_bh(&neigh->lock);
- }
- skb_queue_purge(&neigh->arp_queue);
- }
- out:
- if (update_isrouter)
{
- neigh->flags
= (flags
& NEIGH_UPDATE_F_ISROUTER)
?
- (neigh->flags
| NTF_ROUTER)
:
- (neigh->flags
& ~NTF_ROUTER);
- }
- write_unlock_bh(&neigh->lock);
- #ifdef CONFIG_ARPD
- if (notify
&& neigh->parms->app_probes)
- neigh_app_notify(neigh);
- #endif
- return err;
- }
该函数主要是对邻居表项的状态进行了判断,还有就是对定时器的启动
(1) 当新(new)的状态和老(old)的状态不匹配的时候,就会从新启动相应的定时器
(2) 调用上面的output函数指针进行赋值后的output函数,这里是neigh_resolve_output函数
点击(此处)折叠或打开
- int neigh_resolve_output(struct sk_buff
*skb)
- {
- struct dst_entry *dst
= skb->dst;
- struct neighbour *neigh;
- int rc = 0;
- if (!dst
||
!(neigh = dst->neighbour))
- goto discard;
- __skb_pull(skb, skb->nh.raw
- skb->data);
- if (!neigh_event_send(neigh, skb))
{
- int err;
- struct net_device *dev
= neigh->dev;
- if (dev->hard_header_cache
&&
!dst->hh)
{
- write_lock_bh(&neigh->lock);
- if
(!dst->hh)
- neigh_hh_init(neigh, dst, dst->ops->protocol);
- err
= dev->hard_header(skb, dev, ntohs(skb->protocol),
- neigh->ha,
NULL, skb->len);
- write_unlock_bh(&neigh->lock);
- } else
{
- read_lock_bh(&neigh->lock);
- err
= dev->hard_header(skb, dev, ntohs(skb->protocol),
- neigh->ha,
NULL, skb->len);---------(1)
- read_unlock_bh(&neigh->lock);
- }
- if (err
>= 0)
- rc = neigh->ops->queue_xmit(skb);
- else
- goto out_kfree_skb;
- }
- out:
- return rc;
- discard:
- NEIGH_PRINTK1("neigh_resolve_output: dst=%p neigh=%p\n",
- dst, dst
? dst->neighbour
: NULL);
- out_kfree_skb:
- rc = -EINVAL;
- kfree_skb(skb);
- goto out;
- }
(1)在netdevice.h文件中下面的函数调用
点击(此处)折叠或打开
- static inline int dev_hard_header(struct sk_buff
*skb, struct net_device
*dev,
- unsigned short type,
- const void
*daddr, const void
*saddr,
- unsigned len)
- {
- //检测设备是否有头部操作集
- //检测操作集是否有创建操作
- if (!dev->header_ops
||
!dev->header_ops->create)
- return 0;
- return dev->header_ops->create(skb, dev,
type, daddr, saddr,
len);
- }
由于这里的设备是以太网设备,在以太网设备进行初始化的时候,会有调用下面的函数
点击(此处)折叠或打开
- void ether_setup(struct net_device
*dev)
- {
- dev->header_ops =
ð_header_ops;
- dev->type = ARPHRD_ETHER;
- dev->hard_header_len = ETH_HLEN;
- dev->mtu = ETH_DATA_LEN;
- dev->addr_len = ETH_ALEN;
- dev->tx_queue_len = 1000; /* Ethernet
wants good queues */
- dev->flags = IFF_BROADCAST|IFF_MULTICAST;
- memset(dev->broadcast, 0xFF, ETH_ALEN);
- }
这样,在调用dev->header_ops->create个函数时,实际上是调用的eth_headr
点击(此处)折叠或打开
- const struct header_ops eth_header_ops ____cacheline_aligned
= {
- .create = eth_header,
- .parse = eth_header_parse,
- .rebuild = eth_rebuild_header,
- .cache = eth_header_cache,
- .cache_update = eth_header_cache_update,
- };
- /**
- * eth_header
- create the Ethernet header
- * @skb: buffer
to alter
- * @dev: source device
- * @type: Ethernet type field
- * @daddr: destination address
(NULL leave destination address)
- * @saddr: source address
(NULL use device source address)
- * @len: packet length
(<= skb->len)
- *
- *
- * Set the protocol type.
For a packet of type ETH_P_802_3/2 we put the length
- * in here instead.
- */
- 创建以太网的头部,
- int eth_header(struct sk_buff
*skb, struct net_device
*dev,
- unsigned short type,
- const void
*daddr, const void
*saddr, unsigned
len)
- {
- struct ethhdr *eth
= (struct ethhdr
*)skb_push(skb, ETH_HLEN);//加入到SKB的buf中,使用skb_push向上增长
- if (type
!= ETH_P_802_3
&& type
!= ETH_P_802_2)
- eth->h_proto
= htons(type);
- else
- eth->h_proto
= htons(len);
- /*
- * Set the source hardware address.
- */
- if (!saddr)
- saddr = dev->dev_addr;
- memcpy(eth->h_source, saddr, ETH_ALEN);
- if (daddr)
{
- memcpy(eth->h_dest, daddr, ETH_ALEN);
- return ETH_HLEN;
- }
- /*
- * Anyway, the loopback-device should never use this
function...
- */
- if (dev->flags
& (IFF_LOOPBACK
| IFF_NOARP))
{
- memset(eth->h_dest, 0, ETH_ALEN);
- return ETH_HLEN;
- }
- return -ETH_HLEN;
- }
(2)这个函数是和三层协议进行交互的接口,在IP层协议发送数据时,也就是在static int ip6_finish_output2(struct sk_buff *skb)函数中的最后会调用下面的两个函数,
if (dst->hh)
return neigh_hh_output(dst->hh, skb);
else if(dst->neighbour)
returndst->neighbour->output(skb);
这两个函数调用,最后可能的调用的就是neigh_resolve_output,加上以太网的报头后,通过dev_queue_xmit函数调用,发送出去。
在该函数中调用neigh_event_send()进行发送ND报文
如果这个表项创建成功后,接下来就调用ndisc_send_na函数,发送NA报文。
ndisc_send_na(dev, neigh, saddr, &msg->target,
idev->cnf.forwarding,
1, (ifp != NULL && inc), inc);
自此,在IPv6中的邻居表项就创建完成了。