核心版本:2.6.34
這部分的重點是三個核心的資料結構-鄰居表、鄰居緩衝、代理鄰居表,以及NUD狀態轉移圖。
總的來說,要成功添加一條鄰居表項,需要滿足兩個條件:1. 本機使用該表項;2. 對方主機進行了確認。同時,表項的添加 引入了NUD(Neighbour Unreachability Detection)機制,從建立NUD_NONE到可用NUD_REACHABLE需要經曆一系列狀態轉移,而根 據達到兩個條件順序的不同,可以分為兩條路線:
先引用再確認- NUD_NONE -> NUD_INCOMPLETE -> NUD_REACHABLE
先確認再引用- NUD_NONE -> NUD_STALE -> NUD_DELAY -> NUD_PROBE -> NUD_REACHABLE
下面還是從接收函數入手,當匹配號協議號是0x0806,會調用ARP模組的接收函數arp_rcv()。
arp_rcv() ARP接收函數
首先是對arp協議頭進行檢查,比如大小是否足夠,頭部各數值是否正確等,這裡略過代碼,直 接向下看。每個協議處理都一樣,如果被多個協議佔有,則拷貝一份。
if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) goto out_of_mem;
NEIGH_CB(skb)實際就是skb->cb,在skb聲明為u8 char[48],它用作每個協議模組的私人資料 區(control buffer),每個協議模組可以根據自身需求在其中儲存私人資料。而arp模組就利用了它儲存控制結構neighbour_cb ,它聲明如下,佔8位元組。這個控制結構在代理ARP中使用工作隊列時會發揮作用,sched_next代表下次被調度的時間,flags是 標誌。
memset(NEIGH_CB(skb), 0, sizeof(struct neighbour_cb)); struct neighbour_cb { unsigned long sched_next; unsigned int flags; };
函數最後調用arp_process,其間插入netfilter(關於netfilter,參見前篇:http://hi.csdn.net/link.php? url=http://blog.csdn.net%2Fqy532846454),作為開始處理ARP報文的起點。
return NF_HOOK(NFPROTO_ARP, NF_ARP_IN, skb, dev, NULL, arp_process);
arp_process()
這個函數開始對報 文進行處理,首先會從skb中取出arp前序部分的資訊,如sha, sip, tha, tip等,這部分可查閱代碼,這裡略過。ARP不會查詢 環路地址和組播地址,因為它們沒有對應的mac地址,因此遇到這兩類地址,直接退出。
if (ipv4_is_loopback(tip) || ipv4_is_multicast(tip)) goto out;
如果收到的是重複地址檢測報文,並且本機佔用了檢測了地址,則調用arp_send發送響應。對於重複地址 檢測報文(ARP報文中源IP為全0),它所帶有的鄰居表項資訊還沒通過檢測,此時緩衝它顯然沒有意義,也許下一刻就有其它主機 聲明它非法,因此,重複地址檢測報文中的資訊不會加入鄰居表中。
if (sip == 0) { if (arp->ar_op == htons(ARPOP_REQUEST) && inet_addr_type(net, tip) == RTN_LOCAL && !arp_ignore(in_dev, sip, tip)) arp_send(ARPOP_REPLY, ETH_P_ARP, sip, dev, tip, sha, dev->dev_addr, sha); goto out; }
下面要處理的位址解析報文,並且要解析的地址在路由表中存在
if (arp->ar_op == htons(ARPOP_REQUEST) && ip_route_input(skb, tip, sip, 0, dev) == 0)
第一種情況,如果要解析的是本機地址,則調用neigh_event_ns() ,並根據查到的鄰居表項n發送ARP響應報文。這裡neigh_event_ns的功能是在arp_tbl中尋找是否已含有對方主機的地址資訊, 如果沒有,則進行建立,然後會調用neigh_update來更新狀態。收到對方主機的請求報文,會導致狀態遷移到NUD_STALE。
if (addr_type == RTN_LOCAL) { …… if (!dont_send) { n = neigh_event_ns(&arp_tbl, sha, &sip, dev); if (n) { arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,dev->dev_addr,sha); neigh_release(n); } } goto out; }
#NUD_INCOMPLETE也遷移到NUD_STALE,作何解釋?
第二種情況,如果要解析的不是本機地址,則要判斷是否支援 轉寄,是否支援代理ARP(代理ARP是陸由器的功能,因此能轉寄是先決條件),如果滿足條件,那麼按照代理ARP流程處理。首先 無論如何,主機得通了存在這樣一個鄰居,因此要在在arp_tbl中尋找並(如果不存在)建立相應鄰居表項;然後,對於代理ARP, 這個流程實際上會執行兩遍,第一遍走else部分,第二遍走if部分。第一次的else程式碼片段會觸發定時器,通過定時器引發報文重 新執行arp_process函數,並走if部分。
-第一遍的else部分:調用pneigh_enqueue()將報文skb加入tbl->proxy_queue隊 列,同時設定NEIGH_CB(skb)的值,具體可看後見的代理表項處理。
-第二遍的if部分,發送ARP響應報文,行使代理ARP的功 能。
else if (IN_DEV_FORWARD(in_dev)) { if (addr_type == RTN_UNICAST && (arp_fwd_proxy(in_dev, dev, rt) || arp_fwd_pvlan(in_dev, dev, rt, sip, tip) || pneigh_lookup(&arp_tbl, net, &tip, dev, 0))) { n = neigh_event_ns(&arp_tbl, sha, &sip, dev); if (n) neigh_release(n); if (NEIGH_CB(skb)->flags & LOCALLY_ENQUEUED || skb->pkt_type == PACKET_HOST || in_dev->arp_parms->proxy_delay == 0) { arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,dev->dev_addr,sha); } else { pneigh_enqueue(&arp_tbl, in_dev->arp_parms, skb); in_dev_put(in_dev); return 0; } goto out; } }