网络子系统14_邻居子系统通用接口

//创建一个新的邻居项
//参考 深入理解linux网络技术内幕
//	1.邻居子系统为具体的邻居协议,提供通用的功能接口
//	2.系统中所有的邻居协议被链接在neigh_tables链表中
//	3.neigh_table代表一个具体的邻居协议
//	4.具体邻居协议在运行时的行为,可以通过struct neigh_parms调节,
//	neigh_params与设备关联,每个邻居协议neigh_table提供一个默认的neigh_params。


//注册一个邻居协议到系统中
//	1.与邻居协议neigh_table有关的定时器:垃圾回收定时器gc_timer,代理定时器proxy_timer
1.1 void neigh_table_init(struct neigh_table *tbl)
{
	unsigned long now = jiffies;
	unsigned long phsize;
	//设置引用计数为1
	atomic_set(&tbl->parms.refcnt, 1);
	//tbl->parms 的类型为 struct neigh_parms,用于调整邻居协议的行为
	INIT_RCU_HEAD(&tbl->parms.rcu_head);
	//reachable_time表示一个时间间隔,从上一次收到可到达性认证时间t到t+reachable_time之间,都认为邻居可达
	//reachable_time = base_reachable_time/2 + rand(0,base_reachable_time)
	tbl->parms.reachable_time =
			  neigh_rand_reach_time(tbl->parms.base_reachable_time);//

	//每个邻居协议私有的neighbour缓存
	if (!tbl->kmem_cachep)
		tbl->kmem_cachep = kmem_cache_create(tbl->id,
						     tbl->entry_size,
						     0, SLAB_HWCACHE_ALIGN,
						     NULL, NULL);

	if (!tbl->kmem_cachep)
		panic("cannot create neighbour cache");

	//per-cpu统计变量
	tbl->stats = alloc_percpu(struct neigh_statistics);
	if (!tbl->stats)
		panic("cannot create neighbour cache statistics");
	
	//1.邻居项和代理项的hash_buckets以链表指针的形式组织
	//2.每一个bucket的链表头为struct neighbour的指针
	//3.neighbour通过next域链接在同一个bucket

	//邻居项hash表,2个bucket
	tbl->hash_mask = 1;
	//hash_buckets组织成指针数组的形式,每一个struct neighbour通过next域进行链接
	tbl->hash_buckets = neigh_hash_alloc(tbl->hash_mask + 1);

	//代理
	phsize = (PNEIGH_HASHMASK + 1) * sizeof(struct pneigh_entry *);	
	tbl->phash_buckets = kmalloc(phsize, GFP_KERNEL);		
									
	if (!tbl->hash_buckets || !tbl->phash_buckets)
		panic("cannot allocate neighbour cache hashes");

	memset(tbl->phash_buckets, 0, phsize);

	//hash函数的随机性
	get_random_bytes(&tbl->hash_rnd, sizeof(tbl->hash_rnd));

	//初始化协议垃圾回收定时器
	rwlock_init(&tbl->lock);
	init_timer(&tbl->gc_timer);
	tbl->gc_timer.data     = (unsigned long)tbl;
	tbl->gc_timer.function = neigh_periodic_timer;//异步回收函数
	tbl->gc_timer.expires  = now + 1;
	add_timer(&tbl->gc_timer);

	//代理相关的定时器
	init_timer(&tbl->proxy_timer);
	tbl->proxy_timer.data	  = (unsigned long)tbl;
	tbl->proxy_timer.function = neigh_proxy_process;
	skb_queue_head_init(&tbl->proxy_queue);

	tbl->last_flush = now;//neigh_forced_gc最近执行一次的时间
	tbl->last_rand	= now + tbl->parms.reachable_time * 20;
	write_lock(&neigh_tbl_lock);
	tbl->next	= neigh_tables;//将协议挂接到neigh_tables头部,所有的邻居协议通过neigh_tables列表链接起来
	neigh_tables	= tbl;
	write_unlock(&neigh_tbl_lock);
}
//为邻居协议分配hash表
1.2 static struct neighbour **neigh_hash_alloc(unsigned int entries)
{
	unsigned long size = entries * sizeof(struct neighbour *);
	struct neighbour **ret;

	//大小少于一页
	if (size <= PAGE_SIZE) {
		ret = kmalloc(size, GFP_ATOMIC);
	} else {
		//多于一页,分配连续的物理页
		ret = (struct neighbour **)
			__get_free_pages(GFP_ATOMIC, get_order(size));
	}
	if (ret)
		memset(ret, 0, size);

	return ret;
}

