LWIP协议栈:ARP协议

1. ARP协议简介

  • ARP(Address Resolution Protocol),地址解析协议。ARP协议处于网络层,其主要功能就是通过目标设备的 IP 地址,查询目标设备的 MAC 地址,从而进行网络通信。

  • 网络层中,源主机和目标主机依赖于IP地址进行通信。而链路层又有自己的寻址寻址机制(如,以太网依赖于MAC地址进行通信)。ARP的作用在于将IP地址转换为MAC地址,从而连接网络层与链路层,使得上层可以通过IP地址进行网络通信。

  • ARP协议的基本运作过程如下:

2. ARP缓存表

2.1 ARP缓存表的定义 

每台主机或路由器在其内存中都存储着一个ARP缓存表,表中记录着了<IP 地址,MAC 地址>对,反映着目标主机的IP地址与MAC地址的映射关系。

LwIP协议栈中,使用一个 arp_table 数组描述 ARP 缓存表,数组的内容是表项的内容。
 1 /*lwip/src/netif/etharp.c*/
 2 
 3 enum etharp_state {
 4    ETHARP_STATE_EMPTY = 0,            //空状态,表示该表项为空
 5    ETHARP_STATE_PENDING,              //挂起状态,表示该表项还未收到目标主机的ARP应答
 6    ETHARP_STATE_STABLE,               //可用状态,表示该表项可用
 7    ETHARP_STATE_STABLE_REREQUESTING_1,//过渡状态
 8    ETHARP_STATE_STABLE_REREQUESTING_2 //过渡状态
 9  #if ETHARP_SUPPORT_STATIC_ENTRIES
10    , ETHARP_STATE_STATIC
11  #endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
12 };
13 
14 struct etharp_entry {
15 #if ARP_QUEUEING
16    /* 指向此ARP表项上挂起的数据包队列*/
17    struct etharp_q_entry *q;
18  #else /* ARP_QUEUEING */
19    /*指向此ARP表项上挂起的单个数据包 */
20    struct pbuf *q;
21  #endif /* ARP_QUEUEING */
22    ip_addr_t ipaddr;       //目标主机的IP地址
23    struct netif *netif;    //目标主机的网卡信息
24    struct eth_addr ethaddr;//目标主机的MAC地址
25    u8_t state;             //此表项的状态
26    u8_t ctime;             //此表项的生存时间
27  };
28 
29 static struct etharp_entry arp_table[ARP_TABLE_SIZE]; //LWIP协议栈的ARP缓存表
  • 表项的数据包指针q:指针q指向需要发送至该目标主机的数据包。struct etharp_q_entry *q指向一个数据包队列,struct pbuf *q指向单个数据包。
  • 表项的生存时间ctime:每个表项都包含一个ctime变量,表示该表项的生存时间。当达到指定的生存时间,该表项会被删除。

2.2 缓存表的动态处理

ARP协议的核心就是对缓存表的动态处理。因为IP地址可能是动态的,所以ARP缓存表必须动态更新。

ARP协议中,通过发送ARP请求包、接收ARP应答包、ARP缓存表超时处理来实现缓存表的动态更新。

(1)发送ARP请求包

发送数据时,若不存在目标主机的表项,则新建一个表项,并发送ARP请求包;若存在目标主机的表项,对处于ETHARP_STATE_STABLE的表项,会再次发送ARP请求包,以确认目标主机依然在工作状态。详见4. ARP协议工作流程。

(2)接收ARP应答包

接收到ARP应答包时,将ARP应答包包含的<IP 地址,MAC 地址>对添加到缓存表。详见4. ARP协议工作流程。

(3)ARP缓存表超时处理

缓存表的超时处理。周期性(1S)调用 etharp_tmr()函数,动态更新缓存表项的生存时间,以及它的状态。

在ARP缓存表的动态更新过程中,其表现状态变化过程如下:

3. ARP报文

ARP协议的请求与应答同时通过ARP报文来实现的。ARP的报文将被封装在以太网帧中进行发送。

3.1 以太网帧结构

(1)MAC地址

