在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中的邻居表项就创建完成了。