//邻居项的创建是由事件驱动的,当系统需要一个邻居项,并且缓存不命中时,就创建一个邻居项
//	创建新邻居项的时机:
//		1.传输请求,当有一个向一台l2地址未知的主机传输请求时,就需要对该地址进行解析,
//		当目的主机不是与发送方直连时,解析的l2地址就是下一条网关的地址
//		2.收到solicitation请求,由于发送请求的主机在请求封包中有自己的识别信息,收到该
//		请求的主机会假定即将有两个系统之间的通信
//		3.手工添加,ip neigh add命令创建一个邻居项
//	创建新邻居项的过程:
//		1.通过slab缓存分配struct neighbour结构
//		2.通过neigh_table->constructor协议提供的初始化函数,初始化新邻居项
//		3.在neigh_table->constructor会设置neighbour->params,使用协议默认提供,或者驱动
//		提供的协议调整参数(neigh_params),通过neighbour->params->setup更新邻居项行为。
//		4.更新邻居项可到达性确认时间戳,使新邻居项比正常邻居项提前半个base_reachable_time到期
//		,从而对该邻居项进行一次主动的可到达性确认
//		5.添加到neigh_table->hash_buckets中,可能会引起hash表的扩容。
2.1 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;
	//从邻居协议的neighbour缓存中分配一个邻居项
	struct neighbour *n1, *rc, *n = neigh_alloc(tbl);

	if (!n) {
		rc = ERR_PTR(-ENOBUFS);
		goto out;
	}
	//拷贝hash的key到邻居项中,arp为ip地址
	memcpy(n->primary_key, pkey, key_len);
	n->dev = dev;//到达该邻居项使用的接口
	dev_hold(dev);//增加该接口的引用计数

	//如果邻居协议提供了邻居项的初始化函数,则调用
	if (tbl->constructor &&	(error = tbl->constructor(n)) < 0) {
		rc = ERR_PTR(error);
		goto out_neigh_release;
	}
	//如果设备驱动提供了初始化邻居项的回调函数,则调用
	if (n->parms->neigh_setup &&
	    (error = n->parms->neigh_setup(n)) < 0) {
		rc = ERR_PTR(error);
		goto out_neigh_release;
	}
	//邻居项可到达性确认的时间戳
	//当前时间-base_reachable_time/2,已保证新邻居项比正常邻居项提前base_reachable_time/2时间到期,执行可到达性检测
	n->confirmed = jiffies - (n->parms->base_reachable_time << 1);

	write_lock_bh(&tbl->lock);
	//如果协议表中的邻居数大于协议表hash表的长度
	if (atomic_read(&tbl->entries) > (tbl->hash_mask + 1))
		neigh_hash_grow(tbl, (tbl->hash_mask + 1) << 1);//hash表增加一半当前大小

	//使用协议提供的hash函数,对key,dev做hash
	hash_val = tbl->hash(pkey, dev) & tbl->hash_mask;//保证最终范围在hash表大小内

	if (n->parms->dead) {//如果此邻居项的配置信息标记为不在使用
		rc = ERR_PTR(-EINVAL);
		goto out_tbl_unlock;//则不添加新邻居项
	}

	//遍历hash表中的bucket链表
	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;
		}
	}
	//将邻居添加到bucket的list中
	n->next = tbl->hash_buckets[hash_val];
	tbl->hash_buckets[hash_val] = n;
	n->dead = 0;//标记此neighbour为可使用
	neigh_hold(n);//增加neighbour的引用计数
	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;
}


//删除邻居项
//	邻居项被删除的原因:
//		1.内核企图向一个不可到达的主机发送封包,当邻居子系统察觉到传输不成功,
//		就将neighbour标记为NUD_FAILED状态,可以是异步垃圾回收机制清除该neighbour.
//		2.与该邻居结构关联的主机的l2地址改变了,但它的l3地址还是原来的,一个
//		过时的邻居项必须进入NUD_FAILED态,然后重新创建一个新邻居项.

