网络基础: ARP 是如何工作的?
2017-09-25 计算机网络
概述
ARP(Address Resolution Protocol,地址解析协议)是用来将 IP 地址解析为 MAC 地址的协议。
主机 (客户端) 和三层网络设备 (例如路由器,三层交换机) 上会维护一张 ARP 表,用于存储 IP 地址和 MAC 地址的映射关系,每一个表项表示一个 IP 地址到 MAC 地址的转换关系。
工作层次
第一种看法
ARP 属于二层协议,工作在数据链路层,因为 ARP 无法脱离广播域的限制。
第二种看法
ARP 协议工作在 OSI (或 TCP/IP) 模型的第二层(数据链路层)和第三层(网络层)之间,也许这个结论会颠覆你对网络分层的已有认知,但是如果分析 ARP 的工作方式之后,你会发现情况确实如此。
ARP 不属于二层网络,不能把数据发送到目标主机,因为 ARP 报文数据是包装在二层以太网帧中的,同时 ARP 也不属于三层网络,因为它不具备三层网络中的路由寻址功能 (无法跨越网段)。
具体来说,ARP 通过 ARP 请求和 ARP 应答,将网络层的逻辑地址(IP 地址)映射到数据链路层的物理地址(MAC 地址)。
工作流程
这里假设主机 A 第一次和主机 B 通信。
- 当主机 A 需要发送数据到主机 B 时,首先会检查主机 B 的 IP 地址是否位于同一子网内 (两台主机是否连接的同一个交换机)
- 如果主机 B 的 IP 地址位于同一子网内,主机 A 会发起 ARP 广播请求,主机 B 收到 ARP 请求后,会回复一个 ARP 回复,其中包含了目标 B 的 MAC 地址
- 如果主机 B 的 IP 地址不在同一子网内,主机将向默认网关发送 ARP 请求,ARP 请求中包含了目标 IP 地址和请求者的 MAC 地址,获取主机 B 的 MAC 地址
- 在向默认网关发送 ARP 请求之前,需要获取默认网关路由器的 MAC 地址,所以 会先发出一个 ARP 请求
- 默认网关收到 ARP 请求后,发送一个 ARP 回复到主机 A,其中包含了自己的 MAC 地址
- 此时主机 A 再次向默认网关发送 查询主机 B 的 ARP 请求
- 默认网关收到 ARP 请求后,会将 ARP 请求转发出去然后等待应答 (这里的细节先忽略),接收到应答之后,发送一个 ARP 回复到主机 A,其中包含了主机 B 的 MAC 地址
- 主机 A 将主机 B 的 MAC 地址保存在 ARP 缓存中,后续再向主机 B 发送数据时,就可以直接从 ARP 表中获取到主机 B 的 MAC 地址了
示例
ARP 通过广播 ARP 请求和单播 ARP 应答这两个过程完成 IP 地址到 MAC 地址解析和映射,下面以两个相同子网内的主机来说明 ARP 的工作流程。
这里假设主机 Host_1 要向主机 Host_3 发送主机,但是不知道 Host_3 的 IP 地址,所以需要先通过 ARP 请求来获得 Host_3 的 IP 地址。
- Host_1 查找本地的 ARP 缓存表,确认是否存在 Host_3 的 MAC 地址表项
- 如果在 ARP 缓存表中 找到了 Host_3 的 MAC 地址 (假设为 MAC_3),直接将数据报文进行帧封装 (
目标 MAC 地址: MAC_3
) - 如果在 ARP 缓存表中 找不到 Host_3 的 MAC 地址,先缓存要发送的数据报文,然后通过广播方式发送一个 ARP 请求报文,其中:
源 MAC 地址: Host_1 的 MAC 地址
,源 IP 地址: Host_1 的 IP 地址
,目标 MAC 地址: FF:FF:FF:FF:FF:FF
,目标 IP 地址: Host_3 的 IP 地址
- 如果在 ARP 缓存表中 找到了 Host_3 的 MAC 地址 (假设为 MAC_3),直接将数据报文进行帧封装 (
- 交换机 Switch_1 收到 ARP 请求报文后,转发到在同一广播域内 (也就是转发到 Host_2, Host_3)
- Host_2 收到 ARP 请求后,发现目标 IP 地址不是自己的,直接丢掉,Host_3 收到 ARP 请求后,发现目标 IP 地址是自己,开始处理:
- 首先将 ARP 请求中的 源 IP 地址和源 MAC 地址存入自己的 ARP 表中 (也就是存储 Host_1_IP -> Host_1_MAC 的映射关系)
- 然后使用单播发送 ARP 应答报文给 Host_1, 其中:
源 MAC 地址: Host_3 的 MAC 地址
,源 IP 地址: Host_3 的 IP 地址
,目标 MAC 地址: Host_1 的 MAC 地址
,目标 IP 地址: Host_1 的 IP 地址
- 交换机 Switch_1 收到 ARP 应答报文后,转发到 Host_1, Host_1 收到 ARP 应答后:
- 首先将 ARP 应答中的 源 IP 地址和源 MAC 地址存入自己的 ARP 表中 (也就是存储 Host_3_IP -> Host_3_MAC 的映射关系)
- 然后将要发送的数据进行报文封装,并发送给 Host_3
不同子网
刚才的示例说明了,位于同一子网内的两台主机之间的 ARP 请求和应答过程,那么对于不同子网的两台主机,其中的流程又是怎样的呢? 例如下图中的 主机 Host_1 要向主机 Host_4 发送主机。
因为 Host_1 已经配置了默认网关,所以 Host_1 会先发送 ARP 请求报文获取默认网关路由器的 MAC 地址,收到默认网关的应答报文后,将默认网关的 IP 地址和 MAC 地址存入自己的 ARP 表中,然后将发送给 Host_4 的数据报文发送给默认网关,经由默认网关转发到 Host_4。
网关需要学习 Host_4 的 ARP 表项吗?需要,因为网关必须先把数据报文封装成以太网帧,其中当然必须包含 Host_4 的 MAC 地址。
后序的通信过程中,Host_1 学习 Host_4 的 ARP 表项 (IP -> MAC 映射关系) 过程,和前文中 Host_1 学习 Host_3 的 ARP 表项过程类似,唯一的区别在于多了一层默认网关和一层交换机 (Switch_2) 的转发,这里不再赘述。
Wireshark 抓包
下面通过抓包工具 Wireshark
分别来演示 ARP 请求正常应答和无应答两种情况。
正常应答
向一台正常的主机发送 ping
时,对应的 ICMP 应答为对应的 ttl。
# 本机 IP: 192.168.3.210
$ ping 192.168.3.200
PING 192.168.3.2 (192.168.3.2) 56(84) bytes of data.
64 bytes from 192.168.3.200: icmp_seq=1 ttl=63 time=428 ms
64 bytes from 192.168.3.200: icmp_seq=2 ttl=63 time=181 ms
64 bytes from 192.168.3.200: icmp_seq=3 ttl=63 time=106 ms
64 bytes from 192.168.3.200: icmp_seq=4 ttl=63 time=438 ms
64 bytes from 192.168.3.200: icmp_seq=5 ttl=63 time=357 ms
^C
--- 192.168.3.200 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 5004ms
接下来,通过 Wireshark 抓包来看看 ARP 请求的报文数据:
通过抓包数据,可以将报文中的关键数据摘取出来:
- Opcode: 1, 表示该报文为 ARP 请求报文
- 源 MAC: 本机 MAC 地址
- 源 IP : 本机 IP 地址
- 目标 MAC: FF:FF:FF:FF:FF:FF, 说明这是一个广播请求
- 目标 IP: 192.168.3.200
然后通过 Wireshark 抓包来看看 ARP 应答的报文数据:
通过抓包数据,可以将报文中的关键数据摘取出来:
- Opcode: 2, 表示该报文为 ARP 应答报文
- 源 MAC: 对端主机 MAC 地址
- 源 IP : 对端主机 IP 地址
- 目标 MAC: 本机 MAC 地址
- 目标 IP: 本机 IP 地址
和刚才 ARP 请求时的广播报文不同,这里的 ARP 应答为单播报文,直接从对端主机发送到本机。
无应答
向一台不存在的主机发送 ping
时,对应的 ICMP 应答为 Destination Host Unreachable。
# 本机 IP: 192.168.3.200
$ ping 192.168.3.100
PING 192.168.3.100 (192.168.3.100) 56(84) bytes of data.
From 192.168.3.200 icmp_seq=3 Destination Host Unreachable
From 192.168.3.200 icmp_seq=6 Destination Host Unreachable
From 192.168.3.200 icmp_seq=9 Destination Host Unreachable
From 192.168.3.200 icmp_seq=12 Destination Host Unreachable
From 192.168.3.200 icmp_seq=15 Destination Host Unreachable
^C
--- 192.168.3.100 ping statistics ---
16 packets transmitted, 0 received, +5 errors, 100% packet loss, time 15328ms
使用 Wireshark 抓包,对应的 ARP 报文如下:
如图所示,虽然连续发了好几个 ARP 请求,但是没有收到任何应答。
ARP 表
如果每次发送数据包之前,都要发起 APR 请求查询目标主机 IP 地址,网络中就会增加很多 ARP 包,这显然是没有必要的 (因为单个主机的 IP 地址和 MAC 地址并不会频繁变动),所以会将 APP 查询结果缓存起来。
这个缓存表就是 ARP 表
,里面存储着当前局域网上的各主机和路由器的 IP 地址到 MAC 地址的映射关系。
如果主机 A 知道主机 B 的 IP 地址,但是 ARP 高速缓存中没有该 IP 地址到 MAC 地址的映射,此时主机 A 通过广播的方式发送 ARP 请求分组,主机 B 收到该请求后会发送 ARP 响应分组给主机 A 告知其 MAC 地址,随后主机 A 向其高速缓存中写入主机 B 的 IP 地址到 MAC 地址的映射。
也就是说,在主机发送数据包时,先查询一下 ARP 表,如果其中已经保存了目标主机的 MAC 地址,就不需要发送 ARP 查询请求,而是直接使用 ARP 缓存中的地址,只有当 ARP 表中不存在目标主机的 MAC 地址时,才发送 ARP 查询请求。
Linux 中使用 arp
命令来查看 ARP 表缓存相关信息:
# 查看 ARP 表缓存
$ arp -a
# 查看 ARP 表缓存详细信息
$ arp -an
Windows 中使用相同的命令和参数。
ARP 表项
ARP 表是以每个网络接口为单位保存的,也就是每个以太网卡、无线网卡、虚拟网卡 (例如 Docker) 都有自己的 ARP 表,当一个数据包到达某个网络接口时,操作系统会检查该接口的 ARP 缓存表。
Linux 中使用 ip
命名查看 ARP 表项信息:
# 查看 ARP 表
$ ip neigh show
# 每个网络接口都有自己的 ARP 表
# 以太网卡
10.0.5.91 dev eth0 lladdr ee:ff:ff:ff:ff:ff REACHABLE
10.0.5.253 dev eth0 lladdr ee:ff:ff:ff:ff:ff REACHABLE
# 虚拟网卡
172.27.0.2 dev br-31a91bfb5058 lladdr 15:24:ef:1b:00:02 STALE
172.17.0.2 dev docker0 lladdr 15:24:ef:11:00:02 STALE
172.29.0.4 dev br-37adcdbf3e5f lladdr 15:24:ef:1d:00:04 STALE
172.17.0.4 dev docker0 lladdr 15:24:ef:11:00:04 STALE
172.22.0.2 dev br-456c812bc240 lladdr 15:24:ef:16:00:02 STALE
172.17.0.3 dev docker0 lladdr 15:24:ef:11:00:03 REACHABLE
ARP 表项状态
在 ARP 表的结果输出中,每一行的末尾带有一个 “状态值”, ARP 包含如下的状态。
REACHABLE
表示该 IP 地址的 MAC 地址信息是最新的,并且正常进行通信。
STALE
表示该 IP 地址的 MAC 地址信息仍然可用,但已经超过了过期时间,主机可能会继续使用这个 MAC 地址进行通信,但应尽快发送 ARP 请求来更新 MAC 地址。
DELAY
表示主机已经发送了 ARP 请求,但尚未收到应答。
PROBE
表示主机正在发送 ARP 请求,以确认该 IP 地址是否仍然可用,通常发生在主机重启或重新连接到网络时。
FAILED
表示 ARP 请求失败,主机无法获取到该 IP 地址对应的 MAC 地址。
PERMANENT
表示静态 ARP 表项,在管理员手动删除之前一直生效。
缓存老化
使用 ARP 缓存可以减少 ARP 包的数量,但是和所有 “缓存数据” 一样,ARP 缓存也存在数据失效的问题。
当某个主机的 IP 地址或者 MAC 地址发生变化时,ARP 缓存的数据就会失效,为了最大化减少 ARP 缓存失效带来的问题,ARP 缓存数据会定期删除,缓存数据删除之后,主机发送数据请求之前,重新执行一次 ARP 请求查询就又可以获取到目标主机的 MAC 地址了。
Linux 中可以使用 sysctl
命令来查看 ARP 缓存的默认有效期。
# 查看 ARP 缓存的默认有效期
$ sysctl net.ipv4.neigh.default.gc_stale_time
# APR 缓存默认有效期: 1 分钟
# 每隔 1 分钟,定期删除 ARP 缓存
net.ipv4.neigh.default.gc_stale_time = 60
除了定期删除策略外,在网络接口下线时 (例如无线网卡被拔出),其 ARP 缓存会被立刻删除。
ARP 攻击
ARP 攻击利用 ARP 协议的工作方式来欺骗网络中的主机,使其将网络流量发送到错误的目标,通常有两种常见的方式。
1.ARP 欺骗(ARP Spoofing)
攻击者发送伪造的 ARP 应答消息,欺骗网络中的其他主机,告诉它们: 攻击者的 MAC 地址对应于指定主机的 IP 地址,这样,当其他主机想要发送数据给指定主机时,数据包实际上会被发送到攻击者的主机上,攻击者可以对数据进行监视、修改或丢弃。
为了防止 ARP 欺骗,可以使用 ARP Anti-Spoofing 技术来保护网络安全,交换机会对 ARP 报文进行检查,一旦发现 IP 与 MAC 地址有伪造的情况,立马丢包甚至关闭端口。
当用户手动修改 IP 时,其实已经通过 DHCP Release 报文将自动获取到的的 IP 释放了。所以当交换机检查到该 IP 已经被释放后,任何非 DHCP 数据报文,全部拒绝丢掉。
2. ARP 洪泛(ARP Flooding)
攻击者发送大量伪造的 ARP 请求和应答包,导致网络设备的 ARP 缓存表被填满。当设备的 ARP 缓存被伪造的条目填满时,合法的 ARP 条目无法被正确存储和更新,导致设备无法进行正常的网络通信,从而引发网络瘫痪。
ARP 表类型
1. 动态类型
通过 ARP 请求和 ARP 应答自动生成和维护,表项可以被更新和 (过期) 删除,也可以被静态 ARP 表项覆盖。
2. 静态类型
由网络管理员手动建立的 IP 地址和 MAC 地址之间的映射关系,表项不会被 (过期) 删除,也不会被静态 ARP 表项覆盖。
静态 ARP 表项可以限制指定本端主机和指定 IP 的对端主机通信时只使用指定的 MAC 地址,此时攻击报文无法修改本端主机的 ARP 表中的 IP 地址和 MAC 地址的映射关系,从而保护了本端主机和对端主机之间的正常通信,一般会在网关设备上配置静态 ARP 表项。
免费 ARP
主机使用自己的 IP 地址作为目标 IP 地址发送 ARP 请求 (主机自己向自己发送 ARP 请求),此种方式称免费 ARP, 主要起到如下作用:
- IP 地址冲突检测: 主机对外发送 ARP 请求,正常情况下不会收到 ARP 应答,如果收到了,说明网络中已经存在与自身 IP 重复的主机,检测到重复 (冲突) 之后,主机会周期性地发送 ARP 免费应答报文,直到冲突解决
- 通告新的 MAC 地址: 主机更换了网卡,这时会产生新的 MAC 地址,为了通知网络中的其他主机更新 ARP 表项,主机会发送一个免费的 ARP 请求