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