//	邻居项被删除的过程:
//		1.停用与邻居项有关的定时器
//		2.更新与邻居项有关的l2帧头缓存,设置器hh_cache->output为blackhole函数,
//		丢弃通过此l2帧头缓存发送的数据帧,递减hh_cache的引用计数,如果为0,则释放
//		3.调用邻居项提供的删除回调函数,在neigh_table->constructor或者neigh_table->params->setup中
//		被设置。
//		4.清空该邻居项的arp_queue,此队列中缓存的skb为与此邻居相关的,l2地址待解析的skb。
//		5.更新邻居协议的邻居项个数。
3.1 void neigh_destroy(struct neighbour *neigh)
{
	struct hh_cache *hh;
	//增加协议统计信息,邻居项被删除的次数
	NEIGH_CACHE_STAT_INC(neigh->tbl, destroys);
	//邻居项没有被标记为不在使用,则说明内核有错BUG()
	if (!neigh->dead) {
		printk(KERN_WARNING
		       "Destroying alive neighbour %p\n", neigh);
		dump_stack();
		return;
	}
	//删除邻居项的定时器
	if (neigh_del_timer(neigh))
		printk(KERN_WARNING "Impossible event.\n");

	//该邻居项缓存的l2帧头链表
	//从一台主机发往另一台主机的所有封包的l2帧头都是相同的,当向一个目的地发送第一个封包后,
	//驱动程序就将其l2帧头保存在hh_cache的结构中,如果下一次有相同的封包发往
	//同一个邻居,则只需从缓存中拷贝一个帧头即可,每个邻居项可以缓存多个帧头,但通常只缓存一个。
	while ((hh = neigh->hh) != NULL) {
		neigh->hh = hh->hh_next;
		//将l2地址缓存从列表上摘下来
		hh->hh_next = NULL;
		write_lock_bh(&hh->hh_lock);
		//替换此l2地址的输出函数为neigh_blackhole,以丢弃发往此目的l2地址的所有skb
		hh->hh_output = neigh_blackhole;
		write_unlock_bh(&hh->hh_lock);
		if (atomic_dec_and_test(&hh->hh_refcnt))
			kfree(hh);
	}
	//邻居项提供了删除函数
	if (neigh->ops && neigh->ops->destructor)
		(neigh->ops->destructor)(neigh);

	//清空该邻居项的arp缓存队列
	skb_queue_purge(&neigh->arp_queue);
	//释放对dev的引用
	dev_put(neigh->dev);
	//释放对协议调整参数的引用
	neigh_parms_put(neigh->parms);

	NEIGH_PRINTK2("neigh %p is destroyed.\n", neigh);
	//递减协议中邻居项的个数
	atomic_dec(&neigh->tbl->entries);
	//释放缓存
	kmem_cache_free(neigh->tbl->kmem_cachep, neigh);
}
//	查询l3地址对应的邻居项
//	参数:
//		tbl,被查询的协议表,arp为arp_tbl
//		pkey,查询的key,arp为ip
//		dev,到达此邻居项使用的出口设备
4.1 struct neighbour *neigh_lookup(struct neigh_table *tbl, const void *pkey,
			       struct net_device *dev)
{
	struct neighbour *n;
	int key_len = tbl->key_len;
	//hash pkey
	u32 hash_val = tbl->hash(pkey, dev) & tbl->hash_mask;
	//更新统计信息,递增查询个数
	NEIGH_CACHE_STAT_INC(tbl, lookups);

	read_lock_bh(&tbl->lock);
	//找到对应的bucket头链表
	for (n = tbl->hash_buckets[hash_val]; n; n = n->next) {
		//遍历链表中的每个元素
		if (dev == n->dev && !memcmp(n->primary_key, pkey, key_len)) {
			//增加邻居项的引用计数
			neigh_hold(n);
			//更新统计信息,递增命中率
			NEIGH_CACHE_STAT_INC(tbl, hits);
			break;
		}
	}
	read_unlock_bh(&tbl->lock);
	return n;
}

