ARP协议

协议解析

ARP(Address Resolution Protocol)协议是一种用于在计算机网络中解析网络层地址(如IP地址)与物理地址(如MAC地址)之间映射关系的协议。在数据包从源主机发送到目标主机的过程中,需要将目标主机的网络层地址转换为物理地址,才能在链路层上进行数据传输。

ARP协议工作流程如下:

  1. 源主机检查目标IP地址是否在本地网络,如果不在,则将数据包转发给默认网关。
  2. 源主机检查ARP缓存表中是否已经有目标IP地址对应的物理地址,如果有,则直接将数据包发送到目标主机的物理地址。
  3. 如果ARP缓存表中没有目标IP地址对应的物理地址,源主机就会发送一个ARP请求广播,请求目标主机回复自己的物理地址。
  4. 网络中所有的主机都会接收到这个ARP请求广播,但只有目标主机会回复自己的物理地址。
  5. 目标主机收到ARP请求后,会将自己的物理地址打包在ARP回复中发送给源主机。
  6. 源主机收到ARP回复后,将目标主机的IP地址和物理地址存储在ARP缓存表中,以便下一次发送数据包时可以直接使用。

ARP协议是一个简单而重要的协议,它能够帮助网络中的主机快速地解析IP地址和物理地址之间的映射关系,从而实现数据包在局域网内的快速传输。

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
import binascii
import socket
import struct


def send_arp_request(source_ip, target_ip, interface):
# 构建 Ethernet 请求数据包
# 广播地址
broadcast_bytes = b'\xff\xff\xff\xff\xff\xff'
# 源 MAC 地址
src_mac_bytes = binascii.unhexlify('00:50:56:c0:00:08'.replace(':', ''))
ethernet_header = struct.pack("!6s6s2s", broadcast_bytes, src_mac_bytes, b'\x08\x06') # ARP

# 构建 ARP 请求数据包
source_ip = socket.inet_aton(source_ip) # 源IP地址
target_mac_bytes = b'\x00\x00\x00\x00\x00\x00' # 目标 MAC 地址
target_ip = socket.inet_aton(target_ip) # 目标IP地址

arp_request = struct.pack('!HHBBH6s4s6s4s',
0x0001, # 硬件类型:以太网
0x0800, # 协议类型:IPv4(表示发送方要映射的协议地址类型。对于IP地址,该值为0x0800。)
6, # 硬件地址长度:6字节
4, # 协议地址长度:4字节
0x0001, # 操作码:ARP请求
src_mac_bytes,
source_ip,
target_mac_bytes,
target_ip)

# 创建原始套接字
# 不可或缺:socket.htons(0x0806),否则无法接受返回
raw_socket = socket.socket(socket.PF_PACKET, socket.SOCK_RAW, socket.htons(0x0806))

# 绑定指定的网络接口
raw_socket.bind((interface, 0))

# 发送ARP请求
raw_socket.send(ethernet_header + arp_request)

# 接收并解析响应
response = raw_socket.recv(2048)

# 解析响应数据包
_, _, _, _, _, _, _, _, sender_mac, sender_ip, target_mac, target_ip = struct.unpack('!6s6s2sHHBBH6s4s6s4s',
response)

# 打印目标MAC地址
print(f"Reply -> IP: {socket.inet_ntoa(sender_ip)} MAC: {':'.join(format(x, '02x') for x in sender_mac)}")

# 关闭原始套接字
raw_socket.close()


if __name__ == "__main__":
src_ip = '172.16.66.1' # 目标IP地址
dst_ip = '172.16.66.130' # 目标IP地址
inter = 'vmnet8' # 网络接口名

send_arp_request(src_ip, dst_ip, inter)