前面一節重點說了arp快取表以及如何對其進行相關操作,關於arp,一共想說三個函式,前面已經講過了兩個。
最後要講的乙個函式是update_arp_entry
,該函式用於更新arp快取表中的表項或者在快取表中插入乙個新的表項。該函式會在收到乙個ip資料報或arp資料報後被呼叫。該函式原型如下,
static err_t
update_arp_entry
(struct netif *netif,
struct ip_addr *ipaddr,
struct eth_addr *ethaddr, u8_t flags)
其中重要的兩個引數ipaddr
和ethaddr
分別對應的ip
位址和mac
位址,函式利用這兩個位址去更新或插入arp表項。由於這個函式**量較小,這裡就列出原始碼來講解,注意這個原始碼是經過處理的,已經去掉了編譯選項、原始碼注釋、除錯輸出資訊等非重點部分。
static err_t
update_arp_entry
(struct netif *netif,
struct ip_addr *ipaddr,
struct eth_addr *ethaddr, u8_t flags)
arp_table[i]
.ctime =0;
// 生存時間值置0
#if arp_queueing
//該arp表項上有未傳送的佇列,則把這些佇列傳送出去
while
(arp_table[i]
.q !=
null
)#endif
return err_ok;
}
從源程式中可以看出,update_arp_entry
的流程如下:先通過呼叫find_entry
找到對應ipaddr
對應的表項,並設定相應的arp
表項的成員(主要是state, netif, ethaddr, cttime
),最後如果定義了arp_queueing
,並且這個arp
表項上有未傳送的資料報的話,則把這些資料全部傳送出去。雖然比較囉嗦,但是還是我們還是根據不同的ipaddr
經過find_entry
執行後,來看看update_arp_entry
執行的幾種不同情況。
首先可以肯定的是,update_arp_entry
的兩個引數ipaddr
和ethaddr
必是互相匹配的,因為它們是從源主機發來的ip包或arp包中解析出來的,代表了源主機的mac位址和ip位址。find_entry
利用ipaddr
作為引數執行後,返回乙個arp表項索引。如果該表項處於empty
狀態,那麼該表項現在一定是新建立的,此時設定該表項為stable
狀態並設定該表項其他字段值後即結束。如果該表項是處於pending
狀態,由於此時已經有了和ipaddr
匹配的mac位址返回,所以該表項也被設定為stable
狀態並同時設定該表項其他字段值。如果該表項是處於stable
狀態,其實此時只需要將ctime
的值復位即可,但是lwip為了節省**量,它還是選擇像上面的情況一樣做相同的處理,這樣雖然有些步驟是多餘的,但並不影響函式功能。最後都會檢查該表項是否還有資料需要傳送,如果有,則將所有資料報傳送出去。
現在是時候從巨集觀上來看看到底arp是怎麼乙個工作流程,以及它在整個lwip協議棧當中發揮的重要作用。。
該圖簡潔明瞭的解釋了基本所有lwip的資料報接收與傳送的全過程。我們可以看到幾個熟悉的身影:etharp_query、etharp_request、update_arp_entry
。在前面已經講過了的!
arp從功能上來說可以簡單的分成兩個部分:當有資料報輸入時,更新arp表,如果是ip包則遞交給ip層,如果是arp包,則針對不同的arp包型別做相應的響應;當向目的ip傳送乙個資料報的時候,需要通過arp實現ip到mac位址的對映,必要時,需要傳送廣播資料報獲得目標機器的mac位址。
lwip利用netif.input
指向的函式接收乙太網資料報,通常這個函式是ethernet_input
。注意,這裡並不是說ethernet_input
直接與底層硬體互動接收資料報,而是更底層的函式接收到資料報後將資料報遞交給ethernet_input
,ethernet_input
再對其進行處理。
乙太網的幀型別可以是:ip,arp,甚至可以是pppoe
,wlan
等。這裡主要分析ip和arp兩種型別的資料報。ethernet_input
根據乙太網首部的型別字段判斷收到的資料報的型別,如果是ip包,則將該包遞交給etharp_ip_input
,如果是arp包,則將該包遞交給etharp_arp_input
。
對於ip型別的資料報,etharp_ip_input
首先檢查是否開啟了etharp_trust_ip_mac
這個選項,如果開啟了就是要用這個幀中的資訊和update_arp_entry
函式來更新arp表(利用幀首部的源mac位址和幀資料中ip報文中的源ip位址),然後丟棄乙太網幀首部,將ip報文通過ip_input
函式遞交給ip層。
對於arp型別的資料報,etharp_arp_input
函式首先利用資料報頭資訊更新arp表的內容,
然後再判斷該arp資料報的型別,如果是arp請求包,則首先判斷這個包是不是給自己的,如果是給自己的,則在原有包的基礎上重組乙個arp應答包傳送出去(注意此處並沒有重新分配乙個pbuf
,而是借用了原來的緩衝結構)。如果不是給自己的,則直接忽略。如果是arp應答包,主要的工作就是更新arp表,但是這一步已經在arp包剛進來的時候就處理了,所以這裡不需要再重複做,這樣arp包的處理也完畢。
lwip利用netif.output
指向的函式傳送ip資料報,通常這個函式是etharp_output
。注意,這裡並不是說etharp_output
直接與底層硬體互動傳送資料報,而是將資料報做相應的處理,最終遞交給netif.linkoutput
函式來傳送的。
etharp_output
函式接收ip層要傳送的資料報,並將資料報傳送出去。由於是傳送ip資料報,所以函式一開始需要增加緩衝區大小,大小為乙太網的資料首部的大小。然後檢查ip位址,可以分為廣播包,多播包,單播包(單播包又分為是區域網內部還是區域網外面)。
廣播包:判斷目的ip位址是不是為全1,或者是全0(老版本中使用的),如果是廣播包則目的ip的mac位址不需要查詢arp表,直接將mac位址設定為全1傳送即可,即mac六個位元組值為0xff,0xff,0xff,0xff,0xff,0xff
。
多播包:判斷目的ip位址是不是d類位址,即0xe******x
,如果是多播的話,mac位址也是確定的,即將mac位址01-00-5e-00-00-00
的低23位設定為ip位址的低23位。對於以上的兩種資料報,etharp_output
直接呼叫函式etharp_send_ip
將資料報傳送出去。
單播包:要比較目的ip和本地ip位址,看是否是區域網內的,不是區域網內的,則將目的ip位址設定為預設閘道器的位址,然後再統一呼叫etharp_query
函式將資料報傳送出去,注意這些資料報在這種情況下可能被連線在相關arp表項的傳送鍊錶上,等待傳送。
LwIP中的ARP協議實現(2)
lwip中的arp實現 1 之 arp快取表的資料結構 lwip中的arp實現 2 之 arp快取表的超時處理 lwip中的arp實現 3 之 傳送arp請求包 lwip中的arp實現 4 之 arp資料報接收 lwip中的arp實現 5 之 arp資料報傳送 arp表項的生存時間是5分鐘,而arp...
LWIP協議棧學習 一 ARP協議
arp所實現的功能就是通過ip位址獲取到mac位址,並且將ip和mac對映到一起,儲存到arp表中.dest mac src mac frame type hardware type protocol type hardware addr len protocol addr len opsend m...
LWIP協議 TCP建立流程
如果客戶端發來tcp連線請求,那麼伺服器端就會呼叫tcp new函式來建立乙個tcp塊。tcp new函式的主體內容就是tcp alloc tcp prio normal 函式,傳遞預設的tcp塊優先順序為64 優先順序範圍1 127 tcp alloc pcb struct tcp pcb mem...