LwIP協(xié)議棧源碼詳解——TCP/IP協(xié)議的實現(xiàn)》ARP層流程

姓名:朱小鵬 ? ?學號:16010130023

轉(zhuǎn)載:

http://blog.sina.com.cn/s/blog_62a85b950101anvx.html

【嵌牛導讀】:最后要講的一個函數(shù)是update_arp_entry,該函數(shù)用于更新ARP緩存表中的表項或者在緩存表中插入一個新的表項。該函數(shù)會在收到一個IP數(shù)據(jù)包或ARP數(shù)據(jù)包后被調(diào)用。

【嵌牛鼻子】:ARP表的創(chuàng)建,更新,查詢等操作

【嵌牛提問】:LWIP是怎樣進行ARP表的創(chuàng)建,更新,查詢等操作?

【嵌牛正文】:

最后要講的一個函數(shù)是update_arp_entry,該函數(shù)用于更新ARP緩存表中的表項或者在緩存表中插入一個新的表項。該函數(shù)會在收到一個IP數(shù)據(jù)包或ARP數(shù)據(jù)包后被調(diào)用。該函數(shù)原型如下,

static err_t

update_arp_entry(struct netif *netif, struct ip_addr *ipaddr, struct eth_addr *ethaddr, u8_t flags)

其中重要的兩個參數(shù)ipaddr和ethaddr分別對應的ip地址和mac地址,函數(shù)利用這兩個地址去更新或插入ARP表項。由于這個函數(shù)代碼量較小,這里就列出源碼來講解,注意這個源碼是經(jīng)過我處理的,已經(jīng)去掉了編譯選項、源碼注釋、調(diào)試輸出信息等非重點部分。

static err_t

update_arp_entry(struct netif *netif, struct ip_addr *ipaddr, struct eth_addr *ethaddr, u8_t flags)

{

s8_t i;// 兩個變量,不解釋

u8_t k;

i = find_entry(ipaddr, flags);// 查找或新建一個ARP表項,返回其索引值

if (i < 0)return (err_t)i;// 如果為不合法的索引值,則更新緩存表失敗

arp_table[i].state = ETHARP_STATE_STABLE; // 否則將對應表項狀態(tài)改為stable

arp_table[i].netif = netif;// 記錄下網(wǎng)絡接口

k = ETHARP_HWADDR_LEN;// 這一段是更新緩存表項中的MAC地址

while (k > 0) {

k--;

arp_table[i].ethaddr.addr[k] = ethaddr->addr[k];

}

arp_table[i].ctime = 0;//生存時間值置0

#if ARP_QUEUEING//該ARP表項上有未發(fā)送的隊列,則把這些隊列發(fā)送出去

while (arp_table[i].q != NULL) {// 只要緩沖鏈表中海有數(shù)據(jù)則循環(huán)

struct pbuf *p;

struct etharp_q_entry *q = arp_table[i].q;// 記錄下緩沖鏈表表頭

arp_table[i].q = q->next;// 緩沖鏈表表頭指向下一個節(jié)點

p = q->p;// 取得記錄下的緩沖鏈表表頭指向的數(shù)據(jù)包

memp_free(MEMP_ARP_QUEUE, q);// 釋放記錄下的緩沖鏈表表頭

etharp_send_ip(netif, p, (struct eth_addr*)(netif->hwaddr), ethaddr); // 發(fā)送數(shù)據(jù)包

pbuf_free(p);// 釋放數(shù)據(jù)包緩存空間

}

#endif

return ERR_OK;

}

從源程序中可以看出,update_arp_entry的流程如下:先通過調(diào)用find_entry找到對應ipaddr對應的表項,并設置相應的arp表項的成員(主要是state, netif, ethaddr, cttime),最后如果定義了ARP_QUEUEING,并且這個arp表項上有未發(fā)送的數(shù)據(jù)包的話,則把這些數(shù)據(jù)全部發(fā)送出去。雖然比較啰嗦,但是還是我們還是根據(jù)不同的ipaddr經(jīng)過find_entry執(zhí)行后,來看看update_arp_entry運行的幾種不同情況。

首先可以肯定的是,update_arp_entry的兩個參數(shù)ipaddr和ethaddr必是互相匹配的,因為它們是從源主機發(fā)來的IP包或ARP包中解析出來的,代表了源主機的MAC地址和IP地址。find_entry利用ipaddr作為參數(shù)執(zhí)行后,返回一個ARP表項索引。如果該表項處于empty狀態(tài),那么該表項現(xiàn)在一定是新創(chuàng)建的,此時設置該表項為stable狀態(tài)并設置該表項其他字段值后即結(jié)束。如果該表項是處于pending狀態(tài),由于此時已經(jīng)有了和ipaddr匹配的MAC地址返回,所以該表項也被設置為stable狀態(tài)并同時設置該表項其他字段值。如果該表項是處于stable狀態(tài),其實此時只需要將ctime的值復位即可,但是LWIP為了節(jié)省代碼量,它還是選擇像上面的情況一樣做相同的處理,這樣雖然有些步驟是多余的,但并不影響函數(shù)功能。最后都會檢查該表項是否還有數(shù)據(jù)需要發(fā)送,如果有,則將所有數(shù)據(jù)包發(fā)送出去。