//用于更新一个邻居项 
/* Generic update routine.
   -- lladdr is new lladdr or NULL, if it is not supplied.
   -- new    is new state.
   -- flags
	NEIGH_UPDATE_F_OVERRIDE allows to override existing lladdr,
				if it is different.
	NEIGH_UPDATE_F_WEAK_OVERRIDE will suspect existing "connected"
				lladdr instead of overriding it 
				if it is different.
				It also allows to retain current state
				if lladdr is unchanged.
	NEIGH_UPDATE_F_ADMIN	means that the change is administrative.

	NEIGH_UPDATE_F_OVERRIDE_ISROUTER allows to override existing 
				NTF_ROUTER flag.
	NEIGH_UPDATE_F_ISROUTER	indicates if the neighbour is known as
				a router.

   Caller MUST hold reference count on the entry.
 */

//可能的更新策略:
//1.新状态没有可用的l2地址,删除定时器,怀疑邻居项,退出
//2.新状态有可用的l2地址:
//   2.1旧状态有可用l2地址,
//  	2.1.1新地址与旧地址不同并且没提供更新标志
//   		2.1.1.1 但允许怀疑邻居项并且就状态可达,仍然使用旧地址,标示邻居项需要进行可达性确认
//			2.1.1.2 不允许怀疑邻居项并且就状态不可达,退出
//   2.2新旧地址相同,并且新状态为进行可到达性确认,允许去怀疑或者就状态为可到达,依然保持就状态
//3.新旧状态不同,删除定时器,如果新状态需要定时器,更新定时器。更新邻居状态
//4.新地址与旧地址不同,拷贝新l2地址到邻居项的ha字段,更新l2缓存
//5.如果新旧状态相同,退出
//6.更新邻居项的输出回调函数
//7.如果旧状态没有可用的l2地址
//	 7.1 当前状态有可用的l2地址,将arp_queue中的所有skb发送
//	 7.2 否则,清空arp_queue

5.1 int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new,
		 u32 flags)
{
	u8 old;
	int err;
	...
	struct net_device *dev;
	int update_isrouter = 0;
	//获取此邻居项的锁
	write_lock_bh(&neigh->lock);
	//此邻居项的状态
	dev    = neigh->dev;
	old    = neigh->nud_state;
	err    = -EPERM;
	//如果没有admin,并且就状态为手动设置
	if (!(flags & NEIGH_UPDATE_F_ADMIN) && 
	    (old & (NUD_NOARP | NUD_PERMANENT)))
		goto out;//退出
	//新状态没有可用的l2地址
	if (!(new & NUD_VALID)) {
		neigh_del_timer(neigh);//删除邻居项的定时器
		if (old & NUD_CONNECTED)//旧状态为可达的
			neigh_suspect(neigh);//怀疑邻居项
		neigh->nud_state = new;//更新状态为新状态
		err = 0;

		goto out;
	}

	//运行到此处,说明有新状态有可用的l2地址,开始检查此地址
	//设备没有提供l2地址
	if (!dev->addr_len) {
		//设置提供的l2地址为邻居项的l2地址
		lladdr = neigh->ha;
	} else if (lladdr) {//提供了l2地址
		//如果旧状态有l2地址
		if ((old & NUD_VALID) && 
		    !memcmp(lladdr, neigh->ha, dev->addr_len))//比较新旧地址
			lladdr = neigh->ha;//如果一样的话,lladdr指向邻居的l2的地址
	} else {
		err = -EINVAL;//没有提供地址
		if (!(old & NUD_VALID))//并且旧状态没有可用的l2地址
			goto out;//返回错误
		lladdr = neigh->ha;
	}	
	//新状态为可达状态
	if (new & NUD_CONNECTED)
		neigh->confirmed = jiffies;//更新邻居项上一次被确认的时间为当前
	neigh->updated = jiffies;//上一次更新时间为当前

	err = 0;
	update_isrouter = flags & NEIGH_UPDATE_F_OVERRIDE_ISROUTER;//是否允许去更新NTF_ROUTER
	if (old & NUD_VALID) {//旧状态有l2地址
		if (lladdr != neigh->ha && !(flags & NEIGH_UPDATE_F_OVERRIDE)) {//(设备具有l2地址,或者新地址与旧地址不相同) && 没有提供更新地址的指示标志
			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->timer.expires = jiffies + 
						((new & NUD_REACHABLE) ? 
						 neigh->parms->reachable_time : 0);
			add_timer(&neigh->timer);
		}
		neigh->nud_state = new;
	}

	if (lladdr != neigh->ha) {//新地址与旧地址不同
		memcpy(&neigh->ha, lladdr, dev->addr_len);//拷贝新l2地址到邻居项的ha字段
		neigh_update_hhs(neigh);//更新l2地址缓存
		if (!(new & NUD_CONNECTED))
			neigh->confirmed = jiffies -
				      (neigh->parms->base_reachable_time << 1);

	}
	if (new == old)//如果新旧状态相同
		goto out;
	if (new & NUD_CONNECTED)//新状态表示邻居可达
		neigh_connect(neigh);//
	else
		neigh_suspect(neigh);//否则怀疑邻居项
	if (!(old & NUD_VALID)) {//如果旧状态没有可用的l2地址
		struct sk_buff *skb;


		while (neigh->nud_state & NUD_VALID &&//并且新地址为有可用的l2地址
		       (skb = __skb_dequeue(&neigh->arp_queue)) != NULL) {//从arp_queue中取出所有等待l2地址解析的skb
			struct neighbour *n1 = neigh;
			write_unlock_bh(&neigh->lock);
			if (skb->dst && skb->dst->neighbour)//如果skb有路由缓存
				n1 = skb->dst->neighbour;
			n1->output(skb);//则通过邻居项提供的output完成传输
			write_lock_bh(&neigh->lock);
		}
		skb_queue_purge(&neigh->arp_queue);//否则清空所有待解析l2地址的skb
	}
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;
}

