LwIP之ARP协议
在網絡層,源主機與目的主機之間是通過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的處理流程
總結
以上是生活随笔為你收集整理的LwIP之ARP协议的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: STM32之窗口看门狗原理
- 下一篇: 芯片内部长啥样?牛人用1500张照片,一