网络数据帧与 MTU

以太帧(Ethernet Frame)

以太帧的发展历史得从以太网说起:

  • 1973 年, Bob Metcalfe 在 Xerox 和同事一起发明了以太网, 具体过程可以参考 以太网的发展历史.
  • 1979 年, Metcalfe 成立 3Com 公司, 并对 DEC, Intel 和 Xerox 进行游说, 希望与他们一起将以太网标准化, 规范化.
  • 1980 年, 3Com 和 DEC, Intel, Xerox 一起标准化以太帧的封装格式, 即 Ethernet V1.
  • 1982 年, 3Com 和 DEC, Intel, Xerox 一起, 公布新的以太帧标准, 电气和物理接口有更新, 协议格式并无变化, 即 Ethernet V2, 又叫 ARPA.
  • 1984 年, RFC 894 定稿, 指定了以太帧封装 IP 报文的协议标准.

以太帧的格式如下:

源 MAC 目的 MAC 协议类型 数据 FCS
6 Bytes 6 Bytes 2 Bytes 46 - 1500 Bytes 4 Bytes

常见的协议类型有:

  • 0x0800 IP报文
  • 0x0806 ARP
  • 0x0808 Frame Relay ARP
  • 0x8035 RARP

以太帧是现在以太网的事实标准,最常见就是 0x0800 的 IP 报文了, 通过 wireshark 或者类 pcap 工具抓包也很容易发现(FCS 是帧校验, 由硬件来负责处理, 所以抓到的帧包在 60 - 1514 之间).

IEEE 802.x 标准

1988 年, IEEE 802 委员会公布了一个稍有不同的 802 标准集, 详见rfc 1042:

  • 802.3 针对整个 CSMA/CD 网络, 协议上兼容 Ethernet V2.
  • 802.4 针对令牌总线网络, ARC Net.
  • 802.5 针对令牌环网络, Token Ring.
  • 802.3, 802.4, 802.5 对应数据链路层上的 MAC, 处理对物理层的访问
  • 802.2 逻辑链路控制标准, LLC (Logic Link Control), 用于层协议复用
  • 三种帧结构即为: 802.3 + 802.2, 802.4 + 802.2, 802.5 + 802.2

我们主要关注与 Ethernet Frame 对应的 802.3:

802.3 raw 帧

源 MAC 目的 MAC 总长度 0xFFFF 数据 FCS
6 Bytes 6 Bytes 2 Bytes 2 Bytes 44 - 1498 Bytes 4 Bytes

802.3 SAP 帧

源 MAC 目的 MAC 总长度 DSAP SSAP 控制字段 数据 FCS
6 Bytes 6 Bytes 2 Bytes 1 Byte 1 Byte 1 Byte 43 - 1497 Bytes 4 Bytes

802.3 SNAP 帧

源 MAC 目的 MAC 总长度 DSAP(0xAA) SSAP(0xAA) 控制字段(0x03) OUI Protocol ID 数据 FCS
6 Bytes 6 Bytes 2 Bytes 1 Byte 1 Byte 1 Byte 3 Bytes 2 Bytes 38 - 1492 Bytes 4 Bytes
  • 802.3 兼容 Ethernet V2:
    • 如果 Ethernet V2 type 字段如果是在 0x0000-0x05DC 范围内, 实际上就是 802.3 帧, Ether Type 字段解释为 802.3 帧的总长度.
    • 如果 Ethernet V2 type > 0x0600, 对应 Ethernet V2 的各种协议格式
  • 在 802.3 SAP 帧中, DSAP(目的 SAP) + SSAP(源 SAP) + 控制字段: 组成了 LLC.
  • SAP 用以标志上层应用, 但是只有1字节, 且保留 2 位, 实际只有 6 位可用, 因此所能标识的协议数有限(< 32), 例如 ARP 协议就没有 SAP 值, 所以使用有局限性. 常见的 SAP 类型可以参考 wiki.
  • 当 DSAP & SSAP 类型都是 0xAA 时, 802.3 SAP 帧就变成了 802.3 SNAP 帧, 即 Subnetwork Access Protocol, OUI + Protocol ID 这两个扩展字段组成了 SNAP extension, 代价是数据位又少了 5 Bytes.

Ethernet VS. IEEE 802.3

虽然 IEEE 802.3 是标准, 但实际的现状是绝大部分应用的网络数据包都使用 Ethernet V2, 例如 HTTP, FTP, SMTP 等, 少量交换机除外(使用 802.3 帧).

究其原因, TCP/IP 大规模民用的时间点在 rfc 894 (1984 年, Ethernet V2)和 rfc 1042 (1988 年, 802.x)之间, 为了避免遭遇网络设备不通的风险, 设备商包括 Unix 都采用了 rfc 894 的 Ethernet V2 实现, 所以 Ethernet V2 随着网络设备和 Unix 的普及, 也慢慢成为了事实标准. 802.3 好尴尬...

关于 MTU

MTU, Maximum Transmission Unit, 最大传输单元. 一般是指数据链路层能一次传输的最大数据数. 从上面的内容可以看出, Ethernet V2 帧的最大数据 = 1500 Bytes, 802.3 帧中的最大数据 = 1492 Bytes.

wiki 中描述了如何做 Path MTU Discovery, 简单来说就是:

  1. IP 报文设置 DF(dont fragment) 标记, 然后逐渐增大发送的报文大小.
  2. rfc 792 约定, 超过网络设备 MTU 时, 设备需要回复 "ICMP Destination Unreachable-Fragmentation Needed and DF Set" ICMP 消息,然后丢弃原包.
  3. rfc 1191 约定, 超过网络设备 MTU 时, 设备需要回复 "ICMP Destination Unreachable-Fragmentation Needed and DF Set" ICMP 消息, 并带回当前支持的 MTU 值, 然后丢弃原包.
  4. 当 IP 报文增大到一定值, 第一次收到 ICMP 需要分组不可达消息时, 即可确定链路中的 MTU 值.

然而, 问题来了, 并不是所有的网络设备都会遵循 rfc 792 或者 rfc 1191, 除了诸如路由调整等事件会导致传输链路中 MTU 变化之外, 很多设备为了防止 DDos 禁止 ICMP, 就会将超过 MTU 的报文直接丢弃, 形成黑洞.

标准和协议的制定, 需要依赖于人们去遵循, 当存在漏洞空间可以获取利益IEEE时, 协议的约束就基本失效了, 不能寄希望于"我为人人, 人人为我".

如果运气好的话, 可以通过下面的方法来测试链路中的 MTU:

  • Linux 下, ping domain/ip -f -s packet-size
  • Windows 下, ping domain/ip -f -l packet-size

如果对传输数据有要求, 例如 UDP 传输不希望 IP 层的报文分片, 一个更为稳妥的做法是: 假设 MTU = 576, 这是 rfc 791 中要求的:

All hosts must be prepared to accept datagrams of up to 576 octets (whether they arrive whole or in fragments)

为什么是 576 呢?

The number 576 is selected to allow a reasonable sized data block to be transmitted in addition to the required header information. For example, this size allows a data block of 512 octets plus 64 header octets to fit in a datagram

看, rfc 都帮我们假设好了, 512 Bytes 数据 + 64 Bytes 网络包头 (例如 IP 报文头 20 Byets, TCP 包头 20 Bytes, 还有 24 Bytes 够业务包头用的).

参考文章