在網絡層,源主機與目的主機之間是通過IP地址來唯一標識的。但是以太網是通過一個48bit的MAC地址來標識不同的網絡通信設備的。那麼IP數據包最終需要在物理網絡上進行發送,就必須將IP地址轉換爲目標主機對應的MAC地址。
ARP協議被用來解決上述問題。爲了實現在IP地址和MAC之間的轉換,ARP協議引入了ARP緩存表的概念。ARP緩存表中存放了最近獲得周圍其他主機IP地址到MAC地址之間的映射記錄。
系統初始化時,ARP緩存表是空的(靜態綁定除外)。此時(調用netif_set_up時),會向外界廣播一個自己的地址信息,稱爲無回報ARP請求。其他主機接收到ARP數據包之後,會更新ARP緩存表。
當主機A要與主機B通信時:
第1步:主機A在ARP緩存中,檢查與主機B的IP地址相匹配的MAC地址。
第2步:如果主機A在ARP緩存中沒有找到映射,它將在本地網絡上廣播ARP請求幀。本地網絡上的每臺主機都接收到ARP請求並且檢查是否與自己的IP地址匹配。如果發現請求的IP地址與自己的IP地址不匹配,它將丟棄ARP請求。
第3步:主機B確定ARP請求中的IP地址與自己的IP地址匹配,則將主機A的IP地址和MAC地址映射添加到本地ARP緩存中。
第4步:主機B將包含其MAC地址的ARP回覆消息直接發送回主機A。
第5步:當主機A收到從主機B發來的ARP回覆消息時,會用主機B的IP和MAC地址映射更新ARP緩存。主機B的MAC地址確定後,主機A就能通過IP地址和主機B通信了。
注:ARP緩存是有生存期的,一般爲20分鐘。生存期結束後,將再次重複上面的過程。
IP數據包從源主機到達最終目的主機的過程中,該IP數據包可能會經過中間物理網絡中多種網絡設備的轉發,在每一次轉發過程中都會涉及到地址轉換的問題。在非最後一步轉發中,當轉發主機和目的主機不在同一個局域網中時,即便知道目的主機的MAC地址,兩者也不能直接通信,必須經過路由轉發纔可以。所以此時,發送主機通過ARP協議獲得的將不是目的主機的真實MAC地址,而是一臺可以通往局域網外的路由器的MAC地址。在數據轉發的最後一步,分組必將經過最後一條物理路線到達它的目的站,發送主機這時將目的主機IP地址映射爲目標MAC地址。
ARP報文格式
/* 以太網頭部 */
struct eth_hdr {
PACK_STRUCT_FIELD(struct eth_addr dest); //目的MAC地址
PACK_STRUCT_FIELD(struct eth_addr src); //源MAC地址
PACK_STRUCT_FIELD(u16_t type); //幀類型(IP:0x0800、ARP:0x0806)
} PACK_STRUCT_STRUCT;
/* ARP頭部 */
struct etharp_hdr {
PACK_STRUCT_FIELD(u16_t hwtype); //硬件地址類型(以太網:1)
PACK_STRUCT_FIELD(u16_t proto); //映射協議地址類型(IP:0x0800)
PACK_STRUCT_FIELD(u16_t _hwlen_protolen); //硬件地址長度+協議地址長度
PACK_STRUCT_FIELD(u16_t opcode); //操作字段(ARP請求:1、ARP應答:2)
PACK_STRUCT_FIELD(struct eth_addr shwaddr); //源MAC地址
PACK_STRUCT_FIELD(struct ip_addr2 sipaddr); //源IP地址
PACK_STRUCT_FIELD(struct eth_addr dhwaddr); //目的MAC地址
PACK_STRUCT_FIELD(struct ip_addr2 dipaddr); //目的IP地址
} PACK_STRUCT_STRUCT;
/* 幀類型 */
#define ETHTYPE_ARP 0x0806 //ARP
#define ETHTYPE_IP 0x0800 //IP
#define ETHTYPE_VLAN 0x8100 //VLAN
#define ETHTYPE_PPPOEDISC 0x8863 //PPPOEDISC
#define ETHTYPE_PPPOE 0x8864 //PPPOE
/* ARP數據類型(操作字段OP) */
#define ARP_REQUEST 1 //ARP請求
#define ARP_REPLY 2 //ARP應答
前面說到網絡接口啓動的時候,要向外界發送一個無回報ARP請求,用來通知網絡中的其它主機。在分析網絡接口管理的時候遇到過,代碼如下:
/* 使能網絡接口 */
void netif_set_up(struct netif *netif)
{
/* 設置網絡接口使能標誌位 */
if (!(netif->flags & NETIF_FLAG_UP )) {
netif->flags |= NETIF_FLAG_UP;
/* 廣播無回報ARP */
if (netif->flags & NETIF_FLAG_ETHARP) {
etharp_gratuitous(netif);
}
}
}
下面從ARP發送開始,一步一步分析無回報ARP請求是如何發送的
/* 組建併發送ARP(請求/響應)數據包 */
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 struct ip_addr *ipsrc_addr, const struct eth_addr *hwdst_addr, const struct ip_addr *ipdst_addr, const u16_t opcode)
{
struct pbuf *p;
err_t result = ERR_OK;
u8_t k;
struct eth_hdr *ethhdr;
struct etharp_hdr *hdr;
/* 爲ARP請求申請內存空間 */
p = pbuf_alloc(PBUF_RAW, SIZEOF_ETHARP_PACKET, PBUF_RAM);
if (p == NULL) {
return ERR_MEM;
}
/* 以太網頭部指針 */
ethhdr = p->payload;
/* ARP頭部指針 */
hdr = (struct etharp_hdr *)((u8_t*)ethhdr + SIZEOF_ETH_HDR);
/* 操作字段 */
hdr->opcode = htons(opcode);
/* 源MAC地址和目的MAC地址 */
k = ETHARP_HWADDR_LEN;
while(k > 0) {
k--;
hdr->shwaddr.addr[k] = hwsrc_addr->addr[k];
hdr->dhwaddr.addr[k] = hwdst_addr->addr[k];
ethhdr->dest.addr[k] = ethdst_addr->addr[k];
ethhdr->src.addr[k] = ethsrc_addr->addr[k];
}
/* 源IP地址、目的IP地址 */
hdr->sipaddr = *(struct ip_addr2 *)ipsrc_addr;
hdr->dipaddr = *(struct ip_addr2 *)ipdst_addr;
/* 硬件地址類型、協議地址類型 */
hdr->hwtype = htons(HWTYPE_ETHERNET);
hdr->proto = htons(ETHTYPE_IP);
/* 硬件地址長度、協議地址長度 */
hdr->_hwlen_protolen = htons((ETHARP_HWADDR_LEN << 8) | sizeof(struct ip_addr));
/* 幀類型 */
ethhdr->type = htons(ETHTYPE_ARP);
/* 發送數據包 */
result = netif->linkoutput(netif, p);
/* 釋放數據包空間 */
pbuf_free(p);
p = NULL;
return result;
}
ARP請求,是通過調用 etharp_raw函數實現的。ARP頭部中目的MAC地址全0,表示MAC地址待填充。
/* 廣播MAC地址 */
const struct eth_addr ethbroadcast = {{0xff,0xff,0xff,0xff,0xff,0xff}};
/* 待填充MAC地址 */
const struct eth_addr ethzero = {{0,0,0,0,0,0}};
/* 廣播一個ARP請求 */
err_t etharp_request(struct netif *netif, struct ip_addr *ipaddr)
{
return etharp_raw(netif, (struct eth_addr *)netif->hwaddr, ðbroadcast, (struct eth_addr *)netif->hwaddr, &netif->ip_addr, ðzero, ipaddr, ARP_REQUEST);
}
無回報ARP請求的原理是:將自身IP作爲目的IP發送出去,這樣就不會有任何主機響應,但是其它主機接收到後會更新ARP緩存表
/* 廣播一個無回報ARP請求 */
#define etharp_gratuitous(netif) etharp_request((netif), &(netif)->ip_addr)
前面說到當主機發送數據包時,需要先查找ARP緩存表來獲取目的主機MAC地址。下面來具體分析ARP緩存表的數據結構體,以及ARP緩存表的建立、查找和刪除。
ARP表項數據結構
/* ARP表項 */
struct etharp_entry {
struct etharp_q_entry *q; //待發送數據包緩存鏈表
struct ip_addr ipaddr; //IP地址
struct eth_addr ethaddr; //MAC地址
enum etharp_state state; //ARP表項狀態
u8_t ctime; //時間信息
struct netif *netif; //網絡接口指針
};
/* ARP緩存表 */
static struct etharp_entry arp_table[ARP_TABLE_SIZE];
ARP緩存表項狀態
/* ARP表項狀態 */
enum etharp_state {
ETHARP_STATE_EMPTY = 0, //空
ETHARP_STATE_PENDING, //掛起,已發送ARP請求還未得到響應
ETHARP_STATE_STABLE //已建立
};
發送IP數據包之前,需要查ARP緩存表,如果在ARP緩存表中沒有找到相應表項。則先發送ARP請求,並將數據包暫時緩存起來,得到ARP響應之後再發送。ARP提供了etharp_q_entry 結構體,用於管理這些數據包。
/* 未建立ARP表項之前,待發送IP數據包管理結構體 */
struct etharp_q_entry {
struct etharp_q_entry *next;
struct pbuf *p;
};
ARP緩存表項是有時限的,超過時限這將該ARP緩存表項刪除。一般情況下,已經建立的表項爲20分鐘,處於掛起狀態的表項爲10秒鐘。通過一個定時器回調函數etharp_tmr來進行計時處理。
/* 已建立表項壽命 (240 * 5) seconds = 20 minutes */
#define ARP_MAXAGE 240
/* 掛起表項壽命 (2 * 5) seconds = 10 seconds */
#define ARP_MAXPENDING 2
/* ARP定時器回調函數(週期5秒) */
void etharp_tmr(void)
{
u8_t i;
/* 遍歷ARP緩存表 */
for (i = 0; i < ARP_TABLE_SIZE; ++i) {
/* ARP緩存表時間加一 */
arp_table[i].ctime++;
/* 已建立表項和掛起表項超時 */
if (((arp_table[i].state == ETHARP_STATE_STABLE) && (arp_table[i].ctime >= ARP_MAXAGE)) ||
((arp_table[i].state == ETHARP_STATE_PENDING) && (arp_table[i].ctime >= ARP_MAXPENDING))) {
/* ARP表項待發送數據包緩存鏈表不爲空 */
if (arp_table[i].q != NULL) {
/* 釋放待發送數據包緩存鏈表 */
free_etharp_q(arp_table[i].q);
arp_table[i].q = NULL;
}
/* 設置ARP表項狀態爲空 */
arp_table[i].state = ETHARP_STATE_EMPTY;
}
}
}
/* 釋放ARP表項待發送數據包緩存鏈表 */
static void free_etharp_q(struct etharp_q_entry *q)
{
struct etharp_q_entry *r;
/* 遍歷待發送數據包緩存鏈表 */
while (q) {
r = q;
q = q->next;
/* 釋放待發送數據包 */
pbuf_free(r->p);
/* 釋放待發送數據包管理結構體 */
memp_free(MEMP_ARP_QUEUE, r);
}
}
ARP緩存表的建立和查找都是基於find_entry實現的。下面先從find_entry開始,一步一步分析
/* 匹配ARP緩存表時不允許回收表項 */
#define ETHARP_TRY_HARD 1
/* 匹配ARP緩存表時不建立新表項 */
#define ETHARP_FIND_ONLY 2
/* 通過IP地址查找ARP緩存表,如果不存在則按一定規則建立新表項 */
/* 建立新表項的規則:1.在空表項處 2.刪除已建立的最老表項 3.刪除掛起且沒有緩存待發送數據包的最老表項 4.刪除掛起且有緩存待發送數據包的最老表項 */
static s8_t find_entry(struct ip_addr *ipaddr, u8_t flags)
{
s8_t old_pending = ARP_TABLE_SIZE, old_stable = ARP_TABLE_SIZE;
s8_t empty = ARP_TABLE_SIZE;
u8_t i = 0, age_pending = 0, age_stable = 0;
s8_t old_queue = ARP_TABLE_SIZE;
u8_t age_queue = 0;
if (ipaddr) {
/* 最新一次訪問的表項爲已建立態 */
if (arp_table[etharp_cached_entry].state == ETHARP_STATE_STABLE) {
/* IP地址和表項IP地址匹配 */
if (ip_addr_cmp(ipaddr, &arp_table[etharp_cached_entry].ipaddr)) {
return etharp_cached_entry;
}
}
}
/* 遍歷所有ARP表項,匹配到表項直接返回 */
for (i = 0; i < ARP_TABLE_SIZE; ++i) {
/* 記錄第一個空表項下標 */
if ((empty == ARP_TABLE_SIZE) && (arp_table[i].state == ETHARP_STATE_EMPTY)) {
empty = i;
}
/* 該表項爲掛起態 */
else if (arp_table[i].state == ETHARP_STATE_PENDING) {
/* IP地址和表項IP地址匹配 */
if (ipaddr && ip_addr_cmp(ipaddr, &arp_table[i].ipaddr)) {
/* 將當前表項記錄爲最新一次訪問表項 */
etharp_cached_entry = i;
return i;
}
/* 該表項待發送數據包緩衝區不爲空 */
else if (arp_table[i].q != NULL) {
/* 記錄最老的掛起態且待發送數據包緩衝區不爲空的表項下標 */
if (arp_table[i].ctime >= age_queue) {
old_queue = i;
age_queue = arp_table[i].ctime;
}
}
/* 該表象待發送數據包緩衝區爲空 */
else {
/* 記錄最老的掛起態且待發送數據包緩衝區爲空的表項下標 */
if (arp_table[i].ctime >= age_pending) {
old_pending = i;
age_pending = arp_table[i].ctime;
}
}
}
/* 該表項爲已建立態 */
else if (arp_table[i].state == ETHARP_STATE_STABLE) {
/* IP地址和表項IP地址匹配 */
if (ipaddr && ip_addr_cmp(ipaddr, &arp_table[i].ipaddr)) {
/* 將當前表項記錄爲最新一次訪問表項 */
etharp_cached_entry = i;
return i;
}
/* 記錄最老的已建立態表項下標 */
else if (arp_table[i].ctime >= age_stable) {
old_stable = i;
age_stable = arp_table[i].ctime;
}
}
}
/* 該IP沒有匹配到ARP表項,且沒有空表項,且不允許刪除老表項。或者不允許建立新表項 */
if (((empty == ARP_TABLE_SIZE) && ((flags & ETHARP_TRY_HARD) == 0)) || ((flags & ETHARP_FIND_ONLY) != 0)) {
return (s8_t)ERR_MEM;
}
/* 存在空表項 */
if (empty < ARP_TABLE_SIZE) {
i = empty;
}
/* 存在已建立態的表項 */
else if (old_stable < ARP_TABLE_SIZE) {
i = old_stable;
}
/* 存在掛起態且待發送數據包緩衝區爲空的表項 */
else if (old_pending < ARP_TABLE_SIZE) {
i = old_pending;
}
/* 存在掛起態且待發送數據包緩衝區不爲空的表項 */
else if (old_queue < ARP_TABLE_SIZE) {
i = old_queue;
/* 釋放待發送數據包緩存鏈表 */
free_etharp_q(arp_table[i].q);
arp_table[i].q = NULL;
}
/* 不存在可以刪除的表項 */
else {
return (s8_t)ERR_MEM;
}
/* 刪除該表項 */
arp_table[i].state = ETHARP_STATE_EMPTY;
/* 設置爲新的表項 */
if (ipaddr != NULL) {
ip_addr_set(&arp_table[i].ipaddr, ipaddr);
}
arp_table[i].ctime = 0;
etharp_cached_entry = i; /* 將當前表項記錄爲最新一次訪問表項 */
return (err_t)i;
}
更新(不存在則建立)ARP緩存表。當ARP緩存表從掛起轉爲建立的時候,需要發送原先緩存的待發送數據包。
/* 更新(不存在則建立)ARP緩存表 */
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;
/* 地址不能爲廣播地址、組播地址、不確定地址 */
if (ip_addr_isany(ipaddr) || ip_addr_isbroadcast(ipaddr, netif) || ip_addr_ismulticast(ipaddr)) {
return ERR_ARG;
}
/* 通過IP地址查找ARP緩存表,如果不存在則按一定規則建立新表項 */
i = find_entry(ipaddr, flags);
/* 未找到且建立失敗 */
if (i < 0)
return (err_t)i;
/* 設置該表項爲已建立態 */
arp_table[i].state = ETHARP_STATE_STABLE;
/* 綁定網絡接口 */
arp_table[i].netif = netif;
/* 綁定MAC地址 */
k = ETHARP_HWADDR_LEN;
while (k > 0) {
k--;
arp_table[i].ethaddr.addr[k] = ethaddr->addr[k];
}
/* 時間信息置0 */
arp_table[i].ctime = 0;
/* 遍歷數據包緩衝區鏈表 */
while (arp_table[i].q != NULL) {
struct pbuf *p;
struct etharp_q_entry *q = arp_table[i].q;
arp_table[i].q = q->next;
p = q->p;
/* 釋放待發送數據包管理結構體 */
memp_free(MEMP_ARP_QUEUE, q);
/* 發送數據 */
etharp_send_ip(netif, p, (struct eth_addr*)(netif->hwaddr), ethaddr);
/* 釋放待發送數據包 */
pbuf_free(p);
}
return ERR_OK;
}
查找ARP緩存表,也是基於find_entry實現。
/* 查找ARP緩存表,如果不存在不建立新表項 */
s8_t etharp_find_addr(struct netif *netif, struct ip_addr *ipaddr, struct eth_addr **eth_ret, struct ip_addr **ip_ret)
{
s8_t i;
/* 查找ARP緩存表,如果不存在不建立新表項 */
i = find_entry(ipaddr, ETHARP_FIND_ONLY);
/* 如果該表項爲已建立態 */
if((i >= 0) && arp_table[i].state == ETHARP_STATE_STABLE) {
/* 返回IP地址和MAC地址 */
*eth_ret = &arp_table[i].ethaddr;
*ip_ret = &arp_table[i].ipaddr;
/* 返回表項下標 */
return i;
}
return -1;
}
分析完ARP緩存表的查找之後,繼續來分析IP數據包是怎麼借用功能將數據發送出去的
/* 經過ARP功能填充頭部,發送IP數據包 */
err_t etharp_output(struct netif *netif, struct pbuf *q, struct ip_addr *ipaddr)
{
struct eth_addr *dest, mcastaddr;
/* 向前調整出以太網頭部空間 */
if (pbuf_header(q, sizeof(struct eth_hdr)) != 0) {
return ERR_BUF;
}
/* 目的MAC地址置NULL */
dest = NULL;
/* IP地址是廣播地址 */
if (ip_addr_isbroadcast(ipaddr, netif)) {
/* 設置目的MAC地址爲廣播MAC地址 */
dest = (struct eth_addr *)ðbroadcast;
}
/* IP地址是組播地址 */
else if (ip_addr_ismulticast(ipaddr)) {
/* 設置目的MAC地址爲組播MAC地址 */
mcastaddr.addr[0] = 0x01;
mcastaddr.addr[1] = 0x00;
mcastaddr.addr[2] = 0x5e;
mcastaddr.addr[3] = ip4_addr2(ipaddr) & 0x7f;
mcastaddr.addr[4] = ip4_addr3(ipaddr);
mcastaddr.addr[5] = ip4_addr4(ipaddr);
dest = &mcastaddr;
}
/* IP地址爲單播IP地址 */
else {
/* IP地址不在當前網段 */
if (!ip_addr_netcmp(ipaddr, &(netif->ip_addr), &(netif->netmask))) {
/* 網絡接口網關地址不爲0 */
if (netif->gw.addr != 0) {
/* 將目的IP地址改爲網關IP地址 */
ipaddr = &(netif->gw);
}
/* 網關地址爲0,返回錯誤 */
else {
return ERR_RTE;
}
}
/* 查找ARP緩存表,併發送IP數據包 */
return etharp_query(netif, ipaddr, q);
}
/* 廣播或組播(已知目的MAC地址),發送IP數據包 */
return etharp_send_ip(netif, q, (struct eth_addr*)(netif->hwaddr), dest);
}
/* 已知目的MAC地址(查ARP表或MAC地址可推算(廣播/組播)),發送IP數據包 */
static err_t etharp_send_ip(struct netif *netif, struct pbuf *p, struct eth_addr *src, struct eth_addr *dst)
{
struct eth_hdr *ethhdr = p->payload;
u8_t k;
/* 設置以太網頭部源MAC地址和目的MAC地址 */
k = ETHARP_HWADDR_LEN;
while(k > 0) {
k--;
ethhdr->dest.addr[k] = dst->addr[k];
ethhdr->src.addr[k] = src->addr[k];
}
/* 設置硬件地址類型 */
ethhdr->type = htons(ETHTYPE_IP);
/* 發送數據包 */
return netif->linkoutput(netif, p);
}
同樣分析完ARP緩存表的更新之後,繼續來分析收到數據包(IP數據包或ARP數據包)後的更新步驟
/* 以太網數據包輸入處理 */
err_t ethernet_input(struct pbuf *p, struct netif *netif)
{
struct eth_hdr* ethhdr;
u16_t type;
/* 以太網頭部指針 */
ethhdr = p->payload;
/* 幀類型 */
type = htons(ethhdr->type);
/* 判斷數據包幀類型 */
switch (type) {
/* IP數據包 */
case ETHTYPE_IP:
/* 收到IP數據包,更新ARP緩存表 */
etharp_ip_input(netif, p);
/* 向後調整剝掉以太網頭部 */
if(pbuf_header(p, -(s16_t)SIZEOF_ETH_HDR)) {
pbuf_free(p);
p = NULL;
}
else {
/* IP數據包輸入處理 */
ip_input(p, netif);
}
break;
/* ARP數據包 */
case ETHTYPE_ARP:
/* ARP數據包輸入處理 */
etharp_arp_input(netif, (struct eth_addr*)(netif->hwaddr), p);
break;
default:
pbuf_free(p);
p = NULL;
break;
}
return ERR_OK;
}
/* 收到IP數據包,更新ARP緩存表 */
void etharp_ip_input(struct netif *netif, struct pbuf *p)
{
struct eth_hdr *ethhdr;
struct ip_hdr *iphdr;
/* 以太網頭部指針 */
ethhdr = p->payload;
/* IP頭部指針 */
iphdr = (struct ip_hdr *)((u8_t*)ethhdr + SIZEOF_ETH_HDR);
/* 該IP不在當前網段,直接返回 */
if (!ip_addr_netcmp(&(iphdr->src), &(netif->ip_addr), &(netif->netmask))) {
return;
}
/* 更新ARP緩存表 */
update_arp_entry(netif, &(iphdr->src), &(ethhdr->src), 0);
}
/* ARP數據包輸入處理 */
void etharp_arp_input(struct netif *netif, struct eth_addr *ethaddr, struct pbuf *p)
{
struct etharp_hdr *hdr;
struct eth_hdr *ethhdr;
struct ip_addr sipaddr, dipaddr;
u8_t i;
u8_t for_us;
/* ARP數據包長度過短 */
if (p->len < SIZEOF_ETHARP_PACKET) {
/* 釋放該數據包 */
pbuf_free(p);
return;
}
/* 以太網頭部 */
ethhdr = p->payload;
/* ARP頭部 */
hdr = (struct etharp_hdr *)((u8_t*)ethhdr + SIZEOF_ETH_HDR);
/* 不符合ARP數據包格式 */
if ((hdr->hwtype != htons(HWTYPE_ETHERNET)) || (hdr->_hwlen_protolen != htons((ETHARP_HWADDR_LEN << 8) | sizeof(struct ip_addr))) || (hdr->proto != htons(ETHTYPE_IP)) || (ethhdr->type != htons(ETHTYPE_ARP))) {
/* 釋放該數據包 */
pbuf_free(p);
return;
}
/* 取出源IP地址和目的IP地址 */
SMEMCPY(&sipaddr, &hdr->sipaddr, sizeof(sipaddr));
SMEMCPY(&dipaddr, &hdr->dipaddr, sizeof(dipaddr));
/* 該網絡接口沒有配置IP地址 */
if (netif->ip_addr.addr == 0) {
for_us = 0;
}
/* 判斷該ARP數據是不是發給自己的 */
else {
for_us = ip_addr_cmp(&dipaddr, &(netif->ip_addr));
}
/* 該ARP數據是發給自己的 */
if (for_us) {
/* 更新ARP緩存表。如果不存在則建立新表項,但是如果表已滿,則建立失敗 */
update_arp_entry(netif, &sipaddr, &(hdr->shwaddr), ETHARP_TRY_HARD);
} else {
/* 更新ARP緩存表。如果不存在則建立新表項,如果表已滿,則按一定規則回收舊錶項再建立新表項 */
update_arp_entry(netif, &sipaddr, &(hdr->shwaddr), 0);
}
/* 判斷數據包是ARP請求還是ARP響應 */
switch (htons(hdr->opcode)) {
/* ARP請求 */
case ARP_REQUEST:
/* 該ARP數據是發給自己的 */
if (for_us) {
/* 構建ARP響應包 */
hdr->opcode = htons(ARP_REPLY);
/* 目的IP */
hdr->dipaddr = hdr->sipaddr;
/* 源IP */
SMEMCPY(&hdr->sipaddr, &netif->ip_addr, sizeof(hdr->sipaddr));
/* 源MAC地址、目的MAC地址 */
i = ETHARP_HWADDR_LEN;
while(i > 0) {
i--;
hdr->dhwaddr.addr[i] = hdr->shwaddr.addr[i];
ethhdr->dest.addr[i] = hdr->shwaddr.addr[i];
hdr->shwaddr.addr[i] = ethaddr->addr[i];
ethhdr->src.addr[i] = ethaddr->addr[i];
}
/* 發送ARP響應 */
netif->linkoutput(netif, p);
}
/* 該網絡接口沒有配置IP */
else if (netif->ip_addr.addr == 0) {
}
/* 不是發給自己的 */
else {
}
break;
/* ARP響應 */
case ARP_REPLY:
break;
default:
break;
}
/* 釋放ARP數據包 */
pbuf_free(p);
}
總結一下ARP的處理流程