1. MAC层基础:以太网帧结构、MAC地址、MAC子层功能

丢包排查,说到底就是跟 MAC 层打交道。你想想看,网络数据从应用层一路封装下来,最后变成比特流在线上跑,MAC 层就是那个“装货上车”的环节。如果这层出了错,上层再好的协议也白搭。我个人习惯先把 MAC 层的基本零件拆开,看清楚帧长什么样、地址怎么用、子层管什么。

1.1 以太网帧结构——数据在线上到底长啥样?

以太网帧不是凭空画出来的。IEEE 802.3 标准规定了它必须包含这些字段。我直接列一个表,后面再逐一说。

字段长度(字节)说明
前导码(Preamble)71010…交替,用于时钟同步
帧起始定界符(SFD)110101011,标识帧开始
目的 MAC 地址6接收方地址
源 MAC 地址6发送方地址
类型/长度2> 1536 时为 EtherType,否则为长度
数据 / 填充46–1500最少 46 字节,不够就补 0
帧校验序列(FCS)4CRC32,校验除前导码/SFD 外的所有字段

这里我特别想告诉你一个坑。我在项目中遇到过好几次,有人把前导码也算进帧长,结果抓包软件显示的帧长总是多 8 字节。实际上,前导码和 SFD 在 MAC 层处理完后就被剥离了,真正发给上层的是从目的 MAC 开始到 FCS 结束的那部分。嗯,记住这一点,后面分析错包时就少绕弯路。

我曾经在产线上遇到一台交换机,它错误地把带前导码的帧当作正常帧转发,导致对端 CRC 校验一直失败。排查了整整三天才发现是芯片驱动把 SFD 也送过去了。所以,千万别想当然——看 Frame Check Sequence 的范围,确认你的解析工具到底从哪个字节开始算。
技巧:用 Wireshark 看以太网帧时,把 "Ethernet II" 那一层的首部长度展开,对比目的 MAC 在 hex dump 中的位置。如果 hex 第一个字节不是目的 MAC,很可能包含了前导码,这时要调整 "Link-layer header" 解析方式。

1.2 MAC 地址——它是怎么被分配的?

MAC 地址 48 位,前 24 位是 OUI(组织唯一标识符),后 24 位是设备分配的序列号。你可以在 IEEE 官网查 OUI,但我一般直接 ping 完用 arp -a 看缓存,更快。不过地址的格式里有个字节级的小逻辑:目的 MAC 第一个字节的最低位为 0 表示单播,为 1 表示多播;全 1 是广播。

// 一个典型的单播 MAC:00-1A-2B-3C-4D-5E
// 二进制第一字节:0000 0000 → LSB = 0 → 单播
// 多播 MAC 举例:01-00-5E-00-00-01(用于组播路由的 OSPF)
// 广播 MAC:FF-FF-FF-FF-FF-FF

重要:MAC 地址的比特顺序有大坑。在以太网传输时,每个字节的 LSB 先发送(即所谓的“规范格式”),但有些文档(比如 Ring)用 MSB 先发送。你如果做硬件调试,发现抓到的比特流和预期反了,八成是这个原因。

说白了,MAC 地址只有本地链路意义,出了广播域就不依赖它了。我曾经遇到一个客户,非要用 MAC 跨三层做访问控制,结果路由一跳 MAC 就变了,策略根本执行不了。这就像用门牌号去定位全国的城市,不对路的。

1.3 MAC 子层功能——它到底干了哪些活?

IEEE 802.3 把 MAC 子层拆成两个小部分:MAC 控制子层(可选)和 MAC 数据子层。数据子层就是做帧的封装/解封装,还有介质访问控制——说白了就是 CSMA/CD(半双工时)和现在的全双工流控(PAUSE 帧)。

你可能会问:“现在都是全双工,CSMA/CD 是不是没用了?” 其实不然,很多交换机在端口协商失败后会自动回退到半双工,这时候 MAC 子层仍然要监听碰撞。我排查过一个丢包案例,就是网线只通了两对线导致协商成半双工,单向大量冲突引起 CRC 错误。

MAC 子层还负责两个很关键的“边缘检查”:

  • 帧长度检查:小于 64 字节(含 FCS)视为 runt,大于 1518 字节(含 VLAN 标签时 1522)视为 jabber,直接丢弃。
  • FCS 校验:计算 CRC32,不匹配就打上错误标记。

这里有一个常见的误解:FCS 错误不一定是物理层问题。我见过一个案例,CPU 驱动的 DMA 环形缓冲区写乱了,导致 MAC 层自己发出的帧 CRC 就是错的。后来改了一下 DMA 描述符对齐才解决。所以排查时别只查线缆,也要看看驱动和硬件 FIFO。

以太网帧结构(IEEE 802.3) 前导码 7 字节 SFD 1B 目的 MAC 6 字节 源 MAC 6 字节 类型/长度 2B 数据 + 填充 46 ~ 1500 字节 FCS 4 字节 前导码+SFD 被 MAC 剥离 MAC 层送上层仅从此开始 CRC 校验范围

1.4 几个容易让人栽跟头的细节

  • 帧最小长度 64 字节(含 FCS)。因为 CSMA/CD 需要确保冲突信号在发送结束前传回。全双工下这个限制仍然存在,只是不用于冲突检测了。我见过一台老交换机扔掉了所有小于 64 字节的正常 TCP ACK,就因为 CPU 发送时没补 0。
  • VLAN 标签:802.1Q 在源 MAC 和类型/长度之间插入 4 字节(TPID + TCI),此时最大帧长变为 1522 字节。有些老交换机标称 1518,遇到带 VLAN 的大包直接就丢了。
  • 帧间隔(IFG):发送完一帧后必须空闲至少 12 字节时间(96 bit-time)。不算在帧结构里,但 MAC 层必须遵守,否则接收方可能把两帧当成一帧。
排查小技巧:当你看到接口计数器的 "Frame too long" 在增长,先看一下是否开启了 jumbo frame(巨型帧)。很多设备默认只支持 1518,如果交换机允许 9216 字节,并且对端也配了,但中间某段链路没启用,那个中间设备就会判定 jabber 并丢弃,表现为单向丢包。

好了,这一章我把 MAC 层的几个基本零件拆开讲了一遍。帧结构、地址、子层功能,三者加起来就是以太网通信的基石。记住,很多丢包的根本原因就藏在这些“常识”里——比如 CRC 校验域算错、前导码污染、巨型帧不匹配。下一回你抓个 ping 包,不妨从目的 MAC 第一个字节的第一个比特开始看,说不定能发现点什么。