現(xiàn)在是時候從宏觀上來看看到底ARP是怎么一個工作流程,以及它在整個LWIP協(xié)議棧當中發(fā)揮的重要作用。關(guān)于這點,不得不借鑒網(wǎng)上某位大俠的了,不過對TA的圖做了一些小小的修改(臉紅,用畫圖工具改的,Visio表示不會用)。

該圖簡潔明了的解釋了基本所有LWIP的數(shù)據(jù)包接收與發(fā)送的全過程。我們可以看到幾個熟悉的身影:etharp_query、etharp_request、update_arp_entry。在前面已經(jīng)講過了的!

ARP從功能上來說可以簡單的分成兩個部分:當有數(shù)據(jù)包輸入時,更新arp表,如果是ip包則遞交給ip層,如果是arp包,則針對不同的arp包類型做相應的響應;當向目的ip發(fā)送一個數(shù)據(jù)包的時候,需要通過arp實現(xiàn)ip到MAC地址的映射,必要時,需要發(fā)送廣播數(shù)據(jù)包獲得目標機器的MAC地址。

LWIP利用netif.input指向的函數(shù)接收以太網(wǎng)數(shù)據(jù)包,通常這個函數(shù)是ethernet_input。注意,這里并不是說ethernet_input直接與底層硬件交互接收數(shù)據(jù)包,而是更底層的函數(shù)接收到數(shù)據(jù)包后將數(shù)據(jù)包遞交給ethernet_input,ethernet_input再對其進行處理。

以太網(wǎng)的幀類型可以是:IP,ARP,甚至可以是pppoe, wlan等。這里主要分析IP和ARP兩種類型的數(shù)據(jù)包。ethernet_input根據(jù)以太網(wǎng)首部的類型字段判斷收到的數(shù)據(jù)包的類型,如果是IP包,則將該包遞交給etharp_ip_input,如果是ARP包,則將該包遞交給etharp_arp_input。

對于ip類型的數(shù)據(jù)包,etharp_ip_input首先檢查是否開啟了ETHARP_TRUST_IP_MAC這個選項,如果開啟了就是要用這個幀中的信息和update_arp_entry函數(shù)來更新arp表(利用幀首部的源mac地址和幀數(shù)據(jù)中ip報文中的源ip地址),然后丟棄以太網(wǎng)幀首部,將IP報文通過ip_input函數(shù)遞交給ip層。

對于arp類型的數(shù)據(jù)包,etharp_arp_input函數(shù)首先利用數(shù)據(jù)包頭信息更新arp表的內(nèi)容,

然后再判斷該ARP數(shù)據(jù)包的類型,如果是ARP請求包,則首先判斷這個包是不是給自己的,如果是給自己的,則在原有包的基礎上重組一個ARP應答包發(fā)送出去(注意此處并沒有重新分配一個pbuf,而是借用了原來的緩沖結(jié)構(gòu))。如果不是給自己的,則直接忽略。如果是ARP應答包,主要的工作就是更新arp表,但是這一步已經(jīng)在arp包剛進來的時候就處理了,所以這里不需要再重復做,這樣ARP包的處理也完畢。

LWIP利用netif.output指向的函數(shù)發(fā)送IP數(shù)據(jù)包,通常這個函數(shù)是etharp_output。注意,這里并不是說etharp_output直接與底層硬件交互發(fā)送數(shù)據(jù)包,而是將數(shù)據(jù)包做相應的處理,最終遞交給netif.linkoutput函數(shù)來發(fā)送的。

etharp_output函數(shù)接收IP層要發(fā)送的數(shù)據(jù)包,并將數(shù)據(jù)包發(fā)送出去。由于是發(fā)送ip數(shù)據(jù)包,所以函數(shù)一開始需要增加緩沖區(qū)大小,大小為以太網(wǎng)的數(shù)據(jù)首部的大小。然后檢查ip地址,可以分為廣播包,多播包,單播包(單播包又分為是局域網(wǎng)內(nèi)部還是局域網(wǎng)外面)。

廣播包:判斷目的IP地址是不是為全1,或者是全0(老版本中使用的),如果是廣播包則目的IP的MAC地址不需要查詢arp表,直接將MAC地址設置為全1發(fā)送即可,即MAC六個字節(jié)值為0xff,0xff,0xff,0xff,0xff,0xff。

多播包:判斷目的ip地址是不是d類地址,即0xexxxxxxx,如果是多播的話,mac地址也是確定的,即將MAC地址01-00-5e-00-00-00的低23位設置為IP地址的低23位。對于以上的兩種數(shù)據(jù)包,etharp_output直接調(diào)用函數(shù)etharp_send_ip將數(shù)據(jù)包發(fā)送出去。

單播包:要比較目的IP和本地IP地址,看是否是局域網(wǎng)內(nèi)的,不是局域網(wǎng)內(nèi)的,則將目的IP地址設置為默認網(wǎng)關(guān)的地址,然后再統(tǒng)一調(diào)用etharp_query函數(shù)將數(shù)據(jù)包發(fā)送出去,注意這些數(shù)據(jù)包在這種情況下可能被連接在相關(guān)ARP表項的發(fā)送鏈表上,等待發(fā)送。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容