TCP/IP 学习笔记 | ARP:地址解析协议

切记:少就是多,慢就是快。

ARP (Address Resolution Protocol) 地址解析协议是一个通过解析网络层地址来找寻数据链路层地址的网络传输协议,它在IPv4中极其重要。当一台主机把以太网数据帧发送到位于同一局域网上的另一台主机时,是根据 48 bit 的以太网地址来确定目的接口的。设备驱动程序从不检查 IP 数据报中的目的 IP 地址。ARPIP 地址到对应的硬件地址之间提供动态映射。

执行流程

大致简化流程是这样的,网络层收到 IP 数据报,调用 IP 选路函数,判断目标主机 MAC 地址是否在 ARP 缓存中。若存在,装帧,交给以太网驱动;若不存在,广播 ARP 请求,大声呼喊:“谁是这个 IP 啊,大声告诉我你的 MAC 地址好吗?” 这时路人甲收到广播请求,发现是自己的 IP,便回应:“是我,我的 MAC 地址是 … ” 然后装帧,交给以太网驱动。

MAC 地址

MAC 地址由 IEEE 定义,有三种:MAC-48EUI-48EUI-64,我们常用的是 MAC-48MAC-48,地址空间 48bit,它的设计目标是一百年内够用。常以16进制数表示 MAC 地址,通用表示方法有三种格式:

  • 01-23-45-67-89-ab
  • 01:23:45:67:89:ab
  • 0123.4567.89ab

MAC 地址其前3个字节为 OUI (Organizationally Unique Identifier) 组织唯一标识符,由 IEEE 的注册管理机构给不同厂家分配的代码,区分不同的厂家,后3字节由厂家自行分配。

例如 f0:18:98:1c:8:75 根据前3个字节 F01898 即可在 OUI 上查出硬件厂商为 Apple Inc.

ARP 高速缓存

当主机 A 要向本局域网上的某个主机 B 发送 IP 数据报时,先在其 ARP 高速缓存中查看是否有主机 B 的 IP 地址。如果有,就在 ARP 高速缓存中查出其对应的硬件地址,再把这个硬件地址写入 MAC 帧,然后通过局域网把该 MAC 帧发往此硬件地址。也有可能查不到主机 B 的 IP 地址的项目。这可能是主机 B 才入网,也可能是主机 A 刚刚加电,其高速缓存还是空的。在这种情况下,主机 A 就自动运行 ARP,然后按以下步骤找到主机 B 的硬件地址。

查看系统 arp 缓存

$ arp -a
'''
? (192.168.31.1) at 64:9:80:70:c6:3 on en0 ifscope [ethernet]
? (192.168.31.25) at f0:18:98:1c:8:75 on en0 ifscope permanent [ethernet]
? (192.168.31.163) at 24:f0:94:50:83:41 on en0 ifscope [ethernet]
? (192.168.31.255) at ff:ff:ff:ff:ff:ff on en0 ifscope [ethernet]
? (224.0.0.251) at 1:0:5e:0:0:fb on en0 ifscope permanent [ethernet]
? (239.255.255.250) at 1:0:5e:7f:ff:fa on en0 ifscope permanent [ethernet]

清空 arp 缓存

$ sudo arp -ad
'''
Password:
192.168.31.1 (192.168.31.1) deleted
delete: cannot locate 192.168.31.25
192.168.31.163 (192.168.31.163) deleted
192.168.31.255 (192.168.31.255) deleted
224.0.0.251 (224.0.0.251) deleted
239.255.255.250 (239.255.255.250) deleted

抓包

这里我使用了一个 ARP 扫描工具作为测试,他能查询到同一网段的所有主机地址,安装方式如下:

$ brew install arp-scan
'''
==> Installing dependencies for arp-scan: libpcap
==> Installing arp-scan dependency: libpcap
==> Downloading https://mirrors.aliyun.com/homebrew/homebrew-bottles/bottles/libpcap-1.9.1.mojave.bottle.tar.gz
==> Pouring libpcap-1.9.1.mojave.bottle.tar.gz
==> Caveats

我们发现它依赖了 libpcap (Packet Capture Library),事实上这是一个很有名的用 C 写的包捕获函数库,著名的 tcpdumpwireshark 等工具都是基于这个库写的。下面通过我的 en0 网卡扫描一下试试:

$ sudo arp-scan -I en0 -l
'''
Interface: en0, type: EN10MB, MAC: f0:18:98:1c:08:75, IPv4: 192.168.31.25
Starting arp-scan 1.9.7 with 256 hosts (https://github.com/royhills/arp-scan)
192.168.31.1    64:09:80:70:c6:03    Xiaomi Communications Co Ltd
192.168.31.182  d4:61:da:51:f8:1a    Apple, Inc.

535 packets received by filter, 0 packets dropped by kernel
Ending arp-scan 1.9.7: 256 hosts scanned in 1.863 seconds (137.41 hosts/sec). 2 responded

与此同时,打开 Wireshark 抓包:

从抓包结果不难看出,这个工具广播了包含局域网整个 IP 段的 ARP 请求。

数据结构

以太网帧头

''' 下面这 14B 的数据即为以太网帧头数据
-------------------------------------------------------------------------
0000   ff ff ff ff ff ff f0 18 98 1c 08 75 08 06         ...........u..
  1. Destination6B
    目的地址,抓包结果中为 ff:ff:ff:ff:ff:ff,即广播地址。

  2. Source6B
    源地址,抓包结果中为 f0:18:98:1c:08:75 为本机 MAC 地址。

  3. Type2B
    帧类型,表明后面的数据类型,对于 ARP 请求或应答来说,该字段的值为 0x0806。还有常用的 IPv4 值为 0x0800

ARP 请求/应答

''' 下面这 28B 的数据即为 ARP 请求数据
-------------------------------------------------------------------------
0000   00 01 08 00 06 04 00 01 f0 18 98 1c 08 75 c0 a8   .............u..
0010   1f 19 00 00 00 00 00 00 c0 a8 1f 02               ............
  1. Hardware Type2B
    硬件类型,表示硬件地址的类型,值为 1 表示以太网地址。

  2. Protocol Type2B
    协议类型,表示要映射的协议地址类型。它的值为0x0800表示 IPv4 地址类型,它的值与包含IP数据报的以太网数据帧中的类型字段的值相同。

  3. Hardware Size1B
    硬件地址长度,值为 6 表示 MAC 地址长度为 6B。

  4. Protocol Size1B
    协议地址长度,值为 4 表示 IPv4 地址长度为 4B。

  5. Eth Source Addr6B
    发送端 MAC 地址,抓包结果中,发送端为本机,即本机 MACf0:18:98:1c:08:75

  6. Oper6B
    操作类型,1 表示 ARP 请求,2 表示 ARP 应答。

  7. Proto Source Addr4B
    发送端 IP 地址,本机 IP192.168.31.25

  8. Eth Target Addr6B
    接收端端 MAC 地址,因为不知道,所以为 00:00:00:00:00:00

  9. Proto Target Addr4B
    接受端 IP 地址,希望查询 MAC 地址的 IP192.168.31.2

以上数据大致意思就是,我是 192.168.31.25,谁的 IP192.168.31.2