IPv6 邻居创建和发现流程
来源:互联网 发布:算法优化技巧 编辑:程序博客网 时间:2024/04/28 10:29
在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);
- } elseif (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;
- } elseif (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){
- /* Firstcase: device needs no address.*/
- lladdr = neigh->ha;
- } elseif (lladdr){
- /* Thesecond 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 addressis 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 deadloop 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: bufferto 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, unsignedlen)
- {
- 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 thisfunction...
- */
- 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中的邻居表项就创建完成了。
- IPv6 邻居创建和发现流程
- IPv6邻居发现协议
- IPv6邻居发现协议
- IPv6的邻居发现技术
- IPv6邻居发现协议简介
- IPV6邻居发现协议简介
- 【IPV6基础知识】IPV6邻居发现协议
- IPV6邻居发现协议----路由器发现
- 深度分析IPV6 NDP邻居发现协议
- IPv6 NDP邻居发现协议 1
- IPv6 NDP邻居发现协议 2
- RFC 4861 IPv6邻居发现 阅读笔记(一)
- Linux下IPv6寻址及邻居发现机制
- 邻居发现(Neighbor Discovery)协议
- IPV6的配置过程和流程
- NS2之移动节点邻居节点发现
- NS2之移动节点邻居节点发现
- 命令查看IPV6的IP,路由,邻居信息
- 字符串操作(C语言 C++)
- hibernate主键增长increment与native的区别
- jQuery将物体居中,并且转换显示和隐藏
- tomcat结合nginx使用小结
- 会员卡管理系统技术解析(十六)库存卡作废恢复记录添加
- IPv6 邻居创建和发现流程
- Codeforces 336C Vasily the Bear and Sequence (暴力)
- 第12周 项目1 教师兼干部类
- Pig 和 Hive 的优化
- va_start和va_end使用详解
- Android水波纹实现
- HDU ACM 3887 Counting Offspring 树状数组+DFS
- HDU---1466-计算直线的交点数(DP)
- 第12周 项目2 摩托车继承自行车和机动车