閱讀398 返回首頁    go 阿裏雲 go 技術社區[雲棲]


網絡子係統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;
}


最後更新:2017-04-03 15:21:56

  上一篇:go Java麵向對象高級--抽象類與接口的應用
  下一篇:go [曆年IT筆試題]2014阿裏巴巴麵試題(北京站)