//邻居状态为怀疑状态,即需要可达性确认
5.2 static void neigh_suspect(struct neighbour *neigh)
{
	struct hh_cache *hh;

	NEIGH_PRINTK2("neigh %p is suspected.\n", neigh);
	//更新邻居的输出函数为最通用的输出函数
	neigh->output = neigh->ops->output;
	//更新所有l2缓存的输出函数为最通用的输出函数
	for (hh = neigh->hh; hh; hh = hh->hh_next)
		hh->hh_output = neigh->ops->output;
}

//邻居状态为可达状态
5.3 static void neigh_connect(struct neighbour *neigh)
{
	struct hh_cache *hh;

	NEIGH_PRINTK2("neigh %p is connected.\n", neigh);
	//使用设备驱动程序填充l2头的输出方式
	neigh->output = neigh->ops->connected_output;
	//使用帧头缓存的方式,快速发送报文
	for (hh = neigh->hh; hh; hh = hh->hh_next)
		hh->hh_output = neigh->ops->hh_output;
}

//当设备的硬件地址发生改变时,调用此函数
//标记使用此设备的邻居项不在使用,并且停用此邻居项的所有定时器
6.1 void neigh_changeaddr(struct neigh_table *tbl, struct net_device *dev)
{
	int i;

	write_lock_bh(&tbl->lock);
	//邻居协议表hash表的长度
	for (i=0; i <= tbl->hash_mask; i++) {
		struct neighbour *n, **np;
		//遍历每一个bucket
		np = &tbl->hash_buckets[i];
		while ((n = *np) != NULL) {
			//遍历直到传入设备等于邻居协议所在的设备
			if (dev && n->dev != dev) {
				np = &n->next;
				continue;
			}
			*np = n->next;
			write_lock_bh(&n->lock);
			//将邻居标记为dead
			n->dead = 1;
			//删除邻居的定时器
			neigh_del_timer(n);//垃圾回收进程负责处理停用项
			write_unlock_bh(&n->lock);
			//递减引用计数
			neigh_release(n);
		}
	}
        write_unlock_bh(&tbl->lock);
}