MAC Address(Media Access Control Address),亦称为 EHA(Ethernet Hardware Address)、硬件地址、物理地址(Physical Address)、链路地址。MAC的值被固化在网卡的ROM中,以唯一标识该网卡。MAC地址长度为 6字节,其前 3个字节(组 织唯一标志符)表示厂家的代码,后 3个字节(扩展标识符)由厂家自行分配。 目标 MAC地址可以分成三类,单播地址、多播地址和广播地址。

  • 单播地址:即目标主机的MAC地址,发送数据至特定的目标主机。
  • 多播地址:MAC的第一个字节的bit0为1,发送数据至多个目标主机。
  • 广播地址:MAC地址为全1,即FF-FF-FF-FF-FF-FF,发送数据至同一子网内的所有主机。

(2)以太网帧结构 

在链路层中,数据被封装为以太网帧结构进行发送。以太网帧结构如下:

  前同步码:它的作用是实现物理层帧输入输出的同步,其值都是 10101010(0x55,大端模式)。

  • 帧开始符:表示着以 太网帧的开始,其值都是 10101011(0xD5, 大端模式)。
  • 目标MAC地址:接收设备的MAC地址。
  • 源MAC地址:发送设备的MAC地址。
  • 类型:表示网络协议的类型。一台给定的主机可以支持多种网络层协议,不同的应用采用不同的协议。因此,当以太网帧到达网卡中,网卡需要知道它应该将数据字段的内容传递给哪个网络层协议。如 IP 协议、ARP协议等。 
  • 数据:包含需要被发送的数据(如IP数据包、ARP数据包)。以太网的最大传输单元 (MTU)是1500字节,若数据包超过1500字节,则需要分片传输;若数据包小于46字节,则需要填充至46字节再发送。
  • CRC:以太网的差错校验信息。 

P.S.:当“类型”字段的值小于 1518时,它表示后面数据字段的数据长度,当大于1518的时候才表示递交给哪个协议。 

3.2 ARP报文结构

(1)ARP报文格式如下:

  • 硬件类型:目标网卡的硬件类型,表明ARP报文可以在哪种类型的网络上传输。1表示以太网地址。
  • 协议类型:硬件地址要映射的协议地址类型。映射IP地址时的值为0x0800.
  • 硬件地址长度:即MAC地址的长度(以太网的MAC地址长度为6)。
  • 协议地址长度:即IP地址的长度。
  • 操作类型:指定本次ARP报文的类型。1:ARP请求报文;0:ARP响应报文。
  • 源MAC地址:发送设备的MAC地址。
  • 源IP地址:发送设备的IP地址。
  • 目标MAC地址:接收设备的MAC地址。在ARP请求报文中,目标MAC地址未知,MAC字段的值为全0(即00-00-00-00-00-00)。
  • 目标IP地址:接收设备的IP地址。

(2)ARP帧格式:

ARP报文将被传输到链路层,加上以太网的帧头,形成ARP帧,再通过链路层发送出去。ARP帧的格式如下:

  • 目标MAC地址:接收设备的MAC地址。在ARP请求报文中,它的目标为网络上的所有主机,目标MAC字段的值为FF-FF-FF-FF-FF-FF(广播地址)。
  • 源MAC地址:发送设备的MAC地址。
  • 帧类型:标识帧封装的上层协议。此处封装的为ARP协议,它的值为0x0806。

(3)帧格式的定义

 1 /*lwip2.1.2/src/include/lwip/port/ethernet.h*/
 2 #define ETH_HWADDR_LEN    6     //以太网地址长度
 3 
 4 struct eth_addr    //以太网地址结构体
 5 {    
 6     PACK_STRUCT_FLD_8(u8_t addr[ETH_HWADDR_LEN]); 
 7 } PACK_STRUCT_STRUCT;
 8 
 9 struct eth_hdr    //以太网首部  9 
