Skip to content

ARP协议

Address Resolution Protocol地址解析协议

ARP协议就是根据目标IP地址以广播方式获取相应的MAC地址,并将MAC地址存入ARP缓存表, 是TCP/IP协议里面的其中一个

image-20240705192922908

在发送数据包的时候需要使用这一个格式(以太网帧), 需要知道对方的MAC地址, IP地址主要使用在广域网里面, 是一个可变的, 解决数据在外网里面传输的问题, MAC地址是使用在内网里面, 是不可变的唯一的地址

实际获取的是路由器的MAC地址, 在到达路由器以后路由器会重新进行填充

发送ARP请求

主要是在etharp文件里面实现的

image-20240705193849296

函数的名字是etharp_output()

c
struct etharp_entry {
  /** Pointer to a single pending outgoing packet on this ARP entry. */
  struct pbuf *q;				//挂起数据
  ip4_addr_t ipaddr;			//目标的地址
  struct netif *netif;			//当前使用的网卡
  struct eth_addr ethaddr;		//MAC地址
  u16_t ctime;				   //保存的时间
  u8_t state;				   //状态
};

使用这一个结构体记录ARP缓存表, 这里面记录的信息是有时效的

c
/** ARP states */
enum etharp_state {
  ETHARP_STATE_EMPTY = 0,			//ARP缓存表处于初始化的状态
  ETHARP_STATE_PENDING,				//只记录IP地址, 还没有获取MAC地址
  ETHARP_STATE_STABLE,				//应答前, 数据包被挂起了, 应答以后更新以及发送数据
  ETHARP_STATE_STABLE_REREQUESTING_1,//发送状态
  ETHARP_STATE_STABLE_REREQUESTING_2
};

etharp_output函数调用的时候, 如果可以在这一个表里面获取到对方的MAC地址, 进行添加以太网的头部(etharp_output_to_arp_index => ethernet_output函数里面进行), 然后发送数据

image-20240705195841975

找不到的话会使用etharp_query这一个函数

这一个函数首先调用etharp_find_entry再次进行查找, 如果没有的话返回一个可以使用的etharp_entry表项的索引, 记录ip地址, 这时候他的状态是ETHARP_EMPTY

之后在etharp_query函数里面对这一个结构体进行初始化, 以及使用函数etharp_request发送一个ARP包, 最后调用的是etharp_raw函数, 会在这个函数里面构建一个pbuf进行发送

image-20240705201929248

img

ARP帧(28字节)

000e: 00 01

0010: 08 00 06 04 00 01 00 05 5d 61 58 a8 c0 a8 00 37

0020: 00 00 00 00 00 00 c0 a8 00 02

00 01: 硬件为以太网

08 00 : 解析一个IP地址

06 :硬件地址长度

04 :协议地址长度, 表示ip地址长度

00 01 : op字段为1表示ARP请求,op字段为2表示ARP应答。

00 05 5d 61 58 a8 : 自己的MAC地址

c0 a8 00 37 : IP地址192.168.0.55

00 00 00 00 00 00 : 目的MAC地址, 未知填0

c0 a8 00 02 : 目的地ip地址

c
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;

使用这一个进行填充, 最后使用ethernet_output进行发送

image-20240705210238596

image-20240705210714742

会在ethernet_input这一个函数里面去除以太网首部, 以及根类型调用不同的处理函数