網絡子係統15_arp鄰居項初始化
// 初始化struct neighbour
// 當鄰居子係統新創一個neighbour時,鄰居子係統調用特定協議的初始化函數,初始化鄰居項。
// 調用路徑:neigh_create->arp_constructor
// 函數主要任務:
// 1.設置鄰居項的地址類型,a,b,c,d
// 2.使用與此鄰居項關聯的接口設備的neigh_param作為該鄰居項調整鄰居協議的參數。
// 3.根據與此鄰居項關聯的接口設備的信息,初始化鄰居項的狀態,以及ops
// 3.1 驅動沒有提供填充l2幀頭的函數,表明此接口不需要進行地址解析
// 3.1.1 設置鄰居項nud_state=NUD_NOARP,ops=arp_direct_ops
// 3.2 否則,根據鄰居l3地址類型,設置鄰居項狀態
// 3.2.1 多播地址,廣播地址,nud_state=NUD_NOARP
// 3.2.2 設備dev->flags表明無需進行地址解析,nud_state=NUD_NOARP
// 3.2.3 設備dev->flags表明回環設備,nud_state=NUD_NOARP
// 3.3 如果設備驅動提供l2幀頭緩存的能力:
// 3.3.1 使用arp_hh_ops
// 3.4 否則使用最通用的arp_generic_ops
// 注:根據鄰居項與本機的連接情況,為neighbour設置最合適操作函數
// 1.當鄰居項與本機之連時,無需arp解析,使用arp_direct_ops
// 2.當鄰居項需要進行地址解析時,而且到達此鄰居項的接口設備,提供l2幀頭緩存的能力,
// 則使用優化過的arp_hh_ops
3.當1,2 均不滿足時,使用可以適用於一切情況下的arp_generic_ops
2.2 static int arp_constructor(struct neighbour *neigh)
{
u32 addr = *(u32*)neigh->primary_key;//neighbour的key值,為ip地址
struct net_device *dev = neigh->dev;//到達此鄰居通過的設備接口
struct in_device *in_dev;//ipv4配置信息
struct neigh_parms *parms;//調整鄰居協議的參數
//a,b,c,d類地址
neigh->type = inet_addr_type(addr);
rcu_read_lock();
//獲得設備的ipv4配置信息
in_dev = rcu_dereference(__in_dev_get(dev));
if (in_dev == NULL) {
rcu_read_unlock();
return -EINVAL;
}
//獲取此設備調整arp協議的參數控製塊
parms = in_dev->arp_parms;
//遞減引用計數
__neigh_parms_put(neigh->parms);
//克隆一份設備使用的控製參數,添加到鄰居項中
neigh->parms = neigh_parms_clone(parms);
rcu_read_unlock();
//設備驅動沒有提供填充mac頭的回調函數
if (dev->hard_header == NULL) {
//設置當前鄰居項的狀態為NUD_NOARP,說明不需要進行地址解析
neigh->nud_state = NUD_NOARP;
//使用直連操作回調函數
neigh->ops = &arp_direct_ops;
//通過該鄰居項的輸出函數初始化為arp_direct_ops->queue_xmit,為dev_queue_xmit
neigh->output = neigh->ops->queue_xmit;
} else {
//否則,需要進行地址解析
//如果鄰居的地址類型為多播地址,則不需要進行地址解析
if (neigh->type == RTN_MULTICAST) {
neigh->nud_state = NUD_NOARP;
//初始化多播地址鏈表
arp_mc_map(addr, neigh->ha, dev, 1);
//如果設備指示不能支持地址解析,或者設備為回環設備
} else if (dev->flags&(IFF_NOARP|IFF_LOOPBACK)) {
//則不需要進行地址解析
neigh->nud_state = NUD_NOARP;
//鄰居的mac地址為dev的設備mac地址
memcpy(neigh->ha, dev->dev_addr, dev->addr_len);
//如果鄰居為廣播地址,或者設備為點到點連接
} else if (neigh->type == RTN_BROADCAST || dev->flags&IFF_POINTOPOINT) {
//則不需要進行地址解析
neigh->nud_state = NUD_NOARP;
//鄰居的mac地址為dev的設備的廣播地址
memcpy(neigh->ha, dev->broadcast, dev->addr_len);
}
//設備提供了mac頭緩存回調函數
if (dev->hard_header_cache)
neigh->ops = &arp_hh_ops;//初始化為arp_hh_ops
else
neigh->ops = &arp_generic_ops;//負責初始化為通用例程
if (neigh->nud_state&NUD_VALID)//如果當前鄰居狀態為有效狀態
neigh->output = neigh->ops->connected_output;//則初始化鄰居的輸出函數為連接狀態下的輸出回調函數
else
neigh->output = neigh->ops->output;
}
return 0;
}
// 最通用的鄰居項操作
2.1 static struct neigh_ops arp_generic_ops = {
.family = AF_INET,//地址族
.solicit = arp_solicit,//協議提供,用於發送solicitation請求的回調函數
.error_report = arp_error_report,//當一個arp事物中發生錯誤時,arp_error_report函數就通知上層的網絡層
.output = neigh_resolve_output,//可用於所有情況下,它會檢查地址是否已經被解析過,在沒有被解析的情況下,啟動解析程序,如果地址還沒有準備好,
//則會把封包保存在arp_queue中,並啟動解析程序。該函數為了保證接收方可到達,做好每一件必要的操作
.connected_output = neigh_connected_output,//當已經知道鄰居是可到達時,(NUD_CONNECTED態),使用該函數
.hh_output = dev_queue_xmit,//當地址已經解析過,並且整個封包頭已經根據上一次傳輸結果放入幀頭緩存時,就使用該函數
.queue_xmit = dev_queue_xmit,//之前的所有函數,除了hh_output函數外,都不會實際傳輸封包,他們所做的工作就是確保封包幀頭是編寫好的,然後當幀頭
//緩存準備好時,調用queue_xmit執行傳輸。
};
// 驅動程序提供l2幀頭緩存功能,arp_hh_ops提高性能
3.1 static struct neigh_ops arp_hh_ops = {
.family = AF_INET,
.solicit = arp_solicit,
.error_report = arp_error_report,
.output = neigh_resolve_output,
.connected_output = neigh_resolve_output,
.hh_output = dev_queue_xmit,
.queue_xmit = dev_queue_xmit,
};
// 不需要進行地址解析時采用的操作
4.1 static struct neigh_ops arp_direct_ops = {
.family = AF_INET,
.output = dev_queue_xmit,//直接啟動設備的傳輸
.connected_output = dev_queue_xmit,
.hh_output = dev_queue_xmit,
.queue_xmit = dev_queue_xmit,
};
// 發送solicitation請求
5.1 static void arp_solicit(struct neighbour *neigh, struct sk_buff *skb)
{
u32 saddr = 0;
u8 *dst_ha = NULL;
struct net_device *dev = neigh->dev;
u32 target = *(u32*)neigh->primary_key;
int probes = atomic_read(&neigh->probes);//失敗的solicitation嚐試的次數
//獲取設備上的ipv4配置信息
struct in_device *in_dev = in_dev_get(dev);
if (!in_dev)
return;
//當發送arp請求的主機有多個ip地址時,ANNOUNCE這個選項控製哪個地址應該放到solicitation請求的ARP頭中。
switch (IN_DEV_ARP_ANNOUNCE(in_dev)) {
default:
case 0: //任何本地ip都可以
//源地址為本機地址
if (skb && inet_addr_type(skb->nh.iph->saddr) == RTN_LOCAL)
saddr = skb->nh.iph->saddr;
break;
case 1: //如果可能,選擇和目的地址位於同一子網內的地址
if (!skb)
break;
saddr = skb->nh.iph->saddr;
if (inet_addr_type(saddr) == RTN_LOCAL) {
//判斷skb中的源地址與目標地址是否在同一子網內
if (inet_addr_onlink(in_dev, target, saddr))
break;
}
saddr = 0;
break;
case 2: //優先使用主地址
break;
}
//遞減ipv4配置信息的引用
if (in_dev)
in_dev_put(in_dev);
//說明需要優先使用主地址
if (!saddr)
saddr = inet_select_addr(dev, target, RT_SCOPE_LINK);
//當前鄰居已經消耗盡證實一個地址可到達性測試的探測次數
if ((probes -= neigh->parms->ucast_probes) < 0) {
//當前狀態非VALID
if (!(neigh->nud_state&NUD_VALID))
printk(KERN_DEBUG "trying to ucast probe in NUD_INVALID\n");
dst_ha = neigh->ha;//與primary_key表示的l3地址關聯的l2地址
read_lock_bh(&neigh->lock);
} else if ((probes -= neigh->parms->app_probes) < 0) {
#ifdef CONFIG_ARPD
//如果使用arpd,則喚醒用戶態進程
neigh_app_ns(neigh);
#endif
return;
}
//發送arp報文
arp_send(ARPOP_REQUEST, ETH_P_ARP, target, dev, saddr,
dst_ha, dev->dev_addr, NULL);
if (dst_ha)
read_unlock_bh(&neigh->lock);
}
// 通知上層網絡協議arp事物錯誤
5.2 static void arp_error_report(struct neighbour *neigh, struct sk_buff *skb)
{
//向路由子係統的路由項緩存通知鏈路失效
dst_link_failure(skb);
//釋放當前skb
kfree_skb(skb);
}
// 可用於所有情況下的skb發送
5.3 int neigh_resolve_output(struct sk_buff *skb)
{
//skb對應的路由項緩存
struct dst_entry *dst = skb->dst;
struct neighbour *neigh;
int rc = 0;
//skb沒有關聯的路由緩存或者路由緩存沒有有效的鄰居
if (!dst || !(neigh = dst->neighbour))
goto discard;
//將skb->data移動到l3協議頭處
__skb_pull(skb, skb->nh.raw - skb->data);
//調整鄰居的狀態機,啟動定時器,並且skb沒有接管
if (!neigh_event_send(neigh, skb)) {
int err;
struct net_device *dev = neigh->dev;
//如果驅動程序提供了l2幀頭緩存,並且路由緩存沒有關聯的l2頭緩存
if (dev->hard_header_cache && !dst->hh) {
write_lock_bh(&neigh->lock);
//路由項緩存沒有l2頭緩存
if (!dst->hh)
neigh_hh_init(neigh, dst, dst->ops->protocol);//初始化dst的l2幀頭緩存
//填充skb的l2頭
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);
//驅動沒有提供幀頭緩存,調用驅動提供回調函數,填充skb的l2幀頭
//此時dst依然沒有對應的l2幀頭緩存
err = dev->hard_header(skb, dev, ntohs(skb->protocol),
neigh->ha, NULL, skb->len);
read_unlock_bh(&neigh->lock);
}
if (err >= 0)
{
//準備好l2頭之後,調用queue_xmit進行傳輸
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;
}
//調用路徑neigh_resolve_output->neigh_event_send
5.4 static inline int neigh_event_send(struct neighbour *neigh, struct sk_buff *skb)
{
//更新當前鄰居最近一次被使用的時間
neigh->used = jiffies;
//鄰居狀態已經開始處理solicitation
if (!(neigh->nud_state&(NUD_CONNECTED|NUD_DELAY|NUD_PROBE)))
return __neigh_event_send(neigh, skb);
return 0;
}
//調用路徑neigh_resolve_output->neigh_event_send->__neigh_event_send
//在skb被該函數接管後,返回1,否則返回0
5.5 int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb)
{
int rc;
unsigned long now;
write_lock_bh(&neigh->lock);
rc = 0;
//鄰居沒有未決的solicitation,或者已經開始執行solicitation
if (neigh->nud_state & (NUD_CONNECTED | NUD_DELAY | NUD_PROBE))
goto out_unlock_bh;
now = jiffies;
//鄰居不需要可到底性確認,並且沒有待決的solicitation請求
if (!(neigh->nud_state & (NUD_STALE | NUD_INCOMPLETE))) {
//鄰居項配置參指示,仍然可以使用用戶空間解析地址,或者多播解析地址
if (neigh->parms->mcast_probes + neigh->parms->app_probes) {
//設置鄰居項的試探次數為鄰居項調整參數的單播次數
atomic_set(&neigh->probes, neigh->parms->ucast_probes);
neigh->nud_state = NUD_INCOMPLETE;//設置鄰居項有未決的solicitation請求
neigh_hold(neigh);//增加引用計數
neigh->timer.expires = now + 1;//調整到期時間為下一個jiffies
add_timer(&neigh->timer);
} else {
neigh->nud_state = NUD_FAILED;//設置鄰居不可達
write_unlock_bh(&neigh->lock);
if (skb)
kfree_skb(skb);
return 1;
}
} else if (neigh->nud_state & NUD_STALE) {//鄰居項已經有一段時間沒有被確認過了
NEIGH_PRINTK2("neigh %p is delayed.\n", neigh);
neigh_hold(neigh);//增加引用計數
neigh->nud_state = NUD_DELAY;//鄰居項使用舊的l2地址,進入時間窗
neigh->timer.expires = jiffies + neigh->parms->delay_probe_time;//設置時間窗的長度
add_timer(&neigh->timer);//啟動定時器
}
if (neigh->nud_state == NUD_INCOMPLETE) {//如果已經發送了solicitation請求,但還沒有收到應答
if (skb) {
if (skb_queue_len(&neigh->arp_queue) >=
neigh->parms->queue_len) {
struct sk_buff *buff;
buff = neigh->arp_queue.next;
__skb_unlink(buff, &neigh->arp_queue);
kfree_skb(buff);
}
__skb_queue_tail(&neigh->arp_queue, skb);//將skb添加到arp_queue中,等待收到solicitation應答
}
rc = 1;
}
out_unlock_bh:
write_unlock_bh(&neigh->lock);
return rc;
}
//使用hh_cache,盡快完成skb發送
5.5 int neigh_connected_output(struct sk_buff *skb)
{
int err;
struct dst_entry *dst = skb->dst;
struct neighbour *neigh = dst->neighbour;
struct net_device *dev = neigh->dev;
//將skb->data調整到l3頭
__skb_pull(skb, skb->nh.raw - skb->data);
read_lock_bh(&neigh->lock);
//填充skb的l2頭
err = dev->hard_header(skb, dev, ntohs(skb->protocol),
neigh->ha, NULL, skb->len);
read_unlock_bh(&neigh->lock);
if (err >= 0)
err = neigh->ops->queue_xmit(skb);//委托給ops->queue_xmit完成最後的傳輸
else {
err = -EINVAL;
kfree_skb(skb);
}
return err;
}
最後更新:2017-04-03 15:22:03