//查找代理地址数据库
//pkey为目的ip
//dev为用于代理ip的设备
//creat指示在查找失败时,是否创建
7.1 struct pneigh_entry * pneigh_lookup(struct neigh_table *tbl, const void *pkey,
				    struct net_device *dev, int creat)
{
	struct pneigh_entry *n;
	int key_len = tbl->key_len;
	u32 hash_val = *(u32 *)(pkey + key_len - 4);//通用性,对于arp,hash_val=ip
	//hash计算
	hash_val ^= (hash_val >> 16);
	hash_val ^= hash_val >> 8;
	hash_val ^= hash_val >> 4;
	hash_val &= PNEIGH_HASHMASK;

	read_lock_bh(&tbl->lock);
	//代理hash表
	for (n = tbl->phash_buckets[hash_val]; n; n = n->next) {
		//被代理的邻居的l3地址和提供的目标l3地址相同
		if (!memcmp(n->key, pkey, key_len) &&
		    (n->dev == dev || !n->dev)) {//当邻居项放在表示代理hash表中,neighbour->dev的语义就发生了变化
			read_unlock_bh(&tbl->lock);//在一般情况下,其表示通过此设备可以访问到这个邻居,转义后,表示,由哪个设备代理此目的ip,到达此目的
			goto out;//ip的设备,通过路由表查找得到
		}
	}
	read_unlock_bh(&tbl->lock);
	n = NULL;
	//没有找到代理邻居项
	if (!creat)
		goto out;

	n = kmalloc(sizeof(*n) + key_len, GFP_KERNEL);
	if (!n)
		goto out;
	//创建邻居项,并添加到hash表中
	memcpy(n->key, pkey, key_len);
	n->dev = dev;
	if (dev)
		dev_hold(dev);

	if (tbl->pconstructor && tbl->pconstructor(n)) {
		if (dev)
			dev_put(dev);
		kfree(n);
		n = NULL;
		goto out;
	}

	write_lock_bh(&tbl->lock);
	n->next = tbl->phash_buckets[hash_val];
	tbl->phash_buckets[hash_val] = n;
	write_unlock_bh(&tbl->lock);
out:
	return n;
}

//将代理的solicitation放入延迟队列,延迟处理
8.1 void pneigh_enqueue(struct neigh_table *tbl, struct neigh_parms *p,
		    struct sk_buff *skb)
{
	unsigned long now = jiffies;
	unsigned long sched_next = now + (net_random() % p->proxy_delay);//延时时间

	if (tbl->proxy_queue.qlen > p->proxy_qlen) {//延迟队列超过限制
		kfree_skb(skb);//直接丢弃封包
		return;
	}
	skb->stamp.tv_sec  = LOCALLY_ENQUEUED;//标示入队时间,在出队后,通过此字段可以判断出此skb是否曾经被入过队,防止循环入队
	skb->stamp.tv_usec = sched_next;

	spin_lock(&tbl->proxy_queue.lock);
	if (del_timer(&tbl->proxy_timer)) {//
		if (time_before(tbl->proxy_timer.expires, sched_next))
			sched_next = tbl->proxy_timer.expires;//设置定时器的到期时间为需要最早被处理solicitation的时间,因此通过一个定时器就可以管理整个队列
	}
	dst_release(skb->dst);
	skb->dst = NULL;
	dev_hold(skb->dev);
	__skb_queue_tail(&tbl->proxy_queue, skb);//放入tbl->proxy_queue中
	mod_timer(&tbl->proxy_timer, sched_next);
	spin_unlock(&tbl->proxy_queue.lock);
}

//被动学习
//接收到ARP_REQUEST封包的目的主机从请求封包中也知道了发送方的地址
//该函数会负责检查是否有一个邻居项和请求者关联,然后就会更新这个存在的邻居项,如果不存在,则创建一个新的邻居项
9.1 struct neighbour *neigh_event_ns(struct neigh_table *tbl,
				 u8 *lladdr, void *saddr,
				 struct net_device *dev)
{
	//查找邻居项
	struct neighbour *neigh = __neigh_lookup(tbl, saddr, dev,
						 lladdr || !dev->addr_len);
	//如果存在,则更新为NUD_STALE状态,表示有l2地址可用
	if (neigh)
		neigh_update(neigh, lladdr, NUD_STALE, 
			     NEIGH_UPDATE_F_OVERRIDE);
	return neigh;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值