10 {
11     PACK_STRUCT_FLD_S(struct eth_addr dest);  //以太网目标 MAC 地址
12     PACK_STRUCT_FLD_S(struct eth_addr src);   //以太网源 MAC 地址 
13     PACK_STRUCT_FIELD(u16_t type);            //帧类型 
14 } PACK_STRUCT_STRUCT; 
15 
16 
17 /*lwip2.1.2/src/include/lwip/port/etharp.h*/
18 struct etharp_hdr               //ARP 报文
19 {
20     PACK_STRUCT_FIELD(u16_t hwtype);    //硬件类型 
21     PACK_STRUCT_FIELD(u16_t proto);     //协议类型
22     PACK_STRUCT_FLD_8(u8_t  hwlen);     //硬件地址长度 
23     PACK_STRUCT_FLD_8(u8_t  protolen);  //协议地址长度 
24     PACK_STRUCT_FIELD(u16_t opcode);    //op 字段 
25     /* 以上是 ARP 报文首部 */ 
26     
27     PACK_STRUCT_FLD_S(struct eth_addr shwaddr);            //源 MAC 地址
28     PACK_STRUCT_FLD_S(struct ip4_addr_wordaligned sipaddr);//源 ip 地址
29     PACK_STRUCT_FLD_S(struct eth_addr dhwaddr);            //目标 MAC 地址 
30     PACK_STRUCT_FLD_S(struct ip4_addr_wordaligned dipaddr);//目标 ip 地址 
31 } PACK_STRUCT_STRUCT;  
32 
33 enum etharp_opcode    //op 字段操作 
34 {
35     ARP_REQUEST = 1,     //请求包 33     
36     ARP_REPLY   = 2      //应答包 34 
37 };
ARP报文格式定义

4. ARP协议工作流程

(1)数据包接收流程说明:

  • 硬件网卡接收到数据之后,会调用ethernet_input()函数来处理接收到的数据包。ethernet_input()函数根据数据包中的以太网首部的帧类型进行分别处理;当帧类型为IP协议,则该数据包为IP数据包,调用ip4_input()函数进行处理;当帧类型为ARP协议,则该数据包为ARP数据包,调用 etharp_input()函数进行处理。
  • etharp_input()函数对ARP数据包进行处理。首先调用 etharp_update_arp_entry()更新ARP缓存表。然后判断ARP数据包类型。如果是ARP请求包,且是发送给本机的,则调用 etharp_raw()函数发送ARP应答包;如果不是发送给本机的ARP请求包,则丢弃。
  • etharp_update_arp_entry()对ARP缓存表进行更新。首先查找或创建ARP表项,设置新建表项的信息。然后检查改表项是否含有未发送的数据包,若存在,则调用ethernet_output()函数发送该表项上的数据。

(2) 数据包接收流程说明:

  • 上层调用etharp_output()函数来发送IP数据包。对于广播、多播数据包,调用ethernet_output()函数直接发送数据包。对于单播数据包,若源主机与目标主机不在同一个子网,则修改IP地址为网关地址;然后遍历ARP缓存表,如果存在与目标IP地址对应的表项,且表项状态>=ETHARP_STATE_STABLE,则调用 etharp_output_to_arp_index()函数发送IP数据包;若不存在,则调用etharp_query()函数。
  •  etharp_output_to_arp_index()函数用来发送IP数据包。(此时表项状态必须>=ETHARP_STATE_STABLE)首先,更新该表项信息,确认目标主机是否在工作状态。当表项还有15s到期,调用(etharp_request()函数以广播方式发送ARP数据包;当表项还有30秒到期,调用etharp_request_dst()函数以单播方式发送ARP请求包。若表项有效期大于30s,则调用ethernet_output()函数发送IP数据包。
  • 当ARP表中没有与目标IP地址对应的表项,或表项的状态为ETHARP_STATE_PENDING,则etharp_query()函数被调用。etharp_query()函数首先调用 etharp_find_entry()函数查询或创建新的表项,当该表项为新建表项,或该表项的数据队列为空时,调用etharp_request()函数发送ARP请求。然后判断表项的状态,当表项状态>=ETHARP_STATE_STABLE,则调用ethernet_output函数发送数据包;当表项状态为ETHARP_STATE_PENDING,则将数据包插入该表项的数据包队列。

5. 参考资料

[1] 野火《LwIP应用开发实战指南》。

posted @ 2020-03-06 20:59  LinFeng-Learning  阅读(1682)  评论(0编辑  收藏  举报