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