bigywater.tech
网络协议 · 2

以太网帧:MAC 地址、EtherType、最小帧长与 FCS

以太网帧头只有 14 字节,却决定了数据交给谁、发给谁、能多大。这篇在真实网卡上抓帧,看 EtherType、MAC 的标志位、最小帧长补零、MTU 边界,以及抓包里永远看不到的 FCS。

4007 帧
20 秒里在真实网卡上抓到的以太网帧数(IPv4 3899、ARP 70、IPv6 14)
42 vs 60
本机发的 ARP 请求与对方回的 ARP 应答在抓包里的帧长——补零发生在网卡
1514
ping -D -s 1472 的帧长;多 1 字节(1473)就被拒绝

帧头

实测 · 本机执行输出
  20 秒共抓到 4007 个帧(含周围设备发出的广播 / 组播)
  EtherType 分布: 0x0800(IPv4)×3899, 0x0806(ARP)×70, 0x86dd(IPv6)×14, 0x9998(other)×12, 0x2589(other)×10, 0x002e(other)×1
  目的地址种类: 单播×3306, 广播 ff:ff:ff:ff:ff:ff×372, 组播(IPv4, 01:00:5e:…)×317, 组播(IPv6, 33:33:…)×12
  帧长(不含 FCS)最小 42、最大 1506、中位数 102 字节

以太网帧头 14 字节:目的 MAC 6、源 MAC 6、EtherType 2。EtherType 决定载荷交给 IPv4(0x0800)、ARP(0x0806)还是 IPv6(0x86dd)。

MAC 的两个标志位

实测 · 本机执行输出
  本机网卡 MAC 7e:aa:6f:xx:xx:xx  第一个字节 0x7e: 组播位=0, 本地管理位=1(本地管理位=1:系统生成的随机私有地址,不是出厂烧录的)
  默认网关 MAC 70:85:6c:xx:xx:xx  第一个字节 0x70: 组播位=0, 本地管理位=0(本地管理位=0:厂商烧录的全局唯一地址,前 3 字节是厂商 OUI)
  广播地址 ff:ff:ff:ff:ff:ff  第一个字节 0xff: 组播位=1, 本地管理位=1
  IPv4 组播目的 MAC 01:00:5e:00:00:fb  对应 IP 组播地址 224.0.0.251:低 23 位直接取自 IP 地址

第一个字节最低位是组播位(广播也是组播的一种),次低位是本地管理位:随机私有地址和虚拟网卡的本地管理位为 1,厂商烧录的全局地址为 0。IPv4 组播的 MAC 由 01:00:5e 加 IP 低 23 位构成。

最小帧

实测 · 本机执行输出
  +   0.000 ms  ARP, Request who-has <本机局域网IP> tell <本机局域网IP>, length 28
  (这次共抓到 19 个 ARP 帧,其余是网络里别的设备之间的 ARP,这里只列和本机相关的)
  本机发出的 ARP 请求帧长 42 字节 = 14 以太网头 + 28 ARP(没有补零:补零由网卡在发送时做,抓包点在网卡之前)
  别的设备回来的 ARP 应答帧长 60 字节 = 14 + 28 + 18 字节补零(对方网卡补到 60,抓到时已含补零;FCS 4 字节另算,总共 64)
  ARP 请求是广播(目的 ff:ff:ff:ff:ff:ff),EtherType=0x0806

以太网最小帧 64 字节(含 FCS),短的帧由网卡补零;抓包点在网卡之前时看到的 ARP 请求是 42 字节,收到的别人发来的已经补过零,是 60 字节。

最大帧与 MTU

实测 · 本机执行输出
  -s 1472: 1 packets transmitted, 1 packets received, 0.0% packet loss
  抓到的 ICMP 帧长: [1514, 1514](= 14 + 20 + 8 + 1472 = 1514,这是标准以太网帧去掉 FCS 之后的最大值;含 FCS 是 1518)
  -s 1473: ping: sendto: Message too long
  -s 1473 时有没有新的 ICMP 帧出现在网线上: 没有(只抓到 2 个帧,都来自 -s 1472 的请求和应答)

载荷最大 1500,帧去掉 FCS 最大 1514(含 FCS 1518)。ping -D -s 1473 超过 MTU,本机直接拒绝发送。

FCS

实测 · 本机执行输出
  对这个 42 字节的 ARP 请求帧(不含补零)做 CRC-32 = 0x83f1343a;真实发送时补零到 60 字节再算 FCS 并附在尾部,字节序为小端(低位字节先发)
  所以线上的完整帧 = 60 字节(补零后)+ 4 字节 FCS = 64 字节;抓包文件里这 4 字节不存在

帧尾的 4 字节 CRC-32 由网卡硬件加和校验,抓包文件里不存在。

分步演示

以太网帧
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64(Apple 芯片)/ Python 3.14.8 / tcpdump(libpcap)/ scapy 2.8 / dpkt / dnspython / h2 / aioquic / OpenSSL / curl 8.7.1;抓包在 lo0(环回)上做,少数实验用真实网卡 en0(这台机器上 en0 是 Wi-Fi,tcpdump 看到的是系统转成以太网格式的帧,所以文中的“以太网头”都指这个 14 字节的头)或访问公网(会在文章里注明);需要丢包 / 延迟 / 限速时,用一个只作用于 lo0 端口 9400-9499 的 dummynet 辅助脚本(netlab);所有服务端都是本机自己起的进程;时间戳取自 tcpdump 的 -tt,数字每次运行会有小幅抖动。第一节在 en0 上不加过滤抓 20 秒,统计包括网络里其它设备发出的广播和组播;MAC 地址只保留前 3 字节;ARP 与 ICMP 的节用的是真实的默认网关和一个同网段不存在的 IP;FCS 的数字是 zlib.crc32 的计算值,不是抓到的
    • 没有验证:802.11(Wi-Fi)的帧格式(en0 上 tcpdump 给的是以太网伪头);VLAN 标签帧;巨型帧(MTU 9000);网卡 TSO / LRO 下抓到的超大帧;FCS 错误帧的计数(需要网卡统计);交换机如何处理帧(学习与转发,见后面的篇目)
    京ICP备2026067502号-1京公网安备11010502063487号