以太网帧与ARP:帧结构、ARP协议原理与源码解析
核心原理:以太网帧的封装与寻址基础
在TCP/IP协议栈中,网络接口层(即链路层)负责将IP数据报封装为能够在物理链路上传输的帧。以太网帧是最常见的链路层帧格式,其标准结构(IEEE 802.3 / Ethernet II)由前导码、帧起始定界符(SFD)、目的MAC地址、源MAC地址、类型/长度字段、数据载荷和帧校验序列(FCS)组成。其中,前导码与SFD属于物理层同步信息,真正被驱动与协议栈处理的帧体从目的MAC地址开始。
MAC地址是链路层寻址的核心,长度为48位(6字节),通常以十六进制冒号分隔表示,例如 00:1a:2b:3c:4d:5e。以太网帧中的类型字段(EtherType)用于标识上层协议:0x0800表示IPv4,0x0806表示ARP,0x86DD表示IPv6。当帧携带ARP报文时,数据载荷部分不再是IP数据报,而是完整的ARP协议消息。
理解帧结构的关键在于:MAC地址解决“同一广播域内下一跳是谁”的问题,而IP地址解决“端到端目标在哪里”的问题。这两种地址的映射关系,正是ARP协议存在的根本原因。
Ethernet II 帧结构速览(不含前导码与SFD)
| 字段 | 长度(字节) | 说明 |
|---|---|---|
| 目的MAC地址 | 6 | 接收方网卡地址,可为单播、多播或广播 |
| 源MAC地址 | 6 | 发送方网卡地址 |
| 类型(EtherType) | 2 | 0x0806表示ARP,0x0800表示IPv4 |
| 数据载荷 | 46~1500 | ARP报文或IP数据报,不足46字节需填充 |
| FCS(帧校验序列) | 4 | CRC32校验,覆盖从目的MAC到载荷末尾 |
ARP协议原理:地址解析的工作机制
ARP(Address Resolution Protocol)用于在IPv4环境下,根据目标IP地址获取同一链路(广播域)上对应网卡的MAC地址。其核心流程可概括为请求广播、应答单播。当主机A需要向主机B发送IP数据报时,A首先检查自身的ARP缓存表;若未找到B的IP对应的MAC条目,则构造一个ARP请求报文,将发送方MAC与IP填入对应字段,目标IP填B的IP,目标MAC填全零,然后封装在以太网帧中,目的MAC地址设为广播地址 FF:FF:FF:FF:FF:FF,在本地网络中广播。
网段内所有主机都会收到该广播帧,但只有IP地址与请求中目标IP一致的主机B会处理此请求。B收到后,先将A的IP与MAC映射写入自己的ARP缓存(因为B后续大概率需要回包给A),然后构造ARP应答报文,将自己的MAC填入发送方MAC字段,目标MAC填A的MAC,单播发送给A。A收到应答后,将B的IP-MAC映射写入缓存,随后即可正常封装并发送IP数据报。
ARP缓存表项通常具有老化时间(Linux默认约60秒,Windows约120秒),超时后条目被删除,再次通信需重新解析。此外,ARP协议仅适用于IPv4;IPv6使用ICMPv6的邻居发现协议(NDP)完成类似功能。
方法流程:ARP请求与应答的完整交互
以下以一个具体场景说明:主机A(IP: 192.168.1.10,MAC: AA:AA:AA:AA:AA:AA)首次向主机B(IP: 192.168.1.20,MAC: BB:BB:BB:BB:BB:BB)发送数据。A的ARP缓存为空。
- 构造ARP请求:A填充发送方MAC=AA:AA:AA:AA:AA:AA,发送方IP=192.168.1.10,目标IP=192.168.1.20,目标MAC=00:00:00:00:00:00,操作码=1。
- 封装并广播:以太网帧目的MAC=FF:FF:FF:FF:FF:FF,源MAC=AA:AA:AA:AA:AA:AA,类型=0x0806,帧载荷为上述ARP报文。
- B接收并处理:B检查目标IP与自己匹配,将A的IP-MAC映射写入缓存,然后构造ARP应答(操作码=2),发送方MAC=BB:BB:BB:BB:BB:BB,目标MAC=AA:AA:AA:AA:AA:AA。
- A接收应答:A将B的IP-MAC映射写入缓存,随后用B的MAC作为目的MAC封装IP数据报并发送。
抓包验证技巧:使用Wireshark捕获ARP流量时,可设置过滤表达式 arp 或 ether proto 0x0806。观察请求帧的目的MAC是否为广播地址,应答帧的目的MAC是否为请求方的单播地址。通过对比操作码字段(1=请求,2=应答)可快速判断报文类型。
源码解析:Linux内核ARP核心处理逻辑
Linux内核中,ARP协议的核心实现位于 net/ipv4/arp.c。关键函数包括 arp_process()(处理收到的ARP报文)和 arp_send()(构造并发送ARP报文)。以下为 arp_process() 中处理ARP请求的核心逻辑片段(简化版):
// 内核源码示意:arp_process() 处理ARP请求
static int arp_process(struct net *net, struct sock *sk, struct sk_buff *skb)
{
struct arphdr *arp = arp_hdr(skb);
unsigned char *arp_ptr = (unsigned char *)(arp + 1);
struct net_device *dev = skb->dev;
// 提取发送方MAC、发送方IP、目标MAC、目标IP
unsigned char *sha = arp_ptr;
arp_ptr += dev->addr_len; // 跳过发送方MAC
__be32 sip = *(__be32 *)arp_ptr; // 发送方IP
arp_ptr += 4;
unsigned char *tha = arp_ptr; // 目标MAC
arp_ptr += dev->addr_len;
__be32 tip = *(__be32 *)arp_ptr; // 目标IP
// 判断是否为ARP请求(操作码=1)
if (arp->ar_op == htons(ARPOP_REQUEST)) {
// 检查目标IP是否为本机地址
if (tip == inet_addr(dev->dev_addr)) {
// 更新ARP缓存:记录发送方的IP-MAC映射
__neigh_lookup(&arp_tbl, &sip, dev, 1);
// 构造ARP应答并发送
arp_send(ARPOP_REPLY, ETH_P_ARP, sip, dev, sha,
sha, sip, tha, tip);
}
}
return 0;
}
上述代码展示了三个关键点:地址提取顺序严格遵循ARP报文格式;目标IP匹配决定是否应答;缓存更新在应答前完成,确保后续通信可直接使用。实际内核代码还包含代理ARP、 gratuitous ARP(免费ARP)、冲突检测等复杂逻辑,但核心流程与此一致。
案例与场景:真实网络中的ARP行为
场景一:同一交换机下的两台主机首次通信。主机A ping 主机B,A广播ARP请求,B应答,随后A发送ICMP Echo Request。整个过程可通过抓包看到4个报文:ARP请求(广播)、ARP应答(单播)、ICMP请求、ICMP应答。
场景二:ARP缓存老化后的重解析。若A与B在缓存老化时间内未通信,再次通信时A会重新发起ARP请求。这解释了为何长时间静默后首次ping延迟较高。
场景三:ARP欺骗攻击。攻击者C向A发送伪造的ARP应答,声称自己是B(即发送方IP=192.168.1.20,MAC=C的MAC)。A更新缓存后,发往B的数据实际被C截获。防御措施包括静态ARP表、DAI(动态ARP检测)等。
常见误区与排障要点:
- 误区1:认为ARP请求是单播。实际上ARP请求必须广播,因为目标MAC未知。
- 误区2:混淆ARP应答与免费ARP。免费ARP(Gratuitous ARP)是主机主动广播自己的IP-MAC映射,用于地址冲突检测或网关冗余切换,并非对请求的应答。
- 排障提示:若ping不通但ARP缓存中已有目标条目,可执行
arp -d(Linux)或arp -d *(Windows)删除缓存后重试,排除缓存脏数据问题。
误区与复盘:ARP协议理解中的常见偏差
学习ARP时,一个典型误区是认为“ARP请求会跨路由器传播”。实际上,ARP请求永远只在同一广播域内传播,路由器不会转发ARP广播帧。当主机访问不同网段的目标时,它先将数据报发送给默认网关(路由器接口),由网关负责下一跳的ARP解析。因此,ARP解析的是下一跳设备的MAC地址,而非最终目的主机的MAC地址(除非目标就在同一网段)。
另一个易混淆点是ARP报文长度。ARP报文固定为28字节(以太网+IPv4场景),但以太网帧最小载荷为46字节,因此ARP报文封装后需填充至46字节。抓包时看到的ARP帧总长度为42字节(14字节帧头+28字节ARP),但实际线上传输会补齐到64字节(含前导码与FCS)。理解这一点有助于正确解读抓包文件中的帧长度字段。
复盘实际排障案例:某次网络中断,主机无法访问网关。抓包发现主机持续发送ARP请求但无应答。排查发现网关接口所在VLAN配置错误,导致广播帧未到达网关。这提醒我们:ARP请求无应答时,问题往往不在主机本身,而在链路、VLAN或网关接口状态。此时应检查交换机端口VLAN划分、网关接口是否up、以及是否存在ACL阻断广播。