主要簡述TCPIP協定族相關的。
ARP協定原始碼在etharp.c和etharp.h中,也是本次筆記的主要內容。
ARP原始碼實現的重要資料結構:
原文:李柱明部落格
TCP/IP協定的網路層有自己的IP地址。
單看網路層,傳輸封包時只需要知道目標主機的IP地址即可。
但是網路層封包下傳到鏈路層時,鏈路層需要知道下一個節點的MAC地址,才能發包。
為了實現網路層對MAC地址無感,又能實現封包收發,就需要把IP地址和MAC地址繫結。
一個網路卡,有IP地址,而網路卡對接物理裝置時,物理裝置有MAC地址,可以把IP地址和網路卡裝置MAC地址繫結。
而有時候,IP地址可能是動態的,即是當前網路卡裝置根據需求被賦予不同的IP,所以IP地址與MAC地址對映也需要動態才能更好地把網路層和鏈路層分割。
地址解析協定,即ARP(Address Resolution Protocol),是根據IP地址獲取實體地址的一個TCP/IP協定。
主機A知道主機B的ip地址,但是在二層鏈路,也就是資料鏈路層,是通過mac地址進行轉發的,通過ARP協定實現IP和MAC地址繫結。
ARP協定有靜態獲取和動態獲取:
ARP請求和應答分組的格式如圖:
乙太網首部:
ARP報文:
硬體型別(2):硬體地址的型別。
協定型別(2):表示硬體地址要對映的協定地址型別。
硬體地址長度(1):硬體地址的長度,以位元組為單位。
協定地址長度(1):
OP欄位(2):操作欄位。
傳送端乙太網地址(6)。
傳送端IP地址(4)。
目的乙太網地址(6)。
目的IP地址(4)。
小筆記:
ARP簡要互動:
對於一個ARP請求來說,除目的端硬體地址外的所有其他的欄位都有填充值。
當系統收到一份目的端為本機的ARP請求報文後,它就把硬體地址填進去,然後用兩個目的端地址分別替換兩個傳送端地址,並把操作欄位置為2(ARP應答),最後把它傳送回去。
ARP互動報文例子圖,wireshark分析:
每臺主機或路由器在其記憶體中具有一個ARP快取表(ARP table),這張表包含IP地址到MAC地址的對映關係。
網路層的IP封包需要經過鏈路層轉發時,可以直接查詢快取表是否有這個IP對映的MAC。
如果有,目標鏈路層資料框的目標MAC就直接使用這個MAC,就能轉發了。
如果沒有,通過ARP協定,往鏈路層區域網內廣播一下,詢問下有沒有這個IP對應的結點裝置,如果有就把這個IP對應的鏈路層裝置的MAC返回到這個主機,然後這個主機的鏈路層使用這個MAC傳送資料框出去。而且可以把這個MAC及其對應的IP儲存到自己的ARP快取表中,方便下次直接使用。當然,這個對映也有過期檢查,需要超時機制維護。
lwip的快取表:static struct etharp_entry arp_table[ARP_TABLE_SIZE];
ARP_TABLE_SIZE
預設為10,即是預設能快取10條ARP對映記錄。
struct etharp_entry
:
struct etharp_entry {
#if ARP_QUEUEING
/* 指向此ARP表項上掛起的封包佇列的指標. */
struct etharp_q_entry *q;
#else /* ARP_QUEUEING */
/* 指向此ARP表項上的單個掛起的封包佇列的指標 */
struct pbuf *q;
#endif /* ARP_QUEUEING */
/* 目標IP地址 */
ip4_addr_t ipaddr;
/* 當前ARP對映記錄對應網路卡資訊 */
struct netif *netif;
/* 目標IP對應的MAC地址 */
struct eth_addr ethaddr;
/* 當前netry的生存時間 */
u16_t ctime;
/* 當前netry的狀態資訊 */
u8_t state;
};
struct etharp_q_entry *q;
:
這個欄位用於指向快取表的封包緩衝佇列。
在IP層傳送一個封包時,會先在ARP對映表中查詢與目的IP地址對應的MAC地址,這樣才能封裝乙太網幀,才能在鏈路層把封包傳送出去。
但是如果IP層傳送一個封包時,在ARP對映表中查不到對應的硬體地址MAC,就傳送一個ARP請求包,在請求過程中,把這個IP層的封包快取到這個佇列q
先,直到請求成功後,獲取這個IP層封包IP對應的MAC地址或請求失敗為止。
對於PBUFF_ERF
、PBUF_POOL
、PBUF_RAM
型別的封包是不允許直接掛到ARP entry的掛起快取佇列上的,因為核心等待目標主機的ARP應答期間,這些資料有可能會被上層改動,所以LwIP需要將這些pbuf封包拷貝到新的空間,等待傳送。
這個佇列的資料結構:
MEMP_ARP_QUEUE
記憶體池中有這個資料結構的記憶體資源。共有MEMP_NUM_ARP_QUEUE
個,預設為30個。#if ARP_QUEUEING
struct etharp_q_entry {
struct etharp_q_entry *next; /* 下一個節點 */
struct pbuf *p; /* pbuf */
};
#endif /* ARP_QUEUEING */
u8_t state;
:
/** ARP states */
enum etharp_state {
ETHARP_STATE_EMPTY = 0, /* 空閒態 */
ETHARP_STATE_PENDING, /* pending態 */
ETHARP_STATE_STABLE, /* 有效態 */
ETHARP_STATE_STABLE_REREQUESTING_1, /* 有效過渡態1 */
ETHARP_STATE_STABLE_REREQUESTING_2 /* 有效過渡態2 */
#if ETHARP_SUPPORT_STATIC_ENTRIES
, ETHARP_STATE_STATIC /* 靜態entry */
#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
};
當前netry的狀態資訊:
ETHARP_STATE_EMPTY
:空狀態。當前entry資源無效,可以被填充使用。ETHARP_STATE_PENDING
:PENDING態。當前entry正在ARP請求,但是還沒收到ARP響應。ETHARP_STATE_STABLE
:有效態。當前entry記錄的IP地址與MAC地址對映有效。ETHARP_STATE_STABLE_REREQUESTING_1
:有效過渡態1。就是為了防止entry塊過期前頻繁發起ARP請求。ETHARP_STATE_STABLE_REREQUESTING_2
:有效過渡態2。ETHARP_STATE_STATIC
:靜態條目。手動設定的ARP對映,一直有效。當表項是ETHARP_STATE_STABLE
的時候又傳送一個ARP請求包,那麼表項狀態會暫時被設定為THARP_STATE_STABLE_REREQUESTING_1
,然後被設定為ETHARP_STATE_STABLE_REREQUESTING_2
狀態,這些是一個過渡狀態,當收到ARP應答後,表項又會被設定為ETHARP_STATE_STABLE
,這樣能保持表項的有效。
比如,每個IP層的封包都會先遍歷ARP快取表,如果找到有效的條目後,直接使用該條目,然後會繼續呼叫etharp_output_to_arp_index()
把資料傳送出去,原始碼如下:
在傳送IP包時檢查當前被使用的ARP entry是否塊過期,如果快過期,需要發起ARP請求更新當前條目,更新有兩種級別:
ARP_AGE_REREQUEST_USED_UNICAST
:預設在條目超時前30秒,發起單播級別的ARP請求,減少不必要的廣播。ARP_AGE_REREQUEST_USED_BROADCAST
:預設在條目超時前15秒,發起廣播級別的ARP請求。/* 為避免由於ARP表項超時導致穩定使用的連線中斷,需要在ARP表項過期前重新請求,更新ARP快取表 */
#define ARP_AGE_REREQUEST_USED_UNICAST (ARP_MAXAGE - 30)
#define ARP_AGE_REREQUEST_USED_BROADCAST (ARP_MAXAGE - 15)
/* Just a small helper function that sends a pbuf to an ethernet address in the arp_table specified by the index 'arp_idx'. */
static err_t
etharp_output_to_arp_index(struct netif *netif, struct pbuf *q, netif_addr_idx_t arp_idx)
{
LWIP_ASSERT("arp_table[arp_idx].state >= ETHARP_STATE_STABLE",
arp_table[arp_idx].state >= ETHARP_STATE_STABLE);
/* 在entry快過期前,發起ARP請求進行更新。
為了防止在這段時間頻繁發起ARP請求,所以引入有效過渡態。
這裡為有效態才能發起ARP請求。 */
if (arp_table[arp_idx].state == ETHARP_STATE_STABLE) {
if (arp_table[arp_idx].ctime >= ARP_AGE_REREQUEST_USED_BROADCAST) {
/* 使用廣播級別(過期前15秒),發起標準ARP請求 */
if (etharp_request(netif, &arp_table[arp_idx].ipaddr) == ERR_OK) {
arp_table[arp_idx].state = ETHARP_STATE_STABLE_REREQUESTING_1; /* 更新為有效過渡態1 */
}
} else if (arp_table[arp_idx].ctime >= ARP_AGE_REREQUEST_USED_UNICAST) {
/* 發出單播ARP請求(過期前30秒),以防止不必要的廣播 */
if (etharp_request_dst(netif, &arp_table[arp_idx].ipaddr, &arp_table[arp_idx].ethaddr) == ERR_OK) {
arp_table[arp_idx].state = ETHARP_STATE_STABLE_REREQUESTING_1; /* 更新為有效過渡態1 */
}
}
}
/* IP層傳送資料 */
return ethernet_output(netif, q, (struct eth_addr *)(netif->hwaddr), &arp_table[arp_idx].ethaddr, ETHTYPE_IP);
}
ARP快取表每條對映記錄都是有有效期的(靜態除外),在struct etharp_entry
的u16_t ctime;
這個欄位中記錄這點錢entry的生存時間。
ARP超時處理常式是etharp_tmr()
,是一個週期定時函數。
相關宏:
ARP_TMR_INTERVAL
:該函數的節拍為ARP_TMR_INTERVAL
,預設為1000,即是1秒跑一次。
ARP_MAXAGE
:用於限制ARP條目最大生存時間,預設為300,且節拍為1秒,所以ARP的entry預設最大生存時間是5分鐘。
ARP_MAXPENDING
:限制ARP請求響應超時,也是重發ARP請求的次數,預設為5,且節拍為1秒,所以ARP請求響應預設超時為5秒。
/**
* 清除ARP表中過期的表項
* 該函數應該每隔ARP_TMR_INTERVAL毫秒(1秒)呼叫一次,以使ARP表項過期
*/
void
etharp_tmr(void)
{
int i;
LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_timer\n"));
/* 遍歷、刪除ARP表中過期的表項 */
for (i = 0; i < ARP_TABLE_SIZE; ++i) {
u8_t state = arp_table[i].state;
if (state != ETHARP_STATE_EMPTY /* 跳過空閒態的entry */
#if ETHARP_SUPPORT_STATIC_ENTRIES
&& (state != ETHARP_STATE_STATIC) /* 跳過靜態的entry */
#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
) {
arp_table[i].ctime++; /* 記錄當前entry的生存時間 */
if ((arp_table[i].ctime >= ARP_MAXAGE) ||
((arp_table[i].state == ETHARP_STATE_PENDING) &&
(arp_table[i].ctime >= ARP_MAXPENDING))) { /* entry生存時間超時或者ARP請求超時都要清空本entry */
/* pending or stable entry has become old! */
LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_timer: expired %s entry %d.\n",
arp_table[i].state >= ETHARP_STATE_STABLE ? "stable" : "pending", i));
/* 清理剛剛過期的條目 */
etharp_free_entry(i);
} else if (arp_table[i].state == ETHARP_STATE_STABLE_REREQUESTING_1) {
/* 過渡態1更新到過渡態2,為了防止上層在兩秒能發起多次ARP更新請求 */
arp_table[i].state = ETHARP_STATE_STABLE_REREQUESTING_2;
} else if (arp_table[i].state == ETHARP_STATE_STABLE_REREQUESTING_2) {
/* 恢復到有效態。允許ARP */
arp_table[i].state = ETHARP_STATE_STABLE;
} else if (arp_table[i].state == ETHARP_STATE_PENDING) {
/* 還沒收到ARP響應,重發ARP請求 */
etharp_request(arp_table[i].netif, &arp_table[i].ipaddr);
}
}
}
}
能觸發ARP快取表entry更新的情況:
ARP超時處理。把過期或者ARP請求超時的arp entry刪除。
IP層傳送資料。經過ARP協定時:
新建arp entry邏輯:
呼叫etharp_find_entry()
函數獲取一個entry。
該函數的引數u8_t flags;
表示申請arp entry資源的方式:
ETHARP_FLAG_TRY_HARD
:允許覆蓋已有arp entry。
empty entry
> oldest stable entry
> oldest pending entry without queued packets
> oldest pending entry with queued packets
。ETHARP_FLAG_FIND_ONLY
:唯讀模式。如果ARP快取表中沒有匹配IP(和網路卡)的arp entry,則返回失敗,封包處理終止。
/** the ARP message, see RFC 826 ("Packet format") */
struct etharp_hdr {
PACK_STRUCT_FIELD(u16_t hwtype); /* 硬體型別 */
PACK_STRUCT_FIELD(u16_t proto); /* 協定型別 */
PACK_STRUCT_FLD_8(u8_t hwlen); /* 硬體地址長度 */
PACK_STRUCT_FLD_8(u8_t protolen); /* 協定地址長度 */
PACK_STRUCT_FIELD(u16_t opcode); /* 操作欄位 */
PACK_STRUCT_FLD_S(struct eth_addr shwaddr); /* 源硬體地址 */
PACK_STRUCT_FLD_S(struct ip4_addr_wordaligned sipaddr); /* 源協定地址 */
PACK_STRUCT_FLD_S(struct eth_addr dhwaddr); /* 目標硬體地址 */
PACK_STRUCT_FLD_S(struct ip4_addr_wordaligned dipaddr); /* 目標協定地址 */
} PACK_STRUCT_STRUCT;
ARP請求包是通過etharp_raw()
函數進行組包和傳送的,然後通過封裝該函數得出不同需求的ARP請求函數供給上層使用。
/**
* Send a raw ARP packet (opcode and all addresses can be modified)
*
* @param netif the lwip network interface on which to send the ARP packet
* @param ethsrc_addr the source MAC address for the ethernet header
* @param ethdst_addr the destination MAC address for the ethernet header
* @param hwsrc_addr the source MAC address for the ARP protocol header
* @param ipsrc_addr the source IP address for the ARP protocol header
* @param hwdst_addr the destination MAC address for the ARP protocol header
* @param ipdst_addr the destination IP address for the ARP protocol header
* @param opcode the type of the ARP packet
* @return ERR_OK if the ARP packet has been sent
* ERR_MEM if the ARP packet couldn't be allocated
* any other err_t on failure
*/
static err_t
etharp_raw(struct netif *netif, const struct eth_addr *ethsrc_addr,
const struct eth_addr *ethdst_addr,
const struct eth_addr *hwsrc_addr, const ip4_addr_t *ipsrc_addr,
const struct eth_addr *hwdst_addr, const ip4_addr_t *ipdst_addr,
const u16_t opcode)
{
struct pbuf *p;
err_t result = ERR_OK;
struct etharp_hdr *hdr;
LWIP_ASSERT("netif != NULL", netif != NULL);
/* 鏈路層組包,為ARP報文申請記憶體資源 */
p = pbuf_alloc(PBUF_LINK, SIZEOF_ETHARP_HDR, PBUF_RAM);
if (p == NULL) { /* 記憶體資源申請失敗 */
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
("etharp_raw: could not allocate pbuf for ARP request.\n"));
ETHARP_STATS_INC(etharp.memerr);
return ERR_MEM;
}
LWIP_ASSERT("check that first pbuf can hold struct etharp_hdr",
(p->len >= SIZEOF_ETHARP_HDR));
hdr = (struct etharp_hdr *)p->payload;
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_raw: sending raw ARP packet.\n"));
hdr->opcode = lwip_htons(opcode); /* 操作欄位 */
LWIP_ASSERT("netif->hwaddr_len must be the same as ETH_HWADDR_LEN for etharp!",
(netif->hwaddr_len == ETH_HWADDR_LEN));
SMEMCPY(&hdr->shwaddr, hwsrc_addr, ETH_HWADDR_LEN); /* 源MAC欄位 */
SMEMCPY(&hdr->dhwaddr, hwdst_addr, ETH_HWADDR_LEN); /* 目標MAC欄位 */
IPADDR_WORDALIGNED_COPY_FROM_IP4_ADDR_T(&hdr->sipaddr, ipsrc_addr); /* 源IP欄位 */
IPADDR_WORDALIGNED_COPY_FROM_IP4_ADDR_T(&hdr->dipaddr, ipdst_addr); /* 目標IP欄位 */
hdr->hwtype = PP_HTONS(LWIP_IANA_HWTYPE_ETHERNET); /* 硬體型別 */
hdr->proto = PP_HTONS(ETHTYPE_IP); /* 協定型別 */
hdr->hwlen = ETH_HWADDR_LEN; /* 硬體地址長度 */
hdr->protolen = sizeof(ip4_addr_t); /* 協定地址長度 */
/* 傳送ARP包 */
#if LWIP_AUTOIP
/* 如果我們源IP為本地鏈路的IP,說明本ARP報文為ARP探測包,用於檢查當前鏈路這個目標IP是否被佔用了。所以需要用乙太網幀的目標MAC需要設定為廣播MAC(參見RFC3927第2.5節最後一段)*/
if (ip4_addr_islinklocal(ipsrc_addr)) {
ethernet_output(netif, p, ethsrc_addr, ðbroadcast, ETHTYPE_ARP); /* 傳送乙太網幀 */
} else
#endif /* LWIP_AUTOIP */
{ /* 非ARP探測包,可按指定MAC封裝乙太網首部的目標MAC */
ethernet_output(netif, p, ethsrc_addr, ethdst_addr, ETHTYPE_ARP); /* 傳送乙太網幀 */
}
/* lwip狀態記錄 */
ETHARP_STATS_INC(etharp.xmit);
/* 釋放當前ARP報文資源 */
pbuf_free(p);
p = NULL;
return result;
}
etharp_request_dst()
:
/**
* Send an ARP request packet asking for ipaddr to a specific eth address.
* Used to send unicast request to refresh the ARP table just before an entry times out.
*
* @param netif the lwip network interface on which to send the request
* @param ipaddr the IP address for which to ask
* @param hw_dst_addr the ethernet address to send this packet to
* @return ERR_OK if the request has been sent
* ERR_MEM if the ARP packet couldn't be allocated
* any other err_t on failure
*/
static err_t
etharp_request_dst(struct netif *netif, const ip4_addr_t *ipaddr, const struct eth_addr *hw_dst_addr)
{
return etharp_raw(netif, (struct eth_addr *)netif->hwaddr, hw_dst_addr,
(struct eth_addr *)netif->hwaddr, netif_ip4_addr(netif), ðzero,
ipaddr, ARP_REQUEST); /* ARP請求 */
}
etharp_request_dst()
:
/**
* Send an ARP request packet asking for ipaddr.
*
* @param netif the lwip network interface on which to send the request
* @param ipaddr the IP address for which to ask
* @return ERR_OK if the request has been sent
* ERR_MEM if the ARP packet couldn't be allocated
* any other err_t on failure
*/
err_t
etharp_request(struct netif *netif, const ip4_addr_t *ipaddr)
{
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_request: sending ARP request.\n"));
return etharp_request_dst(netif, ipaddr, ðbroadcast);
}
etharp_acd_probe()
:
/**
* Send an ARP request packet probing for an ipaddr.
* Used to send probe messages for address conflict detection.
*
* @param netif the lwip network interface on which to send the request
* @param ipaddr the IP address to probe
* @return ERR_OK if the request has been sent
* ERR_MEM if the ARP packet couldn't be allocated
* any other err_t on failure
*/
err_t
etharp_acd_probe(struct netif *netif, const ip4_addr_t *ipaddr)
{
return etharp_raw(netif, (struct eth_addr *)netif->hwaddr, ðbroadcast,
(struct eth_addr *)netif->hwaddr, IP4_ADDR_ANY4, ðzero,
ipaddr, ARP_REQUEST);
}
etharp_acd_announce()
:
/**
* Send an ARP request packet announcing an ipaddr.
* Used to send announce messages for address conflict detection.
*
* @param netif the lwip network interface on which to send the request
* @param ipaddr the IP address to announce
* @return ERR_OK if the request has been sent
* ERR_MEM if the ARP packet couldn't be allocated
* any other err_t on failure
*/
err_t
etharp_acd_announce(struct netif *netif, const ip4_addr_t *ipaddr)
{
return etharp_raw(netif, (struct eth_addr *)netif->hwaddr, ðbroadcast,
(struct eth_addr *)netif->hwaddr, ipaddr, ðzero,
ipaddr, ARP_REQUEST);
}
主要分析ARP協定層的處理。
IP層封包通過ip4_output()
函數傳遞到ARP協定處理。通過ARP協定獲得目標IP主機的MAC地址才能封裝乙太網幀,在鏈路層轉發。
etharp_output()
收到IP層發來的封包後按一下邏輯分支處理:
對於廣播或者多播的封包:呼叫ethernet_output()
函數直接把封包丟給網路卡即可。
01:00:5E:00:00:00
—— 01:00:5E:7F:FF:FF
對於單播封包:
遍歷ARP快取表:遍歷時,可以從當前網路卡上次傳送封包使用的arp entry開始查起,找到就呼叫etharp_output_to_arp_index()
把IP封包轉交給鏈路層轉發。
etharp_output_to_arp_index()
概述裡面會更新維護ARP快取表當前arp entry。發起ARP請求:如果快取表中沒有當前IP封包目標IP對映的MAC地址,就需要呼叫etharp_query()
,把IP封包轉交給ARP協定處理。
etharp_query()
會發起ARP請求,在ARP請求過程中,把這些IP層的封包儲存到當前ARP條目的entry的掛起快取佇列中。直到收到ARP響應或者ARP請求超時為止。對於PBUFF_ERF
、PBUF_POOL
、PBUF_RAM
型別的封包是不允許直接掛到ARP entry的掛起快取佇列上的,因為核心等待目標主機的ARP應答期間,這些資料有可能會被上層改動,所以LwIP需要將這些pbuf封包拷貝到新的空間,等待傳送。
解析和填寫乙太網地址頭為傳出IP封包。
/**
* Resolve and fill-in Ethernet address header for outgoing IP packet.
*
* For IP multicast and broadcast, corresponding Ethernet addresses are selected and the packet is transmitted on the link.
*
* For unicast addresses, the packet is submitted to etharp_query(). In case the IP address is outside the local network, the IP address of the gateway is used.
*
* @param netif The lwIP network interface which the IP packet will be sent on.
* @param q The pbuf(s) containing the IP packet to be sent.
* @param ipaddr The IP address of the packet destination.
*
* @return
* - ERR_RTE No route to destination (no gateway to external networks),
* or the return type of either etharp_query() or ethernet_output().
*/
err_t
etharp_output(struct netif *netif, struct pbuf *q, const ip4_addr_t *ipaddr)
{
const struct eth_addr *dest;
struct eth_addr mcastaddr;
const ip4_addr_t *dst_addr = ipaddr;
/* tcpip核心鎖上鎖檢查 */
LWIP_ASSERT_CORE_LOCKED();
/* 引數校驗 */
LWIP_ASSERT("netif != NULL", netif != NULL);
LWIP_ASSERT("q != NULL", q != NULL);
LWIP_ASSERT("ipaddr != NULL", ipaddr != NULL);
if (ip4_addr_isbroadcast(ipaddr, netif)) {
/* 目標IP為廣播地址 */
/* 目標MAC也設定為廣播地址:FF-FF-FF-FF-FF-FF-FF */
dest = (const struct eth_addr *)ðbroadcast;
} else if (ip4_addr_ismulticast(ipaddr)) {
/* 目標IP為多播地址 */
/* 目標MAC也設定為多播地址:01:00:5E:00:00:00 —— 01:00:5E:7F:FF:FF */
mcastaddr.addr[0] = LL_IP4_MULTICAST_ADDR_0;
mcastaddr.addr[1] = LL_IP4_MULTICAST_ADDR_1;
mcastaddr.addr[2] = LL_IP4_MULTICAST_ADDR_2;
mcastaddr.addr[3] = ip4_addr2(ipaddr) & 0x7f;
mcastaddr.addr[4] = ip4_addr3(ipaddr);
mcastaddr.addr[5] = ip4_addr4(ipaddr);
dest = &mcastaddr;
} else { /* 目標IP為單播地址 */
netif_addr_idx_t i;
/* 判斷目標IP是否和原IP主機處於同一個子網上,如果不是,則修改要查詢MAC的的IP為當前網路卡的閘道器IP */
if (!ip4_addr_net_eq(ipaddr, netif_ip4_addr(netif), netif_ip4_netmask(netif)) &&
!ip4_addr_islinklocal(ipaddr)) { /* 不是同一個子網,也不是本地鏈路地址,需要把封包轉發到閘道器 */
#if LWIP_AUTOIP
struct ip_hdr *iphdr = LWIP_ALIGNMENT_CAST(struct ip_hdr *, q->payload);
/* 根據RFC 3297 2.6.2章(轉發規則),如果IP地址為本地鏈路地址(169.254.0.0/16),這樣的IP封包是不能通過路由器轉發的 */
if (!ip4_addr_islinklocal(&iphdr->src)) /* 源IP地址不是本地鏈路地址 */
#endif /* LWIP_AUTOIP */
{
#ifdef LWIP_HOOK_ETHARP_GET_GW
/* 閘道器勾點函數,可以自定義選擇閘道器 */
dst_addr = LWIP_HOOK_ETHARP_GET_GW(netif, ipaddr);
if (dst_addr == NULL)
#endif /* LWIP_HOOK_ETHARP_GET_GW */
{
/* 檢視網路卡是否有預設閘道器 */
if (!ip4_addr_isany_val(*netif_ip4_gw(netif))) {
/* 獲取網路卡預設閘道器 */
dst_addr = netif_ip4_gw(netif);
} else { /* 沒找到有效閘道器 */
/* 沒有路由到目的地錯誤(預設閘道器丟失) */
return ERR_RTE;
}
}
}
}
#if LWIP_NETIF_HWADDRHINT
if (netif->hints != NULL) {
/* 網路卡中上次發包時使用的arp entry優先遍歷 */
netif_addr_idx_t etharp_cached_entry = netif->hints->addr_hint;
if (etharp_cached_entry < ARP_TABLE_SIZE) {
#endif /* LWIP_NETIF_HWADDRHINT */
if ((arp_table[etharp_cached_entry].state >= ETHARP_STATE_STABLE) && /* arp entry有效 */
#if ETHARP_TABLE_MATCH_NETIF
(arp_table[etharp_cached_entry].netif == netif) && /* arp entry對應網路卡匹配 */
#endif
(ip4_addr_eq(dst_addr, &arp_table[etharp_cached_entry].ipaddr))) { /* 找到目標IP的MAC對映 */
ETHARP_STATS_INC(etharp.cachehit); /* 記錄lwip arp相關狀態 */
/* 傳送IP封包 */
return etharp_output_to_arp_index(netif, q, etharp_cached_entry);
}
#if LWIP_NETIF_HWADDRHINT
}
}
#endif /* LWIP_NETIF_HWADDRHINT */
/* 如果網路卡指定先遍歷的arp entry沒有找到合法對映,就需要遍歷ARP快取表 */
for (i = 0; i < ARP_TABLE_SIZE; i++) {
if ((arp_table[i].state >= ETHARP_STATE_STABLE) && /* arp entry有效 */
#if ETHARP_TABLE_MATCH_NETIF
(arp_table[i].netif == netif) && /* 網路卡匹配對應 */
#endif
(ip4_addr_eq(dst_addr, &arp_table[i].ipaddr))) { /* 目標IP對應 */
/* 找到目標IP的MAC對映 */
/* 把當前arp entry索引儲存到網路卡中,以便下次快速遍歷 */
ETHARP_SET_ADDRHINT(netif, i);
/* 傳送IP封包 */
return etharp_output_to_arp_index(netif, q, i);
}
}
/* 在ARP快取表中沒有找到對應的ARP entry,就需要發起ARP 請求 */
return etharp_query(netif, dst_addr, q);
}
/* 對於廣播或組播的封包,直接知道了目標硬體欄位的MAC地址,可以直接往鏈路層傳送 */
return ethernet_output(netif, q, (struct eth_addr *)(netif->hwaddr), dest, ETHTYPE_IP);
}
etharp_output_to_arp_index()
這個函數實現維護arp entry超時前更新,然後把封包通過ethernet_output()
轉交給鏈路層。
其原始碼參考前面快取表狀態。
如果IP封包為單播包,且在 ARP 快取表中沒有找到對應的MAC地址,就需要呼叫etharp_query()
函數發起ARP請求處理。
主要內容:
/**
* Send an ARP request for the given IP address and/or queue a packet.
*
* If the IP address was not yet in the cache, a pending ARP cache entry
* is added and an ARP request is sent for the given address. The packet
* is queued on this entry.
*
* If the IP address was already pending in the cache, a new ARP request
* is sent for the given address. The packet is queued on this entry.
*
* If the IP address was already stable in the cache, and a packet is
* given, it is directly sent and no ARP request is sent out.
*
* If the IP address was already stable in the cache, and no packet is
* given, an ARP request is sent out.
*
* @param netif The lwIP network interface on which ipaddr
* must be queried for.
* @param ipaddr The IP address to be resolved.
* @param q If non-NULL, a pbuf that must be delivered to the IP address.
* q is not freed by this function.
*
* @note q must only be ONE packet, not a packet queue!
*
* @return
* - ERR_BUF Could not make room for Ethernet header.
* - ERR_MEM Hardware address unknown, and no more ARP entries available
* to query for address or queue the packet.
* - ERR_MEM Could not queue packet due to memory shortage.
* - ERR_RTE No route to destination (no gateway to external networks).
* - ERR_ARG Non-unicast address given, those will not appear in ARP cache.
*
*/
err_t
etharp_query(struct netif *netif, const ip4_addr_t *ipaddr, struct pbuf *q)
{
struct eth_addr *srcaddr = (struct eth_addr *)netif->hwaddr;
err_t result = ERR_MEM;
int is_new_entry = 0;
s16_t i_err;
netif_addr_idx_t i;
/* 如果是廣播、組播或者無效的單播,不需要發起ARP請求,不用在繼續往下處理 */
if (ip4_addr_isbroadcast(ipaddr, netif) ||
ip4_addr_ismulticast(ipaddr) ||
ip4_addr_isany(ipaddr)) {
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: will not add non-unicast IP address to ARP cache\n"));
return ERR_ARG;
}
/* 按規則找到arp entry進行操作 */
i_err = etharp_find_entry(ipaddr, ETHARP_FLAG_TRY_HARD, netif);
if (i_err < 0) { /* 沒找到能用的arp entry */
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: could not create ARP entry\n"));
if (q) {
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: packet dropped\n"));
ETHARP_STATS_INC(etharp.memerr);
}
return (err_t)i_err;
}
LWIP_ASSERT("type overflow", (size_t)i_err < NETIF_ADDR_IDX_MAX);
i = (netif_addr_idx_t)i_err; /* 找到合法的arp entry */
if (arp_table[i].state == ETHARP_STATE_EMPTY) { /* 是新的arp entry,做下標記 */
is_new_entry = 1; /* 標記下當前為新的arp entry */
arp_table[i].state = ETHARP_STATE_PENDING; /* 更新為pending態 */
/* 儲存網路卡到arp entry中 */
arp_table[i].netif = netif;
}
/* arp請求中或者arp entry有效即可往下走 */
LWIP_ASSERT("arp_table[i].state == PENDING or STABLE",
((arp_table[i].state == ETHARP_STATE_PENDING) ||
(arp_table[i].state >= ETHARP_STATE_STABLE)));
/* do we have a new entry? or an implicit query request? */
if (is_new_entry || (q == NULL)) { /* 新的arp entry或者隱式ARP請求都需要重新發起ARP請求 */
/* 傳送ARP請求 */
result = etharp_request(netif, ipaddr);
if (result != ERR_OK) {
/* ARP請求傳送失敗 */
/* 該故障可能只是暫時的,而且在etharp_tmr()週期定時函數中會重新發出ARP請求,所以這裡先跳過 */
} else {
/* ARP請求傳送成功 */
if ((arp_table[i].state == ETHARP_STATE_PENDING) && !is_new_entry) {
/* 隱式ARP請求,傳送請求成功,重置下ctime,不讓其快過期 */
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: reset ctime for entry %"S16_F"\n", (s16_t)i));
arp_table[i].ctime = 0;
}
}
if (q == NULL) {
return result; /* 隱式ARP請求的話,不需要往下儲存資料了 */
}
}
LWIP_ASSERT("q != NULL", q != NULL);
if (arp_table[i].state >= ETHARP_STATE_STABLE) { /* 當前條目已經有效 */
/* 更新下當前網路卡傳送IP封包時使用的arp entry,方便下次優先遍歷 */
ETHARP_SET_ADDRHINT(netif, i);
/* 可以直接把IP封包交給鏈路層轉發 */
result = ethernet_output(netif, q, srcaddr, &(arp_table[i].ethaddr), ETHTYPE_IP);
} else if (arp_table[i].state == ETHARP_STATE_PENDING) { /* ARP請求中 */
/* 需要把當前IP封包放到當前arp entry的快取佇列中 */
struct pbuf *p;
int copy_needed = 0;
/* 如果q這個pbuf鏈中包含一個可改動的pbuf(即是PBUFF_ERF、PBUF_POOL、PBUF_RAM),都需要拷貝整個pbuf鏈到arp entry的快取佇列中. */
p = q;
while (p) {
LWIP_ASSERT("no packet queues allowed!", (p->len != p->tot_len) || (p->next == NULL));
if (PBUF_NEEDS_COPY(p)) { /* 遍歷pbuf鏈中各個pbuf,是否含有可改動的pbuf */
copy_needed = 1; /* 需要拷貝整個pbuf鏈 */
break;
}
p = p->next;
}
if (copy_needed) {
/* 將整個包複製到新的pbuf中 */
p = pbuf_clone(PBUF_LINK, PBUF_RAM, q);
} else {
/* 參照舊的pbuf就足夠了 */
p = q;
pbuf_ref(p);
}
if (p != NULL) {
#if ARP_QUEUEING
struct etharp_q_entry *new_entry;
/* 申請一個新的 arp entry queue 節點資源 */
new_entry = (struct etharp_q_entry *)memp_malloc(MEMP_ARP_QUEUE);
if (new_entry != NULL) {
unsigned int qlen = 0; /* 記錄佇列節點數 */
new_entry->next = NULL;
new_entry->p = p; /* 把IP層需要傳送的封包先放到當前arp entry queue節點 */
if (arp_table[i].q != NULL) {
/* 佇列已經存在,將新條目插入到佇列尾 */
struct etharp_q_entry *r;
r = arp_table[i].q;
qlen++;
while (r->next != NULL) {
r = r->next;
qlen++;
}
r->next = new_entry;
} else {
/* 佇列不存在,當前節點放到佇列首部 */
arp_table[i].q = new_entry;
}
#if ARP_QUEUE_LEN
if (qlen >= ARP_QUEUE_LEN) { /* 佇列超員後,需要丟棄舊報文,為新報文騰位 */
struct etharp_q_entry *old;
old = arp_table[i].q;
arp_table[i].q = arp_table[i].q->next; /* 刪除最老的報文 */
pbuf_free(old->p); /* 釋放該報文資源 */
memp_free(MEMP_ARP_QUEUE, old); /* 回收該節點資源 */
}
#endif
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: queued packet %p on ARP entry %"U16_F"\n", (void *)q, i));
result = ERR_OK; /* ARP請求處理完畢 */
} else { /* ARP快取節點MEMP_ARP_QUEUE記憶體池沒有資源了 */
pbuf_free(p); /* 釋放拷貝的的pbuf資源 */
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: could not queue a copy of PBUF_REF packet %p (out of memory)\n", (void *)q));
result = ERR_MEM; /* 返回記憶體不足 */
}
#else /* ARP_QUEUEING */ /* arp entry快取佇列只能是單包設定 */
/* 對於每個ARP請求總是隻排隊一個包,釋放之前排隊的包 */
if (arp_table[i].q != NULL) {
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: dropped previously queued packet %p for ARP entry %"U16_F"\n", (void *)q, (u16_t)i));
pbuf_free(arp_table[i].q);
}
arp_table[i].q = p;
result = ERR_OK;
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: queued packet %p on ARP entry %"U16_F"\n", (void *)q, (u16_t)i));
#endif /* ARP_QUEUEING */
} else { /* 拷貝pbuf連結串列時PBUF_RAM記憶體堆空間不足 */
ETHARP_STATS_INC(etharp.memerr);
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: could not queue a copy of PBUF_REF packet %p (out of memory)\n", (void *)q));
result = ERR_MEM; /* 返回記憶體不足 */
}
}
return result; /* 搞完 */
}
empty entry
> oldest stable entry
> oldest pending entry without queued packets
> oldest pending entry with queued packets
。/**
* Search the ARP table for a matching or new entry.
*
* If an IP address is given, return a pending or stable ARP entry that matches
* the address. If no match is found, create a new entry with this address set,
* but in state ETHARP_EMPTY. The caller must check and possibly change the
* state of the returned entry.
*
* If ipaddr is NULL, return a initialized new entry in state ETHARP_EMPTY.
*
* In all cases, attempt to create new entries from an empty entry. If no
* empty entries are available and ETHARP_FLAG_TRY_HARD flag is set, recycle
* old entries. Heuristic choose the least important entry for recycling.
*
* @param ipaddr IP address to find in ARP cache, or to add if not found.
* @param flags See @ref etharp_state
* @param netif netif related to this address (used for NETIF_HWADDRHINT)
*
* @return The ARP entry index that matched or is created, ERR_MEM if no
* entry is found or could be recycled.
*/
static s16_t
etharp_find_entry(const ip4_addr_t *ipaddr, u8_t flags, struct netif *netif)
{
s16_t old_pending = ARP_TABLE_SIZE; /* pending態沒有阻塞資料最老的arp entry */
s16_t old_stable = ARP_TABLE_SIZE; /* 有效態最老的arp entry */
s16_t empty = ARP_TABLE_SIZE;
s16_t i = 0;
s16_t old_queue = ARP_TABLE_SIZE; /* pending態有阻塞資料最老的arp entry */
/* 對應的age */
u16_t age_queue = 0, age_pending = 0, age_stable = 0;
LWIP_UNUSED_ARG(netif); /* 防止編譯警告 */
/**
* a) do a search through the cache, remember candidates
* b) select candidate entry
* c) create new entry
*/
/* a) in a single search sweep, do all of this
* 1) remember the first empty entry (if any)
* 2) remember the oldest stable entry (if any)
* 3) remember the oldest pending entry without queued packets (if any)
* 4) remember the oldest pending entry with queued packets (if any)
* 5) search for a matching IP entry, either pending or stable
* until 5 matches, or all entries are searched for.
*/
for (i = 0; i < ARP_TABLE_SIZE; ++i) { /* 遍歷ARP快取表 */
u8_t state = arp_table[i].state;
if ((empty == ARP_TABLE_SIZE) && (state == ETHARP_STATE_EMPTY)) {
/* 檢索到空閒的arp entry */
LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_find_entry: found empty entry %d\n", (int)i));
/* 記住第一個空閒的arp entry */
empty = i;
} else if (state != ETHARP_STATE_EMPTY) { /* 當前遍歷到的arp entry不是空閒態 */
LWIP_ASSERT("state == ETHARP_STATE_PENDING || state >= ETHARP_STATE_STABLE",
state == ETHARP_STATE_PENDING || state >= ETHARP_STATE_STABLE);
if (ipaddr && ip4_addr_eq(ipaddr, &arp_table[i].ipaddr) /* 匹配當前arp entry的IP */
#if ETHARP_TABLE_MATCH_NETIF
&& ((netif == NULL) || (netif == arp_table[i].netif)) /* 匹配當前arp entry的網路卡 */
#endif /* ETHARP_TABLE_MATCH_NETIF */
) {
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: found matching entry %d\n", (int)i));
/* 已經找到符合要求,且已經存在的arp entry了 */
return i;
}
if (state == ETHARP_STATE_PENDING) { /* peding態 */
if (arp_table[i].q != NULL) { /* 有阻塞封包 */
if (arp_table[i].ctime >= age_queue) { /* 當前arp entry更老 */
/* 更新值 */
old_queue = i;
age_queue = arp_table[i].ctime;
}
} else
{ /* 沒有阻塞封包 */
if (arp_table[i].ctime >= age_pending) { /* 當前arp entry更老 */
/* 更新值 */
old_pending = i;
age_pending = arp_table[i].ctime;
}
}
} else if (state >= ETHARP_STATE_STABLE) { /* 有效態 */
#if ETHARP_SUPPORT_STATIC_ENTRIES
/* 不要處理靜態的arp entry */
if (state < ETHARP_STATE_STATIC)
#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
{
if (arp_table[i].ctime >= age_stable) { /* 當前arp entry更老 */
/* 更新值 */
old_stable = i;
age_stable = arp_table[i].ctime;
}
}
}
}
}
/* 已經遍歷ARP快取表完畢,沒找到匹配IP已有的arp entry。 */
if (((flags & ETHARP_FLAG_FIND_ONLY) != 0) || /* 唯讀模式,沒有匹配的arp entry就直接退出 */
((empty == ARP_TABLE_SIZE) && ((flags & ETHARP_FLAG_TRY_HARD) == 0))) { /* 非唯讀模式,但是沒找到空閒arp entry,又不能覆蓋,也直接退出 */
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: no empty entry found and not allowed to recycle\n"));
return (s16_t)ERR_MEM;
}
/* b) choose the least destructive entry to recycle:
* 1) empty entry
* 2) oldest stable entry
* 3) oldest pending entry without queued packets
* 4) oldest pending entry with queued packets
*
* { ETHARP_FLAG_TRY_HARD is set at this point }
*/
if (empty < ARP_TABLE_SIZE) { /* 找到空閒的arp entry */
i = empty;
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: selecting empty entry %d\n", (int)i));
} else { /* 沒找到空閒的arp entry */
if (old_stable < ARP_TABLE_SIZE) { /* 2) 先找最老的有效態arp entry */
/* 回收最老的有效態arp entry */
i = old_stable;
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: selecting oldest stable entry %d\n", (int)i));
/* stable態arp entry上不應該存在排隊的封包 */
LWIP_ASSERT("arp_table[i].q == NULL", arp_table[i].q == NULL);
} else if (old_pending < ARP_TABLE_SIZE) { /* 3) 再找沒有阻塞資料的pending態的arp entry */
/* 回收這條arp entry */
i = old_pending;
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: selecting oldest pending entry %d (without queue)\n", (int)i));
} else if (old_queue < ARP_TABLE_SIZE) { /* 4) 最後才找有阻塞資料的pending態的arp entry */
/* 回收這條arp entry */
i = old_queue;
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: selecting oldest pending entry %d, freeing packet queue %p\n", (int)i, (void *)(arp_table[i].q)));
} else { /* 沒找到 */
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: no empty or recyclable entries found\n"));
return (s16_t)ERR_MEM;
}
LWIP_ASSERT("i < ARP_TABLE_SIZE", i < ARP_TABLE_SIZE);
/* 找到需要需要回收的arp entry,將其回收 */
etharp_free_entry(i);
}
/* 確保當前arp entry已經為空閒態,且索引不超限制 */
LWIP_ASSERT("i < ARP_TABLE_SIZE", i < ARP_TABLE_SIZE);
LWIP_ASSERT("arp_table[i].state == ETHARP_STATE_EMPTY",
arp_table[i].state == ETHARP_STATE_EMPTY);
if (ipaddr != NULL) {
/* 儲存IP到新建arp entry */
ip4_addr_copy(arp_table[i].ipaddr, *ipaddr);
}
arp_table[i].ctime = 0; /* 重置age */
#if ETHARP_TABLE_MATCH_NETIF
arp_table[i].netif = netif; /* 儲存網路卡到新建arp entry */
#endif /* ETHARP_TABLE_MATCH_NETIF */
return (s16_t)i; /* 返回找到符合要求的arp entry */
}
主要分析ARP協定層的處理。
通過前面分析了乙太網鏈路層收到資料框後j由ethernet_input()
,如果是一個合法的乙太網資料框,並且協定是ARP型別,就會上傳到etharp_input()
處理。
etharp_input()
主要內容是:
檢查ARP報文。
ARP請求報文:如果是請求報文的目標IP和原生的匹配,就組裝ARP響應報文並行送出去。
ARP響應報文:
etharp_input()
:
/**
* Responds to ARP requests to us. Upon ARP replies to us, add entry to cache
* send out queued IP packets. Updates cache with snooped address pairs.
*
* Should be called for incoming ARP packets. The pbuf in the argument
* is freed by this function.
*
* @param p The ARP packet that arrived on netif. Is freed by this function.
* @param netif The lwIP network interface on which the ARP packet pbuf arrived.
*
* @see pbuf_free()
*/
void
etharp_input(struct pbuf *p, struct netif *netif)
{
struct etharp_hdr *hdr;
ip4_addr_t sipaddr, dipaddr;
u8_t for_us, from_us;
LWIP_ASSERT_CORE_LOCKED();
LWIP_ERROR("netif != NULL", (netif != NULL), return;);
hdr = (struct etharp_hdr *)p->payload; /* 把ARP報文拿出來 */
/* RFC 826 "Packet Reception": */
/* 檢查ARP包的合法性 */
if ((hdr->hwtype != PP_HTONS(LWIP_IANA_HWTYPE_ETHERNET)) || /* 硬體地址型別只支援乙太網型別 */
(hdr->hwlen != ETH_HWADDR_LEN) || /* 硬體地址長度檢查(MAC型別為6) */
(hdr->protolen != sizeof(ip4_addr_t)) || /* 協定地址長度檢查。目前只支援IPV4 */
(hdr->proto != PP_HTONS(ETHTYPE_IP))) /* 協定地址型別,IPv4型別 */
{ /* ARP報文校驗不通過 */
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
("etharp_input: packet dropped, wrong hw type, hwlen, proto, protolen or ethernet type (%"U16_F"/%"U16_F"/%"U16_F"/%"U16_F")\n",
hdr->hwtype, (u16_t)hdr->hwlen, hdr->proto, (u16_t)hdr->protolen));
/* 封包丟棄記錄 */
ETHARP_STATS_INC(etharp.proterr);
ETHARP_STATS_INC(etharp.drop);
/* 釋放pbuf */
pbuf_free(p);
return;
}
/* ARP報文校驗通過,記錄下 */
ETHARP_STATS_INC(etharp.recv);
#if LWIP_ACD
/* We have to check if a host already has configured our ip address and
* continuously check if there is a host with this IP-address so we can
* detect collisions.
* acd_arp_reply ensures the detection of conflicts. It will handle possible
* defending or retreating and will make sure a new IP address is selected.
* etharp_input does not need to handle packets that originate "from_us".
*/
acd_arp_reply(netif, hdr); /* IP衝突處理。AUTOIP協定範疇,如果開啟了AUTOIP,每一個收到的ARP包都會先經過這個處理。 */
#endif /* LWIP_ACD */
IPADDR_WORDALIGNED_COPY_TO_IP4_ADDR_T(&sipaddr, &hdr->sipaddr); /* 把ARP報文的源IP地址提前出來 */
IPADDR_WORDALIGNED_COPY_TO_IP4_ADDR_T(&dipaddr, &hdr->dipaddr); /* 把ARP報文的目標IP地址提前出來 */
if (ip4_addr_isany_val(*netif_ip4_addr(netif))) { /* 主機網路卡沒有設定IP地址 */
for_us = 0; /* 這個ARP包不是給我們的 */
from_us = 0; /* 也不是從我們這裡發出去再被轉回來的 */
} else { /* 主機網路卡已經被設定了IP,可以繼續分析ARP報文 */
/* ARP報文是否是指向我們的 */
for_us = (u8_t)ip4_addr_eq(&dipaddr, netif_ip4_addr(netif));
/* 收到的這個ARP報文是否是從我們這裡發出的 */
from_us = (u8_t)ip4_addr_eq(&sipaddr, netif_ip4_addr(netif));
}
/* 如果這個ARP報文是給我們的:
-> 更新ARP快取表;
-> 如果是一個ARP請求報文,那就組裝ARP響應報文回去。
-> 如果是一個ARP響應報文,那就把當前arp entry中的快取佇列資料發出去。
如果這個ARP報文不是給我們的:
-> 如果快取表中有空閒的arp entry,我們也可以儲存這個IP-MAC對映 */
etharp_update_arp_entry(netif, &sipaddr, &(hdr->shwaddr),
for_us ? ETHARP_FLAG_TRY_HARD : ETHARP_FLAG_FIND_ONLY); /* 更新ARP快取表 */
/* 檢查當前ARP報文的型別 */
switch (hdr->opcode) {
case PP_HTONS(ARP_REQUEST): /* ARP請求 */
/* ARP request. 如果是詢問我們這個IP對映的MAC地址,那麼就組裝ARP響應報文回去 */
LWIP_DEBUGF (ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_input: incoming ARP request\n"));
if (for_us && !from_us) { /* 這個ARP請求是詢問我們的 */
/* 傳送ARP響應 */
etharp_raw(netif,
(struct eth_addr *)netif->hwaddr, &hdr->shwaddr,
(struct eth_addr *)netif->hwaddr, netif_ip4_addr(netif),
&hdr->shwaddr, &sipaddr,
ARP_REPLY);
} else if (ip4_addr_isany_val(*netif_ip4_addr(netif))) { /* 我們還沒有設定IP */
/* { for_us == 0 and netif->ip_addr.addr == 0 } */
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_input: we are unconfigured, ARP request ignored.\n"));
} else { /* 這個ARP請求不是給我們的 */
/* { for_us == 0 and netif->ip_addr.addr != 0 } */
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_input: ARP request was not for us.\n"));
}
break;
case PP_HTONS(ARP_REPLY):
/* ARP reply. 已經更新了ARP快取表 */
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_input: incoming ARP reply\n"));
break;
default:
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_input: ARP unknown opcode type %"S16_F"\n", lwip_htons(hdr->opcode)));
ETHARP_STATS_INC(etharp.err); /* 收到的ARP報文異常,記錄下 */
break;
}
/* ARP報文處理完畢,釋放資源 */
pbuf_free(p);
}